sorokeep 1.0.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 (275) hide show
  1. package/CONTRIBUTING.md +296 -0
  2. package/LICENSE +21 -0
  3. package/README.md +677 -0
  4. package/dist/alerts/alerts.d.ts +21 -0
  5. package/dist/alerts/alerts.d.ts.map +1 -0
  6. package/dist/alerts/alerts.js +18 -0
  7. package/dist/alerts/alerts.js.map +1 -0
  8. package/dist/alerts/discord.d.ts +3 -0
  9. package/dist/alerts/discord.d.ts.map +1 -0
  10. package/dist/alerts/discord.js +199 -0
  11. package/dist/alerts/discord.js.map +1 -0
  12. package/dist/alerts/dispatcher.d.ts +13 -0
  13. package/dist/alerts/dispatcher.d.ts.map +1 -0
  14. package/dist/alerts/dispatcher.js +91 -0
  15. package/dist/alerts/dispatcher.js.map +1 -0
  16. package/dist/alerts/keys.d.ts +24 -0
  17. package/dist/alerts/keys.d.ts.map +1 -0
  18. package/dist/alerts/keys.js +44 -0
  19. package/dist/alerts/keys.js.map +1 -0
  20. package/dist/alerts/pagerduty.d.ts +8 -0
  21. package/dist/alerts/pagerduty.d.ts.map +1 -0
  22. package/dist/alerts/pagerduty.js +164 -0
  23. package/dist/alerts/pagerduty.js.map +1 -0
  24. package/dist/alerts/resource.d.ts +23 -0
  25. package/dist/alerts/resource.d.ts.map +1 -0
  26. package/dist/alerts/resource.js +167 -0
  27. package/dist/alerts/resource.js.map +1 -0
  28. package/dist/alerts/slack.d.ts +7 -0
  29. package/dist/alerts/slack.d.ts.map +1 -0
  30. package/dist/alerts/slack.js +184 -0
  31. package/dist/alerts/slack.js.map +1 -0
  32. package/dist/alerts/telegram.d.ts +9 -0
  33. package/dist/alerts/telegram.d.ts.map +1 -0
  34. package/dist/alerts/telegram.js +133 -0
  35. package/dist/alerts/telegram.js.map +1 -0
  36. package/dist/alerts/templates.d.ts +109 -0
  37. package/dist/alerts/templates.d.ts.map +1 -0
  38. package/dist/alerts/templates.js +139 -0
  39. package/dist/alerts/templates.js.map +1 -0
  40. package/dist/alerts/types.d.ts +113 -0
  41. package/dist/alerts/types.d.ts.map +1 -0
  42. package/dist/alerts/types.js +87 -0
  43. package/dist/alerts/types.js.map +1 -0
  44. package/dist/alerts/webhook.d.ts +13 -0
  45. package/dist/alerts/webhook.d.ts.map +1 -0
  46. package/dist/alerts/webhook.js +56 -0
  47. package/dist/alerts/webhook.js.map +1 -0
  48. package/dist/cli/schemaFormatter.d.ts +11 -0
  49. package/dist/cli/schemaFormatter.d.ts.map +1 -0
  50. package/dist/cli/schemaFormatter.js +74 -0
  51. package/dist/cli/schemaFormatter.js.map +1 -0
  52. package/dist/commands/alerts.d.ts +3 -0
  53. package/dist/commands/alerts.d.ts.map +1 -0
  54. package/dist/commands/alerts.js +256 -0
  55. package/dist/commands/alerts.js.map +1 -0
  56. package/dist/commands/budget.d.ts +3 -0
  57. package/dist/commands/budget.d.ts.map +1 -0
  58. package/dist/commands/budget.js +47 -0
  59. package/dist/commands/budget.js.map +1 -0
  60. package/dist/commands/channels.d.ts +3 -0
  61. package/dist/commands/channels.d.ts.map +1 -0
  62. package/dist/commands/channels.js +84 -0
  63. package/dist/commands/channels.js.map +1 -0
  64. package/dist/commands/check.d.ts +3 -0
  65. package/dist/commands/check.d.ts.map +1 -0
  66. package/dist/commands/check.js +58 -0
  67. package/dist/commands/check.js.map +1 -0
  68. package/dist/commands/completion.d.ts +3 -0
  69. package/dist/commands/completion.d.ts.map +1 -0
  70. package/dist/commands/completion.js +36 -0
  71. package/dist/commands/completion.js.map +1 -0
  72. package/dist/commands/costs.d.ts +15 -0
  73. package/dist/commands/costs.d.ts.map +1 -0
  74. package/dist/commands/costs.js +280 -0
  75. package/dist/commands/costs.js.map +1 -0
  76. package/dist/commands/daemon.d.ts +3 -0
  77. package/dist/commands/daemon.d.ts.map +1 -0
  78. package/dist/commands/daemon.js +93 -0
  79. package/dist/commands/daemon.js.map +1 -0
  80. package/dist/commands/db.d.ts +3 -0
  81. package/dist/commands/db.d.ts.map +1 -0
  82. package/dist/commands/db.js +140 -0
  83. package/dist/commands/db.js.map +1 -0
  84. package/dist/commands/guard.d.ts +3 -0
  85. package/dist/commands/guard.d.ts.map +1 -0
  86. package/dist/commands/guard.js +181 -0
  87. package/dist/commands/guard.js.map +1 -0
  88. package/dist/commands/history.d.ts +3 -0
  89. package/dist/commands/history.d.ts.map +1 -0
  90. package/dist/commands/history.js +57 -0
  91. package/dist/commands/history.js.map +1 -0
  92. package/dist/commands/inspect.d.ts +3 -0
  93. package/dist/commands/inspect.d.ts.map +1 -0
  94. package/dist/commands/inspect.js +82 -0
  95. package/dist/commands/inspect.js.map +1 -0
  96. package/dist/commands/mcp.d.ts +3 -0
  97. package/dist/commands/mcp.d.ts.map +1 -0
  98. package/dist/commands/mcp.js +20 -0
  99. package/dist/commands/mcp.js.map +1 -0
  100. package/dist/commands/pause.d.ts +3 -0
  101. package/dist/commands/pause.d.ts.map +1 -0
  102. package/dist/commands/pause.js +32 -0
  103. package/dist/commands/pause.js.map +1 -0
  104. package/dist/commands/resources.d.ts +3 -0
  105. package/dist/commands/resources.d.ts.map +1 -0
  106. package/dist/commands/resources.js +95 -0
  107. package/dist/commands/resources.js.map +1 -0
  108. package/dist/commands/restore.d.ts +3 -0
  109. package/dist/commands/restore.d.ts.map +1 -0
  110. package/dist/commands/restore.js +92 -0
  111. package/dist/commands/restore.js.map +1 -0
  112. package/dist/commands/resume.d.ts +3 -0
  113. package/dist/commands/resume.d.ts.map +1 -0
  114. package/dist/commands/resume.js +31 -0
  115. package/dist/commands/resume.js.map +1 -0
  116. package/dist/commands/status.d.ts +3 -0
  117. package/dist/commands/status.d.ts.map +1 -0
  118. package/dist/commands/status.js +63 -0
  119. package/dist/commands/status.js.map +1 -0
  120. package/dist/commands/watch.d.ts +3 -0
  121. package/dist/commands/watch.d.ts.map +1 -0
  122. package/dist/commands/watch.js +163 -0
  123. package/dist/commands/watch.js.map +1 -0
  124. package/dist/core/aws_secrets.d.ts +13 -0
  125. package/dist/core/aws_secrets.d.ts.map +1 -0
  126. package/dist/core/aws_secrets.js +34 -0
  127. package/dist/core/aws_secrets.js.map +1 -0
  128. package/dist/core/budget.d.ts +10 -0
  129. package/dist/core/budget.d.ts.map +1 -0
  130. package/dist/core/budget.js +31 -0
  131. package/dist/core/budget.js.map +1 -0
  132. package/dist/core/channels.d.ts +67 -0
  133. package/dist/core/channels.d.ts.map +1 -0
  134. package/dist/core/channels.js +136 -0
  135. package/dist/core/channels.js.map +1 -0
  136. package/dist/core/check.d.ts +25 -0
  137. package/dist/core/check.d.ts.map +1 -0
  138. package/dist/core/check.js +85 -0
  139. package/dist/core/check.js.map +1 -0
  140. package/dist/core/completion.d.ts +9 -0
  141. package/dist/core/completion.d.ts.map +1 -0
  142. package/dist/core/completion.js +68 -0
  143. package/dist/core/completion.js.map +1 -0
  144. package/dist/core/costs.d.ts +90 -0
  145. package/dist/core/costs.d.ts.map +1 -0
  146. package/dist/core/costs.js +147 -0
  147. package/dist/core/costs.js.map +1 -0
  148. package/dist/core/decoder.d.ts +7 -0
  149. package/dist/core/decoder.d.ts.map +1 -0
  150. package/dist/core/decoder.js +38 -0
  151. package/dist/core/decoder.js.map +1 -0
  152. package/dist/core/discovery.d.ts +38 -0
  153. package/dist/core/discovery.d.ts.map +1 -0
  154. package/dist/core/discovery.js +208 -0
  155. package/dist/core/discovery.js.map +1 -0
  156. package/dist/core/extension.d.ts +83 -0
  157. package/dist/core/extension.d.ts.map +1 -0
  158. package/dist/core/extension.js +429 -0
  159. package/dist/core/extension.js.map +1 -0
  160. package/dist/core/inspect.d.ts +62 -0
  161. package/dist/core/inspect.d.ts.map +1 -0
  162. package/dist/core/inspect.js +303 -0
  163. package/dist/core/inspect.js.map +1 -0
  164. package/dist/core/introspection.d.ts +8 -0
  165. package/dist/core/introspection.d.ts.map +1 -0
  166. package/dist/core/introspection.js +8 -0
  167. package/dist/core/introspection.js.map +1 -0
  168. package/dist/core/monitor.d.ts +42 -0
  169. package/dist/core/monitor.d.ts.map +1 -0
  170. package/dist/core/monitor.js +188 -0
  171. package/dist/core/monitor.js.map +1 -0
  172. package/dist/core/rent_projection.d.ts +153 -0
  173. package/dist/core/rent_projection.d.ts.map +1 -0
  174. package/dist/core/rent_projection.js +146 -0
  175. package/dist/core/rent_projection.js.map +1 -0
  176. package/dist/core/scvalTranslator.d.ts +11 -0
  177. package/dist/core/scvalTranslator.d.ts.map +1 -0
  178. package/dist/core/scvalTranslator.js +36 -0
  179. package/dist/core/scvalTranslator.js.map +1 -0
  180. package/dist/core/state_diff.d.ts +45 -0
  181. package/dist/core/state_diff.d.ts.map +1 -0
  182. package/dist/core/state_diff.js +112 -0
  183. package/dist/core/state_diff.js.map +1 -0
  184. package/dist/core/status.d.ts +24 -0
  185. package/dist/core/status.d.ts.map +1 -0
  186. package/dist/core/status.js +57 -0
  187. package/dist/core/status.js.map +1 -0
  188. package/dist/core/vault.d.ts +37 -0
  189. package/dist/core/vault.d.ts.map +1 -0
  190. package/dist/core/vault.js +179 -0
  191. package/dist/core/vault.js.map +1 -0
  192. package/dist/core/watch.d.ts +36 -0
  193. package/dist/core/watch.d.ts.map +1 -0
  194. package/dist/core/watch.js +176 -0
  195. package/dist/core/watch.js.map +1 -0
  196. package/dist/daemon/loop.d.ts +38 -0
  197. package/dist/daemon/loop.d.ts.map +1 -0
  198. package/dist/daemon/loop.js +169 -0
  199. package/dist/daemon/loop.js.map +1 -0
  200. package/dist/db/backup.d.ts +12 -0
  201. package/dist/db/backup.d.ts.map +1 -0
  202. package/dist/db/backup.js +132 -0
  203. package/dist/db/backup.js.map +1 -0
  204. package/dist/db/budget.d.ts +17 -0
  205. package/dist/db/budget.d.ts.map +1 -0
  206. package/dist/db/budget.js +31 -0
  207. package/dist/db/budget.js.map +1 -0
  208. package/dist/db/database.d.ts +6 -0
  209. package/dist/db/database.d.ts.map +1 -0
  210. package/dist/db/database.js +134 -0
  211. package/dist/db/database.js.map +1 -0
  212. package/dist/db/migrations/.gitkeep +1 -0
  213. package/dist/db/migrations/001_resource_usage_logs.sql +28 -0
  214. package/dist/db/migrator.d.ts +28 -0
  215. package/dist/db/migrator.d.ts.map +1 -0
  216. package/dist/db/migrator.js +75 -0
  217. package/dist/db/migrator.js.map +1 -0
  218. package/dist/db/repositories.d.ts +474 -0
  219. package/dist/db/repositories.d.ts.map +1 -0
  220. package/dist/db/repositories.js +809 -0
  221. package/dist/db/repositories.js.map +1 -0
  222. package/dist/db/schema.sql +207 -0
  223. package/dist/index.d.ts +3 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +47 -0
  226. package/dist/index.js.map +1 -0
  227. package/dist/lib.d.ts +20 -0
  228. package/dist/lib.d.ts.map +1 -0
  229. package/dist/lib.js +16 -0
  230. package/dist/lib.js.map +1 -0
  231. package/dist/logging/index.d.ts +22 -0
  232. package/dist/logging/index.d.ts.map +1 -0
  233. package/dist/logging/index.js +34 -0
  234. package/dist/logging/index.js.map +1 -0
  235. package/dist/logging/logger.d.ts +14 -0
  236. package/dist/logging/logger.d.ts.map +1 -0
  237. package/dist/logging/logger.js +135 -0
  238. package/dist/logging/logger.js.map +1 -0
  239. package/dist/logging/types.d.ts +10 -0
  240. package/dist/logging/types.d.ts.map +1 -0
  241. package/dist/logging/types.js +2 -0
  242. package/dist/logging/types.js.map +1 -0
  243. package/dist/mcp/index.d.ts +3 -0
  244. package/dist/mcp/index.d.ts.map +1 -0
  245. package/dist/mcp/index.js +14 -0
  246. package/dist/mcp/index.js.map +1 -0
  247. package/dist/mcp/server.d.ts +10 -0
  248. package/dist/mcp/server.d.ts.map +1 -0
  249. package/dist/mcp/server.js +51 -0
  250. package/dist/mcp/server.js.map +1 -0
  251. package/dist/mcp/tools/get-extension-costs.d.ts +4 -0
  252. package/dist/mcp/tools/get-extension-costs.d.ts.map +1 -0
  253. package/dist/mcp/tools/get-extension-costs.js +33 -0
  254. package/dist/mcp/tools/get-extension-costs.js.map +1 -0
  255. package/dist/mcp/tools/get_contract_status.d.ts +8 -0
  256. package/dist/mcp/tools/get_contract_status.d.ts.map +1 -0
  257. package/dist/mcp/tools/get_contract_status.js +33 -0
  258. package/dist/mcp/tools/get_contract_status.js.map +1 -0
  259. package/dist/rpc/client.d.ts +154 -0
  260. package/dist/rpc/client.d.ts.map +1 -0
  261. package/dist/rpc/client.js +766 -0
  262. package/dist/rpc/client.js.map +1 -0
  263. package/dist/utils/config.d.ts +51 -0
  264. package/dist/utils/config.d.ts.map +1 -0
  265. package/dist/utils/config.js +86 -0
  266. package/dist/utils/config.js.map +1 -0
  267. package/dist/utils/formatting.d.ts +10 -0
  268. package/dist/utils/formatting.d.ts.map +1 -0
  269. package/dist/utils/formatting.js +66 -0
  270. package/dist/utils/formatting.js.map +1 -0
  271. package/dist/utils/watch-config.d.ts +13 -0
  272. package/dist/utils/watch-config.d.ts.map +1 -0
  273. package/dist/utils/watch-config.js +32 -0
  274. package/dist/utils/watch-config.js.map +1 -0
  275. package/package.json +73 -0
