doer-agent 0.1.0 → 0.1.2
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/README.md +92 -97
- package/dist/agent.js +23 -4
- package/package.json +1 -1
- package/runtime/bin/apply_patch +1 -1
package/README.md
CHANGED
|
@@ -1,67 +1,38 @@
|
|
|
1
1
|
# doer-agent
|
|
2
2
|
|
|
3
|
-
`agent
|
|
3
|
+
`doer-agent`는 doer 서버에 연결되는 리버스 폴링 에이전트 런타임입니다.
|
|
4
|
+
로컬 머신이나 원격 워크스페이스에서 작업을 실행하고, 결과를 doer로 다시 전달합니다.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## 현재 구조
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
이 저장소 루트 자체가 `doer-agent` 프로젝트입니다.
|
|
9
|
+
예전 문서의 `agent/` 하위 디렉토리 기준 설명은 더 이상 맞지 않습니다.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
npm install
|
|
12
|
-
```
|
|
11
|
+
주요 엔트리 포인트:
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
- `doer-agent`: 에이전트 본체 CLI
|
|
14
|
+
- `playwright-mcp-call`: Playwright MCP 호출용 CLI
|
|
15
|
+
- `codex`: Codex 래퍼 CLI
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
## 요구 사항
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-d '{"name":"my-laptop"}'
|
|
23
|
-
```
|
|
19
|
+
- Node.js 20+
|
|
20
|
+
- doer 서버 접근 가능
|
|
21
|
+
- 발급된 `user-id`
|
|
22
|
+
- 발급된 `agent-secret`
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
기본 서버는 `https://doer.cranix.net`입니다.
|
|
25
|
+
다른 서버를 쓸 때만 `--server` 또는 `DOER_AGENT_SERVER`를 지정하면 됩니다.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
"agent": { "id": "agent_...", "name": "my-laptop" },
|
|
30
|
-
"agentSecret": "<SECRET>"
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
2. 에이전트 실행:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm run start -- --server http://localhost:2020 --user-id <userId> --agent-secret <SECRET>
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
환경변수 fallback도 지원합니다. CLI 인자가 있으면 그 값이 우선합니다.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
DOER_AGENT_SERVER=http://localhost:2020 DOER_AGENT_USER_ID=<userId> DOER_AGENT_SECRET=<SECRET> WORKSPACE=/absolute/path/to/workspace npm run start
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
3. 원격 Codex 실행 전송:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
curl -X POST 'http://localhost:2020/api/users/<userId>/agent/codex-tasks' \
|
|
51
|
-
-H 'Content-Type: application/json' \
|
|
52
|
-
--cookie 'doer_session=<session-cookie>' \
|
|
53
|
-
-d '{"agentId":"agent_...","prompt":"현재 작업 디렉토리와 파일 목록을 요약해줘"}'
|
|
54
|
-
```
|
|
27
|
+
## 빠른 실행
|
|
55
28
|
|
|
56
|
-
|
|
29
|
+
패키지 설치 없이 바로 실행하려면 `npx`를 사용합니다.
|
|
30
|
+
CLI는 `run start --` 없이 직접 옵션을 받습니다.
|
|
57
31
|
|
|
58
|
-
|
|
32
|
+
macOS / Linux:
|
|
59
33
|
|
|
60
34
|
```bash
|
|
61
|
-
|
|
62
|
-
-v "${PWD}:/workspace" \
|
|
63
|
-
cranix/doer-agent:latest \
|
|
64
|
-
--server http://<doer-host>:2020 \
|
|
35
|
+
WORKSPACE="${WORKSPACE:-$PWD}" npx -y doer-agent \
|
|
65
36
|
--user-id <userId> \
|
|
66
37
|
--agent-secret <SECRET>
|
|
67
38
|
```
|
|
@@ -69,85 +40,109 @@ docker run --rm -it \
|
|
|
69
40
|
PowerShell:
|
|
70
41
|
|
|
71
42
|
```powershell
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
cranix/doer-agent:latest `
|
|
75
|
-
--server http://<doer-host>:2020 `
|
|
43
|
+
$env:WORKSPACE = if ($env:WORKSPACE) { $env:WORKSPACE } else { (Get-Location).Path }
|
|
44
|
+
npx -y doer-agent `
|
|
76
45
|
--user-id <userId> `
|
|
77
46
|
--agent-secret <SECRET>
|
|
78
47
|
```
|
|
79
48
|
|
|
80
|
-
|
|
81
|
-
- 기본 실행은 Docker socket을 마운트하지 않습니다.
|
|
82
|
-
- 따라서 컨테이너 내부의 중첩 `docker` 사용은 기본 실행 범위에 포함되지 않습니다.
|
|
49
|
+
다른 서버를 붙일 때:
|
|
83
50
|
|
|
84
|
-
|
|
51
|
+
```bash
|
|
52
|
+
WORKSPACE="${WORKSPACE:-$PWD}" npx -y doer-agent \
|
|
53
|
+
--server http://localhost:2020 \
|
|
54
|
+
--user-id <userId> \
|
|
55
|
+
--agent-secret <SECRET>
|
|
56
|
+
```
|
|
85
57
|
|
|
86
|
-
|
|
58
|
+
## 로컬 개발
|
|
87
59
|
|
|
88
|
-
|
|
60
|
+
이 저장소를 직접 수정하거나 빌드하려면 루트에서 실행합니다.
|
|
61
|
+
|
|
62
|
+
설치:
|
|
89
63
|
|
|
90
64
|
```bash
|
|
91
|
-
|
|
92
|
-
./scripts/build.sh --tag v1.2.3 --also-latest
|
|
93
|
-
./scripts/build.sh --image docker.io/example/doer-agent --platform linux/amd64
|
|
65
|
+
npm install
|
|
94
66
|
```
|
|
95
67
|
|
|
96
|
-
|
|
68
|
+
개발 모드 실행:
|
|
97
69
|
|
|
98
70
|
```bash
|
|
99
|
-
|
|
100
|
-
./scripts/publish.sh --tag v1.2.3
|
|
101
|
-
./scripts/publish.sh --image docker.io/example/doer-agent --tag v1.2.3
|
|
71
|
+
npm run start -- --user-id <userId> --agent-secret <SECRET>
|
|
102
72
|
```
|
|
103
73
|
|
|
104
|
-
|
|
74
|
+
배포 산출물 빌드:
|
|
105
75
|
|
|
106
|
-
|
|
76
|
+
```bash
|
|
77
|
+
npm run build
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
빌드 산출물 실행:
|
|
107
81
|
|
|
108
82
|
```bash
|
|
109
|
-
|
|
110
|
-
-v "${PWD}:/workspace" \
|
|
111
|
-
ghcr.io/example/doer-agent:v1.2.3 \
|
|
112
|
-
--server http://<doer-host>:2020 \
|
|
113
|
-
--user-id <userId> \
|
|
114
|
-
--agent-secret <SECRET>
|
|
83
|
+
npm run start:dist -- --user-id <userId> --agent-secret <SECRET>
|
|
115
84
|
```
|
|
116
85
|
|
|
117
|
-
|
|
86
|
+
기본 서버가 아닌 경우에는 위 명령들에 `--server <url>`을 추가하면 됩니다.
|
|
118
87
|
|
|
119
|
-
|
|
88
|
+
## 환경변수
|
|
120
89
|
|
|
121
|
-
|
|
122
|
-
export DOER_AGENT_SERVER=http://<doer-host>:2020
|
|
123
|
-
export DOER_AGENT_USER_ID=<userId>
|
|
124
|
-
export DOER_AGENT_SECRET=<SECRET>
|
|
125
|
-
# 선택: 다른 작업 디렉토리를 마운트하려면 지정
|
|
126
|
-
# export DOER_AGENT_WORKSPACE=/absolute/path/to/workspace
|
|
90
|
+
CLI 인자가 우선이고, 없으면 아래 환경변수를 사용합니다.
|
|
127
91
|
|
|
128
|
-
|
|
92
|
+
- `DOER_AGENT_SERVER`: 선택. 기본값은 `https://doer.cranix.net`
|
|
93
|
+
- `DOER_AGENT_USER_ID`: 필수
|
|
94
|
+
- `DOER_AGENT_SECRET`: 필수
|
|
95
|
+
- `WORKSPACE`: 선택. 작업 디렉터리
|
|
96
|
+
- `DOER_AGENT_MAX_CONCURRENCY`: 선택. 동시 작업 수
|
|
97
|
+
|
|
98
|
+
예시:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
DOER_AGENT_USER_ID=<userId> \
|
|
102
|
+
DOER_AGENT_SECRET=<SECRET> \
|
|
103
|
+
WORKSPACE=/absolute/path/to/workspace \
|
|
104
|
+
npm run start
|
|
129
105
|
```
|
|
130
106
|
|
|
131
|
-
|
|
107
|
+
로컬 서버 예시:
|
|
132
108
|
|
|
133
109
|
```bash
|
|
134
|
-
|
|
135
|
-
|
|
110
|
+
DOER_AGENT_SERVER=http://localhost:2020 \
|
|
111
|
+
DOER_AGENT_USER_ID=<userId> \
|
|
112
|
+
DOER_AGENT_SECRET=<SECRET> \
|
|
113
|
+
WORKSPACE=/absolute/path/to/workspace \
|
|
114
|
+
npm run start
|
|
136
115
|
```
|
|
137
116
|
|
|
138
|
-
|
|
117
|
+
## 자주 쓰는 옵션
|
|
118
|
+
|
|
119
|
+
- `--server`: doer 서버 베이스 URL
|
|
120
|
+
- `--user-id`: doer 사용자 ID
|
|
121
|
+
- `--agent-secret`: doer가 발급한 에이전트 시크릿
|
|
122
|
+
- `--workspace-dir`: 실행 전에 이동할 작업 디렉터리
|
|
123
|
+
|
|
124
|
+
## 시크릿 발급 예시
|
|
125
|
+
|
|
126
|
+
서버가 로컬에서 돌고 있을 때 예시입니다.
|
|
139
127
|
|
|
140
128
|
```bash
|
|
141
|
-
|
|
129
|
+
curl -X POST 'http://localhost:2020/api/users/<userId>/agent/secret' \
|
|
130
|
+
-H 'Content-Type: application/json' \
|
|
131
|
+
--cookie 'doer_session=<session-cookie>' \
|
|
132
|
+
-d '{"name":"my-laptop"}'
|
|
142
133
|
```
|
|
143
134
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
응답 예시:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"agent": { "id": "agent_...", "name": "my-laptop" },
|
|
140
|
+
"agentSecret": "<SECRET>"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
147
143
|
|
|
148
|
-
##
|
|
144
|
+
## 참고
|
|
149
145
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
- PowerShell에서 `/var/run/docker.sock` 마운트 가능 여부는 Docker Desktop 설정과 실행 환경에 따라 다릅니다.
|
|
146
|
+
- `runtime/`에는 실행 보조 스크립트가 들어 있습니다.
|
|
147
|
+
- Playwright MCP 프록시는 에이전트 상태 디렉터리(`~/.doer-agent`) 아래 소켓을 사용합니다.
|
|
148
|
+
- 이 저장소에는 예전 README에 있던 `scripts/build.sh`, `scripts/publish.sh`, `docker-compose.dev.yml`이 없습니다. 현재 문서는 실제 파일 구조 기준으로 정리되어 있습니다.
|
package/dist/agent.js
CHANGED
|
@@ -9,8 +9,10 @@ import { AckPolicy, connect, DeliverPolicy, JSONCodec, RetentionPolicy, StorageT
|
|
|
9
9
|
const PLAYWRIGHT_SKIP_BROWSER_GC = "1";
|
|
10
10
|
const PLAYWRIGHT_MCP_DAEMON_IDLE_TTL_SECONDS_DEFAULT = 10800;
|
|
11
11
|
const PLAYWRIGHT_MCP_DAEMON_SIGNATURE_VERSION = "2026-03-15";
|
|
12
|
+
const DEFAULT_SERVER_BASE_URL = "https://doer.cranix.net";
|
|
12
13
|
const AGENT_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const AGENT_PROJECT_DIR = path.join(AGENT_MODULE_DIR, "..");
|
|
15
|
+
const AGENT_PACKAGE_JSON_PATH = path.join(AGENT_PROJECT_DIR, "package.json");
|
|
14
16
|
let activeTaskLogContext = null;
|
|
15
17
|
const activeTaskCancelRequests = new Map();
|
|
16
18
|
function sanitizeUserId(userId) {
|
|
@@ -370,6 +372,19 @@ function resolveAgentStateDir() {
|
|
|
370
372
|
function resolveContainerReachableServerBaseUrl(serverBaseUrl) {
|
|
371
373
|
return serverBaseUrl;
|
|
372
374
|
}
|
|
375
|
+
async function resolveAgentVersion() {
|
|
376
|
+
const raw = await readFile(AGENT_PACKAGE_JSON_PATH, "utf8").catch(() => "");
|
|
377
|
+
if (!raw) {
|
|
378
|
+
return "unknown";
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
const parsed = JSON.parse(raw);
|
|
382
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : "unknown";
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return "unknown";
|
|
386
|
+
}
|
|
387
|
+
}
|
|
373
388
|
function pickFirstNonEmpty(values) {
|
|
374
389
|
for (const value of values) {
|
|
375
390
|
if (typeof value !== "string") {
|
|
@@ -1148,9 +1163,10 @@ async function main() {
|
|
|
1148
1163
|
if (workspaceDir) {
|
|
1149
1164
|
process.chdir(path.resolve(workspaceDir));
|
|
1150
1165
|
}
|
|
1151
|
-
const serverBaseUrlRaw = resolveArgOrEnv(args, ["server", "url"], ["DOER_AGENT_SERVER"],
|
|
1166
|
+
const serverBaseUrlRaw = resolveArgOrEnv(args, ["server", "url"], ["DOER_AGENT_SERVER"], DEFAULT_SERVER_BASE_URL);
|
|
1152
1167
|
const requestedServerBaseUrl = serverBaseUrlRaw.replace(/\/$/, "");
|
|
1153
1168
|
const serverBaseUrl = resolveContainerReachableServerBaseUrl(requestedServerBaseUrl);
|
|
1169
|
+
const usesDefaultServer = requestedServerBaseUrl === DEFAULT_SERVER_BASE_URL;
|
|
1154
1170
|
const userId = resolveArgOrEnv(args, ["user-id", "userId"], ["DOER_AGENT_USER_ID"]);
|
|
1155
1171
|
const agentSecret = resolveArgOrEnv(args, ["agent-secret", "agentSecret"], ["DOER_AGENT_SECRET"]);
|
|
1156
1172
|
if (!userId || !agentSecret) {
|
|
@@ -1162,9 +1178,12 @@ async function main() {
|
|
|
1162
1178
|
userId,
|
|
1163
1179
|
agentToken,
|
|
1164
1180
|
});
|
|
1165
|
-
const maxConcurrency = Math.max(1, parseEnvInteger(process.env.DOER_AGENT_MAX_CONCURRENCY,
|
|
1166
|
-
|
|
1167
|
-
process.stdout.write(
|
|
1181
|
+
const maxConcurrency = Math.max(1, parseEnvInteger(process.env.DOER_AGENT_MAX_CONCURRENCY, 5));
|
|
1182
|
+
const agentVersion = await resolveAgentVersion();
|
|
1183
|
+
process.stdout.write(`\n[doer-agent v${agentVersion}]\n`);
|
|
1184
|
+
if (!usesDefaultServer) {
|
|
1185
|
+
process.stdout.write(`- server: ${serverBaseUrl}\n`);
|
|
1186
|
+
}
|
|
1168
1187
|
process.stdout.write(`- userId: ${userId}\n`);
|
|
1169
1188
|
process.stdout.write(`- agentId: ${typeof natsBootstrap.agentId === "string" ? natsBootstrap.agentId : "unknown"}\n`);
|
|
1170
1189
|
process.stdout.write(`\n- transport: nats\n`);
|
package/package.json
CHANGED
package/runtime/bin/apply_patch
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
4
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
5
|
-
exec node
|
|
5
|
+
exec node "$PROJECT_DIR/dist/apply-patch.js" "$@"
|