@dexcost/sdk 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.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/adapters/_netbytes.d.ts +31 -0
  4. package/dist/adapters/_netbytes.d.ts.map +1 -0
  5. package/dist/adapters/_netbytes.js +154 -0
  6. package/dist/adapters/_netbytes.js.map +1 -0
  7. package/dist/adapters/aws-lambda.d.ts +41 -0
  8. package/dist/adapters/aws-lambda.d.ts.map +1 -0
  9. package/dist/adapters/aws-lambda.js +65 -0
  10. package/dist/adapters/aws-lambda.js.map +1 -0
  11. package/dist/adapters/browser.d.ts +52 -0
  12. package/dist/adapters/browser.d.ts.map +1 -0
  13. package/dist/adapters/browser.js +127 -0
  14. package/dist/adapters/browser.js.map +1 -0
  15. package/dist/adapters/compute-wrap.d.ts +33 -0
  16. package/dist/adapters/compute-wrap.d.ts.map +1 -0
  17. package/dist/adapters/compute-wrap.js +188 -0
  18. package/dist/adapters/compute-wrap.js.map +1 -0
  19. package/dist/adapters/data/aws_lambda_pricing.json +61 -0
  20. package/dist/adapters/gpu-wrap.d.ts +31 -0
  21. package/dist/adapters/gpu-wrap.d.ts.map +1 -0
  22. package/dist/adapters/gpu-wrap.js +147 -0
  23. package/dist/adapters/gpu-wrap.js.map +1 -0
  24. package/dist/adapters/http.d.ts +58 -0
  25. package/dist/adapters/http.d.ts.map +1 -0
  26. package/dist/adapters/http.js +769 -0
  27. package/dist/adapters/http.js.map +1 -0
  28. package/dist/adapters/index.d.ts +11 -0
  29. package/dist/adapters/index.d.ts.map +1 -0
  30. package/dist/adapters/index.js +12 -0
  31. package/dist/adapters/index.js.map +1 -0
  32. package/dist/adapters/network-accountant.d.ts +63 -0
  33. package/dist/adapters/network-accountant.d.ts.map +1 -0
  34. package/dist/adapters/network-accountant.js +153 -0
  35. package/dist/adapters/network-accountant.js.map +1 -0
  36. package/dist/cli/index.d.ts +13 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +225 -0
  39. package/dist/cli/index.js.map +1 -0
  40. package/dist/cli/scanner.d.ts +39 -0
  41. package/dist/cli/scanner.d.ts.map +1 -0
  42. package/dist/cli/scanner.js +480 -0
  43. package/dist/cli/scanner.js.map +1 -0
  44. package/dist/clients.d.ts +54 -0
  45. package/dist/clients.d.ts.map +1 -0
  46. package/dist/clients.js +240 -0
  47. package/dist/clients.js.map +1 -0
  48. package/dist/cloud-detect.d.ts +96 -0
  49. package/dist/cloud-detect.d.ts.map +1 -0
  50. package/dist/cloud-detect.js +545 -0
  51. package/dist/cloud-detect.js.map +1 -0
  52. package/dist/core/auto-task.d.ts +20 -0
  53. package/dist/core/auto-task.d.ts.map +1 -0
  54. package/dist/core/auto-task.js +34 -0
  55. package/dist/core/auto-task.js.map +1 -0
  56. package/dist/core/cgroup-reader.d.ts +45 -0
  57. package/dist/core/cgroup-reader.d.ts.map +1 -0
  58. package/dist/core/cgroup-reader.js +124 -0
  59. package/dist/core/cgroup-reader.js.map +1 -0
  60. package/dist/core/cgroup-walker.d.ts +60 -0
  61. package/dist/core/cgroup-walker.d.ts.map +1 -0
  62. package/dist/core/cgroup-walker.js +166 -0
  63. package/dist/core/cgroup-walker.js.map +1 -0
  64. package/dist/core/compute-accountant.d.ts +51 -0
  65. package/dist/core/compute-accountant.d.ts.map +1 -0
  66. package/dist/core/compute-accountant.js +179 -0
  67. package/dist/core/compute-accountant.js.map +1 -0
  68. package/dist/core/compute-runtime.d.ts +42 -0
  69. package/dist/core/compute-runtime.d.ts.map +1 -0
  70. package/dist/core/compute-runtime.js +80 -0
  71. package/dist/core/compute-runtime.js.map +1 -0
  72. package/dist/core/config.d.ts +44 -0
  73. package/dist/core/config.d.ts.map +1 -0
  74. package/dist/core/config.js +66 -0
  75. package/dist/core/config.js.map +1 -0
  76. package/dist/core/context.d.ts +76 -0
  77. package/dist/core/context.d.ts.map +1 -0
  78. package/dist/core/context.js +91 -0
  79. package/dist/core/context.js.map +1 -0
  80. package/dist/core/fargate-metadata.d.ts +27 -0
  81. package/dist/core/fargate-metadata.d.ts.map +1 -0
  82. package/dist/core/fargate-metadata.js +102 -0
  83. package/dist/core/fargate-metadata.js.map +1 -0
  84. package/dist/core/gpu-accountant.d.ts +104 -0
  85. package/dist/core/gpu-accountant.d.ts.map +1 -0
  86. package/dist/core/gpu-accountant.js +383 -0
  87. package/dist/core/gpu-accountant.js.map +1 -0
  88. package/dist/core/gpu-runtime.d.ts +58 -0
  89. package/dist/core/gpu-runtime.d.ts.map +1 -0
  90. package/dist/core/gpu-runtime.js +131 -0
  91. package/dist/core/gpu-runtime.js.map +1 -0
  92. package/dist/core/heuristics.d.ts +74 -0
  93. package/dist/core/heuristics.d.ts.map +1 -0
  94. package/dist/core/heuristics.js +182 -0
  95. package/dist/core/heuristics.js.map +1 -0
  96. package/dist/core/models.d.ts +149 -0
  97. package/dist/core/models.d.ts.map +1 -0
  98. package/dist/core/models.js +226 -0
  99. package/dist/core/models.js.map +1 -0
  100. package/dist/core/nvml-reader.d.ts +114 -0
  101. package/dist/core/nvml-reader.d.ts.map +1 -0
  102. package/dist/core/nvml-reader.js +323 -0
  103. package/dist/core/nvml-reader.js.map +1 -0
  104. package/dist/core/session.d.ts +48 -0
  105. package/dist/core/session.d.ts.map +1 -0
  106. package/dist/core/session.js +123 -0
  107. package/dist/core/session.js.map +1 -0
  108. package/dist/core/tracker.d.ts +364 -0
  109. package/dist/core/tracker.d.ts.map +1 -0
  110. package/dist/core/tracker.js +1073 -0
  111. package/dist/core/tracker.js.map +1 -0
  112. package/dist/data/compute_prices.json +180 -0
  113. package/dist/data/egress_prices.json +418 -0
  114. package/dist/data/gpu_prices.json +412 -0
  115. package/dist/data/service_prices.json +2595 -0
  116. package/dist/dev-console.d.ts +12 -0
  117. package/dist/dev-console.d.ts.map +1 -0
  118. package/dist/dev-console.js +60 -0
  119. package/dist/dev-console.js.map +1 -0
  120. package/dist/index.d.ts +52 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +61 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/instruments/anthropic.d.ts +26 -0
  125. package/dist/instruments/anthropic.d.ts.map +1 -0
  126. package/dist/instruments/anthropic.js +242 -0
  127. package/dist/instruments/anthropic.js.map +1 -0
  128. package/dist/instruments/bedrock.d.ts +29 -0
  129. package/dist/instruments/bedrock.d.ts.map +1 -0
  130. package/dist/instruments/bedrock.js +215 -0
  131. package/dist/instruments/bedrock.js.map +1 -0
  132. package/dist/instruments/cohere.d.ts +29 -0
  133. package/dist/instruments/cohere.d.ts.map +1 -0
  134. package/dist/instruments/cohere.js +237 -0
  135. package/dist/instruments/cohere.js.map +1 -0
  136. package/dist/instruments/gemini.d.ts +30 -0
  137. package/dist/instruments/gemini.d.ts.map +1 -0
  138. package/dist/instruments/gemini.js +247 -0
  139. package/dist/instruments/gemini.js.map +1 -0
  140. package/dist/instruments/index.d.ts +35 -0
  141. package/dist/instruments/index.d.ts.map +1 -0
  142. package/dist/instruments/index.js +54 -0
  143. package/dist/instruments/index.js.map +1 -0
  144. package/dist/instruments/mcp.d.ts +24 -0
  145. package/dist/instruments/mcp.d.ts.map +1 -0
  146. package/dist/instruments/mcp.js +459 -0
  147. package/dist/instruments/mcp.js.map +1 -0
  148. package/dist/instruments/openai.d.ts +26 -0
  149. package/dist/instruments/openai.d.ts.map +1 -0
  150. package/dist/instruments/openai.js +221 -0
  151. package/dist/instruments/openai.js.map +1 -0
  152. package/dist/instruments/vercel-ai.d.ts +28 -0
  153. package/dist/instruments/vercel-ai.d.ts.map +1 -0
  154. package/dist/instruments/vercel-ai.js +192 -0
  155. package/dist/instruments/vercel-ai.js.map +1 -0
  156. package/dist/integrations/langchain.d.ts +65 -0
  157. package/dist/integrations/langchain.d.ts.map +1 -0
  158. package/dist/integrations/langchain.js +165 -0
  159. package/dist/integrations/langchain.js.map +1 -0
  160. package/dist/middleware/express.d.ts +55 -0
  161. package/dist/middleware/express.d.ts.map +1 -0
  162. package/dist/middleware/express.js +101 -0
  163. package/dist/middleware/express.js.map +1 -0
  164. package/dist/middleware/index.d.ts +6 -0
  165. package/dist/middleware/index.d.ts.map +1 -0
  166. package/dist/middleware/index.js +5 -0
  167. package/dist/middleware/index.js.map +1 -0
  168. package/dist/pricing/compute-pricing.d.ts +57 -0
  169. package/dist/pricing/compute-pricing.d.ts.map +1 -0
  170. package/dist/pricing/compute-pricing.js +627 -0
  171. package/dist/pricing/compute-pricing.js.map +1 -0
  172. package/dist/pricing/cost_map.json +37665 -0
  173. package/dist/pricing/egress-pricing.d.ts +55 -0
  174. package/dist/pricing/egress-pricing.d.ts.map +1 -0
  175. package/dist/pricing/egress-pricing.js +226 -0
  176. package/dist/pricing/egress-pricing.js.map +1 -0
  177. package/dist/pricing/engine.d.ts +24 -0
  178. package/dist/pricing/engine.d.ts.map +1 -0
  179. package/dist/pricing/engine.js +148 -0
  180. package/dist/pricing/engine.js.map +1 -0
  181. package/dist/pricing/gpu-pricing.d.ts +63 -0
  182. package/dist/pricing/gpu-pricing.d.ts.map +1 -0
  183. package/dist/pricing/gpu-pricing.js +484 -0
  184. package/dist/pricing/gpu-pricing.js.map +1 -0
  185. package/dist/pricing/rates.d.ts +17 -0
  186. package/dist/pricing/rates.d.ts.map +1 -0
  187. package/dist/pricing/rates.js +102 -0
  188. package/dist/pricing/rates.js.map +1 -0
  189. package/dist/pricing/service-catalog.d.ts +87 -0
  190. package/dist/pricing/service-catalog.d.ts.map +1 -0
  191. package/dist/pricing/service-catalog.js +406 -0
  192. package/dist/pricing/service-catalog.js.map +1 -0
  193. package/dist/schema/dexcost-event.v1.json +111 -0
  194. package/dist/schema/dexcost-task.v1.json +160 -0
  195. package/dist/schema/validate.d.ts +15 -0
  196. package/dist/schema/validate.d.ts.map +1 -0
  197. package/dist/schema/validate.js +87 -0
  198. package/dist/schema/validate.js.map +1 -0
  199. package/dist/security/redaction.d.ts +55 -0
  200. package/dist/security/redaction.d.ts.map +1 -0
  201. package/dist/security/redaction.js +144 -0
  202. package/dist/security/redaction.js.map +1 -0
  203. package/dist/transport/buffer.d.ts +117 -0
  204. package/dist/transport/buffer.d.ts.map +1 -0
  205. package/dist/transport/buffer.js +759 -0
  206. package/dist/transport/buffer.js.map +1 -0
  207. package/dist/transport/pusher.d.ts +89 -0
  208. package/dist/transport/pusher.d.ts.map +1 -0
  209. package/dist/transport/pusher.js +323 -0
  210. package/dist/transport/pusher.js.map +1 -0
  211. package/package.json +93 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dexwox Innovations and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # @dexcost/sdk
