@almyty/acp-server 1.0.6 → 1.0.7

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.
Files changed (2) hide show
  1. package/dist/index.js +48 -88
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -49,136 +49,96 @@ if (!agentArg) {
49
49
  process.exit(1);
50
50
  }
51
51
  // ── Main ─────────────────────────────────────────────────────────
52
- async function main() {
53
- // Resolve credentials
52
+ async function resolveAgent(ref, send) {
54
53
  const creds = resolveCredentials();
55
- if (!creds) {
56
- process.stderr.write('Error: No authentication token found.\n' +
57
- 'Set ALMYTY_TOKEN environment variable or run: npx @almyty/auth login\n');
58
- process.exit(1);
59
- }
54
+ if (!creds)
55
+ throw new Error('No auth token. Run: npx @almyty/auth login');
60
56
  const proxy = new AlmytyProxy(creds.url, creds.token);
61
- // Resolve agent. Accepts:
62
- // my-agent slug
63
- // acme/my-agent org/slug
64
- // My Agent display name
65
- // 550e8400-... UUID
66
- let agentId;
67
- const ref = agentArg;
68
- const hasSlash = ref.includes('/');
69
- const orgPrefix = hasSlash ? ref.split('/')[0] : undefined;
70
- const agentRef = hasSlash ? ref.split('/').slice(1).join('/') : ref;
71
- // 1. Try UUID direct lookup
72
- const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(agentRef);
73
- if (isUUID) {
74
- try {
75
- const agent = await proxy.getAgent(agentRef);
76
- agentId = agent.id;
77
- process.stderr.write(`[acp] ${agent.name} (${agent.mode})\n`);
78
- }
79
- catch {
80
- process.stderr.write(`Error: Agent not found: ${agentRef}\n`);
81
- process.exit(1);
82
- }
57
+ const agentRef = ref.includes('/') ? ref.split('/').slice(1).join('/') : ref;
58
+ // UUID
59
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(agentRef)) {
60
+ const a = await proxy.getAgent(agentRef);
61
+ process.stderr.write(`[acp] ${a.name} (${a.mode})\n`);
62
+ return new AlmytyAcpAgent(proxy, a.id, send);
83
63
  }
84
- // 2. List agents and match by slug, name, or slug-ified name
85
- if (!agentId) {
86
- try {
87
- const agents = await proxy.listAgents();
88
- const slugify = (s) => s.toLowerCase().replace(/\s+/g, '-');
89
- const needle = slugify(agentRef);
90
- const match = agents.find((a) => {
91
- const aSlug = a.slug || slugify(a.name);
92
- return aSlug === needle || a.name === agentRef || slugify(a.name) === needle || a.id === agentRef;
93
- });
94
- if (match) {
95
- agentId = match.id;
96
- process.stderr.write(`[acp] ${match.name} (${match.mode})\n`);
97
- }
98
- else {
99
- const available = agents.map((a) => a.slug || a.name.toLowerCase().replace(/\s+/g, '-')).join(', ');
100
- process.stderr.write(`Error: Agent "${ref}" not found.\nAvailable: ${available || '(none)'}\n`);
101
- process.exit(1);
102
- }
103
- }
104
- catch (err) {
105
- process.stderr.write(`Error: Could not resolve agent "${ref}": ${err}\n`);
106
- process.exit(1);
107
- }
64
+ // Name/slug lookup
65
+ const agents = await proxy.listAgents();
66
+ const slugify = (s) => s.toLowerCase().replace(/\s+/g, '-');
67
+ const needle = slugify(agentRef);
68
+ const match = agents.find((a) => {
69
+ const aSlug = a.slug || slugify(a.name);
70
+ return aSlug === needle || a.name === agentRef || slugify(a.name) === needle;
71
+ });
72
+ if (match) {
73
+ process.stderr.write(`[acp] ${match.name} (${match.mode})\n`);
74
+ return new AlmytyAcpAgent(proxy, match.id, send);
108
75
  }
109
- // Create the send function: write ndjson to stdout
76
+ const available = agents.map((a) => a.slug || slugify(a.name)).join(', ');
77
+ throw new Error(`Agent "${ref}" not found. Available: ${available || '(none)'}`);
78
+ }
79
+ async function main() {
80
+ // Send function: write ndjson to stdout
110
81
  const send = (msg) => {
111
82
  try {
112
- const line = JSON.stringify(msg);
113
- process.stdout.write(line + '\n');
83
+ process.stdout.write(JSON.stringify(msg) + '\n');
114
84
  }
115
85
  catch (err) {
116
- process.stderr.write(`[acp] Failed to serialize message: ${err}\n`);
86
+ process.stderr.write(`[acp] Failed to serialize: ${err}\n`);
117
87
  }
118
88
  };
119
- // Create the ACP agent handler
120
- const agent = new AlmytyAcpAgent(proxy, agentId, send);
121
- // Set up ndjson readline on stdin
122
- const rl = readline.createInterface({
123
- input: process.stdin,
124
- terminal: false,
125
- });
126
- process.stderr.write(`[acp] Server ready. Listening for JSON-RPC messages on stdin.\n`);
89
+ // Start listening on stdin IMMEDIATELY — Zed expects the process
90
+ // to be ready for JSON-RPC as soon as it spawns. Agent resolution
91
+ // and auth happen lazily on first `initialize` message.
92
+ let agent = null;
93
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
127
94
  rl.on('line', async (line) => {
128
- // Skip empty lines
129
95
  const trimmed = line.trim();
130
96
  if (!trimmed)
131
97
  return;
132
- // Parse JSON-RPC message
133
98
  let msg;
134
99
  try {
135
100
  msg = JSON.parse(trimmed);
136
101
  }
137
102
  catch {
138
- // Malformed JSON — send parse error if it looks like it had an id
139
- send({
140
- jsonrpc: '2.0',
141
- id: null,
142
- error: { code: -32700, message: 'Parse error: invalid JSON' },
143
- });
103
+ send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } });
144
104
  return;
145
105
  }
146
- // Validate basic JSON-RPC structure
147
106
  if (!msg.jsonrpc || msg.jsonrpc !== '2.0' || !msg.method) {
148
107
  if (msg.id !== undefined) {
149
- send({
150
- jsonrpc: '2.0',
151
- id: msg.id,
152
- error: { code: -32600, message: 'Invalid JSON-RPC 2.0 request' },
153
- });
108
+ send({ jsonrpc: '2.0', id: msg.id, error: { code: -32600, message: 'Invalid request' } });
154
109
  }
155
110
  return;
156
111
  }
157
- // Dispatch to agent handler
112
+ // Lazy init: resolve credentials + agent on first message
113
+ if (!agent) {
114
+ try {
115
+ agent = await resolveAgent(agentArg, send);
116
+ }
117
+ catch (err) {
118
+ send({ jsonrpc: '2.0', id: msg.id ?? null, error: { code: -32603, message: err.message } });
119
+ return;
120
+ }
121
+ }
158
122
  try {
159
123
  await agent.handleMessage(msg);
160
124
  }
161
125
  catch (err) {
162
- process.stderr.write(`[acp] Unhandled error in message handler: ${err}\n`);
126
+ process.stderr.write(`[acp] Error: ${err}\n`);
163
127
  if (msg.id !== undefined && msg.id !== null) {
164
- send({
165
- jsonrpc: '2.0',
166
- id: msg.id,
167
- error: { code: -32603, message: 'Internal error' },
168
- });
128
+ send({ jsonrpc: '2.0', id: msg.id, error: { code: -32603, message: 'Internal error' } });
169
129
  }
170
130
  }
171
131
  });
172
132
  // Handle stdin close (client disconnected)
173
133
  rl.on('close', () => {
174
134
  process.stderr.write('[acp] Client disconnected. Shutting down.\n');
175
- agent.shutdown();
135
+ agent?.shutdown();
176
136
  process.exit(0);
177
137
  });
178
138
  // Handle process signals
179
139
  const shutdown = () => {
180
140
  process.stderr.write('[acp] Shutting down.\n');
181
- agent.shutdown();
141
+ agent?.shutdown();
182
142
  process.exit(0);
183
143
  };
184
144
  process.on('SIGINT', shutdown);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almyty/acp-server",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "ACP agent server for almyty — expose any almyty agent via the Agent Client Protocol",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",