@usewind/node 0.1.0 → 0.1.2

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,60 @@ 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, settlement: "settled", idempotencyKey };
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 finalChunk(data.wind_final, idempotencyKey);
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) {
702
+ yield finalChunk(data, idempotencyKey);
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
+ }
727
+ }
728
+ function finalChunk(final, idempotencyKey) {
729
+ return {
730
+ delta: null,
731
+ wind: final.wind ?? null,
732
+ usage: final.usage ?? void 0,
733
+ settlement: final.settlement ?? "settled",
734
+ idempotencyKey
735
+ };
678
736
  }
679
737
  var TOLERANCE_S = 300;
680
738
  var WebhookVerifier = class {
@@ -997,7 +1055,7 @@ var Wind = class {
997
1055
  update: (appId, slug, patch) => this.raw.updateFeature(appId, slug, patch)
998
1056
  };
999
1057
  // ── webhooks ─────────────────────────────────────────────────────────────
1000
- /** Verify a webhook's `X-Wind-Signature` and return the typed event. Needs the raw body. */
1058
+ /** Verify a webhook's `Wind-Signature` and return the typed event. Needs the raw body. */
1001
1059
  verify(input) {
1002
1060
  return this.#verifier.verify(input);
1003
1061
  }