ghosty-acp 0.0.1 → 0.0.3
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 +50 -14
- package/bridge.mjs +35 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,15 +17,14 @@ Nothing to install — `npx` fetches it:
|
|
|
17
17
|
npx -y ghosty-acp wss://your-agent.example.com/acp
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## VS Code
|
|
21
21
|
|
|
22
|
-
`settings.json`:
|
|
22
|
+
Install the **ACP Client** extension, then in `settings.json`:
|
|
23
23
|
|
|
24
24
|
```jsonc
|
|
25
25
|
{
|
|
26
|
-
"
|
|
26
|
+
"acp.agents": {
|
|
27
27
|
"My agent": {
|
|
28
|
-
"type": "custom",
|
|
29
28
|
"command": "npx",
|
|
30
29
|
"args": ["-y", "ghosty-acp", "wss://your-agent.example.com/acp"],
|
|
31
30
|
"env": { "GHOSTY_ACP_TOKEN": "…" }
|
|
@@ -34,14 +33,15 @@ npx -y ghosty-acp wss://your-agent.example.com/acp
|
|
|
34
33
|
}
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
##
|
|
36
|
+
## Zed
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
`settings.json`:
|
|
40
39
|
|
|
41
40
|
```jsonc
|
|
42
41
|
{
|
|
43
|
-
"
|
|
42
|
+
"agent_servers": {
|
|
44
43
|
"My agent": {
|
|
44
|
+
"type": "custom",
|
|
45
45
|
"command": "npx",
|
|
46
46
|
"args": ["-y", "ghosty-acp", "wss://your-agent.example.com/acp"],
|
|
47
47
|
"env": { "GHOSTY_ACP_TOKEN": "…" }
|
|
@@ -52,6 +52,23 @@ Install the **ACP Client** extension, then in `settings.json`:
|
|
|
52
52
|
|
|
53
53
|
The token is optional — omit it if the other end doesn't require one.
|
|
54
54
|
|
|
55
|
+
## The working directory (`cwd`)
|
|
56
|
+
|
|
57
|
+
Your editor runs on your machine; the agent lives in a remote box, and they share no disk.
|
|
58
|
+
Every ACP client sends the path of the project **open on your machine** in `session/new` —
|
|
59
|
+
a path that does not exist over there. The agent rejects it (`Invalid params: invalid
|
|
60
|
+
directory path`) and editors show it as *"Failed to connect"*, which sends you off to check
|
|
61
|
+
the URL and the token, the two things that were fine.
|
|
62
|
+
|
|
63
|
+
So the bridge rewrites that one field: `cwd` becomes `/root`, or whatever you set in
|
|
64
|
+
`GHOSTY_ACP_CWD`. Nothing else in the message is touched.
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
"env": { "GHOSTY_ACP_TOKEN": "…", "GHOSTY_ACP_CWD": "/workspace" }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The agent edits **what is in the box**, not your working copy.
|
|
71
|
+
|
|
55
72
|
## ⚠️ Put the token in `env`, never in `args`
|
|
56
73
|
|
|
57
74
|
Any process on your machine can read another process's arguments with `ps`. The token is
|
|
@@ -67,6 +84,7 @@ else there breaks the editor.
|
|
|
67
84
|
| `rechazado (1006)` | Bad credential, or the server refused the connection |
|
|
68
85
|
| `la máquina estaba en reposo…` | The agent was asleep; connecting wakes it |
|
|
69
86
|
| `conexión cerrada (…)` | The other end closed; the bridge exits so the editor notices |
|
|
87
|
+
| `cwd … → …` | The project path was remapped to one the agent can see |
|
|
70
88
|
|
|
71
89
|
Requires Node 22+. MIT.
|
|
72
90
|
|
|
@@ -92,15 +110,14 @@ Nada que instalar — `npx` lo descarga:
|
|
|
92
110
|
npx -y ghosty-acp wss://tu-agente.example.com/acp
|
|
93
111
|
```
|
|
94
112
|
|
|
95
|
-
##
|
|
113
|
+
## VS Code
|
|
96
114
|
|
|
97
|
-
|
|
115
|
+
Instala la extensión **ACP Client** y en `settings.json`:
|
|
98
116
|
|
|
99
117
|
```jsonc
|
|
100
118
|
{
|
|
101
|
-
"
|
|
119
|
+
"acp.agents": {
|
|
102
120
|
"Mi agente": {
|
|
103
|
-
"type": "custom",
|
|
104
121
|
"command": "npx",
|
|
105
122
|
"args": ["-y", "ghosty-acp", "wss://tu-agente.example.com/acp"],
|
|
106
123
|
"env": { "GHOSTY_ACP_TOKEN": "…" }
|
|
@@ -109,14 +126,15 @@ En `settings.json`:
|
|
|
109
126
|
}
|
|
110
127
|
```
|
|
111
128
|
|
|
112
|
-
##
|
|
129
|
+
## Zed
|
|
113
130
|
|
|
114
|
-
|
|
131
|
+
En `settings.json`:
|
|
115
132
|
|
|
116
133
|
```jsonc
|
|
117
134
|
{
|
|
118
|
-
"
|
|
135
|
+
"agent_servers": {
|
|
119
136
|
"Mi agente": {
|
|
137
|
+
"type": "custom",
|
|
120
138
|
"command": "npx",
|
|
121
139
|
"args": ["-y", "ghosty-acp", "wss://tu-agente.example.com/acp"],
|
|
122
140
|
"env": { "GHOSTY_ACP_TOKEN": "…" }
|
|
@@ -127,6 +145,23 @@ Instala la extensión **ACP Client** y en `settings.json`:
|
|
|
127
145
|
|
|
128
146
|
El token es opcional: si el otro extremo no pide credencial, omítelo.
|
|
129
147
|
|
|
148
|
+
## El directorio de trabajo (`cwd`)
|
|
149
|
+
|
|
150
|
+
Tu editor corre en tu máquina y el agente vive en una caja remota: no comparten disco.
|
|
151
|
+
Todo cliente ACP manda en `session/new` la ruta del proyecto **abierto en tu máquina**, una
|
|
152
|
+
ruta que allá no existe. El agente la rechaza (`Invalid params: invalid directory path`) y
|
|
153
|
+
los editores lo pintan como *«Failed to connect»*, que manda a revisar la URL y el token
|
|
154
|
+
—justo lo que sí estaba bien—.
|
|
155
|
+
|
|
156
|
+
Por eso el puente reescribe ese único campo: `cwd` pasa a ser `/root`, o lo que pongas en
|
|
157
|
+
`GHOSTY_ACP_CWD`. Del resto del mensaje no toca nada.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
"env": { "GHOSTY_ACP_TOKEN": "…", "GHOSTY_ACP_CWD": "/workspace" }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
El agente edita **lo que hay en la caja**, no tu copia de trabajo.
|
|
164
|
+
|
|
130
165
|
## ⚠️ El token va en `env`, nunca en `args`
|
|
131
166
|
|
|
132
167
|
Los argumentos de un proceso los puede leer cualquier otro proceso de la máquina con un
|
|
@@ -142,5 +177,6 @@ flujo JSON-RPC, porque cualquier otra cosa ahí rompe al editor.
|
|
|
142
177
|
| `rechazado (1006)` | La credencial no vale, o el servidor no aceptó la conexión |
|
|
143
178
|
| `la máquina estaba en reposo…` | El agente estaba dormido; la conexión lo despierta |
|
|
144
179
|
| `conexión cerrada (…)` | El otro extremo cerró; el puente sale para que el editor se entere |
|
|
180
|
+
| `cwd … → …` | Se remapeó la ruta del proyecto a una que el agente sí ve |
|
|
145
181
|
|
|
146
182
|
Necesita Node 22 o superior. MIT.
|
package/bridge.mjs
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const url = process.argv[2];
|
|
19
19
|
const token = process.env.GHOSTY_ACP_TOKEN || "";
|
|
20
|
+
const cwdRemoto = process.env.GHOSTY_ACP_CWD || "/root";
|
|
20
21
|
|
|
21
22
|
if (!url) {
|
|
22
23
|
console.error("uso: node bridge.mjs <wss://…/acp> (token en GHOSTY_ACP_TOKEN)");
|
|
@@ -91,6 +92,35 @@ ws.onmessage = (e) => {
|
|
|
91
92
|
}
|
|
92
93
|
};
|
|
93
94
|
|
|
95
|
+
// ── `cwd`: LO ÚNICO que este puente traduce ──────────────────────────────────────
|
|
96
|
+
//
|
|
97
|
+
// Rompe a propósito el "no traduce nada" de arriba, y vale la pena decir por qué. El
|
|
98
|
+
// editor corre en tu máquina y el agente vive en una microVM: NO comparten disco. Todo
|
|
99
|
+
// cliente ACP manda en `session/new` el directorio del proyecto ABIERTO EN TU MÁQUINA
|
|
100
|
+
// (`/Users/tu/proyecto`), una ruta que dentro de la caja sencillamente no existe. goose
|
|
101
|
+
// la rechaza con `-32602 Invalid params: invalid directory path`, y los editores pintan
|
|
102
|
+
// eso como «Failed to connect» — un mensaje que manda a revisar la URL y el token, que
|
|
103
|
+
// son justamente lo que sí estaba bien. Se pierde la tarde.
|
|
104
|
+
//
|
|
105
|
+
// Se reescribe SÓLO el campo `cwd` y SÓLO en los dos métodos que lo llevan. El resto del
|
|
106
|
+
// mensaje pasa intacto; si no es JSON, o no es uno de esos métodos, no se toca nada.
|
|
107
|
+
const METODOS_CON_CWD = new Set(["session/new", "session/load"]);
|
|
108
|
+
|
|
109
|
+
const remapearCwd = (linea) => {
|
|
110
|
+
// Un mensaje que no parsea no es asunto nuestro: se mueve tal cual, como todo lo demás.
|
|
111
|
+
let msg;
|
|
112
|
+
try {
|
|
113
|
+
msg = JSON.parse(linea);
|
|
114
|
+
} catch {
|
|
115
|
+
return linea;
|
|
116
|
+
}
|
|
117
|
+
if (!METODOS_CON_CWD.has(msg?.method) || typeof msg?.params?.cwd !== "string") return linea;
|
|
118
|
+
if (msg.params.cwd === cwdRemoto) return linea;
|
|
119
|
+
diag(`cwd ${msg.params.cwd} → ${cwdRemoto} (el agente no ve tu disco)`);
|
|
120
|
+
msg.params.cwd = cwdRemoto;
|
|
121
|
+
return JSON.stringify(msg);
|
|
122
|
+
};
|
|
123
|
+
|
|
94
124
|
// ── editor → caja ────────────────────────────────────────────────────────────────
|
|
95
125
|
//
|
|
96
126
|
// Aquí SÍ hace falta acumular: stdin es un flujo y un mensaje puede llegar partido en
|
|
@@ -101,9 +131,12 @@ process.stdin.on("data", (chunk) => {
|
|
|
101
131
|
buffer += chunk;
|
|
102
132
|
let nl;
|
|
103
133
|
while ((nl = buffer.indexOf("\n")) !== -1) {
|
|
104
|
-
const
|
|
134
|
+
const cruda = buffer.slice(0, nl).trim();
|
|
105
135
|
buffer = buffer.slice(nl + 1);
|
|
106
|
-
if (!
|
|
136
|
+
if (!cruda) continue;
|
|
137
|
+
// Antes de la cola y antes del envío: así la línea que se guarda dormida ya va
|
|
138
|
+
// corregida y no hay dos caminos que mantener.
|
|
139
|
+
const linea = remapearCwd(cruda);
|
|
107
140
|
// Si el socket aún no está abierto la línea se descarta y se dice: mandarla al vacío
|
|
108
141
|
// en silencio dejaría al editor esperando para siempre una respuesta que nadie oyó.
|
|
109
142
|
if (ws.readyState === WebSocket.CONNECTING) {
|
package/package.json
CHANGED