@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,45 @@
1
+ /**
2
+ * Cgroup v2 file readers (node-only, browser-safe).
3
+ *
4
+ * Fail-silent contract (convention §9): every read returns `null` on missing
5
+ * or malformed input. Non-Linux hosts, cgroup-v1 kernels, browsers, and
6
+ * containers without a cgroup mount all silently return `null` — the caller
7
+ * decides the fallback.
8
+ *
9
+ * Backed file layouts (all under `/sys/fs/cgroup/`):
10
+ *
11
+ * - `cpu.stat` — multi-line; `usage_usec <N>` is the cumulative CPU
12
+ * time consumed (microseconds). Read at task start +
13
+ * end to compute vcpu-seconds for long-running runtimes.
14
+ * - `cpu.max` — single line `<quota|"max"> <period>` (both in
15
+ * microseconds). `quota/period` is the vCPU count
16
+ * enforced on this cgroup; `"max"` means no limit
17
+ * (fall back to `os.cpus().length`).
18
+ * - `memory.peak` — single integer (bytes); the high-water mark since
19
+ * cgroup creation. Available on kernels >= 5.19;
20
+ * absent otherwise.
21
+ * - `memory.max` — single integer (bytes) or `"max"` (unlimited).
22
+ * - `memory.current` — single integer (bytes); the current RSS.
23
+ *
24
+ * Mirrors `python/src/dexcost/cgroup_reader.py`.
25
+ */
26
+ export interface CpuStat {
27
+ /** Cumulative CPU time consumed (microseconds). */
28
+ usageUsec: number;
29
+ }
30
+ export interface CpuMax {
31
+ /** Period quota in microseconds, or `null` when cgroup is unlimited. */
32
+ quotaUs: number | null;
33
+ /** Period length in microseconds. */
34
+ periodUs: number;
35
+ /** Computed `quotaUs / periodUs`; `os.cpus().length` when unlimited. */
36
+ vcpuCount: number;
37
+ }
38
+ /** Test-only — override the cgroup root. Pass `null` to restore default. */
39
+ export declare function _setCgroupRootForTests(p: string | null): void;
40
+ export declare function readCpuStat(): CpuStat | null;
41
+ export declare function readCpuMax(): CpuMax | null;
42
+ export declare function readMemoryPeak(): number | null;
43
+ export declare function readMemoryMax(): number | null;
44
+ export declare function readMemoryCurrent(): number | null;
45
+ //# sourceMappingURL=cgroup-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-reader.d.ts","sourceRoot":"","sources":["../../src/core/cgroup-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,MAAM,WAAW,OAAO;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACrB,wEAAwE;IACxE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7D;AA0BD,wBAAgB,WAAW,IAAI,OAAO,GAAG,IAAI,CAe5C;AAED,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAkB1C;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAG9C;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAG7C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAGjD"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Cgroup v2 file readers (node-only, browser-safe).
3
+ *
4
+ * Fail-silent contract (convention §9): every read returns `null` on missing
5
+ * or malformed input. Non-Linux hosts, cgroup-v1 kernels, browsers, and
6
+ * containers without a cgroup mount all silently return `null` — the caller
7
+ * decides the fallback.
8
+ *
9
+ * Backed file layouts (all under `/sys/fs/cgroup/`):
10
+ *
11
+ * - `cpu.stat` — multi-line; `usage_usec <N>` is the cumulative CPU
12
+ * time consumed (microseconds). Read at task start +
13
+ * end to compute vcpu-seconds for long-running runtimes.
14
+ * - `cpu.max` — single line `<quota|"max"> <period>` (both in
15
+ * microseconds). `quota/period` is the vCPU count
16
+ * enforced on this cgroup; `"max"` means no limit
17
+ * (fall back to `os.cpus().length`).
18
+ * - `memory.peak` — single integer (bytes); the high-water mark since
19
+ * cgroup creation. Available on kernels >= 5.19;
20
+ * absent otherwise.
21
+ * - `memory.max` — single integer (bytes) or `"max"` (unlimited).
22
+ * - `memory.current` — single integer (bytes); the current RSS.
23
+ *
24
+ * Mirrors `python/src/dexcost/cgroup_reader.py`.
25
+ */
26
+ import { readFileSync } from "node:fs";
27
+ import { join } from "node:path";
28
+ import { cpus } from "node:os";
29
+ let _cgroupRoot = "/sys/fs/cgroup";
30
+ /** Test-only — override the cgroup root. Pass `null` to restore default. */
31
+ export function _setCgroupRootForTests(p) {
32
+ _cgroupRoot = p ?? "/sys/fs/cgroup";
33
+ }
34
+ function _isNode() {
35
+ return typeof process !== "undefined" && !!process.versions?.node;
36
+ }
37
+ function _readText(name) {
38
+ if (!_isNode())
39
+ return null;
40
+ try {
41
+ return readFileSync(join(_cgroupRoot, name), "utf-8");
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function _readInt(name) {
48
+ const raw = _readText(name);
49
+ if (raw === null)
50
+ return null;
51
+ const trimmed = raw.trim();
52
+ if (trimmed === "max")
53
+ return null;
54
+ const n = Number(trimmed);
55
+ if (!Number.isFinite(n) || Number.isNaN(n))
56
+ return null;
57
+ if (!/^-?\d+$/.test(trimmed))
58
+ return null;
59
+ return n;
60
+ }
61
+ export function readCpuStat() {
62
+ if (!_isNode())
63
+ return null;
64
+ const raw = _readText("cpu.stat");
65
+ if (raw === null)
66
+ return null;
67
+ for (const line of raw.split("\n")) {
68
+ if (line.startsWith("usage_usec ")) {
69
+ const parts = line.split(/\s+/);
70
+ if (parts.length < 2)
71
+ return null;
72
+ const n = Number(parts[1]);
73
+ if (!Number.isFinite(n) || Number.isNaN(n))
74
+ return null;
75
+ if (!/^-?\d+$/.test(parts[1]))
76
+ return null;
77
+ return { usageUsec: n };
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ export function readCpuMax() {
83
+ if (!_isNode())
84
+ return null;
85
+ const raw = _readText("cpu.max");
86
+ if (raw === null)
87
+ return null;
88
+ const parts = raw.trim().split(/\s+/);
89
+ if (parts.length !== 2)
90
+ return null;
91
+ const periodUs = Number(parts[1]);
92
+ if (!Number.isFinite(periodUs) || Number.isNaN(periodUs))
93
+ return null;
94
+ if (!/^-?\d+$/.test(parts[1]))
95
+ return null;
96
+ if (periodUs <= 0)
97
+ return null;
98
+ if (parts[0] === "max") {
99
+ const n = cpus().length || 1;
100
+ return { quotaUs: null, periodUs, vcpuCount: n };
101
+ }
102
+ const quotaUs = Number(parts[0]);
103
+ if (!Number.isFinite(quotaUs) || Number.isNaN(quotaUs))
104
+ return null;
105
+ if (!/^-?\d+$/.test(parts[0]))
106
+ return null;
107
+ return { quotaUs, periodUs, vcpuCount: quotaUs / periodUs };
108
+ }
109
+ export function readMemoryPeak() {
110
+ if (!_isNode())
111
+ return null;
112
+ return _readInt("memory.peak");
113
+ }
114
+ export function readMemoryMax() {
115
+ if (!_isNode())
116
+ return null;
117
+ return _readInt("memory.max");
118
+ }
119
+ export function readMemoryCurrent() {
120
+ if (!_isNode())
121
+ return null;
122
+ return _readInt("memory.current");
123
+ }
124
+ //# sourceMappingURL=cgroup-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-reader.js","sourceRoot":"","sources":["../../src/core/cgroup-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Cgroup-scope classifier — Phase 2 GPU foundation Decision #1.
3
+ *
4
+ * Reads `/proc/self/cgroup` and classifies the cgroup scope by prefix into
5
+ * one of:
6
+ *
7
+ * - "container" — kubepods.slice / docker / containerd / crio /
8
+ * system.slice/{docker,containerd,crio}-.
9
+ * cgroup.procs enumerates the container's PIDs.
10
+ * - "bare_metal_user_slice" — `/user.slice/...` (systemd user session).
11
+ * Walking would capture every PID in the SSH
12
+ * session, not just dexcost's task. Degrade to
13
+ * self-PID-only at `estimated` confidence with
14
+ * pricing_source suffix :no_container_scope.
15
+ * - "root_cgroup" — `/` (privileged single-tenant host). Ambiguous;
16
+ * degrade to self-PID-only.
17
+ * - "cgroup_v1" — multi-line file (multiple v1 controllers).
18
+ * v1.1 will walk; v1 degrades to self-PID-only.
19
+ * - "unknown" — anything else.
20
+ *
21
+ * **Browser safety**: returns "unknown" off-Node.
22
+ *
23
+ * Mirrors python/src/dexcost/cgroup_walker.py.
24
+ */
25
+ /** Test-only — override /proc/self/cgroup path. Pass null to restore. */
26
+ export declare function _setProcSelfCgroupPathForTests(p: string | null): void;
27
+ /** Test-only — override the cgroup root. Pass null to restore. */
28
+ export declare function _setCgroupRootForTests(p: string | null): void;
29
+ export declare function _resetWarningStateForTests(): void;
30
+ export type CgroupScopeKind = "container" | "bare_metal_user_slice" | "root_cgroup" | "cgroup_v1" | "unknown";
31
+ export interface CgroupScope {
32
+ kind: CgroupScopeKind;
33
+ /** Unified cgroup-v2 path for `container` scope; null for every other kind. */
34
+ path: string | null;
35
+ }
36
+ export declare function classifyScope(): CgroupScope;
37
+ /**
38
+ * Return the PID set to attribute GPU usage to.
39
+ *
40
+ * - container scope → walk `<cgroupRoot><scope.path>/cgroup.procs`. Returns
41
+ * `null` (NOT empty list) on read failure — signals the caller to log-
42
+ * once `gpu_cgroup_walk_forbidden` and fall back.
43
+ * - every other scope → return `[process.pid]`. Bare-metal-no-container
44
+ * deliberately does NOT walk the systemd user slice (which would capture
45
+ * unrelated user PIDs — the silent-overcount case).
46
+ *
47
+ * Off-Node, returns `[1]` as a non-failing placeholder (matches Python
48
+ * semantics where `os.getpid()` is always available in CPython; in TS the
49
+ * browser doesn't have a PID so we surface a benign sentinel).
50
+ */
51
+ export declare function enumeratePids(scope: CgroupScope): number[] | null;
52
+ /**
53
+ * Map scope kind to the pricing_source suffix per Decision #1.
54
+ *
55
+ * - container → null (full-fidelity attribution; no suffix)
56
+ * - bare_metal_user_slice / root_cgroup → "no_container_scope"
57
+ * - cgroup_v1 / unknown → "self_pid_only"
58
+ */
59
+ export declare function fallbackLabelFor(scope: CgroupScope): string | null;
60
+ //# sourceMappingURL=cgroup-walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-walker.d.ts","sourceRoot":"","sources":["../../src/core/cgroup-walker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAUH,yEAAyE;AACzE,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAErE;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7D;AAMD,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAkCD,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,uBAAuB,GACvB,aAAa,GACb,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAID,wBAAgB,aAAa,IAAI,WAAW,CAwC3C;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,GAAG,IAAI,CA6BjE;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAMlE"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Cgroup-scope classifier — Phase 2 GPU foundation Decision #1.
3
+ *
4
+ * Reads `/proc/self/cgroup` and classifies the cgroup scope by prefix into
5
+ * one of:
6
+ *
7
+ * - "container" — kubepods.slice / docker / containerd / crio /
8
+ * system.slice/{docker,containerd,crio}-.
9
+ * cgroup.procs enumerates the container's PIDs.
10
+ * - "bare_metal_user_slice" — `/user.slice/...` (systemd user session).
11
+ * Walking would capture every PID in the SSH
12
+ * session, not just dexcost's task. Degrade to
13
+ * self-PID-only at `estimated` confidence with
14
+ * pricing_source suffix :no_container_scope.
15
+ * - "root_cgroup" — `/` (privileged single-tenant host). Ambiguous;
16
+ * degrade to self-PID-only.
17
+ * - "cgroup_v1" — multi-line file (multiple v1 controllers).
18
+ * v1.1 will walk; v1 degrades to self-PID-only.
19
+ * - "unknown" — anything else.
20
+ *
21
+ * **Browser safety**: returns "unknown" off-Node.
22
+ *
23
+ * Mirrors python/src/dexcost/cgroup_walker.py.
24
+ */
25
+ import { readFileSync } from "node:fs";
26
+ import { join } from "node:path";
27
+ // ─── Module state (testable) ────────────────────────────────────────────────
28
+ let _procSelfCgroupPath = "/proc/self/cgroup";
29
+ let _cgroupRoot = "/sys/fs/cgroup";
30
+ /** Test-only — override /proc/self/cgroup path. Pass null to restore. */
31
+ export function _setProcSelfCgroupPathForTests(p) {
32
+ _procSelfCgroupPath = p ?? "/proc/self/cgroup";
33
+ }
34
+ /** Test-only — override the cgroup root. Pass null to restore. */
35
+ export function _setCgroupRootForTests(p) {
36
+ _cgroupRoot = p ?? "/sys/fs/cgroup";
37
+ }
38
+ // ─── Warn-once state ────────────────────────────────────────────────────────
39
+ const _warnedModes = new Set();
40
+ export function _resetWarningStateForTests() {
41
+ _warnedModes.clear();
42
+ }
43
+ function _warnOnce(mode, message) {
44
+ if (_warnedModes.has(mode))
45
+ return;
46
+ _warnedModes.add(mode);
47
+ // eslint-disable-next-line no-console
48
+ console.warn(message);
49
+ }
50
+ // ─── Browser-safety guard ───────────────────────────────────────────────────
51
+ function _isNode() {
52
+ return typeof process !== "undefined" && !!process.versions?.node;
53
+ }
54
+ // ─── Decision #1 prefix table (priority order) ──────────────────────────────
55
+ const CONTAINER_PREFIXES = [
56
+ "/kubepods.slice/",
57
+ "/kubepods/",
58
+ "/docker/",
59
+ "/system.slice/docker-",
60
+ "/containerd/",
61
+ "/system.slice/containerd-",
62
+ "/crio/",
63
+ "/system.slice/crio-",
64
+ ];
65
+ const BARE_METAL_PREFIXES = [
66
+ "/user.slice/",
67
+ ];
68
+ // ─── Classification ─────────────────────────────────────────────────────────
69
+ export function classifyScope() {
70
+ if (!_isNode()) {
71
+ return { kind: "unknown", path: null };
72
+ }
73
+ let raw;
74
+ try {
75
+ raw = readFileSync(_procSelfCgroupPath, "utf-8");
76
+ }
77
+ catch {
78
+ return { kind: "unknown", path: null };
79
+ }
80
+ const lines = raw
81
+ .split("\n")
82
+ .map((l) => l.trim())
83
+ .filter((l) => l.length > 0);
84
+ if (lines.length === 0) {
85
+ return { kind: "unknown", path: null };
86
+ }
87
+ // cgroup v1 → multiple controller lines; cgroup v2 → single "0::/path".
88
+ if (lines.length > 1 || !lines[0].startsWith("0::")) {
89
+ return { kind: "cgroup_v1", path: null };
90
+ }
91
+ const path = lines[0].substring(3); // strip "0::" prefix
92
+ if (path === "/" || path === "") {
93
+ return { kind: "root_cgroup", path: null };
94
+ }
95
+ for (const prefix of CONTAINER_PREFIXES) {
96
+ if (path.startsWith(prefix)) {
97
+ return { kind: "container", path };
98
+ }
99
+ }
100
+ for (const prefix of BARE_METAL_PREFIXES) {
101
+ if (path.startsWith(prefix)) {
102
+ return { kind: "bare_metal_user_slice", path: null };
103
+ }
104
+ }
105
+ return { kind: "unknown", path: null };
106
+ }
107
+ // ─── PID enumeration ────────────────────────────────────────────────────────
108
+ /**
109
+ * Return the PID set to attribute GPU usage to.
110
+ *
111
+ * - container scope → walk `<cgroupRoot><scope.path>/cgroup.procs`. Returns
112
+ * `null` (NOT empty list) on read failure — signals the caller to log-
113
+ * once `gpu_cgroup_walk_forbidden` and fall back.
114
+ * - every other scope → return `[process.pid]`. Bare-metal-no-container
115
+ * deliberately does NOT walk the systemd user slice (which would capture
116
+ * unrelated user PIDs — the silent-overcount case).
117
+ *
118
+ * Off-Node, returns `[1]` as a non-failing placeholder (matches Python
119
+ * semantics where `os.getpid()` is always available in CPython; in TS the
120
+ * browser doesn't have a PID so we surface a benign sentinel).
121
+ */
122
+ export function enumeratePids(scope) {
123
+ if (!_isNode()) {
124
+ return [1];
125
+ }
126
+ if (scope.kind !== "container" || scope.path === null) {
127
+ return [process.pid];
128
+ }
129
+ const cgroupProcsPath = join(_cgroupRoot + scope.path, "cgroup.procs");
130
+ let raw;
131
+ try {
132
+ raw = readFileSync(cgroupProcsPath, "utf-8");
133
+ }
134
+ catch (err) {
135
+ _warnOnce("gpu_cgroup_walk_forbidden", `Could not read ${cgroupProcsPath} (${String(err)}); ` +
136
+ `GpuAccountant will degrade to self-PID-only`);
137
+ return null;
138
+ }
139
+ const pids = [];
140
+ for (const line of raw.split("\n")) {
141
+ const s = line.trim();
142
+ if (!s)
143
+ continue;
144
+ const n = parseInt(s, 10);
145
+ if (!Number.isNaN(n))
146
+ pids.push(n);
147
+ }
148
+ return pids;
149
+ }
150
+ // ─── Decision #1 confidence labelling ───────────────────────────────────────
151
+ /**
152
+ * Map scope kind to the pricing_source suffix per Decision #1.
153
+ *
154
+ * - container → null (full-fidelity attribution; no suffix)
155
+ * - bare_metal_user_slice / root_cgroup → "no_container_scope"
156
+ * - cgroup_v1 / unknown → "self_pid_only"
157
+ */
158
+ export function fallbackLabelFor(scope) {
159
+ if (scope.kind === "container")
160
+ return null;
161
+ if (scope.kind === "bare_metal_user_slice" || scope.kind === "root_cgroup") {
162
+ return "no_container_scope";
163
+ }
164
+ return "self_pid_only";
165
+ }
166
+ //# sourceMappingURL=cgroup-walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cgroup-walker.js","sourceRoot":"","sources":["../../src/core/cgroup-walker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Per-task compute accountant.
3
+ *
4
+ * Holds start cgroup snapshot + runtime context for one dexcost task. At
5
+ * task finalize, emits exactly one `compute_cost` event with
6
+ * `cost_pending: true` — the pricing engine back-fills `cost_usd` via the
7
+ * deferred-cost pattern inherited from network v2 §6.4.
8
+ *
9
+ * Capture §5.3 invariant: at most one event per task per runtime. Idempotent —
10
+ * second call to snapshotEndAndBuild / buildServerlessEvent returns null.
11
+ *
12
+ * TS is single-threaded → freeze flag is sufficient, no mutex needed.
13
+ *
14
+ * Mirrors python/src/dexcost/compute_accountant.py.
15
+ */
16
+ import { RuntimeKind } from "./compute-runtime.js";
17
+ export interface ComputeAccountantOptions {
18
+ runtime: RuntimeKind;
19
+ lambdaMemoryMb?: number;
20
+ fargateVcpu?: number;
21
+ fargateMemoryMib?: number;
22
+ architecture?: string;
23
+ initializationType?: string;
24
+ region?: string;
25
+ }
26
+ export declare class ComputeAccountant {
27
+ private _frozen;
28
+ private _startCpuUsec;
29
+ readonly runtime: RuntimeKind;
30
+ readonly lambdaMemoryMb: number | undefined;
31
+ readonly fargateVcpu: number | undefined;
32
+ readonly fargateMemoryMib: number | undefined;
33
+ readonly architecture: string;
34
+ readonly initializationType: string | undefined;
35
+ readonly region: string | undefined;
36
+ constructor(opts: ComputeAccountantOptions);
37
+ /** Capture the cgroup CPU counter at task start. Idempotent. */
38
+ snapshotStart(): void;
39
+ /**
40
+ * Capture cgroup CPU/memory at task end and build the event details.
41
+ *
42
+ * Returns null if already frozen (second call) or runtime is unknown.
43
+ */
44
+ snapshotEndAndBuild(durationMs: number): Record<string, any> | null;
45
+ /**
46
+ * Build a per-invocation event for Lambda / Cloud Run / Cloud Functions /
47
+ * Azure Functions / Vercel.
48
+ */
49
+ buildServerlessEvent(durationMs: number, memoryBytesPeak: number): Record<string, any> | null;
50
+ }
51
+ //# sourceMappingURL=compute-accountant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-accountant.d.ts","sourceRoot":"","sources":["../../src/core/compute-accountant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAyCnD,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,aAAa,CAAuB;IAE5C,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAExB,IAAI,EAAE,wBAAwB;IAe1C,gEAAgE;IAChE,aAAa,IAAI,IAAI;IAMrB;;;;OAIG;IACH,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAwCnE;;;OAGG;IACH,oBAAoB,CAClB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;CAmC9B"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Per-task compute accountant.
3
+ *
4
+ * Holds start cgroup snapshot + runtime context for one dexcost task. At
5
+ * task finalize, emits exactly one `compute_cost` event with
6
+ * `cost_pending: true` — the pricing engine back-fills `cost_usd` via the
7
+ * deferred-cost pattern inherited from network v2 §6.4.
8
+ *
9
+ * Capture §5.3 invariant: at most one event per task per runtime. Idempotent —
10
+ * second call to snapshotEndAndBuild / buildServerlessEvent returns null.
11
+ *
12
+ * TS is single-threaded → freeze flag is sufficient, no mutex needed.
13
+ *
14
+ * Mirrors python/src/dexcost/compute_accountant.py.
15
+ */
16
+ import { cpus } from "node:os";
17
+ import { readCpuMax, readCpuStat, readMemoryCurrent, readMemoryMax, readMemoryPeak, } from "./cgroup-reader.js";
18
+ import { RuntimeKind } from "./compute-runtime.js";
19
+ /** Map a RuntimeKind to the details.billing_model discriminator. */
20
+ function billingModelFor(runtime) {
21
+ switch (runtime) {
22
+ case RuntimeKind.Lambda:
23
+ return "lambda";
24
+ case RuntimeKind.Fargate:
25
+ return "fargate";
26
+ case RuntimeKind.Ec2:
27
+ return "ec2";
28
+ case RuntimeKind.Gce:
29
+ return "gce";
30
+ case RuntimeKind.AzureVm:
31
+ return "azure_vm";
32
+ case RuntimeKind.CloudRun:
33
+ return "cloud_run_request";
34
+ case RuntimeKind.CloudFunctions:
35
+ return "cloud_functions";
36
+ case RuntimeKind.AzureFunctions:
37
+ return "azure_functions";
38
+ case RuntimeKind.Vercel:
39
+ return "vercel_fluid";
40
+ case RuntimeKind.K8sPod:
41
+ return "k8s_pod";
42
+ default:
43
+ return "unknown";
44
+ }
45
+ }
46
+ function _isNode() {
47
+ return typeof process !== "undefined" && !!process.versions?.node;
48
+ }
49
+ function detectArch() {
50
+ if (!_isNode())
51
+ return "x86_64";
52
+ const a = process.arch;
53
+ if (a === "arm64" || a === "arm")
54
+ return "arm64";
55
+ return "x86_64";
56
+ }
57
+ export class ComputeAccountant {
58
+ _frozen = false;
59
+ _startCpuUsec = null;
60
+ runtime;
61
+ lambdaMemoryMb;
62
+ fargateVcpu;
63
+ fargateMemoryMib;
64
+ architecture;
65
+ initializationType;
66
+ region;
67
+ constructor(opts) {
68
+ this.runtime = opts.runtime;
69
+ this.lambdaMemoryMb = opts.lambdaMemoryMb;
70
+ this.fargateVcpu = opts.fargateVcpu;
71
+ this.fargateMemoryMib = opts.fargateMemoryMib;
72
+ this.architecture = opts.architecture ?? detectArch();
73
+ this.initializationType = opts.initializationType;
74
+ this.region = opts.region;
75
+ }
76
+ // ------------------------------------------------------------------
77
+ // Long-running runtimes (Fargate / EC2 / GCE / Azure VM / K8s pod /
78
+ // Cloud Run instance-based)
79
+ // ------------------------------------------------------------------
80
+ /** Capture the cgroup CPU counter at task start. Idempotent. */
81
+ snapshotStart() {
82
+ if (this._startCpuUsec !== null)
83
+ return;
84
+ const s = readCpuStat();
85
+ this._startCpuUsec = s ? s.usageUsec : 0;
86
+ }
87
+ /**
88
+ * Capture cgroup CPU/memory at task end and build the event details.
89
+ *
90
+ * Returns null if already frozen (second call) or runtime is unknown.
91
+ */
92
+ snapshotEndAndBuild(durationMs) {
93
+ if (this._frozen)
94
+ return null;
95
+ this._frozen = true;
96
+ const startCpu = this._startCpuUsec ?? 0;
97
+ const end = readCpuStat();
98
+ const cpuMax = readCpuMax();
99
+ // Capture §6 case 6 — memory.peak missing → fall back to memory.current.
100
+ let memPeak = readMemoryPeak();
101
+ if (memPeak === null) {
102
+ memPeak = readMemoryCurrent() ?? 0;
103
+ }
104
+ const memLimit = readMemoryMax() ?? 0;
105
+ let vcpuSecondsUsed = 0;
106
+ if (end !== null && end.usageUsec >= startCpu) {
107
+ vcpuSecondsUsed = (end.usageUsec - startCpu) / 1_000_000;
108
+ }
109
+ const vcpuCount = cpuMax ? cpuMax.vcpuCount : _nproc();
110
+ return {
111
+ billing_model: billingModelFor(this.runtime),
112
+ duration_ms: durationMs,
113
+ memory_bytes_peak: Math.trunc(memPeak),
114
+ memory_bytes_limit: Math.trunc(memLimit),
115
+ vcpu_count: vcpuCount,
116
+ vcpu_seconds_used: vcpuSecondsUsed,
117
+ invocation_count: 0,
118
+ region: this.region ?? null,
119
+ architecture: this.architecture,
120
+ initialization_type: null,
121
+ cost_pending: true,
122
+ };
123
+ }
124
+ // ------------------------------------------------------------------
125
+ // Serverless runtimes
126
+ // ------------------------------------------------------------------
127
+ /**
128
+ * Build a per-invocation event for Lambda / Cloud Run / Cloud Functions /
129
+ * Azure Functions / Vercel.
130
+ */
131
+ buildServerlessEvent(durationMs, memoryBytesPeak) {
132
+ if (this._frozen)
133
+ return null;
134
+ this._frozen = true;
135
+ let memLimit;
136
+ let vcpuCount;
137
+ if (this.runtime === RuntimeKind.Lambda) {
138
+ // Lambda's AWS_LAMBDA_FUNCTION_MEMORY_SIZE is DECIMAL MB.
139
+ memLimit = (this.lambdaMemoryMb ?? 128) * 1_000_000;
140
+ vcpuCount = vcpuCountFromCgroup();
141
+ }
142
+ else if (this.runtime === RuntimeKind.Fargate) {
143
+ memLimit = (this.fargateMemoryMib ?? 0) * 1024 * 1024;
144
+ vcpuCount =
145
+ this.fargateVcpu !== undefined ? this.fargateVcpu : vcpuCountFromCgroup();
146
+ }
147
+ else {
148
+ // Cloud Run / Cloud Functions / Azure Functions / Vercel —
149
+ // cgroup memory.max is the declared limit.
150
+ memLimit = readMemoryMax() ?? memoryBytesPeak;
151
+ vcpuCount = vcpuCountFromCgroup();
152
+ }
153
+ return {
154
+ billing_model: billingModelFor(this.runtime),
155
+ duration_ms: durationMs,
156
+ memory_bytes_peak: memoryBytesPeak,
157
+ memory_bytes_limit: memLimit,
158
+ vcpu_count: vcpuCount,
159
+ vcpu_seconds_used: 0,
160
+ invocation_count: 1,
161
+ region: this.region ?? null,
162
+ architecture: this.architecture,
163
+ initialization_type: this.initializationType ?? null,
164
+ cost_pending: true,
165
+ };
166
+ }
167
+ }
168
+ function _nproc() {
169
+ if (!_isNode())
170
+ return 1;
171
+ return cpus().length || 1;
172
+ }
173
+ function vcpuCountFromCgroup() {
174
+ const cpuMax = readCpuMax();
175
+ if (cpuMax !== null)
176
+ return cpuMax.vcpuCount;
177
+ return _nproc();
178
+ }
179
+ //# sourceMappingURL=compute-accountant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute-accountant.js","sourceRoot":"","sources":["../../src/core/compute-accountant.ts"],"names":[],"mappings":"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"}