@agentbean/daemon 0.1.35 → 0.2.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.
- package/dist/apps/daemon-next/src/bin.d.ts +2 -0
- package/dist/apps/daemon-next/src/bin.js +6 -0
- package/dist/apps/daemon-next/src/cli.d.ts +26 -0
- package/dist/apps/daemon-next/src/cli.js +124 -0
- package/dist/apps/daemon-next/src/executor.d.ts +6 -0
- package/dist/apps/daemon-next/src/executor.js +51 -0
- package/dist/apps/daemon-next/src/index.d.ts +60 -0
- package/dist/apps/daemon-next/src/index.js +87 -0
- package/dist/apps/daemon-next/src/scanner.d.ts +15 -0
- package/dist/apps/daemon-next/src/scanner.js +94 -0
- package/dist/packages/contracts/src/agent.d.ts +69 -0
- package/dist/packages/contracts/src/agent.js +4 -0
- package/dist/packages/contracts/src/auth.d.ts +20 -0
- package/dist/packages/contracts/src/auth.js +1 -0
- package/dist/packages/contracts/src/channel.d.ts +58 -0
- package/dist/packages/contracts/src/channel.js +1 -0
- package/dist/packages/contracts/src/common.d.ts +17 -0
- package/dist/packages/contracts/src/common.js +27 -0
- package/dist/packages/contracts/src/device.d.ts +27 -0
- package/dist/packages/contracts/src/device.js +1 -0
- package/dist/packages/contracts/src/dispatch.d.ts +46 -0
- package/dist/packages/contracts/src/dispatch.js +1 -0
- package/dist/packages/contracts/src/index.d.ts +9 -0
- package/dist/packages/contracts/src/index.js +9 -0
- package/dist/packages/contracts/src/message.d.ts +20 -0
- package/dist/packages/contracts/src/message.js +1 -0
- package/dist/packages/contracts/src/socket.d.ts +74 -0
- package/dist/packages/contracts/src/socket.js +74 -0
- package/dist/packages/contracts/src/team.d.ts +13 -0
- package/dist/packages/contracts/src/team.js +1 -0
- package/package.json +14 -25
- package/README.md +0 -158
- package/dist/adapters/adapter.js +0 -9
- package/dist/adapters/claude-code.js +0 -83
- package/dist/adapters/codex.js +0 -280
- package/dist/adapters/factory.js +0 -38
- package/dist/adapters/hermes.js +0 -178
- package/dist/adapters/openclaw.js +0 -129
- package/dist/agent-instance.js +0 -181
- package/dist/auth-store.js +0 -44
- package/dist/bin.js +0 -6
- package/dist/config.js +0 -148
- package/dist/connection.js +0 -211
- package/dist/device-daemon.js +0 -530
- package/dist/index.js +0 -368
- package/dist/log.js +0 -7
- package/dist/post-process.js +0 -177
- package/dist/profile-paths.js +0 -39
- package/dist/sandbox.js +0 -53
- package/dist/scanner.js +0 -423
- package/dist/uploader.js +0 -46
- package/dist/workspace-manager.js +0 -196
- package/dist/workspace-sync.js +0 -69
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const ERROR_CODES = [
|
|
2
|
+
'BAD_REQUEST',
|
|
3
|
+
'UNAUTHENTICATED',
|
|
4
|
+
'FORBIDDEN',
|
|
5
|
+
'NOT_FOUND',
|
|
6
|
+
'CONFLICT',
|
|
7
|
+
'VALIDATION_ERROR',
|
|
8
|
+
'DEVICE_OFFLINE',
|
|
9
|
+
'AGENT_OFFLINE',
|
|
10
|
+
'DISPATCH_TIMEOUT',
|
|
11
|
+
'INTERNAL_ERROR',
|
|
12
|
+
];
|
|
13
|
+
const ERROR_CODE_SET = new Set(ERROR_CODES);
|
|
14
|
+
export function isErrorCode(value) {
|
|
15
|
+
return ERROR_CODE_SET.has(value);
|
|
16
|
+
}
|
|
17
|
+
export function makeSuccess(payload) {
|
|
18
|
+
return { ok: true, ...(payload ?? {}) };
|
|
19
|
+
}
|
|
20
|
+
export function makeFailure(error, message, details) {
|
|
21
|
+
return {
|
|
22
|
+
ok: false,
|
|
23
|
+
error,
|
|
24
|
+
...(message === undefined ? {} : { message }),
|
|
25
|
+
...(details === undefined ? {} : { details }),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ID, UnixMs } from './common.js';
|
|
2
|
+
import type { AgentDto, RuntimeDto } from './agent.js';
|
|
3
|
+
export type DeviceStatus = 'online' | 'offline' | 'unknown';
|
|
4
|
+
export interface DeviceSystemInfoDto {
|
|
5
|
+
hostname?: string;
|
|
6
|
+
platform?: string;
|
|
7
|
+
arch?: string;
|
|
8
|
+
release?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DeviceCapabilitiesDto {
|
|
11
|
+
scanAgents?: boolean;
|
|
12
|
+
runDispatches?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface DeviceDto {
|
|
15
|
+
id: ID;
|
|
16
|
+
teamId: ID;
|
|
17
|
+
ownerId: ID;
|
|
18
|
+
status: DeviceStatus;
|
|
19
|
+
name?: string;
|
|
20
|
+
systemInfo?: DeviceSystemInfoDto;
|
|
21
|
+
capabilities?: DeviceCapabilitiesDto;
|
|
22
|
+
lastSeenAt?: UnixMs;
|
|
23
|
+
}
|
|
24
|
+
export interface DeviceDetailDto extends DeviceDto {
|
|
25
|
+
runtimes: RuntimeDto[];
|
|
26
|
+
agents: AgentDto[];
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ID, UnixMs } from './common.js';
|
|
2
|
+
import type { AdapterKind } from './agent.js';
|
|
3
|
+
export type DispatchStatus = 'queued' | 'sent' | 'accepted' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'timed_out';
|
|
4
|
+
export interface DispatchAttachmentDto {
|
|
5
|
+
id: ID;
|
|
6
|
+
name: string;
|
|
7
|
+
mimeType?: string;
|
|
8
|
+
sizeBytes?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DispatchCustomAgentDto {
|
|
11
|
+
id?: ID;
|
|
12
|
+
name?: string;
|
|
13
|
+
adapterKind: AdapterKind;
|
|
14
|
+
args?: string[];
|
|
15
|
+
command?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export interface DispatchRequestDto {
|
|
20
|
+
teamId: ID;
|
|
21
|
+
channelId: ID;
|
|
22
|
+
messageId: ID;
|
|
23
|
+
agentId: ID;
|
|
24
|
+
deviceId?: ID;
|
|
25
|
+
requestId: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
attachments?: DispatchAttachmentDto[];
|
|
28
|
+
customAgent?: DispatchCustomAgentDto;
|
|
29
|
+
}
|
|
30
|
+
export interface DispatchDto {
|
|
31
|
+
id: ID;
|
|
32
|
+
teamId: ID;
|
|
33
|
+
channelId: ID;
|
|
34
|
+
messageId: ID;
|
|
35
|
+
agentId: ID;
|
|
36
|
+
status: DispatchStatus;
|
|
37
|
+
requestId: string;
|
|
38
|
+
createdAt: UnixMs;
|
|
39
|
+
updatedAt: UnixMs;
|
|
40
|
+
acceptedAt?: UnixMs;
|
|
41
|
+
completedAt?: UnixMs;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface DispatchHistoryItemDto extends DispatchDto {
|
|
45
|
+
promptPreview?: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './common.js';
|
|
2
|
+
export * from './auth.js';
|
|
3
|
+
export * from './team.js';
|
|
4
|
+
export * from './agent.js';
|
|
5
|
+
export * from './device.js';
|
|
6
|
+
export * from './channel.js';
|
|
7
|
+
export * from './message.js';
|
|
8
|
+
export * from './dispatch.js';
|
|
9
|
+
export * from './socket.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './common.js';
|
|
2
|
+
export * from './auth.js';
|
|
3
|
+
export * from './team.js';
|
|
4
|
+
export * from './agent.js';
|
|
5
|
+
export * from './device.js';
|
|
6
|
+
export * from './channel.js';
|
|
7
|
+
export * from './message.js';
|
|
8
|
+
export * from './dispatch.js';
|
|
9
|
+
export * from './socket.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ID, UnixMs } from './common.js';
|
|
2
|
+
export type SenderKind = 'human' | 'agent' | 'system';
|
|
3
|
+
export type RouteReason = 'MENTION' | 'DIRECT' | 'CHANNEL_DEFAULT' | 'MANUAL';
|
|
4
|
+
export interface MessageMetaDto {
|
|
5
|
+
routeReason?: RouteReason;
|
|
6
|
+
mentionedName?: string;
|
|
7
|
+
attachments?: ID[];
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface MessageDto {
|
|
11
|
+
id: ID;
|
|
12
|
+
teamId: ID;
|
|
13
|
+
channelId: ID;
|
|
14
|
+
senderKind: SenderKind;
|
|
15
|
+
senderId: ID;
|
|
16
|
+
body: string;
|
|
17
|
+
createdAt: UnixMs;
|
|
18
|
+
updatedAt?: UnixMs;
|
|
19
|
+
meta?: MessageMetaDto;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export declare const WEB_EVENTS: {
|
|
2
|
+
readonly auth: {
|
|
3
|
+
readonly login: "auth:login";
|
|
4
|
+
readonly register: "auth:register";
|
|
5
|
+
readonly whoami: "auth:whoami";
|
|
6
|
+
};
|
|
7
|
+
readonly team: {
|
|
8
|
+
readonly list: "team:list";
|
|
9
|
+
readonly create: "team:create";
|
|
10
|
+
readonly switch: "team:switch";
|
|
11
|
+
readonly snapshot: "teams:snapshot";
|
|
12
|
+
readonly update: "team:update";
|
|
13
|
+
readonly delete: "team:delete";
|
|
14
|
+
};
|
|
15
|
+
readonly member: {
|
|
16
|
+
readonly list: "members:list";
|
|
17
|
+
readonly updateHuman: "member:update-human";
|
|
18
|
+
};
|
|
19
|
+
readonly device: {
|
|
20
|
+
readonly list: "device:list";
|
|
21
|
+
readonly get: "device:get";
|
|
22
|
+
readonly scan: "device:scan";
|
|
23
|
+
readonly snapshot: "devices:snapshot";
|
|
24
|
+
readonly status: "device:status";
|
|
25
|
+
readonly runtimes: "device:runtimes";
|
|
26
|
+
};
|
|
27
|
+
readonly agent: {
|
|
28
|
+
readonly subscribe: "agents:subscribe";
|
|
29
|
+
readonly create: "agent:create";
|
|
30
|
+
readonly publish: "agent:publish";
|
|
31
|
+
readonly unpublish: "agent:unpublish";
|
|
32
|
+
readonly snapshot: "agents:snapshot";
|
|
33
|
+
readonly status: "agent:status";
|
|
34
|
+
readonly discovered: "agents:discovered";
|
|
35
|
+
readonly updateConfig: "agent:update-config";
|
|
36
|
+
readonly delete: "agent:delete";
|
|
37
|
+
readonly metrics: "agent:metrics";
|
|
38
|
+
};
|
|
39
|
+
readonly channel: {
|
|
40
|
+
readonly subscribe: "channels:subscribe";
|
|
41
|
+
readonly create: "channel:create";
|
|
42
|
+
readonly join: "channel:join";
|
|
43
|
+
readonly snapshot: "channels:snapshot";
|
|
44
|
+
readonly message: "channel:message";
|
|
45
|
+
readonly update: "channel:update";
|
|
46
|
+
readonly addMember: "channel:add-member";
|
|
47
|
+
readonly removeMember: "channel:remove-member";
|
|
48
|
+
readonly addAgent: "channel:add-agent";
|
|
49
|
+
readonly removeAgent: "channel:remove-agent";
|
|
50
|
+
readonly members: "channel:members";
|
|
51
|
+
};
|
|
52
|
+
readonly message: {
|
|
53
|
+
readonly send: "message:send";
|
|
54
|
+
readonly dispatchStatus: "message:dispatch-status";
|
|
55
|
+
readonly search: "message:search";
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export declare const AGENT_EVENTS: {
|
|
59
|
+
readonly device: {
|
|
60
|
+
readonly hello: "device:hello";
|
|
61
|
+
readonly runtimes: "device:runtimes";
|
|
62
|
+
readonly scanRequested: "device:scan-requested";
|
|
63
|
+
};
|
|
64
|
+
readonly agent: {
|
|
65
|
+
readonly registerBatch: "agent:register-batch";
|
|
66
|
+
};
|
|
67
|
+
readonly dispatch: {
|
|
68
|
+
readonly request: "dispatch:request";
|
|
69
|
+
readonly cancel: "dispatch:cancel";
|
|
70
|
+
readonly accepted: "dispatch:accepted";
|
|
71
|
+
readonly result: "dispatch:result";
|
|
72
|
+
readonly error: "dispatch:error";
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const WEB_EVENTS = {
|
|
2
|
+
auth: {
|
|
3
|
+
login: 'auth:login',
|
|
4
|
+
register: 'auth:register',
|
|
5
|
+
whoami: 'auth:whoami',
|
|
6
|
+
},
|
|
7
|
+
team: {
|
|
8
|
+
list: 'team:list',
|
|
9
|
+
create: 'team:create',
|
|
10
|
+
switch: 'team:switch',
|
|
11
|
+
snapshot: 'teams:snapshot',
|
|
12
|
+
update: 'team:update',
|
|
13
|
+
delete: 'team:delete',
|
|
14
|
+
},
|
|
15
|
+
member: {
|
|
16
|
+
list: 'members:list',
|
|
17
|
+
updateHuman: 'member:update-human',
|
|
18
|
+
},
|
|
19
|
+
device: {
|
|
20
|
+
list: 'device:list',
|
|
21
|
+
get: 'device:get',
|
|
22
|
+
scan: 'device:scan',
|
|
23
|
+
snapshot: 'devices:snapshot',
|
|
24
|
+
status: 'device:status',
|
|
25
|
+
runtimes: 'device:runtimes',
|
|
26
|
+
},
|
|
27
|
+
agent: {
|
|
28
|
+
subscribe: 'agents:subscribe',
|
|
29
|
+
create: 'agent:create',
|
|
30
|
+
publish: 'agent:publish',
|
|
31
|
+
unpublish: 'agent:unpublish',
|
|
32
|
+
snapshot: 'agents:snapshot',
|
|
33
|
+
status: 'agent:status',
|
|
34
|
+
discovered: 'agents:discovered',
|
|
35
|
+
updateConfig: 'agent:update-config',
|
|
36
|
+
delete: 'agent:delete',
|
|
37
|
+
metrics: 'agent:metrics',
|
|
38
|
+
},
|
|
39
|
+
channel: {
|
|
40
|
+
subscribe: 'channels:subscribe',
|
|
41
|
+
create: 'channel:create',
|
|
42
|
+
join: 'channel:join',
|
|
43
|
+
snapshot: 'channels:snapshot',
|
|
44
|
+
message: 'channel:message',
|
|
45
|
+
update: 'channel:update',
|
|
46
|
+
addMember: 'channel:add-member',
|
|
47
|
+
removeMember: 'channel:remove-member',
|
|
48
|
+
addAgent: 'channel:add-agent',
|
|
49
|
+
removeAgent: 'channel:remove-agent',
|
|
50
|
+
members: 'channel:members',
|
|
51
|
+
},
|
|
52
|
+
message: {
|
|
53
|
+
send: 'message:send',
|
|
54
|
+
dispatchStatus: 'message:dispatch-status',
|
|
55
|
+
search: 'message:search',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const AGENT_EVENTS = {
|
|
59
|
+
device: {
|
|
60
|
+
hello: 'device:hello',
|
|
61
|
+
runtimes: 'device:runtimes',
|
|
62
|
+
scanRequested: 'device:scan-requested',
|
|
63
|
+
},
|
|
64
|
+
agent: {
|
|
65
|
+
registerBatch: 'agent:register-batch',
|
|
66
|
+
},
|
|
67
|
+
dispatch: {
|
|
68
|
+
request: 'dispatch:request',
|
|
69
|
+
cancel: 'dispatch:cancel',
|
|
70
|
+
accepted: 'dispatch:accepted',
|
|
71
|
+
result: 'dispatch:result',
|
|
72
|
+
error: 'dispatch:error',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ID, UnixMs } from './common.js';
|
|
2
|
+
import type { TeamMemberRole } from './auth.js';
|
|
3
|
+
export type TeamVisibility = 'private' | 'public';
|
|
4
|
+
export interface TeamDto {
|
|
5
|
+
id: ID;
|
|
6
|
+
name: string;
|
|
7
|
+
path: string;
|
|
8
|
+
visibility: TeamVisibility;
|
|
9
|
+
ownerId: ID;
|
|
10
|
+
currentUserRole: TeamMemberRole;
|
|
11
|
+
createdAt: UnixMs;
|
|
12
|
+
updatedAt?: UnixMs;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,37 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentbean/daemon",
|
|
3
|
+
"version": "0.2.0",
|
|
3
4
|
"private": false,
|
|
4
|
-
"version": "0.1.35",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
6
|
+
"main": "./dist/apps/daemon-next/src/index.js",
|
|
7
|
+
"types": "./dist/apps/daemon-next/src/index.d.ts",
|
|
7
8
|
"bin": {
|
|
8
|
-
"daemon": "dist/bin.js",
|
|
9
|
-
"agentbean-daemon": "dist/bin.js"
|
|
9
|
+
"daemon": "./dist/apps/daemon-next/src/bin.js",
|
|
10
|
+
"agentbean-daemon": "./dist/apps/daemon-next/src/bin.js",
|
|
11
|
+
"agentbean-next-daemon": "./dist/apps/daemon-next/src/bin.js"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/apps/daemon-next/src/index.d.ts",
|
|
16
|
+
"import": "./dist/apps/daemon-next/src/index.js"
|
|
17
|
+
}
|
|
10
18
|
},
|
|
11
19
|
"files": [
|
|
12
20
|
"dist/**/*"
|
|
13
21
|
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"dev": "tsx watch src/bin.ts",
|
|
17
|
-
"start": "node dist/bin.js",
|
|
18
|
-
"test": "vitest run",
|
|
19
|
-
"test:watch": "vitest",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
21
|
-
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"pino": "^9.4.0",
|
|
26
|
-
"pino-pretty": "^11.2.2",
|
|
27
|
-
"socket.io-client": "^4.7.5",
|
|
28
|
-
"ulid": "^2.3.0"
|
|
29
|
-
},
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@types/js-yaml": "^4.0.9",
|
|
32
|
-
"@types/node": "^20.16.5",
|
|
33
|
-
"tsx": "^4.19.0",
|
|
34
|
-
"typescript": "^5.6.2",
|
|
35
|
-
"vitest": "^2.0.5"
|
|
23
|
+
"@agentbean/contracts": "0.2.0",
|
|
24
|
+
"socket.io-client": "^4.8.3"
|
|
36
25
|
}
|
|
37
26
|
}
|
package/README.md
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# AgentBean Device Daemon
|
|
2
|
-
|
|
3
|
-
Agent 适配器层 —— 运行在用户机器上,将真实 Coding Agent 接入 AgentBean Server。
|
|
4
|
-
|
|
5
|
-
## 启动
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install
|
|
9
|
-
npm run dev # 开发模式(tsx watch)
|
|
10
|
-
npm start # 运行(tsx)
|
|
11
|
-
npm test # 运行测试
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
### 带配置文件启动
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npx tsx src/bin.ts ~/.agentbean/device-agent.yaml
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### 自动扫描模式
|
|
21
|
-
|
|
22
|
-
如果不提供配置文件,或配置文件中 `agents` 数组为空,Daemon 会自动扫描本机 Agent:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npx tsx src/bin.ts
|
|
26
|
-
# 扫描 Coding Agent (which claude-code, codex, kimi...)
|
|
27
|
-
# 扫描 AgentOS Gateway (localhost:PORT)
|
|
28
|
-
# 扫描 ~/.agentbean/agents/ 目录
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## 配置文件格式
|
|
32
|
-
|
|
33
|
-
`device-agent.yaml`:
|
|
34
|
-
|
|
35
|
-
```yaml
|
|
36
|
-
deviceId: my-macbook-pro # 设备标识
|
|
37
|
-
networkId: default # 所属团队
|
|
38
|
-
server:
|
|
39
|
-
url: http://localhost:3000/agent # Server Socket.IO 地址
|
|
40
|
-
token: default:default:dev-token-change-me # 三截 token
|
|
41
|
-
heartbeatIntervalMs: 10000 # 心跳间隔(默认 10s)
|
|
42
|
-
|
|
43
|
-
agents: # Agent 配置列表
|
|
44
|
-
- id: claude-shaw
|
|
45
|
-
name: Claude
|
|
46
|
-
role: 高级编程助手
|
|
47
|
-
category: coding # coding | executor-hosted | agentos-hosted | standalone-cli
|
|
48
|
-
visibility: public # public | private
|
|
49
|
-
adapter:
|
|
50
|
-
kind: claude-code # claude-code | codex | openclaw | hermes | standalone
|
|
51
|
-
command: claude # 可执行命令
|
|
52
|
-
args: [] # 命令参数
|
|
53
|
-
cwd: ~/projects # 工作目录(可选)
|
|
54
|
-
systemPrompt: | # 系统提示词(可选)
|
|
55
|
-
You are a helpful coding assistant.
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
支持环境变量插值:`${SERVER_URL}`
|
|
59
|
-
|
|
60
|
-
## 核心模块
|
|
61
|
-
|
|
62
|
-
### 设备守护进程 (`device-daemon.ts`)
|
|
63
|
-
|
|
64
|
-
`DeviceDaemon` 类:
|
|
65
|
-
- 维护与 Server 的 Socket.IO 连接
|
|
66
|
-
- 管理多个 `AgentInstance`
|
|
67
|
-
- 定期发送心跳
|
|
68
|
-
- 处理 Server 下发的 `dispatch` 任务
|
|
69
|
-
|
|
70
|
-
### Agent 实例 (`agent-instance.ts`)
|
|
71
|
-
|
|
72
|
-
`AgentInstance` 封装单个 Agent:
|
|
73
|
-
- 持有 `CliAdapter`(适配器实例)
|
|
74
|
-
- 管理 Agent 生命周期(启动、运行、停止)
|
|
75
|
-
- 将 Server 的 `dispatch` 转换为适配器输入
|
|
76
|
-
- 将适配器输出包装为 `reply` 发送回 Server
|
|
77
|
-
|
|
78
|
-
### 扫描器 (`scanner.ts`)
|
|
79
|
-
|
|
80
|
-
三类自动发现:
|
|
81
|
-
|
|
82
|
-
**`scanCodingAgents()`** — 通过 `which` 发现本机 Coding Agent:
|
|
83
|
-
```typescript
|
|
84
|
-
const CODING_BINARIES = ['claude-code', 'codex', 'kimi'];
|
|
85
|
-
// 对每个执行 which,存在的加入结果
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**`scanAgentOSAgents()`** — 扫描 OpenClaw / Hermes gateway:
|
|
89
|
-
- 尝试连接 `http://localhost:PORT/openclaw/agents`
|
|
90
|
-
- 如果 gateway 未运行,返回空数组
|
|
91
|
-
|
|
92
|
-
**`scanLocalAgents(scanDir)`** — 扫描约定目录:
|
|
93
|
-
- 扫描 `~/.agentbean/agents/` 或指定目录
|
|
94
|
-
- 查找 `agent.json` / `agent.yaml` 配置文件
|
|
95
|
-
- 识别执行器承载型(有 `executor` 字段)和独立 CLI Agent
|
|
96
|
-
|
|
97
|
-
### 连接管理 (`connection.ts`)
|
|
98
|
-
|
|
99
|
-
`createConnection()`:
|
|
100
|
-
- 建立 Socket.IO 连接到 Server `/agent` 命名空间
|
|
101
|
-
- 发送 `register` 事件进行认证
|
|
102
|
-
- 自动重连和心跳
|
|
103
|
-
- 处理 `dispatch` 事件并路由到对应 Agent
|
|
104
|
-
|
|
105
|
-
### CLI 适配器 (`adapters/`)
|
|
106
|
-
|
|
107
|
-
| 适配器 | 说明 | 命令 |
|
|
108
|
-
|--------|------|------|
|
|
109
|
-
| `ClaudeCodeAdapter` | Anthropic Claude Code | `claude` |
|
|
110
|
-
| `CodexAdapter` | OpenAI Codex CLI | `codex` |
|
|
111
|
-
| `OpenClawAdapter` | OpenClaw gateway | `openclaw` |
|
|
112
|
-
| `HermesAdapter` | Hermes gateway | `hermes` |
|
|
113
|
-
|
|
114
|
-
所有适配器实现 `CliAdapter` 接口:
|
|
115
|
-
```typescript
|
|
116
|
-
interface CliAdapter {
|
|
117
|
-
start(): void;
|
|
118
|
-
stop(): void;
|
|
119
|
-
send(input: string): void;
|
|
120
|
-
onOutput(handler: (text: string) => void): void;
|
|
121
|
-
onExit(handler: (code: number | null) => void): void;
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
底层使用 `node-pty` 创建伪终端与 CLI 工具交互,支持实时流式输出。
|
|
126
|
-
|
|
127
|
-
### 配置解析 (`config.ts`)
|
|
128
|
-
|
|
129
|
-
- `loadConfig()` — 解析单 Agent 配置
|
|
130
|
-
- `loadDeviceConfig()` — 解析设备级多 Agent 配置
|
|
131
|
-
- `AgentCategory` — 四类 Agent 分类
|
|
132
|
-
- `AdapterKind` — 五种适配器类型
|
|
133
|
-
- 支持 YAML 环境变量插值
|
|
134
|
-
|
|
135
|
-
## 启动流程
|
|
136
|
-
|
|
137
|
-
```
|
|
138
|
-
1. 解析命令行参数(配置文件路径)
|
|
139
|
-
2. 尝试 loadDeviceConfig() — 静态配置优先
|
|
140
|
-
3. 如果无静态配置或 agents 数组为空:
|
|
141
|
-
a. scanCodingAgents() — 扫描本机 CLI 工具
|
|
142
|
-
b. scanAgentOSAgents() — 扫描 gateway
|
|
143
|
-
c. scanLocalAgents() — 扫描约定目录
|
|
144
|
-
d. 合并去重,生成 AgentConfigEntry 列表
|
|
145
|
-
4. 对每个 entry 创建 AgentInstance + 适配器
|
|
146
|
-
5. 创建 DeviceDaemon,连接 Server
|
|
147
|
-
6. 发送 register + 定期 heartbeat
|
|
148
|
-
7. 等待 dispatch 任务
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## 环境变量
|
|
152
|
-
|
|
153
|
-
| 变量 | 说明 |
|
|
154
|
-
|------|------|
|
|
155
|
-
| `SERVER_URL` | Server WebSocket 地址 |
|
|
156
|
-
| `SERVER_TOKEN` | 接入令牌 |
|
|
157
|
-
| `DEVICE_ID` | 设备标识 |
|
|
158
|
-
| `NETWORK_ID` | 所属团队 |
|
package/dist/adapters/adapter.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
function buildPrompt(input, systemPrompt) {
|
|
5
|
-
const parts = [];
|
|
6
|
-
if (systemPrompt)
|
|
7
|
-
parts.push(systemPrompt);
|
|
8
|
-
for (const h of input.history.slice(-10)) {
|
|
9
|
-
parts.push(`${h.speaker} (${h.role}): ${h.body}`);
|
|
10
|
-
}
|
|
11
|
-
parts.push(input.prompt);
|
|
12
|
-
return parts.join('\n\n---\n\n');
|
|
13
|
-
}
|
|
14
|
-
function normalizeClaudeArgs(args) {
|
|
15
|
-
return ['-p', ...(args ?? [])];
|
|
16
|
-
}
|
|
17
|
-
export class ClaudeCodeAdapter {
|
|
18
|
-
opts;
|
|
19
|
-
kind = 'claude-code';
|
|
20
|
-
constructor(opts) {
|
|
21
|
-
this.opts = opts;
|
|
22
|
-
}
|
|
23
|
-
async ask(input, signal) {
|
|
24
|
-
return new Promise((resolve, reject) => {
|
|
25
|
-
const prompt = buildPrompt(input, this.opts.systemPrompt ?? input.systemPrompt);
|
|
26
|
-
const cwd = input.workspace ?? this.opts.cwd ?? process.cwd();
|
|
27
|
-
const baseArgs = normalizeClaudeArgs(this.opts.args);
|
|
28
|
-
if (input.workspace)
|
|
29
|
-
baseArgs.push('--add-dir', input.workspace);
|
|
30
|
-
baseArgs.push('--add-dir', join(homedir(), '.codex', 'generated_images'));
|
|
31
|
-
const command = input.sandboxProfilePath ? 'sandbox-exec' : this.opts.command;
|
|
32
|
-
const args = input.sandboxProfilePath
|
|
33
|
-
? ['-f', input.sandboxProfilePath, '--', this.opts.command, ...baseArgs]
|
|
34
|
-
: baseArgs;
|
|
35
|
-
const child = spawn(command, args, {
|
|
36
|
-
cwd,
|
|
37
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
-
env: { ...process.env, ...(input.env ?? {}) },
|
|
39
|
-
});
|
|
40
|
-
const stdoutChunks = [];
|
|
41
|
-
const stderrChunks = [];
|
|
42
|
-
const MAX_EXEC_MS = 600_000;
|
|
43
|
-
const onAbort = () => {
|
|
44
|
-
child.kill('SIGTERM');
|
|
45
|
-
setTimeout(() => child.kill('SIGKILL'), 2_000).unref();
|
|
46
|
-
};
|
|
47
|
-
signal.addEventListener('abort', onAbort);
|
|
48
|
-
const maxTimer = setTimeout(() => {
|
|
49
|
-
child.kill('SIGKILL');
|
|
50
|
-
signal.removeEventListener('abort', onAbort);
|
|
51
|
-
reject(new Error('claude-code adapter timeout'));
|
|
52
|
-
}, MAX_EXEC_MS).unref();
|
|
53
|
-
child.stdout.on('data', (b) => stdoutChunks.push(b));
|
|
54
|
-
child.stderr.on('data', (b) => stderrChunks.push(b));
|
|
55
|
-
child.on('error', (err) => {
|
|
56
|
-
clearTimeout(maxTimer);
|
|
57
|
-
signal.removeEventListener('abort', onAbort);
|
|
58
|
-
reject(err);
|
|
59
|
-
});
|
|
60
|
-
child.on('exit', (code) => {
|
|
61
|
-
clearTimeout(maxTimer);
|
|
62
|
-
signal.removeEventListener('abort', onAbort);
|
|
63
|
-
if (signal.aborted)
|
|
64
|
-
return reject(new Error('aborted'));
|
|
65
|
-
const out = Buffer.concat(stdoutChunks).toString('utf8').trim();
|
|
66
|
-
const err = Buffer.concat(stderrChunks).toString('utf8').trim();
|
|
67
|
-
if (code !== 0 && out.length === 0) {
|
|
68
|
-
return reject(new Error(`claude-code exit ${code}: ${err.slice(0, 200)}`));
|
|
69
|
-
}
|
|
70
|
-
resolve(out);
|
|
71
|
-
});
|
|
72
|
-
child.stdin.write(prompt);
|
|
73
|
-
child.stdin.end();
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
async health() {
|
|
77
|
-
return new Promise((resolve) => {
|
|
78
|
-
const child = spawn(this.opts.command, ['--version'], { stdio: 'ignore' });
|
|
79
|
-
child.on('error', (err) => resolve({ ok: false, detail: err.message }));
|
|
80
|
-
child.on('exit', (code) => resolve({ ok: code === 0, detail: code === 0 ? undefined : `exit ${code}` }));
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|