@buildwithtrace/sdk 0.1.2 → 0.1.3

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.d.mts CHANGED
@@ -323,7 +323,9 @@ declare class Trace {
323
323
  /**
324
324
  * Execute one tool call. FILE tools run locally (sandboxed to projectDir) and
325
325
  * their result is POSTed to /tools/result so the backend continues the stream.
326
- * Engine/GUI tools are out of scope and throw TraceToolExecutionError.
326
+ * Engine/GUI tools are out of scope: the SDK POSTs an is_error result (so the
327
+ * backend can finalize the turn) and continues — it does NOT throw, which would
328
+ * abandon the open stream.
327
329
  */
328
330
  private _handleToolCall;
329
331
  /**
package/dist/index.d.ts CHANGED
@@ -323,7 +323,9 @@ declare class Trace {
323
323
  /**
324
324
  * Execute one tool call. FILE tools run locally (sandboxed to projectDir) and
325
325
  * their result is POSTed to /tools/result so the backend continues the stream.
326
- * Engine/GUI tools are out of scope and throw TraceToolExecutionError.
326
+ * Engine/GUI tools are out of scope: the SDK POSTs an is_error result (so the
327
+ * backend can finalize the turn) and continues — it does NOT throw, which would
328
+ * abandon the open stream.
327
329
  */
328
330
  private _handleToolCall;
329
331
  /**
package/dist/index.js CHANGED
@@ -574,12 +574,6 @@ function openBrowser(url) {
574
574
  } catch {
575
575
  }
576
576
  }
577
- var SUCCESS_HTML = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Trace</title></head>
578
- <body style="font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:64px;color:#2a2119">
579
- <h1 style="font-weight:600">&#10003; Authentication successful</h1>
580
- <p>You can close this window and return to your terminal.</p>
581
- <script>setTimeout(function(){window.close();},500)</script>
582
- </body></html>`;
583
577
  var PENDING_HTML = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Trace</title></head>
584
578
  <body style="font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:64px;color:#2a2119">
585
579
  <h1 style="font-weight:600">Waiting for authentication&#8230;</h1>
@@ -637,7 +631,13 @@ function browserLogin(opts = {}) {
637
631
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(PENDING_HTML);
638
632
  return;
639
633
  }
640
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(SUCCESS_HTML);
634
+ const successUrl = `${frontendUrl}/login?success=desktop`;
635
+ res.writeHead(302, {
636
+ Location: successUrl,
637
+ "Content-Type": "text/html; charset=utf-8"
638
+ }).end(
639
+ `<!DOCTYPE html><meta http-equiv="refresh" content="0;url=${successUrl}"><body style="font-family:system-ui;text-align:center;padding:60px"><p>Signed in. Returning to Trace&#8230; <a href="${successUrl}">continue</a></p></body>`
640
+ );
641
641
  res.on("finish", shutdown);
642
642
  res.on("close", shutdown);
643
643
  settle(() => resolve3(result));
@@ -691,7 +691,7 @@ var import_posthog_node = require("posthog-node");
691
691
  // src/_build_config.ts
692
692
  var BUILD_POSTHOG_KEY = "phc_YbXW9ynLyGf9qHVxOY87InoZNSRikTCQ14GDJOZtSxX";
693
693
  var BUILD_POSTHOG_HOST = "https://us.i.posthog.com";
694
- var BUILD_SDK_VERSION = "0.1.2";
694
+ var BUILD_SDK_VERSION = "0.1.3";
695
695
 
696
696
  // src/analytics.ts
697
697
  var DEFAULT_HOST = "https://us.i.posthog.com";
@@ -854,6 +854,8 @@ function alias(distinctId, aliasId) {
854
854
  // src/index.ts
855
855
  var DEFAULT_API_URL2 = "https://api.buildwithtrace.com";
856
856
  var API_VERSION = "latest";
857
+ var RETRYABLE_STREAM_STATUS = /* @__PURE__ */ new Set([429, 502, 503, 504]);
858
+ var MAX_STREAM_RETRIES = 3;
857
859
  var TraceError = class extends Error {
858
860
  constructor(message) {
859
861
  super(message);
@@ -1184,21 +1186,35 @@ var Trace = class _Trace {
1184
1186
  async _streamChat(message, opts) {
1185
1187
  const sessionId = `sdk-${Date.now().toString(36)}`;
1186
1188
  const body = this._buildChatBody(message, opts, sessionId);
1187
- const resp = await fetch(`${this.baseUrl}/api/${API_VERSION}/chat/stream`, {
1188
- method: "POST",
1189
- headers: {
1190
- "Authorization": `Bearer ${this.apiKey}`,
1191
- "Content-Type": "application/json",
1192
- "Accept": "text/event-stream",
1193
- "User-Agent": "buildwithtrace-node-sdk/0.1.0"
1194
- },
1195
- body: JSON.stringify(body)
1196
- });
1197
- if (!resp.ok) {
1198
- const errText = await resp.text().catch(() => "");
1199
- throw _Trace._httpError(resp.status, errText, opts.mode);
1189
+ const controller = new AbortController();
1190
+ let idleTimer = setTimeout(
1191
+ () => controller.abort(),
1192
+ this.timeout
1193
+ );
1194
+ let resp;
1195
+ for (let attempt = 0; attempt < MAX_STREAM_RETRIES; attempt++) {
1196
+ resp = await fetch(`${this.baseUrl}/api/${API_VERSION}/chat/stream`, {
1197
+ method: "POST",
1198
+ headers: {
1199
+ "Authorization": `Bearer ${this.apiKey}`,
1200
+ "Content-Type": "application/json",
1201
+ "Accept": "text/event-stream",
1202
+ "User-Agent": "buildwithtrace-node-sdk/0.1.0"
1203
+ },
1204
+ body: JSON.stringify(body),
1205
+ signal: controller.signal
1206
+ });
1207
+ if (resp.ok || !RETRYABLE_STREAM_STATUS.has(resp.status) || attempt === MAX_STREAM_RETRIES - 1)
1208
+ break;
1209
+ await new Promise((r) => setTimeout(r, 500 * 2 ** attempt));
1210
+ }
1211
+ if (!resp || !resp.ok) {
1212
+ clearTimeout(idleTimer);
1213
+ const errText = resp ? await resp.text().catch(() => "") : "";
1214
+ throw _Trace._httpError(resp ? resp.status : 0, errText, opts.mode);
1200
1215
  }
1201
1216
  if (!resp.body) {
1217
+ clearTimeout(idleTimer);
1202
1218
  throw new TraceError("The backend returned an empty response stream.");
1203
1219
  }
1204
1220
  const projectDir = opts.projectDir ? (0, import_path2.resolve)(opts.projectDir) : process.cwd();
@@ -1233,6 +1249,8 @@ var Trace = class _Trace {
1233
1249
  for (; ; ) {
1234
1250
  const { done, value } = await reader.read();
1235
1251
  if (done) break;
1252
+ clearTimeout(idleTimer);
1253
+ idleTimer = setTimeout(() => controller.abort(), this.timeout);
1236
1254
  buffer += decoder.decode(value, { stream: true });
1237
1255
  let nl;
1238
1256
  while ((nl = buffer.indexOf("\n")) >= 0) {
@@ -1248,6 +1266,7 @@ var Trace = class _Trace {
1248
1266
  if (ev) await handleEvent(ev);
1249
1267
  }
1250
1268
  } finally {
1269
+ clearTimeout(idleTimer);
1251
1270
  reader.cancel().catch(() => {
1252
1271
  });
1253
1272
  }
@@ -1266,16 +1285,18 @@ var Trace = class _Trace {
1266
1285
  /**
1267
1286
  * Execute one tool call. FILE tools run locally (sandboxed to projectDir) and
1268
1287
  * their result is POSTed to /tools/result so the backend continues the stream.
1269
- * Engine/GUI tools are out of scope and throw TraceToolExecutionError.
1288
+ * Engine/GUI tools are out of scope: the SDK POSTs an is_error result (so the
1289
+ * backend can finalize the turn) and continues — it does NOT throw, which would
1290
+ * abandon the open stream.
1270
1291
  */
1271
1292
  async _handleToolCall(event, sessionId, projectDir, defaultFile) {
1272
1293
  const toolName = event.tool_name || "unknown";
1273
1294
  const toolCallId = event.tool_call_id || "";
1274
1295
  const toolArgs = event.tool_args || {};
1275
1296
  if (!isFileTool(toolName)) {
1276
- throw new TraceToolExecutionError(
1277
- `The agent requested '${toolName}', which the SDK does not execute. The SDK runs FILE tools only (read_file, write, search_replace, list_dir, grep, delete_trace_file); engine/GUI tools (ERC/DRC, gerber/export, take_snapshot, run_*, autoroute) require the \`buildwithtrace\` CLI or the desktop app.`
1278
- );
1297
+ const msg = `The agent requested '${toolName}', which the SDK does not execute. The SDK runs FILE tools only (read_file, write, search_replace, list_dir, grep, delete_trace_file); engine/GUI tools (ERC/DRC, gerber/export, take_snapshot, run_*, autoroute) require the \`buildwithtrace\` CLI or the desktop app.`;
1298
+ await this._postToolResult(sessionId, toolCallId, `Error: ${msg}`, true);
1299
+ return;
1279
1300
  }
1280
1301
  const result = executeFileTool(toolName, toolArgs, projectDir, defaultFile);
1281
1302
  await this._postToolResult(sessionId, toolCallId, result.result, !result.success);
package/dist/index.mjs CHANGED
@@ -537,12 +537,6 @@ function openBrowser(url) {
537
537
  } catch {
538
538
  }
539
539
  }
540
- var SUCCESS_HTML = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Trace</title></head>
541
- <body style="font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:64px;color:#2a2119">
542
- <h1 style="font-weight:600">&#10003; Authentication successful</h1>
543
- <p>You can close this window and return to your terminal.</p>
544
- <script>setTimeout(function(){window.close();},500)</script>
545
- </body></html>`;
546
540
  var PENDING_HTML = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Trace</title></head>
547
541
  <body style="font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:64px;color:#2a2119">
548
542
  <h1 style="font-weight:600">Waiting for authentication&#8230;</h1>
@@ -600,7 +594,13 @@ function browserLogin(opts = {}) {
600
594
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(PENDING_HTML);
601
595
  return;
602
596
  }
603
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }).end(SUCCESS_HTML);
597
+ const successUrl = `${frontendUrl}/login?success=desktop`;
598
+ res.writeHead(302, {
599
+ Location: successUrl,
600
+ "Content-Type": "text/html; charset=utf-8"
601
+ }).end(
602
+ `<!DOCTYPE html><meta http-equiv="refresh" content="0;url=${successUrl}"><body style="font-family:system-ui;text-align:center;padding:60px"><p>Signed in. Returning to Trace&#8230; <a href="${successUrl}">continue</a></p></body>`
603
+ );
604
604
  res.on("finish", shutdown);
605
605
  res.on("close", shutdown);
606
606
  settle(() => resolve3(result));
@@ -654,7 +654,7 @@ import { PostHog } from "posthog-node";
654
654
  // src/_build_config.ts
655
655
  var BUILD_POSTHOG_KEY = "phc_YbXW9ynLyGf9qHVxOY87InoZNSRikTCQ14GDJOZtSxX";
656
656
  var BUILD_POSTHOG_HOST = "https://us.i.posthog.com";
657
- var BUILD_SDK_VERSION = "0.1.2";
657
+ var BUILD_SDK_VERSION = "0.1.3";
658
658
 
659
659
  // src/analytics.ts
660
660
  var DEFAULT_HOST = "https://us.i.posthog.com";
@@ -817,6 +817,8 @@ function alias(distinctId, aliasId) {
817
817
  // src/index.ts
818
818
  var DEFAULT_API_URL2 = "https://api.buildwithtrace.com";
819
819
  var API_VERSION = "latest";
820
+ var RETRYABLE_STREAM_STATUS = /* @__PURE__ */ new Set([429, 502, 503, 504]);
821
+ var MAX_STREAM_RETRIES = 3;
820
822
  var TraceError = class extends Error {
821
823
  constructor(message) {
822
824
  super(message);
@@ -1147,21 +1149,35 @@ var Trace = class _Trace {
1147
1149
  async _streamChat(message, opts) {
1148
1150
  const sessionId = `sdk-${Date.now().toString(36)}`;
1149
1151
  const body = this._buildChatBody(message, opts, sessionId);
1150
- const resp = await fetch(`${this.baseUrl}/api/${API_VERSION}/chat/stream`, {
1151
- method: "POST",
1152
- headers: {
1153
- "Authorization": `Bearer ${this.apiKey}`,
1154
- "Content-Type": "application/json",
1155
- "Accept": "text/event-stream",
1156
- "User-Agent": "buildwithtrace-node-sdk/0.1.0"
1157
- },
1158
- body: JSON.stringify(body)
1159
- });
1160
- if (!resp.ok) {
1161
- const errText = await resp.text().catch(() => "");
1162
- throw _Trace._httpError(resp.status, errText, opts.mode);
1152
+ const controller = new AbortController();
1153
+ let idleTimer = setTimeout(
1154
+ () => controller.abort(),
1155
+ this.timeout
1156
+ );
1157
+ let resp;
1158
+ for (let attempt = 0; attempt < MAX_STREAM_RETRIES; attempt++) {
1159
+ resp = await fetch(`${this.baseUrl}/api/${API_VERSION}/chat/stream`, {
1160
+ method: "POST",
1161
+ headers: {
1162
+ "Authorization": `Bearer ${this.apiKey}`,
1163
+ "Content-Type": "application/json",
1164
+ "Accept": "text/event-stream",
1165
+ "User-Agent": "buildwithtrace-node-sdk/0.1.0"
1166
+ },
1167
+ body: JSON.stringify(body),
1168
+ signal: controller.signal
1169
+ });
1170
+ if (resp.ok || !RETRYABLE_STREAM_STATUS.has(resp.status) || attempt === MAX_STREAM_RETRIES - 1)
1171
+ break;
1172
+ await new Promise((r) => setTimeout(r, 500 * 2 ** attempt));
1173
+ }
1174
+ if (!resp || !resp.ok) {
1175
+ clearTimeout(idleTimer);
1176
+ const errText = resp ? await resp.text().catch(() => "") : "";
1177
+ throw _Trace._httpError(resp ? resp.status : 0, errText, opts.mode);
1163
1178
  }
1164
1179
  if (!resp.body) {
1180
+ clearTimeout(idleTimer);
1165
1181
  throw new TraceError("The backend returned an empty response stream.");
1166
1182
  }
1167
1183
  const projectDir = opts.projectDir ? resolve2(opts.projectDir) : process.cwd();
@@ -1196,6 +1212,8 @@ var Trace = class _Trace {
1196
1212
  for (; ; ) {
1197
1213
  const { done, value } = await reader.read();
1198
1214
  if (done) break;
1215
+ clearTimeout(idleTimer);
1216
+ idleTimer = setTimeout(() => controller.abort(), this.timeout);
1199
1217
  buffer += decoder.decode(value, { stream: true });
1200
1218
  let nl;
1201
1219
  while ((nl = buffer.indexOf("\n")) >= 0) {
@@ -1211,6 +1229,7 @@ var Trace = class _Trace {
1211
1229
  if (ev) await handleEvent(ev);
1212
1230
  }
1213
1231
  } finally {
1232
+ clearTimeout(idleTimer);
1214
1233
  reader.cancel().catch(() => {
1215
1234
  });
1216
1235
  }
@@ -1229,16 +1248,18 @@ var Trace = class _Trace {
1229
1248
  /**
1230
1249
  * Execute one tool call. FILE tools run locally (sandboxed to projectDir) and
1231
1250
  * their result is POSTed to /tools/result so the backend continues the stream.
1232
- * Engine/GUI tools are out of scope and throw TraceToolExecutionError.
1251
+ * Engine/GUI tools are out of scope: the SDK POSTs an is_error result (so the
1252
+ * backend can finalize the turn) and continues — it does NOT throw, which would
1253
+ * abandon the open stream.
1233
1254
  */
1234
1255
  async _handleToolCall(event, sessionId, projectDir, defaultFile) {
1235
1256
  const toolName = event.tool_name || "unknown";
1236
1257
  const toolCallId = event.tool_call_id || "";
1237
1258
  const toolArgs = event.tool_args || {};
1238
1259
  if (!isFileTool(toolName)) {
1239
- throw new TraceToolExecutionError(
1240
- `The agent requested '${toolName}', which the SDK does not execute. The SDK runs FILE tools only (read_file, write, search_replace, list_dir, grep, delete_trace_file); engine/GUI tools (ERC/DRC, gerber/export, take_snapshot, run_*, autoroute) require the \`buildwithtrace\` CLI or the desktop app.`
1241
- );
1260
+ const msg = `The agent requested '${toolName}', which the SDK does not execute. The SDK runs FILE tools only (read_file, write, search_replace, list_dir, grep, delete_trace_file); engine/GUI tools (ERC/DRC, gerber/export, take_snapshot, run_*, autoroute) require the \`buildwithtrace\` CLI or the desktop app.`;
1261
+ await this._postToolResult(sessionId, toolCallId, `Error: ${msg}`, true);
1262
+ return;
1242
1263
  }
1243
1264
  const result = executeFileTool(toolName, toolArgs, projectDir, defaultFile);
1244
1265
  await this._postToolResult(sessionId, toolCallId, result.result, !result.success);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildwithtrace/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "TypeScript SDK for Trace — AI-powered PCB & schematic design",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",