@syncagent/js 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 +60 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +183 -0
- package/dist/index.mjs +156 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @syncagent/js
|
|
2
|
+
|
|
3
|
+
Core JavaScript SDK for SyncAgent — add an AI database agent to any app.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @syncagent/js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SyncAgentClient } from "@syncagent/js";
|
|
15
|
+
|
|
16
|
+
const agent = new SyncAgentClient({
|
|
17
|
+
apiKey: "sa_your_api_key",
|
|
18
|
+
baseUrl: "https://your-syncagent-instance.com",
|
|
19
|
+
connectionString: process.env.DATABASE_URL, // your DB — never stored on our servers
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Streaming chat
|
|
23
|
+
await agent.chat(
|
|
24
|
+
[{ role: "user", content: "Show me all active users" }],
|
|
25
|
+
{
|
|
26
|
+
onToken: (token) => process.stdout.write(token),
|
|
27
|
+
onComplete: (text) => console.log("\n\nDone:", text),
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Non-streaming
|
|
32
|
+
const result = await agent.chat([{ role: "user", content: "Count all orders" }]);
|
|
33
|
+
console.log(result.text);
|
|
34
|
+
|
|
35
|
+
// Get database schema
|
|
36
|
+
const schema = await agent.getSchema();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
### `new SyncAgentClient(config)`
|
|
42
|
+
|
|
43
|
+
| Option | Type | Required | Description |
|
|
44
|
+
| ------------------ | ------ | -------- | ---------------------------------------------- |
|
|
45
|
+
| `apiKey` | string | ✅ | Your SyncAgent API key |
|
|
46
|
+
| `baseUrl` | string | ✅ | Your SyncAgent instance URL |
|
|
47
|
+
| `connectionString` | string | ✅ | Your database URL (sent at runtime, not stored) |
|
|
48
|
+
|
|
49
|
+
### `client.chat(messages, options?)`
|
|
50
|
+
|
|
51
|
+
| Option | Type | Description |
|
|
52
|
+
| ------------ | -------------------------- | ------------------------ |
|
|
53
|
+
| `onToken` | `(token: string) => void` | Called on each text chunk |
|
|
54
|
+
| `onComplete` | `(text: string) => void` | Called when done |
|
|
55
|
+
| `onError` | `(error: Error) => void` | Called on error |
|
|
56
|
+
| `signal` | `AbortSignal` | For cancellation |
|
|
57
|
+
|
|
58
|
+
### `client.getSchema()`
|
|
59
|
+
|
|
60
|
+
Returns `Promise<CollectionSchema[]>`.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
interface ToolParameter {
|
|
2
|
+
type: "string" | "number" | "boolean" | "object" | "array";
|
|
3
|
+
description?: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
enum?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface ToolDefinition {
|
|
8
|
+
description: string;
|
|
9
|
+
inputSchema: Record<string, ToolParameter>;
|
|
10
|
+
execute: (args: Record<string, any>) => any | Promise<any>;
|
|
11
|
+
}
|
|
12
|
+
interface SyncAgentConfig {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
/** Your database connection string — sent at runtime, never stored on SyncAgent servers */
|
|
15
|
+
connectionString: string;
|
|
16
|
+
/** Custom tools the AI agent can call — executed client-side in your app */
|
|
17
|
+
tools?: Record<string, ToolDefinition>;
|
|
18
|
+
}
|
|
19
|
+
interface Message {
|
|
20
|
+
role: "user" | "assistant";
|
|
21
|
+
content: string;
|
|
22
|
+
}
|
|
23
|
+
interface ChatOptions {
|
|
24
|
+
onToken?: (token: string) => void;
|
|
25
|
+
onComplete?: (text: string) => void;
|
|
26
|
+
onError?: (error: Error) => void;
|
|
27
|
+
onToolCall?: (toolName: string, args: Record<string, any>, result: any) => void;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
interface ChatResult {
|
|
31
|
+
text: string;
|
|
32
|
+
}
|
|
33
|
+
interface SchemaField {
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
sampleValue?: any;
|
|
37
|
+
}
|
|
38
|
+
interface CollectionSchema {
|
|
39
|
+
name: string;
|
|
40
|
+
fields: SchemaField[];
|
|
41
|
+
documentCount?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare class SyncAgentClient {
|
|
45
|
+
private apiKey;
|
|
46
|
+
private baseUrl;
|
|
47
|
+
private connectionString;
|
|
48
|
+
private tools;
|
|
49
|
+
constructor(config: SyncAgentConfig);
|
|
50
|
+
private headers;
|
|
51
|
+
private toWireMessages;
|
|
52
|
+
/** Serialize tool definitions for the API (strips execute functions) */
|
|
53
|
+
private serializeTools;
|
|
54
|
+
chat(messages: Message[], options?: ChatOptions): Promise<ChatResult>;
|
|
55
|
+
private chatLoop;
|
|
56
|
+
getSchema(): Promise<CollectionSchema[]>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { type ChatOptions, type ChatResult, type CollectionSchema, type Message, type SchemaField, SyncAgentClient, type SyncAgentConfig, type ToolDefinition, type ToolParameter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
interface ToolParameter {
|
|
2
|
+
type: "string" | "number" | "boolean" | "object" | "array";
|
|
3
|
+
description?: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
enum?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface ToolDefinition {
|
|
8
|
+
description: string;
|
|
9
|
+
inputSchema: Record<string, ToolParameter>;
|
|
10
|
+
execute: (args: Record<string, any>) => any | Promise<any>;
|
|
11
|
+
}
|
|
12
|
+
interface SyncAgentConfig {
|
|
13
|
+
apiKey: string;
|
|
14
|
+
/** Your database connection string — sent at runtime, never stored on SyncAgent servers */
|
|
15
|
+
connectionString: string;
|
|
16
|
+
/** Custom tools the AI agent can call — executed client-side in your app */
|
|
17
|
+
tools?: Record<string, ToolDefinition>;
|
|
18
|
+
}
|
|
19
|
+
interface Message {
|
|
20
|
+
role: "user" | "assistant";
|
|
21
|
+
content: string;
|
|
22
|
+
}
|
|
23
|
+
interface ChatOptions {
|
|
24
|
+
onToken?: (token: string) => void;
|
|
25
|
+
onComplete?: (text: string) => void;
|
|
26
|
+
onError?: (error: Error) => void;
|
|
27
|
+
onToolCall?: (toolName: string, args: Record<string, any>, result: any) => void;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
interface ChatResult {
|
|
31
|
+
text: string;
|
|
32
|
+
}
|
|
33
|
+
interface SchemaField {
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
sampleValue?: any;
|
|
37
|
+
}
|
|
38
|
+
interface CollectionSchema {
|
|
39
|
+
name: string;
|
|
40
|
+
fields: SchemaField[];
|
|
41
|
+
documentCount?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare class SyncAgentClient {
|
|
45
|
+
private apiKey;
|
|
46
|
+
private baseUrl;
|
|
47
|
+
private connectionString;
|
|
48
|
+
private tools;
|
|
49
|
+
constructor(config: SyncAgentConfig);
|
|
50
|
+
private headers;
|
|
51
|
+
private toWireMessages;
|
|
52
|
+
/** Serialize tool definitions for the API (strips execute functions) */
|
|
53
|
+
private serializeTools;
|
|
54
|
+
chat(messages: Message[], options?: ChatOptions): Promise<ChatResult>;
|
|
55
|
+
private chatLoop;
|
|
56
|
+
getSchema(): Promise<CollectionSchema[]>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { type ChatOptions, type ChatResult, type CollectionSchema, type Message, type SchemaField, SyncAgentClient, type SyncAgentConfig, type ToolDefinition, type ToolParameter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SyncAgentClient: () => SyncAgentClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/stream.ts
|
|
28
|
+
function parseDataStreamChunk(chunk) {
|
|
29
|
+
const tokens = [];
|
|
30
|
+
const lines = chunk.split("\n");
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
if (!trimmed || !trimmed.startsWith("0:")) continue;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(trimmed.substring(2));
|
|
36
|
+
if (typeof parsed === "string") tokens.push(parsed);
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return tokens;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/client.ts
|
|
44
|
+
var SYNCAGENT_API_URL = "https://syncagentdev.vercel.app";
|
|
45
|
+
var SyncAgentClient = class {
|
|
46
|
+
constructor(config) {
|
|
47
|
+
if (!config.apiKey) throw new Error("SyncAgent: apiKey is required");
|
|
48
|
+
if (!config.connectionString) throw new Error("SyncAgent: connectionString is required");
|
|
49
|
+
this.apiKey = config.apiKey;
|
|
50
|
+
this.baseUrl = SYNCAGENT_API_URL;
|
|
51
|
+
this.connectionString = config.connectionString;
|
|
52
|
+
this.tools = config.tools || {};
|
|
53
|
+
}
|
|
54
|
+
headers() {
|
|
55
|
+
return {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
toWireMessages(messages) {
|
|
61
|
+
return messages.map((m) => ({
|
|
62
|
+
id: Math.random().toString(36).slice(2),
|
|
63
|
+
role: m.role,
|
|
64
|
+
parts: [{ type: "text", text: m.content }]
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
/** Serialize tool definitions for the API (strips execute functions) */
|
|
68
|
+
serializeTools() {
|
|
69
|
+
if (!Object.keys(this.tools).length) return void 0;
|
|
70
|
+
const serialized = {};
|
|
71
|
+
for (const [name, def] of Object.entries(this.tools)) {
|
|
72
|
+
serialized[name] = { description: def.description, inputSchema: def.inputSchema };
|
|
73
|
+
}
|
|
74
|
+
return serialized;
|
|
75
|
+
}
|
|
76
|
+
async chat(messages, options = {}) {
|
|
77
|
+
const { onToken, onComplete, onError, onToolCall, signal } = options;
|
|
78
|
+
const wireMessages = this.toWireMessages(messages);
|
|
79
|
+
const clientTools = this.serializeTools();
|
|
80
|
+
try {
|
|
81
|
+
const fullText = await this.chatLoop(wireMessages, clientTools, { onToken, onToolCall, signal });
|
|
82
|
+
onComplete?.(fullText);
|
|
83
|
+
return { text: fullText };
|
|
84
|
+
} catch (e) {
|
|
85
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
86
|
+
onError?.(error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async chatLoop(wireMessages, clientTools, opts, maxRounds = 10) {
|
|
91
|
+
let fullText = "";
|
|
92
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
93
|
+
const res = await fetch(`${this.baseUrl}/api/v1/chat`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: this.headers(),
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
messages: wireMessages,
|
|
98
|
+
connectionString: this.connectionString,
|
|
99
|
+
...clientTools && { clientTools }
|
|
100
|
+
}),
|
|
101
|
+
signal: opts.signal
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
105
|
+
throw new Error(err.error || `HTTP ${res.status}`);
|
|
106
|
+
}
|
|
107
|
+
const reader = res.body?.getReader();
|
|
108
|
+
if (!reader) throw new Error("No response body");
|
|
109
|
+
const decoder = new TextDecoder();
|
|
110
|
+
let roundText = "";
|
|
111
|
+
let pendingToolCalls = [];
|
|
112
|
+
while (true) {
|
|
113
|
+
const { done, value } = await reader.read();
|
|
114
|
+
if (done) break;
|
|
115
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
116
|
+
for (const line of chunk.split("\n")) {
|
|
117
|
+
if (line.startsWith("a:")) {
|
|
118
|
+
try {
|
|
119
|
+
const data = JSON.parse(line.slice(2));
|
|
120
|
+
if (data.type === "client_tool_call") {
|
|
121
|
+
pendingToolCalls.push({ id: data.id, name: data.name, args: data.args });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const tokens = parseDataStreamChunk(chunk);
|
|
129
|
+
for (const token of tokens) {
|
|
130
|
+
roundText += token;
|
|
131
|
+
fullText += token;
|
|
132
|
+
opts.onToken?.(token);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!pendingToolCalls.length) return fullText;
|
|
136
|
+
const toolResults = [];
|
|
137
|
+
for (const tc of pendingToolCalls) {
|
|
138
|
+
const toolDef = this.tools[tc.name];
|
|
139
|
+
if (!toolDef) {
|
|
140
|
+
toolResults.push({ id: tc.id, name: tc.name, result: { error: `Tool "${tc.name}" not found on client` } });
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const result = await toolDef.execute(tc.args);
|
|
145
|
+
opts.onToolCall?.(tc.name, tc.args, result);
|
|
146
|
+
toolResults.push({ id: tc.id, name: tc.name, result });
|
|
147
|
+
} catch (e) {
|
|
148
|
+
const errResult = { error: e.message || String(e) };
|
|
149
|
+
opts.onToolCall?.(tc.name, tc.args, errResult);
|
|
150
|
+
toolResults.push({ id: tc.id, name: tc.name, result: errResult });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
wireMessages.push({
|
|
154
|
+
id: Math.random().toString(36).slice(2),
|
|
155
|
+
role: "tool",
|
|
156
|
+
parts: toolResults.map((tr) => ({
|
|
157
|
+
type: "tool-result",
|
|
158
|
+
toolCallId: tr.id,
|
|
159
|
+
toolName: tr.name,
|
|
160
|
+
result: tr.result
|
|
161
|
+
}))
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return fullText;
|
|
165
|
+
}
|
|
166
|
+
async getSchema() {
|
|
167
|
+
const res = await fetch(`${this.baseUrl}/api/v1/schema`, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: this.headers(),
|
|
170
|
+
body: JSON.stringify({ connectionString: this.connectionString })
|
|
171
|
+
});
|
|
172
|
+
if (!res.ok) {
|
|
173
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
174
|
+
throw new Error(err.error || `HTTP ${res.status}`);
|
|
175
|
+
}
|
|
176
|
+
const data = await res.json();
|
|
177
|
+
return data.collections;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
SyncAgentClient
|
|
183
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// src/stream.ts
|
|
2
|
+
function parseDataStreamChunk(chunk) {
|
|
3
|
+
const tokens = [];
|
|
4
|
+
const lines = chunk.split("\n");
|
|
5
|
+
for (const line of lines) {
|
|
6
|
+
const trimmed = line.trim();
|
|
7
|
+
if (!trimmed || !trimmed.startsWith("0:")) continue;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(trimmed.substring(2));
|
|
10
|
+
if (typeof parsed === "string") tokens.push(parsed);
|
|
11
|
+
} catch {
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return tokens;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/client.ts
|
|
18
|
+
var SYNCAGENT_API_URL = "https://syncagentdev.vercel.app";
|
|
19
|
+
var SyncAgentClient = class {
|
|
20
|
+
constructor(config) {
|
|
21
|
+
if (!config.apiKey) throw new Error("SyncAgent: apiKey is required");
|
|
22
|
+
if (!config.connectionString) throw new Error("SyncAgent: connectionString is required");
|
|
23
|
+
this.apiKey = config.apiKey;
|
|
24
|
+
this.baseUrl = SYNCAGENT_API_URL;
|
|
25
|
+
this.connectionString = config.connectionString;
|
|
26
|
+
this.tools = config.tools || {};
|
|
27
|
+
}
|
|
28
|
+
headers() {
|
|
29
|
+
return {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
toWireMessages(messages) {
|
|
35
|
+
return messages.map((m) => ({
|
|
36
|
+
id: Math.random().toString(36).slice(2),
|
|
37
|
+
role: m.role,
|
|
38
|
+
parts: [{ type: "text", text: m.content }]
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
/** Serialize tool definitions for the API (strips execute functions) */
|
|
42
|
+
serializeTools() {
|
|
43
|
+
if (!Object.keys(this.tools).length) return void 0;
|
|
44
|
+
const serialized = {};
|
|
45
|
+
for (const [name, def] of Object.entries(this.tools)) {
|
|
46
|
+
serialized[name] = { description: def.description, inputSchema: def.inputSchema };
|
|
47
|
+
}
|
|
48
|
+
return serialized;
|
|
49
|
+
}
|
|
50
|
+
async chat(messages, options = {}) {
|
|
51
|
+
const { onToken, onComplete, onError, onToolCall, signal } = options;
|
|
52
|
+
const wireMessages = this.toWireMessages(messages);
|
|
53
|
+
const clientTools = this.serializeTools();
|
|
54
|
+
try {
|
|
55
|
+
const fullText = await this.chatLoop(wireMessages, clientTools, { onToken, onToolCall, signal });
|
|
56
|
+
onComplete?.(fullText);
|
|
57
|
+
return { text: fullText };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
60
|
+
onError?.(error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async chatLoop(wireMessages, clientTools, opts, maxRounds = 10) {
|
|
65
|
+
let fullText = "";
|
|
66
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
67
|
+
const res = await fetch(`${this.baseUrl}/api/v1/chat`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: this.headers(),
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
messages: wireMessages,
|
|
72
|
+
connectionString: this.connectionString,
|
|
73
|
+
...clientTools && { clientTools }
|
|
74
|
+
}),
|
|
75
|
+
signal: opts.signal
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
79
|
+
throw new Error(err.error || `HTTP ${res.status}`);
|
|
80
|
+
}
|
|
81
|
+
const reader = res.body?.getReader();
|
|
82
|
+
if (!reader) throw new Error("No response body");
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
let roundText = "";
|
|
85
|
+
let pendingToolCalls = [];
|
|
86
|
+
while (true) {
|
|
87
|
+
const { done, value } = await reader.read();
|
|
88
|
+
if (done) break;
|
|
89
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
90
|
+
for (const line of chunk.split("\n")) {
|
|
91
|
+
if (line.startsWith("a:")) {
|
|
92
|
+
try {
|
|
93
|
+
const data = JSON.parse(line.slice(2));
|
|
94
|
+
if (data.type === "client_tool_call") {
|
|
95
|
+
pendingToolCalls.push({ id: data.id, name: data.name, args: data.args });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const tokens = parseDataStreamChunk(chunk);
|
|
103
|
+
for (const token of tokens) {
|
|
104
|
+
roundText += token;
|
|
105
|
+
fullText += token;
|
|
106
|
+
opts.onToken?.(token);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!pendingToolCalls.length) return fullText;
|
|
110
|
+
const toolResults = [];
|
|
111
|
+
for (const tc of pendingToolCalls) {
|
|
112
|
+
const toolDef = this.tools[tc.name];
|
|
113
|
+
if (!toolDef) {
|
|
114
|
+
toolResults.push({ id: tc.id, name: tc.name, result: { error: `Tool "${tc.name}" not found on client` } });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const result = await toolDef.execute(tc.args);
|
|
119
|
+
opts.onToolCall?.(tc.name, tc.args, result);
|
|
120
|
+
toolResults.push({ id: tc.id, name: tc.name, result });
|
|
121
|
+
} catch (e) {
|
|
122
|
+
const errResult = { error: e.message || String(e) };
|
|
123
|
+
opts.onToolCall?.(tc.name, tc.args, errResult);
|
|
124
|
+
toolResults.push({ id: tc.id, name: tc.name, result: errResult });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
wireMessages.push({
|
|
128
|
+
id: Math.random().toString(36).slice(2),
|
|
129
|
+
role: "tool",
|
|
130
|
+
parts: toolResults.map((tr) => ({
|
|
131
|
+
type: "tool-result",
|
|
132
|
+
toolCallId: tr.id,
|
|
133
|
+
toolName: tr.name,
|
|
134
|
+
result: tr.result
|
|
135
|
+
}))
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return fullText;
|
|
139
|
+
}
|
|
140
|
+
async getSchema() {
|
|
141
|
+
const res = await fetch(`${this.baseUrl}/api/v1/schema`, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: this.headers(),
|
|
144
|
+
body: JSON.stringify({ connectionString: this.connectionString })
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
148
|
+
throw new Error(err.error || `HTTP ${res.status}`);
|
|
149
|
+
}
|
|
150
|
+
const data = await res.json();
|
|
151
|
+
return data.collections;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
export {
|
|
155
|
+
SyncAgentClient
|
|
156
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@syncagent/js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SyncAgent JavaScript SDK — AI database agent for any app",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"tsup": "^8.4.0",
|
|
22
|
+
"typescript": "^5.8.3"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"keywords": ["syncagent", "ai", "database", "agent", "sdk", "chat"]
|
|
26
|
+
}
|