amalgm 0.1.111 → 0.1.112
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/lib/app-chat.js +271 -0
- package/lib/react.js +123 -0
- package/package.json +10 -2
package/lib/app-chat.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function defaultFetch() {
|
|
4
|
+
if (typeof fetch !== 'function') {
|
|
5
|
+
throw new Error('A fetch implementation is required');
|
|
6
|
+
}
|
|
7
|
+
return fetch.bind(globalThis);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function responseError(response, fallback) {
|
|
11
|
+
const authUrl = response.headers.get('x-amalgm-app-auth-url') || undefined;
|
|
12
|
+
const text = await response.text().catch(() => '');
|
|
13
|
+
if (!text) {
|
|
14
|
+
if (response.status === 401) return new AmalgmAppAuthRequiredError(fallback, authUrl);
|
|
15
|
+
return Object.assign(new Error(fallback), { status: response.status });
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(text);
|
|
19
|
+
const message = parsed.error || parsed.message || fallback;
|
|
20
|
+
if (
|
|
21
|
+
response.status === 401
|
|
22
|
+
&& (
|
|
23
|
+
parsed.code === 'APP_AUTH_REQUIRED'
|
|
24
|
+
|| /fresh Amalgm session|app access/i.test(String(message))
|
|
25
|
+
)
|
|
26
|
+
) {
|
|
27
|
+
return new AmalgmAppAuthRequiredError(message, parsed.authUrl || authUrl);
|
|
28
|
+
}
|
|
29
|
+
return Object.assign(new Error(message), { status: response.status });
|
|
30
|
+
} catch {
|
|
31
|
+
if (response.status === 401 && /fresh Amalgm session|app access/i.test(text)) {
|
|
32
|
+
return new AmalgmAppAuthRequiredError(text.slice(0, 500) || fallback, authUrl);
|
|
33
|
+
}
|
|
34
|
+
return Object.assign(new Error(text.slice(0, 500) || fallback), { status: response.status });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class AmalgmAppAuthRequiredError extends Error {
|
|
39
|
+
constructor(message = 'App access requires a fresh Amalgm session.', authUrl) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = 'AmalgmAppAuthRequiredError';
|
|
42
|
+
this.status = 401;
|
|
43
|
+
this.code = 'APP_AUTH_REQUIRED';
|
|
44
|
+
this.authUrl = authUrl;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isAmalgmAppAuthRequiredError(error) {
|
|
49
|
+
return error instanceof AmalgmAppAuthRequiredError
|
|
50
|
+
|| Boolean(error && typeof error === 'object' && error.code === 'APP_AUTH_REQUIRED');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function refreshAmalgmAppAccess(authUrl) {
|
|
54
|
+
if (typeof window === 'undefined' || !window.location) return false;
|
|
55
|
+
const target = authUrl || `${window.location.pathname}${window.location.search}${window.location.hash}` || '/';
|
|
56
|
+
window.location.assign(target);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseSseText(input) {
|
|
61
|
+
const events = [];
|
|
62
|
+
let event = 'message';
|
|
63
|
+
let data = [];
|
|
64
|
+
let id;
|
|
65
|
+
const flush = () => {
|
|
66
|
+
if (data.length > 0) {
|
|
67
|
+
events.push({ event, data: data.join('\n'), ...(id !== undefined ? { id } : {}) });
|
|
68
|
+
}
|
|
69
|
+
event = 'message';
|
|
70
|
+
data = [];
|
|
71
|
+
id = undefined;
|
|
72
|
+
};
|
|
73
|
+
for (const rawLine of String(input || '').split('\n')) {
|
|
74
|
+
const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine;
|
|
75
|
+
if (line === '') {
|
|
76
|
+
flush();
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (line.startsWith(':')) continue;
|
|
80
|
+
if (line.startsWith('event:')) event = line.slice(6).trim() || 'message';
|
|
81
|
+
if (line.startsWith('id:')) id = line.slice(3).trimStart();
|
|
82
|
+
if (line.startsWith('data:')) data.push(line.slice(5).trimStart());
|
|
83
|
+
}
|
|
84
|
+
flush();
|
|
85
|
+
return events;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function readSseStream(response, onEvent) {
|
|
89
|
+
if (!response.body) throw new Error('No response body');
|
|
90
|
+
const reader = response.body.getReader();
|
|
91
|
+
const decoder = new TextDecoder();
|
|
92
|
+
let buffer = '';
|
|
93
|
+
for (;;) {
|
|
94
|
+
const { done, value } = await reader.read();
|
|
95
|
+
if (done) break;
|
|
96
|
+
buffer += decoder.decode(value, { stream: true });
|
|
97
|
+
const marker = buffer.lastIndexOf('\n\n');
|
|
98
|
+
if (marker === -1) continue;
|
|
99
|
+
const ready = buffer.slice(0, marker + 2);
|
|
100
|
+
buffer = buffer.slice(marker + 2);
|
|
101
|
+
for (const event of parseSseText(ready)) onEvent(event);
|
|
102
|
+
}
|
|
103
|
+
if (buffer.trim()) {
|
|
104
|
+
for (const event of parseSseText(buffer)) onEvent(event);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function dispatchAgentEvent(event, handlers) {
|
|
109
|
+
handlers.onEvent?.(event);
|
|
110
|
+
if (event?._type === 'title_generated' && typeof event.title === 'string') {
|
|
111
|
+
handlers.onTitle?.(event.title, typeof event.sessionId === 'string' ? event.sessionId : undefined);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (event?._type === 'complete') {
|
|
115
|
+
handlers.onComplete?.(event);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (event?._type === 'error' || event?._type === 'auth_error' || event?._type === 'model_error') {
|
|
119
|
+
handlers.onError?.(new Error(event.message || event.error || 'Amalgm chat failed'));
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (event?._type === 'update') {
|
|
123
|
+
const content = event.content || {};
|
|
124
|
+
if (content.type === 'text' && typeof content.text === 'string') {
|
|
125
|
+
if (event.sessionUpdate === 'agent_thought_chunk') {
|
|
126
|
+
handlers.onReasoningDelta?.(content.text, event);
|
|
127
|
+
} else if (event.sessionUpdate === 'agent_message_chunk') {
|
|
128
|
+
handlers.onTextDelta?.(content.text, event);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (content.type === 'image' && typeof content.data === 'string' && typeof content.mimeType === 'string') {
|
|
132
|
+
handlers.onImage?.(content.data, content.mimeType, event);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function jsonHeaders(extra) {
|
|
139
|
+
return { 'Content-Type': 'application/json', ...(extra || {}) };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class AmalgmAppChatClient {
|
|
143
|
+
constructor(options = {}) {
|
|
144
|
+
this.basePath = String(options.basePath || '/_amalgm/chat').replace(/\/$/, '');
|
|
145
|
+
this.fetch = options.fetch || defaultFetch();
|
|
146
|
+
this.headers = options.headers || undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async capabilities() {
|
|
150
|
+
const response = await this.fetch(`${this.basePath}/capabilities`, {
|
|
151
|
+
method: 'GET',
|
|
152
|
+
headers: this.headers,
|
|
153
|
+
credentials: 'include',
|
|
154
|
+
});
|
|
155
|
+
if (!response.ok) throw await responseError(response, `Capabilities failed: ${response.status}`);
|
|
156
|
+
return response.json();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async createSession(request = {}) {
|
|
160
|
+
const response = await this.fetch(`${this.basePath}/sessions`, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: jsonHeaders(this.headers),
|
|
163
|
+
credentials: 'include',
|
|
164
|
+
body: JSON.stringify(request),
|
|
165
|
+
});
|
|
166
|
+
if (!response.ok) throw await responseError(response, `Session create failed: ${response.status}`);
|
|
167
|
+
return response.json();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async send(request, handlers = {}) {
|
|
171
|
+
let completed = false;
|
|
172
|
+
let lastEventId = null;
|
|
173
|
+
let sessionId = request?.sessionId || null;
|
|
174
|
+
const handle = (parsed) => {
|
|
175
|
+
if (parsed.id !== undefined) lastEventId = parsed.id;
|
|
176
|
+
if (!parsed.data) return;
|
|
177
|
+
let payload;
|
|
178
|
+
try {
|
|
179
|
+
payload = JSON.parse(parsed.data);
|
|
180
|
+
} catch {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (payload._type === 'event_id' && typeof payload.eventId === 'string') {
|
|
184
|
+
lastEventId = payload.eventId;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
completed = dispatchAgentEvent(payload, handlers) || completed;
|
|
188
|
+
};
|
|
189
|
+
const response = await this.fetch(`${this.basePath}/stream`, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: jsonHeaders(this.headers),
|
|
192
|
+
credentials: 'include',
|
|
193
|
+
body: JSON.stringify(request || {}),
|
|
194
|
+
});
|
|
195
|
+
if (!response.ok) throw await responseError(response, `Chat stream failed: ${response.status}`);
|
|
196
|
+
sessionId = response.headers.get('x-amalgm-session-id') || sessionId;
|
|
197
|
+
if (sessionId) handlers.onSession?.(sessionId);
|
|
198
|
+
await readSseStream(response, handle);
|
|
199
|
+
if (sessionId && !completed) {
|
|
200
|
+
await this.reconnect(sessionId, {
|
|
201
|
+
lastEventId,
|
|
202
|
+
handlers,
|
|
203
|
+
onComplete: () => { completed = true; },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
sessionId,
|
|
208
|
+
assistantMessageId: response.headers.get('x-amalgm-assistant-message-id'),
|
|
209
|
+
userMessageId: response.headers.get('x-amalgm-user-message-id'),
|
|
210
|
+
completed,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async reconnect(sessionId, options = {}) {
|
|
215
|
+
const response = await this.fetch(`${this.basePath}/reconnect`, {
|
|
216
|
+
method: 'POST',
|
|
217
|
+
headers: jsonHeaders(this.headers),
|
|
218
|
+
credentials: 'include',
|
|
219
|
+
body: JSON.stringify({ sessionId, lastEventId: options.lastEventId || undefined }),
|
|
220
|
+
});
|
|
221
|
+
if (!response.ok) throw await responseError(response, `Reconnect failed: ${response.status}`);
|
|
222
|
+
await readSseStream(response, (parsed) => {
|
|
223
|
+
if (!parsed.data) return;
|
|
224
|
+
let payload;
|
|
225
|
+
try {
|
|
226
|
+
payload = JSON.parse(parsed.data);
|
|
227
|
+
} catch {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const replay = (frame) => {
|
|
231
|
+
for (const nested of parseSseText(frame || '')) {
|
|
232
|
+
if (!nested.data) continue;
|
|
233
|
+
try {
|
|
234
|
+
const event = JSON.parse(nested.data);
|
|
235
|
+
if (dispatchAgentEvent(event, options.handlers || {})) options.onComplete?.();
|
|
236
|
+
} catch {
|
|
237
|
+
// Ignore malformed replay frames.
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
if (parsed.event === 'existing' && Array.isArray(payload.chunks)) {
|
|
242
|
+
for (const chunk of payload.chunks) replay(chunk?.data);
|
|
243
|
+
}
|
|
244
|
+
if (parsed.event === 'chunk') replay(payload.chunk?.data);
|
|
245
|
+
if (parsed.event === 'complete') options.onComplete?.();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async stop(sessionId, turnId) {
|
|
250
|
+
const response = await this.fetch(`${this.basePath}/stop`, {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: jsonHeaders(this.headers),
|
|
253
|
+
credentials: 'include',
|
|
254
|
+
body: JSON.stringify({ sessionId, turnId }),
|
|
255
|
+
});
|
|
256
|
+
if (!response.ok) throw await responseError(response, `Stop failed: ${response.status}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function createAmalgmAppChatClient(options) {
|
|
261
|
+
return new AmalgmAppChatClient(options);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = {
|
|
265
|
+
AmalgmAppChatClient,
|
|
266
|
+
AmalgmAppAuthRequiredError,
|
|
267
|
+
createAmalgmAppChatClient,
|
|
268
|
+
isAmalgmAppAuthRequiredError,
|
|
269
|
+
parseSseText,
|
|
270
|
+
refreshAmalgmAppAccess,
|
|
271
|
+
};
|
package/lib/react.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const React = require('react');
|
|
4
|
+
const {
|
|
5
|
+
AmalgmAppChatClient,
|
|
6
|
+
isAmalgmAppAuthRequiredError,
|
|
7
|
+
refreshAmalgmAppAccess,
|
|
8
|
+
} = require('./app-chat');
|
|
9
|
+
|
|
10
|
+
function id(prefix) {
|
|
11
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();
|
|
12
|
+
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function partsForRequest(request) {
|
|
16
|
+
if (Array.isArray(request?.parts) && request.parts.length > 0) return request.parts;
|
|
17
|
+
if (Array.isArray(request?.chatInput?.parts) && request.chatInput.parts.length > 0) return request.chatInput.parts;
|
|
18
|
+
return request?.prompt ? [{ type: 'text', text: request.prompt }] : [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function appendText(parts, text) {
|
|
22
|
+
const next = parts.slice();
|
|
23
|
+
const last = next[next.length - 1];
|
|
24
|
+
if (last && last.type === 'text') next[next.length - 1] = { ...last, text: `${last.text || ''}${text}` };
|
|
25
|
+
else next.push({ type: 'text', text });
|
|
26
|
+
return next;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function useAmalgmChat(options = {}) {
|
|
30
|
+
const client = React.useMemo(
|
|
31
|
+
() => new AmalgmAppChatClient({ basePath: options.basePath, fetch: options.fetch, headers: options.headers }),
|
|
32
|
+
[options.basePath, options.fetch, options.headers],
|
|
33
|
+
);
|
|
34
|
+
const [sessionId, setSessionIdState] = React.useState(options.sessionId || null);
|
|
35
|
+
const [messages, setMessages] = React.useState([]);
|
|
36
|
+
const [events, setEvents] = React.useState([]);
|
|
37
|
+
const [isStreaming, setIsStreaming] = React.useState(false);
|
|
38
|
+
const [error, setError] = React.useState(null);
|
|
39
|
+
const sessionRef = React.useRef(options.sessionId || null);
|
|
40
|
+
const assistantRef = React.useRef(null);
|
|
41
|
+
|
|
42
|
+
const setSessionId = React.useCallback((next) => {
|
|
43
|
+
sessionRef.current = next;
|
|
44
|
+
setSessionIdState(next);
|
|
45
|
+
if (next) options.onSession?.(next);
|
|
46
|
+
}, [options]);
|
|
47
|
+
|
|
48
|
+
const send = React.useCallback(async (input) => {
|
|
49
|
+
const request = typeof input === 'string' ? { prompt: input } : (input || {});
|
|
50
|
+
const userParts = partsForRequest(request);
|
|
51
|
+
const userId = id('user');
|
|
52
|
+
const assistantId = id('assistant');
|
|
53
|
+
assistantRef.current = assistantId;
|
|
54
|
+
setError(null);
|
|
55
|
+
setIsStreaming(true);
|
|
56
|
+
setMessages((current) => [
|
|
57
|
+
...current,
|
|
58
|
+
{ id: userId, role: 'user', parts: userParts },
|
|
59
|
+
{ id: assistantId, role: 'assistant', parts: [] },
|
|
60
|
+
]);
|
|
61
|
+
try {
|
|
62
|
+
const result = await client.send({
|
|
63
|
+
...request,
|
|
64
|
+
sessionId: request.sessionId || sessionRef.current,
|
|
65
|
+
agent: request.agent || options.agent,
|
|
66
|
+
origin: request.origin || options.origin,
|
|
67
|
+
context: request.context ?? options.context,
|
|
68
|
+
}, {
|
|
69
|
+
onSession: setSessionId,
|
|
70
|
+
onEvent: (event) => {
|
|
71
|
+
setEvents((current) => [...current, event]);
|
|
72
|
+
options.onEvent?.(event);
|
|
73
|
+
},
|
|
74
|
+
onTextDelta: (text) => {
|
|
75
|
+
setMessages((current) => current.map((message) => (
|
|
76
|
+
message.id === assistantId ? { ...message, parts: appendText(message.parts, text) } : message
|
|
77
|
+
)));
|
|
78
|
+
},
|
|
79
|
+
onComplete: options.onFinish,
|
|
80
|
+
onError: (nextError) => {
|
|
81
|
+
setError(nextError);
|
|
82
|
+
options.onError?.(nextError);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
if (result.sessionId) setSessionId(result.sessionId);
|
|
86
|
+
return result;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
89
|
+
setError(nextError);
|
|
90
|
+
options.onError?.(nextError);
|
|
91
|
+
if (options.refreshOnAuthRequired !== false && isAmalgmAppAuthRequiredError(nextError)) {
|
|
92
|
+
refreshAmalgmAppAccess(nextError.authUrl);
|
|
93
|
+
}
|
|
94
|
+
throw nextError;
|
|
95
|
+
} finally {
|
|
96
|
+
setIsStreaming(false);
|
|
97
|
+
assistantRef.current = null;
|
|
98
|
+
}
|
|
99
|
+
}, [client, options, setSessionId]);
|
|
100
|
+
|
|
101
|
+
const stop = React.useCallback(async () => {
|
|
102
|
+
if (!sessionRef.current) return;
|
|
103
|
+
await client.stop(sessionRef.current, assistantRef.current);
|
|
104
|
+
setIsStreaming(false);
|
|
105
|
+
}, [client]);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
client,
|
|
109
|
+
sessionId,
|
|
110
|
+
setSessionId,
|
|
111
|
+
messages,
|
|
112
|
+
setMessages,
|
|
113
|
+
events,
|
|
114
|
+
isStreaming,
|
|
115
|
+
error,
|
|
116
|
+
send,
|
|
117
|
+
stop,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
useAmalgmChat,
|
|
123
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalgm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.112",
|
|
4
4
|
"description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -17,11 +17,19 @@
|
|
|
17
17
|
"sync-runtime": "node ../../scripts/sync-npm-package-runtime.mjs",
|
|
18
18
|
"prepack": "node ../../scripts/sync-npm-package-runtime.mjs",
|
|
19
19
|
"pack:dry": "npm pack --dry-run",
|
|
20
|
-
"check": "node --check bin/amalgm.js && node --check lib/auth-store.js && node --check lib/cli.js && node --check lib/paths.js && node --check lib/process-cleanup.js && node --check lib/runtime-identity.js && node --check lib/runtime-manifest.js && node --check lib/service.js && node --check lib/state-migration.js && node --check lib/supervisor.js && node --check lib/tunnel-chat.js && node --check lib/tunnel-events.js && node --check lib/updater.js && node --check runtime/lib/runtime-manifest.js && node --check runtime/scripts/runtime-auth.js && node --check runtime/scripts/proxy-token-store.js && node --check runtime/scripts/local-gateway.js && node --check runtime/scripts/port-monitor.js && node --check runtime/scripts/chat-server.js && node --check runtime/scripts/chat-server/index.js && node --check runtime/scripts/chat-server/config.js && node --check runtime/scripts/chat-core/tooling/native-binaries.js && node --check runtime/scripts/chat-core/tooling/package-import.js && node --check runtime/scripts/chat-core/tooling/runtime-home.js && node --check runtime/scripts/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.js && node --check runtime/scripts/lib/project-paths.js && node --check runtime/scripts/lib/runtime-paths.js"
|
|
20
|
+
"check": "node --check bin/amalgm.js && node --check lib/app-chat.js && node --check lib/react.js && node --check lib/auth-store.js && node --check lib/cli.js && node --check lib/paths.js && node --check lib/process-cleanup.js && node --check lib/runtime-identity.js && node --check lib/runtime-manifest.js && node --check lib/service.js && node --check lib/state-migration.js && node --check lib/supervisor.js && node --check lib/tunnel-chat.js && node --check lib/tunnel-events.js && node --check lib/updater.js && node --check runtime/lib/runtime-manifest.js && node --check runtime/scripts/runtime-auth.js && node --check runtime/scripts/proxy-token-store.js && node --check runtime/scripts/local-gateway.js && node --check runtime/scripts/port-monitor.js && node --check runtime/scripts/chat-server.js && node --check runtime/scripts/chat-server/index.js && node --check runtime/scripts/chat-server/config.js && node --check runtime/scripts/chat-core/tooling/native-binaries.js && node --check runtime/scripts/chat-core/tooling/package-import.js && node --check runtime/scripts/chat-core/tooling/runtime-home.js && node --check runtime/scripts/amalgm-mcp/index.js && node --check runtime/scripts/amalgm-mcp/config.js && node --check runtime/scripts/lib/project-paths.js && node --check runtime/scripts/lib/runtime-paths.js"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20"
|
|
24
24
|
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"react": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
25
33
|
"dependencies": {
|
|
26
34
|
"@ai-sdk/gateway": "^3.0.131",
|
|
27
35
|
"@anthropic-ai/claude-agent-sdk": "^0.3.177",
|