@engineeros/connector 0.8.8 → 0.8.9
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 +18 -6
- package/bin/engineeros-connector.mjs +168 -78
- package/package.json +2 -2
- package/src/acp-client.mjs +348 -84
- package/src/agent-registry.mjs +643 -0
- package/src/config.mjs +7 -0
- package/src/runner.mjs +57 -20
package/README.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# EngineerOS Connector
|
|
2
2
|
|
|
3
|
-
The connector uses an authenticated EngineerOS WebSocket for pairing, workspace identity, run lifecycle, and evidence. Coding work can run through any ACP v1-compatible
|
|
3
|
+
The connector uses an authenticated EngineerOS WebSocket for pairing, workspace identity, run lifecycle, and evidence. Coding work can run through any agent in the official ACP registry or a custom ACP v1-compatible command over stdio. Direct Codex CLI remains available as the compatibility path.
|
|
4
|
+
|
|
5
|
+
## Connect an official ACP agent
|
|
6
|
+
|
|
7
|
+
EngineerOS reads the curated [ACP agent registry](https://agentclientprotocol.com/registry), caches it for 24 hours, and uses the registry's pinned distribution for the current platform. The catalog includes Codex, Claude, Gemini, GitHub Copilot, Goose, OpenCode, Qwen Code, Cursor, and other compatible agents as they are published. List the current catalog, prepare the selected agent, then pair the workspace:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx --yes @engineeros/connector@latest agents
|
|
11
|
+
npx --yes @engineeros/connector@latest agent gemini install
|
|
12
|
+
npx --yes @engineeros/connector@latest pair PAIRING-CODE --url https://your-engineeros.example --workspace . --onboard --agent gemini
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Registry `npx` and `uvx` packages are prepared with their native package runner. Registry binaries are downloaded into `~/.engineeros/agents`, checked against the published SHA-256 digest when present, and launched from that managed location. The connector checks the selected distribution before pairing. Agent processes stay alive while the connector is active, project conversations reuse their ACP sessions, and response chunks reach Copilot as the agent produces them.
|
|
4
16
|
|
|
5
17
|
## Pair an ACP coding agent
|
|
6
18
|
|
|
@@ -10,7 +22,7 @@ Run the command from the repository the agent should work in:
|
|
|
10
22
|
npx --yes @engineeros/connector@latest pair PAIRING-CODE --url http://localhost:8000 --workspace . --onboard --agent-name "My ACP agent" --agent-command my-agent --agent-args '["--acp"]'
|
|
11
23
|
```
|
|
12
24
|
|
|
13
|
-
`--agent-command` must start an ACP v1 agent over NDJSON stdio. `--agent-args` is a JSON array so arguments are passed without invoking a shell. Omit
|
|
25
|
+
`--agent-command` must start an ACP v1 agent over NDJSON stdio. `--agent-args` is a JSON array so arguments are passed without invoking a shell. Omit `--agent` and the custom command options to use the installed Codex CLI directly.
|
|
14
26
|
|
|
15
27
|
Connect a local Codex CLI workspace to EngineerOS through an outbound WebSocket.
|
|
16
28
|
|
|
@@ -29,9 +41,9 @@ npx --yes @engineeros/connector@latest pair PAIRING-CODE --url https://your-engi
|
|
|
29
41
|
|
|
30
42
|
The connector uploads a bounded ZIP snapshot for a safe file inventory, then stays online for deep assessments, rescans, and Goal Runs. Inventory never executes repository code. It excludes known secrets, dependency directories, build output, compiled binaries, files larger than 5 MB, agent-tool caches, Git metadata, and connector state before upload.
|
|
31
43
|
|
|
32
|
-
From **Project steering -> Workspace**,
|
|
44
|
+
From **Project steering -> Workspace**, run the workspace assessment to use the connected agent subscription already authenticated on that computer. EngineerOS stores the source-cited findings in System State and promotes the highest-return corrective action in Steering. Assessment cannot modify the workspace.
|
|
33
45
|
|
|
34
|
-
After onboarding, every project prompt is routed to this connection. Copilot, shaping, planning, architecture, and experience generation use the
|
|
46
|
+
After onboarding, every project prompt is routed to this connection. Copilot, shaping, planning, architecture, and experience generation use the connected agent subscription and workspace context. Interactive prompts run independently from assessments and Goal scheduling. Prompt runs are read-only; only an explicitly registered Goal Run receives workspace-write access. If the connector is offline, EngineerOS asks the user to reconnect instead of silently switching models.
|
|
35
47
|
|
|
36
48
|
Project prompts use resumable, purpose-specific coding-agent sessions. The connector keeps the external session identifiers in its local configuration, so Copilot and artifact conversations survive connector restarts. Changing the purpose, model, or reasoning effort starts a separate session. Goal implementation and verification remain isolated runs.
|
|
37
49
|
|
|
@@ -48,11 +60,11 @@ If a new pairing command is accidentally run from the same folder against the sa
|
|
|
48
60
|
|
|
49
61
|
## Run Goals
|
|
50
62
|
|
|
51
|
-
Keep the connector online to receive Goals assigned from EngineerOS. Each Goal runs in an isolated worktree below `~/.engineeros/runs`. Cancellation stops
|
|
63
|
+
Keep the connector online to receive Goals assigned from EngineerOS. Each Goal runs in an isolated worktree below `~/.engineeros/runs`. Cancellation stops the agent. The connector returns changed paths, a bounded diff, and the exact repository ZIP; a human still performs independent attestation.
|
|
52
64
|
|
|
53
65
|
During the writable implementation phase, Codex receives an authenticated EngineerOS MCP server automatically. It can list, read, create, update, reclassify, soft-delete, and materialize project artifacts into canonical Product records through the same repository boundary used by Copilot. The backend accepts those calls only while the assigned Goal is running. Read-only project prompts and the independent verification phase do not receive mutation tools.
|
|
54
66
|
|
|
55
|
-
Requirements: Node.js 22 or newer, Git, and an authenticated
|
|
67
|
+
Requirements: Node.js 22 or newer, Git, and an authenticated agent. `npx` distributions use npm, `uvx` distributions require uv, and binary archives require `tar` (`unzip` for ZIP files on Linux). Registry agents report their own authentication prerequisites when they start.
|
|
56
68
|
|
|
57
69
|
## Execution profiles
|
|
58
70
|
|
|
@@ -18,11 +18,17 @@ import {
|
|
|
18
18
|
executeConnectedPrompt,
|
|
19
19
|
executeWorkspaceAssessment,
|
|
20
20
|
inspectCodingAgent,
|
|
21
|
-
|
|
22
|
-
promptQueueMessage,
|
|
21
|
+
promptStreamEvent,
|
|
23
22
|
stopProcess,
|
|
24
23
|
workspaceSnapshot,
|
|
25
24
|
} from "../src/runner.mjs";
|
|
25
|
+
import { disposeAcpRuntimes } from "../src/acp-client.mjs";
|
|
26
|
+
import {
|
|
27
|
+
installRegisteredAgent,
|
|
28
|
+
inspectRegisteredAgent,
|
|
29
|
+
registeredAgentConfig,
|
|
30
|
+
registeredAgents,
|
|
31
|
+
} from "../src/agent-registry.mjs";
|
|
26
32
|
import {
|
|
27
33
|
describeWebSocketError,
|
|
28
34
|
startConnectionWatchdog,
|
|
@@ -32,13 +38,60 @@ import { parseConnectorArgs } from "../src/cli-args.mjs";
|
|
|
32
38
|
import { runMcpServer } from "../src/mcp-server.mjs";
|
|
33
39
|
import packageJson from "../package.json" with { type: "json" };
|
|
34
40
|
|
|
35
|
-
const { command, positional, flags } = parseConnectorArgs(
|
|
41
|
+
const { command, positional, flags } = parseConnectorArgs(
|
|
42
|
+
process.argv.slice(2),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (command === "agents") {
|
|
46
|
+
let agents;
|
|
47
|
+
try {
|
|
48
|
+
agents = await registeredAgents();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
51
|
+
}
|
|
52
|
+
const statuses = await Promise.all(
|
|
53
|
+
agents.map(async (agent) => {
|
|
54
|
+
try {
|
|
55
|
+
await inspectRegisteredAgent(agent.id);
|
|
56
|
+
return `${agent.id}: ready (${agent.name} ${agent.version}, ${agent.distribution_type})`;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
59
|
+
return `${agent.id}: setup needed (${agent.name} ${agent.version}, ${agent.distribution_type}) - ${detail}`;
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
for (const status of statuses) {
|
|
64
|
+
console.log(status);
|
|
65
|
+
}
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (command === "agent") {
|
|
70
|
+
const agentId = positional[0];
|
|
71
|
+
const action = positional[1] || "check";
|
|
72
|
+
if (!agentId || !["check", "install"].includes(action)) {
|
|
73
|
+
fail("Usage: engineeros-connector agent AGENT_ID [check|install]");
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const installed =
|
|
77
|
+
action === "install"
|
|
78
|
+
? await installRegisteredAgent(agentId)
|
|
79
|
+
: await inspectRegisteredAgent(agentId);
|
|
80
|
+
console.log(
|
|
81
|
+
`${installed.name} is ready (${installed.version}, ${installed.distribution}).`,
|
|
82
|
+
);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
85
|
+
}
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
36
88
|
|
|
37
89
|
if (command === "mcp") {
|
|
38
90
|
const config = await loadConfig(flags.workspace || process.cwd());
|
|
39
91
|
if (!config) fail("This workspace is not paired with EngineerOS.");
|
|
40
92
|
const runId = flags["run-id"] || positional[0];
|
|
41
|
-
if (!runId)
|
|
93
|
+
if (!runId)
|
|
94
|
+
fail("Usage: engineeros-connector mcp --run-id RUN_ID [--workspace PATH]");
|
|
42
95
|
await runMcpServer({
|
|
43
96
|
config,
|
|
44
97
|
runId,
|
|
@@ -66,10 +119,22 @@ if (command === "pair") {
|
|
|
66
119
|
const pairingCode = positional[0];
|
|
67
120
|
if (!pairingCode)
|
|
68
121
|
fail(
|
|
69
|
-
"Usage: engineeros-connector pair CODE --url URL [--name NAME] [--workspace PATH] [--skip-git-repo-check]",
|
|
122
|
+
"Usage: engineeros-connector pair CODE --url URL [--agent AGENT_ID] [--name NAME] [--workspace PATH] [--skip-git-repo-check]",
|
|
70
123
|
);
|
|
71
124
|
const url = flags.url;
|
|
72
125
|
if (!url) fail("Pairing requires --url with the EngineerOS backend address.");
|
|
126
|
+
if (flags.agent && flags["agent-command"]) {
|
|
127
|
+
fail("Use either --agent or --agent-command, not both.");
|
|
128
|
+
}
|
|
129
|
+
let agentConfig = {};
|
|
130
|
+
try {
|
|
131
|
+
if (flags.agent) {
|
|
132
|
+
await inspectRegisteredAgent(flags.agent);
|
|
133
|
+
agentConfig = await registeredAgentConfig(flags.agent);
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
137
|
+
}
|
|
73
138
|
config = {
|
|
74
139
|
server_url: socketUrl(url),
|
|
75
140
|
workspace: path.resolve(flags.workspace || process.cwd()),
|
|
@@ -79,6 +144,7 @@ if (command === "pair") {
|
|
|
79
144
|
agent_command: flags["agent-command"] || null,
|
|
80
145
|
agent_args: parseAgentArgs(flags["agent-args"]),
|
|
81
146
|
agent_name: flags["agent-name"] || null,
|
|
147
|
+
...agentConfig,
|
|
82
148
|
skip_git_repo_check: flags["skip-git-repo-check"] === true,
|
|
83
149
|
name:
|
|
84
150
|
flags.name ||
|
|
@@ -104,7 +170,9 @@ if (command === "pair") {
|
|
|
104
170
|
token: config.token,
|
|
105
171
|
};
|
|
106
172
|
} else {
|
|
107
|
-
fail(
|
|
173
|
+
fail(
|
|
174
|
+
"Use `engineeros-connector pair`, `start`, `status`, `agents`, `agent`, or `mcp`.",
|
|
175
|
+
);
|
|
108
176
|
}
|
|
109
177
|
|
|
110
178
|
let codingAgent;
|
|
@@ -113,7 +181,9 @@ try {
|
|
|
113
181
|
} catch (error) {
|
|
114
182
|
fail(error instanceof Error ? error.message : String(error));
|
|
115
183
|
}
|
|
116
|
-
console.log(
|
|
184
|
+
console.log(
|
|
185
|
+
`Using ${codingAgent.name} through ${codingAgent.protocol} (${codingAgent.version}).`,
|
|
186
|
+
);
|
|
117
187
|
const capabilities = advertisedCapabilities(config, codingAgent);
|
|
118
188
|
firstMessage.capabilities = capabilities;
|
|
119
189
|
|
|
@@ -121,7 +191,7 @@ let stopped = false;
|
|
|
121
191
|
let active = null;
|
|
122
192
|
const available = [];
|
|
123
193
|
const assessments = [];
|
|
124
|
-
const
|
|
194
|
+
const activePrompts = new Map();
|
|
125
195
|
let socket;
|
|
126
196
|
let pingTimer;
|
|
127
197
|
let reconnectTimer;
|
|
@@ -137,6 +207,8 @@ process.on("SIGINT", async () => {
|
|
|
137
207
|
clearTimeout(reconnectTimer);
|
|
138
208
|
clearInterval(pingTimer);
|
|
139
209
|
await stopProcess(active?.child);
|
|
210
|
+
await Promise.all([...activePrompts.values()].map(cancelPrompt));
|
|
211
|
+
await disposeAcpRuntimes();
|
|
140
212
|
socket?.close();
|
|
141
213
|
process.exit(0);
|
|
142
214
|
});
|
|
@@ -212,29 +284,12 @@ async function connect() {
|
|
|
212
284
|
return;
|
|
213
285
|
}
|
|
214
286
|
if (message.type === "prompt.execute") {
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
active?.runId !== message.prompt_id &&
|
|
218
|
-
!prompts.some((candidate) => candidate.prompt_id === message.prompt_id)
|
|
219
|
-
) {
|
|
220
|
-
prompts.push(message);
|
|
221
|
-
added = true;
|
|
222
|
-
}
|
|
223
|
-
if (added && active) {
|
|
224
|
-
sendPromptProgress(message.prompt_id, promptQueueMessage(active.kind));
|
|
225
|
-
}
|
|
226
|
-
pump();
|
|
287
|
+
if (!activePrompts.has(message.prompt_id)) void executePrompt(message);
|
|
227
288
|
return;
|
|
228
289
|
}
|
|
229
290
|
if (message.type === "prompt.cancel") {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
);
|
|
233
|
-
if (queued >= 0) prompts.splice(queued, 1);
|
|
234
|
-
if (active?.kind === "prompt" && active.runId === message.prompt_id) {
|
|
235
|
-
active.cancelled = true;
|
|
236
|
-
await stopProcess(active.child);
|
|
237
|
-
}
|
|
291
|
+
const promptState = activePrompts.get(message.prompt_id);
|
|
292
|
+
if (promptState) await cancelPrompt(promptState);
|
|
238
293
|
return;
|
|
239
294
|
}
|
|
240
295
|
if (message.type === "run.available") {
|
|
@@ -274,10 +329,8 @@ async function connect() {
|
|
|
274
329
|
socket.addEventListener("close", (event) => {
|
|
275
330
|
clearConnectionWatchdog?.();
|
|
276
331
|
clearInterval(pingTimer);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
void stopProcess(active.child);
|
|
280
|
-
}
|
|
332
|
+
for (const promptState of activePrompts.values())
|
|
333
|
+
void cancelPrompt(promptState);
|
|
281
334
|
if (event.code === 4001 && active?.kind === "assessment") {
|
|
282
335
|
void stopProcess(active.child);
|
|
283
336
|
}
|
|
@@ -301,7 +354,9 @@ async function connect() {
|
|
|
301
354
|
|
|
302
355
|
function scheduleReconnect() {
|
|
303
356
|
if (stopped || connectionRejected || reconnectTimer) return;
|
|
304
|
-
const detail = lastConnectionError
|
|
357
|
+
const detail = lastConnectionError
|
|
358
|
+
? ` Last error: ${lastConnectionError}`
|
|
359
|
+
: "";
|
|
305
360
|
console.error(
|
|
306
361
|
`Connection unavailable.${detail} Retrying in ${Math.round(reconnectDelay / 1_000)}s.`,
|
|
307
362
|
);
|
|
@@ -366,30 +421,21 @@ function startPings() {
|
|
|
366
421
|
}, 10_000);
|
|
367
422
|
}
|
|
368
423
|
|
|
369
|
-
function
|
|
370
|
-
if (!
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
);
|
|
424
|
+
function sendPromptEvent(promptId, event) {
|
|
425
|
+
if (!event || socket.readyState !== WebSocket.OPEN) return;
|
|
426
|
+
const payload = {
|
|
427
|
+
type: "prompt.event",
|
|
428
|
+
prompt_id: promptId,
|
|
429
|
+
kind: event.kind,
|
|
430
|
+
};
|
|
431
|
+
if (event.message) payload.message = String(event.message).slice(0, 500);
|
|
432
|
+
if (event.delta) payload.delta = String(event.delta).slice(0, 50_000);
|
|
433
|
+
if (event.status) payload.status = String(event.status).slice(0, 100);
|
|
434
|
+
socket.send(JSON.stringify(payload));
|
|
378
435
|
}
|
|
379
436
|
|
|
380
437
|
function pump() {
|
|
381
438
|
if (active || socket.readyState !== WebSocket.OPEN) return;
|
|
382
|
-
const prompt = prompts.shift();
|
|
383
|
-
if (prompt) {
|
|
384
|
-
active = {
|
|
385
|
-
kind: "prompt",
|
|
386
|
-
runId: prompt.prompt_id,
|
|
387
|
-
child: null,
|
|
388
|
-
cancelled: false,
|
|
389
|
-
};
|
|
390
|
-
void executePrompt(prompt);
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
439
|
const assessment = assessments.shift();
|
|
394
440
|
if (assessment) {
|
|
395
441
|
active = {
|
|
@@ -418,36 +464,56 @@ function pump() {
|
|
|
418
464
|
|
|
419
465
|
async function executePrompt(assignment) {
|
|
420
466
|
const promptId = assignment.prompt_id;
|
|
421
|
-
|
|
422
|
-
|
|
467
|
+
const promptState = {
|
|
468
|
+
runId: promptId,
|
|
469
|
+
child: null,
|
|
470
|
+
controller: null,
|
|
471
|
+
cancelled: false,
|
|
472
|
+
};
|
|
473
|
+
activePrompts.set(promptId, promptState);
|
|
474
|
+
let lastEvent;
|
|
475
|
+
const reportEvent = (event) => {
|
|
476
|
+
const serialized = event ? JSON.stringify(event) : null;
|
|
423
477
|
if (
|
|
424
|
-
!
|
|
425
|
-
|
|
478
|
+
!serialized ||
|
|
479
|
+
serialized === lastEvent ||
|
|
426
480
|
socket.readyState !== WebSocket.OPEN ||
|
|
427
|
-
|
|
481
|
+
activePrompts.get(promptId) !== promptState
|
|
428
482
|
) {
|
|
429
483
|
return;
|
|
430
484
|
}
|
|
431
|
-
|
|
432
|
-
|
|
485
|
+
lastEvent = serialized;
|
|
486
|
+
sendPromptEvent(promptId, event);
|
|
433
487
|
};
|
|
434
|
-
|
|
488
|
+
sendPromptEvent(promptId, {
|
|
489
|
+
kind: "status",
|
|
490
|
+
message: "Agent started this request",
|
|
491
|
+
});
|
|
435
492
|
console.log(
|
|
436
493
|
`Answering ${assignment.purpose || "project"} prompt with ${codingAgent.name}.`,
|
|
437
494
|
);
|
|
438
495
|
try {
|
|
439
496
|
const result = await executeConnectedPrompt(assignment, config, {
|
|
440
497
|
onProcess: (child) => {
|
|
441
|
-
if (
|
|
498
|
+
if (activePrompts.get(promptId) === promptState)
|
|
499
|
+
promptState.child = child;
|
|
442
500
|
},
|
|
443
|
-
|
|
501
|
+
onController: (controller) => {
|
|
502
|
+
if (activePrompts.get(promptId) === promptState) {
|
|
503
|
+
promptState.controller = controller;
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
onEvent: (event) => reportEvent(promptStreamEvent(event)),
|
|
444
507
|
});
|
|
445
508
|
config = {
|
|
446
509
|
...config,
|
|
447
|
-
sessions: {
|
|
510
|
+
sessions: {
|
|
511
|
+
...(config.sessions || {}),
|
|
512
|
+
[result.sessionKey]: result.sessionId,
|
|
513
|
+
},
|
|
448
514
|
};
|
|
449
515
|
await saveConfig(config);
|
|
450
|
-
if (
|
|
516
|
+
if (promptState.cancelled || socket.readyState !== WebSocket.OPEN) return;
|
|
451
517
|
socket.send(
|
|
452
518
|
JSON.stringify({
|
|
453
519
|
type: "prompt.completed",
|
|
@@ -458,7 +524,7 @@ async function executePrompt(assignment) {
|
|
|
458
524
|
}),
|
|
459
525
|
);
|
|
460
526
|
} catch (error) {
|
|
461
|
-
if (!
|
|
527
|
+
if (!promptState.cancelled && socket.readyState === WebSocket.OPEN) {
|
|
462
528
|
socket.send(
|
|
463
529
|
JSON.stringify({
|
|
464
530
|
type: "prompt.failed",
|
|
@@ -467,18 +533,29 @@ async function executePrompt(assignment) {
|
|
|
467
533
|
}),
|
|
468
534
|
);
|
|
469
535
|
}
|
|
470
|
-
if (!
|
|
536
|
+
if (!promptState.cancelled) {
|
|
471
537
|
console.error(error instanceof Error ? error.message : String(error));
|
|
472
538
|
}
|
|
473
539
|
} finally {
|
|
474
|
-
|
|
475
|
-
|
|
540
|
+
if (activePrompts.get(promptId) === promptState)
|
|
541
|
+
activePrompts.delete(promptId);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function cancelPrompt(promptState) {
|
|
546
|
+
promptState.cancelled = true;
|
|
547
|
+
if (typeof promptState.controller?.cancel === "function") {
|
|
548
|
+
await promptState.controller.cancel();
|
|
549
|
+
return;
|
|
476
550
|
}
|
|
551
|
+
await stopProcess(promptState.child);
|
|
477
552
|
}
|
|
478
553
|
|
|
479
554
|
async function executeAssessment(assignment) {
|
|
480
555
|
const assessmentId = assignment.assessment_id;
|
|
481
|
-
console.log(
|
|
556
|
+
console.log(
|
|
557
|
+
`Assessing workspace with ${codingAgent.name} (${assessmentId}).`,
|
|
558
|
+
);
|
|
482
559
|
let progress = 10;
|
|
483
560
|
const reportedMilestones = new Set();
|
|
484
561
|
const reportProgress = (message, { milestone = true } = {}) => {
|
|
@@ -530,7 +607,9 @@ async function executeAssessment(assignment) {
|
|
|
530
607
|
`EngineerOS rejected the assessment (${response.status}): ${await response.text()}`,
|
|
531
608
|
);
|
|
532
609
|
}
|
|
533
|
-
console.log(
|
|
610
|
+
console.log(
|
|
611
|
+
`${codingAgent.name} workspace assessment is current in EngineerOS.`,
|
|
612
|
+
);
|
|
534
613
|
} catch (error) {
|
|
535
614
|
if (socket.readyState === WebSocket.OPEN) {
|
|
536
615
|
socket.send(
|
|
@@ -561,7 +640,7 @@ async function execute(assignment) {
|
|
|
561
640
|
type: "run.progress",
|
|
562
641
|
run_id: runId,
|
|
563
642
|
progress_percent: progress,
|
|
564
|
-
message:
|
|
643
|
+
message: "Agent is working",
|
|
565
644
|
}),
|
|
566
645
|
);
|
|
567
646
|
}
|
|
@@ -573,7 +652,7 @@ async function execute(assignment) {
|
|
|
573
652
|
},
|
|
574
653
|
onEvent: (event) => {
|
|
575
654
|
const message =
|
|
576
|
-
event.message || event.item?.text || event.type ||
|
|
655
|
+
event.message || event.item?.text || event.type || "Agent is working";
|
|
577
656
|
if (socket.readyState === WebSocket.OPEN) {
|
|
578
657
|
socket.send(
|
|
579
658
|
JSON.stringify({
|
|
@@ -608,10 +687,16 @@ async function execute(assignment) {
|
|
|
608
687
|
result.head_revision,
|
|
609
688
|
);
|
|
610
689
|
if (integration.applied) {
|
|
611
|
-
console.log(
|
|
690
|
+
console.log(
|
|
691
|
+
`Run accepted and applied to the connected repository at ${integration.revision}.`,
|
|
692
|
+
);
|
|
612
693
|
} else {
|
|
613
|
-
console.warn(
|
|
614
|
-
|
|
694
|
+
console.warn(
|
|
695
|
+
`Run accepted but not applied locally. ${integration.reason}`,
|
|
696
|
+
);
|
|
697
|
+
console.warn(
|
|
698
|
+
`The verified run workspace remains at ${result.run_workspace}.`,
|
|
699
|
+
);
|
|
615
700
|
}
|
|
616
701
|
} catch (error) {
|
|
617
702
|
if (!active?.cancelled && socket.readyState === WebSocket.OPEN) {
|
|
@@ -636,12 +721,17 @@ function parseAgentArgs(value) {
|
|
|
636
721
|
if (!value) return [];
|
|
637
722
|
try {
|
|
638
723
|
const parsed = JSON.parse(value);
|
|
639
|
-
if (
|
|
724
|
+
if (
|
|
725
|
+
!Array.isArray(parsed) ||
|
|
726
|
+
parsed.some((item) => typeof item !== "string")
|
|
727
|
+
) {
|
|
640
728
|
throw new Error();
|
|
641
729
|
}
|
|
642
730
|
return parsed;
|
|
643
731
|
} catch {
|
|
644
|
-
fail(
|
|
732
|
+
fail(
|
|
733
|
+
'--agent-args must be a JSON array, for example: --agent-args "[\\"acp\\"]"',
|
|
734
|
+
);
|
|
645
735
|
}
|
|
646
736
|
}
|
|
647
737
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@engineeros/connector",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"start": "node ./bin/engineeros-connector.mjs",
|
|
18
18
|
"test": "node --test --test-concurrency=1",
|
|
19
|
-
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
|
|
19
|
+
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/acp-client.mjs && node --check ./src/agent-registry.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/config.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=22"
|