@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 +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +65 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -4
- package/dist/index.d.ts +58 -4
- package/dist/index.js +65 -7
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
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
|
-
`
|
|
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['
|
|
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
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
}
|