@@ -0,0 +1,280 @@
1
+ import chalk from "chalk";
2
+ import { getDatabase } from "../db/database.js";
3
+ import { getExtensionCosts, calculateFeeAdjustedProjection, getMultiPeriodCosts } from "../core/costs.js";
4
+ import { getEntriesForContract } from "../db/repositories.js";
5
+ import { projectRentWindows, DEFAULT_FEE_PER_RENT_1KB, } from "../core/rent_projection.js";
6
+ import { StellarRpcClient } from "../rpc/client.js";
7
+ import { formatContractID } from "../utils/formatting.js";
8
+ import { loadConfig } from "../utils/config.js";
9
+ import { getLogger } from "../logging/index.js";
10
+ const logger = getLogger().child({ component: "CostsCommand" });
11
+ function padRight(str, len) {
12
+ return str.length >= len ? str : str + " ".repeat(len - str.length);
13
+ }
14
+ function padLeft(str, len) {
15
+ return str.length >= len ? str : " ".repeat(len - str.length) + str;
16
+ }
17
+ function printMultiPeriodTable(data, feeStats) {
18
+ const displayName = data.contract.name ?? formatContractID(data.contract.id);
19
+ console.log(`\n${chalk.bold("Extension Costs")} — ${chalk.cyan(displayName)}`);
20
+ console.log(` Network: ${chalk.cyan(data.contract.network)}`);
21
+ const header = ` ${padRight("Period", 12)} ${padLeft("Extensions", 12)} ${padLeft("Total XLM", 16)}`;
22
+ console.log(`\n${chalk.bold(header)}`);
23
+ console.log(` ${"─".repeat(40)}`);
24
+ for (const p of data.periods) {
25
+ const period = padRight(`${p.days} days`, 12);
26
+ const extensions = padLeft(p.totalExtensions.toString(), 12);
27
+ const cost = padLeft(p.totalCostXlm.toFixed(7), 16);
28
+ console.log(` ${period} ${chalk.cyan(extensions)} ${chalk.cyan(cost)}`);
29
+ }
30
+ console.log(`\n ${chalk.bold("Projection")}`);
31
+ console.log(` Estimated 30-day cost: ~${chalk.cyan(data.projection.adjustedProjectedCostXlm.toFixed(7))} XLM`);
32
+ if (feeStats) {
33
+ console.log(` Live base fee: ${chalk.cyan(feeStats.baseFeeStroops.toString())} stroops`);
34
+ console.log(` Surge multiplier: ${chalk.cyan(`${data.projection.surgePricingMultiplier.toFixed(2)}x`)}`);
35
+ }
36
+ }
37
+ /**
38
+ * Fallback entry size when mem_bytes is not recorded for any extension of
39
+ * an entry. 1 KB is the smallest meaningful allocation for a Soroban entry
40
+ * and keeps projections conservative.
41
+ */
42
+ const DEFAULT_ENTRY_SIZE_BYTES = 1024;
43
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
44
+ /**
45
+ * Render a single RentWindowsResult as a table under the "Forecasted Rent"
46
+ * section. Returns an array of console lines (without printing) so callers can
47
+ * test the output easily.
48
+ *
49
+ * @param result - Output of `projectRentWindows` for one entry.
50
+ * @param entryType - Human-readable entry type label (instance, wasm, …).
51
+ * @param budget - Monthly budget in XLM (optional). When provided, windows
52
+ * that exceed the budget are flagged in red.
53
+ */
54
+ export function formatForecastedRentEntry(result, entryType, budget) {
55
+ const lines = [];
56
+ lines.push(` ${chalk.dim("Entry type:")} ${entryType}`);
57
+ for (const window of result.windows) {
58
+ const xlm = window.estimatedFeeXlm.toFixed(7);
59
+ const overBudget = budget !== undefined && window.estimatedFeeXlm > budget;
60
+ const budgetFlag = overBudget
61
+ ? " " + chalk.bold.red(`⚠ OVER BUDGET (limit: ${budget.toFixed(7)} XLM)`)
62
+ : "";
63
+ const dayLabel = `${window.days}-day`;
64
+ const costStr = overBudget
65
+ ? chalk.red(`~${xlm} XLM`)
66
+ : chalk.cyan(`~${xlm} XLM`);
67
+ lines.push(` ${chalk.bold(dayLabel)}: ${costStr}${budgetFlag}`);
68
+ }
69
+ return lines;
70
+ }
71
+ // ─── Command registration ─────────────────────────────────────────────────────
72
+ export function registerCostsCommand(program) {
73
+ program
74
+ .command("costs <contractId>")
75
+ .description("Show rent costs and extension history for a contract")
76
+ .option("--period <days>", "Show costs for the last N days", "30")
77
+ .option("--all", "Show all extension history")
78
+ .option("--json", "Output machine-readable JSON")
79
+ .option("--monthly-budget <xlm>", "Monthly budget in XLM — highlight forecast windows that exceed this limit")
80
+ .action(async (contractId, options = {}) => {
81
+ options = options || {};
82
+ try {
83
+ const db = getDatabase();
84
+ const days = options.all ? undefined : parseInt(options.period ?? "30", 10);
85
+ if (days !== undefined && (!Number.isInteger(days) || days <= 0)) {
86
+ if (options.json) {
87
+ console.log(JSON.stringify({
88
+ success: false,
89
+ error: "invalid_period",
90
+ period: options.period,
91
+ }));
92
+ }
93
+ else {
94
+ console.error(chalk.red("--period must be a positive integer number of days"));
95
+ }
96
+ process.exit(1);
97
+ return;
98
+ }
99
+ // Resolve monthly budget: CLI flag > config file
100
+ const config = loadConfig();
101
+ let monthlyBudget;
102
+ if (options.monthlyBudget !== undefined) {
103
+ const parsed = parseFloat(options.monthlyBudget);
104
+ if (!Number.isFinite(parsed) || parsed <= 0) {
105
+ if (options.json) {
106
+ console.log(JSON.stringify({
107
+ success: false,
108
+ error: "invalid_monthly_budget",
109
+ monthlyBudget: options.monthlyBudget,
110
+ }));
111
+ }
112
+ else {
113
+ console.error(chalk.red("--monthly-budget must be a positive number"));
114
+ }
115
+ process.exit(1);
116
+ return;
117
+ }
118
+ monthlyBudget = parsed;
119
+ }
120
+ else {
121
+ monthlyBudget = config.monthlyBudgetXlm;
122
+ }
123
+ const result = getExtensionCosts(db, contractId, options.all ? { all: true } : { period: days });
124
+ if (!result.success) {
125
+ if (options.json) {
126
+ console.log(JSON.stringify(result));
127
+ }
128
+ else if (result.error === "contract_not_found") {
129
+ console.error(chalk.red(`Contract ${formatContractID(contractId)} not found. Run 'sorokeep watch' first.`));
130
+ }
131
+ else {
132
+ console.error(chalk.red("An error occurred computing extension costs."));
133
+ }
134
+ process.exit(1);
135
+ return;
136
+ }
137
+ const { data } = result;
138
+ if (options.json) {
139
+ console.log(JSON.stringify(data, null, 2));
140
+ return;
141
+ }
142
+ const displayName = data.contract.name ?? formatContractID(contractId);
143
+ let feeStats;
144
+ try {
145
+ feeStats = await new StellarRpcClient(data.contract.network).getFeeStats();
146
+ }
147
+ catch (error) {
148
+ const message = error instanceof Error ? error.message : String(error);
149
+ logger.warn("Unable to fetch live fee stats; using historical projection", { error: message });
150
+ }
151
+ const multiResult = getMultiPeriodCosts(db, contractId, feeStats);
152
+ if (multiResult.success) {
153
+ printMultiPeriodTable(multiResult.data, feeStats);
154
+ }
155
+ console.log(`\n${chalk.bold("Extension History")} — ${chalk.cyan(displayName)} (${data.period.label})`);
156
+ console.log(` Network: ${chalk.cyan(data.contract.network)}`);
157
+ if (data.message) {
158
+ console.log(chalk.dim(`\n ${data.message}`));
159
+ return;
160
+ }
161
+ console.log(`\n ${chalk.bold("Summary")}`);
162
+ console.log(` Total extensions: ${chalk.cyan(data.summary.totalExtensions.toString())}`);
163
+ console.log(` Total cost: ${chalk.cyan(data.summary.totalCostXlm.toFixed(7))} XLM`);
164
+ console.log(`\n ${chalk.bold("By Entry Type")}`);
165
+ for (const [type, entryData] of Object.entries(data.byEntryType)) {
166
+ console.log(` ${type}: ${entryData.count} extensions (${entryData.costXlm.toFixed(7)} XLM)`);
167
+ }
168
+ if (days && data.summary.totalExtensions > 0) {
169
+ const projection = calculateFeeAdjustedProjection(data.summary.totalCostXlm, days, feeStats);
170
+ console.log(`\n ${chalk.bold("Projection")}`);
171
+ console.log(` Estimated 30-day cost: ~${chalk.cyan(projection.adjustedProjectedCostXlm.toFixed(7))} XLM`);
172
+ if (feeStats) {
173
+ console.log(` Live base fee: ${chalk.cyan(feeStats.baseFeeStroops.toString())} stroops`);
174
+ console.log(` Surge multiplier: ${chalk.cyan(`${projection.surgePricingMultiplier.toFixed(2)}x`)}`);
175
+ }
176
+ // ── Forecasted Rent (30/60/90-day windows per entry) ──────────
177
+ const entries = getEntriesForContract(db, contractId);
178
+ // Build a map of entry_type → max observed mem_bytes from
179
+ // recent extensions. Falls back to DEFAULT_ENTRY_SIZE_BYTES
180
+ // when no size information is available.
181
+ const sizeByEntryType = new Map();
182
+ for (const record of data.recentExtensions) {
183
+ // recentExtensions carries the entry_type label; we need
184
+ // the raw entry_type so look it up from the DB entries.
185
+ const dbEntry = entries.find((e) => (e.label ?? e.entry_type) === record.entryLabel) ?? entries.find((e) => e.entry_type === record.entryType);
186
+ if (!dbEntry)
187
+ continue;
188
+ // mem_bytes on the extension record is the closest proxy
189
+ // for the on-chain entry size we have stored locally.
190
+ // The raw record doesn't carry mem_bytes directly through
191
+ // the ExtensionCostDetail shape — query the history rows
192
+ // that we already have in data.recentExtensions; however
193
+ // that shape has no mem_bytes field. So we query the DB
194
+ // entries table for a stored size_bytes (doesn't exist) or
195
+ // fall back to the DEFAULT. The actual mem_bytes used during
196
+ // the extension transaction is in extension_history.mem_bytes
197
+ // but wasn't surfaced. We access it here via the DB directly.
198
+ if (!sizeByEntryType.has(dbEntry.entry_type)) {
199
+ const row = db
200
+ .prepare(`SELECT mem_bytes FROM extension_history
201
+ WHERE contract_entry_id = ? AND mem_bytes IS NOT NULL
202
+ ORDER BY executed_at DESC LIMIT 1`)
203
+ .get(dbEntry.id);
204
+ sizeByEntryType.set(dbEntry.entry_type, row?.mem_bytes ?? DEFAULT_ENTRY_SIZE_BYTES);
205
+ }
206
+ }
207
+ // Also account for entry types that exist in the DB but might
208
+ // not have appeared in recentExtensions (e.g. --period filtered
209
+ // them out).
210
+ for (const entry of entries) {
211
+ if (!sizeByEntryType.has(entry.entry_type)) {
212
+ const row = db
213
+ .prepare(`SELECT mem_bytes FROM extension_history
214
+ WHERE contract_entry_id = ? AND mem_bytes IS NOT NULL
215
+ ORDER BY executed_at DESC LIMIT 1`)
216
+ .get(entry.id);
217
+ sizeByEntryType.set(entry.entry_type, row?.mem_bytes ?? DEFAULT_ENTRY_SIZE_BYTES);
218
+ }
219
+ }
220
+ // Only show the section for entry types that actually had
221
+ // extensions in this period.
222
+ const activeEntryTypes = Object.keys(data.byEntryType);
223
+ if (activeEntryTypes.length > 0) {
224
+ const feePerRent1kb = feeStats
225
+ ? DEFAULT_FEE_PER_RENT_1KB // TODO: fetch from network config
226
+ : DEFAULT_FEE_PER_RENT_1KB;
227
+ console.log(`\n ${chalk.bold("Forecasted Rent")} (based on Soroban rent formula)`);
228
+ if (monthlyBudget !== undefined) {
229
+ console.log(` Monthly budget: ${chalk.cyan(monthlyBudget.toFixed(7))} XLM`);
230
+ }
231
+ let anyBreach = false;
232
+ for (const entryType of activeEntryTypes) {
233
+ const entrySizeBytes = sizeByEntryType.get(entryType) ?? DEFAULT_ENTRY_SIZE_BYTES;
234
+ const isPersistent = entryType === "instance" ||
235
+ entryType === "wasm" ||
236
+ entryType === "persistent";
237
+ const isCodeEntry = entryType === "wasm";
238
+ const forecast = projectRentWindows({
239
+ entrySizeBytes,
240
+ feePerRent1kb,
241
+ isPersistent,
242
+ isCodeEntry,
243
+ });
244
+ const lines = formatForecastedRentEntry(forecast, entryType, monthlyBudget);
245
+ for (const line of lines) {
246
+ console.log(line);
247
+ }
248
+ // Track whether any window breaches the budget
249
+ if (monthlyBudget !== undefined &&
250
+ forecast.windows.some((w) => w.estimatedFeeXlm > monthlyBudget)) {
251
+ anyBreach = true;
252
+ }
253
+ }
254
+ if (anyBreach) {
255
+ console.log(chalk.bold.red(`\n ⚠ One or more forecast windows exceed your monthly budget of ` +
256
+ `${monthlyBudget.toFixed(7)} XLM. ` +
257
+ `Consider raising TTLs or reviewing entry sizes to reduce rent costs.`));
258
+ }
259
+ }
260
+ }
261
+ console.log(`\n ${chalk.bold("Recent Extensions")}`);
262
+ const recent = options.all ? data.recentExtensions : data.recentExtensions.slice(0, 10);
263
+ for (const record of recent) {
264
+ const cost = record.costXlm !== null ? `${record.costXlm.toFixed(7)} XLM` : "N/A";
265
+ console.log(` ${chalk.dim(record.executedAt)} ${record.entryLabel}: ${record.oldTtlFormatted} → ${record.newTtlFormatted} (${cost})`);
266
+ console.log(` ${chalk.dim(`tx: ${record.txHash.slice(0, 16)}...`)}`);
267
+ }
268
+ if (!options.all && data.recentExtensions.length > 10) {
269
+ console.log(chalk.dim(`\n ... and ${data.recentExtensions.length - 10} more. Use --all to see everything.`));
270
+ }
271
+ }
272
+ catch (error) {
273
+ const msg = error instanceof Error ? error.message : String(error);
274
+ logger.error("Costs command failed", { error: msg });
275
+ console.error(chalk.red(`Error: ${msg}`));
276
+ process.exit(1);
277
+ }
278
+ });
279
+ }
280
+ //# sourceMappingURL=costs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"costs.js","sourceRoot":"","sources":["../../src/commands/costs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDaemonCommand(program: Command): void;
3
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwG5D"}
@@ -0,0 +1,93 @@
1
+ import chalk from "chalk";
2
+ import { getDatabase } from "../db/database.js";
3
+ import { startDaemon, stopDaemon } from "../daemon/loop.js";
4
+ import { configureLogger, getLogger } from "../logging/index.js";
5
+ import { loadConfig } from "../utils/config.js";
6
+ export function registerDaemonCommand(program) {
7
+ program
8
+ .command("daemon")
9
+ .description("Start the monitoring daemon — polls contracts at a fixed interval")
10
+ .option("--network <network>", "Stellar network to monitor")
11
+ .option("--interval <ms>", "Polling interval in milliseconds")
12
+ .option("-r, --rpc-url <url>", "Custom RPC endpoint URL")
13
+ .option("--log-format <format>", "Log output format: 'pretty' (human-readable) or 'json' (structured)", "pretty")
14
+ .action(async (options) => {
15
+ const config = loadConfig();
16
+ const network = options.network ?? config.network;
17
+ const intervalMs = options.interval !== undefined
18
+ ? parseInt(options.interval, 10)
19
+ : config.pollingIntervalSeconds * 1000;
20
+ if (isNaN(intervalMs) || intervalMs < 10000) {
21
+ console.log(chalk.red("Error: --interval must be a number >= 10000 (10 seconds)"));
22
+ process.exit(1);
23
+ }
24
+ if (options.logFormat !== "pretty" && options.logFormat !== "json") {
25
+ console.log(chalk.red("Error: --log-format must be either 'pretty' or 'json'"));
26
+ process.exit(1);
27
+ }
28
+ // Reconfigure the global logger for the daemon process so every
29
+ // component (this command and the loop) honours the chosen format.
30
+ configureLogger({ mode: "daemon", format: options.logFormat });
31
+ const logger = getLogger().child({ component: "DaemonCommand" });
32
+ let db;
33
+ try {
34
+ db = getDatabase();
35
+ }
36
+ catch (err) {
37
+ const msg = err instanceof Error ? err.message : String(err);
38
+ console.log(chalk.red(`Failed to open database: ${msg}`));
39
+ logger.error("Database initialization failed", { error: msg });
40
+ process.exit(1);
41
+ }
42
+ console.log();
43
+ console.log(chalk.bold(" Sorokeep — Daemon"));
44
+ console.log(` Network: ${chalk.cyan(network)}`);
45
+ console.log(` Interval: ${chalk.cyan(Math.floor(intervalMs / 1000) + "s")}`);
46
+ if (options.rpcUrl) {
47
+ console.log(` RPC: ${chalk.cyan(options.rpcUrl)}`);
48
+ }
49
+ console.log();
50
+ console.log(chalk.dim(" Press Ctrl+C to stop.\n"));
51
+ // ── Graceful shutdown ────────────────────────────────────
52
+ const shutdown = () => {
53
+ console.log(chalk.yellow("\n Shutting down daemon..."));
54
+ stopDaemon();
55
+ try {
56
+ db.close();
57
+ }
58
+ catch {
59
+ // DB may already be closed — not an error worth surfacing
60
+ }
61
+ process.exit(0);
62
+ };
63
+ process.on("SIGINT", shutdown);
64
+ process.on("SIGTERM", shutdown);
65
+ // ── Start the loop ───────────────────────────────────────
66
+ await startDaemon(db, network, {
67
+ intervalMs,
68
+ rpcUrl: options.rpcUrl,
69
+ feeSponsorSecret: config.feeSponsorSecret,
70
+ onCycle: (result, error) => {
71
+ const timestamp = new Date().toLocaleTimeString();
72
+ if (error) {
73
+ console.log(chalk.dim(` [${timestamp}]`) +
74
+ chalk.red(` Cycle failed: ${error.message}`));
75
+ return;
76
+ }
77
+ if (result) {
78
+ const parts = [
79
+ `Checked ${result.contractsChecked} contract(s)`,
80
+ `updated ${result.entriesUpdated} entries`,
81
+ `${result.thresholdsCrossed} threshold(s) crossed`,
82
+ `${result.alertsResolved} resolved`,
83
+ ];
84
+ if (result.errors.length > 0) {
85
+ parts.push(chalk.red(`${result.errors.length} error(s)`));
86
+ }
87
+ console.log(chalk.dim(` [${timestamp}]`) + ` ${parts.join(", ")}`);
88
+ }
89
+ },
90
+ });
91
+ });
92
+ }
93
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDbCommand(program: Command): void;
3
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8HxD"}
@@ -0,0 +1,140 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import chalk from "chalk";
5
+ import { getDatabase, vacuumDatabase } from "../db/database.js";
6
+ import { Migrator } from "../db/migrator.js";
7
+ import { exportDatabase, importDatabase } from "../db/backup.js";
8
+ import { getLogger } from "../logging/index.js";
9
+ const logger = getLogger().child({ component: "DbCommand" });
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const migrationsDir = path.resolve(__dirname, "../db/migrations");
13
+ export function registerDbCommand(program) {
14
+ const dbCommand = program
15
+ .command("db")
16
+ .description("Backup, restore, and maintain database state");
17
+ dbCommand
18
+ .command("export")
19
+ .description("Export tracked database tables as JSON to stdout")
20
+ .action(() => {
21
+ try {
22
+ const db = getDatabase();
23
+ const backup = exportDatabase(db);
24
+ process.stdout.write(`${JSON.stringify(backup, null, 2)}\n`);
25
+ }
26
+ catch (error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ logger.error("Database export failed", { error: message });
29
+ console.error(chalk.red(`Error: ${message}`));
30
+ process.exit(1);
31
+ }
32
+ });
33
+ dbCommand
34
+ .command("import <file>")
35
+ .description("Import tracked database tables from a JSON backup file")
36
+ .action((file) => {
37
+ try {
38
+ const db = getDatabase();
39
+ const raw = fs.readFileSync(file, "utf-8");
40
+ const backup = JSON.parse(raw);
41
+ importDatabase(db, backup);
42
+ console.log(chalk.green("Database import complete"));
43
+ }
44
+ catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ logger.error("Database import failed", { error: message, file });
47
+ console.error(chalk.red(`Error: ${message}`));
48
+ process.exit(1);
49
+ }
50
+ });
51
+ dbCommand
52
+ .command("status")
53
+ .description("Show status of database migrations")
54
+ .action(() => {
55
+ try {
56
+ const db = getDatabase();
57
+ const migrator = new Migrator(db, migrationsDir);
58
+ const applied = migrator.getAppliedMigrations();
59
+ const pending = migrator.getPendingMigrations();
60
+ console.log(chalk.bold("Applied Migrations:"));
61
+ if (applied.length === 0) {
62
+ console.log(" No migrations applied yet.");
63
+ }
64
+ else {
65
+ applied.forEach((version) => {
66
+ console.log(` - Version ${version}`);
67
+ });
68
+ }
69
+ console.log();
70
+ console.log(chalk.bold("Pending Migrations:"));
71
+ if (pending.length === 0) {
72
+ console.log(" No pending migrations.");
73
+ }
74
+ else {
75
+ pending.forEach((p) => {
76
+ console.log(` - Version ${p.version} (${p.filename})`);
77
+ });
78
+ }
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ logger.error("Database status failed", { error: message });
83
+ console.error(chalk.red(`Error: ${message}`));
84
+ process.exit(1);
85
+ }
86
+ });
87
+ dbCommand
88
+ .command("migrate")
89
+ .description("Run all pending database migrations")
90
+ .action(() => {
91
+ try {
92
+ const db = getDatabase();
93
+ const migrator = new Migrator(db, migrationsDir);
94
+ const pending = migrator.getPendingMigrations();
95
+ if (pending.length === 0) {
96
+ console.log("No pending migrations. Database is up to date.");
97
+ }
98
+ else {
99
+ console.log(`Running ${pending.length} pending migration(s)...`);
100
+ migrator.run();
101
+ console.log(chalk.green("Migrations applied successfully."));
102
+ }
103
+ const applied = migrator.getAppliedMigrations();
104
+ console.log(chalk.bold("Applied Migrations:"));
105
+ applied.forEach((version) => {
106
+ console.log(` - Version ${version}`);
107
+ });
108
+ }
109
+ catch (error) {
110
+ const message = error instanceof Error ? error.message : String(error);
111
+ logger.error("Database migration failed", { error: message });
112
+ console.error(chalk.red(`Error: ${message}`));
113
+ process.exit(1);
114
+ }
115
+ });
116
+ dbCommand
117
+ .command("vacuum")
118
+ .description("Run VACUUM to reclaim space and optimize the database")
119
+ .action(() => {
120
+ try {
121
+ const db = getDatabase();
122
+ const success = vacuumDatabase(db);
123
+ if (success) {
124
+ console.log(chalk.green("Database vacuum completed successfully."));
125
+ }
126
+ else {
127
+ logger.error("Database vacuum failed or was skipped because of an active transaction or lock");
128
+ console.error(chalk.red("Error: Database vacuum failed or was skipped because of an active transaction or lock."));
129
+ process.exit(1);
130
+ }
131
+ }
132
+ catch (error) {
133
+ const message = error instanceof Error ? error.message : String(error);
134
+ logger.error("Database vacuum failed with exception", { error: message });
135
+ console.error(chalk.red(`Error: ${message}`));
136
+ process.exit(1);
137
+ }
138
+ });
139
+ }
140
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAuB,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;AAE7D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAElE,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAC9C,MAAM,SAAS,GAAG,OAAO;SACpB,OAAO,CAAC,IAAI,CAAC;SACb,WAAW,CAAC,8CAA8C,CAAC,CAAC;IAEjE,SAAS;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,kDAAkD,CAAC;SAC/D,MAAM,CAAC,GAAG,EAAE;QACT,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;YAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC3D,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;IAEP,SAAS;SACJ,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,wDAAwD,CAAC;SACrE,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE;QACrB,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;YACjD,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACjE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;IAEP,SAAS;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,GAAG,EAAE;QACT,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YAEhD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBACxB,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;gBAC1C,CAAC,CAAC,CAAC;YACP,CAAC;YAED,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBAClB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAC5D,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC3D,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;IAEP,SAAS;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CAAC,GAAG,EAAE;QACT,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YAEhD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,MAAM,0BAA0B,CAAC,CAAC;gBACjE,QAAQ,CAAC,GAAG,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;YACjE,CAAC;YAED,MAAM,OAAO,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBACxB,OAAO,CAAC,GAAG,CAAC,eAAe,OAAO,EAAE,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9D,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;IAEP,SAAS;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,GAAG,EAAE;QACT,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACxE,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC;gBAC/F,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,wFAAwF,CAAC,CAAC,CAAC;gBACnH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,uCAAuC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAC1E,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerGuardCommand(program: Command): void;
3
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/commands/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0M3D"}