@zanii/blackbox 0.0.0-stage → 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 (135) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +109 -2
  3. package/dist/agents/index.d.ts +34 -0
  4. package/dist/agents/index.js +73 -0
  5. package/dist/analysis/detectors.d.ts +36 -0
  6. package/dist/analysis/detectors.js +339 -0
  7. package/dist/analysis/faults.d.ts +9 -0
  8. package/dist/analysis/faults.js +250 -0
  9. package/dist/analysis/index.d.ts +68 -0
  10. package/dist/analysis/index.js +388 -0
  11. package/dist/analysis/landing.d.ts +25 -0
  12. package/dist/analysis/landing.js +225 -0
  13. package/dist/analysis/memory.d.ts +13 -0
  14. package/dist/analysis/memory.js +33 -0
  15. package/dist/analysis/waste.d.ts +29 -0
  16. package/dist/analysis/waste.js +79 -0
  17. package/dist/approvals/index.d.ts +11 -0
  18. package/dist/approvals/index.js +27 -0
  19. package/dist/approvals/warnings.d.ts +2 -0
  20. package/dist/approvals/warnings.js +28 -0
  21. package/dist/attest/index.d.ts +17 -0
  22. package/dist/attest/index.js +106 -0
  23. package/dist/authority/index.d.ts +24 -0
  24. package/dist/authority/index.js +77 -0
  25. package/dist/billing/index.d.ts +99 -0
  26. package/dist/billing/index.js +174 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +1057 -0
  29. package/dist/client/index.d.ts +146 -0
  30. package/dist/client/index.js +210 -0
  31. package/dist/compliance/index.d.ts +41 -0
  32. package/dist/compliance/index.js +96 -0
  33. package/dist/cost/index.d.ts +133 -0
  34. package/dist/cost/index.js +293 -0
  35. package/dist/data/index.d.ts +191 -0
  36. package/dist/data/index.js +762 -0
  37. package/dist/directives/index.d.ts +35 -0
  38. package/dist/directives/index.js +80 -0
  39. package/dist/drills/index.d.ts +43 -0
  40. package/dist/drills/index.js +101 -0
  41. package/dist/duty/index.d.ts +21 -0
  42. package/dist/duty/index.js +68 -0
  43. package/dist/fleet/index.d.ts +141 -0
  44. package/dist/fleet/index.js +454 -0
  45. package/dist/hooks/ai-sdk.d.ts +42 -0
  46. package/dist/hooks/ai-sdk.js +62 -0
  47. package/dist/hooks/claude-agent-sdk.d.ts +14 -0
  48. package/dist/hooks/claude-agent-sdk.js +70 -0
  49. package/dist/hooks/index.d.ts +7 -0
  50. package/dist/hooks/index.js +10 -0
  51. package/dist/hooks/langchain-agent.d.ts +69 -0
  52. package/dist/hooks/langchain-agent.js +163 -0
  53. package/dist/hooks/langchain.d.ts +41 -0
  54. package/dist/hooks/langchain.js +216 -0
  55. package/dist/hooks/langgraph-checkpoint.d.ts +12 -0
  56. package/dist/hooks/langgraph-checkpoint.js +73 -0
  57. package/dist/hooks/memory.d.ts +17 -0
  58. package/dist/hooks/memory.js +64 -0
  59. package/dist/hooks/openai-agents.d.ts +6 -0
  60. package/dist/hooks/openai-agents.js +40 -0
  61. package/dist/hooks/protect.d.ts +7 -0
  62. package/dist/hooks/protect.js +39 -0
  63. package/dist/hooks/providers.d.ts +16 -0
  64. package/dist/hooks/providers.js +149 -0
  65. package/dist/hooks/shared.d.ts +11 -0
  66. package/dist/hooks/shared.js +39 -0
  67. package/dist/index.d.ts +46 -0
  68. package/dist/index.js +48 -0
  69. package/dist/investigate/index.d.ts +66 -0
  70. package/dist/investigate/index.js +119 -0
  71. package/dist/mcp-server/index.d.ts +85 -0
  72. package/dist/mcp-server/index.js +216 -0
  73. package/dist/mcp-wrap/index.d.ts +17 -0
  74. package/dist/mcp-wrap/index.js +170 -0
  75. package/dist/money/index.d.ts +114 -0
  76. package/dist/money/index.js +622 -0
  77. package/dist/occurrence/index.d.ts +108 -0
  78. package/dist/occurrence/index.js +168 -0
  79. package/dist/ocsf/index.d.ts +22 -0
  80. package/dist/ocsf/index.js +168 -0
  81. package/dist/otlp/index.d.ts +24 -0
  82. package/dist/otlp/index.js +143 -0
  83. package/dist/packs/index.d.ts +48 -0
  84. package/dist/packs/index.js +343 -0
  85. package/dist/policy/delta.d.ts +11 -0
  86. package/dist/policy/delta.js +39 -0
  87. package/dist/policy/drafts.d.ts +34 -0
  88. package/dist/policy/drafts.js +129 -0
  89. package/dist/policy/index.d.ts +47 -0
  90. package/dist/policy/index.js +154 -0
  91. package/dist/precog/index.d.ts +96 -0
  92. package/dist/precog/index.js +167 -0
  93. package/dist/precog/intervention.d.ts +22 -0
  94. package/dist/precog/intervention.js +44 -0
  95. package/dist/precog/normal.d.ts +31 -0
  96. package/dist/precog/normal.js +89 -0
  97. package/dist/preflight/index.d.ts +11 -0
  98. package/dist/preflight/index.js +19 -0
  99. package/dist/ratings/index.d.ts +21 -0
  100. package/dist/ratings/index.js +48 -0
  101. package/dist/reconcile/claude-code.d.ts +19 -0
  102. package/dist/reconcile/claude-code.js +220 -0
  103. package/dist/reconcile/codex.d.ts +5 -0
  104. package/dist/reconcile/codex.js +191 -0
  105. package/dist/reconcile/index.d.ts +19 -0
  106. package/dist/reconcile/index.js +50 -0
  107. package/dist/reconcile/record.d.ts +49 -0
  108. package/dist/reconcile/record.js +225 -0
  109. package/dist/reconcile/shared.d.ts +65 -0
  110. package/dist/reconcile/shared.js +113 -0
  111. package/dist/replay/index.d.ts +11 -0
  112. package/dist/replay/index.js +64 -0
  113. package/dist/replay/repair.d.ts +10 -0
  114. package/dist/replay/repair.js +62 -0
  115. package/dist/session/drain.d.ts +13 -0
  116. package/dist/session/drain.js +35 -0
  117. package/dist/session/index.d.ts +275 -0
  118. package/dist/session/index.js +681 -0
  119. package/dist/undo/index.d.ts +45 -0
  120. package/dist/undo/index.js +212 -0
  121. package/dist/verify/anchor.d.ts +54 -0
  122. package/dist/verify/anchor.js +77 -0
  123. package/dist/verify/chain.d.ts +27 -0
  124. package/dist/verify/chain.js +105 -0
  125. package/dist/verify/envelope.d.ts +28 -0
  126. package/dist/verify/envelope.js +55 -0
  127. package/dist/verify/index.d.ts +3 -0
  128. package/dist/verify/index.js +3 -0
  129. package/dist/version.d.ts +1 -0
  130. package/dist/version.js +2 -0
  131. package/dist/weather/index.d.ts +24 -0
  132. package/dist/weather/index.js +45 -0
  133. package/dist/workspace-receipt/index.d.ts +15 -0
  134. package/dist/workspace-receipt/index.js +121 -0
  135. package/package.json +56 -3
