@agentbean/daemon 0.1.31 → 0.1.33
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/dist/adapters/claude-code.js +1 -2
- package/dist/adapters/codex.js +147 -22
- package/dist/agent-instance.js +1 -1
- package/dist/config.js +12 -0
- package/dist/device-daemon.js +12 -1
- package/dist/sandbox.js +10 -2
- package/dist/scanner.js +3 -1
- package/package.json +1 -1
|
@@ -12,8 +12,7 @@ function buildPrompt(input, systemPrompt) {
|
|
|
12
12
|
return parts.join('\n\n---\n\n');
|
|
13
13
|
}
|
|
14
14
|
function normalizeClaudeArgs(args) {
|
|
15
|
-
|
|
16
|
-
return ['-p', ...filtered];
|
|
15
|
+
return ['-p', ...(args ?? [])];
|
|
17
16
|
}
|
|
18
17
|
export class ClaudeCodeAdapter {
|
|
19
18
|
opts;
|
package/dist/adapters/codex.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { spawn } from 'node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { spawn as spawnChild } from 'node:child_process';
|
|
2
|
+
import { spawn as spawnPty } from 'node-pty';
|
|
3
|
+
import { accessSync, constants, existsSync, mkdtempSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { homedir, tmpdir } from 'node:os';
|
|
5
|
+
import { delimiter, join } from 'node:path';
|
|
4
6
|
function renderPayload(input, systemPrompt) {
|
|
5
7
|
const parts = [];
|
|
6
8
|
if (systemPrompt)
|
|
@@ -42,13 +44,46 @@ export function extractCodexReply(output, payload) {
|
|
|
42
44
|
}
|
|
43
45
|
return clean.trim();
|
|
44
46
|
}
|
|
45
|
-
function
|
|
47
|
+
function hasFlag(args, ...flags) {
|
|
48
|
+
return args.some((arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
|
|
49
|
+
}
|
|
50
|
+
function flagValue(args, ...flags) {
|
|
51
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
52
|
+
const arg = args[i];
|
|
53
|
+
for (const flag of flags) {
|
|
54
|
+
if (arg === flag)
|
|
55
|
+
return args[i + 1];
|
|
56
|
+
if (arg.startsWith(`${flag}=`))
|
|
57
|
+
return arg.slice(flag.length + 1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function createOutputLastMessagePath() {
|
|
63
|
+
return join(mkdtempSync(join(tmpdir(), 'agentbean-codex-')), 'last-message.txt');
|
|
64
|
+
}
|
|
65
|
+
function normalizeExecArgs(args, outputLastMessagePath) {
|
|
46
66
|
const baseArgs = args && args.length > 0 ? args : ['exec'];
|
|
47
67
|
const subcommand = baseArgs[0];
|
|
48
|
-
if (
|
|
49
|
-
|
|
68
|
+
if (subcommand === 'exec' || subcommand === 'e') {
|
|
69
|
+
const rest = baseArgs.slice(1);
|
|
70
|
+
const normalized = [subcommand];
|
|
71
|
+
if (!hasFlag(rest, '--skip-git-repo-check')) {
|
|
72
|
+
normalized.push('--skip-git-repo-check');
|
|
73
|
+
}
|
|
74
|
+
const configuredOutputPath = flagValue(rest, '--output-last-message', '-o');
|
|
75
|
+
if (!configuredOutputPath) {
|
|
76
|
+
normalized.push('--output-last-message', outputLastMessagePath);
|
|
77
|
+
}
|
|
78
|
+
if (!hasFlag(rest, '--json', '--experimental-json')) {
|
|
79
|
+
normalized.push('--json');
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
args: [...normalized, ...rest],
|
|
83
|
+
outputLastMessagePath: configuredOutputPath ?? outputLastMessagePath,
|
|
84
|
+
};
|
|
50
85
|
}
|
|
51
|
-
return baseArgs;
|
|
86
|
+
return { args: baseArgs };
|
|
52
87
|
}
|
|
53
88
|
function adapterTimeoutMs() {
|
|
54
89
|
const fromEnv = Number.parseInt(process.env.AGENTBEAN_CODEX_TIMEOUT_MS ?? '', 10);
|
|
@@ -67,6 +102,89 @@ function buildRuntimeEnv(extra) {
|
|
|
67
102
|
].filter(Boolean).join(':');
|
|
68
103
|
return { ...process.env, PATH: pathEntries, ...(extra ?? {}) };
|
|
69
104
|
}
|
|
105
|
+
function assertExecutable(command, env, label) {
|
|
106
|
+
const trimmed = command.trim();
|
|
107
|
+
if (!trimmed) {
|
|
108
|
+
throw new Error(`${label} command is empty`);
|
|
109
|
+
}
|
|
110
|
+
if (trimmed.includes('/')) {
|
|
111
|
+
try {
|
|
112
|
+
accessSync(trimmed, constants.X_OK);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new Error(`${label} command is not executable: ${trimmed}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const pathEntries = (env.PATH ?? '').split(delimiter).filter(Boolean);
|
|
120
|
+
for (const dir of pathEntries) {
|
|
121
|
+
try {
|
|
122
|
+
accessSync(join(dir, trimmed), constants.X_OK);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`${label} command was not found on PATH: ${trimmed}. PATH=${env.PATH ?? ''}`);
|
|
128
|
+
}
|
|
129
|
+
function readOutputLastMessage(path) {
|
|
130
|
+
if (!path || !existsSync(path))
|
|
131
|
+
return null;
|
|
132
|
+
const text = readFileSync(path, 'utf8').trim();
|
|
133
|
+
return text || null;
|
|
134
|
+
}
|
|
135
|
+
function spawnRuntimeProcess(command, args, opts) {
|
|
136
|
+
try {
|
|
137
|
+
const pty = spawnPty(command, args, {
|
|
138
|
+
name: 'xterm-color',
|
|
139
|
+
cols: 80,
|
|
140
|
+
rows: 30,
|
|
141
|
+
cwd: opts.cwd,
|
|
142
|
+
env: opts.env,
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
onData: (cb) => pty.onData(cb),
|
|
146
|
+
onExit: (cb) => pty.onExit(({ exitCode }) => cb({ exitCode })),
|
|
147
|
+
kill: (signal) => pty.kill(signal),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const child = spawnChild(command, args, {
|
|
152
|
+
cwd: opts.cwd,
|
|
153
|
+
env: opts.env,
|
|
154
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
155
|
+
});
|
|
156
|
+
const dataHandlers = [];
|
|
157
|
+
const exitHandlers = [];
|
|
158
|
+
let exited = false;
|
|
159
|
+
const emitExit = (exitCode) => {
|
|
160
|
+
if (exited)
|
|
161
|
+
return;
|
|
162
|
+
exited = true;
|
|
163
|
+
for (const handler of exitHandlers)
|
|
164
|
+
handler({ exitCode });
|
|
165
|
+
};
|
|
166
|
+
child.stdout?.on('data', (data) => {
|
|
167
|
+
for (const handler of dataHandlers)
|
|
168
|
+
handler(String(data));
|
|
169
|
+
});
|
|
170
|
+
child.stderr?.on('data', (data) => {
|
|
171
|
+
for (const handler of dataHandlers)
|
|
172
|
+
handler(String(data));
|
|
173
|
+
});
|
|
174
|
+
child.on('error', (childErr) => {
|
|
175
|
+
const message = childErr instanceof Error ? childErr.message : String(childErr);
|
|
176
|
+
for (const handler of dataHandlers)
|
|
177
|
+
handler(message);
|
|
178
|
+
emitExit(1);
|
|
179
|
+
});
|
|
180
|
+
child.on('exit', (code) => emitExit(code ?? 1));
|
|
181
|
+
return {
|
|
182
|
+
onData: (cb) => { dataHandlers.push(cb); },
|
|
183
|
+
onExit: (cb) => { exitHandlers.push(cb); },
|
|
184
|
+
kill: (signal) => { child.kill(signal); },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
70
188
|
export class CodexAdapter {
|
|
71
189
|
opts;
|
|
72
190
|
kind = 'codex';
|
|
@@ -78,19 +196,26 @@ export class CodexAdapter {
|
|
|
78
196
|
const payload = renderPayload(input, this.opts.systemPrompt ?? input.systemPrompt);
|
|
79
197
|
const cwd = input.workspace ?? this.opts.cwd ?? process.cwd();
|
|
80
198
|
const baseCommand = this.opts.command || 'codex';
|
|
81
|
-
const
|
|
199
|
+
const defaultOutputLastMessagePath = createOutputLastMessagePath();
|
|
200
|
+
const normalizedExec = normalizeExecArgs(this.opts.args, defaultOutputLastMessagePath);
|
|
201
|
+
const configuredArgs = normalizedExec.args;
|
|
82
202
|
const baseArgs = [...configuredArgs, payload];
|
|
83
203
|
const command = input.sandboxProfilePath ? 'sandbox-exec' : baseCommand;
|
|
84
204
|
const args = input.sandboxProfilePath
|
|
85
205
|
? ['-f', input.sandboxProfilePath, '--', baseCommand, ...baseArgs]
|
|
86
206
|
: baseArgs;
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
207
|
+
const env = buildRuntimeEnv(input.env);
|
|
208
|
+
try {
|
|
209
|
+
assertExecutable(command, env, input.sandboxProfilePath ? 'Sandbox launcher' : 'Codex runtime');
|
|
210
|
+
if (input.sandboxProfilePath) {
|
|
211
|
+
assertExecutable(baseCommand, env, 'Codex runtime');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
reject(err);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const runtime = spawnRuntimeProcess(command, args, { cwd, env });
|
|
94
219
|
const chunks = [];
|
|
95
220
|
let finished = false;
|
|
96
221
|
const MAX_EXEC_MS = adapterTimeoutMs();
|
|
@@ -100,10 +225,10 @@ export class CodexAdapter {
|
|
|
100
225
|
finished = true;
|
|
101
226
|
clearTimeout(maxTimer);
|
|
102
227
|
signal.removeEventListener('abort', onAbort);
|
|
103
|
-
|
|
228
|
+
runtime.kill('SIGTERM');
|
|
104
229
|
setTimeout(() => {
|
|
105
230
|
try {
|
|
106
|
-
|
|
231
|
+
runtime.kill('SIGKILL');
|
|
107
232
|
}
|
|
108
233
|
catch { }
|
|
109
234
|
}, 2_000).unref();
|
|
@@ -114,12 +239,12 @@ export class CodexAdapter {
|
|
|
114
239
|
if (finished)
|
|
115
240
|
return;
|
|
116
241
|
finished = true;
|
|
117
|
-
|
|
242
|
+
runtime.kill('SIGKILL');
|
|
118
243
|
signal.removeEventListener('abort', onAbort);
|
|
119
244
|
reject(new Error('codex adapter timeout'));
|
|
120
245
|
}, MAX_EXEC_MS).unref();
|
|
121
|
-
|
|
122
|
-
|
|
246
|
+
runtime.onData((data) => chunks.push(data));
|
|
247
|
+
runtime.onExit(({ exitCode }) => {
|
|
123
248
|
clearTimeout(maxTimer);
|
|
124
249
|
signal.removeEventListener('abort', onAbort);
|
|
125
250
|
if (finished)
|
|
@@ -132,7 +257,7 @@ export class CodexAdapter {
|
|
|
132
257
|
const detail = stripAnsi(raw).trim();
|
|
133
258
|
return reject(new Error(detail ? `codex exit ${exitCode}: ${detail}` : `codex exit ${exitCode}`));
|
|
134
259
|
}
|
|
135
|
-
const reply = extractCodexReply(raw, payload);
|
|
260
|
+
const reply = readOutputLastMessage(normalizedExec.outputLastMessagePath) ?? extractCodexReply(raw, payload);
|
|
136
261
|
resolve(reply || '(Codex 已完成处理)');
|
|
137
262
|
});
|
|
138
263
|
});
|
|
@@ -140,7 +265,7 @@ export class CodexAdapter {
|
|
|
140
265
|
async health() {
|
|
141
266
|
return new Promise((resolve) => {
|
|
142
267
|
try {
|
|
143
|
-
const pty =
|
|
268
|
+
const pty = spawnPty('bash', ['-c', 'codex --version'], {
|
|
144
269
|
name: 'xterm-color', cols: 80, rows: 30,
|
|
145
270
|
cwd: this.opts.cwd ?? process.cwd(),
|
|
146
271
|
env: buildRuntimeEnv(),
|
package/dist/agent-instance.js
CHANGED
|
@@ -108,7 +108,7 @@ export class AgentInstance {
|
|
|
108
108
|
sandboxProfilePath: req.sandboxed && isSandboxAvailable()
|
|
109
109
|
? generateSandboxProfile(this.id, this.config.adapter.command)
|
|
110
110
|
: undefined,
|
|
111
|
-
env: workspaceEnv(run),
|
|
111
|
+
env: { ...(this.config.adapter.env ?? {}), ...workspaceEnv(run) },
|
|
112
112
|
}, ctl.signal);
|
|
113
113
|
const processed = await postProcess(rawBody, projectWorkspace, this.adapter.kind, dispatchStart, {
|
|
114
114
|
outputDirs: [run.outputDir, run.intermediateDir],
|
package/dist/config.js
CHANGED
|
@@ -23,6 +23,16 @@ function deepInterpolate(node) {
|
|
|
23
23
|
}
|
|
24
24
|
return node;
|
|
25
25
|
}
|
|
26
|
+
function parseEnvMap(value) {
|
|
27
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
28
|
+
return undefined;
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
31
|
+
if (key.trim())
|
|
32
|
+
out[key.trim()] = String(raw ?? '');
|
|
33
|
+
}
|
|
34
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
35
|
+
}
|
|
26
36
|
export function loadConfig(path) {
|
|
27
37
|
const raw = parseYaml(readFileSync(path, 'utf8'));
|
|
28
38
|
if (!raw || typeof raw !== 'object')
|
|
@@ -62,6 +72,7 @@ export function loadConfig(path) {
|
|
|
62
72
|
cwd: typeof a.cwd === 'string' ? a.cwd : undefined,
|
|
63
73
|
workspace: typeof a.workspace === 'string' ? a.workspace : undefined,
|
|
64
74
|
systemPrompt: typeof a.systemPrompt === 'string' ? a.systemPrompt : undefined,
|
|
75
|
+
env: parseEnvMap(a.env),
|
|
65
76
|
},
|
|
66
77
|
server: { url: s.url, token: s.token },
|
|
67
78
|
heartbeatIntervalMs: isValidHeartbeat(interp.heartbeatIntervalMs) ? interp.heartbeatIntervalMs : 10_000,
|
|
@@ -121,6 +132,7 @@ export function loadDeviceConfig(path) {
|
|
|
121
132
|
cwd: typeof ad.cwd === 'string' ? ad.cwd : undefined,
|
|
122
133
|
workspace: typeof ad.workspace === 'string' ? ad.workspace : undefined,
|
|
123
134
|
systemPrompt: typeof ad.systemPrompt === 'string' ? ad.systemPrompt : undefined,
|
|
135
|
+
env: parseEnvMap(ad.env),
|
|
124
136
|
},
|
|
125
137
|
visibility: a.visibility === 'public' ? 'public' : 'private',
|
|
126
138
|
sandboxed: a.sandboxed === true,
|
package/dist/device-daemon.js
CHANGED
|
@@ -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 [{
|
|
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 [{
|
|
@@ -391,6 +401,7 @@ export function createDeviceDaemon(cfg, agents) {
|
|
|
391
401
|
args: custom.args ?? [],
|
|
392
402
|
cwd: custom.cwd ?? undefined,
|
|
393
403
|
workspace: custom.cwd ?? undefined,
|
|
404
|
+
env: custom.env ?? undefined,
|
|
394
405
|
systemPrompt: custom.description ?? undefined,
|
|
395
406
|
},
|
|
396
407
|
visibility: 'public',
|
package/dist/sandbox.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { accessSync, constants, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
function escapeSchemeString(value) {
|
|
@@ -11,7 +11,15 @@ export function getWorkspaceDir(agentId) {
|
|
|
11
11
|
return dir;
|
|
12
12
|
}
|
|
13
13
|
export function isSandboxAvailable() {
|
|
14
|
-
|
|
14
|
+
if (process.platform !== 'darwin')
|
|
15
|
+
return false;
|
|
16
|
+
try {
|
|
17
|
+
accessSync('/usr/bin/sandbox-exec', constants.X_OK);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
15
23
|
}
|
|
16
24
|
export function generateSandboxProfile(agentId, runtimePath) {
|
|
17
25
|
const workspaceDir = getWorkspaceDir(agentId);
|
package/dist/scanner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { readdirSync, readFileSync, statSync, existsSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, 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";
|
|
@@ -274,6 +274,7 @@ async function checkHermesGateway() {
|
|
|
274
274
|
adapterKind: "hermes",
|
|
275
275
|
command: path,
|
|
276
276
|
args: [],
|
|
277
|
+
cwd: dirname(path),
|
|
277
278
|
source: "gateway",
|
|
278
279
|
};
|
|
279
280
|
}
|
|
@@ -293,6 +294,7 @@ async function checkOpenClawGateway() {
|
|
|
293
294
|
adapterKind: "openclaw",
|
|
294
295
|
command: path,
|
|
295
296
|
args: ["agent", "--agent", agentId ?? "main"],
|
|
297
|
+
cwd: dirname(path),
|
|
296
298
|
source: "gateway",
|
|
297
299
|
};
|
|
298
300
|
}
|