@modelrelay/sdk 0.14.1 → 0.18.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 +79 -3
- package/dist/index.cjs +334 -72
- package/dist/index.d.cts +112 -43
- package/dist/index.d.ts +112 -43
- package/dist/index.js +331 -68
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -261,7 +261,7 @@ function isTokenReusable(token) {
|
|
|
261
261
|
// package.json
|
|
262
262
|
var package_default = {
|
|
263
263
|
name: "@modelrelay/sdk",
|
|
264
|
-
version: "0.
|
|
264
|
+
version: "0.18.0",
|
|
265
265
|
description: "TypeScript SDK for the ModelRelay API",
|
|
266
266
|
type: "module",
|
|
267
267
|
main: "dist/index.cjs",
|
|
@@ -320,30 +320,6 @@ var StopReasons = {
|
|
|
320
320
|
Incomplete: "incomplete",
|
|
321
321
|
Unknown: "unknown"
|
|
322
322
|
};
|
|
323
|
-
var Providers = {
|
|
324
|
-
OpenAI: "openai",
|
|
325
|
-
Anthropic: "anthropic",
|
|
326
|
-
XAI: "xai",
|
|
327
|
-
GoogleAIStudio: "google-ai-studio",
|
|
328
|
-
Echo: "echo"
|
|
329
|
-
};
|
|
330
|
-
var Models = {
|
|
331
|
-
// OpenAI models (provider-agnostic identifiers)
|
|
332
|
-
Gpt4o: "gpt-4o",
|
|
333
|
-
Gpt4oMini: "gpt-4o-mini",
|
|
334
|
-
Gpt51: "gpt-5.1",
|
|
335
|
-
// Anthropic models (provider-agnostic identifiers)
|
|
336
|
-
Claude35HaikuLatest: "claude-3-5-haiku-latest",
|
|
337
|
-
Claude35SonnetLatest: "claude-3-5-sonnet-latest",
|
|
338
|
-
ClaudeOpus45: "claude-opus-4-5",
|
|
339
|
-
Claude35Haiku: "claude-3.5-haiku",
|
|
340
|
-
// xAI / Grok models
|
|
341
|
-
Grok2: "grok-2",
|
|
342
|
-
Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
|
|
343
|
-
Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
|
|
344
|
-
// Internal echo model for testing.
|
|
345
|
-
Echo1: "echo-1"
|
|
346
|
-
};
|
|
347
323
|
function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
348
324
|
return {
|
|
349
325
|
inputTokens,
|
|
@@ -354,14 +330,24 @@ function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
|
354
330
|
var ToolTypes = {
|
|
355
331
|
Function: "function",
|
|
356
332
|
Web: "web",
|
|
333
|
+
WebSearch: "web_search",
|
|
357
334
|
XSearch: "x_search",
|
|
358
335
|
CodeExecution: "code_execution"
|
|
359
336
|
};
|
|
337
|
+
var WebToolModes = {
|
|
338
|
+
Search: "search",
|
|
339
|
+
Browse: "browse"
|
|
340
|
+
};
|
|
360
341
|
var ToolChoiceTypes = {
|
|
361
342
|
Auto: "auto",
|
|
362
343
|
Required: "required",
|
|
363
344
|
None: "none"
|
|
364
345
|
};
|
|
346
|
+
var ResponseFormatTypes = {
|
|
347
|
+
Text: "text",
|
|
348
|
+
JsonObject: "json_object",
|
|
349
|
+
JsonSchema: "json_schema"
|
|
350
|
+
};
|
|
365
351
|
function mergeMetrics(base, override) {
|
|
366
352
|
if (!base && !override) return void 0;
|
|
367
353
|
return {
|
|
@@ -395,34 +381,14 @@ function stopReasonToString(value) {
|
|
|
395
381
|
if (typeof value === "string") return value;
|
|
396
382
|
return value.other?.trim() || void 0;
|
|
397
383
|
}
|
|
398
|
-
function normalizeProvider(value) {
|
|
399
|
-
if (value === void 0 || value === null) return void 0;
|
|
400
|
-
const str = String(value).trim();
|
|
401
|
-
if (!str) return void 0;
|
|
402
|
-
const lower = str.toLowerCase();
|
|
403
|
-
for (const p of Object.values(Providers)) {
|
|
404
|
-
if (lower === p) return p;
|
|
405
|
-
}
|
|
406
|
-
return { other: str };
|
|
407
|
-
}
|
|
408
|
-
function providerToString(value) {
|
|
409
|
-
if (!value) return void 0;
|
|
410
|
-
if (typeof value === "string") return value;
|
|
411
|
-
return value.other?.trim() || void 0;
|
|
412
|
-
}
|
|
413
384
|
function normalizeModelId(value) {
|
|
414
385
|
if (value === void 0 || value === null) return void 0;
|
|
415
386
|
const str = String(value).trim();
|
|
416
387
|
if (!str) return void 0;
|
|
417
|
-
|
|
418
|
-
for (const m of Object.values(Models)) {
|
|
419
|
-
if (lower === m) return m;
|
|
420
|
-
}
|
|
421
|
-
return { other: str };
|
|
388
|
+
return str;
|
|
422
389
|
}
|
|
423
390
|
function modelToString(value) {
|
|
424
|
-
|
|
425
|
-
return value.other?.trim() || "";
|
|
391
|
+
return String(value).trim();
|
|
426
392
|
}
|
|
427
393
|
|
|
428
394
|
// src/tools.ts
|
|
@@ -1080,7 +1046,6 @@ var ChatCompletionsClient = class {
|
|
|
1080
1046
|
if (!hasUserMessage(params.messages)) {
|
|
1081
1047
|
throw new ConfigError("at least one user message is required");
|
|
1082
1048
|
}
|
|
1083
|
-
validateRequestModel(params.model);
|
|
1084
1049
|
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
1085
1050
|
const body = buildProxyBody(
|
|
1086
1051
|
params,
|
|
@@ -1094,7 +1059,6 @@ var ChatCompletionsClient = class {
|
|
|
1094
1059
|
const baseContext = {
|
|
1095
1060
|
method: "POST",
|
|
1096
1061
|
path: "/llm/proxy",
|
|
1097
|
-
provider: params.provider,
|
|
1098
1062
|
model: params.model,
|
|
1099
1063
|
requestId
|
|
1100
1064
|
};
|
|
@@ -1144,6 +1108,80 @@ var ChatCompletionsClient = class {
|
|
|
1144
1108
|
trace
|
|
1145
1109
|
);
|
|
1146
1110
|
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Stream structured JSON responses using the NDJSON contract defined for
|
|
1113
|
+
* /llm/proxy. The request must include a structured responseFormat.
|
|
1114
|
+
*/
|
|
1115
|
+
async streamJSON(params, options = {}) {
|
|
1116
|
+
const metrics = mergeMetrics(this.metrics, options.metrics);
|
|
1117
|
+
const trace = mergeTrace(this.trace, options.trace);
|
|
1118
|
+
if (!params?.messages?.length) {
|
|
1119
|
+
throw new ConfigError("at least one message is required");
|
|
1120
|
+
}
|
|
1121
|
+
if (!hasUserMessage(params.messages)) {
|
|
1122
|
+
throw new ConfigError("at least one user message is required");
|
|
1123
|
+
}
|
|
1124
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_object" && params.responseFormat.type !== "json_schema") {
|
|
1125
|
+
throw new ConfigError(
|
|
1126
|
+
"responseFormat with type=json_object or json_schema is required for structured streaming"
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
1130
|
+
const body = buildProxyBody(
|
|
1131
|
+
params,
|
|
1132
|
+
mergeMetadata(this.defaultMetadata, params.metadata, options.metadata)
|
|
1133
|
+
);
|
|
1134
|
+
const requestId = params.requestId || options.requestId;
|
|
1135
|
+
const headers = { ...options.headers || {} };
|
|
1136
|
+
if (requestId) {
|
|
1137
|
+
headers[REQUEST_ID_HEADER] = requestId;
|
|
1138
|
+
}
|
|
1139
|
+
const baseContext = {
|
|
1140
|
+
method: "POST",
|
|
1141
|
+
path: "/llm/proxy",
|
|
1142
|
+
model: params.model,
|
|
1143
|
+
requestId
|
|
1144
|
+
};
|
|
1145
|
+
const response = await this.http.request("/llm/proxy", {
|
|
1146
|
+
method: "POST",
|
|
1147
|
+
body,
|
|
1148
|
+
headers,
|
|
1149
|
+
apiKey: authHeaders.apiKey,
|
|
1150
|
+
accessToken: authHeaders.accessToken,
|
|
1151
|
+
accept: "application/x-ndjson",
|
|
1152
|
+
raw: true,
|
|
1153
|
+
signal: options.signal,
|
|
1154
|
+
timeoutMs: options.timeoutMs ?? 0,
|
|
1155
|
+
useDefaultTimeout: false,
|
|
1156
|
+
connectTimeoutMs: options.connectTimeoutMs,
|
|
1157
|
+
retry: options.retry,
|
|
1158
|
+
metrics,
|
|
1159
|
+
trace,
|
|
1160
|
+
context: baseContext
|
|
1161
|
+
});
|
|
1162
|
+
const resolvedRequestId = requestIdFromHeaders(response.headers) || requestId || void 0;
|
|
1163
|
+
if (!response.ok) {
|
|
1164
|
+
throw await parseErrorResponse(response);
|
|
1165
|
+
}
|
|
1166
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
1167
|
+
if (!/application\/(x-)?ndjson/i.test(contentType)) {
|
|
1168
|
+
throw new TransportError(
|
|
1169
|
+
`expected NDJSON structured stream, got Content-Type ${contentType || "missing"}`,
|
|
1170
|
+
{ kind: "request" }
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
const streamContext = {
|
|
1174
|
+
...baseContext,
|
|
1175
|
+
requestId: resolvedRequestId ?? baseContext.requestId
|
|
1176
|
+
};
|
|
1177
|
+
return new StructuredJSONStream(
|
|
1178
|
+
response,
|
|
1179
|
+
resolvedRequestId,
|
|
1180
|
+
streamContext,
|
|
1181
|
+
metrics,
|
|
1182
|
+
trace
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1147
1185
|
};
|
|
1148
1186
|
var ChatCompletionsStream = class {
|
|
1149
1187
|
constructor(response, requestId, context, metrics, trace) {
|
|
@@ -1245,6 +1283,164 @@ var ChatCompletionsStream = class {
|
|
|
1245
1283
|
});
|
|
1246
1284
|
}
|
|
1247
1285
|
};
|
|
1286
|
+
var StructuredJSONStream = class {
|
|
1287
|
+
constructor(response, requestId, context, metrics, trace) {
|
|
1288
|
+
this.closed = false;
|
|
1289
|
+
this.sawTerminal = false;
|
|
1290
|
+
if (!response.body) {
|
|
1291
|
+
throw new ConfigError("streaming response is missing a body");
|
|
1292
|
+
}
|
|
1293
|
+
this.response = response;
|
|
1294
|
+
this.requestId = requestId;
|
|
1295
|
+
this.context = context;
|
|
1296
|
+
this.metrics = metrics;
|
|
1297
|
+
this.trace = trace;
|
|
1298
|
+
}
|
|
1299
|
+
async cancel(reason) {
|
|
1300
|
+
this.closed = true;
|
|
1301
|
+
try {
|
|
1302
|
+
await this.response.body?.cancel(reason);
|
|
1303
|
+
} catch {
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
async *[Symbol.asyncIterator]() {
|
|
1307
|
+
if (this.closed) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
const body = this.response.body;
|
|
1311
|
+
if (!body) {
|
|
1312
|
+
throw new ConfigError("streaming response is missing a body");
|
|
1313
|
+
}
|
|
1314
|
+
const reader = body.getReader();
|
|
1315
|
+
const decoder = new TextDecoder();
|
|
1316
|
+
let buffer = "";
|
|
1317
|
+
try {
|
|
1318
|
+
while (true) {
|
|
1319
|
+
if (this.closed) {
|
|
1320
|
+
await reader.cancel();
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const { value, done } = await reader.read();
|
|
1324
|
+
if (done) {
|
|
1325
|
+
const { records: records2 } = consumeNDJSONBuffer(buffer, true);
|
|
1326
|
+
for (const line of records2) {
|
|
1327
|
+
const evt = this.parseRecord(line);
|
|
1328
|
+
if (evt) {
|
|
1329
|
+
this.traceStructuredEvent(evt, line);
|
|
1330
|
+
yield evt;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
if (!this.sawTerminal) {
|
|
1334
|
+
throw new TransportError(
|
|
1335
|
+
"structured stream ended without completion or error",
|
|
1336
|
+
{ kind: "request" }
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1342
|
+
const { records, remainder } = consumeNDJSONBuffer(buffer);
|
|
1343
|
+
buffer = remainder;
|
|
1344
|
+
for (const line of records) {
|
|
1345
|
+
const evt = this.parseRecord(line);
|
|
1346
|
+
if (evt) {
|
|
1347
|
+
this.traceStructuredEvent(evt, line);
|
|
1348
|
+
yield evt;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
} catch (err) {
|
|
1353
|
+
this.trace?.streamError?.({ context: this.context, error: err });
|
|
1354
|
+
throw err;
|
|
1355
|
+
} finally {
|
|
1356
|
+
this.closed = true;
|
|
1357
|
+
reader.releaseLock();
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
async collect() {
|
|
1361
|
+
let last;
|
|
1362
|
+
for await (const evt of this) {
|
|
1363
|
+
last = evt;
|
|
1364
|
+
if (evt.type === "completion") {
|
|
1365
|
+
return evt.payload;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
throw new TransportError(
|
|
1369
|
+
"structured stream ended without completion or error",
|
|
1370
|
+
{ kind: "request" }
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
parseRecord(line) {
|
|
1374
|
+
let parsed;
|
|
1375
|
+
try {
|
|
1376
|
+
parsed = JSON.parse(line);
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
throw new TransportError("invalid JSON in structured stream", {
|
|
1379
|
+
kind: "request",
|
|
1380
|
+
cause: err
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1384
|
+
throw new TransportError("structured stream record is not an object", {
|
|
1385
|
+
kind: "request"
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
const obj = parsed;
|
|
1389
|
+
const rawType = String(obj.type || "").trim().toLowerCase();
|
|
1390
|
+
if (!rawType) return null;
|
|
1391
|
+
if (rawType === "start") {
|
|
1392
|
+
return null;
|
|
1393
|
+
}
|
|
1394
|
+
if (rawType === "error") {
|
|
1395
|
+
this.sawTerminal = true;
|
|
1396
|
+
const status = typeof obj.status === "number" && obj.status > 0 ? obj.status : 500;
|
|
1397
|
+
const message = typeof obj.message === "string" && obj.message.trim() ? obj.message : "structured stream error";
|
|
1398
|
+
const code = typeof obj.code === "string" && obj.code.trim() ? obj.code : void 0;
|
|
1399
|
+
throw new APIError(message, {
|
|
1400
|
+
status,
|
|
1401
|
+
code,
|
|
1402
|
+
requestId: this.requestId
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
if (rawType !== "update" && rawType !== "completion") {
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
if (obj.payload === void 0 || obj.payload === null) {
|
|
1409
|
+
throw new TransportError(
|
|
1410
|
+
"structured stream record missing payload",
|
|
1411
|
+
{ kind: "request" }
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
if (rawType === "completion") {
|
|
1415
|
+
this.sawTerminal = true;
|
|
1416
|
+
}
|
|
1417
|
+
const event = {
|
|
1418
|
+
type: rawType,
|
|
1419
|
+
// biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
|
|
1420
|
+
payload: obj.payload,
|
|
1421
|
+
requestId: this.requestId
|
|
1422
|
+
};
|
|
1423
|
+
return event;
|
|
1424
|
+
}
|
|
1425
|
+
traceStructuredEvent(evt, raw) {
|
|
1426
|
+
if (!this.trace?.streamEvent) return;
|
|
1427
|
+
const event = {
|
|
1428
|
+
type: "custom",
|
|
1429
|
+
event: "structured",
|
|
1430
|
+
data: { type: evt.type, payload: evt.payload },
|
|
1431
|
+
textDelta: void 0,
|
|
1432
|
+
toolCallDelta: void 0,
|
|
1433
|
+
toolCalls: void 0,
|
|
1434
|
+
responseId: void 0,
|
|
1435
|
+
model: void 0,
|
|
1436
|
+
stopReason: void 0,
|
|
1437
|
+
usage: void 0,
|
|
1438
|
+
requestId: this.requestId,
|
|
1439
|
+
raw
|
|
1440
|
+
};
|
|
1441
|
+
this.trace.streamEvent({ context: this.context, event });
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1248
1444
|
function consumeSSEBuffer(buffer, flush = false) {
|
|
1249
1445
|
const events = [];
|
|
1250
1446
|
let eventName = "";
|
|
@@ -1287,6 +1483,19 @@ function consumeSSEBuffer(buffer, flush = false) {
|
|
|
1287
1483
|
}
|
|
1288
1484
|
return { events, remainder };
|
|
1289
1485
|
}
|
|
1486
|
+
function consumeNDJSONBuffer(buffer, flush = false) {
|
|
1487
|
+
const lines = buffer.split(/\r?\n/);
|
|
1488
|
+
const records = [];
|
|
1489
|
+
const lastIndex = lines.length - 1;
|
|
1490
|
+
const limit = flush ? lines.length : Math.max(0, lastIndex);
|
|
1491
|
+
for (let i = 0; i < limit; i++) {
|
|
1492
|
+
const line = lines[i]?.trim();
|
|
1493
|
+
if (!line) continue;
|
|
1494
|
+
records.push(line);
|
|
1495
|
+
}
|
|
1496
|
+
const remainder = flush ? "" : lines[lastIndex] ?? "";
|
|
1497
|
+
return { records, remainder };
|
|
1498
|
+
}
|
|
1290
1499
|
function mapChatEvent(raw, requestId) {
|
|
1291
1500
|
let parsed = raw.data;
|
|
1292
1501
|
if (raw.data) {
|
|
@@ -1415,7 +1624,6 @@ function normalizeChatResponse(payload, requestId) {
|
|
|
1415
1624
|
const p = payload;
|
|
1416
1625
|
const response = {
|
|
1417
1626
|
id: p?.id,
|
|
1418
|
-
provider: normalizeProvider(p?.provider),
|
|
1419
1627
|
content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
|
|
1420
1628
|
stopReason: normalizeStopReason(p?.stop_reason),
|
|
1421
1629
|
model: normalizeModelId(p?.model),
|
|
@@ -1446,19 +1654,6 @@ function normalizeUsage(payload) {
|
|
|
1446
1654
|
const totalTokens = Number(payload.total_tokens ?? 0);
|
|
1447
1655
|
return createUsage(inputTokens, outputTokens, totalTokens || void 0);
|
|
1448
1656
|
}
|
|
1449
|
-
function validateRequestModel(model) {
|
|
1450
|
-
if (model === void 0 || model === null) return;
|
|
1451
|
-
const value = modelToString(model).trim();
|
|
1452
|
-
if (!value) {
|
|
1453
|
-
throw new ConfigError("model id must be a non-empty string when provided");
|
|
1454
|
-
}
|
|
1455
|
-
const knownModels = Object.values(Models);
|
|
1456
|
-
if (!knownModels.includes(value)) {
|
|
1457
|
-
throw new ConfigError(
|
|
1458
|
-
`unsupported model id "${value}". Use one of the SDK Models.* constants or omit model to use the tier's default model.`
|
|
1459
|
-
);
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
1657
|
function buildProxyBody(params, metadata) {
|
|
1463
1658
|
const modelValue = params.model ? modelToString(params.model).trim() : "";
|
|
1464
1659
|
const body = {
|
|
@@ -1468,7 +1663,6 @@ function buildProxyBody(params, metadata) {
|
|
|
1468
1663
|
body.model = modelValue;
|
|
1469
1664
|
}
|
|
1470
1665
|
if (typeof params.maxTokens === "number") body.max_tokens = params.maxTokens;
|
|
1471
|
-
if (params.provider) body.provider = providerToString(params.provider);
|
|
1472
1666
|
if (typeof params.temperature === "number")
|
|
1473
1667
|
body.temperature = params.temperature;
|
|
1474
1668
|
if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
|
|
@@ -1476,6 +1670,7 @@ function buildProxyBody(params, metadata) {
|
|
|
1476
1670
|
if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
|
|
1477
1671
|
if (params.tools?.length) body.tools = normalizeTools(params.tools);
|
|
1478
1672
|
if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
|
|
1673
|
+
if (params.responseFormat) body.response_format = params.responseFormat;
|
|
1479
1674
|
return body;
|
|
1480
1675
|
}
|
|
1481
1676
|
function normalizeMessages(messages) {
|
|
@@ -1655,6 +1850,32 @@ var CustomersClient = class {
|
|
|
1655
1850
|
});
|
|
1656
1851
|
return response.customer;
|
|
1657
1852
|
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Claim a customer by email, setting their external_id.
|
|
1855
|
+
* Used when a customer subscribes via Stripe Checkout (email only) and later
|
|
1856
|
+
* authenticates to the app, needing to link their identity.
|
|
1857
|
+
*
|
|
1858
|
+
* @throws {APIError} with status 404 if customer not found by email
|
|
1859
|
+
* @throws {APIError} with status 409 if customer already claimed or external_id in use
|
|
1860
|
+
*/
|
|
1861
|
+
async claim(request) {
|
|
1862
|
+
this.ensureSecretKey();
|
|
1863
|
+
if (!request.email?.trim()) {
|
|
1864
|
+
throw new ConfigError("email is required");
|
|
1865
|
+
}
|
|
1866
|
+
if (!isValidEmail(request.email)) {
|
|
1867
|
+
throw new ConfigError("invalid email format");
|
|
1868
|
+
}
|
|
1869
|
+
if (!request.external_id?.trim()) {
|
|
1870
|
+
throw new ConfigError("external_id is required");
|
|
1871
|
+
}
|
|
1872
|
+
const response = await this.http.json("/customers/claim", {
|
|
1873
|
+
method: "POST",
|
|
1874
|
+
body: request,
|
|
1875
|
+
apiKey: this.apiKey
|
|
1876
|
+
});
|
|
1877
|
+
return response.customer;
|
|
1878
|
+
}
|
|
1658
1879
|
/**
|
|
1659
1880
|
* Delete a customer by ID.
|
|
1660
1881
|
*/
|
|
@@ -1719,6 +1940,13 @@ var TiersClient = class {
|
|
|
1719
1940
|
);
|
|
1720
1941
|
}
|
|
1721
1942
|
}
|
|
1943
|
+
ensureSecretKey() {
|
|
1944
|
+
if (!this.apiKey || !this.apiKey.startsWith("mr_sk_")) {
|
|
1945
|
+
throw new ConfigError(
|
|
1946
|
+
"Secret key (mr_sk_*) required for checkout operations"
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1722
1950
|
/**
|
|
1723
1951
|
* List all tiers in the project.
|
|
1724
1952
|
*/
|
|
@@ -1744,6 +1972,42 @@ var TiersClient = class {
|
|
|
1744
1972
|
});
|
|
1745
1973
|
return response.tier;
|
|
1746
1974
|
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Create a Stripe checkout session for a tier (Stripe-first flow).
|
|
1977
|
+
*
|
|
1978
|
+
* This enables users to subscribe before authenticating. After checkout
|
|
1979
|
+
* completes, a customer record is created with the provided email. The
|
|
1980
|
+
* customer can later be linked to an identity via POST /customers/claim.
|
|
1981
|
+
*
|
|
1982
|
+
* Requires a secret key (mr_sk_*).
|
|
1983
|
+
*
|
|
1984
|
+
* @param tierId - The tier ID to create a checkout session for
|
|
1985
|
+
* @param request - Checkout session request with email and redirect URLs
|
|
1986
|
+
* @returns Checkout session with Stripe URL
|
|
1987
|
+
*/
|
|
1988
|
+
async checkout(tierId, request) {
|
|
1989
|
+
this.ensureSecretKey();
|
|
1990
|
+
if (!tierId?.trim()) {
|
|
1991
|
+
throw new ConfigError("tierId is required");
|
|
1992
|
+
}
|
|
1993
|
+
if (!request.email?.trim()) {
|
|
1994
|
+
throw new ConfigError("email is required");
|
|
1995
|
+
}
|
|
1996
|
+
if (!request.success_url?.trim()) {
|
|
1997
|
+
throw new ConfigError("success_url is required");
|
|
1998
|
+
}
|
|
1999
|
+
if (!request.cancel_url?.trim()) {
|
|
2000
|
+
throw new ConfigError("cancel_url is required");
|
|
2001
|
+
}
|
|
2002
|
+
return await this.http.json(
|
|
2003
|
+
`/tiers/${tierId}/checkout`,
|
|
2004
|
+
{
|
|
2005
|
+
method: "POST",
|
|
2006
|
+
apiKey: this.apiKey,
|
|
2007
|
+
body: request
|
|
2008
|
+
}
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
1747
2011
|
};
|
|
1748
2012
|
|
|
1749
2013
|
// src/http.ts
|
|
@@ -2131,10 +2395,10 @@ export {
|
|
|
2131
2395
|
ErrorCodes,
|
|
2132
2396
|
ModelRelay,
|
|
2133
2397
|
ModelRelayError,
|
|
2134
|
-
|
|
2135
|
-
Providers,
|
|
2398
|
+
ResponseFormatTypes,
|
|
2136
2399
|
SDK_VERSION,
|
|
2137
2400
|
StopReasons,
|
|
2401
|
+
StructuredJSONStream,
|
|
2138
2402
|
TiersClient,
|
|
2139
2403
|
ToolArgsError,
|
|
2140
2404
|
ToolCallAccumulator,
|
|
@@ -2142,6 +2406,7 @@ export {
|
|
|
2142
2406
|
ToolRegistry,
|
|
2143
2407
|
ToolTypes,
|
|
2144
2408
|
TransportError,
|
|
2409
|
+
WebToolModes,
|
|
2145
2410
|
assistantMessageWithToolCalls,
|
|
2146
2411
|
createAccessTokenAuth,
|
|
2147
2412
|
createApiKeyAuth,
|
|
@@ -2166,12 +2431,10 @@ export {
|
|
|
2166
2431
|
mergeTrace,
|
|
2167
2432
|
modelToString,
|
|
2168
2433
|
normalizeModelId,
|
|
2169
|
-
normalizeProvider,
|
|
2170
2434
|
normalizeStopReason,
|
|
2171
2435
|
parseErrorResponse,
|
|
2172
2436
|
parseToolArgs,
|
|
2173
2437
|
parseToolArgsRaw,
|
|
2174
|
-
providerToString,
|
|
2175
2438
|
respondToToolCall,
|
|
2176
2439
|
stopReasonToString,
|
|
2177
2440
|
toolChoiceAuto,
|