@@ -0,0 +1,174 @@
1
+ // Invoices in integer fils with UAE VAT (spec/billing.md §3), issued tax invoices (§5) and the
2
+ // Stripe webhook signature (§6). Mirrors sdks/python/src/zanii_blackbox/billing.py.
3
+ import { createHmac, timingSafeEqual } from "node:crypto";
4
+ import { formatAed } from "../cost/index.js";
5
+ const grouped = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
6
+ export function invoice(plan, usage, vatPercent = 5) {
7
+ const lines = [
8
+ {
9
+ description: `${plan.name} plan, monthly`,
10
+ quantity: 1,
11
+ unit_fils: plan.monthly_fils,
12
+ amount_fils: plan.monthly_fils,
13
+ },
14
+ ];
15
+ if (plan.included_events !== undefined && plan.per_1000_events_fils !== undefined) {
16
+ const blocks = Math.ceil(Math.max(0, usage.events - plan.included_events) / 1000);
17
+ if (blocks > 0)
18
+ lines.push({
19
+ description: `Events over ${grouped(plan.included_events)} (per 1,000)`,
20
+ quantity: blocks,
21
+ unit_fils: plan.per_1000_events_fils,
22
+ amount_fils: blocks * plan.per_1000_events_fils,
23
+ });
24
+ }
25
+ if (plan.included_sessions !== undefined && plan.per_session_fils !== undefined) {
26
+ const over = Math.max(0, (usage.sessions ?? 0) - plan.included_sessions);
27
+ if (over > 0)
28
+ lines.push({
29
+ description: `Sessions over ${grouped(plan.included_sessions)}`,
30
+ quantity: over,
31
+ unit_fils: plan.per_session_fils,
32
+ amount_fils: over * plan.per_session_fils,
33
+ });
34
+ }
35
+ const subtotal = lines.reduce((sum, l) => sum + l.amount_fils, 0);
36
+ // VAT once, on the subtotal, rounded half up (integer arithmetic only).
37
+ const vat = Math.floor((subtotal * vatPercent + 50) / 100);
38
+ return {
39
+ lines,
40
+ subtotal_fils: subtotal,
41
+ vat_percent: vatPercent,
42
+ vat_fils: vat,
43
+ total_fils: subtotal + vat,
44
+ };
45
+ }
46
+ const PAIRS = [
47
+ ["included_events", "per_1000_events_fils"],
48
+ ["included_sessions", "per_session_fils"],
49
+ ];
50
+ /** Parses a plans file (spec/billing.md §1); throws on anything that isn't integer fils. */
51
+ export function loadPlans(bytes) {
52
+ const doc = JSON.parse(new TextDecoder().decode(bytes));
53
+ if (doc.currency !== "AED" || typeof doc.version !== "string" || typeof doc.plans !== "object")
54
+ throw new Error("not a v1 plans file (AED, integer fils)");
55
+ const fils = (id, k, v) => {
56
+ if (!Number.isSafeInteger(v) || v < 0)
57
+ throw new Error(`plan ${id}: ${k} must be an integer ≥ 0`);
58
+ };
59
+ for (const [id, p] of Object.entries(doc.plans)) {
60
+ fils(id, "monthly_fils", p.monthly_fils);
61
+ for (const [a, b] of PAIRS) {
62
+ if ((p[a] === undefined) !== (p[b] === undefined))
63
+ throw new Error(`plan ${id}: ${a} and ${b} go together`);
64
+ if (p[a] !== undefined) {
65
+ fils(id, a, p[a]);
66
+ fils(id, b, p[b]);
67
+ }
68
+ }
69
+ }
70
+ return doc;
71
+ }
72
+ /** The month's `[from, to)` and its last day (spec/billing.md §5). */
73
+ function period(month) {
74
+ const [y, m] = month.split("-").map(Number);
75
+ const iso = (t) => new Date(t).toISOString().slice(0, 10);
76
+ const next = Date.UTC(y, m, 1); // month m (1-based) is index m-1; index m is the next month
77
+ return { from: `${month}-01`, to: iso(next), last: iso(next - 86_400_000) };
78
+ }
79
+ /** spec/billing.md §5: the tax invoice document. */
80
+ export function taxInvoice(input) {
81
+ const p = period(input.month);
82
+ const inv = invoice(input.plan, input.usage, input.vat_percent ?? 5);
83
+ return {
84
+ v: 1,
85
+ title: "Tax Invoice",
86
+ number: input.number,
87
+ issued_at: input.issued_at,
88
+ period: { from: p.from, to: p.to },
89
+ date_of_supply: p.last,
90
+ supplier: {
91
+ name: input.supplier.name,
92
+ address: input.supplier.address,
93
+ trn: input.supplier.trn,
94
+ },
95
+ customer: {
96
+ legal_name: input.customer.legal_name,
97
+ address: input.customer.address,
98
+ trn: input.customer.trn ?? null,
99
+ },
100
+ currency: "AED",
101
+ plan: { id: input.plan_id, name: input.plan.name, version: input.plans_version },
102
+ usage: { events: input.usage.events, sessions: input.usage.sessions },
103
+ ...inv,
104
+ total_aed: formatAed(inv.total_fils),
105
+ };
106
+ }
107
+ const aed = (fils) => `${grouped(Math.floor(fils / 100))}.${String(fils % 100).padStart(2, "0")}`;
108
+ const cell = (s) => s.replace(/\r\n|\r|\n/g, " ").replace(/\|/g, "\\|");
109
+ const oneLine = (s) => s
110
+ .split(/\r\n|\r|\n/)
111
+ .map((l) => l.trim())
112
+ .filter(Boolean)
113
+ .join(", ");
114
+ /** spec/billing.md §5: the printable copy. */
115
+ export function renderTaxInvoice(d) {
116
+ const out = [
117
+ "# Tax Invoice",
118
+ "",
119
+ "| Field | Value |",
120
+ "|---|---|",
121
+ `| Invoice number | ${cell(d.number)} |`,
122
+ `| Date of issue | ${d.issued_at} |`,
123
+ `| Date of supply | ${d.date_of_supply} (period ${d.period.from} to ${d.date_of_supply}) |`,
124
+ "",
125
+ "## Supplier",
126
+ "",
127
+ `${oneLine(d.supplier.name)} `,
128
+ `${oneLine(d.supplier.address)} `,
129
+ `TRN: ${d.supplier.trn}`,
130
+ "",
131
+ "## Customer",
132
+ "",
133
+ `${oneLine(d.customer.legal_name)} `,
134
+ `${oneLine(d.customer.address)} `,
135
+ `TRN: ${d.customer.trn ?? "not registered"}`,
136
+ "",
137
+ "## Lines",
138
+ "",
139
+ "| Description | Quantity | Unit (AED) | Amount (AED) |",
140
+ "|---|---:|---:|---:|",
141
+ ...d.lines.map((l) => `| ${cell(l.description)} | ${grouped(l.quantity)} | ${aed(l.unit_fils)} | ${aed(l.amount_fils)} |`),
142
+ "",
143
+ "| Total | AED |",
144
+ "|---|---:|",
145
+ `| Subtotal | ${aed(d.subtotal_fils)} |`,
146
+ `| VAT ${d.vat_percent}% | ${aed(d.vat_fils)} |`,
147
+ `| **Total** | **${aed(d.total_fils)}** |`,
148
+ "",
149
+ `Plan: ${oneLine(d.plan.name)} (${d.plan.id}, plans ${d.plan.version}). Usage: ${grouped(d.usage.events)} events, ${grouped(d.usage.sessions)} sessions.`,
150
+ "",
151
+ ];
152
+ return out.join("\n");
153
+ }
154
+ // ---------------------------------------------------------------- Stripe webhook signature (§6)
155
+ /** spec/billing.md §6: a `Stripe-Signature` header checked against the raw body. */
156
+ export function stripeSignatureOk(header, rawBody, secret, nowSeconds, toleranceSeconds = 300) {
157
+ let t = null;
158
+ const v1 = [];
159
+ for (const part of header.split(",")) {
160
+ const i = part.indexOf("=");
161
+ if (i < 0)
162
+ continue;
163
+ const k = part.slice(0, i).trim();
164
+ const v = part.slice(i + 1).trim();
165
+ if (k === "t" && /^\d{1,12}$/.test(v))
166
+ t = Number(v);
167
+ else if (k === "v1" && /^[0-9a-f]{64}$/.test(v))
168
+ v1.push(v);
169
+ }
170
+ if (t === null || v1.length === 0 || Math.abs(nowSeconds - t) > toleranceSeconds)
171
+ return false;
172
+ const expected = createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest();
173
+ return v1.some((sig) => timingSafeEqual(Buffer.from(sig, "hex"), expected));
174
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};