edgecrab-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,482 @@
1
+ // src/agent.ts
2
+ import { randomUUID } from "crypto";
3
+
4
+ // src/client.ts
5
+ var DEFAULT_BASE_URL = "http://127.0.0.1:8642";
6
+ var DEFAULT_TIMEOUT = 12e4;
7
+ var DEFAULT_MAX_RETRIES = 3;
8
+ var DEFAULT_RETRY_BASE_DELAY = 1e3;
9
+ var EdgeCrabError = class extends Error {
10
+ statusCode;
11
+ constructor(message, statusCode) {
12
+ super(message);
13
+ this.name = "EdgeCrabError";
14
+ this.statusCode = statusCode;
15
+ }
16
+ };
17
+ var AuthenticationError = class extends EdgeCrabError {
18
+ constructor(message, statusCode) {
19
+ super(message, statusCode);
20
+ this.name = "AuthenticationError";
21
+ }
22
+ };
23
+ var RateLimitError = class extends EdgeCrabError {
24
+ retryAfter;
25
+ constructor(message, retryAfter) {
26
+ super(message, 429);
27
+ this.name = "RateLimitError";
28
+ this.retryAfter = retryAfter;
29
+ }
30
+ };
31
+ var ServerError = class extends EdgeCrabError {
32
+ constructor(message, statusCode) {
33
+ super(message, statusCode);
34
+ this.name = "ServerError";
35
+ }
36
+ };
37
+ var TimeoutError = class extends EdgeCrabError {
38
+ constructor(message = "Request timed out") {
39
+ super(message);
40
+ this.name = "TimeoutError";
41
+ }
42
+ };
43
+ var ConnectionError = class extends EdgeCrabError {
44
+ constructor(message = "Could not connect to EdgeCrab server") {
45
+ super(message);
46
+ this.name = "ConnectionError";
47
+ }
48
+ };
49
+ var MaxTurnsExceededError = class extends EdgeCrabError {
50
+ maxTurns;
51
+ constructor(maxTurns) {
52
+ super(`Agent exceeded maximum turns (${maxTurns})`);
53
+ this.name = "MaxTurnsExceededError";
54
+ this.maxTurns = maxTurns;
55
+ }
56
+ };
57
+ var InterruptedError = class extends EdgeCrabError {
58
+ constructor() {
59
+ super("Agent conversation was interrupted");
60
+ this.name = "InterruptedError";
61
+ }
62
+ };
63
+ function classifyError(status, detail, headers) {
64
+ const msg = `API error ${status}: ${detail}`;
65
+ if (status === 401 || status === 403) return new AuthenticationError(msg, status);
66
+ if (status === 429) {
67
+ const ra = headers?.get("retry-after");
68
+ return new RateLimitError(msg, ra ? parseFloat(ra) : void 0);
69
+ }
70
+ if (status >= 500) return new ServerError(msg, status);
71
+ return new EdgeCrabError(msg, status);
72
+ }
73
+ function isRetryable(err) {
74
+ if (err instanceof ServerError || err instanceof TimeoutError || err instanceof ConnectionError)
75
+ return true;
76
+ if (err instanceof RateLimitError) return true;
77
+ if (err instanceof TypeError && String(err.message).includes("fetch")) return true;
78
+ return false;
79
+ }
80
+ function buildHeaders(apiKey) {
81
+ const headers = { "Content-Type": "application/json" };
82
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
83
+ return headers;
84
+ }
85
+ var EdgeCrabClient = class {
86
+ baseUrl;
87
+ headers;
88
+ timeout;
89
+ maxRetries;
90
+ retryBaseDelay;
91
+ constructor(options = {}) {
92
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
93
+ this.headers = buildHeaders(options.apiKey);
94
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
95
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
96
+ this.retryBaseDelay = options.retryBaseDelay ?? DEFAULT_RETRY_BASE_DELAY;
97
+ }
98
+ /** Simple chat — send a message, get a reply string. */
99
+ async chat(message, options) {
100
+ const messages = [];
101
+ if (options?.system) {
102
+ messages.push({ role: "system", content: options.system });
103
+ }
104
+ messages.push({ role: "user", content: message });
105
+ const resp = await this.createCompletion(messages, {
106
+ model: options?.model,
107
+ temperature: options?.temperature,
108
+ maxTokens: options?.maxTokens
109
+ });
110
+ return resp.choices?.[0]?.message?.content ?? "";
111
+ }
112
+ /** Create a chat completion (non-streaming). */
113
+ async createCompletion(messages, options) {
114
+ const body = {
115
+ model: options?.model ?? "anthropic/claude-sonnet-4-20250514",
116
+ messages,
117
+ stream: false
118
+ };
119
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
120
+ if (options?.maxTokens !== void 0) body.max_tokens = options.maxTokens;
121
+ if (options?.tools) body.tools = options.tools;
122
+ return this.postWithRetry(body);
123
+ }
124
+ async postWithRetry(body) {
125
+ let lastError;
126
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
127
+ try {
128
+ const response = await this.fetchJSON("/v1/chat/completions", {
129
+ method: "POST",
130
+ body: JSON.stringify(body)
131
+ });
132
+ return response;
133
+ } catch (err) {
134
+ lastError = err;
135
+ if (!isRetryable(err) || attempt === this.maxRetries) throw err;
136
+ let delay = this.retryBaseDelay * 2 ** attempt;
137
+ if (err instanceof RateLimitError && err.retryAfter) {
138
+ delay = Math.max(delay, err.retryAfter * 1e3);
139
+ }
140
+ await new Promise((r) => setTimeout(r, delay));
141
+ }
142
+ }
143
+ throw lastError;
144
+ }
145
+ /** Create a streaming chat completion. Returns an async iterator of chunks. */
146
+ async *streamCompletion(messages, options) {
147
+ const body = {
148
+ model: options?.model ?? "anthropic/claude-sonnet-4-20250514",
149
+ messages,
150
+ stream: true
151
+ };
152
+ if (options?.temperature !== void 0) body.temperature = options.temperature;
153
+ if (options?.maxTokens !== void 0) body.max_tokens = options.maxTokens;
154
+ if (options?.tools) body.tools = options.tools;
155
+ const controller = new AbortController();
156
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
157
+ try {
158
+ const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
159
+ method: "POST",
160
+ headers: this.headers,
161
+ body: JSON.stringify(body),
162
+ signal: controller.signal
163
+ });
164
+ if (!response.ok) {
165
+ const text = await response.text();
166
+ throw classifyError(response.status, text, response.headers);
167
+ }
168
+ if (!response.body) {
169
+ throw new EdgeCrabError("No response body for streaming request");
170
+ }
171
+ const reader = response.body.getReader();
172
+ const decoder = new TextDecoder();
173
+ let buffer = "";
174
+ while (true) {
175
+ const { done, value } = await reader.read();
176
+ if (done) break;
177
+ buffer += decoder.decode(value, { stream: true });
178
+ const lines = buffer.split("\n");
179
+ buffer = lines.pop() ?? "";
180
+ for (const line of lines) {
181
+ const trimmed = line.trim();
182
+ if (!trimmed.startsWith("data: ")) continue;
183
+ const payload = trimmed.slice(6);
184
+ if (payload === "[DONE]") return;
185
+ yield JSON.parse(payload);
186
+ }
187
+ }
188
+ } finally {
189
+ clearTimeout(timeoutId);
190
+ }
191
+ }
192
+ /** List available models. */
193
+ async listModels() {
194
+ const data = await this.fetchJSON("/v1/models");
195
+ if (Array.isArray(data)) return data;
196
+ return data.data ?? [];
197
+ }
198
+ /** Check server health. */
199
+ async health() {
200
+ return this.fetchJSON("/v1/health");
201
+ }
202
+ async fetchJSON(path, init) {
203
+ const controller = new AbortController();
204
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
205
+ try {
206
+ const response = await fetch(`${this.baseUrl}${path}`, {
207
+ ...init,
208
+ headers: { ...this.headers, ...init?.headers ?? {} },
209
+ signal: controller.signal
210
+ });
211
+ if (!response.ok) {
212
+ const text = await response.text();
213
+ let detail = text;
214
+ try {
215
+ const json = JSON.parse(text);
216
+ detail = json?.error?.message ?? text;
217
+ } catch {
218
+ }
219
+ throw classifyError(response.status, detail, response.headers);
220
+ }
221
+ return await response.json();
222
+ } finally {
223
+ clearTimeout(timeoutId);
224
+ }
225
+ }
226
+ };
227
+
228
+ // src/agent.ts
229
+ var Agent = class _Agent {
230
+ model;
231
+ systemPrompt;
232
+ maxTurns;
233
+ temperature;
234
+ maxTokens;
235
+ streaming;
236
+ sessionId;
237
+ // Callbacks
238
+ onToken;
239
+ onToolCall;
240
+ onTurn;
241
+ onError;
242
+ messages = [];
243
+ turnCount = 0;
244
+ totalUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
245
+ client;
246
+ interrupted = false;
247
+ constructor(options = {}) {
248
+ this.model = options.model ?? "anthropic/claude-sonnet-4-20250514";
249
+ this.systemPrompt = options.systemPrompt;
250
+ this.maxTurns = options.maxTurns ?? 50;
251
+ this.temperature = options.temperature;
252
+ this.maxTokens = options.maxTokens;
253
+ this.streaming = options.streaming ?? false;
254
+ this.sessionId = options.sessionId ?? randomUUID();
255
+ this.onToken = options.onToken;
256
+ this.onToolCall = options.onToolCall;
257
+ this.onTurn = options.onTurn;
258
+ this.onError = options.onError;
259
+ const baseUrl = options.baseUrl ?? process.env.EDGECRAB_BASE_URL ?? "http://127.0.0.1:8642";
260
+ const apiKey = options.apiKey ?? process.env.EDGECRAB_API_KEY;
261
+ this.client = new EdgeCrabClient({
262
+ baseUrl,
263
+ apiKey,
264
+ timeout: options.timeout ? options.timeout * 1e3 : void 0,
265
+ maxRetries: options.maxRetries
266
+ });
267
+ if (this.systemPrompt) {
268
+ this.messages.push({ role: "system", content: this.systemPrompt });
269
+ }
270
+ }
271
+ // ── Interrupt ───────────────────────────────────────────────────
272
+ /** Signal the agent to stop after the current turn. */
273
+ interrupt() {
274
+ this.interrupted = true;
275
+ }
276
+ /** Clear the interrupt flag so the agent can continue. */
277
+ clearInterrupt() {
278
+ this.interrupted = false;
279
+ }
280
+ /** Whether the agent has been interrupted. */
281
+ get isInterrupted() {
282
+ return this.interrupted;
283
+ }
284
+ // ── Chat ────────────────────────────────────────────────────────
285
+ /** Send a message and return the assistant's text reply. Maintains history. */
286
+ async chat(message) {
287
+ if (this.interrupted) throw new InterruptedError();
288
+ if (this.turnCount >= this.maxTurns) throw new MaxTurnsExceededError(this.maxTurns);
289
+ this.messages.push({ role: "user", content: message });
290
+ this.turnCount++;
291
+ if (this.streaming && this.onToken) {
292
+ return this.chatStreaming();
293
+ }
294
+ const resp = await this.client.createCompletion(this.messages, {
295
+ model: this.model,
296
+ temperature: this.temperature,
297
+ maxTokens: this.maxTokens
298
+ });
299
+ const assistantMsg = this.extractResponse(resp);
300
+ this.accumulateUsage(resp.usage);
301
+ this.onTurn?.(this.turnCount, assistantMsg);
302
+ return assistantMsg.content;
303
+ }
304
+ async chatStreaming() {
305
+ const collected = [];
306
+ for await (const chunk of this.client.streamCompletion(this.messages, {
307
+ model: this.model,
308
+ temperature: this.temperature,
309
+ maxTokens: this.maxTokens
310
+ })) {
311
+ if (this.interrupted) break;
312
+ for (const choice of chunk.choices) {
313
+ if (choice.delta.content) {
314
+ collected.push(choice.delta.content);
315
+ this.onToken?.(choice.delta.content);
316
+ }
317
+ }
318
+ }
319
+ const fullText = collected.join("");
320
+ const assistantMsg = { role: "assistant", content: fullText };
321
+ this.messages.push(assistantMsg);
322
+ this.onTurn?.(this.turnCount, assistantMsg);
323
+ return fullText;
324
+ }
325
+ // ── Run ─────────────────────────────────────────────────────────
326
+ /** Run a full agent conversation. Returns a structured AgentResult. */
327
+ async run(message, options) {
328
+ if (options?.conversationHistory) {
329
+ for (const msg of options.conversationHistory) {
330
+ this.messages.push(msg);
331
+ }
332
+ }
333
+ let response;
334
+ let wasInterrupted = false;
335
+ let wasMaxTurnsExceeded = false;
336
+ try {
337
+ response = await this.chat(message);
338
+ } catch (err) {
339
+ if (err instanceof InterruptedError) {
340
+ response = this.messages.length > 0 ? this.messages[this.messages.length - 1].content : "";
341
+ wasInterrupted = true;
342
+ } else if (err instanceof MaxTurnsExceededError) {
343
+ response = this.messages.length > 0 ? this.messages[this.messages.length - 1].content : "";
344
+ wasMaxTurnsExceeded = true;
345
+ } else {
346
+ throw err;
347
+ }
348
+ }
349
+ return {
350
+ response,
351
+ messages: [...this.messages],
352
+ sessionId: this.sessionId,
353
+ model: this.model,
354
+ turnsUsed: this.turnCount,
355
+ finishedNaturally: !wasInterrupted && !wasMaxTurnsExceeded,
356
+ interrupted: wasInterrupted,
357
+ maxTurnsExceeded: wasMaxTurnsExceeded,
358
+ usage: { ...this.totalUsage }
359
+ };
360
+ }
361
+ // ── Stream ──────────────────────────────────────────────────────
362
+ /** Stream response tokens as an async iterable. */
363
+ async *stream(message) {
364
+ if (this.interrupted) throw new InterruptedError();
365
+ if (this.turnCount >= this.maxTurns) throw new MaxTurnsExceededError(this.maxTurns);
366
+ this.messages.push({ role: "user", content: message });
367
+ this.turnCount++;
368
+ const collected = [];
369
+ for await (const chunk of this.client.streamCompletion(this.messages, {
370
+ model: this.model,
371
+ temperature: this.temperature,
372
+ maxTokens: this.maxTokens
373
+ })) {
374
+ if (this.interrupted) break;
375
+ for (const choice of chunk.choices) {
376
+ if (choice.delta.content) {
377
+ collected.push(choice.delta.content);
378
+ yield choice.delta.content;
379
+ }
380
+ }
381
+ }
382
+ this.messages.push({ role: "assistant", content: collected.join("") });
383
+ }
384
+ // ── Conversation management ─────────────────────────────────────
385
+ /** Manually inject a message into the conversation history. */
386
+ addMessage(role, content) {
387
+ this.messages.push({ role, content });
388
+ }
389
+ /** Reset conversation state for a new session. */
390
+ reset() {
391
+ this.messages = [];
392
+ this.turnCount = 0;
393
+ this.totalUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
394
+ this.sessionId = randomUUID();
395
+ this.interrupted = false;
396
+ if (this.systemPrompt) {
397
+ this.messages.push({ role: "system", content: this.systemPrompt });
398
+ }
399
+ }
400
+ // ── Conversation persistence ────────────────────────────────────
401
+ /** Export the current conversation state as a serializable object. */
402
+ exportConversation() {
403
+ return {
404
+ sessionId: this.sessionId,
405
+ model: this.model,
406
+ messages: [...this.messages],
407
+ turnCount: this.turnCount,
408
+ usage: { ...this.totalUsage }
409
+ };
410
+ }
411
+ /** Restore a conversation state from a previously exported object. */
412
+ importConversation(data) {
413
+ this.sessionId = data.sessionId ?? this.sessionId;
414
+ this.messages = [...data.messages ?? []];
415
+ this.turnCount = data.turnCount ?? 0;
416
+ if (data.usage) this.totalUsage = { ...data.usage };
417
+ }
418
+ /** Create a fork of this agent with an independent copy of the conversation. */
419
+ clone() {
420
+ const newAgent = new _Agent({
421
+ model: this.model,
422
+ systemPrompt: this.systemPrompt,
423
+ maxTurns: this.maxTurns,
424
+ temperature: this.temperature,
425
+ maxTokens: this.maxTokens,
426
+ streaming: this.streaming,
427
+ onToken: this.onToken,
428
+ onToolCall: this.onToolCall,
429
+ onTurn: this.onTurn,
430
+ onError: this.onError
431
+ });
432
+ newAgent.importConversation(this.exportConversation());
433
+ newAgent.sessionId = randomUUID();
434
+ return newAgent;
435
+ }
436
+ // ── Introspection ───────────────────────────────────────────────
437
+ /** Current conversation history (copy). */
438
+ getMessages() {
439
+ return [...this.messages];
440
+ }
441
+ /** Number of user turns completed. */
442
+ getTurnCount() {
443
+ return this.turnCount;
444
+ }
445
+ /** Accumulated token usage. */
446
+ getUsage() {
447
+ return { ...this.totalUsage };
448
+ }
449
+ /** List available models from the server. */
450
+ async listModels() {
451
+ return this.client.listModels();
452
+ }
453
+ /** Check server health. */
454
+ async health() {
455
+ return this.client.health();
456
+ }
457
+ // ── Internal ────────────────────────────────────────────────────
458
+ extractResponse(resp) {
459
+ const msg = resp.choices?.[0]?.message ?? { role: "assistant", content: "" };
460
+ this.messages.push(msg);
461
+ return msg;
462
+ }
463
+ accumulateUsage(usage) {
464
+ if (usage) {
465
+ this.totalUsage.prompt_tokens += usage.prompt_tokens;
466
+ this.totalUsage.completion_tokens += usage.completion_tokens;
467
+ this.totalUsage.total_tokens += usage.total_tokens;
468
+ }
469
+ }
470
+ };
471
+ export {
472
+ Agent,
473
+ AuthenticationError,
474
+ ConnectionError,
475
+ EdgeCrabClient,
476
+ EdgeCrabError,
477
+ InterruptedError,
478
+ MaxTurnsExceededError,
479
+ RateLimitError,
480
+ ServerError,
481
+ TimeoutError
482
+ };
@@ -0,0 +1,55 @@
1
+ // package.json
2
+ var package_default = {
3
+ name: "edgecrab-sdk",
4
+ version: "0.1.0",
5
+ description: "Node.js SDK for EdgeCrab \u2014 a Rust-native autonomous coding agent",
6
+ main: "./dist/index.cjs",
7
+ module: "./dist/index.js",
8
+ types: "./dist/index.d.ts",
9
+ exports: {
10
+ ".": {
11
+ types: "./dist/index.d.ts",
12
+ import: "./dist/index.js",
13
+ require: "./dist/index.cjs"
14
+ }
15
+ },
16
+ bin: {
17
+ edgecrab: "dist/cli.js"
18
+ },
19
+ files: [
20
+ "dist/",
21
+ "README.md"
22
+ ],
23
+ engines: {
24
+ node: ">=18"
25
+ },
26
+ license: "MIT",
27
+ homepage: "https://github.com/raphaelmansuy/edgecrab#readme",
28
+ repository: {
29
+ type: "git",
30
+ url: "git+https://github.com/raphaelmansuy/edgecrab.git",
31
+ directory: "sdks/node"
32
+ },
33
+ keywords: [
34
+ "ai",
35
+ "agent",
36
+ "llm",
37
+ "edgecrab",
38
+ "coding-agent",
39
+ "rust"
40
+ ],
41
+ scripts: {
42
+ build: "npx tsup src/index.ts --format cjs,esm --dts && npx tsup src/cli.ts --format esm --no-dts",
43
+ test: "npx vitest run",
44
+ "test:watch": "npx vitest"
45
+ },
46
+ devDependencies: {
47
+ "@types/node": "^22.0.0",
48
+ tsup: "^8.0.0",
49
+ typescript: "^5.0.0",
50
+ vitest: "^3.0.0"
51
+ }
52
+ };
53
+ export {
54
+ package_default as default
55
+ };
@@ -0,0 +1,55 @@
1
+ // package.json
2
+ var package_default = {
3
+ name: "edgecrab-sdk",
4
+ version: "0.1.0",
5
+ description: "Node.js SDK for EdgeCrab \u2014 a Rust-native autonomous coding agent",
6
+ main: "./dist/index.cjs",
7
+ module: "./dist/index.js",
8
+ types: "./dist/index.d.ts",
9
+ exports: {
10
+ ".": {
11
+ types: "./dist/index.d.ts",
12
+ import: "./dist/index.js",
13
+ require: "./dist/index.cjs"
14
+ }
15
+ },
16
+ bin: {
17
+ edgecrab: "dist/cli.mjs"
18
+ },
19
+ files: [
20
+ "dist/",
21
+ "README.md"
22
+ ],
23
+ engines: {
24
+ node: ">=18"
25
+ },
26
+ license: "MIT",
27
+ homepage: "https://github.com/raphaelmansuy/edgecrab#readme",
28
+ repository: {
29
+ type: "git",
30
+ url: "git+https://github.com/raphaelmansuy/edgecrab.git",
31
+ directory: "sdks/node"
32
+ },
33
+ keywords: [
34
+ "ai",
35
+ "agent",
36
+ "llm",
37
+ "edgecrab",
38
+ "coding-agent",
39
+ "rust"
40
+ ],
41
+ scripts: {
42
+ build: "npx tsup src/index.ts --format cjs,esm --dts && npx tsup src/cli.ts --format esm --no-dts",
43
+ test: "npx vitest run",
44
+ "test:watch": "npx vitest"
45
+ },
46
+ devDependencies: {
47
+ "@types/node": "^22.0.0",
48
+ tsup: "^8.0.0",
49
+ typescript: "^5.0.0",
50
+ vitest: "^3.0.0"
51
+ }
52
+ };
53
+ export {
54
+ package_default as default
55
+ };
@@ -0,0 +1,55 @@
1
+ // package.json
2
+ var package_default = {
3
+ name: "edgecrab-sdk",
4
+ version: "0.1.0",
5
+ description: "Node.js SDK for EdgeCrab \u2014 a Rust-native autonomous coding agent",
6
+ main: "./dist/index.cjs",
7
+ module: "./dist/index.js",
8
+ types: "./dist/index.d.ts",
9
+ exports: {
10
+ ".": {
11
+ types: "./dist/index.d.ts",
12
+ import: "./dist/index.js",
13
+ require: "./dist/index.cjs"
14
+ }
15
+ },
16
+ bin: {
17
+ edgecrab: "dist/cli.js"
18
+ },
19
+ files: [
20
+ "dist/",
21
+ "README.md"
22
+ ],
23
+ engines: {
24
+ node: ">=18"
25
+ },
26
+ license: "MIT",
27
+ homepage: "https://github.com/raphaelmansuy/edgecrab#readme",
28
+ repository: {
29
+ type: "git",
30
+ url: "git+https://github.com/raphaelmansuy/edgecrab.git",
31
+ directory: "sdks/node"
32
+ },
33
+ keywords: [
34
+ "ai",
35
+ "agent",
36
+ "llm",
37
+ "edgecrab",
38
+ "coding-agent",
39
+ "rust"
40
+ ],
41
+ scripts: {
42
+ build: "npx tsup src/index.ts src/cli.ts --format cjs,esm --dts",
43
+ test: "npx vitest run",
44
+ "test:watch": "npx vitest"
45
+ },
46
+ devDependencies: {
47
+ "@types/node": "^22.0.0",
48
+ tsup: "^8.0.0",
49
+ typescript: "^5.0.0",
50
+ vitest: "^3.0.0"
51
+ }
52
+ };
53
+ export {
54
+ package_default as default
55
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "edgecrab-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Node.js SDK for EdgeCrab — a Rust-native autonomous coding agent",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "bin": {
16
+ "edgecrab": "dist/cli.mjs"
17
+ },
18
+ "files": [
19
+ "dist/",
20
+ "README.md"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "license": "MIT",
26
+ "homepage": "https://github.com/raphaelmansuy/edgecrab#readme",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/raphaelmansuy/edgecrab.git",
30
+ "directory": "sdks/node"
31
+ },
32
+ "keywords": [
33
+ "ai",
34
+ "agent",
35
+ "llm",
36
+ "edgecrab",
37
+ "coding-agent",
38
+ "rust"
39
+ ],
40
+ "scripts": {
41
+ "build": "npx tsup src/index.ts --format cjs,esm --dts && npx tsup src/cli.ts --format esm --no-dts",
42
+ "test": "npx vitest run",
43
+ "test:watch": "npx vitest"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.0.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.0.0",
49
+ "vitest": "^3.0.0"
50
+ }
51
+ }