@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
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Thin wrapper classes around the OpenAI and Anthropic clients that
3
+ * auto-record LLM cost events.
4
+ *
5
+ * Alternative to auto-instrumentation (no monkey-patching). Matching
6
+ * the Python SDK's clients.py.
7
+ *
8
+ * Usage:
9
+ *
10
+ * import { TrackedOpenAI } from "dexcost/clients";
11
+ *
12
+ * const client = new TrackedOpenAI({ tracker });
13
+ * // Inside a tracked task, events are auto-recorded:
14
+ * const response = await client.chat.completions.create({ model: "gpt-4o", messages: [] });
15
+ */
16
+ import { randomUUID } from "node:crypto";
17
+ import { getCurrentTask } from "./core/context.js";
18
+ import { createCostEvent } from "./core/models.js";
19
+ import { createAutoTask } from "./core/auto-task.js";
20
+ import { PricingEngine } from "./pricing/engine.js";
21
+ // ---------------------------------------------------------------------------
22
+ // TrackedOpenAI
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Wraps `openai.OpenAI` and auto-records llm_call events on every
26
+ * `chat.completions.create` call when running inside a tracked task context.
27
+ */
28
+ export class TrackedOpenAI {
29
+ _client;
30
+ _pricing;
31
+ _buffer;
32
+ constructor(opts) {
33
+ if (opts?.client !== undefined) {
34
+ this._client = opts.client;
35
+ }
36
+ else {
37
+ // Lazy require — openai is an optional peer dependency
38
+ let OpenAIClass;
39
+ try {
40
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
41
+ const openaiModule = require("openai");
42
+ OpenAIClass = (openaiModule.default ?? openaiModule.OpenAI);
43
+ }
44
+ catch {
45
+ throw new Error("The 'openai' package is required for TrackedOpenAI. Install it with: npm install openai");
46
+ }
47
+ this._client = new OpenAIClass();
48
+ }
49
+ if (opts?.tracker !== undefined) {
50
+ this._pricing = opts.tracker.pricing;
51
+ this._buffer = opts.tracker.buffer;
52
+ }
53
+ else if (opts?.pricing !== undefined) {
54
+ this._pricing = opts.pricing;
55
+ this._buffer = null;
56
+ }
57
+ else {
58
+ this._pricing = new PricingEngine();
59
+ this._buffer = null;
60
+ }
61
+ }
62
+ get chat() {
63
+ const pricing = this._pricing;
64
+ const buffer = this._buffer;
65
+ const client = this._client;
66
+ return {
67
+ completions: {
68
+ create: async (...args) => {
69
+ const response = await client.chat.completions.create(...args);
70
+ recordOpenAIEvent(response, args[0], pricing, buffer);
71
+ return response;
72
+ },
73
+ },
74
+ };
75
+ }
76
+ }
77
+ function recordOpenAIEvent(response, body, pricing, buffer) {
78
+ if (!buffer)
79
+ return;
80
+ let task = getCurrentTask();
81
+ if (!task) {
82
+ // Auto-create a task so LLM costs are never silently lost
83
+ // (mirrors Python create_auto_task).
84
+ task = createAutoTask("openai.chat");
85
+ buffer.upsertTask(task);
86
+ }
87
+ const resp = response;
88
+ const bodyObj = (body ?? {});
89
+ const model = resp["model"] ??
90
+ bodyObj["model"] ??
91
+ "unknown";
92
+ const usage = resp["usage"];
93
+ const hasUsage = usage != null;
94
+ const inputTokens = usage?.["prompt_tokens"] ?? 0;
95
+ const outputTokens = usage?.["completion_tokens"] ?? 0;
96
+ const promptDetails = usage?.["prompt_tokens_details"];
97
+ const cachedTokens = promptDetails?.["cached_tokens"] ?? 0;
98
+ let costUsd = 0;
99
+ let costConfidence = "estimated";
100
+ let pricingSource = "unknown";
101
+ if (hasUsage) {
102
+ const result = pricing.getCost(model, inputTokens, outputTokens, cachedTokens);
103
+ costUsd = result.costUsd;
104
+ costConfidence = result.costConfidence;
105
+ pricingSource = result.pricingSource;
106
+ }
107
+ _addEventAndUpdateTask(task, buffer, {
108
+ provider: "openai",
109
+ model,
110
+ inputTokens,
111
+ outputTokens,
112
+ cachedTokens,
113
+ costUsd,
114
+ costConfidence,
115
+ pricingSource,
116
+ });
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // TrackedAnthropic
120
+ // ---------------------------------------------------------------------------
121
+ /**
122
+ * Wraps `@anthropic-ai/sdk Anthropic` and auto-records llm_call events on
123
+ * every `messages.create` call when running inside a tracked task context.
124
+ */
125
+ export class TrackedAnthropic {
126
+ _client;
127
+ _pricing;
128
+ _buffer;
129
+ constructor(opts) {
130
+ if (opts?.client !== undefined) {
131
+ this._client = opts.client;
132
+ }
133
+ else {
134
+ // Lazy require — @anthropic-ai/sdk is an optional peer dependency
135
+ let AnthropicClass;
136
+ try {
137
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
138
+ const anthropicModule = require("@anthropic-ai/sdk");
139
+ AnthropicClass = (anthropicModule.default ?? anthropicModule.Anthropic);
140
+ }
141
+ catch {
142
+ throw new Error("The '@anthropic-ai/sdk' package is required for TrackedAnthropic. Install it with: npm install @anthropic-ai/sdk");
143
+ }
144
+ this._client = new AnthropicClass();
145
+ }
146
+ if (opts?.tracker !== undefined) {
147
+ this._pricing = opts.tracker.pricing;
148
+ this._buffer = opts.tracker.buffer;
149
+ }
150
+ else if (opts?.pricing !== undefined) {
151
+ this._pricing = opts.pricing;
152
+ this._buffer = null;
153
+ }
154
+ else {
155
+ this._pricing = new PricingEngine();
156
+ this._buffer = null;
157
+ }
158
+ }
159
+ get messages() {
160
+ const pricing = this._pricing;
161
+ const buffer = this._buffer;
162
+ const client = this._client;
163
+ return {
164
+ create: async (...args) => {
165
+ const response = await client.messages.create(...args);
166
+ recordAnthropicEvent(response, args[0], pricing, buffer);
167
+ return response;
168
+ },
169
+ };
170
+ }
171
+ }
172
+ function recordAnthropicEvent(response, body, pricing, buffer) {
173
+ if (!buffer)
174
+ return;
175
+ let task = getCurrentTask();
176
+ if (!task) {
177
+ // Auto-create a task so LLM costs are never silently lost
178
+ // (mirrors Python create_auto_task).
179
+ task = createAutoTask("anthropic.messages");
180
+ buffer.upsertTask(task);
181
+ }
182
+ const resp = response;
183
+ const bodyObj = (body ?? {});
184
+ const model = resp["model"] ??
185
+ bodyObj["model"] ??
186
+ "unknown";
187
+ const usage = resp["usage"];
188
+ const hasUsage = usage != null;
189
+ const inputTokens = usage?.["input_tokens"] ?? 0;
190
+ const outputTokens = usage?.["output_tokens"] ?? 0;
191
+ const cacheCreationTokens = usage?.["cache_creation_input_tokens"] ?? 0;
192
+ const cacheReadTokens = usage?.["cache_read_input_tokens"] ?? 0;
193
+ // cachedTokens tracks cache_read (for aggregation); cache_creation stored in details
194
+ const cachedTokens = cacheReadTokens;
195
+ let costUsd = 0;
196
+ let costConfidence = "estimated";
197
+ let pricingSource = "unknown";
198
+ if (hasUsage) {
199
+ const result = pricing.getCost(model, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens);
200
+ costUsd = result.costUsd;
201
+ costConfidence = result.costConfidence;
202
+ pricingSource = result.pricingSource;
203
+ }
204
+ _addEventAndUpdateTask(task, buffer, {
205
+ provider: "anthropic",
206
+ model,
207
+ inputTokens,
208
+ outputTokens,
209
+ cachedTokens,
210
+ costUsd,
211
+ costConfidence,
212
+ pricingSource,
213
+ details: cacheCreationTokens > 0 ? { cache_creation_input_tokens: cacheCreationTokens } : undefined,
214
+ });
215
+ }
216
+ function _addEventAndUpdateTask(task, buffer, spec) {
217
+ const event = createCostEvent({
218
+ eventId: randomUUID(),
219
+ taskId: task.taskId,
220
+ eventType: "llm_call",
221
+ costUsd: spec.costUsd,
222
+ costConfidence: spec.costConfidence,
223
+ pricingSource: spec.pricingSource,
224
+ provider: spec.provider,
225
+ model: spec.model,
226
+ inputTokens: spec.inputTokens,
227
+ outputTokens: spec.outputTokens,
228
+ cachedTokens: spec.cachedTokens,
229
+ isRetry: false,
230
+ details: spec.details ?? {},
231
+ });
232
+ buffer.addEvent(event);
233
+ task.llmCostUsd += spec.costUsd;
234
+ task.totalCostUsd += spec.costUsd;
235
+ task.totalInputTokens += spec.inputTokens;
236
+ task.totalOutputTokens += spec.outputTokens;
237
+ task.totalCachedTokens += spec.cachedTokens;
238
+ buffer.upsertTask(task);
239
+ }
240
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Cloud-environment detection for egress pricing.
3
+ *
4
+ * Mirrors `python/src/dexcost/cloud_detect.py` exactly — env-var names,
5
+ * DMI strings, and IMDS endpoints have all been verified against May-2026
6
+ * docs and MUST NOT be changed in this port.
7
+ *
8
+ * Phase 1a — env-var detection (sub-millisecond, synchronous).
9
+ * Phase 1b — DMI vendor check (~1 ms, Linux-only — silent no-op elsewhere).
10
+ * Phase 2 — background metadata probe (fire-and-forget Promise, ~250 ms
11
+ * budget, never blocks `dexcost.init()`).
12
+ *
13
+ * Notes — research May 2026:
14
+ * - AWS Lambda / Fargate / App Runner set `AWS_REGION` automatically; ECS
15
+ * (Fargate and on-EC2) also sets `ECS_CONTAINER_METADATA_URI_V4`.
16
+ * - Azure Container Apps embeds the region in `CONTAINER_APP_HOSTNAME` and
17
+ * `CONTAINER_APP_ENV_DNS_SUFFIX` as `<host>.<REGION>.azurecontainerapps.io`.
18
+ * - GCP Cloud Run / Cloud Functions Gen2 / App Engine do NOT expose a region
19
+ * env var; region must come from the metadata server (Phase 2).
20
+ * - AWS IMDSv2 has a default HTTP hop-limit of 1, which prevents Docker/Pod
21
+ * containers from reaching the metadata service; the probe fails silent.
22
+ */
23
+ export interface CloudEnv {
24
+ provider: string | null;
25
+ region: string | null;
26
+ source: "env" | "dmi" | "imds" | "none";
27
+ /**
28
+ * IaaS instance type when known — e.g. "c7g.xlarge", "n2-standard-2",
29
+ * "Standard_D2s_v3". Compute pricing reads this at task finalize for
30
+ * EC2 / GCE / Azure VM SKU rate resolution (Decision #3 — extracted in
31
+ * the same Phase 2 background thread as the region probe).
32
+ */
33
+ instanceType?: string | null;
34
+ }
35
+ export declare function getCloudEnv(): CloudEnv;
36
+ /**
37
+ * Test-only — force the resolved CloudEnv to a known value. Used by
38
+ * downstream finalize tests (Phase D Task 10) that need a deterministic
39
+ * egress rate without actually probing IMDS or reading DMI. The
40
+ * `_for_tests` suffix marks this as not-public-API.
41
+ */
42
+ export declare function _setResultForTests(env: CloudEnv): void;
43
+ export declare function _resetCloudDetectForTests(): void;
44
+ export declare function _setReadDmiForTests(fn: (() => Record<string, string>) | null): void;
45
+ /**
46
+ * Strip a GCP metadata-server `projects/.../<X>/<name>` response to a region.
47
+ *
48
+ * Zone form (`projects/123/zones/us-central1-a`) → `us-central1` when
49
+ * `dropZoneLetter=true`.
50
+ * Region form (`projects/123/regions/us-central1`) → `us-central1` when
51
+ * `dropZoneLetter=false`.
52
+ */
53
+ export declare function _gcpPathToRegion(value: string, dropZoneLetter: boolean): string | null;
54
+ export declare function _setFetchForTests(impl: typeof fetch | null): void;
55
+ declare function _probeAws(): Promise<CloudEnv | null>;
56
+ declare function _probeGcp(): Promise<CloudEnv | null>;
57
+ declare function _probeAzure(): Promise<CloudEnv | null>;
58
+ declare function _probeOci(): Promise<CloudEnv | null>;
59
+ declare function _probeDigitalOcean(): Promise<CloudEnv | null>;
60
+ declare function _probeAlibaba(): Promise<CloudEnv | null>;
61
+ /**
62
+ * Provider hint → metadata probe. Mutable so tests can stub individual
63
+ * probes via `monkeypatch.setitem` (Python idiom) → `_PROBES["aws"] = …`.
64
+ */
65
+ export declare const _PROBES: Record<string, () => Promise<CloudEnv | null>>;
66
+ /**
67
+ * Phase 2 fanout providers. When DMI doesn't pre-classify, we race AWS/GCP/
68
+ * Azure in parallel — adding OCI/DO/Alibaba would lengthen worst-case wait
69
+ * and hit the wrong metadata server (DO uses the same 169.254.169.254 IP).
70
+ */
71
+ export declare const _FANOUT_PROBES: readonly ["aws", "gcp", "azure"];
72
+ declare function _runProbe(providerHint: string | null): Promise<CloudEnv>;
73
+ /** Run Phase 1a + 1b synchronously. Used by tests; never calls IMDS. */
74
+ export declare function detectNow(): CloudEnv;
75
+ /**
76
+ * Resolve provider/region without blocking. Idempotent.
77
+ *
78
+ * When `trackNetwork` is false, no probe is launched.
79
+ *
80
+ * Phase 1a + 1b run synchronously; Phase 2 is fire-and-forget so init()
81
+ * returns immediately. Tests can await `_backgroundPromise` via the
82
+ * `_awaitBackgroundForTests` helper.
83
+ */
84
+ export declare function startBackgroundDetection(trackNetwork?: boolean): void;
85
+ /** Test-only helper to await the background Phase 2 probe. */
86
+ export declare function _awaitBackgroundForTests(): Promise<void>;
87
+ export declare const _probes: {
88
+ aws: typeof _probeAws;
89
+ gcp: typeof _probeGcp;
90
+ azure: typeof _probeAzure;
91
+ oci: typeof _probeOci;
92
+ digitalocean: typeof _probeDigitalOcean;
93
+ alibaba: typeof _probeAlibaba;
94
+ };
95
+ export { _runProbe };
96
+ //# sourceMappingURL=cloud-detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-detect.d.ts","sourceRoot":"","sources":["../src/cloud-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAiED,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAEtD;AAGD,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAmID,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAEnF;AA0BD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUtF;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,KAAK,GAAG,IAAI,GAAG,IAAI,CAEjE;AAED,iBAAe,SAAS,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgDnD;AAED,iBAAe,SAAS,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAsDnD;AAED,iBAAe,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAqBrD;AAED,iBAAe,SAAS,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAenD;AAED,iBAAe,kBAAkB,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAW5D;AAED,iBAAe,aAAa,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAWvD;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAOlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,kCAAmC,CAAC;AAE/D,iBAAe,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgDvE;AAMD,wEAAwE;AACxE,wBAAgB,SAAS,IAAI,QAAQ,CAQpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,GAAE,OAAc,GAAG,IAAI,CAsC3E;AAED,8DAA8D;AAC9D,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAI9D;AAID,eAAO,MAAM,OAAO;;;;;;;CAOnB,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}