@caitlyn-ai/sdk-core 0.0.1 → 0.1.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.
@@ -132,10 +132,12 @@ __export(src_exports, {
132
132
  SseStream: () => SseStream,
133
133
  WaiterError: () => WaiterError,
134
134
  caitlynEndpointProvider: () => caitlynEndpointProvider,
135
+ caitlynTracer: () => caitlynTracer,
135
136
  clientConfig: () => clientConfig,
136
137
  createAuth: () => createAuth,
137
138
  defaultAwsCredentials: () => defaultAwsCredentials,
138
139
  getCaitlynAuthPlugin: () => getCaitlynAuthPlugin,
140
+ getCaitlynConnectionErrorPlugin: () => getCaitlynConnectionErrorPlugin,
139
141
  getCaitlynSdkPlugin: () => getCaitlynSdkPlugin,
140
142
  getCaitlynTelemetryPlugin: () => getCaitlynTelemetryPlugin,
141
143
  getWebIdentityToken: () => getWebIdentityToken,
@@ -151,9 +153,184 @@ __export(src_exports, {
151
153
  module.exports = __toCommonJS(src_exports);
152
154
 
153
155
  // packages/sdk-core/src/protocol.ts
154
- var import_protocols = require("@smithy/core/protocols");
156
+ var import_protocols3 = require("@smithy/core/protocols");
155
157
  var import_schema = require("@smithy/core/schema");
156
158
  var import_client = require("@smithy/core/client");
159
+
160
+ // packages/sdk-core/src/plugin.ts
161
+ var import_retry = require("@smithy/core/retry");
162
+ var import_protocols = require("@smithy/core/protocols");
163
+ var import_protocols2 = require("@smithy/core/protocols");
164
+
165
+ // packages/sdk-core/src/retry.ts
166
+ var NO_RETRY = /* @__PURE__ */ Symbol.for("caitlyn.noRetry");
167
+ var Token = class {
168
+ constructor(count, delay) {
169
+ this.count = count;
170
+ this.delay = delay;
171
+ }
172
+ count;
173
+ delay;
174
+ getRetryCount() {
175
+ return this.count;
176
+ }
177
+ getRetryDelay() {
178
+ return this.delay;
179
+ }
180
+ };
181
+ var CaitlynRetryStrategy = class {
182
+ constructor(maxAttempts = 3, maxRetryDelayMs = 6e4) {
183
+ this.maxRetryDelayMs = maxRetryDelayMs;
184
+ this.maxAttempts = typeof maxAttempts === "number" ? async () => maxAttempts : maxAttempts;
185
+ }
186
+ maxRetryDelayMs;
187
+ mode = "standard";
188
+ maxAttempts;
189
+ async acquireInitialRetryToken() {
190
+ return new Token(0, 0);
191
+ }
192
+ async refreshRetryTokenForRetry(token, info) {
193
+ const count = token.getRetryCount();
194
+ const err = info.error;
195
+ if (count + 1 >= await this.maxAttempts()) throw new Error("retry: attempts exhausted");
196
+ if (!err || err[NO_RETRY]) throw new Error("retry: operation is not safe to retry");
197
+ const noResponse = err.$metadata?.httpStatusCode === void 0 && err.name !== "AbortError";
198
+ if (err.$retryable === void 0 && !noResponse) throw new Error("retry: error is not retryable");
199
+ let delay;
200
+ if (info.retryAfterHint instanceof Date) {
201
+ delay = Math.max(0, info.retryAfterHint.getTime() - Date.now());
202
+ if (delay > this.maxRetryDelayMs) throw new Error("retry: Retry-After exceeds maxRetryDelay");
203
+ } else {
204
+ delay = Math.random() * Math.min(8e3, 500 * 2 ** count);
205
+ }
206
+ return new Token(count + 1, delay);
207
+ }
208
+ recordSuccess() {
209
+ }
210
+ };
211
+
212
+ // packages/sdk-core/src/plugin.ts
213
+ var CAITLYN_SDK_USER_AGENT_PREFIX = "caitlyn-sdk-ts";
214
+ var NO_RESPONSE = /* @__PURE__ */ Symbol.for("caitlyn.noResponse");
215
+ function markNoResponse(err) {
216
+ if (err && typeof err === "object" && err.name !== "AbortError") Object.defineProperty(err, NO_RESPONSE, { value: true, configurable: true });
217
+ return err;
218
+ }
219
+ function getCaitlynConnectionErrorPlugin(toError) {
220
+ const mark = (next) => async (args) => {
221
+ try {
222
+ return await next(args);
223
+ } catch (err) {
224
+ const e = err;
225
+ if (!import_protocols2.HttpResponse.isInstance(e?.$response) && e?.$metadata?.httpStatusCode === void 0) markNoResponse(err);
226
+ throw err;
227
+ }
228
+ };
229
+ const wrap = (next, context2) => async (args) => {
230
+ try {
231
+ return await next(args);
232
+ } catch (err) {
233
+ if (err && typeof err === "object" && err[NO_RESPONSE]) {
234
+ throw toError(context2.commandName?.replace(/Command$/, "") ?? "call", err);
235
+ }
236
+ throw err;
237
+ }
238
+ };
239
+ return {
240
+ applyToStack(stack) {
241
+ stack.add(mark, { step: "deserialize", name: "caitlynNoResponse", priority: "low", override: true });
242
+ stack.add(wrap, { step: "initialize", name: "caitlynConnectionError", priority: "normal", override: true });
243
+ }
244
+ };
245
+ }
246
+ var NotSupportedByServer = class extends Error {
247
+ constructor(operation, requires, server) {
248
+ super(`NotSupportedByServer(${operation}, requires ${requires}, server is ${server})`);
249
+ this.operation = operation;
250
+ this.requires = requires;
251
+ this.server = server;
252
+ this.name = "NotSupportedByServer";
253
+ }
254
+ operation;
255
+ requires;
256
+ server;
257
+ };
258
+ var semver = (a, b) => {
259
+ const pa = a.split(".").map(Number);
260
+ const pb = b.split(".").map(Number);
261
+ for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return (pa[i] ?? 0) - (pb[i] ?? 0);
262
+ return 0;
263
+ };
264
+ function runtime() {
265
+ const g = globalThis;
266
+ if (g.process?.versions?.bun) return `md/bun#${g.process.versions.bun}`;
267
+ if (g.Deno?.version?.deno) return `md/deno#${g.Deno.version.deno}`;
268
+ if (g.process?.versions?.node) return `md/nodejs#${g.process.versions.node}`;
269
+ return "md/browser";
270
+ }
271
+ function getCaitlynSdkPlugin(config, options) {
272
+ let server;
273
+ const since = new Map((options.since ?? []).map((e) => e.split("=")));
274
+ const sinceCheck = (next, context2) => async (args) => {
275
+ const op = context2.commandName?.replace(/Command$/, "");
276
+ if (op && server) {
277
+ const required = since.get(op);
278
+ if (required && semver(required, server) > 0) throw new NotSupportedByServer(op, required, server);
279
+ for (const [member, value] of Object.entries(args.input)) {
280
+ const memberSince = value === void 0 ? void 0 : since.get(`${op}.${member}`);
281
+ if (memberSince && semver(memberSince, server) > 0) throw new NotSupportedByServer(`${op}.${member}`, memberSince, server);
282
+ }
283
+ }
284
+ return next(args);
285
+ };
286
+ const serverVersion = (next) => async (args) => {
287
+ try {
288
+ const out = await next(args);
289
+ if (import_protocols2.HttpResponse.isInstance(out.response)) server = out.response.headers["caitlyn-api-version"] ?? server;
290
+ return out;
291
+ } catch (err) {
292
+ const res = err.$response;
293
+ if (import_protocols2.HttpResponse.isInstance(res)) server = res.headers["caitlyn-api-version"] ?? server;
294
+ throw err;
295
+ }
296
+ };
297
+ const userAgent = [`${CAITLYN_SDK_USER_AGENT_PREFIX}/${config.caitlynSdkVersion ?? "0.0.0"}`, "lang/js", runtime(), config.customUserAgent].filter(Boolean).join(" ");
298
+ const identity = (next) => async (args) => {
299
+ if (import_protocols.HttpRequest.isInstance(args.request)) {
300
+ args.request.headers["user-agent"] = userAgent;
301
+ args.request.headers["caitlyn-sdk-model"] = options.sdkModel;
302
+ }
303
+ return next(args);
304
+ };
305
+ const safe = new Set(options.retrySafeOperations);
306
+ const retrySafety = (next, context2) => async (args) => {
307
+ try {
308
+ return await next(args);
309
+ } catch (err) {
310
+ const op = context2.commandName?.replace(/Command$/, "");
311
+ if (op && !safe.has(op) && err && typeof err === "object") Object.defineProperty(err, NO_RETRY, { value: true });
312
+ throw err;
313
+ }
314
+ };
315
+ return {
316
+ applyToStack(stack) {
317
+ stack.add(sinceCheck, { step: "initialize", name: "caitlynSinceCheck", priority: "high", override: true });
318
+ stack.add(serverVersion, { step: "deserialize", name: "caitlynServerVersion", priority: "low", override: true });
319
+ stack.add(identity, { step: "build", name: "caitlynSdkIdentity", priority: "low", override: true });
320
+ stack.addRelativeTo(retrySafety, { toMiddleware: "retryMiddleware", relation: "after", name: "caitlynRetrySafety" });
321
+ (0, import_retry.getOmitRetryHeadersPlugin)({}).applyToStack(stack);
322
+ }
323
+ };
324
+ }
325
+
326
+ // packages/sdk-core/src/protocol.ts
327
+ async function readBody(body, context2) {
328
+ try {
329
+ return await (0, import_protocols3.collectBody)(body, context2);
330
+ } catch (err) {
331
+ throw markNoResponse(err);
332
+ }
333
+ }
157
334
  var SETTINGS = {
158
335
  timestampFormat: {
159
336
  useTrait: true,
@@ -163,15 +340,15 @@ var SETTINGS = {
163
340
  httpBindings: true,
164
341
  jsonName: true
165
342
  };
166
- var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol {
343
+ var CaitlynRestJsonProtocol = class extends import_protocols3.HttpBindingProtocol {
167
344
  serializer;
168
345
  deserializer;
169
346
  codec;
170
347
  constructor({ defaultNamespace, errorTypeRegistries }) {
171
348
  super({ defaultNamespace, errorTypeRegistries });
172
- this.codec = new import_protocols.JsonCodec2(SETTINGS);
173
- this.serializer = new import_protocols.HttpInterceptingShapeSerializer(this.codec.createSerializer(), SETTINGS);
174
- this.deserializer = new import_protocols.HttpInterceptingShapeDeserializer(this.codec.createDeserializer(), SETTINGS);
349
+ this.codec = new import_protocols3.JsonCodec2(SETTINGS);
350
+ this.serializer = new import_protocols3.HttpInterceptingShapeSerializer(this.codec.createSerializer(), SETTINGS);
351
+ this.deserializer = new import_protocols3.HttpInterceptingShapeDeserializer(this.codec.createDeserializer(), SETTINGS);
175
352
  }
176
353
  getShapeId() {
177
354
  return "ai.caitlyn.protocols#restJson";
@@ -205,7 +382,7 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
205
382
  const ns = import_schema.NormalizedSchema.of(operationSchema.output);
206
383
  const streaming = [...ns.structIterator()].some(([, m]) => m.getMemberTraits().httpPayload && m.isStreaming());
207
384
  if (response.statusCode >= 300) {
208
- const bytes2 = await (0, import_protocols.collectBody)(response.body, context2);
385
+ const bytes2 = await readBody(response.body, context2);
209
386
  response.body = bytes2;
210
387
  if (!isJsonObject(bytes2)) {
211
388
  await this.handleError(operationSchema, context2, response, {}, this.deserializeMetadata(response));
@@ -213,7 +390,7 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
213
390
  return super.deserializeResponse(operationSchema, context2, response);
214
391
  }
215
392
  if (streaming) return super.deserializeResponse(operationSchema, context2, response);
216
- const bytes = await (0, import_protocols.collectBody)(response.body, context2);
393
+ const bytes = await readBody(response.body, context2);
217
394
  response.body = bytes;
218
395
  const output = await super.deserializeResponse(operationSchema, context2, response);
219
396
  if (bytes.byteLength > 0) {
@@ -234,7 +411,7 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
234
411
  const registry = this.compositeErrorRegistry;
235
412
  registry.copyFrom(import_schema.TypeRegistry.for(this.options.defaultNamespace));
236
413
  const errorMetadata = { $metadata: metadata, $fault: response.statusCode < 500 ? "client" : "server" };
237
- const rawBody = response.body instanceof Uint8Array ? new TextDecoder().decode(response.body) : "";
414
+ const { rawBody, rawBodyTruncated } = rawBodyOf(response.body instanceof Uint8Array ? response.body : new Uint8Array());
238
415
  const body = {
239
416
  ...dataObject,
240
417
  // Always the HTTP status: a body's `status` can be wrong (a proxy, a faulty handler), and the class is
@@ -242,7 +419,8 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
242
419
  status: response.statusCode,
243
420
  code: typeof dataObject.code === "string" && dataObject.code ? dataObject.code : `HTTP_${response.statusCode}`,
244
421
  requestId: dataObject.requestId ?? response.headers["caitlyn-request-id"],
245
- rawBody
422
+ rawBody,
423
+ rawBodyTruncated
246
424
  };
247
425
  const message = body.detail ?? body.title ?? `HTTP ${response.statusCode}`;
248
426
  const schema = registry.find((s) => {
@@ -253,7 +431,7 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
253
431
  if (!schema) {
254
432
  const base = registry.getBaseException();
255
433
  const Ctor2 = base && registry.getErrorCtor(base) || Error;
256
- const name = body.code;
434
+ const name = "CaitlynError";
257
435
  throw (0, import_client.decorateServiceException)(Object.assign(new Ctor2({ name, message }), errorMetadata), body);
258
436
  }
259
437
  const ns = import_schema.NormalizedSchema.of(schema);
@@ -270,6 +448,13 @@ var CaitlynRestJsonProtocol = class extends import_protocols.HttpBindingProtocol
270
448
  throw (0, import_client.decorateServiceException)(Object.assign(exception, errorMetadata, { $fault: ns.getMergedTraits().error, message }, fields), body);
271
449
  }
272
450
  };
451
+ var RAW_BODY_LIMIT = 8192;
452
+ function rawBodyOf(bytes) {
453
+ if (bytes.byteLength <= RAW_BODY_LIMIT) return { rawBody: new TextDecoder().decode(bytes), rawBodyTruncated: false };
454
+ let end = RAW_BODY_LIMIT;
455
+ while (end > 0 && (bytes[end] & 192) === 128) end--;
456
+ return { rawBody: new TextDecoder().decode(bytes.subarray(0, end)), rawBodyTruncated: true };
457
+ }
273
458
  function isJsonObject(bytes) {
274
459
  if (bytes.byteLength === 0) return false;
275
460
  try {
@@ -310,140 +495,6 @@ function resolveContentType(input) {
310
495
  return hasBody ? "application/json" : void 0;
311
496
  }
312
497
 
313
- // packages/sdk-core/src/plugin.ts
314
- var import_retry = require("@smithy/core/retry");
315
- var import_protocols2 = require("@smithy/core/protocols");
316
- var import_protocols3 = require("@smithy/core/protocols");
317
-
318
- // packages/sdk-core/src/retry.ts
319
- var NO_RETRY = /* @__PURE__ */ Symbol.for("caitlyn.noRetry");
320
- var Token = class {
321
- constructor(count, delay) {
322
- this.count = count;
323
- this.delay = delay;
324
- }
325
- count;
326
- delay;
327
- getRetryCount() {
328
- return this.count;
329
- }
330
- getRetryDelay() {
331
- return this.delay;
332
- }
333
- };
334
- var CaitlynRetryStrategy = class {
335
- constructor(maxAttempts = 3, maxRetryDelayMs = 6e4) {
336
- this.maxRetryDelayMs = maxRetryDelayMs;
337
- this.maxAttempts = typeof maxAttempts === "number" ? async () => maxAttempts : maxAttempts;
338
- }
339
- maxRetryDelayMs;
340
- mode = "standard";
341
- maxAttempts;
342
- async acquireInitialRetryToken() {
343
- return new Token(0, 0);
344
- }
345
- async refreshRetryTokenForRetry(token, info) {
346
- const count = token.getRetryCount();
347
- const err = info.error;
348
- if (count + 1 >= await this.maxAttempts()) throw new Error("retry: attempts exhausted");
349
- if (!err || err[NO_RETRY]) throw new Error("retry: operation is not safe to retry");
350
- const noResponse = err.$metadata?.httpStatusCode === void 0 && err.name !== "AbortError";
351
- if (err.$retryable === void 0 && !noResponse) throw new Error("retry: error is not retryable");
352
- let delay;
353
- if (info.retryAfterHint instanceof Date) {
354
- delay = Math.max(0, info.retryAfterHint.getTime() - Date.now());
355
- if (delay > this.maxRetryDelayMs) throw new Error("retry: Retry-After exceeds maxRetryDelay");
356
- } else {
357
- delay = Math.random() * Math.min(8e3, 500 * 2 ** count);
358
- }
359
- return new Token(count + 1, delay);
360
- }
361
- recordSuccess() {
362
- }
363
- };
364
-
365
- // packages/sdk-core/src/plugin.ts
366
- var CAITLYN_SDK_USER_AGENT_PREFIX = "caitlyn-sdk-ts";
367
- var NotSupportedByServer = class extends Error {
368
- constructor(operation, requires, server) {
369
- super(`NotSupportedByServer(${operation}, requires ${requires}, server is ${server})`);
370
- this.operation = operation;
371
- this.requires = requires;
372
- this.server = server;
373
- this.name = "NotSupportedByServer";
374
- }
375
- operation;
376
- requires;
377
- server;
378
- };
379
- var semver = (a, b) => {
380
- const pa = a.split(".").map(Number);
381
- const pb = b.split(".").map(Number);
382
- for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return (pa[i] ?? 0) - (pb[i] ?? 0);
383
- return 0;
384
- };
385
- function runtime() {
386
- const g = globalThis;
387
- if (g.process?.versions?.bun) return `md/bun#${g.process.versions.bun}`;
388
- if (g.Deno?.version?.deno) return `md/deno#${g.Deno.version.deno}`;
389
- if (g.process?.versions?.node) return `md/nodejs#${g.process.versions.node}`;
390
- return "md/browser";
391
- }
392
- function getCaitlynSdkPlugin(config, options) {
393
- let server;
394
- const since = new Map((options.since ?? []).map((e) => e.split("=")));
395
- const sinceCheck = (next, context2) => async (args) => {
396
- const op = context2.commandName?.replace(/Command$/, "");
397
- if (op && server) {
398
- const required = since.get(op);
399
- if (required && semver(required, server) > 0) throw new NotSupportedByServer(op, required, server);
400
- for (const [member, value] of Object.entries(args.input)) {
401
- const memberSince = value === void 0 ? void 0 : since.get(`${op}.${member}`);
402
- if (memberSince && semver(memberSince, server) > 0) throw new NotSupportedByServer(`${op}.${member}`, memberSince, server);
403
- }
404
- }
405
- return next(args);
406
- };
407
- const serverVersion = (next) => async (args) => {
408
- try {
409
- const out = await next(args);
410
- if (import_protocols3.HttpResponse.isInstance(out.response)) server = out.response.headers["caitlyn-api-version"] ?? server;
411
- return out;
412
- } catch (err) {
413
- const res = err.$response;
414
- if (import_protocols3.HttpResponse.isInstance(res)) server = res.headers["caitlyn-api-version"] ?? server;
415
- throw err;
416
- }
417
- };
418
- const userAgent = [`${CAITLYN_SDK_USER_AGENT_PREFIX}/${config.caitlynSdkVersion ?? "0.0.0"}`, "lang/js", runtime(), config.customUserAgent].filter(Boolean).join(" ");
419
- const identity = (next) => async (args) => {
420
- if (import_protocols2.HttpRequest.isInstance(args.request)) {
421
- args.request.headers["user-agent"] = userAgent;
422
- args.request.headers["caitlyn-sdk-model"] = options.sdkModel;
423
- }
424
- return next(args);
425
- };
426
- const safe = new Set(options.retrySafeOperations);
427
- const retrySafety = (next, context2) => async (args) => {
428
- try {
429
- return await next(args);
430
- } catch (err) {
431
- const op = context2.commandName?.replace(/Command$/, "");
432
- if (op && !safe.has(op) && err && typeof err === "object") Object.defineProperty(err, NO_RETRY, { value: true });
433
- throw err;
434
- }
435
- };
436
- return {
437
- applyToStack(stack) {
438
- stack.add(sinceCheck, { step: "initialize", name: "caitlynSinceCheck", priority: "high", override: true });
439
- stack.add(serverVersion, { step: "deserialize", name: "caitlynServerVersion", priority: "low", override: true });
440
- stack.add(identity, { step: "build", name: "caitlynSdkIdentity", priority: "low", override: true });
441
- stack.addRelativeTo(retrySafety, { toMiddleware: "retryMiddleware", relation: "after", name: "caitlynRetrySafety" });
442
- (0, import_retry.getOmitRetryHeadersPlugin)({}).applyToStack(stack);
443
- }
444
- };
445
- }
446
-
447
498
  // packages/sdk-core/src/endpoint.ts
448
499
  var caitlynEndpointProvider = (params) => {
449
500
  if (!params.endpoint) throw new Error("Caitlyn: no endpoint. Pass `workspace` or `endpoint` when creating the client.");
@@ -453,23 +504,38 @@ var caitlynEndpointProvider = (params) => {
453
504
  // packages/sdk-core/src/telemetry.ts
454
505
  var import_protocols4 = require("@smithy/core/protocols");
455
506
  var import_api = require("@opentelemetry/api");
507
+ function caitlynTracer(provider) {
508
+ return (provider ?? import_api.trace.getTracerProvider()).getTracer("@caitlyn-ai/sdk", "0.1.0");
509
+ }
510
+ function urlWithoutQuery(url) {
511
+ const u = new URL(url);
512
+ u.search = "";
513
+ u.hash = "";
514
+ u.username = "";
515
+ u.password = "";
516
+ return u.toString();
517
+ }
518
+ function recordFailure(span, err) {
519
+ const e = err;
520
+ span.recordException(err instanceof Error ? err : { name: String(e?.name ?? "Error"), message: String(e?.message ?? err) });
521
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: e?.message });
522
+ span.setAttribute("error.type", e?.name ?? "Error");
523
+ if (typeof e?.code === "string") span.setAttribute("caitlyn.error.code", e.code);
524
+ }
456
525
  function getCaitlynTelemetryPlugin(provider) {
457
- const tracer = (provider ?? import_api.trace.getTracerProvider()).getTracer("@caitlyn-ai/sdk", "0.0.1");
526
+ const tracer = caitlynTracer(provider);
458
527
  return {
459
528
  applyToStack(stack) {
460
529
  stack.add(
461
530
  (next, ctx) => async (args) => {
462
531
  const op = ctx.commandName?.replace(/Command$/, "") ?? "unknown";
463
- return tracer.startActiveSpan(`caitlyn ${op}`, { kind: import_api.SpanKind.CLIENT, attributes: { "caitlyn.operation": op } }, async (span) => {
532
+ return tracer.startActiveSpan("caitlyn.call", { kind: import_api.SpanKind.CLIENT, attributes: { "rpc.system": "caitlyn", "rpc.method": op } }, async (span) => {
464
533
  ctx.__caitlynSpan = span;
465
534
  ctx.__attempt = 0;
466
535
  try {
467
- const out = await next(args);
468
- return out;
536
+ return await next(args);
469
537
  } catch (err) {
470
- span.recordException(err);
471
- span.setStatus({ code: import_api.SpanStatusCode.ERROR });
472
- span.setAttribute("error.type", err.name);
538
+ recordFailure(span, err);
473
539
  throw err;
474
540
  } finally {
475
541
  span.end();
@@ -483,26 +549,34 @@ function getCaitlynTelemetryPlugin(provider) {
483
549
  const attempt = ctx.__attempt ?? 0;
484
550
  ctx.__attempt = attempt + 1;
485
551
  const parent = ctx.__caitlynSpan ? import_api.trace.setSpan(import_api.context.active(), ctx.__caitlynSpan) : import_api.context.active();
486
- const span = tracer.startSpan(`HTTP ${args.request.method}`, { kind: import_api.SpanKind.CLIENT, attributes: { "http.request.resend_count": attempt } }, parent);
552
+ const span = tracer.startSpan("caitlyn.attempt", { kind: import_api.SpanKind.CLIENT, attributes: { "http.request.resend_count": attempt } }, parent);
487
553
  const req = args.request;
488
554
  if (import_protocols4.HttpRequest.isInstance(req)) {
489
- span.setAttribute("server.address", req.hostname);
555
+ span.setAttributes({
556
+ "http.request.method": req.method,
557
+ "server.address": req.hostname,
558
+ "url.full": urlWithoutQuery(`${req.protocol}//${req.hostname}${req.port ? `:${req.port}` : ""}${req.path}`)
559
+ });
490
560
  import_api.propagation.inject(import_api.trace.setSpan(parent, span), req.headers);
491
561
  }
562
+ const response = (res) => {
563
+ if (!import_protocols4.HttpResponse.isInstance(res)) return;
564
+ span.setAttribute("http.response.status_code", res.statusCode);
565
+ const rid = res.headers["caitlyn-request-id"];
566
+ if (rid) {
567
+ span.setAttribute("caitlyn.request_id", rid);
568
+ ctx.__caitlynSpan?.setAttribute("caitlyn.request_id", rid);
569
+ }
570
+ const api = res.headers["caitlyn-api-version"];
571
+ if (api) span.setAttribute("caitlyn.server.api_version", api);
572
+ if (res.statusCode >= 400) span.setStatus({ code: import_api.SpanStatusCode.ERROR });
573
+ };
492
574
  try {
493
575
  const out = await next(args);
494
- if (import_protocols4.HttpResponse.isInstance(out.response)) {
495
- span.setAttribute("http.response.status_code", out.response.statusCode);
496
- const rid = out.response.headers["caitlyn-request-id"];
497
- if (rid) {
498
- span.setAttribute("caitlyn.request_id", rid);
499
- ctx.__caitlynSpan?.setAttribute("caitlyn.request_id", rid);
500
- }
501
- }
576
+ response(out.response);
502
577
  return out;
503
578
  } catch (err) {
504
- const res = err.$response;
505
- if (import_protocols4.HttpResponse.isInstance(res)) span.setAttribute("http.response.status_code", res.statusCode);
579
+ response(err.$response);
506
580
  span.setStatus({ code: import_api.SpanStatusCode.ERROR });
507
581
  throw err;
508
582
  } finally {
@@ -565,17 +639,23 @@ var SseStream = class {
565
639
  seen = /* @__PURE__ */ new Set();
566
640
  lastEventId;
567
641
  reconnects = 0;
642
+ /** The Caitlyn-Request-Id of the response that opened the stream; set once iteration has started. */
643
+ requestId;
568
644
  aborted() {
569
645
  return this.signal?.reason ?? new DOMException("Aborted", "AbortError");
570
646
  }
571
647
  async *[Symbol.asyncIterator]() {
572
648
  if (this.consumed) throw new Error("SseStream can only be iterated once");
573
649
  this.consumed = true;
574
- let body = await this.open();
650
+ const opened = await this.open();
651
+ this.requestId = opened.$metadata?.requestId;
652
+ let body = opened.events;
575
653
  for (; ; ) {
576
654
  let ended = false;
655
+ let onResponse = 0;
577
656
  try {
578
657
  for await (const msg of parseSse(body)) {
658
+ onResponse++;
579
659
  if (msg.id) {
580
660
  if (this.seen.has(msg.id)) continue;
581
661
  this.seen.add(msg.id);
@@ -592,7 +672,7 @@ var SseStream = class {
592
672
  return;
593
673
  }
594
674
  }
595
- if (!this.spec.final) return;
675
+ if (!this.spec.final || onResponse === 0) return;
596
676
  } catch (err) {
597
677
  if (this.signal?.aborted) throw this.aborted();
598
678
  if (ended || err?.name === "AbortError" || err === void 0 || err.$fault || err instanceof SyntaxError) throw err;
@@ -600,7 +680,7 @@ var SseStream = class {
600
680
  if (this.signal?.aborted) throw this.aborted();
601
681
  if (!this.resume || ++this.reconnects > this.maxReconnects)
602
682
  throw this.spec.toError({ status: 0, code: "STREAM_LOST", title: "The stream dropped and could not be resumed" });
603
- body = await this.resume(this.lastEventId);
683
+ body = (await this.resume(this.lastEventId)).events;
604
684
  }
605
685
  }
606
686
  /** Drains the stream if needed and returns the final event's member. */
@@ -612,15 +692,40 @@ var SseStream = class {
612
692
  };
613
693
 
614
694
  // packages/sdk-core/src/upload.ts
615
- async function uploadFile(file, fileName, reserve, toError, signal) {
695
+ var import_api2 = require("@opentelemetry/api");
696
+ var STATUS = /* @__PURE__ */ Symbol("status");
697
+ async function uploadFile(file, fileName, reserve, toError, signal, tracer) {
616
698
  const bytes = new Uint8Array(await file.arrayBuffer());
617
699
  const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
618
700
  let binary = "";
619
701
  for (const b of digest) binary += String.fromCharCode(b);
620
702
  const slot = await reserve({ fileName, contentType: file.type || "application/octet-stream", byteSize: bytes.byteLength, sha256: btoa(binary) });
621
- const res = await fetch(slot.uploadUrl, { method: slot.uploadMethod, headers: slot.requiredHeaders, body: bytes, signal });
622
- if (!res.ok) throw toError({ status: res.status, code: "UPLOAD_PUT_FAILED", title: "Uploading the bytes failed", detail: await res.text() });
623
- return slot;
703
+ const put = async () => {
704
+ const res = await fetch(slot.uploadUrl, { method: slot.uploadMethod, headers: slot.requiredHeaders, body: bytes, signal });
705
+ if (!res.ok) throw Object.assign(toError({ status: res.status, code: "UPLOAD_PUT_FAILED", title: "Uploading the bytes failed", detail: await res.text() }), { [STATUS]: res.status });
706
+ return res.status;
707
+ };
708
+ if (!tracer) {
709
+ await put();
710
+ return slot;
711
+ }
712
+ const url = new URL(slot.uploadUrl);
713
+ const span = tracer.startSpan("caitlyn.upload", {
714
+ kind: import_api2.SpanKind.CLIENT,
715
+ attributes: { "http.request.method": slot.uploadMethod || "PUT", "server.address": url.hostname, "url.full": urlWithoutQuery(slot.uploadUrl) }
716
+ });
717
+ try {
718
+ span.setAttribute("http.response.status_code", await put());
719
+ return slot;
720
+ } catch (err) {
721
+ const status = err[STATUS];
722
+ if (status !== void 0) span.setAttribute("http.response.status_code", status);
723
+ recordFailure(span, err);
724
+ span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
725
+ throw err;
726
+ } finally {
727
+ span.end();
728
+ }
624
729
  }
625
730
 
626
731
  // packages/sdk-core/src/auth.ts
@@ -644,8 +749,30 @@ var SESSION_PLACEHOLDER = "session";
644
749
  var refreshAt = (t) => t.expiresAt === void 0 ? void 0 : t.expiresAt - Math.min(REFRESH_EARLY_MS, (t.expiresAt - Date.now()) / 2);
645
750
  var b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
646
751
  var randomString = (n = 32) => b64url(crypto.getRandomValues(new Uint8Array(n)));
647
- var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api/v3`;
752
+ var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api`;
648
753
  var workspaceHost = (workspace) => workspace.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
754
+ var isBrowser = () => {
755
+ const g = globalThis;
756
+ return typeof g.window !== "undefined" && typeof g.document !== "undefined";
757
+ };
758
+ var IN_BROWSER = "not_allowed_in_browser";
759
+ function guardBrowser(o) {
760
+ if (!isBrowser()) return;
761
+ if (o.mode === "clientCredentials" || o.mode === "oauth" && o.clientSecret) {
762
+ throw new CaitlynAuthError(
763
+ `${o.mode === "oauth" ? "oauth with a clientSecret" : "clientCredentials"} cannot be used in a browser: a client secret in a browser is visible to everyone who loads the page. Use oauth without a clientSecret (authorisation code with PKCE), or sign in on your server.`,
764
+ IN_BROWSER
765
+ );
766
+ }
767
+ if (o.mode === "awsIam" || o.mode === "workloadIdentity") {
768
+ throw new CaitlynAuthError(`${o.mode} cannot be used in a browser: it signs in a server workload with that workload's own identity. Use oauth in a browser.`, IN_BROWSER);
769
+ }
770
+ if (o.mode === "token" && !o.allowBrowserToken) {
771
+ console.warn(
772
+ "Caitlyn: a long-lived token in a browser is exposed to anyone who can run script on the page. token mode in a browser is for local testing; use oauth instead. Set auth.allowBrowserToken: true to silence this warning."
773
+ );
774
+ }
775
+ }
649
776
  var AuthSession = class {
650
777
  constructor(options, endpoint, workspace, fetchFn = (...a) => fetch(...a)) {
651
778
  this.options = options;
@@ -656,6 +783,7 @@ var AuthSession = class {
656
783
  this.authorizationEndpoint = `${endpoint}/oauth/authorize`;
657
784
  this.workspaceUrl = `https://${workspace}`;
658
785
  this.resource = "resource" in options && options.resource || workspaceAudience(workspace);
786
+ guardBrowser(options);
659
787
  }
660
788
  options;
661
789
  endpoint;
@@ -668,7 +796,7 @@ var AuthSession = class {
668
796
  memoryTokens;
669
797
  tokenEndpoint;
670
798
  authorizationEndpoint;
671
- /** The workspace's resource URL, https://<workspace>/api/v3: the audience every token is bound to. */
799
+ /** The workspace's resource URL, https://<workspace>/api: the audience every token is bound to. */
672
800
  resource;
673
801
  /** https://<workspace>: the default Origin for session writes. */
674
802
  workspaceUrl;
@@ -892,6 +1020,9 @@ function clientConfig(opts, auth = createAuth(opts)) {
892
1020
  workspace: workspaceHost(opts.workspace),
893
1021
  endpoint: opts.endpoint ?? workspaceEndpoint(opts.workspace),
894
1022
  token: tokenIdentityProvider(auth),
1023
+ /** The tracer upload helpers record the byte transfer with (PROTOCOL.md 13); absent with telemetry off. */
1024
+ caitlynTracer: opts.telemetry?.enabled === false ? void 0 : caitlynTracer(opts.telemetry?.tracerProvider),
1025
+ ...opts.requestHandler !== void 0 ? { requestHandler: opts.requestHandler } : {},
895
1026
  ...opts.maxAttempts !== void 0 ? { maxAttempts: opts.maxAttempts } : {},
896
1027
  ...opts.maxRetryDelayMs !== void 0 ? { retryStrategy: new CaitlynRetryStrategy(opts.maxAttempts ?? 3, opts.maxRetryDelayMs) } : {}
897
1028
  };
@@ -965,10 +1096,12 @@ async function waitUntil(name, read, accept, opts = {}) {
965
1096
  SseStream,
966
1097
  WaiterError,
967
1098
  caitlynEndpointProvider,
1099
+ caitlynTracer,
968
1100
  clientConfig,
969
1101
  createAuth,
970
1102
  defaultAwsCredentials,
971
1103
  getCaitlynAuthPlugin,
1104
+ getCaitlynConnectionErrorPlugin,
972
1105
  getCaitlynSdkPlugin,
973
1106
  getCaitlynTelemetryPlugin,
974
1107
  getWebIdentityToken,