@agentconnect/sdk 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 +116 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.js +403 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @agentconnect/sdk
|
|
2
|
+
|
|
3
|
+
AgentConnect SDK for talking to a local AgentConnect host from browser or Node.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @agentconnect/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @agentconnect/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @agentconnect/sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start (browser)
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { AgentConnect } from '@agentconnect/sdk';
|
|
23
|
+
|
|
24
|
+
const client = await AgentConnect.connect();
|
|
25
|
+
const session = await client.sessions.create({ model: 'default' });
|
|
26
|
+
|
|
27
|
+
let output = '';
|
|
28
|
+
session.on('delta', (event) => {
|
|
29
|
+
output += event.text;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
session.on('final', (event) => {
|
|
33
|
+
console.log('Final:', event.text);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
session.on('error', (event) => {
|
|
37
|
+
console.error('Agent error:', event.message);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await session.send('Summarize this draft in 3 bullets.');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Session context and resume
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const session = await client.sessions.create({
|
|
47
|
+
model: 'codex',
|
|
48
|
+
cwd: '/path/to/project',
|
|
49
|
+
repoRoot: '/path/to/project',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
session.on('final', (event) => {
|
|
53
|
+
console.log('Session id:', event.providerSessionId);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await session.send('Audit the README for clarity.', {
|
|
57
|
+
cwd: '/path/to/project/docs',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const resumed = await client.sessions.resume('sess_123', {
|
|
61
|
+
providerSessionId: 'provider-session-id',
|
|
62
|
+
cwd: '/path/to/project',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Additional session events
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
session.on('raw_line', (event) => {
|
|
70
|
+
console.log('CLI:', event.line);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
session.on('provider_event', (event) => {
|
|
74
|
+
if (event.provider === 'codex') {
|
|
75
|
+
console.log('Codex event:', event.event);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Node usage
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { WebSocket } from 'ws';
|
|
84
|
+
import { AgentConnect } from '@agentconnect/sdk';
|
|
85
|
+
|
|
86
|
+
const client = await AgentConnect.connect({ webSocket: WebSocket });
|
|
87
|
+
const session = await client.sessions.create({ model: 'codex', reasoningEffort: 'medium' });
|
|
88
|
+
|
|
89
|
+
await session.send('Draft a product description for a local AI writing assistant.');
|
|
90
|
+
await session.close();
|
|
91
|
+
client.close();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Provider and model helpers
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const providers = await client.providers.list();
|
|
98
|
+
const claude = await client.providers.status('claude');
|
|
99
|
+
|
|
100
|
+
if (!claude.loggedIn) {
|
|
101
|
+
await client.providers.ensureInstalled('claude');
|
|
102
|
+
await client.providers.login('claude');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const models = await client.models.list('claude');
|
|
106
|
+
const recent = await client.models.recent('claude');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Node 20+ for Node usage.
|
|
112
|
+
- Browser usage relies on WebSocket support.
|
|
113
|
+
|
|
114
|
+
## Docs
|
|
115
|
+
|
|
116
|
+
See `docs/SDK.md` in the repo for the full SDK reference and `SPEC.md` for the protocol contract.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
export type ProviderId = 'claude' | 'codex' | 'local';
|
|
2
|
+
export type PackageManager = 'bun' | 'pnpm' | 'npm' | 'brew' | 'unknown';
|
|
3
|
+
export type InstallResult = {
|
|
4
|
+
installed: boolean;
|
|
5
|
+
version?: string;
|
|
6
|
+
packageManager?: PackageManager;
|
|
7
|
+
};
|
|
8
|
+
export type ProviderInfo = {
|
|
9
|
+
id: ProviderId;
|
|
10
|
+
name?: string;
|
|
11
|
+
installed: boolean;
|
|
12
|
+
loggedIn: boolean;
|
|
13
|
+
version?: string;
|
|
14
|
+
};
|
|
15
|
+
export type ReasoningEffortOption = {
|
|
16
|
+
id: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
};
|
|
19
|
+
export type ModelInfo = {
|
|
20
|
+
id: string;
|
|
21
|
+
provider: ProviderId;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
contextWindow?: number;
|
|
24
|
+
maxOutputTokens?: number;
|
|
25
|
+
reasoningEfforts?: ReasoningEffortOption[];
|
|
26
|
+
defaultReasoningEffort?: string;
|
|
27
|
+
};
|
|
28
|
+
export type SessionEvent = {
|
|
29
|
+
type: 'delta';
|
|
30
|
+
text: string;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'final';
|
|
33
|
+
text: string;
|
|
34
|
+
providerSessionId?: string | null;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'usage';
|
|
37
|
+
usage: Record<string, number>;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'status';
|
|
40
|
+
status: 'thinking' | 'idle' | 'error';
|
|
41
|
+
error?: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'error';
|
|
44
|
+
message: string;
|
|
45
|
+
} | {
|
|
46
|
+
type: 'raw_line';
|
|
47
|
+
line: string;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'provider_event';
|
|
50
|
+
provider?: ProviderId;
|
|
51
|
+
event: Record<string, unknown>;
|
|
52
|
+
};
|
|
53
|
+
export type ProviderLoginOptions = {
|
|
54
|
+
baseUrl?: string;
|
|
55
|
+
apiKey?: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
models?: string[];
|
|
58
|
+
loginMethod?: 'claudeai' | 'console';
|
|
59
|
+
loginExperience?: 'embedded' | 'terminal';
|
|
60
|
+
};
|
|
61
|
+
export type AgentConnectConnectOptions = {
|
|
62
|
+
host?: string;
|
|
63
|
+
preferInjected?: boolean;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
webSocket?: WebSocketConstructor;
|
|
66
|
+
};
|
|
67
|
+
export type SessionCreateOptions = {
|
|
68
|
+
model: string;
|
|
69
|
+
reasoningEffort?: string;
|
|
70
|
+
system?: string;
|
|
71
|
+
metadata?: Record<string, unknown>;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
repoRoot?: string;
|
|
74
|
+
temperature?: number;
|
|
75
|
+
maxTokens?: number;
|
|
76
|
+
topP?: number;
|
|
77
|
+
};
|
|
78
|
+
export type SessionSendOptions = {
|
|
79
|
+
metadata?: Record<string, unknown>;
|
|
80
|
+
cwd?: string;
|
|
81
|
+
repoRoot?: string;
|
|
82
|
+
};
|
|
83
|
+
export type SessionResumeOptions = {
|
|
84
|
+
model?: string;
|
|
85
|
+
reasoningEffort?: string;
|
|
86
|
+
providerSessionId?: string | null;
|
|
87
|
+
cwd?: string;
|
|
88
|
+
repoRoot?: string;
|
|
89
|
+
};
|
|
90
|
+
export interface AgentConnectSession {
|
|
91
|
+
id: string;
|
|
92
|
+
send(message: string, metadata?: Record<string, unknown>): Promise<void>;
|
|
93
|
+
send(message: string, options?: SessionSendOptions): Promise<void>;
|
|
94
|
+
cancel(): Promise<void>;
|
|
95
|
+
close(): Promise<void>;
|
|
96
|
+
on(type: SessionEvent['type'], handler: (ev: SessionEvent) => void): () => void;
|
|
97
|
+
}
|
|
98
|
+
export interface AgentConnectClient {
|
|
99
|
+
hello(): Promise<{
|
|
100
|
+
hostId: string;
|
|
101
|
+
hostName: string;
|
|
102
|
+
hostVersion: string;
|
|
103
|
+
protocolVersion: string;
|
|
104
|
+
mode: 'hosted' | 'local';
|
|
105
|
+
capabilities: string[];
|
|
106
|
+
providers: ProviderId[];
|
|
107
|
+
loginExperience?: 'embedded' | 'terminal';
|
|
108
|
+
}>;
|
|
109
|
+
close(): void;
|
|
110
|
+
providers: {
|
|
111
|
+
list(): Promise<ProviderInfo[]>;
|
|
112
|
+
status(provider: ProviderId): Promise<ProviderInfo>;
|
|
113
|
+
ensureInstalled(provider: ProviderId): Promise<InstallResult>;
|
|
114
|
+
login(provider: ProviderId, options?: ProviderLoginOptions): Promise<{
|
|
115
|
+
loggedIn: boolean;
|
|
116
|
+
}>;
|
|
117
|
+
logout(provider: ProviderId): Promise<{
|
|
118
|
+
loggedIn: boolean;
|
|
119
|
+
}>;
|
|
120
|
+
};
|
|
121
|
+
models: {
|
|
122
|
+
list(provider?: ProviderId): Promise<ModelInfo[]>;
|
|
123
|
+
recent(provider?: ProviderId): Promise<ModelInfo[]>;
|
|
124
|
+
info(model: string): Promise<ModelInfo>;
|
|
125
|
+
};
|
|
126
|
+
capabilities: {
|
|
127
|
+
observed(appId?: string): Promise<{
|
|
128
|
+
appId: string;
|
|
129
|
+
requested: string[];
|
|
130
|
+
observed: string[];
|
|
131
|
+
updatedAt: string;
|
|
132
|
+
}>;
|
|
133
|
+
};
|
|
134
|
+
sessions: {
|
|
135
|
+
create(options: SessionCreateOptions): Promise<AgentConnectSession>;
|
|
136
|
+
resume(sessionId: string, options?: SessionResumeOptions): Promise<AgentConnectSession>;
|
|
137
|
+
};
|
|
138
|
+
fs: {
|
|
139
|
+
read(path: string): Promise<{
|
|
140
|
+
content: string;
|
|
141
|
+
}>;
|
|
142
|
+
write(path: string, content: string): Promise<{
|
|
143
|
+
bytes: number;
|
|
144
|
+
}>;
|
|
145
|
+
list(path: string): Promise<{
|
|
146
|
+
entries: Array<{
|
|
147
|
+
name: string;
|
|
148
|
+
path: string;
|
|
149
|
+
type: string;
|
|
150
|
+
}>;
|
|
151
|
+
}>;
|
|
152
|
+
stat(path: string): Promise<{
|
|
153
|
+
type: string;
|
|
154
|
+
size: number;
|
|
155
|
+
mtime: string;
|
|
156
|
+
}>;
|
|
157
|
+
};
|
|
158
|
+
process: {
|
|
159
|
+
spawn(command: string, args?: string[]): Promise<{
|
|
160
|
+
pid: number;
|
|
161
|
+
}>;
|
|
162
|
+
kill(pid: number, signal?: string): Promise<{
|
|
163
|
+
success: boolean;
|
|
164
|
+
}>;
|
|
165
|
+
};
|
|
166
|
+
net: {
|
|
167
|
+
request(url: string, init?: {
|
|
168
|
+
method?: string;
|
|
169
|
+
headers?: Record<string, string>;
|
|
170
|
+
body?: string;
|
|
171
|
+
}): Promise<{
|
|
172
|
+
status: number;
|
|
173
|
+
headers: Record<string, string>;
|
|
174
|
+
body: string;
|
|
175
|
+
}>;
|
|
176
|
+
};
|
|
177
|
+
backend: {
|
|
178
|
+
start(appId: string): Promise<{
|
|
179
|
+
status: string;
|
|
180
|
+
url?: string;
|
|
181
|
+
}>;
|
|
182
|
+
stop(appId: string): Promise<{
|
|
183
|
+
status: string;
|
|
184
|
+
}>;
|
|
185
|
+
status(appId: string): Promise<{
|
|
186
|
+
status: string;
|
|
187
|
+
url?: string;
|
|
188
|
+
}>;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
type WebSocketLike = {
|
|
192
|
+
addEventListener: (type: string, listener: (event: {
|
|
193
|
+
data?: unknown;
|
|
194
|
+
}) => void) => void;
|
|
195
|
+
removeEventListener: (type: string, listener: (event: {
|
|
196
|
+
data?: unknown;
|
|
197
|
+
}) => void) => void;
|
|
198
|
+
send: (data: string) => void;
|
|
199
|
+
close: () => void;
|
|
200
|
+
};
|
|
201
|
+
type WebSocketConstructor = new (url: string) => WebSocketLike;
|
|
202
|
+
export declare class AgentConnect {
|
|
203
|
+
static connect(options?: AgentConnectConnectOptions): Promise<AgentConnectClient>;
|
|
204
|
+
}
|
|
205
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
class WebSocketTransport {
|
|
2
|
+
socket;
|
|
3
|
+
pending = new Map();
|
|
4
|
+
notifyHandlers = new Set();
|
|
5
|
+
nextId = 1;
|
|
6
|
+
ready;
|
|
7
|
+
timeoutMs;
|
|
8
|
+
closed = false;
|
|
9
|
+
constructor(url, timeoutMs, webSocket) {
|
|
10
|
+
const WebSocketCtor = webSocket || globalThis.WebSocket;
|
|
11
|
+
if (!WebSocketCtor) {
|
|
12
|
+
throw new Error('WebSocket is not available in this environment. Provide AgentConnect.connect({ webSocket }) in Node.');
|
|
13
|
+
}
|
|
14
|
+
this.socket = new WebSocketCtor(url);
|
|
15
|
+
this.timeoutMs = timeoutMs;
|
|
16
|
+
const handleDisconnect = (message) => {
|
|
17
|
+
if (this.closed)
|
|
18
|
+
return;
|
|
19
|
+
this.closed = true;
|
|
20
|
+
for (const pending of this.pending.values()) {
|
|
21
|
+
pending.reject(new Error(message));
|
|
22
|
+
}
|
|
23
|
+
this.pending.clear();
|
|
24
|
+
};
|
|
25
|
+
this.ready = new Promise((resolve, reject) => {
|
|
26
|
+
const onOpen = () => {
|
|
27
|
+
cleanup();
|
|
28
|
+
resolve();
|
|
29
|
+
};
|
|
30
|
+
const onError = () => {
|
|
31
|
+
cleanup();
|
|
32
|
+
reject(new Error('Failed to connect to AgentConnect host.'));
|
|
33
|
+
};
|
|
34
|
+
const onClose = () => {
|
|
35
|
+
cleanup();
|
|
36
|
+
reject(new Error('AgentConnect connection closed.'));
|
|
37
|
+
};
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
this.socket.removeEventListener('open', onOpen);
|
|
40
|
+
this.socket.removeEventListener('error', onError);
|
|
41
|
+
this.socket.removeEventListener('close', onClose);
|
|
42
|
+
};
|
|
43
|
+
this.socket.addEventListener('open', onOpen);
|
|
44
|
+
this.socket.addEventListener('error', onError);
|
|
45
|
+
this.socket.addEventListener('close', onClose);
|
|
46
|
+
});
|
|
47
|
+
this.socket.addEventListener('close', () => {
|
|
48
|
+
handleDisconnect('AgentConnect connection closed.');
|
|
49
|
+
});
|
|
50
|
+
this.socket.addEventListener('error', () => {
|
|
51
|
+
handleDisconnect('AgentConnect connection error.');
|
|
52
|
+
});
|
|
53
|
+
this.socket.addEventListener('message', (event) => {
|
|
54
|
+
const payload = typeof event.data === 'string' ? event.data : '';
|
|
55
|
+
if (!payload)
|
|
56
|
+
return;
|
|
57
|
+
let msg;
|
|
58
|
+
try {
|
|
59
|
+
msg = JSON.parse(payload);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if ('id' in msg && msg.result !== undefined) {
|
|
65
|
+
const pending = this.pending.get(msg.id);
|
|
66
|
+
if (pending) {
|
|
67
|
+
this.pending.delete(msg.id);
|
|
68
|
+
pending.resolve(msg.result);
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if ('id' in msg && msg.error) {
|
|
73
|
+
const pending = this.pending.get(msg.id);
|
|
74
|
+
if (pending) {
|
|
75
|
+
this.pending.delete(msg.id);
|
|
76
|
+
const err = msg.error;
|
|
77
|
+
pending.reject(new Error(`${err.code}: ${err.message}`));
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if ('method' in msg && !('id' in msg)) {
|
|
82
|
+
for (const handler of this.notifyHandlers) {
|
|
83
|
+
handler(msg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async request(method, params) {
|
|
89
|
+
await this.ready;
|
|
90
|
+
if (this.closed) {
|
|
91
|
+
throw new Error('AgentConnect connection closed.');
|
|
92
|
+
}
|
|
93
|
+
const id = this.nextId++;
|
|
94
|
+
const payload = {
|
|
95
|
+
jsonrpc: '2.0',
|
|
96
|
+
id,
|
|
97
|
+
method,
|
|
98
|
+
params,
|
|
99
|
+
};
|
|
100
|
+
const timeout = this.timeoutMs;
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
const timer = timeout
|
|
103
|
+
? setTimeout(() => {
|
|
104
|
+
this.pending.delete(id);
|
|
105
|
+
reject(new Error('AgentConnect request timed out.'));
|
|
106
|
+
}, timeout)
|
|
107
|
+
: null;
|
|
108
|
+
this.pending.set(id, {
|
|
109
|
+
resolve: (value) => {
|
|
110
|
+
if (timer)
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
resolve(value);
|
|
113
|
+
},
|
|
114
|
+
reject: (err) => {
|
|
115
|
+
if (timer)
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
reject(err);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
this.socket.send(JSON.stringify(payload));
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
onNotification(handler) {
|
|
124
|
+
this.notifyHandlers.add(handler);
|
|
125
|
+
return () => {
|
|
126
|
+
this.notifyHandlers.delete(handler);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
close() {
|
|
130
|
+
this.socket.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
class BridgeTransport {
|
|
134
|
+
bridge;
|
|
135
|
+
constructor(bridge) {
|
|
136
|
+
this.bridge = bridge;
|
|
137
|
+
}
|
|
138
|
+
request(method, params) {
|
|
139
|
+
return this.bridge.request(method, params);
|
|
140
|
+
}
|
|
141
|
+
onNotification(handler) {
|
|
142
|
+
if (!this.bridge.onEvent) {
|
|
143
|
+
return () => { };
|
|
144
|
+
}
|
|
145
|
+
return this.bridge.onEvent(handler);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
class AgentConnectSessionImpl {
|
|
149
|
+
id;
|
|
150
|
+
client;
|
|
151
|
+
listeners = new Map();
|
|
152
|
+
constructor(id, client) {
|
|
153
|
+
this.id = id;
|
|
154
|
+
this.client = client;
|
|
155
|
+
}
|
|
156
|
+
async send(message, options) {
|
|
157
|
+
const normalized = this.normalizeSendOptions(options);
|
|
158
|
+
await this.client.request('acp.sessions.send', {
|
|
159
|
+
sessionId: this.id,
|
|
160
|
+
message: { role: 'user', content: message },
|
|
161
|
+
metadata: normalized.metadata,
|
|
162
|
+
cwd: normalized.cwd,
|
|
163
|
+
repoRoot: normalized.repoRoot,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async cancel() {
|
|
167
|
+
await this.client.request('acp.sessions.cancel', { sessionId: this.id });
|
|
168
|
+
}
|
|
169
|
+
async close() {
|
|
170
|
+
try {
|
|
171
|
+
await this.client.request('acp.sessions.close', { sessionId: this.id });
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
this.client.dropSession(this.id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
on(type, handler) {
|
|
178
|
+
if (!this.listeners.has(type)) {
|
|
179
|
+
this.listeners.set(type, new Set());
|
|
180
|
+
}
|
|
181
|
+
const set = this.listeners.get(type);
|
|
182
|
+
if (set)
|
|
183
|
+
set.add(handler);
|
|
184
|
+
return () => {
|
|
185
|
+
set?.delete(handler);
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
emit(event) {
|
|
189
|
+
const handlers = this.listeners.get(event.type);
|
|
190
|
+
if (!handlers)
|
|
191
|
+
return;
|
|
192
|
+
for (const handler of handlers) {
|
|
193
|
+
handler(event);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
normalizeSendOptions(options) {
|
|
197
|
+
if (!options)
|
|
198
|
+
return {};
|
|
199
|
+
const candidate = options;
|
|
200
|
+
if ('metadata' in candidate ||
|
|
201
|
+
'cwd' in candidate ||
|
|
202
|
+
'repoRoot' in candidate) {
|
|
203
|
+
return candidate;
|
|
204
|
+
}
|
|
205
|
+
return { metadata: options };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
class AgentConnectClientImpl {
|
|
209
|
+
transport;
|
|
210
|
+
sessionStore = new Map();
|
|
211
|
+
constructor(transport) {
|
|
212
|
+
this.transport = transport;
|
|
213
|
+
this.transport.onNotification((notification) => {
|
|
214
|
+
if (notification.method !== 'acp.session.event')
|
|
215
|
+
return;
|
|
216
|
+
const params = notification.params ?? {};
|
|
217
|
+
const sessionId = String(params.sessionId ?? '');
|
|
218
|
+
if (!sessionId)
|
|
219
|
+
return;
|
|
220
|
+
const session = this.sessionStore.get(sessionId);
|
|
221
|
+
if (!session)
|
|
222
|
+
return;
|
|
223
|
+
const type = String(params.type ?? '');
|
|
224
|
+
const data = params.data;
|
|
225
|
+
const event = this.normalizeEvent(type, data);
|
|
226
|
+
if (event)
|
|
227
|
+
session.emit(event);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
normalizeEvent(type, data) {
|
|
231
|
+
if (type === 'delta') {
|
|
232
|
+
return { type: 'delta', text: String(data?.text ?? '') };
|
|
233
|
+
}
|
|
234
|
+
if (type === 'final') {
|
|
235
|
+
const providerSessionId = typeof data?.providerSessionId === 'string'
|
|
236
|
+
? data.providerSessionId
|
|
237
|
+
: typeof data?.sessionId === 'string'
|
|
238
|
+
? data.sessionId
|
|
239
|
+
: undefined;
|
|
240
|
+
return { type: 'final', text: String(data?.text ?? ''), providerSessionId };
|
|
241
|
+
}
|
|
242
|
+
if (type === 'usage') {
|
|
243
|
+
return { type: 'usage', usage: data?.usage ?? {} };
|
|
244
|
+
}
|
|
245
|
+
if (type === 'status') {
|
|
246
|
+
const status = String(data?.status ?? 'idle');
|
|
247
|
+
const error = typeof data?.error === 'string' ? data.error : undefined;
|
|
248
|
+
return { type: 'status', status, error };
|
|
249
|
+
}
|
|
250
|
+
if (type === 'error') {
|
|
251
|
+
return { type: 'error', message: String(data?.message ?? 'Unknown error') };
|
|
252
|
+
}
|
|
253
|
+
if (type === 'raw_line') {
|
|
254
|
+
return { type: 'raw_line', line: String(data?.line ?? '') };
|
|
255
|
+
}
|
|
256
|
+
if (type === 'provider_event') {
|
|
257
|
+
const provider = typeof data?.provider === 'string' ? data.provider : undefined;
|
|
258
|
+
const event = data?.event && typeof data.event === 'object'
|
|
259
|
+
? data.event
|
|
260
|
+
: {};
|
|
261
|
+
return { type: 'provider_event', provider, event };
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
request(method, params) {
|
|
266
|
+
return this.transport.request(method, params);
|
|
267
|
+
}
|
|
268
|
+
async hello() {
|
|
269
|
+
return (await this.request('acp.hello'));
|
|
270
|
+
}
|
|
271
|
+
close() {
|
|
272
|
+
this.transport.close?.();
|
|
273
|
+
}
|
|
274
|
+
providers = {
|
|
275
|
+
list: async () => {
|
|
276
|
+
const res = (await this.request('acp.providers.list'));
|
|
277
|
+
return res.providers ?? [];
|
|
278
|
+
},
|
|
279
|
+
status: async (provider) => {
|
|
280
|
+
const res = (await this.request('acp.providers.status', { provider }));
|
|
281
|
+
return res.provider;
|
|
282
|
+
},
|
|
283
|
+
ensureInstalled: async (provider) => {
|
|
284
|
+
return (await this.request('acp.providers.ensureInstalled', { provider }));
|
|
285
|
+
},
|
|
286
|
+
login: async (provider, options) => {
|
|
287
|
+
return (await this.request('acp.providers.login', { provider, options }));
|
|
288
|
+
},
|
|
289
|
+
logout: async (provider) => {
|
|
290
|
+
return (await this.request('acp.providers.logout', { provider }));
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
models = {
|
|
294
|
+
list: async (provider) => {
|
|
295
|
+
const res = (await this.request('acp.models.list', provider ? { provider } : undefined));
|
|
296
|
+
return res.models ?? [];
|
|
297
|
+
},
|
|
298
|
+
recent: async (provider) => {
|
|
299
|
+
const res = (await this.request('acp.models.recent', provider ? { provider } : undefined));
|
|
300
|
+
return res.models ?? [];
|
|
301
|
+
},
|
|
302
|
+
info: async (model) => {
|
|
303
|
+
const res = (await this.request('acp.models.info', { model }));
|
|
304
|
+
return res.model;
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
capabilities = {
|
|
308
|
+
observed: async (appId) => {
|
|
309
|
+
return (await this.request('acp.capabilities.observed', appId ? { appId } : undefined));
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
sessions = {
|
|
313
|
+
create: async (options) => {
|
|
314
|
+
const res = (await this.request('acp.sessions.create', options));
|
|
315
|
+
return this.getOrCreateSession(res.sessionId);
|
|
316
|
+
},
|
|
317
|
+
resume: async (sessionId, options) => {
|
|
318
|
+
await this.request('acp.sessions.resume', { sessionId, ...(options ?? {}) });
|
|
319
|
+
return this.getOrCreateSession(sessionId);
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
fs = {
|
|
323
|
+
read: async (path) => {
|
|
324
|
+
return (await this.request('acp.fs.read', { path }));
|
|
325
|
+
},
|
|
326
|
+
write: async (path, content) => {
|
|
327
|
+
return (await this.request('acp.fs.write', { path, content }));
|
|
328
|
+
},
|
|
329
|
+
list: async (path) => {
|
|
330
|
+
return (await this.request('acp.fs.list', { path }));
|
|
331
|
+
},
|
|
332
|
+
stat: async (path) => {
|
|
333
|
+
return (await this.request('acp.fs.stat', { path }));
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
process = {
|
|
337
|
+
spawn: async (command, args) => {
|
|
338
|
+
return (await this.request('acp.process.spawn', { command, args }));
|
|
339
|
+
},
|
|
340
|
+
kill: async (pid, signal) => {
|
|
341
|
+
return (await this.request('acp.process.kill', { pid, signal }));
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
net = {
|
|
345
|
+
request: async (url, init) => {
|
|
346
|
+
return (await this.request('acp.net.request', { url, ...init }));
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
backend = {
|
|
350
|
+
start: async (appId) => {
|
|
351
|
+
return (await this.request('acp.backend.start', { appId }));
|
|
352
|
+
},
|
|
353
|
+
stop: async (appId) => {
|
|
354
|
+
return (await this.request('acp.backend.stop', { appId }));
|
|
355
|
+
},
|
|
356
|
+
status: async (appId) => {
|
|
357
|
+
return (await this.request('acp.backend.status', { appId }));
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
getOrCreateSession(sessionId) {
|
|
361
|
+
const existing = this.sessionStore.get(sessionId);
|
|
362
|
+
if (existing)
|
|
363
|
+
return existing;
|
|
364
|
+
const session = new AgentConnectSessionImpl(sessionId, this);
|
|
365
|
+
this.sessionStore.set(sessionId, session);
|
|
366
|
+
return session;
|
|
367
|
+
}
|
|
368
|
+
dropSession(sessionId) {
|
|
369
|
+
this.sessionStore.delete(sessionId);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
export class AgentConnect {
|
|
373
|
+
static async connect(options = {}) {
|
|
374
|
+
const { host, preferInjected = true, timeoutMs = 8000, webSocket } = options;
|
|
375
|
+
const injected = getInjectedBridge();
|
|
376
|
+
if (preferInjected && injected) {
|
|
377
|
+
return new AgentConnectClientImpl(new BridgeTransport(injected));
|
|
378
|
+
}
|
|
379
|
+
const target = host || resolveHostOverride() || 'ws://127.0.0.1:9630';
|
|
380
|
+
const transport = new WebSocketTransport(target, timeoutMs, webSocket);
|
|
381
|
+
return new AgentConnectClientImpl(transport);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function getInjectedBridge() {
|
|
385
|
+
const candidate = globalThis
|
|
386
|
+
.__AGENTCONNECT_BRIDGE__;
|
|
387
|
+
return candidate ?? null;
|
|
388
|
+
}
|
|
389
|
+
function resolveHostOverride() {
|
|
390
|
+
const location = globalThis.location;
|
|
391
|
+
if (location?.search) {
|
|
392
|
+
const params = new URLSearchParams(location.search);
|
|
393
|
+
const override = params.get('agentconnect');
|
|
394
|
+
if (override)
|
|
395
|
+
return override;
|
|
396
|
+
}
|
|
397
|
+
if (typeof process !== 'undefined' && typeof process.env !== 'undefined') {
|
|
398
|
+
const env = process.env.AGENTCONNECT_HOST;
|
|
399
|
+
if (env)
|
|
400
|
+
return env;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentconnect/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"homepage": "https://github.com/rayzhudev/agent-connect",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rayzhudev/agent-connect.git",
|
|
10
|
+
"directory": "packages/sdk"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/rayzhudev/agent-connect/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"dev": "tsc --watch"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.6.2"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"sideEffects": false
|
|
37
|
+
}
|