@usagetap/sdk 0.2.1 → 0.4.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 CHANGED
@@ -23,6 +23,12 @@ const usageTap = new UsageTapClient({
23
23
 
24
24
  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
25
25
 
26
+ await usageTap.createCustomer({
27
+ customerId: "cust_123",
28
+ customerFriendlyName: "Acme AI",
29
+ customerEmail: "billing@acme.ai",
30
+ });
31
+
26
32
  function selectCapabilities(allowed: {
27
33
  standard?: boolean;
28
34
  premium?: boolean;
@@ -385,7 +391,8 @@ const { begin, end, vendor, endUsage } = envelope.data;
385
391
 
386
392
  Key exports from `@usagetap/sdk`:
387
393
 
388
- - `UsageTapClient` – minimal HTTP client for `call_begin`, `call_end`, and `checkUsage`.
394
+ - `UsageTapClient` – minimal HTTP client for `createCustomer`, `call_begin`, `call_end`, and `checkUsage`.
395
+ - `createCustomer` – idempotently ensure a customer subscription exists before starting a call.
389
396
  - `checkUsage` – lightweight method to query current usage status without creating a call session.
390
397
  - `wrapOpenAI` – wraps an OpenAI client instance with automatic begin/end handling.
391
398
  - `wrapFetch` – wraps a fetch function to automatically instrument OpenAI API calls (minimal integration).
@@ -397,6 +404,25 @@ Key exports from `@usagetap/sdk`:
397
404
 
398
405
  All helpers are designed for server runtimes. Use `UsageTapClient` with `allowBrowser: true` only for sandbox/test scenarios.
399
406
 
407
+ ### Ensure a customer subscription exists
408
+
409
+ Run `createCustomer` before you invoke `call_begin` (or higher-level helpers) to guarantee the customer has an active subscription. The endpoint is fully idempotent—repeat calls return the existing snapshot and set `newCustomer: false`:
410
+
411
+ ```ts
412
+ const snapshot = await usageTap.createCustomer({
413
+ customerId: "cust_123",
414
+ customerFriendlyName: "Acme AI",
415
+ customerEmail: "billing@acme.ai",
416
+ stripeCustomerId: "cus_123",
417
+ });
418
+
419
+ console.log("New customer?", snapshot.data.newCustomer);
420
+ console.log("Plan:", snapshot.data.plan);
421
+ console.log("Allowed entitlements:", snapshot.data.allowed);
422
+ ```
423
+
424
+ This returns the same rich subscription snapshot surfaces by `call_begin` and `checkUsage`, making it safe to cache the response for onboarding flows. Pass an `idempotencyKey` in `CreateCustomerOptions` when you need deterministic keys across services; otherwise the client auto-generates one by default.
425
+
400
426
  ### Check usage without creating a call
401
427
 
402
428
  When you need to display current quota status, plan details, or remaining balances without tracking a vendor call, use `checkUsage()`:
@@ -1,2 +1,2 @@
1
1
  import 'openai';
2
- export { N as NodeResponseLike, O as OpenAIAdapter, b as OpenAIAdapterInit, d as OpenAIInvokeParams, e as OpenAIInvokeResult, $ as OpenAIRequestContext, a0 as OpenAIStreamCallResult, f as OpenAIStreamParams, g as OpenAIStreamResult, S as StreamMode, l as StreamOpenAIRouteOptions, h as StreamToResponseOptions, a1 as UsageTapStream, i as WrapOpenAICallOptions, W as WrapOpenAIContext, a as WrapOpenAIOptions, j as WrapOpenAIResponseCallOptions, k as WrappedOpenAI, c as createOpenAIAdapter, p as pipeToResponse, s as streamOpenAIRoute, t as toNextResponse, w as wrapOpenAI } from '../openai-CeptbEGH.cjs';
2
+ export { N as NodeResponseLike, O as OpenAIAdapter, b as OpenAIAdapterInit, d as OpenAIInvokeParams, e as OpenAIInvokeResult, a2 as OpenAIRequestContext, a3 as OpenAIStreamCallResult, f as OpenAIStreamParams, g as OpenAIStreamResult, S as StreamMode, l as StreamOpenAIRouteOptions, h as StreamToResponseOptions, a4 as UsageTapStream, i as WrapOpenAICallOptions, W as WrapOpenAIContext, a as WrapOpenAIOptions, j as WrapOpenAIResponseCallOptions, k as WrappedOpenAI, c as createOpenAIAdapter, p as pipeToResponse, s as streamOpenAIRoute, t as toNextResponse, w as wrapOpenAI } from '../openai-Ch7hN2vD.cjs';
@@ -1,2 +1,2 @@
1
1
  import 'openai';
2
- export { N as NodeResponseLike, O as OpenAIAdapter, b as OpenAIAdapterInit, d as OpenAIInvokeParams, e as OpenAIInvokeResult, $ as OpenAIRequestContext, a0 as OpenAIStreamCallResult, f as OpenAIStreamParams, g as OpenAIStreamResult, S as StreamMode, l as StreamOpenAIRouteOptions, h as StreamToResponseOptions, a1 as UsageTapStream, i as WrapOpenAICallOptions, W as WrapOpenAIContext, a as WrapOpenAIOptions, j as WrapOpenAIResponseCallOptions, k as WrappedOpenAI, c as createOpenAIAdapter, p as pipeToResponse, s as streamOpenAIRoute, t as toNextResponse, w as wrapOpenAI } from '../openai-CeptbEGH.js';
2
+ export { N as NodeResponseLike, O as OpenAIAdapter, b as OpenAIAdapterInit, d as OpenAIInvokeParams, e as OpenAIInvokeResult, a2 as OpenAIRequestContext, a3 as OpenAIStreamCallResult, f as OpenAIStreamParams, g as OpenAIStreamResult, S as StreamMode, l as StreamOpenAIRouteOptions, h as StreamToResponseOptions, a4 as UsageTapStream, i as WrapOpenAICallOptions, W as WrapOpenAIContext, a as WrapOpenAIOptions, j as WrapOpenAIResponseCallOptions, k as WrappedOpenAI, c as createOpenAIAdapter, p as pipeToResponse, s as streamOpenAIRoute, t as toNextResponse, w as wrapOpenAI } from '../openai-Ch7hN2vD.js';
@@ -1,5 +1,5 @@
1
1
  import OpenAI from 'openai';
2
- import { U as UsageTapClient, O as OpenAIAdapter } from '../openai-CeptbEGH.cjs';
2
+ import { U as UsageTapClient, O as OpenAIAdapter } from '../openai-Ch7hN2vD.cjs';
3
3
 
4
4
  interface OpenRouterAdapterInit {
5
5
  client: OpenAI;
@@ -1,5 +1,5 @@
1
1
  import OpenAI from 'openai';
2
- import { U as UsageTapClient, O as OpenAIAdapter } from '../openai-CeptbEGH.js';
2
+ import { U as UsageTapClient, O as OpenAIAdapter } from '../openai-Ch7hN2vD.js';
3
3
 
4
4
  interface OpenRouterAdapterInit {
5
5
  client: OpenAI;
package/dist/index.cjs CHANGED
@@ -119,6 +119,7 @@ async function runWithRetry(operation, options, shouldRetry, onSchedule, signal)
119
119
  var CALL_BEGIN_PATH = "call_begin";
120
120
  var CALL_END_PATH = "call_end";
121
121
  var CHECK_USAGE_PATH = "customers/{customerId}/usage";
122
+ var CREATE_CUSTOMER_PATH = "customers";
122
123
  var AUTH_HEADER = "authorization";
123
124
  var API_KEY_HEADER = "x-api-key";
124
125
  var CORRELATION_HEADER = "x-usage-correlation-id";
@@ -126,7 +127,7 @@ var IDEMPOTENCY_HEADER = "idempotency-key";
126
127
  var SDK_HEADER = "x-usage-sdk";
127
128
  var USER_AGENT = "UsageTapClient";
128
129
  var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
129
- var SDK_VERSION = "0.2.1" ;
130
+ var SDK_VERSION = "0.4.0" ;
130
131
  var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
131
132
  var UsageTapClient = class {
132
133
  apiKey;
@@ -237,6 +238,24 @@ var UsageTapClient = class {
237
238
  );
238
239
  return response;
239
240
  }
241
+ async createCustomer(request, options = {}) {
242
+ if (!request?.customerId) {
243
+ throw new UsageTapError(
244
+ "USAGETAP_BAD_REQUEST",
245
+ "createCustomer requires customerId"
246
+ );
247
+ }
248
+ const idempotencyKey = options.idempotencyKey ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
249
+ const response = await this.request(
250
+ CREATE_CUSTOMER_PATH,
251
+ { ...request },
252
+ {
253
+ ...options,
254
+ idempotencyKey
255
+ }
256
+ );
257
+ return response;
258
+ }
240
259
  async withUsage(beginRequest, handler, options = {}) {
241
260
  const idempotencyKey = beginRequest.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
242
261
  const beginPayload = idempotencyKey ? { ...beginRequest, idempotency: idempotencyKey } : { ...beginRequest };
@@ -480,10 +499,12 @@ var UsageTapClient = class {
480
499
  const headers = {
481
500
  ...this.defaultHeaders,
482
501
  [SDK_HEADER]: `js/${SDK_VERSION}`,
483
- "user-agent": `${USER_AGENT}/${SDK_VERSION}`,
484
502
  "content-type": "application/json",
485
503
  accept: CANONICAL_MEDIA_TYPE
486
504
  };
505
+ if (!HAS_WINDOW) {
506
+ headers["user-agent"] = `${USER_AGENT}/${SDK_VERSION}`;
507
+ }
487
508
  if (this.authHeader === API_KEY_HEADER) {
488
509
  headers[API_KEY_HEADER] = this.apiKey;
489
510
  } else {