@openagents-org/agent-connector 0.1.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/README.md +86 -0
- package/bin/agent-connector.js +4 -0
- package/package.json +31 -0
- package/registry.json +457 -0
- package/src/cli.js +526 -0
- package/src/config.js +299 -0
- package/src/daemon.js +541 -0
- package/src/env.js +111 -0
- package/src/index.js +198 -0
- package/src/installer.js +228 -0
- package/src/registry.js +188 -0
- package/src/utils.js +93 -0
- package/src/workspace-client.js +194 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_ENDPOINT = 'https://endpoint.openagents.org';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HTTP client for workspace API operations.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors the Python SDK's WorkspaceClient — same endpoints, same
|
|
12
|
+
* auth headers (X-Workspace-Token), same request/response shapes.
|
|
13
|
+
*/
|
|
14
|
+
class WorkspaceClient {
|
|
15
|
+
constructor(endpoint) {
|
|
16
|
+
this.endpoint = (endpoint || DEFAULT_ENDPOINT).replace(/\/$/, '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register an agent identity via POST /v1/agentid/register.
|
|
21
|
+
*/
|
|
22
|
+
async registerAgent(agentName, { apiKey, origin = 'cli' } = {}) {
|
|
23
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
24
|
+
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
25
|
+
|
|
26
|
+
const data = await this._post('/v1/agentid/register', {
|
|
27
|
+
agent_name: agentName,
|
|
28
|
+
origin,
|
|
29
|
+
}, headers);
|
|
30
|
+
|
|
31
|
+
return data.data || data;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a workspace via POST /v1/workspaces.
|
|
36
|
+
* @returns {{ workspaceId, slug, name, token, url, channelName }}
|
|
37
|
+
*/
|
|
38
|
+
async createWorkspace({ agentName, name, agentType } = {}) {
|
|
39
|
+
const payload = {
|
|
40
|
+
name: name || (agentName ? `${agentName}'s workspace` : 'My Workspace'),
|
|
41
|
+
};
|
|
42
|
+
if (agentName) payload.agent_name = agentName;
|
|
43
|
+
if (agentType) payload.agent_type = agentType;
|
|
44
|
+
|
|
45
|
+
const data = await this._post('/v1/workspaces', payload);
|
|
46
|
+
const result = data.data || data;
|
|
47
|
+
|
|
48
|
+
const frontendUrl = this.endpoint
|
|
49
|
+
.replace('workspace-endpoint', 'workspace')
|
|
50
|
+
.replace('/v1', '');
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
workspaceId: result.workspaceId,
|
|
54
|
+
slug: result.slug || result.workspaceId,
|
|
55
|
+
name: result.name,
|
|
56
|
+
token: result.token,
|
|
57
|
+
url: `${frontendUrl}/${result.slug || result.workspaceId}?token=${result.token}`,
|
|
58
|
+
channelName: (result.channel || {}).name || '',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Join a workspace via POST /v1/join.
|
|
64
|
+
*/
|
|
65
|
+
async joinNetwork(agentName, token, { network, agentType, serverHost, workingDir } = {}) {
|
|
66
|
+
const body = { agent_name: agentName, token };
|
|
67
|
+
if (network) body.network = network;
|
|
68
|
+
if (agentType) body.agent_type = agentType;
|
|
69
|
+
if (serverHost) body.server_host = serverHost;
|
|
70
|
+
if (workingDir) body.working_dir = workingDir;
|
|
71
|
+
|
|
72
|
+
const data = await this._post('/v1/join', body);
|
|
73
|
+
return data.data || data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve a workspace token to workspace info via POST /v1/token/resolve.
|
|
78
|
+
* @returns {{ workspace_id, slug, name }}
|
|
79
|
+
*/
|
|
80
|
+
async resolveToken(token) {
|
|
81
|
+
const data = await this._post('/v1/token/resolve', { token });
|
|
82
|
+
return data.data || data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Send heartbeat via POST /v1/heartbeat.
|
|
87
|
+
*/
|
|
88
|
+
async heartbeat(workspaceId, agentName, token) {
|
|
89
|
+
const data = await this._post('/v1/heartbeat', {
|
|
90
|
+
agent_name: agentName,
|
|
91
|
+
network: workspaceId,
|
|
92
|
+
}, this._wsHeaders(token));
|
|
93
|
+
return data.data || data;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Disconnect agent via POST /v1/leave. Best-effort (ignores errors).
|
|
98
|
+
*/
|
|
99
|
+
async disconnect(workspaceId, agentName, token) {
|
|
100
|
+
try {
|
|
101
|
+
await this._post('/v1/leave', {
|
|
102
|
+
agent_name: agentName,
|
|
103
|
+
network: workspaceId,
|
|
104
|
+
}, this._wsHeaders(token));
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Poll for pending tasks via POST /v1/poll_pending.
|
|
110
|
+
*/
|
|
111
|
+
async pollPending(workspaceId, agentName, token) {
|
|
112
|
+
const data = await this._post('/v1/poll_pending', {
|
|
113
|
+
agent_name: agentName,
|
|
114
|
+
network: workspaceId,
|
|
115
|
+
}, this._wsHeaders(token));
|
|
116
|
+
return data.data || data;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Post a task result via POST /v1/events.
|
|
121
|
+
*/
|
|
122
|
+
async sendEvent(workspaceId, event, token) {
|
|
123
|
+
event.network = workspaceId;
|
|
124
|
+
const data = await this._post('/v1/events', event, this._wsHeaders(token));
|
|
125
|
+
return data.data || data;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Send a chat message to a workspace channel.
|
|
130
|
+
*/
|
|
131
|
+
async sendMessage(workspaceId, channelName, token, content, {
|
|
132
|
+
senderType = 'agent', senderName, messageType = 'chat', metadata,
|
|
133
|
+
} = {}) {
|
|
134
|
+
const sourcePrefix = senderType === 'agent' ? 'openagents' : 'human';
|
|
135
|
+
const source = senderName ? `${sourcePrefix}:${senderName}` : `${sourcePrefix}:unknown`;
|
|
136
|
+
|
|
137
|
+
return this.sendEvent(workspaceId, {
|
|
138
|
+
type: 'workspace.message.posted',
|
|
139
|
+
source,
|
|
140
|
+
target: `channel/${channelName}`,
|
|
141
|
+
payload: { content, message_type: messageType },
|
|
142
|
+
metadata: metadata || {},
|
|
143
|
+
}, token);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// -- Internal --
|
|
147
|
+
|
|
148
|
+
_wsHeaders(token) {
|
|
149
|
+
return {
|
|
150
|
+
'Content-Type': 'application/json',
|
|
151
|
+
'X-Workspace-Token': token,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_post(urlPath, body, headers = {}) {
|
|
156
|
+
if (!headers['Content-Type']) headers['Content-Type'] = 'application/json';
|
|
157
|
+
const jsonBody = JSON.stringify(body);
|
|
158
|
+
const fullUrl = this.endpoint + urlPath;
|
|
159
|
+
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const parsedUrl = new URL(fullUrl);
|
|
162
|
+
const transport = parsedUrl.protocol === 'https:' ? https : http;
|
|
163
|
+
|
|
164
|
+
const req = transport.request(fullUrl, {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
headers: { ...headers, 'Content-Length': Buffer.byteLength(jsonBody) },
|
|
167
|
+
timeout: 30000,
|
|
168
|
+
}, (res) => {
|
|
169
|
+
let data = '';
|
|
170
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
171
|
+
res.on('end', () => {
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(data);
|
|
174
|
+
if (res.statusCode >= 400) {
|
|
175
|
+
const msg = parsed.message || `HTTP ${res.statusCode}`;
|
|
176
|
+
reject(new Error(msg));
|
|
177
|
+
} else {
|
|
178
|
+
resolve(parsed);
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
reject(new Error(`Invalid response: ${data.slice(0, 200)}`));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
req.on('error', reject);
|
|
187
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Request timed out')); });
|
|
188
|
+
req.write(jsonBody);
|
|
189
|
+
req.end();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = { WorkspaceClient };
|