@amaster.ai/copilot-client 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +338 -0
- package/dist/index.cjs +237 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +235 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amaster Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# @amaster.ai/copilot-client
|
|
2
|
+
|
|
3
|
+
AI copilot client with full A2A Agent-to-Agent protocol support and streaming chat capabilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 💬 **Streaming Chat**: Real-time streaming responses with SSE
|
|
8
|
+
- 🔄 **Task Management**: Cancel, monitor, and resubscribe to tasks
|
|
9
|
+
- 🤖 **Multi-turn Conversations**: Support for conversation history
|
|
10
|
+
- 🎯 **A2A Protocol**: Full JSON-RPC 2.0 implementation
|
|
11
|
+
- 📡 **Auto Context**: Automatically extracts app context from URL
|
|
12
|
+
- 🌳 **Tree-shakeable**: ESM/CJS builds with TypeScript support
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @amaster.ai/copilot-client @amaster.ai/http-client axios @a2a-js/sdk eventsource-parser
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { createCopilotClient } from "@amaster.ai/copilot-client";
|
|
24
|
+
|
|
25
|
+
const copilot = createCopilotClient();
|
|
26
|
+
|
|
27
|
+
// Stream chat responses
|
|
28
|
+
for await (const chunk of copilot.chat([
|
|
29
|
+
{ role: "user", content: "Hello, how are you?" }
|
|
30
|
+
])) {
|
|
31
|
+
console.log(chunk.text); // Print each chunk as it arrives
|
|
32
|
+
|
|
33
|
+
if (chunk.isFinal) {
|
|
34
|
+
console.log("Chat completed with status:", chunk.status);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Architecture
|
|
40
|
+
|
|
41
|
+
This client implements the **A2A (Agent-to-Agent)** protocol for AI interactions:
|
|
42
|
+
|
|
43
|
+
- **JSON-RPC 2.0**: Standard protocol for method calls
|
|
44
|
+
- **Server-Sent Events (SSE)**: Streaming responses
|
|
45
|
+
- **Task-based**: Each conversation is a trackable task
|
|
46
|
+
- **Context-aware**: Automatically links to app context
|
|
47
|
+
|
|
48
|
+
## API Reference
|
|
49
|
+
|
|
50
|
+
### `chat(messages, options?)`
|
|
51
|
+
|
|
52
|
+
Stream chat responses using simplified API.
|
|
53
|
+
|
|
54
|
+
**Parameters:**
|
|
55
|
+
- `messages`: Array of chat messages
|
|
56
|
+
- `options`: Optional configuration
|
|
57
|
+
- `taskId`: Continue an existing task (optional)
|
|
58
|
+
|
|
59
|
+
**Returns:** `AsyncGenerator<ChatChunk>`
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const messages = [
|
|
63
|
+
{ role: "system", content: "You are a helpful assistant" },
|
|
64
|
+
{ role: "user", content: "What is TypeScript?" },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
for await (const chunk of copilot.chat(messages)) {
|
|
68
|
+
process.stdout.write(chunk.text); // Stream output
|
|
69
|
+
|
|
70
|
+
// chunk.status: Current state (RUNNING, SUCCEEDED, FAILED, CANCELLED)
|
|
71
|
+
// chunk.isFinal: true when chat is complete
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Types
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
interface ChatMessage {
|
|
79
|
+
role: "system" | "user" | "assistant";
|
|
80
|
+
content: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ChatChunk {
|
|
84
|
+
taskId: string; // Unique task identifier
|
|
85
|
+
text: string; // Chunk of response text
|
|
86
|
+
status: TaskState; // RUNNING | SUCCEEDED | FAILED | CANCELLED
|
|
87
|
+
isFinal: boolean | undefined; // Whether this is the final chunk
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface ChatOptions {
|
|
91
|
+
taskId?: string; // Optional: provide your own task ID to resume
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type TaskState = "RUNNING" | "SUCCEEDED" | "FAILED" | "CANCELLED";
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `cancelChat(taskId)`
|
|
98
|
+
|
|
99
|
+
Cancel an ongoing chat task.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const taskId = "task-123";
|
|
103
|
+
const result = await copilot.cancelChat(taskId);
|
|
104
|
+
|
|
105
|
+
if (result.data) {
|
|
106
|
+
console.log("Chat cancelled successfully");
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `getChatStatus(taskId)`
|
|
111
|
+
|
|
112
|
+
Get current status of a chat task.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const result = await copilot.getChatStatus("task-123");
|
|
116
|
+
|
|
117
|
+
if (result.data) {
|
|
118
|
+
console.log("Task status:", result.data.result.status.state);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Usage Examples
|
|
123
|
+
|
|
124
|
+
### Multi-turn Conversation
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const messages: ChatMessage[] = [];
|
|
128
|
+
|
|
129
|
+
// First message
|
|
130
|
+
messages.push({ role: "user", content: "What is React?" });
|
|
131
|
+
|
|
132
|
+
let response = "";
|
|
133
|
+
for await (const chunk of copilot.chat(messages)) {
|
|
134
|
+
response += chunk.text;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
messages.push({ role: "assistant", content: response });
|
|
138
|
+
|
|
139
|
+
// Follow-up message
|
|
140
|
+
messages.push({ role: "user", content: "Can you give me an example?" });
|
|
141
|
+
|
|
142
|
+
response = "";
|
|
143
|
+
for await (const chunk of copilot.chat(messages)) {
|
|
144
|
+
response += chunk.text;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### With System Prompt
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const messages = [
|
|
152
|
+
{
|
|
153
|
+
role: "system",
|
|
154
|
+
content: "You are a coding assistant specialized in TypeScript."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
role: "user",
|
|
158
|
+
content: "How do I define an interface?"
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
for await (const chunk of copilot.chat(messages)) {
|
|
163
|
+
console.log(chunk.text);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Resume Existing Task
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Start a chat
|
|
171
|
+
let taskId: string;
|
|
172
|
+
for await (const chunk of copilot.chat([
|
|
173
|
+
{ role: "user", content: "Tell me a long story" }
|
|
174
|
+
])) {
|
|
175
|
+
taskId = chunk.taskId;
|
|
176
|
+
console.log(chunk.text);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Later, continue the same task
|
|
180
|
+
for await (const chunk of copilot.chat([
|
|
181
|
+
{ role: "user", content: "What happened next?" }
|
|
182
|
+
], { taskId })) {
|
|
183
|
+
console.log(chunk.text);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Cancel Long-running Chat
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
let taskId: string;
|
|
191
|
+
|
|
192
|
+
const chatPromise = (async () => {
|
|
193
|
+
for await (const chunk of copilot.chat(
|
|
194
|
+
[{ role: "user", content: "Write a long essay" }]
|
|
195
|
+
)) {
|
|
196
|
+
taskId = chunk.taskId;
|
|
197
|
+
console.log(chunk.text);
|
|
198
|
+
}
|
|
199
|
+
})();
|
|
200
|
+
|
|
201
|
+
// Cancel after 5 seconds
|
|
202
|
+
setTimeout(async () => {
|
|
203
|
+
if (taskId) {
|
|
204
|
+
await copilot.cancelChat(taskId);
|
|
205
|
+
console.log("Chat cancelled");
|
|
206
|
+
}
|
|
207
|
+
}, 5000);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### React Integration
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { useState } from "react";
|
|
214
|
+
import { createCopilotClient } from "@amaster.ai/copilot-client";
|
|
215
|
+
|
|
216
|
+
const copilot = createCopilotClient();
|
|
217
|
+
|
|
218
|
+
function ChatComponent() {
|
|
219
|
+
const [message, setMessage] = useState("");
|
|
220
|
+
const [response, setResponse] = useState("");
|
|
221
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
222
|
+
const [taskId, setTaskId] = useState<string>();
|
|
223
|
+
|
|
224
|
+
const handleSend = async () => {
|
|
225
|
+
setIsLoading(true);
|
|
226
|
+
setResponse("");
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
for await (const chunk of copilot.chat([
|
|
230
|
+
{ role: "user", content: message }
|
|
231
|
+
])) {
|
|
232
|
+
setTaskId(chunk.taskId);
|
|
233
|
+
setResponse(prev => prev + chunk.text);
|
|
234
|
+
}
|
|
235
|
+
} finally {
|
|
236
|
+
setIsLoading(false);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const handleCancel = async () => {
|
|
241
|
+
if (taskId) {
|
|
242
|
+
await copilot.cancelChat(taskId);
|
|
243
|
+
setIsLoading(false);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div>
|
|
249
|
+
<input
|
|
250
|
+
value={message}
|
|
251
|
+
onChange={e => setMessage(e.target.value)}
|
|
252
|
+
disabled={isLoading}
|
|
253
|
+
/>
|
|
254
|
+
<button onClick={handleSend} disabled={isLoading}>
|
|
255
|
+
Send
|
|
256
|
+
</button>
|
|
257
|
+
{isLoading && (
|
|
258
|
+
<button onClick={handleCancel}>Cancel</button>
|
|
259
|
+
)}
|
|
260
|
+
<div>{response}</div>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Custom HTTP Client
|
|
267
|
+
|
|
268
|
+
You can provide your own pre-configured HTTP client:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { createHttpClient } from "@amaster.ai/http-client";
|
|
272
|
+
import axios from "axios";
|
|
273
|
+
|
|
274
|
+
// Configure axios instance
|
|
275
|
+
const axiosInstance = axios.create({
|
|
276
|
+
baseURL: "https://api.example.com",
|
|
277
|
+
timeout: 30000,
|
|
278
|
+
headers: {
|
|
279
|
+
"X-Custom-Header": "value"
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Create HTTP client with custom axios
|
|
284
|
+
const httpClient = createHttpClient(axiosInstance);
|
|
285
|
+
|
|
286
|
+
// Create copilot client with custom HTTP client
|
|
287
|
+
const copilot = createCopilotClient(httpClient);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Error Handling
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
try {
|
|
294
|
+
for await (const chunk of copilot.chat([
|
|
295
|
+
{ role: "user", content: "Hello" }
|
|
296
|
+
])) {
|
|
297
|
+
console.log(chunk.text);
|
|
298
|
+
|
|
299
|
+
// Check for errors in status
|
|
300
|
+
if (chunk.status === "FAILED" && chunk.isFinal) {
|
|
301
|
+
console.error("Chat failed");
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error("Chat error:", error);
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## How It Works
|
|
311
|
+
|
|
312
|
+
### A2A Protocol
|
|
313
|
+
|
|
314
|
+
The client implements the A2A (Agent-to-Agent) protocol using JSON-RPC 2.0:
|
|
315
|
+
|
|
316
|
+
1. **Message Streaming**: `method: "message/stream"` - Send message and receive streaming response
|
|
317
|
+
2. **Task Cancellation**: `method: "tasks/cancel"` - Cancel an ongoing task
|
|
318
|
+
3. **Task Status**: `method: "tasks/get"` - Get task status
|
|
319
|
+
4. **Task Resubscription**: Reconnect to an existing task stream
|
|
320
|
+
|
|
321
|
+
### Auto Context Extraction
|
|
322
|
+
|
|
323
|
+
The client automatically extracts `contextId` from:
|
|
324
|
+
1. URL path: `/app/{app_id}/...`
|
|
325
|
+
2. Domain subdomain: `{app_id}-{env}.amaster.local`
|
|
326
|
+
|
|
327
|
+
This links conversations to the current app context.
|
|
328
|
+
|
|
329
|
+
### SSE Streaming
|
|
330
|
+
|
|
331
|
+
Uses `eventsource-parser` for robust SSE parsing:
|
|
332
|
+
- Handles reconnections
|
|
333
|
+
- Parses JSON payloads
|
|
334
|
+
- Manages stream lifecycle
|
|
335
|
+
|
|
336
|
+
## License
|
|
337
|
+
|
|
338
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdk = require('@a2a-js/sdk');
|
|
4
|
+
var eventsourceParser = require('eventsource-parser');
|
|
5
|
+
var httpClient = require('@amaster.ai/http-client');
|
|
6
|
+
|
|
7
|
+
// src/copilot-client.ts
|
|
8
|
+
function generateUUID() {
|
|
9
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
10
|
+
return crypto.randomUUID();
|
|
11
|
+
}
|
|
12
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
13
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
14
|
+
const randomValues = crypto.getRandomValues(new Uint8Array(1));
|
|
15
|
+
const r = (randomValues[0] ?? 0) % 16;
|
|
16
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
17
|
+
return v.toString(16);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
21
|
+
const r = Math.random() * 16 | 0;
|
|
22
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
23
|
+
return v.toString(16);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function extractAppIdFromUrl() {
|
|
27
|
+
if (typeof window === "undefined") {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const url = window.location.href;
|
|
32
|
+
const pathMatch = /\/app\/([\da-f-]+)(?:\/|$)/.exec(url);
|
|
33
|
+
if (pathMatch && pathMatch[1]) {
|
|
34
|
+
return pathMatch[1];
|
|
35
|
+
}
|
|
36
|
+
const hostname = window.location.hostname;
|
|
37
|
+
const domainMatch = /^([\da-f-]+)(?:-[^.]+)?\.amaster\.(?:local|ai)$/.exec(hostname);
|
|
38
|
+
if (domainMatch && domainMatch[1]) {
|
|
39
|
+
return domainMatch[1];
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function* streamSSEResponse(response) {
|
|
47
|
+
if (!response.body) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const reader = response.body.getReader();
|
|
51
|
+
const decoder = new TextDecoder();
|
|
52
|
+
const queue = [];
|
|
53
|
+
const parser = eventsourceParser.createParser({
|
|
54
|
+
onEvent: (event) => {
|
|
55
|
+
try {
|
|
56
|
+
queue.push(JSON.parse(event.data));
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
try {
|
|
62
|
+
while (true) {
|
|
63
|
+
const { done, value } = await reader.read();
|
|
64
|
+
if (done) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
68
|
+
while (queue.length > 0) {
|
|
69
|
+
const item = queue.shift();
|
|
70
|
+
if (item) {
|
|
71
|
+
yield item;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
reader.releaseLock();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function extractTextFromResponse(response) {
|
|
80
|
+
if ("error" in response) {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
const result = response.result;
|
|
84
|
+
if (!result) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
if ("kind" in result && result.kind === "status-update") {
|
|
88
|
+
const parts = result.status?.message?.parts;
|
|
89
|
+
if (!parts) {
|
|
90
|
+
return "";
|
|
91
|
+
}
|
|
92
|
+
return parts.filter((part) => part.kind === "text").map((part) => part.text).join("");
|
|
93
|
+
}
|
|
94
|
+
return "";
|
|
95
|
+
}
|
|
96
|
+
function parseChunk(response) {
|
|
97
|
+
if ("error" in response) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const result = response.result;
|
|
101
|
+
if (!result) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if ("kind" in result && result.kind === "status-update") {
|
|
105
|
+
return {
|
|
106
|
+
taskId: result.taskId,
|
|
107
|
+
text: extractTextFromResponse(response),
|
|
108
|
+
status: result.status.state,
|
|
109
|
+
isFinal: result.final ?? false
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function createCopilotClient(http = httpClient.createHttpClient()) {
|
|
115
|
+
const baseUrl = "/api/proxy/builtin/platform/copilot";
|
|
116
|
+
const internal = {
|
|
117
|
+
getAgentCard() {
|
|
118
|
+
return http.request({
|
|
119
|
+
url: `${baseUrl}/${sdk.AGENT_CARD_PATH}`,
|
|
120
|
+
method: "GET"
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
sendMessage(request) {
|
|
124
|
+
return http.request({
|
|
125
|
+
url: baseUrl,
|
|
126
|
+
method: "POST",
|
|
127
|
+
data: request
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
async *sendMessageStream(request) {
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch(baseUrl, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: {
|
|
135
|
+
"Content-Type": "application/json"
|
|
136
|
+
},
|
|
137
|
+
credentials: "include",
|
|
138
|
+
body: JSON.stringify(request)
|
|
139
|
+
});
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
142
|
+
}
|
|
143
|
+
yield* streamSSEResponse(response);
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
cancelTask(request) {
|
|
148
|
+
return http.request({
|
|
149
|
+
url: baseUrl,
|
|
150
|
+
method: "POST",
|
|
151
|
+
data: request
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
getTask(request) {
|
|
155
|
+
return http.request({
|
|
156
|
+
url: baseUrl,
|
|
157
|
+
method: "POST",
|
|
158
|
+
data: request
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
async *resubscribeTask(request) {
|
|
162
|
+
try {
|
|
163
|
+
const response = await fetch(baseUrl, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: {
|
|
166
|
+
"Content-Type": "application/json"
|
|
167
|
+
},
|
|
168
|
+
credentials: "include",
|
|
169
|
+
body: JSON.stringify(request)
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
173
|
+
}
|
|
174
|
+
yield* streamSSEResponse(response);
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
// ============ Simplified API Implementation ============
|
|
179
|
+
async *chat(messages, options = {}) {
|
|
180
|
+
const { taskId } = options;
|
|
181
|
+
const systemMsg = messages.find((m) => m.role === "system");
|
|
182
|
+
const nonSystemMsgs = messages.filter((m) => m.role !== "system");
|
|
183
|
+
const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];
|
|
184
|
+
const request = {
|
|
185
|
+
jsonrpc: "2.0",
|
|
186
|
+
id: generateUUID(),
|
|
187
|
+
method: "message/stream",
|
|
188
|
+
params: {
|
|
189
|
+
message: {
|
|
190
|
+
contextId: extractAppIdFromUrl() || generateUUID(),
|
|
191
|
+
kind: "message",
|
|
192
|
+
messageId: generateUUID(),
|
|
193
|
+
role: "user",
|
|
194
|
+
parts: [{ kind: "text", text: lastUserMsg?.content ?? "" }],
|
|
195
|
+
...taskId && { taskId },
|
|
196
|
+
...systemMsg && {
|
|
197
|
+
metadata: { systemPrompt: systemMsg.content }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
for await (const response of this.sendMessageStream(request)) {
|
|
203
|
+
const chunk = parseChunk(response);
|
|
204
|
+
if (chunk) {
|
|
205
|
+
yield chunk;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
cancelChat(taskId) {
|
|
210
|
+
const request = {
|
|
211
|
+
jsonrpc: "2.0",
|
|
212
|
+
id: generateUUID(),
|
|
213
|
+
method: "tasks/cancel",
|
|
214
|
+
params: { id: taskId }
|
|
215
|
+
};
|
|
216
|
+
return this.cancelTask(request);
|
|
217
|
+
},
|
|
218
|
+
getChatStatus(taskId) {
|
|
219
|
+
const request = {
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
id: generateUUID(),
|
|
222
|
+
method: "tasks/get",
|
|
223
|
+
params: { id: taskId }
|
|
224
|
+
};
|
|
225
|
+
return this.getTask(request);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
chat: internal.chat.bind(internal),
|
|
230
|
+
cancelChat: internal.cancelChat.bind(internal),
|
|
231
|
+
getChatStatus: internal.getChatStatus.bind(internal)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
exports.createCopilotClient = createCopilotClient;
|
|
236
|
+
//# sourceMappingURL=index.cjs.map
|
|
237
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/copilot-client.ts"],"names":["createParser","createHttpClient","AGENT_CARD_PATH"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n AgentCard,\n CancelTaskRequest,\n CancelTaskResponse,\n GetTaskRequest,\n GetTaskResponse,\n SendMessageRequest,\n SendMessageResponse,\n SendStreamingMessageRequest,\n SendStreamingMessageResponse,\n TaskResubscriptionRequest,\n TaskState,\n} from \"@a2a-js/sdk\";\nimport { AGENT_CARD_PATH } from \"@a2a-js/sdk\";\nimport { createParser } from \"eventsource-parser\";\nimport { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\n\n// ============ UUID Generator with fallback ============\n\n/**\n * Generate a UUID v4 with better browser compatibility\n * Falls back to crypto.getRandomValues if crypto.randomUUID is not available\n */\nfunction generateUUID(): string {\n // Try using crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback: use crypto.getRandomValues (better compatibility)\n if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const randomValues = crypto.getRandomValues(new Uint8Array(1));\n const r = (randomValues[0] ?? 0) % 16;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Extract app_id from current page URL or domain\n * Method 1: From URL path - /app/{app_id}/...\n * Method 2: From domain (subdomain) - {app_id}-{env}.amaster.local\n */\nfunction extractAppIdFromUrl(): string | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n try {\n // Method 1: Try to extract from URL path first\n const url = window.location.href;\n const pathMatch = /\\/app\\/([\\da-f-]+)(?:\\/|$)/.exec(url);\n if (pathMatch && pathMatch[1]) {\n return pathMatch[1];\n }\n\n // Method 2: Try to extract from domain (subdomain)\n const hostname = window.location.hostname;\n const domainMatch = /^([\\da-f-]+)(?:-[^.]+)?\\.amaster\\.(?:local|ai)$/.exec(hostname);\n if (domainMatch && domainMatch[1]) {\n return domainMatch[1];\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n// ============ Simplified API Types ============\n\nexport interface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface ChatOptions {\n taskId?: string;\n}\n\nexport interface ChatChunk {\n taskId: string;\n text: string;\n status: TaskState;\n isFinal: boolean | undefined;\n}\n\n// ============ Client Types ============\n\n// Internal client with all A2A methods\ntype InternalCopilotClient = {\n getAgentCard(): Promise<ClientResult<AgentCard>>;\n sendMessage(request: SendMessageRequest): Promise<ClientResult<SendMessageResponse>>;\n sendMessageStream(\n request: SendStreamingMessageRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n cancelTask(request: CancelTaskRequest): Promise<ClientResult<CancelTaskResponse>>;\n getTask(request: GetTaskRequest): Promise<ClientResult<GetTaskResponse>>;\n resubscribeTask(\n request: TaskResubscriptionRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\n// Public client with only simplified API\nexport type CopilotClient = {\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\nasync function* streamSSEResponse(\n response: Response\n): AsyncGenerator<SendStreamingMessageResponse, void, unknown> {\n if (!response.body) {\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n const queue: SendStreamingMessageResponse[] = [];\n\n const parser = createParser({\n onEvent: (event) => {\n try {\n queue.push(JSON.parse(event.data));\n } catch {\n // Ignore parse errors for malformed SSE data\n }\n },\n });\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n while (queue.length > 0) {\n const item = queue.shift();\n if (item) {\n yield item;\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction extractTextFromResponse(response: SendStreamingMessageResponse): string {\n if (\"error\" in response) {\n return \"\";\n }\n const result = response.result;\n if (!result) {\n return \"\";\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n const parts = result.status?.message?.parts;\n if (!parts) {\n return \"\";\n }\n return parts\n .filter((part): part is { kind: \"text\"; text: string } => part.kind === \"text\")\n .map((part) => part.text)\n .join(\"\");\n }\n return \"\";\n}\n\nfunction parseChunk(response: SendStreamingMessageResponse): ChatChunk | null {\n if (\"error\" in response) {\n return null;\n }\n const result = response.result;\n if (!result) {\n return null;\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n return {\n taskId: result.taskId,\n text: extractTextFromResponse(response),\n status: result.status.state,\n isFinal: result.final ?? false,\n };\n }\n return null;\n}\n\nexport function createCopilotClient(http: HttpClient = createHttpClient()): CopilotClient {\n const baseUrl = \"/api/proxy/builtin/platform/copilot\";\n\n const internal: InternalCopilotClient = {\n getAgentCard() {\n return http.request<AgentCard>({\n url: `${baseUrl}/${AGENT_CARD_PATH}`,\n method: \"GET\",\n });\n },\n\n sendMessage(request) {\n return http.request<SendMessageResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *sendMessageStream(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore stream errors - caller should handle connection issues\n }\n },\n\n cancelTask(request) {\n return http.request<CancelTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n getTask(request) {\n return http.request<GetTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *resubscribeTask(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore resubscribe errors - caller should handle reconnection\n }\n },\n\n // ============ Simplified API Implementation ============\n\n async *chat(messages, options = {}) {\n const { taskId } = options;\n\n const systemMsg = messages.find((m) => m.role === \"system\");\n const nonSystemMsgs = messages.filter((m) => m.role !== \"system\");\n const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];\n\n const request: SendStreamingMessageRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"message/stream\",\n params: {\n message: {\n contextId: extractAppIdFromUrl() || generateUUID(),\n kind: \"message\",\n messageId: generateUUID(),\n role: \"user\",\n parts: [{ kind: \"text\", text: lastUserMsg?.content ?? \"\" }],\n ...(taskId && { taskId }),\n ...(systemMsg && {\n metadata: { systemPrompt: systemMsg.content },\n }),\n },\n },\n };\n\n for await (const response of this.sendMessageStream(request)) {\n const chunk = parseChunk(response);\n if (chunk) {\n yield chunk;\n }\n }\n },\n\n cancelChat(taskId) {\n const request: CancelTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/cancel\",\n params: { id: taskId },\n };\n return this.cancelTask(request);\n },\n\n getChatStatus(taskId) {\n const request: GetTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/get\",\n params: { id: taskId },\n };\n return this.getTask(request);\n },\n };\n\n // Return only the public API\n return {\n chat: internal.chat.bind(internal),\n cancelChat: internal.cancelChat.bind(internal),\n getChatStatus: internal.getChatStatus.bind(internal),\n };\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TaskState, CancelTaskResponse, GetTaskResponse } from '@a2a-js/sdk';
|
|
2
|
+
import { ClientResult, HttpClient } from '@amaster.ai/http-client';
|
|
3
|
+
|
|
4
|
+
interface ChatMessage {
|
|
5
|
+
role: "system" | "user" | "assistant";
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
interface ChatOptions {
|
|
9
|
+
taskId?: string;
|
|
10
|
+
}
|
|
11
|
+
interface ChatChunk {
|
|
12
|
+
taskId: string;
|
|
13
|
+
text: string;
|
|
14
|
+
status: TaskState;
|
|
15
|
+
isFinal: boolean | undefined;
|
|
16
|
+
}
|
|
17
|
+
type CopilotClient = {
|
|
18
|
+
chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;
|
|
19
|
+
cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;
|
|
20
|
+
getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;
|
|
21
|
+
};
|
|
22
|
+
declare function createCopilotClient(http?: HttpClient): CopilotClient;
|
|
23
|
+
|
|
24
|
+
export { type ChatChunk, type ChatMessage, type ChatOptions, type CopilotClient, createCopilotClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TaskState, CancelTaskResponse, GetTaskResponse } from '@a2a-js/sdk';
|
|
2
|
+
import { ClientResult, HttpClient } from '@amaster.ai/http-client';
|
|
3
|
+
|
|
4
|
+
interface ChatMessage {
|
|
5
|
+
role: "system" | "user" | "assistant";
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
interface ChatOptions {
|
|
9
|
+
taskId?: string;
|
|
10
|
+
}
|
|
11
|
+
interface ChatChunk {
|
|
12
|
+
taskId: string;
|
|
13
|
+
text: string;
|
|
14
|
+
status: TaskState;
|
|
15
|
+
isFinal: boolean | undefined;
|
|
16
|
+
}
|
|
17
|
+
type CopilotClient = {
|
|
18
|
+
chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;
|
|
19
|
+
cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;
|
|
20
|
+
getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;
|
|
21
|
+
};
|
|
22
|
+
declare function createCopilotClient(http?: HttpClient): CopilotClient;
|
|
23
|
+
|
|
24
|
+
export { type ChatChunk, type ChatMessage, type ChatOptions, type CopilotClient, createCopilotClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { AGENT_CARD_PATH } from '@a2a-js/sdk';
|
|
2
|
+
import { createParser } from 'eventsource-parser';
|
|
3
|
+
import { createHttpClient } from '@amaster.ai/http-client';
|
|
4
|
+
|
|
5
|
+
// src/copilot-client.ts
|
|
6
|
+
function generateUUID() {
|
|
7
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
8
|
+
return crypto.randomUUID();
|
|
9
|
+
}
|
|
10
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
11
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
12
|
+
const randomValues = crypto.getRandomValues(new Uint8Array(1));
|
|
13
|
+
const r = (randomValues[0] ?? 0) % 16;
|
|
14
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
15
|
+
return v.toString(16);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
19
|
+
const r = Math.random() * 16 | 0;
|
|
20
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
21
|
+
return v.toString(16);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function extractAppIdFromUrl() {
|
|
25
|
+
if (typeof window === "undefined") {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const url = window.location.href;
|
|
30
|
+
const pathMatch = /\/app\/([\da-f-]+)(?:\/|$)/.exec(url);
|
|
31
|
+
if (pathMatch && pathMatch[1]) {
|
|
32
|
+
return pathMatch[1];
|
|
33
|
+
}
|
|
34
|
+
const hostname = window.location.hostname;
|
|
35
|
+
const domainMatch = /^([\da-f-]+)(?:-[^.]+)?\.amaster\.(?:local|ai)$/.exec(hostname);
|
|
36
|
+
if (domainMatch && domainMatch[1]) {
|
|
37
|
+
return domainMatch[1];
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function* streamSSEResponse(response) {
|
|
45
|
+
if (!response.body) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const reader = response.body.getReader();
|
|
49
|
+
const decoder = new TextDecoder();
|
|
50
|
+
const queue = [];
|
|
51
|
+
const parser = createParser({
|
|
52
|
+
onEvent: (event) => {
|
|
53
|
+
try {
|
|
54
|
+
queue.push(JSON.parse(event.data));
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
try {
|
|
60
|
+
while (true) {
|
|
61
|
+
const { done, value } = await reader.read();
|
|
62
|
+
if (done) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
66
|
+
while (queue.length > 0) {
|
|
67
|
+
const item = queue.shift();
|
|
68
|
+
if (item) {
|
|
69
|
+
yield item;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
reader.releaseLock();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function extractTextFromResponse(response) {
|
|
78
|
+
if ("error" in response) {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
const result = response.result;
|
|
82
|
+
if (!result) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
if ("kind" in result && result.kind === "status-update") {
|
|
86
|
+
const parts = result.status?.message?.parts;
|
|
87
|
+
if (!parts) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
return parts.filter((part) => part.kind === "text").map((part) => part.text).join("");
|
|
91
|
+
}
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
function parseChunk(response) {
|
|
95
|
+
if ("error" in response) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const result = response.result;
|
|
99
|
+
if (!result) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
if ("kind" in result && result.kind === "status-update") {
|
|
103
|
+
return {
|
|
104
|
+
taskId: result.taskId,
|
|
105
|
+
text: extractTextFromResponse(response),
|
|
106
|
+
status: result.status.state,
|
|
107
|
+
isFinal: result.final ?? false
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function createCopilotClient(http = createHttpClient()) {
|
|
113
|
+
const baseUrl = "/api/proxy/builtin/platform/copilot";
|
|
114
|
+
const internal = {
|
|
115
|
+
getAgentCard() {
|
|
116
|
+
return http.request({
|
|
117
|
+
url: `${baseUrl}/${AGENT_CARD_PATH}`,
|
|
118
|
+
method: "GET"
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
sendMessage(request) {
|
|
122
|
+
return http.request({
|
|
123
|
+
url: baseUrl,
|
|
124
|
+
method: "POST",
|
|
125
|
+
data: request
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
async *sendMessageStream(request) {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(baseUrl, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
"Content-Type": "application/json"
|
|
134
|
+
},
|
|
135
|
+
credentials: "include",
|
|
136
|
+
body: JSON.stringify(request)
|
|
137
|
+
});
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
140
|
+
}
|
|
141
|
+
yield* streamSSEResponse(response);
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
cancelTask(request) {
|
|
146
|
+
return http.request({
|
|
147
|
+
url: baseUrl,
|
|
148
|
+
method: "POST",
|
|
149
|
+
data: request
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
getTask(request) {
|
|
153
|
+
return http.request({
|
|
154
|
+
url: baseUrl,
|
|
155
|
+
method: "POST",
|
|
156
|
+
data: request
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
async *resubscribeTask(request) {
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch(baseUrl, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/json"
|
|
165
|
+
},
|
|
166
|
+
credentials: "include",
|
|
167
|
+
body: JSON.stringify(request)
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
171
|
+
}
|
|
172
|
+
yield* streamSSEResponse(response);
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
// ============ Simplified API Implementation ============
|
|
177
|
+
async *chat(messages, options = {}) {
|
|
178
|
+
const { taskId } = options;
|
|
179
|
+
const systemMsg = messages.find((m) => m.role === "system");
|
|
180
|
+
const nonSystemMsgs = messages.filter((m) => m.role !== "system");
|
|
181
|
+
const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];
|
|
182
|
+
const request = {
|
|
183
|
+
jsonrpc: "2.0",
|
|
184
|
+
id: generateUUID(),
|
|
185
|
+
method: "message/stream",
|
|
186
|
+
params: {
|
|
187
|
+
message: {
|
|
188
|
+
contextId: extractAppIdFromUrl() || generateUUID(),
|
|
189
|
+
kind: "message",
|
|
190
|
+
messageId: generateUUID(),
|
|
191
|
+
role: "user",
|
|
192
|
+
parts: [{ kind: "text", text: lastUserMsg?.content ?? "" }],
|
|
193
|
+
...taskId && { taskId },
|
|
194
|
+
...systemMsg && {
|
|
195
|
+
metadata: { systemPrompt: systemMsg.content }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
for await (const response of this.sendMessageStream(request)) {
|
|
201
|
+
const chunk = parseChunk(response);
|
|
202
|
+
if (chunk) {
|
|
203
|
+
yield chunk;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
cancelChat(taskId) {
|
|
208
|
+
const request = {
|
|
209
|
+
jsonrpc: "2.0",
|
|
210
|
+
id: generateUUID(),
|
|
211
|
+
method: "tasks/cancel",
|
|
212
|
+
params: { id: taskId }
|
|
213
|
+
};
|
|
214
|
+
return this.cancelTask(request);
|
|
215
|
+
},
|
|
216
|
+
getChatStatus(taskId) {
|
|
217
|
+
const request = {
|
|
218
|
+
jsonrpc: "2.0",
|
|
219
|
+
id: generateUUID(),
|
|
220
|
+
method: "tasks/get",
|
|
221
|
+
params: { id: taskId }
|
|
222
|
+
};
|
|
223
|
+
return this.getTask(request);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
return {
|
|
227
|
+
chat: internal.chat.bind(internal),
|
|
228
|
+
cancelChat: internal.cancelChat.bind(internal),
|
|
229
|
+
getChatStatus: internal.getChatStatus.bind(internal)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export { createCopilotClient };
|
|
234
|
+
//# sourceMappingURL=index.js.map
|
|
235
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/copilot-client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AgentCard,\n CancelTaskRequest,\n CancelTaskResponse,\n GetTaskRequest,\n GetTaskResponse,\n SendMessageRequest,\n SendMessageResponse,\n SendStreamingMessageRequest,\n SendStreamingMessageResponse,\n TaskResubscriptionRequest,\n TaskState,\n} from \"@a2a-js/sdk\";\nimport { AGENT_CARD_PATH } from \"@a2a-js/sdk\";\nimport { createParser } from \"eventsource-parser\";\nimport { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\n\n// ============ UUID Generator with fallback ============\n\n/**\n * Generate a UUID v4 with better browser compatibility\n * Falls back to crypto.getRandomValues if crypto.randomUUID is not available\n */\nfunction generateUUID(): string {\n // Try using crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback: use crypto.getRandomValues (better compatibility)\n if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const randomValues = crypto.getRandomValues(new Uint8Array(1));\n const r = (randomValues[0] ?? 0) % 16;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Extract app_id from current page URL or domain\n * Method 1: From URL path - /app/{app_id}/...\n * Method 2: From domain (subdomain) - {app_id}-{env}.amaster.local\n */\nfunction extractAppIdFromUrl(): string | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n try {\n // Method 1: Try to extract from URL path first\n const url = window.location.href;\n const pathMatch = /\\/app\\/([\\da-f-]+)(?:\\/|$)/.exec(url);\n if (pathMatch && pathMatch[1]) {\n return pathMatch[1];\n }\n\n // Method 2: Try to extract from domain (subdomain)\n const hostname = window.location.hostname;\n const domainMatch = /^([\\da-f-]+)(?:-[^.]+)?\\.amaster\\.(?:local|ai)$/.exec(hostname);\n if (domainMatch && domainMatch[1]) {\n return domainMatch[1];\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n// ============ Simplified API Types ============\n\nexport interface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface ChatOptions {\n taskId?: string;\n}\n\nexport interface ChatChunk {\n taskId: string;\n text: string;\n status: TaskState;\n isFinal: boolean | undefined;\n}\n\n// ============ Client Types ============\n\n// Internal client with all A2A methods\ntype InternalCopilotClient = {\n getAgentCard(): Promise<ClientResult<AgentCard>>;\n sendMessage(request: SendMessageRequest): Promise<ClientResult<SendMessageResponse>>;\n sendMessageStream(\n request: SendStreamingMessageRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n cancelTask(request: CancelTaskRequest): Promise<ClientResult<CancelTaskResponse>>;\n getTask(request: GetTaskRequest): Promise<ClientResult<GetTaskResponse>>;\n resubscribeTask(\n request: TaskResubscriptionRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\n// Public client with only simplified API\nexport type CopilotClient = {\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\nasync function* streamSSEResponse(\n response: Response\n): AsyncGenerator<SendStreamingMessageResponse, void, unknown> {\n if (!response.body) {\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n const queue: SendStreamingMessageResponse[] = [];\n\n const parser = createParser({\n onEvent: (event) => {\n try {\n queue.push(JSON.parse(event.data));\n } catch {\n // Ignore parse errors for malformed SSE data\n }\n },\n });\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n while (queue.length > 0) {\n const item = queue.shift();\n if (item) {\n yield item;\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction extractTextFromResponse(response: SendStreamingMessageResponse): string {\n if (\"error\" in response) {\n return \"\";\n }\n const result = response.result;\n if (!result) {\n return \"\";\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n const parts = result.status?.message?.parts;\n if (!parts) {\n return \"\";\n }\n return parts\n .filter((part): part is { kind: \"text\"; text: string } => part.kind === \"text\")\n .map((part) => part.text)\n .join(\"\");\n }\n return \"\";\n}\n\nfunction parseChunk(response: SendStreamingMessageResponse): ChatChunk | null {\n if (\"error\" in response) {\n return null;\n }\n const result = response.result;\n if (!result) {\n return null;\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n return {\n taskId: result.taskId,\n text: extractTextFromResponse(response),\n status: result.status.state,\n isFinal: result.final ?? false,\n };\n }\n return null;\n}\n\nexport function createCopilotClient(http: HttpClient = createHttpClient()): CopilotClient {\n const baseUrl = \"/api/proxy/builtin/platform/copilot\";\n\n const internal: InternalCopilotClient = {\n getAgentCard() {\n return http.request<AgentCard>({\n url: `${baseUrl}/${AGENT_CARD_PATH}`,\n method: \"GET\",\n });\n },\n\n sendMessage(request) {\n return http.request<SendMessageResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *sendMessageStream(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore stream errors - caller should handle connection issues\n }\n },\n\n cancelTask(request) {\n return http.request<CancelTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n getTask(request) {\n return http.request<GetTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *resubscribeTask(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore resubscribe errors - caller should handle reconnection\n }\n },\n\n // ============ Simplified API Implementation ============\n\n async *chat(messages, options = {}) {\n const { taskId } = options;\n\n const systemMsg = messages.find((m) => m.role === \"system\");\n const nonSystemMsgs = messages.filter((m) => m.role !== \"system\");\n const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];\n\n const request: SendStreamingMessageRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"message/stream\",\n params: {\n message: {\n contextId: extractAppIdFromUrl() || generateUUID(),\n kind: \"message\",\n messageId: generateUUID(),\n role: \"user\",\n parts: [{ kind: \"text\", text: lastUserMsg?.content ?? \"\" }],\n ...(taskId && { taskId }),\n ...(systemMsg && {\n metadata: { systemPrompt: systemMsg.content },\n }),\n },\n },\n };\n\n for await (const response of this.sendMessageStream(request)) {\n const chunk = parseChunk(response);\n if (chunk) {\n yield chunk;\n }\n }\n },\n\n cancelChat(taskId) {\n const request: CancelTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/cancel\",\n params: { id: taskId },\n };\n return this.cancelTask(request);\n },\n\n getChatStatus(taskId) {\n const request: GetTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/get\",\n params: { id: taskId },\n };\n return this.getTask(request);\n },\n };\n\n // Return only the public API\n return {\n chat: internal.chat.bind(internal),\n cancelChat: internal.cancelChat.bind(internal),\n getChatStatus: internal.getChatStatus.bind(internal),\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amaster.ai/copilot-client",
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
4
|
+
"description": "AI copilot chat client with streaming support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"ai",
|
|
22
|
+
"copilot",
|
|
23
|
+
"chat",
|
|
24
|
+
"streaming",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"author": "Amaster Team",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"registry": "https://registry.npmjs.org/"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@amaster.ai/http-client": "1.0.0-beta.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@a2a-js/sdk": "^0.3.7",
|
|
38
|
+
"axios": "^1.11.0",
|
|
39
|
+
"eventsource-parser": "^3.0.6"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@a2a-js/sdk": "^0.3.7",
|
|
43
|
+
"axios": "^1.11.0",
|
|
44
|
+
"eventsource-parser": "^3.0.6",
|
|
45
|
+
"tsup": "^8.3.5",
|
|
46
|
+
"typescript": "~5.7.2"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsup --watch",
|
|
51
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
52
|
+
"type-check": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|