2
+
3
+ **Agent Unit Economics SDK for Node.js** — track LLM costs, non-LLM service fees, and retry waste attributed to customers, projects, and workflows.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @dexcost/sdk
9
+ ```
10
+
11
+ With LLM provider SDKs (peer dependencies):
12
+
13
+ ```bash
14
+ npm install @dexcost/sdk openai @anthropic-ai/sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { init, track, close } from '@dexcost/sdk';
21
+
22
+ init({ apiKey: 'dx_live_...' }); // or set DEXCOST_API_KEY env var
23
+
24
+ await track({ taskType: 'summarise', customerId: 'acme' }, async (task) => {
25
+ // LLM calls are auto-captured — just use OpenAI/Anthropic normally
26
+ const response = await openai.chat.completions.create({
27
+ model: 'gpt-4o',
28
+ messages: [{ role: 'user', content: 'Summarise this document' }],
29
+ });
30
+
31
+ // Record non-LLM costs manually
32
+ task.recordCost('pdf_parser', 0.002);
33
+ });
34
+
35
+ await close();
36
+ ```
37
+
38
+ ## Auto-Instrumentation
39
+
40
+ dexcost auto-instruments **6 LLM providers** and the **global fetch API**.
41
+
42
+ ### LLM Providers
43
+
44
+ | Provider | Package | Auto-Patched |
45
+ |----------|---------|-------------|
46
+ | OpenAI | `openai` | `chat.completions.create` |
47
+ | Anthropic | `@anthropic-ai/sdk` | `messages.create` |
48
+ | Vercel AI | `ai` | Vercel AI SDK functions |
49
+ | Google Gemini | `@google/generative-ai` | `generateContent` |
50
+ | AWS Bedrock | `@aws-sdk/client-bedrock-runtime` | `invokeModel` |
51
+ | Cohere | `cohere-ai` | `chat` / `generate` |
52
+
53
+ LLM provider packages are **peer dependencies** — install only the ones you use. dexcost detects them at runtime and patches automatically.
54
+
55
+ ### HTTP (Non-LLM Cost Capture)
56
+
57
+ dexcost patches `globalThis.fetch` to capture HTTP calls to domains in the [163-service catalog](src/data/service_prices.json) — Pinecone, Twilio, SendGrid, Stripe, Firecrawl, Exa, and more. Costs are extracted from response headers/body and recorded as `external_cost` events.
58
+
59
+ ### Controlling Instrumentation
60
+
61
+ ```typescript
62
+ // Instrument only specific providers
63
+ init({ autoInstrument: ['openai', 'anthropic'] });
64
+
65
+ // Disable all auto-instrumentation
66
+ init({ autoInstrument: [] });
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ ### `init()` Options
72
+
73
+ | Option | Type | Default | Description |
74
+ |--------|------|---------|-------------|
75
+ | `apiKey` | `string` | `DEXCOST_API_KEY` env | API key for cloud push |
76
+ | `autoInstrument` | `string[]` | All 6 providers | Which LLM SDKs to patch |
77
+ | `batchSize` | `number` | `100` | Events per sync batch |
78
+ | `flushIntervalMs` | `number` | `30000` | Milliseconds between sync pushes |
79
+ | `redactFields` | `string[]` | `undefined` | Field names to redact from event details |
80
+ | `hashCustomerId` | `boolean` | `false` | SHA-256 hash customer_id before storage |
81
+ | `environment` | `string` | `undefined` | Set to `"development"` for dev console mode |
82
+ | `dbPath` | `string` | `~/.dexcost/buffer.db` | Path to local SQLite buffer |
83
+ | `enableRetryHeuristics` | `boolean` | `false` | Auto-detect retries via pattern matching |
84
+
85
+ ### Environment Variables
86
+
87
+ | Variable | Description |
88
+ |----------|-------------|
89
+ | `DEXCOST_API_KEY` | API key (if not passed to `init()`) |
90
+ | `DEXCOST_ENDPOINT` | Control Layer URL (default: `https://api.dexcost.io`) |
91
+ | `DEXCOST_ENV` | Set to `development` for dev console output |
92
+
93
+ ## API
94
+
95
+ ### Singleton Pattern
96
+
97
+ dexcost uses a singleton. Call `init()` once at app startup:
98
+
99
+ ```typescript
100
+ import { init, getTracker, track, flush, close } from '@dexcost/sdk';
101
+
102
+ init({ apiKey: 'dx_live_...' });
103
+
104
+ // Use the global track() anywhere
105
+ await track({ taskType: 'chat', customerId: 'acme' }, async (task) => {
106
+ task.recordLlmCall('openai', 'gpt-4o', 800, 150);
107
+ });
108
+
109
+ // Or get the tracker instance
110
+ const tracker = getTracker();
111
+ ```
112
+
113
+ ### TrackedTask Methods
114
+
115
+ ```typescript
116
+ await track({ taskType: '...' }, async (task) => {
117
+ // Record LLM call manually (usually auto-captured)
118
+ task.recordLlmCall('openai', 'gpt-4o', 800, 150);
119
+
120
+ // Record non-LLM cost
121
+ task.recordCost('pinecone', 0.001);
122
+
123
+ // Record usage (cost computed from registered rates)
124
+ task.recordUsage('s3_storage', 1024);
125
+
126
+ // Mark a retry
127
+ task.markRetry('rate_limit', 0.005);
128
+
129
+ // Link to external trace
130
+ task.linkTrace('datadog', 'trace-abc123');
131
+
132
+ // End with status (auto-detected from exceptions)
133
+ task.end('success');
134
+ });
135
+ ```
136
+
137
+ ### Customer Attribution
138
+
139
+ ```typescript
140
+ import { setContext, track } from '@dexcost/sdk';
141
+
142
+ setContext({ customerId: 'acme-corp', projectId: 'proj-alpha' });
143
+
144
+ // All tasks inherit the context
145
+ await track({ taskType: 'resolve_ticket' }, async (task) => {
146
+ // task.task.customerId === 'acme-corp'
147
+ });
148
+ ```
149
+
150
+ ### Context Propagation
151
+
152
+ dexcost uses `AsyncLocalStorage` — task context propagates across `await`, `Promise.all`, `setTimeout`, and any async operation without manual threading.
153
+
154
+ ```typescript
155
+ import { getCurrentTask } from '@dexcost/sdk';
156
+
157
+ // Inside any async function within a tracked task:
158
+ const task = getCurrentTask(); // Returns the active Task or undefined
159
+ ```
160
+
161
+ ### Nested Tasks
162
+
163
+ ```typescript
164
+ await track({ taskType: 'pipeline' }, async (parent) => {
165
+ await track({ taskType: 'step_1' }, async (child) => {
166
+ // child.task.parentTaskId === parent.task.taskId (auto-linked)
167
+ });
168
+ });
169
+ ```
170
+
171
+ ## Dev Mode
172
+
173
+ Set `DEXCOST_ENV=development` or pass `environment: "development"` to `init()`. In dev mode:
174
+ - Cost events are printed to the console
175
+ - No data is pushed to the cloud
176
+
177
+ ## Express Middleware
178
+
179
+ ```typescript
180
+ import { createExpressMiddleware } from '@dexcost/sdk';
181
+
182
+ app.use(createExpressMiddleware({
183
+ taskType: 'api_request',
184
+ extractCustomerId: (req) => req.headers['x-customer-id'],
185
+ }));
186
+ ```
187
+
188
+ ## Runtime Dependencies
189
+
190
+ - `better-sqlite3` — local event buffer
191
+ - `ajv` — JSON Schema validation
192
+ - `js-yaml` — rate file parsing
193
+
194
+ ## Development
195
+
196
+ ```bash
197
+ npm install
198
+ npm test
199
+ npm run build
200
+ npm run lint
201
+ ```
202
+
203
+ ## Privacy
204
+
205
+ When you connect to the Dexcost Control Layer, the SDK transmits usage data
206
+ subject to our [Privacy Policy](https://dexcost.io/privacy).
207
+
208
+ ## License
209
+
210
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Helpers for the HTTP network adapter: destination classification and
3
+ * byte measurement. Pure functions — no SDK state, no I/O beyond parsing.
4
+ *
5
+ * Mirrors python/src/dexcost/adapters/_netbytes.py.
6
+ */
7
+ /**
8
+ * Return whether *host* is internal traffic.
9
+ *
10
+ * - `true` — host is an RFC1918 / loopback / link-local IPv4 literal, an
11
+ * IPv6 ULA (`fc00::/7`), IPv6 link-local (`fe80::/10`), or
12
+ * IPv6 loopback (`::1`).
13
+ * - `false` — host parses as an IP literal but is not in the above ranges.
14
+ * - `null` — host is a name (not an IP literal); the SDK does not perform
15
+ * an extra DNS lookup to resolve it.
16
+ *
17
+ * Note: CGNAT shared address space (100.64.0.0/10, RFC 6598) is classified
18
+ * `false` — Python's `ipaddress.is_private` does not include it, and we
19
+ * mirror that behaviour for cross-SDK parity.
20
+ */
21
+ export declare function classifyDestination(host: string): boolean | null;
22
+ /**
23
+ * Approximate the on-the-wire byte size of one HTTP message.
24
+ *
25
+ * `request line + header block + body`. Used for both directions: pass the
26
+ * request method/url/headers for bytes-out, or `"" / "" / response headers`
27
+ * for bytes-in. `bodyLen` is the known body length in bytes; negative values
28
+ * are clamped to zero.
29
+ */
30
+ export declare function measureBytesFromHeaders(method: string, url: string, headers: Record<string, string>, bodyLen: number): number;
31
+ //# sourceMappingURL=_netbytes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_netbytes.d.ts","sourceRoot":"","sources":["../../src/adapters/_netbytes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA+BhE;AAeD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,EAAE,MAAM,GACd,MAAM,CAKR"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Helpers for the HTTP network adapter: destination classification and
3
+ * byte measurement. Pure functions — no SDK state, no I/O beyond parsing.
4
+ *
5
+ * Mirrors python/src/dexcost/adapters/_netbytes.py.
6
+ */
7
+ // ---------------------------------------------------------------------------
8
+ // IP literal parsing (open-coded, no `ipaddr.js` dependency — recommended in
9
+ // the network-capture plan to avoid adding a dep just for classification).
10
+ // ---------------------------------------------------------------------------
11
+ const IPV4_RE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
12
+ function parseIPv4(host) {
13
+ const m = IPV4_RE.exec(host);
14
+ if (!m)
15
+ return null;
16
+ const octets = [];
17
+ for (let i = 1; i <= 4; i++) {
18
+ const n = Number(m[i]);
19
+ if (!Number.isInteger(n) || n < 0 || n > 255)
20
+ return null;
21
+ // Reject leading zeros beyond a single "0" (e.g. "010" is ambiguous on the
22
+ // wire). ipaddress.ip_address in Python is similarly strict in 3.10+.
23
+ if (m[i].length > 1 && m[i].startsWith("0"))
24
+ return null;
25
+ octets.push(n);
26
+ }
27
+ return octets;
28
+ }
29
+ /**
30
+ * Loose-grammar IPv6 detector: covers the common literal shapes including
31
+ * `::`-compressed forms. We do not need full numeric reconstruction because
32
+ * the classifier only inspects the textual prefix (`fc00::/7`, `fe80::/10`,
33
+ * `::1`).
34
+ */
35
+ function isIPv6Literal(host) {
36
+ if (host.length === 0)
37
+ return false;
38
+ // Strip optional zone-id (`fe80::1%eth0`) before validation.
39
+ const bare = host.split("%", 1)[0];
40
+ if (bare.indexOf(":") === -1)
41
+ return false;
42
+ // Disallow obvious non-hex characters.
43
+ if (!/^[0-9a-fA-F:.]+$/.test(bare))
44
+ return false;
45
+ // At most one "::".
46
+ const dblIdx = bare.indexOf("::");
47
+ if (dblIdx !== -1 && bare.indexOf("::", dblIdx + 1) !== -1)
48
+ return false;
49
+ // Split and sanity-check each group.
50
+ const parts = bare.split(":");
51
+ if (parts.length < 3 || parts.length > 8)
52
+ return false;
53
+ for (const p of parts) {
54
+ if (p.length === 0)
55
+ continue; // produced by "::"
56
+ if (p.length > 4) {
57
+ // Could be an embedded IPv4 ("::ffff:1.2.3.4") — accept if last segment.
58
+ if (p === parts[parts.length - 1] && parseIPv4(p) !== null)
59
+ continue;
60
+ return false;
61
+ }
62
+ }
63
+ return true;
64
+ }
65
+ function isPrivateIPv4(octets) {
66
+ const [a, b] = octets;
67
+ // 10.0.0.0/8
68
+ if (a === 10)
69
+ return true;
70
+ // 172.16.0.0/12
71
+ if (a === 172 && b >= 16 && b <= 31)
72
+ return true;
73
+ // 192.168.0.0/16
74
+ if (a === 192 && b === 168)
75
+ return true;
76
+ return false;
77
+ }
78
+ function isLoopbackIPv4(octets) {
79
+ return octets[0] === 127;
80
+ }
81
+ function isLinkLocalIPv4(octets) {
82
+ return octets[0] === 169 && octets[1] === 254;
83
+ }
84
+ /**
85
+ * Return whether *host* is internal traffic.
86
+ *
87
+ * - `true` — host is an RFC1918 / loopback / link-local IPv4 literal, an
88
+ * IPv6 ULA (`fc00::/7`), IPv6 link-local (`fe80::/10`), or
89
+ * IPv6 loopback (`::1`).
90
+ * - `false` — host parses as an IP literal but is not in the above ranges.
91
+ * - `null` — host is a name (not an IP literal); the SDK does not perform
92
+ * an extra DNS lookup to resolve it.
93
+ *
94
+ * Note: CGNAT shared address space (100.64.0.0/10, RFC 6598) is classified
95
+ * `false` — Python's `ipaddress.is_private` does not include it, and we
96
+ * mirror that behaviour for cross-SDK parity.
97
+ */
98
+ export function classifyDestination(host) {
99
+ if (!host)
100
+ return null;
101
+ const v4 = parseIPv4(host);
102
+ if (v4 !== null) {
103
+ return (isPrivateIPv4(v4) || isLoopbackIPv4(v4) || isLinkLocalIPv4(v4));
104
+ }
105
+ if (isIPv6Literal(host)) {
106
+ const lower = host.toLowerCase().split("%", 1)[0];
107
+ // ::1 loopback (also covers "0:0:0:0:0:0:0:1").
108
+ if (lower === "::1")
109
+ return true;
110
+ if (/^0:0:0:0:0:0:0:0*1$/.test(lower))
111
+ return true;
112
+ // fc00::/7 — ULA. First byte is 0xfc or 0xfd; in text form that means
113
+ // the address starts with "fc" or "fd" followed by a hex digit (or "::").
114
+ if (/^f[cd][0-9a-f]{0,2}:/.test(lower) || /^f[cd]:/.test(lower)) {
115
+ return true;
116
+ }
117
+ // fe80::/10 — link-local. First 10 bits are 1111111010, so the leading
118
+ // 16 bits are between 0xfe80 and 0xfebf.
119
+ const firstGroup = lower.split(":")[0];
120
+ if (firstGroup.length > 0 && firstGroup.length <= 4) {
121
+ const v = parseInt(firstGroup, 16);
122
+ if (!Number.isNaN(v) && v >= 0xfe80 && v <= 0xfebf)
123
+ return true;
124
+ }
125
+ return false;
126
+ }
127
+ return null;
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // Byte-size measurement
131
+ // ---------------------------------------------------------------------------
132
+ function _headersByteLen(headers) {
133
+ let total = 0;
134
+ for (const [key, value] of Object.entries(headers)) {
135
+ // ": " + CRLF == 4 extra bytes per header line.
136
+ total += String(key).length + String(value).length + 4;
137
+ }
138
+ return total + 2; // trailing CRLF that ends the header block
139
+ }
140
+ /**
141
+ * Approximate the on-the-wire byte size of one HTTP message.
142
+ *
143
+ * `request line + header block + body`. Used for both directions: pass the
144
+ * request method/url/headers for bytes-out, or `"" / "" / response headers`
145
+ * for bytes-in. `bodyLen` is the known body length in bytes; negative values
146
+ * are clamped to zero.
147
+ */
148
+ export function measureBytesFromHeaders(method, url, headers, bodyLen) {
149
+ // method + url + " HTTP/1.1\r\n" → +12 trailing bytes
150
+ const requestLine = String(method).length + String(url).length + 12;
151
+ const body = Math.max(0, Math.trunc(Number(bodyLen) || 0));
152
+ return requestLine + _headersByteLen(headers) + body;
153
+ }
154
+ //# sourceMappingURL=_netbytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_netbytes.js","sourceRoot":"","sources":["../../src/adapters/_netbytes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * AWS Lambda cost adapter — compute cost from duration, memory, and region.
3
+ *
4
+ * `lambdaCost` is a pure function that returns the cost (in USD) and a
5
+ * breakdown for a single Lambda invocation, using bundled pricing data
6
+ * (no network I/O). Mirrors the Python SDK's `adapters/aws_lambda.py`.
7
+ */
8
+ /** Breakdown of a Lambda invocation's cost. */
9
+ export interface LambdaCostDetails {
10
+ region: string;
11
+ durationMs: number;
12
+ memoryMb: number;
13
+ gbSeconds: number;
14
+ durationCostUsd: number;
15
+ requestCostUsd: number;
16
+ ratePerGbSecond: number;
17
+ }
18
+ /** Result of {@link lambdaCost}. */
19
+ export interface LambdaCostResult {
20
+ /** Total invocation cost in USD. */
21
+ costUsd: number;
22
+ /** Cost breakdown. */
23
+ details: LambdaCostDetails;
24
+ }
25
+ /**
26
+ * Return a sorted list of AWS region codes with bundled pricing data.
27
+ */
28
+ export declare function getSupportedRegions(): string[];
29
+ /**
30
+ * Calculate the cost of a single AWS Lambda invocation.
31
+ *
32
+ * Pure function — no I/O, no side effects. Uses the bundled
33
+ * `aws_lambda_pricing.json` for rates.
34
+ *
35
+ * @param durationMs - Execution duration in milliseconds (>= 0).
36
+ * @param memoryMb - Allocated memory in MB (> 0).
37
+ * @param region - AWS region code (e.g. `"us-east-1"`).
38
+ * @throws Error when `region` is unknown, `durationMs` < 0, or `memoryMb` <= 0.
39
+ */
40
+ export declare function lambdaCost(durationMs: number, memoryMb: number, region: string): LambdaCostResult;
41
+ //# sourceMappingURL=aws-lambda.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-lambda.d.ts","sourceRoot":"","sources":["../../src/adapters/aws-lambda.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,gBAAgB,CAuClB"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * AWS Lambda cost adapter — compute cost from duration, memory, and region.
3
+ *
4
+ * `lambdaCost` is a pure function that returns the cost (in USD) and a
5
+ * breakdown for a single Lambda invocation, using bundled pricing data
6
+ * (no network I/O). Mirrors the Python SDK's `adapters/aws_lambda.py`.
7
+ */
8
+ // Sprint 3 Theme E / §4.2.3 — Node 18 compat: runtime JSON load.
9
+ import { readFileSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
11
+ import { dirname, join } from "node:path";
12
+ const _thisDir = dirname(fileURLToPath(import.meta.url));
13
+ const pricingData = JSON.parse(readFileSync(join(_thisDir, "data", "aws_lambda_pricing.json"), "utf-8"));
14
+ const _pricing = pricingData;
15
+ /**
16
+ * Return a sorted list of AWS region codes with bundled pricing data.
17
+ */
18
+ export function getSupportedRegions() {
19
+ return Object.keys(_pricing.regions).sort();
20
+ }
21
+ /**
22
+ * Calculate the cost of a single AWS Lambda invocation.
23
+ *
24
+ * Pure function — no I/O, no side effects. Uses the bundled
25
+ * `aws_lambda_pricing.json` for rates.
26
+ *
27
+ * @param durationMs - Execution duration in milliseconds (>= 0).
28
+ * @param memoryMb - Allocated memory in MB (> 0).
29
+ * @param region - AWS region code (e.g. `"us-east-1"`).
30
+ * @throws Error when `region` is unknown, `durationMs` < 0, or `memoryMb` <= 0.
31
+ */
32
+ export function lambdaCost(durationMs, memoryMb, region) {
33
+ if (durationMs < 0) {
34
+ throw new Error(`durationMs must be >= 0, got ${durationMs}`);
35
+ }
36
+ if (memoryMb <= 0) {
37
+ throw new Error(`memoryMb must be > 0, got ${memoryMb}`);
38
+ }
39
+ const regionPricing = _pricing.regions[region];
40
+ if (regionPricing === undefined) {
41
+ const supported = getSupportedRegions().join(", ");
42
+ throw new Error(`Unknown AWS region '${region}'. Supported regions: ${supported}`);
43
+ }
44
+ // GB-seconds = duration (s) * memory (GB)
45
+ const durationSeconds = durationMs / 1000;
46
+ const memoryGb = memoryMb / 1024;
47
+ const gbSeconds = durationSeconds * memoryGb;
48
+ const ratePerGbSecond = Number(regionPricing.duration_per_gb_second);
49
+ const requestCharge = Number(regionPricing.request_per_invocation);
50
+ const durationCost = gbSeconds * ratePerGbSecond;
51
+ const totalCost = durationCost + requestCharge;
52
+ return {
53
+ costUsd: totalCost,
54
+ details: {
55
+ region,
56
+ durationMs,
57
+ memoryMb,
58
+ gbSeconds,
59
+ durationCostUsd: durationCost,
60
+ requestCostUsd: requestCharge,
61
+ ratePerGbSecond,
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=aws-lambda.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-lambda.js","sourceRoot":"","sources":["../../src/adapters/aws-lambda.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,iEAAiE;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACzD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,yBAAyB,CAAC,EAAE,OAAO,CAAC,CACzE,CAAC;AAWF,MAAM,QAAQ,GAAG,WAA2C,CAAC;AAqB7D;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CACxB,UAAkB,EAClB,QAAgB,EAChB,MAAc;IAEd,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,mBAAmB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,uBAAuB,MAAM,yBAAyB,SAAS,EAAE,CAClE,CAAC;IACJ,CAAC;IAED,0CAA0C;IAC1C,MAAM,eAAe,GAAG,UAAU,GAAG,IAAI,CAAC;IAC1C,MAAM,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;IACjC,MAAM,SAAS,GAAG,eAAe,GAAG,QAAQ,CAAC;IAE7C,MAAM,eAAe,GAAG,MAAM,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAEnE,MAAM,YAAY,GAAG,SAAS,GAAG,eAAe,CAAC;IACjD,MAAM,SAAS,GAAG,YAAY,GAAG,aAAa,CAAC;IAE/C,OAAO;QACL,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE;YACP,MAAM;YACN,UAAU;YACV,QAAQ;YACR,SAAS;YACT,eAAe,EAAE,YAAY;YAC7B,cAAc,EAAE,aAAa;YAC7B,eAAe;SAChB;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Browser cost adapter — automatic cost tracking for Playwright sessions.
3
+ *
4
+ * `trackBrowser` wraps a block of Playwright work, measures wall-clock
5
+ * time, and records a `compute_cost` event proportional to session
6
+ * duration. Mirrors the Python SDK's `adapters/browser.py`.
7
+ *
8
+ * `playwright` is an optional peer dependency — this adapter is duck-typed
9
+ * and never imports it, so it works with any object exposing a `.url`.
10
+ *
11
+ * Usage:
12
+ *
13
+ * import { trackBrowser } from "dexcost";
14
+ *
15
+ * await tracker.track({ taskType: "scrape" }, async () => {
16
+ * await trackBrowser(page, async () => {
17
+ * await page.goto("https://example.com");
18
+ * }, { ratePerMinute: 0.01 });
19
+ * });
20
+ */
21
+ import { type CostEvent } from "../core/models.js";
22
+ import type { EventBuffer } from "../transport/buffer.js";
23
+ /**
24
+ * Wire the browser adapter to a storage buffer. The CostTracker calls this on
25
+ * construction so `trackBrowser()` cost events reach SQLite and the sync
26
+ * pusher. Pass `null` to detach (events then stay in-memory only).
27
+ */
28
+ export declare function setBrowserBuffer(buffer: EventBuffer | null): void;
29
+ /** Return all events recorded by the browser adapter since the last clear. */
30
+ export declare function getBrowserEvents(): CostEvent[];
31
+ /** Clear the browser adapter's recorded events list. */
32
+ export declare function clearBrowserEvents(): void;
33
+ /** Options for {@link trackBrowser}. */
34
+ export interface TrackBrowserOptions {
35
+ /** Cost per minute of browser usage in USD. Defaults to `0.01`. */
36
+ ratePerMinute?: number;
37
+ }
38
+ /**
39
+ * Run `fn` while measuring browser session wall-clock time, then record a
40
+ * `compute_cost` event with `costUsd = elapsedMinutes * ratePerMinute`.
41
+ *
42
+ * The event is only recorded when there is an active task in the context
43
+ * (via `getCurrentTask()`). When no task is active, the timing wrapper is
44
+ * a silent pass-through. The cost is always recorded even if `fn` throws.
45
+ *
46
+ * @param page - A Playwright `Page` (or any object with a `.url`). Not
47
+ * type-checked, to avoid requiring `playwright` as a hard dependency.
48
+ * @param fn - The browser work to run and time.
49
+ * @param options - Optional `ratePerMinute` override.
50
+ */
51
+ export declare function trackBrowser<T>(page: any, fn: () => Promise<T>, options?: TrackBrowserOptions): Promise<T>;
52
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/adapters/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAwB1D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAEjE;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,IAAI,SAAS,EAAE,CAE9C;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,GAAG,EACT,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,CAAC,CAAC,CASZ"}