@castari/sdk 0.0.3 → 0.0.5
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 +39 -0
- package/dist/client.d.ts +10 -39
- package/dist/client.js +43 -217
- package/dist/server.js +6 -0
- package/dist/types.d.ts +1 -0
- package/package.json +2 -3
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @castari/sdk
|
|
2
|
+
|
|
3
|
+
The SDK for building Castari agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @castari/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
See the [SDK Reference](../../docs/sdk-reference.md) for detailed documentation.
|
|
14
|
+
|
|
15
|
+
### Defining an Agent
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { serve, tool } from '@castari/sdk'
|
|
19
|
+
|
|
20
|
+
const myTool = tool({ ... })
|
|
21
|
+
|
|
22
|
+
serve({
|
|
23
|
+
tools: [myTool],
|
|
24
|
+
systemPrompt: 'You are a helpful assistant.'
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Connecting to an Agent
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { CastariClient } from '@castari/sdk/client'
|
|
32
|
+
|
|
33
|
+
const client = new CastariClient({
|
|
34
|
+
snapshot: 'my-agent',
|
|
35
|
+
// platformUrl: '...' // Optional
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
await client.start()
|
|
39
|
+
```
|
package/dist/client.d.ts
CHANGED
|
@@ -1,64 +1,35 @@
|
|
|
1
1
|
import type { QueryConfig, WSInputMessage, WSOutputMessage } from './types';
|
|
2
2
|
export * from './types';
|
|
3
|
-
type DaytonaCreateOptions = {
|
|
4
|
-
snapshot?: string;
|
|
5
|
-
image?: unknown;
|
|
6
|
-
resources?: {
|
|
7
|
-
cpu?: number;
|
|
8
|
-
memory?: number;
|
|
9
|
-
disk?: number;
|
|
10
|
-
};
|
|
11
|
-
autoStopInterval?: number;
|
|
12
|
-
autoArchiveInterval?: number;
|
|
13
|
-
autoDeleteInterval?: number;
|
|
14
|
-
ephemeral?: boolean;
|
|
15
|
-
public?: boolean;
|
|
16
|
-
volumes?: {
|
|
17
|
-
volumeId: string;
|
|
18
|
-
mountPath: string;
|
|
19
|
-
}[];
|
|
20
|
-
};
|
|
21
3
|
/**
|
|
22
4
|
* Configuration options for the Castari Client.
|
|
23
5
|
*/
|
|
24
6
|
export interface ClientOptions extends Partial<QueryConfig> {
|
|
25
|
-
/** Local/custom connection URL (e.g., 'http://localhost:3000'). If omitted,
|
|
7
|
+
/** Local/custom connection URL (e.g., 'http://localhost:3000'). If omitted, Platform mode is used. */
|
|
26
8
|
connectionUrl?: string;
|
|
27
9
|
/** Anthropic API key (required unless present in process.env.ANTHROPIC_API_KEY) */
|
|
28
10
|
anthropicApiKey?: string;
|
|
29
11
|
/** Enable debug logging */
|
|
30
12
|
debug?: boolean;
|
|
31
|
-
|
|
32
|
-
daytonaApiUrl?: string;
|
|
33
|
-
daytonaTarget?: string;
|
|
34
|
-
/** Override to skip preview discovery; should be base URL to server (e.g., https://3000-<sandboxId>.<domain>) */
|
|
35
|
-
daytonaConnectionUrl?: string;
|
|
13
|
+
/** Snapshot name to deploy/start */
|
|
36
14
|
snapshot?: string;
|
|
37
|
-
image?: unknown;
|
|
38
|
-
/** Optional override for preview domain if provider metadata is missing. Example: "preview.daytona.io" */
|
|
39
|
-
daytonaPreviewDomain?: string;
|
|
40
|
-
resources?: DaytonaCreateOptions['resources'];
|
|
41
|
-
autoStopInterval?: number;
|
|
42
|
-
autoArchiveInterval?: number;
|
|
43
|
-
autoDeleteInterval?: number;
|
|
44
|
-
ephemeral?: boolean;
|
|
45
|
-
/** Optional Daytona volume name to mount at /home/daytona/agent-workspace. Defaults to undefined (no volume). */
|
|
46
|
-
volume?: string;
|
|
47
15
|
/** Optional labels to apply to the sandbox (and filter by for reuse) */
|
|
48
16
|
labels?: Record<string, string>;
|
|
49
|
-
/**
|
|
50
|
-
|
|
17
|
+
/** Optional volume name to mount at /home/daytona/agent-workspace */
|
|
18
|
+
volume?: string;
|
|
19
|
+
/** Castari Platform API URL. Defaults to https://api.castari.com (or localhost in dev) */
|
|
20
|
+
platformUrl?: string;
|
|
21
|
+
/** Optional sessionId to resume */
|
|
22
|
+
resume?: string;
|
|
51
23
|
}
|
|
52
24
|
export declare class CastariClient {
|
|
53
25
|
private ws?;
|
|
54
26
|
private options;
|
|
55
27
|
private messageHandlers;
|
|
56
|
-
private
|
|
57
|
-
private daytona?;
|
|
28
|
+
private sandboxId?;
|
|
58
29
|
constructor(options?: ClientOptions);
|
|
59
30
|
start(): Promise<void>;
|
|
60
31
|
private setupLocalConnection;
|
|
61
|
-
private
|
|
32
|
+
private setupPlatformConnection;
|
|
62
33
|
private handleMessage;
|
|
63
34
|
onMessage(handler: (message: WSOutputMessage) => void): () => void;
|
|
64
35
|
send(message: WSInputMessage): void;
|
package/dist/client.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
const DEFAULT_LOCAL_URL = 'http://localhost:3000';
|
|
3
|
-
const DEFAULT_WORKSPACE_MOUNT = '/home/daytona/agent-workspace';
|
|
4
3
|
export class CastariClient {
|
|
5
4
|
ws;
|
|
6
5
|
options;
|
|
7
6
|
messageHandlers = [];
|
|
8
|
-
|
|
9
|
-
daytona;
|
|
7
|
+
sandboxId;
|
|
10
8
|
constructor(options = {}) {
|
|
11
9
|
this.options = {
|
|
12
10
|
...options,
|
|
@@ -19,7 +17,7 @@ export class CastariClient {
|
|
|
19
17
|
}
|
|
20
18
|
const connection = this.options.connectionUrl
|
|
21
19
|
? await this.setupLocalConnection()
|
|
22
|
-
: await this.
|
|
20
|
+
: await this.setupPlatformConnection();
|
|
23
21
|
if (this.options.debug) {
|
|
24
22
|
console.log(`📡 Configuring server at ${connection.configUrl}...`);
|
|
25
23
|
}
|
|
@@ -29,7 +27,11 @@ export class CastariClient {
|
|
|
29
27
|
allowedTools: this.options.allowedTools,
|
|
30
28
|
systemPrompt: this.options.systemPrompt,
|
|
31
29
|
model: this.options.model,
|
|
30
|
+
resume: this.options.resume,
|
|
32
31
|
};
|
|
32
|
+
if (this.options.debug) {
|
|
33
|
+
console.log(`📋 Config payload:`, JSON.stringify(configPayload, null, 2));
|
|
34
|
+
}
|
|
33
35
|
const configHeaders = {
|
|
34
36
|
'Content-Type': 'application/json',
|
|
35
37
|
};
|
|
@@ -113,212 +115,39 @@ export class CastariClient {
|
|
|
113
115
|
wsUrl: `${baseUrl.replace('http://', 'ws://').replace('https://', 'wss://')}/ws`,
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
|
-
async
|
|
117
|
-
const
|
|
118
|
-
const apiKey = this.options.daytonaApiKey || process.env.DAYTONA_API_KEY;
|
|
119
|
-
if (!apiKey) {
|
|
120
|
-
throw new Error('DAYTONA_API_KEY is required for Daytona mode');
|
|
121
|
-
}
|
|
122
|
-
this.daytona = new Daytona({
|
|
123
|
-
apiKey,
|
|
124
|
-
apiUrl: this.options.daytonaApiUrl || process.env.DAYTONA_API_URL,
|
|
125
|
-
target: this.options.daytonaTarget || process.env.DAYTONA_TARGET,
|
|
126
|
-
});
|
|
127
|
-
const volumeName = this.options.volume || this.options.workspaceVolumeName;
|
|
128
|
-
let volumeMounts = [];
|
|
129
|
-
if (volumeName) {
|
|
130
|
-
const volumeService = this.daytona.volume;
|
|
131
|
-
if (volumeService && typeof volumeService.get === 'function') {
|
|
132
|
-
// Ensure volume exists
|
|
133
|
-
let volume;
|
|
134
|
-
try {
|
|
135
|
-
volume = await volumeService.get(volumeName, true); // true = create if missing
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
// If get fails, try create explicitly if the SDK requires it, but usually get(name, true) handles it.
|
|
139
|
-
// Assuming get(name, true) works as per previous implementation.
|
|
140
|
-
console.error('Failed to get/create volume:', err);
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
volumeMounts = [
|
|
144
|
-
{
|
|
145
|
-
volumeId: volume.id,
|
|
146
|
-
mountPath: DEFAULT_WORKSPACE_MOUNT,
|
|
147
|
-
},
|
|
148
|
-
];
|
|
149
|
-
if (this.options.debug) {
|
|
150
|
-
console.log(`🗂️ Using Daytona volume ${volume.id} (${volumeName}) at ${DEFAULT_WORKSPACE_MOUNT}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
console.warn('Daytona SDK does not expose volume.get; skipping volume mount. Update @daytonaio/sdk or disable volume.');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
118
|
+
async setupPlatformConnection() {
|
|
119
|
+
const platformUrl = this.options.platformUrl || process.env.CASTARI_PLATFORM_URL || 'http://localhost:3000';
|
|
157
120
|
if (this.options.debug) {
|
|
158
|
-
console.log(
|
|
159
|
-
if (volumeName) {
|
|
160
|
-
console.log(`📦 Mounting volume ${volumeName} at ${DEFAULT_WORKSPACE_MOUNT}`);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
console.log('📦 No volume configured; using ephemeral container filesystem.');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const createParams = {
|
|
167
|
-
snapshot: this.options.snapshot,
|
|
168
|
-
image: this.options.image,
|
|
169
|
-
resources: this.options.resources,
|
|
170
|
-
autoStopInterval: this.options.autoStopInterval,
|
|
171
|
-
autoArchiveInterval: this.options.autoArchiveInterval,
|
|
172
|
-
autoDeleteInterval: this.options.autoDeleteInterval,
|
|
173
|
-
ephemeral: this.options.ephemeral,
|
|
174
|
-
public: true,
|
|
175
|
-
volumes: volumeMounts,
|
|
176
|
-
};
|
|
177
|
-
// Check for existing sandbox if labels are provided
|
|
178
|
-
if (this.options.labels) {
|
|
179
|
-
const existing = await this.daytona.list(this.options.labels);
|
|
180
|
-
if (existing.items.length > 0) {
|
|
181
|
-
// Use the first matching sandbox
|
|
182
|
-
this.sandbox = existing.items[0];
|
|
183
|
-
if (this.options.debug) {
|
|
184
|
-
console.log(`♻️ Found existing sandbox: ${this.sandbox.id} (${this.sandbox.state})`);
|
|
185
|
-
}
|
|
186
|
-
// Ensure it's started
|
|
187
|
-
if (this.sandbox.state !== 'started') {
|
|
188
|
-
if (this.options.debug) {
|
|
189
|
-
console.log('▶️ Starting existing sandbox...');
|
|
190
|
-
}
|
|
191
|
-
await this.sandbox.start();
|
|
192
|
-
}
|
|
193
|
-
}
|
|
121
|
+
console.log(`🚀 Requesting sandbox from ${platformUrl}...`);
|
|
194
122
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
123
|
+
const response = await fetch(`${platformUrl}/sandbox/start`, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: { 'Content-Type': 'application/json' },
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
snapshot: this.options.snapshot,
|
|
200
128
|
labels: this.options.labels,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// Wait for sandbox to reach STARTED state before interacting with it
|
|
208
|
-
const sandboxAny = this.sandbox;
|
|
209
|
-
if (typeof sandboxAny.waitUntilStarted === 'function') {
|
|
210
|
-
if (this.options.debug) {
|
|
211
|
-
console.log('⏳ Waiting for sandbox to reach STARTED state...');
|
|
212
|
-
}
|
|
213
|
-
await sandboxAny.waitUntilStarted(60);
|
|
214
|
-
}
|
|
215
|
-
// Start the Castari server process inside the sandbox.
|
|
216
|
-
// We keep the container's default ENTRYPOINT as a no-op ('sleep infinity') for snapshot validation,
|
|
217
|
-
// and explicitly start the server here via Daytona's process API, invoking the server entrypoint directly.
|
|
218
|
-
const serverCommand = `sh -lc "cd /home/daytona/app && CASTARI_WORKSPACE=${DEFAULT_WORKSPACE_MOUNT} bun start"`;
|
|
219
|
-
try {
|
|
220
|
-
const sessionId = `castari-server-${this.sandbox.id}`;
|
|
221
|
-
const processApi = this.sandbox.process;
|
|
222
|
-
if (processApi &&
|
|
223
|
-
typeof processApi.createSession === 'function' &&
|
|
224
|
-
typeof processApi.executeSessionCommand === 'function') {
|
|
225
|
-
if (this.options.debug) {
|
|
226
|
-
console.log(`▶️ Creating process session ${sessionId} and starting server...`);
|
|
227
|
-
}
|
|
228
|
-
await processApi.createSession(sessionId);
|
|
229
|
-
await processApi.executeSessionCommand(sessionId, {
|
|
230
|
-
command: serverCommand,
|
|
231
|
-
async: true,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
else if (processApi && typeof processApi.executeCommand === 'function') {
|
|
235
|
-
if (this.options.debug) {
|
|
236
|
-
console.log('▶️ Starting server via executeCommand...');
|
|
237
|
-
}
|
|
238
|
-
await processApi.executeCommand({
|
|
239
|
-
command: serverCommand,
|
|
240
|
-
async: true,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
console.warn('Daytona SDK process API is not available; unable to start server automatically inside sandbox.');
|
|
245
|
-
}
|
|
246
|
-
// Give the server a brief moment to start listening on port 3000 before configuring it.
|
|
247
|
-
if (this.options.debug) {
|
|
248
|
-
console.log('⏳ Waiting briefly for Castari server to start inside sandbox...');
|
|
249
|
-
}
|
|
250
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
251
|
-
}
|
|
252
|
-
catch (err) {
|
|
253
|
-
console.error('Failed to start Castari server inside sandbox:', err);
|
|
129
|
+
volume: this.options.volume
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
const errorText = await response.text();
|
|
134
|
+
throw new Error(`Failed to start sandbox: ${errorText}`);
|
|
254
135
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Try getPreviewUrl (newer SDKs), then getPreviewLink (older SDKs)
|
|
260
|
-
let preview = null;
|
|
261
|
-
if (typeof this.sandbox.getPreviewUrl === 'function') {
|
|
262
|
-
try {
|
|
263
|
-
preview = await this.sandbox.getPreviewUrl(3000);
|
|
264
|
-
}
|
|
265
|
-
catch (err) {
|
|
266
|
-
if (this.options.debug) {
|
|
267
|
-
console.warn('⚠️ getPreviewUrl failed; will fall back to getPreviewLink or manual domain.', err);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (!preview?.url && typeof this.sandbox.getPreviewLink === 'function') {
|
|
272
|
-
try {
|
|
273
|
-
preview = await this.sandbox.getPreviewLink(3000);
|
|
274
|
-
}
|
|
275
|
-
catch (err) {
|
|
276
|
-
if (this.options.debug) {
|
|
277
|
-
console.warn('⚠️ getPreviewLink failed; will attempt fallback domain if provided.', err);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
baseUrl = preview?.url || null;
|
|
282
|
-
previewToken = preview?.token;
|
|
283
|
-
if (!baseUrl) {
|
|
284
|
-
// Fallback: construct from provided preview domain if available
|
|
285
|
-
const previewDomain = this.options.daytonaPreviewDomain || process.env.DAYTONA_PREVIEW_DOMAIN;
|
|
286
|
-
if (previewDomain) {
|
|
287
|
-
baseUrl = `https://${3000}-${this.sandbox.id}.${previewDomain}`;
|
|
288
|
-
if (this.options.debug) {
|
|
289
|
-
console.warn(`⚠️ Preview URL not provided by SDK; using fallback domain ${previewDomain}`);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
throw new Error('Failed to get preview URL from Daytona sandbox. Provide daytonaConnectionUrl or DAYTONA_PREVIEW_DOMAIN.');
|
|
294
|
-
}
|
|
295
|
-
}
|
|
136
|
+
const { id, url, token } = await response.json();
|
|
137
|
+
this.sandboxId = id;
|
|
138
|
+
if (this.options.debug) {
|
|
139
|
+
console.log(`✅ Sandbox started: ${id} at ${url}`);
|
|
296
140
|
}
|
|
297
|
-
baseUrl =
|
|
141
|
+
const baseUrl = url.replace(/\/$/, '');
|
|
298
142
|
const configUrl = `${baseUrl.replace('ws://', 'http://').replace('wss://', 'https://')}/config`;
|
|
299
143
|
const wsUrlBase = `${baseUrl.replace('https://', 'wss://').replace('http://', 'ws://')}/ws`;
|
|
300
144
|
return {
|
|
301
145
|
configUrl,
|
|
302
146
|
wsUrl: wsUrlBase,
|
|
303
|
-
previewToken,
|
|
147
|
+
previewToken: token,
|
|
304
148
|
cleanup: async () => {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (this.options.debug)
|
|
308
|
-
console.log('🧹 Sandbox deleted');
|
|
309
|
-
}
|
|
310
|
-
catch (err) {
|
|
311
|
-
const msg = err?.response?.data?.message || err?.message || String(err);
|
|
312
|
-
if (msg.includes('state change in progress')) {
|
|
313
|
-
if (this.options.debug) {
|
|
314
|
-
console.warn('⚠️ Sandbox deletion skipped (state change in progress)');
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
console.error('Failed to delete sandbox:', err);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
},
|
|
149
|
+
await this.stop({ delete: true });
|
|
150
|
+
}
|
|
322
151
|
};
|
|
323
152
|
}
|
|
324
153
|
handleMessage(message) {
|
|
@@ -343,29 +172,26 @@ export class CastariClient {
|
|
|
343
172
|
if (this.ws) {
|
|
344
173
|
this.ws.close();
|
|
345
174
|
}
|
|
346
|
-
if (this.
|
|
175
|
+
if (this.sandboxId) {
|
|
176
|
+
const platformUrl = this.options.platformUrl || process.env.CASTARI_PLATFORM_URL || 'http://localhost:3000';
|
|
347
177
|
try {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
178
|
+
const response = await fetch(`${platformUrl}/sandbox/stop`, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: { 'Content-Type': 'application/json' },
|
|
181
|
+
body: JSON.stringify({
|
|
182
|
+
sandboxId: this.sandboxId,
|
|
183
|
+
delete: options.delete
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
console.error(`Failed to stop sandbox: ${await response.text()}`);
|
|
352
188
|
}
|
|
353
|
-
else {
|
|
354
|
-
|
|
355
|
-
if (this.options.debug)
|
|
356
|
-
console.log('🛑 Sandbox stopped (preserved)');
|
|
189
|
+
else if (this.options.debug) {
|
|
190
|
+
console.log(`🛑 Sandbox ${options.delete ? 'deleted' : 'stopped'}`);
|
|
357
191
|
}
|
|
358
192
|
}
|
|
359
193
|
catch (err) {
|
|
360
|
-
|
|
361
|
-
if (msg.includes('state change in progress')) {
|
|
362
|
-
if (this.options.debug) {
|
|
363
|
-
console.warn(`⚠️ Sandbox ${options.delete ? 'deletion' : 'stop'} skipped (state change in progress)`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
console.error(`Failed to ${options.delete ? 'delete' : 'stop'} sandbox:`, err);
|
|
368
|
-
}
|
|
194
|
+
console.error('Failed to call stop endpoint:', err);
|
|
369
195
|
}
|
|
370
196
|
}
|
|
371
197
|
}
|
package/dist/server.js
CHANGED
|
@@ -115,6 +115,12 @@ async function processMessages(initialOptions) {
|
|
|
115
115
|
...options,
|
|
116
116
|
prompt: '[generator]', // avoid logging generator internals
|
|
117
117
|
});
|
|
118
|
+
if (options.resume) {
|
|
119
|
+
console.info(`📋 Resuming session: ${options.resume}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.info('📋 Starting new session');
|
|
123
|
+
}
|
|
118
124
|
activeStream = query({
|
|
119
125
|
prompt: generateMessages(),
|
|
120
126
|
options,
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@castari/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@anthropic-ai/claude-agent-sdk": "^0.1.44"
|
|
33
|
-
"@daytonaio/sdk": "^0.115.2"
|
|
32
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.44"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
|
36
35
|
"@types/bun": "latest"
|