@weckr/sdk 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.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @weckr/sdk
2
+
3
+ AI cost and margin intelligence for SaaS founders.
4
+
5
+ See exactly which users cost more than they pay — per LLM call, zero added latency.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @weckr/sdk
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { Weckr } from '@weckr/sdk';
17
+ import OpenAI from 'openai';
18
+
19
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
20
+
21
+ const wk = new Weckr({
22
+ apiKey: 'wk_your_key_here',
23
+ plans: {
24
+ free: 0,
25
+ starter: 9,
26
+ pro: 29,
27
+ business: 99,
28
+ },
29
+ });
30
+
31
+ const result = await wk.chat(openai, {
32
+ model: 'gpt-4o',
33
+ messages: [{ role: 'user', content: prompt }],
34
+ userId: user.id,
35
+ feature: 'ai-summary',
36
+ plan: user.plan,
37
+ });
38
+ ```
39
+
40
+ The original LLM call runs unchanged and returns immediately. After it resolves, Weckr fires an async log to the Weckr API. The log call is fire-and-forget — if it fails or stalls, your request is unaffected.
41
+
42
+ ## Get your API key
43
+
44
+ Sign up at [https://useweckr.com](https://useweckr.com).
45
+
46
+ ## Supported providers
47
+
48
+ - **OpenAI** — `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`
49
+ - **Anthropic** — `claude-opus-4`, `claude-sonnet-4`, `claude-haiku-4`
50
+ - **Gemini** — `gemini-2.5-flash`, `gemini-2.5-pro`
51
+
52
+ ## What gets logged
53
+
54
+ ```ts
55
+ {
56
+ userId, feature, model, provider,
57
+ inputTokens, outputTokens,
58
+ costUsd, latencyMs,
59
+ planName, planRevenueUsd, marginUsd,
60
+ timestamp,
61
+ }
62
+ ```
63
+
64
+ Cost is computed from public per-token pricing. Margin is `planRevenueUsd - costUsd` (negative means you're losing money on that user).
65
+
66
+ ## Dashboard
67
+
68
+ View cost and margin data at [https://useweckr.com/dashboard](https://useweckr.com/dashboard).
69
+
70
+ ## License
71
+
72
+ MIT
@@ -0,0 +1,65 @@
1
+ type Provider = 'openai' | 'anthropic' | 'gemini';
2
+ interface WeckrConfig {
3
+ apiKey: string;
4
+ plans?: Record<string, number>;
5
+ endpoint?: string;
6
+ fetch?: typeof fetch;
7
+ onError?: (err: unknown) => void;
8
+ }
9
+ interface ChatOptions {
10
+ model: string;
11
+ messages: Array<{
12
+ role: string;
13
+ content: string;
14
+ }>;
15
+ userId?: string;
16
+ feature?: string;
17
+ plan?: string;
18
+ [key: string]: unknown;
19
+ }
20
+ interface NormalizedUsage {
21
+ inputTokens: number;
22
+ outputTokens: number;
23
+ }
24
+ interface LogPayload {
25
+ userId: string | null;
26
+ feature: string | null;
27
+ model: string;
28
+ provider: Provider;
29
+ inputTokens: number;
30
+ outputTokens: number;
31
+ costUsd: number;
32
+ latencyMs: number;
33
+ planName: string | null;
34
+ planRevenueUsd: number | null;
35
+ marginUsd: number | null;
36
+ timestamp: string;
37
+ }
38
+ interface ProviderAdapter<TClient = unknown, TResult = unknown> {
39
+ name: Provider;
40
+ matches(client: unknown): client is TClient;
41
+ call(client: TClient, options: ChatOptions): Promise<TResult>;
42
+ extractUsage(result: TResult): NormalizedUsage;
43
+ }
44
+
45
+ declare class Weckr {
46
+ private readonly apiKey;
47
+ private readonly plans;
48
+ private readonly log;
49
+ constructor(config: WeckrConfig);
50
+ chat<TClient, TResult = unknown>(client: TClient, options: ChatOptions): Promise<TResult>;
51
+ }
52
+
53
+ interface ModelPricing {
54
+ provider: Provider;
55
+ inputPerMillion: number;
56
+ outputPerMillion: number;
57
+ }
58
+ declare const PRICING: Record<string, ModelPricing>;
59
+ declare function resolvePricing(model: string): ModelPricing | null;
60
+ declare function calculateCost(model: string, inputTokens: number, outputTokens: number): {
61
+ costUsd: number;
62
+ provider: Provider | null;
63
+ };
64
+
65
+ export { type ChatOptions, type LogPayload, type NormalizedUsage, PRICING, type Provider, type ProviderAdapter, Weckr, type WeckrConfig, calculateCost, resolvePricing };
@@ -0,0 +1,65 @@
1
+ type Provider = 'openai' | 'anthropic' | 'gemini';
2
+ interface WeckrConfig {
3
+ apiKey: string;
4
+ plans?: Record<string, number>;
5
+ endpoint?: string;
6
+ fetch?: typeof fetch;
7
+ onError?: (err: unknown) => void;
8
+ }
9
+ interface ChatOptions {
10
+ model: string;
11
+ messages: Array<{
12
+ role: string;
13
+ content: string;
14
+ }>;
15
+ userId?: string;
16
+ feature?: string;
17
+ plan?: string;
18
+ [key: string]: unknown;
19
+ }
20
+ interface NormalizedUsage {
21
+ inputTokens: number;
22
+ outputTokens: number;
23
+ }
24
+ interface LogPayload {
25
+ userId: string | null;
26
+ feature: string | null;
27
+ model: string;
28
+ provider: Provider;
29
+ inputTokens: number;
30
+ outputTokens: number;
31
+ costUsd: number;
32
+ latencyMs: number;
33
+ planName: string | null;
34
+ planRevenueUsd: number | null;
35
+ marginUsd: number | null;
36
+ timestamp: string;
37
+ }
38
+ interface ProviderAdapter<TClient = unknown, TResult = unknown> {
39
+ name: Provider;
40
+ matches(client: unknown): client is TClient;
41
+ call(client: TClient, options: ChatOptions): Promise<TResult>;
42
+ extractUsage(result: TResult): NormalizedUsage;
43
+ }
44
+
45
+ declare class Weckr {
46
+ private readonly apiKey;
47
+ private readonly plans;
48
+ private readonly log;
49
+ constructor(config: WeckrConfig);
50
+ chat<TClient, TResult = unknown>(client: TClient, options: ChatOptions): Promise<TResult>;
51
+ }
52
+
53
+ interface ModelPricing {
54
+ provider: Provider;
55
+ inputPerMillion: number;
56
+ outputPerMillion: number;
57
+ }
58
+ declare const PRICING: Record<string, ModelPricing>;
59
+ declare function resolvePricing(model: string): ModelPricing | null;
60
+ declare function calculateCost(model: string, inputTokens: number, outputTokens: number): {
61
+ costUsd: number;
62
+ provider: Provider | null;
63
+ };
64
+
65
+ export { type ChatOptions, type LogPayload, type NormalizedUsage, PRICING, type Provider, type ProviderAdapter, Weckr, type WeckrConfig, calculateCost, resolvePricing };
package/dist/index.js ADDED
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PRICING: () => PRICING,
24
+ Weckr: () => Weckr,
25
+ calculateCost: () => calculateCost,
26
+ resolvePricing: () => resolvePricing
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/pricing.ts
31
+ var PRICING = {
32
+ "gpt-4o": { provider: "openai", inputPerMillion: 2.5, outputPerMillion: 10 },
33
+ "gpt-4o-mini": { provider: "openai", inputPerMillion: 0.15, outputPerMillion: 0.6 },
34
+ "gpt-3.5-turbo": { provider: "openai", inputPerMillion: 0.5, outputPerMillion: 1.5 },
35
+ "claude-opus-4": { provider: "anthropic", inputPerMillion: 15, outputPerMillion: 75 },
36
+ "claude-sonnet-4": { provider: "anthropic", inputPerMillion: 3, outputPerMillion: 15 },
37
+ "claude-haiku-4": { provider: "anthropic", inputPerMillion: 0.8, outputPerMillion: 4 },
38
+ "gemini-2.5-flash": { provider: "gemini", inputPerMillion: 0.15, outputPerMillion: 0.6 },
39
+ "gemini-2.5-pro": { provider: "gemini", inputPerMillion: 1.25, outputPerMillion: 10 }
40
+ };
41
+ function resolvePricing(model) {
42
+ if (PRICING[model]) return PRICING[model];
43
+ const lower = model.toLowerCase();
44
+ for (const key of Object.keys(PRICING)) {
45
+ if (lower.startsWith(key.toLowerCase())) return PRICING[key];
46
+ }
47
+ return null;
48
+ }
49
+ function calculateCost(model, inputTokens, outputTokens) {
50
+ const pricing = resolvePricing(model);
51
+ if (!pricing) return { costUsd: 0, provider: null };
52
+ const cost = inputTokens / 1e6 * pricing.inputPerMillion + outputTokens / 1e6 * pricing.outputPerMillion;
53
+ return { costUsd: round6(cost), provider: pricing.provider };
54
+ }
55
+ function round6(n) {
56
+ return Math.round(n * 1e6) / 1e6;
57
+ }
58
+
59
+ // src/providers.ts
60
+ var openaiAdapter = {
61
+ name: "openai",
62
+ matches(client) {
63
+ return isObject(client) && isObject(client.chat) && isObject(client.chat.completions) && typeof client.chat.completions.create === "function";
64
+ },
65
+ async call(client, options) {
66
+ const { userId, feature, plan, ...rest } = options;
67
+ return client.chat.completions.create(rest);
68
+ },
69
+ extractUsage(result) {
70
+ const usage = result?.usage ?? {};
71
+ return {
72
+ inputTokens: toInt(usage.prompt_tokens ?? usage.input_tokens),
73
+ outputTokens: toInt(usage.completion_tokens ?? usage.output_tokens)
74
+ };
75
+ }
76
+ };
77
+ var anthropicAdapter = {
78
+ name: "anthropic",
79
+ matches(client) {
80
+ return isObject(client) && isObject(client.messages) && typeof client.messages.create === "function";
81
+ },
82
+ async call(client, options) {
83
+ const { userId, feature, plan, ...rest } = options;
84
+ return client.messages.create(rest);
85
+ },
86
+ extractUsage(result) {
87
+ const usage = result?.usage ?? {};
88
+ return {
89
+ inputTokens: toInt(usage.input_tokens),
90
+ outputTokens: toInt(usage.output_tokens)
91
+ };
92
+ }
93
+ };
94
+ var geminiAdapter = {
95
+ name: "gemini",
96
+ matches(client) {
97
+ return isObject(client) && isObject(client.models) && typeof client.models.generateContent === "function";
98
+ },
99
+ async call(client, options) {
100
+ const { userId, feature, plan, messages, ...rest } = options;
101
+ const contents = (messages ?? []).map((m) => ({
102
+ role: m.role === "assistant" ? "model" : "user",
103
+ parts: [{ text: m.content }]
104
+ }));
105
+ return client.models.generateContent({ ...rest, contents });
106
+ },
107
+ extractUsage(result) {
108
+ const meta = result?.usageMetadata ?? {};
109
+ return {
110
+ inputTokens: toInt(meta.promptTokenCount),
111
+ outputTokens: toInt(meta.candidatesTokenCount)
112
+ };
113
+ }
114
+ };
115
+ var adapters = [openaiAdapter, anthropicAdapter, geminiAdapter];
116
+ function detectAdapter(client) {
117
+ for (const adapter of adapters) {
118
+ if (adapter.matches(client)) return adapter;
119
+ }
120
+ return null;
121
+ }
122
+ function isObject(v) {
123
+ return typeof v === "object" && v !== null;
124
+ }
125
+ function toInt(v) {
126
+ const n = typeof v === "number" ? v : Number(v);
127
+ return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
128
+ }
129
+
130
+ // src/logger.ts
131
+ function createLogger(opts) {
132
+ const f = opts.fetch ?? globalThis.fetch;
133
+ if (typeof f !== "function") {
134
+ throw new Error("Weckr: global fetch is unavailable. Pass a fetch implementation via config.fetch.");
135
+ }
136
+ return function log(payload) {
137
+ queueMicrotask(() => {
138
+ let promise;
139
+ try {
140
+ promise = f(opts.endpoint, {
141
+ method: "POST",
142
+ headers: {
143
+ "content-type": "application/json",
144
+ "x-api-key": opts.apiKey
145
+ },
146
+ body: JSON.stringify(payload),
147
+ keepalive: true
148
+ });
149
+ } catch (err) {
150
+ opts.onError?.(err);
151
+ return;
152
+ }
153
+ promise.then(async (res) => {
154
+ if (!res.ok) {
155
+ const body = await res.text().catch(() => "");
156
+ opts.onError?.(
157
+ new Error(`Weckr log failed: ${res.status} ${res.statusText} ${body}`.trim())
158
+ );
159
+ }
160
+ }).catch((err) => {
161
+ opts.onError?.(err);
162
+ });
163
+ });
164
+ };
165
+ }
166
+
167
+ // src/weckr.ts
168
+ var DEFAULT_ENDPOINT = "https://useweckr.com/api/v1/log";
169
+ var Weckr = class {
170
+ apiKey;
171
+ plans;
172
+ log;
173
+ constructor(config) {
174
+ if (!config?.apiKey) {
175
+ throw new Error("Weckr: apiKey is required.");
176
+ }
177
+ this.apiKey = config.apiKey;
178
+ this.plans = config.plans ?? {};
179
+ this.log = createLogger({
180
+ apiKey: config.apiKey,
181
+ endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
182
+ fetch: config.fetch,
183
+ onError: config.onError
184
+ });
185
+ }
186
+ async chat(client, options) {
187
+ const adapter = detectAdapter(client);
188
+ if (!adapter) {
189
+ throw new Error(
190
+ "Weckr: could not detect provider. Pass an OpenAI, Anthropic, or Gemini client instance."
191
+ );
192
+ }
193
+ const startedAt = nowMs();
194
+ const result = await adapter.call(client, options);
195
+ const latencyMs = Math.round(nowMs() - startedAt);
196
+ try {
197
+ const usage = adapter.extractUsage(result);
198
+ const { costUsd } = calculateCost(options.model, usage.inputTokens, usage.outputTokens);
199
+ const planName = options.plan ?? null;
200
+ const planRevenueUsd = planName != null && Object.prototype.hasOwnProperty.call(this.plans, planName) ? this.plans[planName] : null;
201
+ const marginUsd = planRevenueUsd != null ? round2(planRevenueUsd - costUsd) : null;
202
+ const payload = {
203
+ userId: options.userId ?? null,
204
+ feature: options.feature ?? null,
205
+ model: options.model,
206
+ provider: adapter.name,
207
+ inputTokens: usage.inputTokens,
208
+ outputTokens: usage.outputTokens,
209
+ costUsd,
210
+ latencyMs,
211
+ planName,
212
+ planRevenueUsd,
213
+ marginUsd,
214
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
215
+ };
216
+ this.log(payload);
217
+ } catch {
218
+ }
219
+ return result;
220
+ }
221
+ };
222
+ function nowMs() {
223
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
224
+ return performance.now();
225
+ }
226
+ return Date.now();
227
+ }
228
+ function round2(n) {
229
+ return Math.round(n * 100) / 100;
230
+ }
231
+ // Annotate the CommonJS export names for ESM import in node:
232
+ 0 && (module.exports = {
233
+ PRICING,
234
+ Weckr,
235
+ calculateCost,
236
+ resolvePricing
237
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,207 @@
1
+ // src/pricing.ts
2
+ var PRICING = {
3
+ "gpt-4o": { provider: "openai", inputPerMillion: 2.5, outputPerMillion: 10 },
4
+ "gpt-4o-mini": { provider: "openai", inputPerMillion: 0.15, outputPerMillion: 0.6 },
5
+ "gpt-3.5-turbo": { provider: "openai", inputPerMillion: 0.5, outputPerMillion: 1.5 },
6
+ "claude-opus-4": { provider: "anthropic", inputPerMillion: 15, outputPerMillion: 75 },
7
+ "claude-sonnet-4": { provider: "anthropic", inputPerMillion: 3, outputPerMillion: 15 },
8
+ "claude-haiku-4": { provider: "anthropic", inputPerMillion: 0.8, outputPerMillion: 4 },
9
+ "gemini-2.5-flash": { provider: "gemini", inputPerMillion: 0.15, outputPerMillion: 0.6 },
10
+ "gemini-2.5-pro": { provider: "gemini", inputPerMillion: 1.25, outputPerMillion: 10 }
11
+ };
12
+ function resolvePricing(model) {
13
+ if (PRICING[model]) return PRICING[model];
14
+ const lower = model.toLowerCase();
15
+ for (const key of Object.keys(PRICING)) {
16
+ if (lower.startsWith(key.toLowerCase())) return PRICING[key];
17
+ }
18
+ return null;
19
+ }
20
+ function calculateCost(model, inputTokens, outputTokens) {
21
+ const pricing = resolvePricing(model);
22
+ if (!pricing) return { costUsd: 0, provider: null };
23
+ const cost = inputTokens / 1e6 * pricing.inputPerMillion + outputTokens / 1e6 * pricing.outputPerMillion;
24
+ return { costUsd: round6(cost), provider: pricing.provider };
25
+ }
26
+ function round6(n) {
27
+ return Math.round(n * 1e6) / 1e6;
28
+ }
29
+
30
+ // src/providers.ts
31
+ var openaiAdapter = {
32
+ name: "openai",
33
+ matches(client) {
34
+ return isObject(client) && isObject(client.chat) && isObject(client.chat.completions) && typeof client.chat.completions.create === "function";
35
+ },
36
+ async call(client, options) {
37
+ const { userId, feature, plan, ...rest } = options;
38
+ return client.chat.completions.create(rest);
39
+ },
40
+ extractUsage(result) {
41
+ const usage = result?.usage ?? {};
42
+ return {
43
+ inputTokens: toInt(usage.prompt_tokens ?? usage.input_tokens),
44
+ outputTokens: toInt(usage.completion_tokens ?? usage.output_tokens)
45
+ };
46
+ }
47
+ };
48
+ var anthropicAdapter = {
49
+ name: "anthropic",
50
+ matches(client) {
51
+ return isObject(client) && isObject(client.messages) && typeof client.messages.create === "function";
52
+ },
53
+ async call(client, options) {
54
+ const { userId, feature, plan, ...rest } = options;
55
+ return client.messages.create(rest);
56
+ },
57
+ extractUsage(result) {
58
+ const usage = result?.usage ?? {};
59
+ return {
60
+ inputTokens: toInt(usage.input_tokens),
61
+ outputTokens: toInt(usage.output_tokens)
62
+ };
63
+ }
64
+ };
65
+ var geminiAdapter = {
66
+ name: "gemini",
67
+ matches(client) {
68
+ return isObject(client) && isObject(client.models) && typeof client.models.generateContent === "function";
69
+ },
70
+ async call(client, options) {
71
+ const { userId, feature, plan, messages, ...rest } = options;
72
+ const contents = (messages ?? []).map((m) => ({
73
+ role: m.role === "assistant" ? "model" : "user",
74
+ parts: [{ text: m.content }]
75
+ }));
76
+ return client.models.generateContent({ ...rest, contents });
77
+ },
78
+ extractUsage(result) {
79
+ const meta = result?.usageMetadata ?? {};
80
+ return {
81
+ inputTokens: toInt(meta.promptTokenCount),
82
+ outputTokens: toInt(meta.candidatesTokenCount)
83
+ };
84
+ }
85
+ };
86
+ var adapters = [openaiAdapter, anthropicAdapter, geminiAdapter];
87
+ function detectAdapter(client) {
88
+ for (const adapter of adapters) {
89
+ if (adapter.matches(client)) return adapter;
90
+ }
91
+ return null;
92
+ }
93
+ function isObject(v) {
94
+ return typeof v === "object" && v !== null;
95
+ }
96
+ function toInt(v) {
97
+ const n = typeof v === "number" ? v : Number(v);
98
+ return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
99
+ }
100
+
101
+ // src/logger.ts
102
+ function createLogger(opts) {
103
+ const f = opts.fetch ?? globalThis.fetch;
104
+ if (typeof f !== "function") {
105
+ throw new Error("Weckr: global fetch is unavailable. Pass a fetch implementation via config.fetch.");
106
+ }
107
+ return function log(payload) {
108
+ queueMicrotask(() => {
109
+ let promise;
110
+ try {
111
+ promise = f(opts.endpoint, {
112
+ method: "POST",
113
+ headers: {
114
+ "content-type": "application/json",
115
+ "x-api-key": opts.apiKey
116
+ },
117
+ body: JSON.stringify(payload),
118
+ keepalive: true
119
+ });
120
+ } catch (err) {
121
+ opts.onError?.(err);
122
+ return;
123
+ }
124
+ promise.then(async (res) => {
125
+ if (!res.ok) {
126
+ const body = await res.text().catch(() => "");
127
+ opts.onError?.(
128
+ new Error(`Weckr log failed: ${res.status} ${res.statusText} ${body}`.trim())
129
+ );
130
+ }
131
+ }).catch((err) => {
132
+ opts.onError?.(err);
133
+ });
134
+ });
135
+ };
136
+ }
137
+
138
+ // src/weckr.ts
139
+ var DEFAULT_ENDPOINT = "https://useweckr.com/api/v1/log";
140
+ var Weckr = class {
141
+ apiKey;
142
+ plans;
143
+ log;
144
+ constructor(config) {
145
+ if (!config?.apiKey) {
146
+ throw new Error("Weckr: apiKey is required.");
147
+ }
148
+ this.apiKey = config.apiKey;
149
+ this.plans = config.plans ?? {};
150
+ this.log = createLogger({
151
+ apiKey: config.apiKey,
152
+ endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
153
+ fetch: config.fetch,
154
+ onError: config.onError
155
+ });
156
+ }
157
+ async chat(client, options) {
158
+ const adapter = detectAdapter(client);
159
+ if (!adapter) {
160
+ throw new Error(
161
+ "Weckr: could not detect provider. Pass an OpenAI, Anthropic, or Gemini client instance."
162
+ );
163
+ }
164
+ const startedAt = nowMs();
165
+ const result = await adapter.call(client, options);
166
+ const latencyMs = Math.round(nowMs() - startedAt);
167
+ try {
168
+ const usage = adapter.extractUsage(result);
169
+ const { costUsd } = calculateCost(options.model, usage.inputTokens, usage.outputTokens);
170
+ const planName = options.plan ?? null;
171
+ const planRevenueUsd = planName != null && Object.prototype.hasOwnProperty.call(this.plans, planName) ? this.plans[planName] : null;
172
+ const marginUsd = planRevenueUsd != null ? round2(planRevenueUsd - costUsd) : null;
173
+ const payload = {
174
+ userId: options.userId ?? null,
175
+ feature: options.feature ?? null,
176
+ model: options.model,
177
+ provider: adapter.name,
178
+ inputTokens: usage.inputTokens,
179
+ outputTokens: usage.outputTokens,
180
+ costUsd,
181
+ latencyMs,
182
+ planName,
183
+ planRevenueUsd,
184
+ marginUsd,
185
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
186
+ };
187
+ this.log(payload);
188
+ } catch {
189
+ }
190
+ return result;
191
+ }
192
+ };
193
+ function nowMs() {
194
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
195
+ return performance.now();
196
+ }
197
+ return Date.now();
198
+ }
199
+ function round2(n) {
200
+ return Math.round(n * 100) / 100;
201
+ }
202
+ export {
203
+ PRICING,
204
+ Weckr,
205
+ calculateCost,
206
+ resolvePricing
207
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@weckr/sdk",
3
+ "version": "0.1.0",
4
+ "description": "AI cost and margin intelligence for SaaS founders",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts",
21
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "ai",
29
+ "llm",
30
+ "openai",
31
+ "anthropic",
32
+ "gemini",
33
+ "cost",
34
+ "monitoring",
35
+ "saas",
36
+ "finops"
37
+ ],
38
+ "author": "Weckr",
39
+ "license": "MIT",
40
+ "homepage": "https://useweckr.com",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/Ghiles3232/weckr"
44
+ },
45
+ "peerDependencies": {
46
+ "openai": ">=4.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "openai": { "optional": true },
50
+ "@anthropic-ai/sdk": { "optional": true },
51
+ "@google/generative-ai": { "optional": true }
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.10.0",
55
+ "openai": "^4.77.0",
56
+ "tsup": "^8.3.5",
57
+ "tsx": "^4.19.2",
58
+ "typescript": "^5.7.2",
59
+ "vitest": "^2.1.8"
60
+ }
61
+ }