@openkeyai/sdk 0.1.0 → 0.2.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/README.md +52 -4
- package/dist/index.cjs +368 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +525 -10
- package/dist/index.d.ts +525 -10
- package/dist/index.js +363 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -117,6 +117,41 @@ var SecureKeyConsumedError = class extends HubSdkError {
|
|
|
117
117
|
this.name = "SecureKeyConsumedError";
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
+
var ProviderNotConfiguredError = class extends HubSdkError {
|
|
121
|
+
constructor(provider) {
|
|
122
|
+
super(
|
|
123
|
+
"provider_not_configured",
|
|
124
|
+
`Provider "${provider}" is not registered with the hub proxy. Available providers are listed at /api/proxy (coming) or in the hub's src/lib/proxy/providers.ts.`,
|
|
125
|
+
404
|
|
126
|
+
);
|
|
127
|
+
this.name = "ProviderNotConfiguredError";
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var ProviderError = class extends HubSdkError {
|
|
131
|
+
/** The provider slug we tried to call. */
|
|
132
|
+
provider;
|
|
133
|
+
/** The path on the provider that returned non-2xx. */
|
|
134
|
+
path;
|
|
135
|
+
/**
|
|
136
|
+
* The upstream response body. Parsed as JSON when the upstream's
|
|
137
|
+
* content-type was JSON; raw text otherwise.
|
|
138
|
+
*/
|
|
139
|
+
body;
|
|
140
|
+
/** The upstream response status code (relayed unchanged). */
|
|
141
|
+
upstreamStatus;
|
|
142
|
+
constructor(provider, path, upstreamStatus, body) {
|
|
143
|
+
super(
|
|
144
|
+
"provider_error",
|
|
145
|
+
`${provider} ${path} returned ${upstreamStatus}.`,
|
|
146
|
+
upstreamStatus
|
|
147
|
+
);
|
|
148
|
+
this.name = "ProviderError";
|
|
149
|
+
this.provider = provider;
|
|
150
|
+
this.path = path;
|
|
151
|
+
this.upstreamStatus = upstreamStatus;
|
|
152
|
+
this.body = body;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
120
155
|
function errorFromResponse(status, body, context) {
|
|
121
156
|
const code = body?.error ?? "internal";
|
|
122
157
|
switch (code) {
|
|
@@ -134,6 +169,8 @@ function errorFromResponse(status, body, context) {
|
|
|
134
169
|
return new NotSubscribedError();
|
|
135
170
|
case "provider_not_granted":
|
|
136
171
|
return new ProviderNotGrantedError(context.provider ?? "unknown");
|
|
172
|
+
case "provider_not_configured":
|
|
173
|
+
return new ProviderNotConfiguredError(context.provider ?? "unknown");
|
|
137
174
|
case "rate_limited":
|
|
138
175
|
return new RateLimitedError(context.retryAfter ?? 60);
|
|
139
176
|
case "key_not_found":
|
|
@@ -143,6 +180,25 @@ function errorFromResponse(status, body, context) {
|
|
|
143
180
|
return new InternalError(status);
|
|
144
181
|
}
|
|
145
182
|
}
|
|
183
|
+
function isHubErrorBody(body) {
|
|
184
|
+
if (typeof body !== "object" || body === null) return false;
|
|
185
|
+
const errorField = body.error;
|
|
186
|
+
if (typeof errorField !== "string") return false;
|
|
187
|
+
const knownCodes = [
|
|
188
|
+
"missing_token",
|
|
189
|
+
"bad_token",
|
|
190
|
+
"missing_scope",
|
|
191
|
+
"subscription_inactive",
|
|
192
|
+
"tool_not_found",
|
|
193
|
+
"not_subscribed",
|
|
194
|
+
"provider_not_granted",
|
|
195
|
+
"rate_limited",
|
|
196
|
+
"key_not_found",
|
|
197
|
+
"internal",
|
|
198
|
+
"provider_not_configured"
|
|
199
|
+
];
|
|
200
|
+
return knownCodes.includes(errorField);
|
|
201
|
+
}
|
|
146
202
|
var resolvers = /* @__PURE__ */ new Map();
|
|
147
203
|
function getJwksResolver(hubUrl) {
|
|
148
204
|
const cached = resolvers.get(hubUrl);
|
|
@@ -197,6 +253,7 @@ async function verify(jwt, opts = {}) {
|
|
|
197
253
|
const jti = payload.jti;
|
|
198
254
|
const iat = payload.iat;
|
|
199
255
|
const exp = payload.exp;
|
|
256
|
+
const rawMode = payload.mode;
|
|
200
257
|
if (typeof sub !== "string" || sub.length === 0) {
|
|
201
258
|
throw new BadTokenError("sub claim missing or invalid.");
|
|
202
259
|
}
|
|
@@ -215,12 +272,19 @@ async function verify(jwt, opts = {}) {
|
|
|
215
272
|
if (typeof iat !== "number" || typeof exp !== "number") {
|
|
216
273
|
throw new BadTokenError("iat / exp claims missing or invalid.");
|
|
217
274
|
}
|
|
275
|
+
const mode = typeof rawMode === "string" && (rawMode === "production" || rawMode === "owner_test") ? rawMode : rawMode === void 0 ? "production" : null;
|
|
276
|
+
if (mode === null) {
|
|
277
|
+
throw new BadTokenError(
|
|
278
|
+
`mode claim must be 'production' or 'owner_test' (got ${JSON.stringify(rawMode)}).`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
218
281
|
return {
|
|
219
282
|
iss: "https://openkeyai.com",
|
|
220
283
|
sub,
|
|
221
284
|
aud,
|
|
222
285
|
scopes,
|
|
223
286
|
subscription_active: subscriptionActive,
|
|
287
|
+
mode,
|
|
224
288
|
iat,
|
|
225
289
|
exp,
|
|
226
290
|
jti
|
|
@@ -355,6 +419,298 @@ async function get(jwt, provider, opts = {}) {
|
|
|
355
419
|
});
|
|
356
420
|
}
|
|
357
421
|
|
|
422
|
+
// src/proxy.ts
|
|
423
|
+
var PROXY_PATH = "/api/proxy";
|
|
424
|
+
function ensureBearer(token) {
|
|
425
|
+
if (!token || typeof token !== "string") {
|
|
426
|
+
throw new BadTokenError("No JWT provided.");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function buildUrl(opts) {
|
|
430
|
+
if (!opts.provider) {
|
|
431
|
+
throw new BadTokenError("ProxyCallOptions.provider is required.");
|
|
432
|
+
}
|
|
433
|
+
if (!opts.path || !opts.path.startsWith("/")) {
|
|
434
|
+
throw new BadTokenError(
|
|
435
|
+
"ProxyCallOptions.path must start with '/' (e.g. '/v1/chat/completions')."
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const hubUrl = opts.hubUrl ?? "https://openkeyai.com";
|
|
439
|
+
return new URL(
|
|
440
|
+
`${PROXY_PATH}/${encodeURIComponent(opts.provider)}${opts.path}`,
|
|
441
|
+
hubUrl
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
function buildRequestInit(token, opts) {
|
|
445
|
+
const headers = new Headers();
|
|
446
|
+
if (opts.headers) {
|
|
447
|
+
for (const [k, v] of Object.entries(opts.headers)) {
|
|
448
|
+
headers.set(k, v);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
452
|
+
let body;
|
|
453
|
+
if (opts.method === "GET" || opts.method === "DELETE" || opts.body == null) {
|
|
454
|
+
body = void 0;
|
|
455
|
+
} else if (typeof opts.body === "string" || opts.body instanceof Blob || opts.body instanceof FormData || opts.body instanceof ArrayBuffer || opts.body instanceof ReadableStream) {
|
|
456
|
+
body = opts.body;
|
|
457
|
+
} else {
|
|
458
|
+
body = JSON.stringify(opts.body);
|
|
459
|
+
if (!headers.has("content-type")) {
|
|
460
|
+
headers.set("content-type", "application/json");
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (!headers.has("accept")) {
|
|
464
|
+
headers.set("accept", "application/json");
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
method: opts.method,
|
|
468
|
+
headers,
|
|
469
|
+
body,
|
|
470
|
+
signal: opts.signal,
|
|
471
|
+
// @ts-expect-error duplex is part of fetch's RequestInit on Workers + recent Node
|
|
472
|
+
duplex: body instanceof ReadableStream ? "half" : void 0
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async function rawFetch(token, opts) {
|
|
476
|
+
ensureBearer(token);
|
|
477
|
+
const url = buildUrl(opts);
|
|
478
|
+
try {
|
|
479
|
+
return await fetch(url.toString(), buildRequestInit(token, opts));
|
|
480
|
+
} catch (err) {
|
|
481
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
482
|
+
throw err;
|
|
483
|
+
}
|
|
484
|
+
throw new NetworkError(err);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async function parseBodyByContentType(response) {
|
|
488
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
489
|
+
if (ct.includes("application/json")) {
|
|
490
|
+
try {
|
|
491
|
+
return await response.json();
|
|
492
|
+
} catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
return await response.text();
|
|
498
|
+
} catch {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async function handleResponse(response, opts, expectJson) {
|
|
503
|
+
if (response.ok) {
|
|
504
|
+
if (!expectJson) return response;
|
|
505
|
+
return await response.json();
|
|
506
|
+
}
|
|
507
|
+
const body = await parseBodyByContentType(response);
|
|
508
|
+
if (isHubErrorBody(body)) {
|
|
509
|
+
const retryAfter = parseInt(response.headers.get("retry-after") ?? "60", 10);
|
|
510
|
+
throw errorFromResponse(
|
|
511
|
+
response.status,
|
|
512
|
+
{ error: body.error },
|
|
513
|
+
{
|
|
514
|
+
provider: opts.provider,
|
|
515
|
+
scopeNeeded: "keys.read",
|
|
516
|
+
retryAfter: Number.isFinite(retryAfter) ? retryAfter : 60
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
throw new ProviderError(opts.provider, opts.path, response.status, body);
|
|
521
|
+
}
|
|
522
|
+
async function call(token, opts) {
|
|
523
|
+
const response = await rawFetch(token, opts);
|
|
524
|
+
return handleResponse(response, opts, true);
|
|
525
|
+
}
|
|
526
|
+
async function callRaw(token, opts) {
|
|
527
|
+
const response = await rawFetch(token, opts);
|
|
528
|
+
return handleResponse(response, opts, false);
|
|
529
|
+
}
|
|
530
|
+
async function callStream(token, opts) {
|
|
531
|
+
const response = await rawFetch(token, opts);
|
|
532
|
+
if (!response.ok) {
|
|
533
|
+
const body = await parseBodyByContentType(response);
|
|
534
|
+
if (isHubErrorBody(body)) {
|
|
535
|
+
const retryAfter = parseInt(
|
|
536
|
+
response.headers.get("retry-after") ?? "60",
|
|
537
|
+
10
|
|
538
|
+
);
|
|
539
|
+
throw errorFromResponse(
|
|
540
|
+
response.status,
|
|
541
|
+
{ error: body.error },
|
|
542
|
+
{
|
|
543
|
+
provider: opts.provider,
|
|
544
|
+
scopeNeeded: "keys.read",
|
|
545
|
+
retryAfter: Number.isFinite(retryAfter) ? retryAfter : 60
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
throw new ProviderError(opts.provider, opts.path, response.status, body);
|
|
550
|
+
}
|
|
551
|
+
if (response.body == null) {
|
|
552
|
+
throw new BadTokenError("Upstream returned 2xx with no body to stream.");
|
|
553
|
+
}
|
|
554
|
+
return response.body;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/providers/openai.ts
|
|
558
|
+
var openai = {
|
|
559
|
+
images: {
|
|
560
|
+
generate(token, params, opts = {}) {
|
|
561
|
+
return call(token, {
|
|
562
|
+
...opts,
|
|
563
|
+
provider: "openai",
|
|
564
|
+
method: "POST",
|
|
565
|
+
path: "/v1/images/generations",
|
|
566
|
+
body: params
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
chat: {
|
|
571
|
+
completions: {
|
|
572
|
+
create(token, params, opts = {}) {
|
|
573
|
+
return call(token, {
|
|
574
|
+
...opts,
|
|
575
|
+
provider: "openai",
|
|
576
|
+
method: "POST",
|
|
577
|
+
path: "/v1/chat/completions",
|
|
578
|
+
body: params
|
|
579
|
+
});
|
|
580
|
+
},
|
|
581
|
+
stream(token, params, opts = {}) {
|
|
582
|
+
return callStream(token, {
|
|
583
|
+
...opts,
|
|
584
|
+
provider: "openai",
|
|
585
|
+
method: "POST",
|
|
586
|
+
path: "/v1/chat/completions",
|
|
587
|
+
body: { ...params, stream: true }
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
embeddings: {
|
|
593
|
+
create(token, params, opts = {}) {
|
|
594
|
+
return call(token, {
|
|
595
|
+
...opts,
|
|
596
|
+
provider: "openai",
|
|
597
|
+
method: "POST",
|
|
598
|
+
path: "/v1/embeddings",
|
|
599
|
+
body: params
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
audio: {
|
|
604
|
+
transcriptions: {
|
|
605
|
+
create(token, params, opts = {}) {
|
|
606
|
+
const fd = new FormData();
|
|
607
|
+
fd.append("file", params.file, params.filename ?? "audio.webm");
|
|
608
|
+
fd.append("model", params.model);
|
|
609
|
+
if (params.language) fd.append("language", params.language);
|
|
610
|
+
if (params.prompt) fd.append("prompt", params.prompt);
|
|
611
|
+
if (params.response_format)
|
|
612
|
+
fd.append("response_format", params.response_format);
|
|
613
|
+
if (params.temperature !== void 0)
|
|
614
|
+
fd.append("temperature", String(params.temperature));
|
|
615
|
+
return call(token, {
|
|
616
|
+
...opts,
|
|
617
|
+
provider: "openai",
|
|
618
|
+
method: "POST",
|
|
619
|
+
path: "/v1/audio/transcriptions",
|
|
620
|
+
body: fd
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
speech: {
|
|
625
|
+
create(token, params, opts = {}) {
|
|
626
|
+
return callRaw(token, {
|
|
627
|
+
...opts,
|
|
628
|
+
provider: "openai",
|
|
629
|
+
method: "POST",
|
|
630
|
+
path: "/v1/audio/speech",
|
|
631
|
+
body: params
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/providers/anthropic.ts
|
|
639
|
+
var anthropic = {
|
|
640
|
+
messages: {
|
|
641
|
+
create(token, params, opts = {}) {
|
|
642
|
+
return call(token, {
|
|
643
|
+
...opts,
|
|
644
|
+
provider: "anthropic",
|
|
645
|
+
method: "POST",
|
|
646
|
+
path: "/v1/messages",
|
|
647
|
+
body: params
|
|
648
|
+
});
|
|
649
|
+
},
|
|
650
|
+
stream(token, params, opts = {}) {
|
|
651
|
+
return callStream(token, {
|
|
652
|
+
...opts,
|
|
653
|
+
provider: "anthropic",
|
|
654
|
+
method: "POST",
|
|
655
|
+
path: "/v1/messages",
|
|
656
|
+
body: { ...params, stream: true }
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
// src/providers/replicate.ts
|
|
663
|
+
var replicate = {
|
|
664
|
+
predictions: {
|
|
665
|
+
create(token, params, opts = {}) {
|
|
666
|
+
return call(token, {
|
|
667
|
+
...opts,
|
|
668
|
+
provider: "replicate",
|
|
669
|
+
method: "POST",
|
|
670
|
+
path: "/v1/predictions",
|
|
671
|
+
body: params
|
|
672
|
+
});
|
|
673
|
+
},
|
|
674
|
+
get(token, predictionId, opts = {}) {
|
|
675
|
+
return call(token, {
|
|
676
|
+
...opts,
|
|
677
|
+
provider: "replicate",
|
|
678
|
+
method: "GET",
|
|
679
|
+
path: `/v1/predictions/${encodeURIComponent(predictionId)}`
|
|
680
|
+
});
|
|
681
|
+
},
|
|
682
|
+
cancel(token, predictionId, opts = {}) {
|
|
683
|
+
return call(token, {
|
|
684
|
+
...opts,
|
|
685
|
+
provider: "replicate",
|
|
686
|
+
method: "POST",
|
|
687
|
+
path: `/v1/predictions/${encodeURIComponent(predictionId)}/cancel`
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
/**
|
|
691
|
+
* Convenience: create + poll until the prediction reaches a terminal
|
|
692
|
+
* status. Polls every `pollIntervalMs` (default 1000ms) with the
|
|
693
|
+
* provided AbortSignal honoured.
|
|
694
|
+
*
|
|
695
|
+
* For long-running predictions consider using webhooks via
|
|
696
|
+
* `create({ webhook, webhook_events_filter })` instead so you're not
|
|
697
|
+
* holding open a long fetch.
|
|
698
|
+
*/
|
|
699
|
+
async run(token, params, opts = {}) {
|
|
700
|
+
const interval = opts.pollIntervalMs ?? 1e3;
|
|
701
|
+
let prediction = await this.create(token, params, opts);
|
|
702
|
+
while (prediction.status === "starting" || prediction.status === "processing") {
|
|
703
|
+
if (opts.signal?.aborted) {
|
|
704
|
+
throw opts.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
705
|
+
}
|
|
706
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
707
|
+
prediction = await this.get(token, prediction.id, opts);
|
|
708
|
+
}
|
|
709
|
+
return prediction;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
358
714
|
// src/index.ts
|
|
359
715
|
var session = {
|
|
360
716
|
verify
|
|
@@ -362,8 +718,13 @@ var session = {
|
|
|
362
718
|
var keys = {
|
|
363
719
|
get
|
|
364
720
|
};
|
|
365
|
-
var
|
|
721
|
+
var proxy = {
|
|
722
|
+
call,
|
|
723
|
+
callRaw,
|
|
724
|
+
callStream
|
|
725
|
+
};
|
|
726
|
+
var SDK_VERSION = "0.2.0";
|
|
366
727
|
|
|
367
|
-
export { BadTokenError, HubSdkError, InternalError, KeyNotFoundError, MissingScopeError, MissingTokenError, NetworkError, NotSubscribedError, ProviderNotGrantedError, RateLimitedError, SDK_VERSION, SecureKey, SecureKeyConsumedError, SubscriptionInactiveError, ToolNotFoundError, keys, session };
|
|
728
|
+
export { BadTokenError, HubSdkError, InternalError, KeyNotFoundError, MissingScopeError, MissingTokenError, NetworkError, NotSubscribedError, ProviderError, ProviderNotConfiguredError, ProviderNotGrantedError, RateLimitedError, SDK_VERSION, SecureKey, SecureKeyConsumedError, SubscriptionInactiveError, ToolNotFoundError, anthropic, keys, openai, proxy, replicate, session };
|
|
368
729
|
//# sourceMappingURL=index.js.map
|
|
369
730
|
//# sourceMappingURL=index.js.map
|