@usewind/node 0.1.0 → 0.1.1

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/DESIGN.md CHANGED
@@ -270,7 +270,7 @@ const event = wind.verify(req) // throws WindSignatureError on mismatch
270
270
  ```
271
271
 
272
272
  - HMAC-SHA256 over the raw body with `webhookSecret`, constant-time compare,
273
- `X-Wind-Signature: t=<unix>,v1=<hex>` header, 5-minute timestamp tolerance
273
+ `Wind-Signature: t=<unix>,v1=<hex>` header, 5-minute timestamp tolerance
274
274
  (replay guard). Requires the raw (unparsed) body — documented loudly.
275
275
 
276
276
  ### Escape hatch
package/README.md CHANGED
@@ -44,7 +44,7 @@ const res = await wind.connection(userId).model('gpt-4o').chat({
44
44
  const res2 = await wind.connection(userId).feature('chat').chat({ messages })
45
45
 
46
46
  // 3. webhooks
47
- const event = wind.verify({ payload: rawBody, signature: req.headers['x-wind-signature'] })
47
+ const event = wind.verify({ payload: rawBody, signature: req.headers['wind-signature'] })
48
48
  ```
49
49
 
50
50
  Credentials come from `wind.configure({ … })` or the env vars `WIND_CLIENT_ID`,
package/dist/index.cjs CHANGED
@@ -236,6 +236,9 @@ var RawClient = class {
236
236
  signal: req.signal
237
237
  });
238
238
  const requestId = res.headers.get("x-request-id");
239
+ if (req.stream && res.ok && res.body && (res.headers.get("content-type") ?? "").includes("text/event-stream")) {
240
+ return { status: res.status, headers: res.headers, body: res.body, requestId };
241
+ }
239
242
  let parsed = void 0;
240
243
  const text = await res.text();
241
244
  if (text) {
@@ -290,7 +293,8 @@ var RawClient = class {
290
293
  bearer: args.accessToken,
291
294
  headers,
292
295
  body: args.body,
293
- signal: args.signal
296
+ signal: args.signal,
297
+ stream: isStreamBody(args.body)
294
298
  });
295
299
  }
296
300
  /** `POST /v1/ai/features/{slug}`. */
@@ -301,7 +305,8 @@ var RawClient = class {
301
305
  bearer: args.accessToken,
302
306
  headers: { "Idempotency-Key": args.idempotencyKey },
303
307
  body: args.body,
304
- signal: args.signal
308
+ signal: args.signal,
309
+ stream: isStreamBody(args.body)
305
310
  });
306
311
  }
307
312
  // ── Management (client-credentials) ────────────────────────────────────────
@@ -364,6 +369,9 @@ var RawClient = class {
364
369
  return this.request({ method: "POST", path, ...init });
365
370
  }
366
371
  };
372
+ function isStreamBody(body) {
373
+ return typeof body === "object" && body !== null && body.stream === true;
374
+ }
367
375
 
368
376
  // src/tokens.ts
369
377
  var ACCESS_TOKEN_SKEW_MS = 6e4;
@@ -622,6 +630,9 @@ function requestBody(params) {
622
630
  messages: params.messages,
623
631
  ...params.max_tokens !== void 0 && { max_tokens: params.max_tokens },
624
632
  ...params.temperature !== void 0 && { temperature: params.temperature },
633
+ ...params.tools !== void 0 && { tools: params.tools },
634
+ ...params.tool_choice !== void 0 && { tool_choice: params.tool_choice },
635
+ ...params.parallel_tool_calls !== void 0 && { parallel_tool_calls: params.parallel_tool_calls },
625
636
  ...params.stream !== void 0 && { stream: params.stream }
626
637
  };
627
638
  }
@@ -668,13 +679,51 @@ async function* billedStream(ctx, userId, params, call) {
668
679
  if (res.status < 200 || res.status >= 300) {
669
680
  throw WindError.fromResponse(res.status, res.body, { requestId: res.requestId });
670
681
  }
671
- const contentType = res.headers.get("content-type") ?? "";
672
- if (!contentType.includes("text/event-stream")) {
682
+ if (!(res.body instanceof ReadableStream)) {
673
683
  const result = res.body;
674
- yield { delta: result, wind: result.wind };
684
+ yield { delta: result, wind: result.wind ?? null, usage: result.usage };
675
685
  return;
676
686
  }
677
- yield { delta: res.body, wind: null };
687
+ for await (const event of sseEvents(res.body)) {
688
+ if (event === "[DONE]") return;
689
+ let data;
690
+ try {
691
+ data = JSON.parse(event);
692
+ } catch {
693
+ continue;
694
+ }
695
+ if (data?.wind_final) {
696
+ yield { delta: null, wind: data.wind_final.wind ?? null, usage: data.wind_final.usage };
697
+ } else if (data?.error) {
698
+ const err = data.error.error ?? data.error;
699
+ const status = err.code === "provider_error" ? 502 : 403;
700
+ throw WindError.fromResponse(status, { error: err }, { requestId: res.requestId });
701
+ } else if (data?.streamed && data.wind) {
702
+ yield { delta: null, wind: data.wind, usage: data.usage };
703
+ } else {
704
+ yield { delta: data, wind: null };
705
+ }
706
+ }
707
+ }
708
+ async function* sseEvents(stream) {
709
+ const reader = stream.getReader();
710
+ const decoder = new TextDecoder();
711
+ let buffer = "";
712
+ try {
713
+ while (true) {
714
+ const { done, value } = await reader.read();
715
+ if (done) break;
716
+ buffer += decoder.decode(value, { stream: true });
717
+ const lines = buffer.split("\n");
718
+ buffer = lines.pop() ?? "";
719
+ for (const line of lines) {
720
+ if (line.startsWith("data:")) yield line.slice(5).trim();
721
+ }
722
+ }
723
+ if (buffer.startsWith("data:")) yield buffer.slice(5).trim();
724
+ } finally {
725
+ reader.releaseLock();
726
+ }
678
727
  }
679
728
  var TOLERANCE_S = 300;
680
729
  var WebhookVerifier = class {
@@ -997,7 +1046,7 @@ var Wind = class {
997
1046
  update: (appId, slug, patch) => this.raw.updateFeature(appId, slug, patch)
998
1047
  };
999
1048
  // ── webhooks ─────────────────────────────────────────────────────────────
1000
- /** Verify a webhook's `X-Wind-Signature` and return the typed event. Needs the raw body. */
1049
+ /** Verify a webhook's `Wind-Signature` and return the typed event. Needs the raw body. */
1001
1050
  verify(input) {
1002
1051
  return this.#verifier.verify(input);
1003
1052
  }