@telepat/snoopy 0.1.4

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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/dist/src/cli/commands/analytics.d.ts +3 -0
  4. package/dist/src/cli/commands/analytics.js +147 -0
  5. package/dist/src/cli/commands/analytics.js.map +1 -0
  6. package/dist/src/cli/commands/daemon.d.ts +5 -0
  7. package/dist/src/cli/commands/daemon.js +85 -0
  8. package/dist/src/cli/commands/daemon.js.map +1 -0
  9. package/dist/src/cli/commands/doctor.d.ts +1 -0
  10. package/dist/src/cli/commands/doctor.js +106 -0
  11. package/dist/src/cli/commands/doctor.js.map +1 -0
  12. package/dist/src/cli/commands/errors.d.ts +3 -0
  13. package/dist/src/cli/commands/errors.js +51 -0
  14. package/dist/src/cli/commands/errors.js.map +1 -0
  15. package/dist/src/cli/commands/export.d.ts +1 -0
  16. package/dist/src/cli/commands/export.js +48 -0
  17. package/dist/src/cli/commands/export.js.map +1 -0
  18. package/dist/src/cli/commands/job.d.ts +16 -0
  19. package/dist/src/cli/commands/job.js +350 -0
  20. package/dist/src/cli/commands/job.js.map +1 -0
  21. package/dist/src/cli/commands/logs.d.ts +3 -0
  22. package/dist/src/cli/commands/logs.js +44 -0
  23. package/dist/src/cli/commands/logs.js.map +1 -0
  24. package/dist/src/cli/commands/selection.d.ts +19 -0
  25. package/dist/src/cli/commands/selection.js +182 -0
  26. package/dist/src/cli/commands/selection.js.map +1 -0
  27. package/dist/src/cli/commands/settings.d.ts +1 -0
  28. package/dist/src/cli/commands/settings.js +31 -0
  29. package/dist/src/cli/commands/settings.js.map +1 -0
  30. package/dist/src/cli/commands/startup.d.ts +5 -0
  31. package/dist/src/cli/commands/startup.js +26 -0
  32. package/dist/src/cli/commands/startup.js.map +1 -0
  33. package/dist/src/cli/flows/jobAddFlow.d.ts +26 -0
  34. package/dist/src/cli/flows/jobAddFlow.js +209 -0
  35. package/dist/src/cli/flows/jobAddFlow.js.map +1 -0
  36. package/dist/src/cli/flows/settingsFlow.d.ts +15 -0
  37. package/dist/src/cli/flows/settingsFlow.js +180 -0
  38. package/dist/src/cli/flows/settingsFlow.js.map +1 -0
  39. package/dist/src/cli/flows/settingsFlowModel.d.ts +47 -0
  40. package/dist/src/cli/flows/settingsFlowModel.js +143 -0
  41. package/dist/src/cli/flows/settingsFlowModel.js.map +1 -0
  42. package/dist/src/cli/index.d.ts +2 -0
  43. package/dist/src/cli/index.js +138 -0
  44. package/dist/src/cli/index.js.map +1 -0
  45. package/dist/src/cli/ui/consoleUi.d.ts +13 -0
  46. package/dist/src/cli/ui/consoleUi.js +165 -0
  47. package/dist/src/cli/ui/consoleUi.js.map +1 -0
  48. package/dist/src/cli/ui/time.d.ts +9 -0
  49. package/dist/src/cli/ui/time.js +35 -0
  50. package/dist/src/cli/ui/time.js.map +1 -0
  51. package/dist/src/index.d.ts +1 -0
  52. package/dist/src/index.js +2 -0
  53. package/dist/src/index.js.map +1 -0
  54. package/dist/src/scripts/e2eSmoke.d.ts +1 -0
  55. package/dist/src/scripts/e2eSmoke.js +102 -0
  56. package/dist/src/scripts/e2eSmoke.js.map +1 -0
  57. package/dist/src/services/analytics/analyticsService.d.ts +50 -0
  58. package/dist/src/services/analytics/analyticsService.js +88 -0
  59. package/dist/src/services/analytics/analyticsService.js.map +1 -0
  60. package/dist/src/services/daemonControl.d.ts +12 -0
  61. package/dist/src/services/daemonControl.js +58 -0
  62. package/dist/src/services/daemonControl.js.map +1 -0
  63. package/dist/src/services/db/repositories/jobsRepo.d.ts +19 -0
  64. package/dist/src/services/db/repositories/jobsRepo.js +164 -0
  65. package/dist/src/services/db/repositories/jobsRepo.js.map +1 -0
  66. package/dist/src/services/db/repositories/runsRepo.d.ts +58 -0
  67. package/dist/src/services/db/repositories/runsRepo.js +190 -0
  68. package/dist/src/services/db/repositories/runsRepo.js.map +1 -0
  69. package/dist/src/services/db/repositories/scanItemsRepo.d.ts +69 -0
  70. package/dist/src/services/db/repositories/scanItemsRepo.js +176 -0
  71. package/dist/src/services/db/repositories/scanItemsRepo.js.map +1 -0
  72. package/dist/src/services/db/repositories/settingsRepo.d.ts +14 -0
  73. package/dist/src/services/db/repositories/settingsRepo.js +132 -0
  74. package/dist/src/services/db/repositories/settingsRepo.js.map +1 -0
  75. package/dist/src/services/db/sqlite.d.ts +2 -0
  76. package/dist/src/services/db/sqlite.js +192 -0
  77. package/dist/src/services/db/sqlite.js.map +1 -0
  78. package/dist/src/services/export/csvResults.d.ts +10 -0
  79. package/dist/src/services/export/csvResults.js +42 -0
  80. package/dist/src/services/export/csvResults.js.map +1 -0
  81. package/dist/src/services/logging/logReader.d.ts +4 -0
  82. package/dist/src/services/logging/logReader.js +230 -0
  83. package/dist/src/services/logging/logReader.js.map +1 -0
  84. package/dist/src/services/logging/logRotation.d.ts +1 -0
  85. package/dist/src/services/logging/logRotation.js +30 -0
  86. package/dist/src/services/logging/logRotation.js.map +1 -0
  87. package/dist/src/services/logging/runLogger.d.ts +9 -0
  88. package/dist/src/services/logging/runLogger.js +42 -0
  89. package/dist/src/services/logging/runLogger.js.map +1 -0
  90. package/dist/src/services/openrouter/client.d.ts +60 -0
  91. package/dist/src/services/openrouter/client.js +437 -0
  92. package/dist/src/services/openrouter/client.js.map +1 -0
  93. package/dist/src/services/openrouter/prompts.d.ts +5 -0
  94. package/dist/src/services/openrouter/prompts.js +48 -0
  95. package/dist/src/services/openrouter/prompts.js.map +1 -0
  96. package/dist/src/services/reddit/client.d.ts +25 -0
  97. package/dist/src/services/reddit/client.js +186 -0
  98. package/dist/src/services/reddit/client.js.map +1 -0
  99. package/dist/src/services/scheduler/cronScheduler.d.ts +11 -0
  100. package/dist/src/services/scheduler/cronScheduler.js +76 -0
  101. package/dist/src/services/scheduler/cronScheduler.js.map +1 -0
  102. package/dist/src/services/scheduler/jobRunner.d.ts +76 -0
  103. package/dist/src/services/scheduler/jobRunner.js +414 -0
  104. package/dist/src/services/scheduler/jobRunner.js.map +1 -0
  105. package/dist/src/services/scheduler/jobRunnerStub.d.ts +5 -0
  106. package/dist/src/services/scheduler/jobRunnerStub.js +11 -0
  107. package/dist/src/services/scheduler/jobRunnerStub.js.map +1 -0
  108. package/dist/src/services/security/secretStore.d.ts +6 -0
  109. package/dist/src/services/security/secretStore.js +193 -0
  110. package/dist/src/services/security/secretStore.js.map +1 -0
  111. package/dist/src/services/startup/index.d.ts +13 -0
  112. package/dist/src/services/startup/index.js +120 -0
  113. package/dist/src/services/startup/index.js.map +1 -0
  114. package/dist/src/services/startup/linuxCronFallback.d.ts +2 -0
  115. package/dist/src/services/startup/linuxCronFallback.js +29 -0
  116. package/dist/src/services/startup/linuxCronFallback.js.map +1 -0
  117. package/dist/src/services/startup/linuxSystemd.d.ts +3 -0
  118. package/dist/src/services/startup/linuxSystemd.js +47 -0
  119. package/dist/src/services/startup/linuxSystemd.js.map +1 -0
  120. package/dist/src/services/startup/macosLaunchd.d.ts +2 -0
  121. package/dist/src/services/startup/macosLaunchd.js +40 -0
  122. package/dist/src/services/startup/macosLaunchd.js.map +1 -0
  123. package/dist/src/services/startup/windowsRunFallback.d.ts +2 -0
  124. package/dist/src/services/startup/windowsRunFallback.js +17 -0
  125. package/dist/src/services/startup/windowsRunFallback.js.map +1 -0
  126. package/dist/src/services/startup/windowsTaskScheduler.d.ts +2 -0
  127. package/dist/src/services/startup/windowsTaskScheduler.js +16 -0
  128. package/dist/src/services/startup/windowsTaskScheduler.js.map +1 -0
  129. package/dist/src/types/job.d.ts +34 -0
  130. package/dist/src/types/job.js +2 -0
  131. package/dist/src/types/job.js.map +1 -0
  132. package/dist/src/types/settings.d.ts +35 -0
  133. package/dist/src/types/settings.js +8 -0
  134. package/dist/src/types/settings.js.map +1 -0
  135. package/dist/src/ui/components/AppFrame.d.ts +17 -0
  136. package/dist/src/ui/components/AppFrame.js +26 -0
  137. package/dist/src/ui/components/AppFrame.js.map +1 -0
  138. package/dist/src/ui/components/CliHeader.d.ts +8 -0
  139. package/dist/src/ui/components/CliHeader.js +8 -0
  140. package/dist/src/ui/components/CliHeader.js.map +1 -0
  141. package/dist/src/ui/components/SubredditMultiSelect.d.ts +7 -0
  142. package/dist/src/ui/components/SubredditMultiSelect.js +91 -0
  143. package/dist/src/ui/components/SubredditMultiSelect.js.map +1 -0
  144. package/dist/src/ui/components/TextPrompt.d.ts +10 -0
  145. package/dist/src/ui/components/TextPrompt.js +13 -0
  146. package/dist/src/ui/components/TextPrompt.js.map +1 -0
  147. package/dist/src/ui/components/YesNoSelector.d.ts +10 -0
  148. package/dist/src/ui/components/YesNoSelector.js +25 -0
  149. package/dist/src/ui/components/YesNoSelector.js.map +1 -0
  150. package/dist/src/ui/components/subredditOptions.d.ts +5 -0
  151. package/dist/src/ui/components/subredditOptions.js +14 -0
  152. package/dist/src/ui/components/subredditOptions.js.map +1 -0
  153. package/dist/src/ui/components/yesNoSelectorModel.d.ts +9 -0
  154. package/dist/src/ui/components/yesNoSelectorModel.js +23 -0
  155. package/dist/src/ui/components/yesNoSelectorModel.js.map +1 -0
  156. package/dist/src/ui/theme.d.ts +26 -0
  157. package/dist/src/ui/theme.js +37 -0
  158. package/dist/src/ui/theme.js.map +1 -0
  159. package/dist/src/utils/logger.d.ts +5 -0
  160. package/dist/src/utils/logger.js +15 -0
  161. package/dist/src/utils/logger.js.map +1 -0
  162. package/dist/src/utils/notify.d.ts +6 -0
  163. package/dist/src/utils/notify.js +14 -0
  164. package/dist/src/utils/notify.js.map +1 -0
  165. package/dist/src/utils/paths.d.ts +10 -0
  166. package/dist/src/utils/paths.js +24 -0
  167. package/dist/src/utils/paths.js.map +1 -0
  168. package/dist/src/utils/scanLogFormatting.d.ts +26 -0
  169. package/dist/src/utils/scanLogFormatting.js +60 -0
  170. package/dist/src/utils/scanLogFormatting.js.map +1 -0
  171. package/package.json +74 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 snoopy contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ ```text
2
+ ┌─┐┌┐┌┌─┐┌─┐┌─┐┬ ┬
3
+ └─┐││││ ││ │├─┘└┬┘
4
+ └─┘┘└┘└─┘└─┘┴ ┴
5
+ ```
6
+
7
+ # Monitor Reddit Conversations With AI
8
+
9
+ [![Build](https://img.shields.io/github/actions/workflow/status/telepat-io/snoopy/ci.yml?branch=main&label=build)](https://github.com/telepat-io/snoopy/actions/workflows/ci.yml)
10
+ [![Coverage](https://img.shields.io/badge/coverage-94.6%25-brightgreen)](#development)
11
+ [![npm](https://img.shields.io/npm/v/@telepat/snoopy)](https://www.npmjs.com/package/@telepat/snoopy)
12
+
13
+ 📖 [Full documentation](https://docs.telepat.io/ideon/)
14
+
15
+ Snoopy helps you monitor Reddit for high-intent conversations that match your business goals.
16
+
17
+ Define what you care about in plain language, let Snoopy create a monitoring job, and continuously scan and qualify posts/comments so you can focus on response and outreach.
18
+
19
+ ## Why Use Snoopy
20
+
21
+ - Turn broad Reddit traffic into a focused stream of opportunities.
22
+ - Define qualification logic once, then run continuously.
23
+ - Trigger manual runs when you want quick validation.
24
+ - Track run analytics (discovered/new/qualified items, token usage, cost estimate).
25
+ - Run cross-platform with startup-on-reboot support.
26
+
27
+ ## What It Does
28
+
29
+ - Interactive job creation flow from natural-language criteria.
30
+ - AI-assisted clarification and job spec generation.
31
+ - Qualification against your prompt for posts (and comments when enabled).
32
+ - Local SQLite persistence for jobs, runs, and scan items.
33
+ - Built-in daemon for scheduled scanning (cron expressions).
34
+ - On-demand CSV export of qualified results per job.
35
+ - Startup registration for macOS, Linux, and Windows.
36
+ - Health checks via the doctor command.
37
+
38
+ ## Install
39
+
40
+ Requirements:
41
+ - Node.js 20+
42
+ - npm 10+
43
+
44
+ From npm:
45
+
46
+ ```bash
47
+ npm install -g @telepat/snoopy
48
+ snoopy --help
49
+ ```
50
+
51
+ From source:
52
+
53
+ ```bash
54
+ npm install
55
+ npm run build
56
+ npm link
57
+ ```
58
+
59
+ For first-time onboarding (OpenRouter key setup, first `job add`, and verification), see [Installation & Setup](docs/installation-and-setup.md).
60
+
61
+ ## Development
62
+
63
+ Run without a global install (contributors):
64
+
65
+ ```bash
66
+ npm run dev -- --help
67
+ ```
68
+
69
+ Core validation commands:
70
+
71
+ ```bash
72
+ npm run lint
73
+ npm run build
74
+ npm test
75
+ ```
76
+
77
+ To refresh coverage locally:
78
+
79
+ ```bash
80
+ npm test -- --coverage
81
+ ```
82
+
83
+ ## Releases
84
+
85
+ Versioning and changelogs are managed automatically by [release-please](https://github.com/googleapis/release-please).
86
+
87
+ **How it works:**
88
+ 1. Merge commits to `main` following [Conventional Commits](https://www.conventionalcommits.org/) (`fix:`, `feat:`, `feat!:`, etc.).
89
+ 2. release-please maintains an open "Release PR" that accumulates version bumps and CHANGELOG entries.
90
+ 3. Merge the Release PR to cut a release: `package.json` version is bumped, `CHANGELOG.md` is updated, a git tag is created, and the package is published to npm automatically.
91
+
92
+ **Commit types and semver mapping (while version < 1.0.0):**
93
+ - `fix:` → patch bump
94
+ - `feat:` → patch bump (minor bump is suppressed pre-1.0)
95
+ - `feat!:` or `fix!:` (breaking change) → minor bump (major bump is suppressed pre-1.0)
96
+
97
+ No manual `git tag` or `npm version` steps are needed.
98
+
99
+ ## Quick Start
100
+
101
+ Note:
102
+ - Snoopy uses Reddit public JSON endpoints by default.
103
+ - Optional Reddit OAuth fallback credentials can be configured in `snoopy settings` for environments where unauthenticated access is blocked.
104
+ - `snoopy settings` shows a full settings menu so you can jump directly to any setting and save once.
105
+
106
+ 1. Start interactive setup and create your first job:
107
+
108
+ ```bash
109
+ snoopy job add
110
+ ```
111
+
112
+ `job add` now runs an immediate first scan after saving the job so you can validate results right away.
113
+ Snoopy pauses scheduled scans for that new job during this first run attempt, then enables scheduling when it ends (including interruption/failure cases).
114
+
115
+ 2. List jobs:
116
+
117
+ ```bash
118
+ snoopy jobs list
119
+ ```
120
+
121
+ 3. Run one job immediately (limit to 5 new items while testing):
122
+
123
+ ```bash
124
+ snoopy job run --limit 5
125
+ snoopy job run <jobRef> --limit 5
126
+ ```
127
+
128
+ If `<jobRef>` is omitted for `job run`, `job enable`, `job disable`, `job delete`, `start`, `stop`, or `errors`, Snoopy shows your job list and lets you pick with up/down arrows and Enter.
129
+
130
+ 4. View run history:
131
+
132
+ ```bash
133
+ snoopy job runs <jobRef>
134
+ ```
135
+
136
+ 5. View analytics globally or for one job:
137
+
138
+ ```bash
139
+ snoopy analytics
140
+ snoopy analytics <jobRef>
141
+ snoopy analytics --days 7
142
+ ```
143
+
144
+ 6. Regenerate results CSV files (all jobs or one job):
145
+
146
+ ```bash
147
+ snoopy export csv
148
+ snoopy export csv <jobRef>
149
+ ```
150
+
151
+ 7. Inspect one run's detailed log output:
152
+
153
+ ```bash
154
+ snoopy logs
155
+ snoopy logs <runId>
156
+ snoopy logs <runId> --raw
157
+ ```
158
+
159
+ When `runId` is omitted for `logs`, Snoopy first prompts for a job, then prompts for a run from that job (up/down arrows + Enter).
160
+
161
+ 8. Show recent errors for one job:
162
+
163
+ ```bash
164
+ snoopy errors <jobRef>
165
+ ```
166
+
167
+ 9. Enable daemon mode:
168
+
169
+ ```bash
170
+ snoopy daemon start
171
+ snoopy daemon reload
172
+ ```
173
+
174
+ ## Most Used Commands
175
+
176
+ - `job add`
177
+ - `job list`
178
+ - `job run [jobRef] --limit <N>`
179
+ - `job runs [jobRef]`
180
+ - `analytics [jobRef] --days <N>`
181
+ - `export csv [jobRef]`
182
+ - `logs [runId]`
183
+ - `errors [jobRef] --hours <N>`
184
+ - `start [jobRef]` / `stop [jobRef]`
185
+ - `delete [jobRef]`
186
+ - `daemon start|stop|status`
187
+ - `daemon reload`
188
+ - `startup status`
189
+ - `doctor`
190
+
191
+ ## Run Logs
192
+
193
+ - Each job run writes a dedicated log file under `~/.snoopy/logs/`.
194
+ - Files are named `run-<runId>.log`.
195
+ - `snoopy logs` now supports guided selection (job first, then run) and shows a pretty timeline by default with post/comment text snippets, qualification result + justification, and clickable post/comment links.
196
+ - Use `snoopy logs [runId] --raw` to print the full raw log file content, including full JSON request/response payloads for Reddit and OpenRouter calls.
197
+ - Rich TTY manual runs (`snoopy job run <jobRef>`) show compact multi-line scan blocks with indented fields, clickable links, and qualification justifications.
198
+ - In rich terminals, scan field labels are colorized and qualification status is highlighted (`qualified` in green, `not qualified` in red, `pending` in yellow).
199
+ - Run logs older than 5 days are deleted automatically on daemon startup and after each job run.
200
+ - Deleting a job also deletes all associated per-run log files for that job.
201
+
202
+ ## Results CSV Exports
203
+
204
+ - Export files are generated on demand with `export csv`.
205
+ - Files are written under `~/.snoopy/results/`.
206
+ - Each job gets one file named `<job-slug>.csv`.
207
+ - CSV files are regenerated from database truth on each export command.
208
+ - Deleting a job also deletes that job's CSV file.
209
+
210
+ ## Live E2E Smoke Test
211
+
212
+ Use the built-in smoke harness to verify create -> run(5) -> delete:
213
+
214
+ ```bash
215
+ npm run e2e:smoke
216
+ ```
217
+
218
+ Optional env vars:
219
+ - `SNOOPY_E2E_LIMIT` (default `5`)
220
+ - `SNOOPY_E2E_SUBREDDITS` (default `startups,entrepreneur`)
221
+ - `SNOOPY_E2E_KEEP_JOB=true` to skip cleanup for debugging
222
+
223
+ ## Full Documentation
224
+
225
+ - [Documentation Index](docs/README.md)
226
+ - [Installation & Setup](docs/installation-and-setup.md)
227
+ - [Command Reference](docs/commands/index.md)
228
+ - [Database Schema](docs/database-schema.md)
229
+ - [Agent DB Operations](docs/agents-db.md)
230
+ - [Scheduling, Cron, Daemon, and Startup](docs/scheduling-and-startup.md)
231
+ - [Security and Secret Storage](docs/security.md)
232
+ - [E2E Smoke Testing Guide](docs/e2e-testing.md)
233
+
234
+ ## Docs Site
235
+
236
+ Serve the Docusaurus docs site locally:
237
+
238
+ ```bash
239
+ npm run docs:start
240
+ ```
241
+
242
+ Build and preview the static docs site:
243
+
244
+ ```bash
245
+ npm run docs:build
246
+ npm run docs:serve
247
+ ```
248
+
249
+ Deploy to GitHub Pages:
250
+
251
+ ```bash
252
+ GITHUB_OWNER=telepat-io GITHUB_REPO=snoopy npm run docs:deploy
253
+ ```
254
+
255
+ Docs changes pushed to `main` under `docs/` or `website/` are also rebuilt and published to GitHub Pages automatically via GitHub Actions.
@@ -0,0 +1,3 @@
1
+ export declare function showAnalytics(jobRef?: string, options?: {
2
+ days?: number;
3
+ }): void;
@@ -0,0 +1,147 @@
1
+ import { JobsRepository } from '../../services/db/repositories/jobsRepo.js';
2
+ import { AnalyticsService } from '../../services/analytics/analyticsService.js';
3
+ import { printCommandScreen, printError, printInfo, printKeyValue, printMuted, printSection, printWarning } from '../ui/consoleUi.js';
4
+ import { formatRunDisplayTimestamp } from '../ui/time.js';
5
+ function formatInteger(value) {
6
+ return new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value);
7
+ }
8
+ function formatFloat(value, digits = 2) {
9
+ return new Intl.NumberFormat('en-US', {
10
+ minimumFractionDigits: digits,
11
+ maximumFractionDigits: digits
12
+ }).format(value);
13
+ }
14
+ function formatUsd(value) {
15
+ return `$${value.toFixed(6)} (est.)`;
16
+ }
17
+ function formatMaybe(value, digits = 2) {
18
+ if (value === null) {
19
+ return '-';
20
+ }
21
+ return formatFloat(value, digits);
22
+ }
23
+ function printMetricsBlock(metrics) {
24
+ printKeyValue('New posts scanned', formatInteger(metrics.newPosts));
25
+ printKeyValue('New comments scanned', formatInteger(metrics.newComments));
26
+ printKeyValue('Prompt tokens', formatInteger(metrics.promptTokens));
27
+ printKeyValue('Completion tokens', formatInteger(metrics.completionTokens));
28
+ printKeyValue('Total tokens', formatInteger(metrics.totalTokens));
29
+ printKeyValue('Estimated cost', formatUsd(metrics.estimatedCostUsd));
30
+ printKeyValue('Avg posts/day', formatFloat(metrics.avgNewPostsPerDay));
31
+ printKeyValue('Avg comments/day', formatFloat(metrics.avgNewCommentsPerDay));
32
+ printKeyValue('Avg tokens/day', formatFloat(metrics.avgTotalTokensPerDay));
33
+ printKeyValue('Avg cost/day', formatUsd(metrics.avgEstimatedCostUsdPerDay));
34
+ printKeyValue('Tokens per post', formatMaybe(metrics.tokensPerPost));
35
+ printKeyValue('Cost per post', metrics.costPerPost === null ? '-' : formatUsd(metrics.costPerPost));
36
+ }
37
+ function printRunCard(run) {
38
+ printInfo(`${formatRunDisplayTimestamp(run)} ${run.jobName ?? run.jobId}`);
39
+ printKeyValue('Run ID', run.id);
40
+ printKeyValue('Status', run.status);
41
+ printKeyValue('Duration', run.durationSeconds === null ? '-' : `${run.durationSeconds}s`);
42
+ printKeyValue('New posts', formatInteger(run.newPosts));
43
+ printKeyValue('New comments', formatInteger(run.newComments));
44
+ printKeyValue('Prompt tokens', formatInteger(run.promptTokens));
45
+ printKeyValue('Completion tokens', formatInteger(run.completionTokens));
46
+ printKeyValue('Total tokens', formatInteger(run.totalTokens));
47
+ printKeyValue('Estimated cost', run.estimatedCostUsd === null ? '-' : formatUsd(run.estimatedCostUsd));
48
+ printKeyValue('Tokens per post', formatMaybe(run.tokensPerPost));
49
+ printKeyValue('Cost per post', run.costPerPost === null ? '-' : formatUsd(run.costPerPost));
50
+ printKeyValue('Log', run.logFilePath ?? '-');
51
+ if (run.message) {
52
+ printWarning(`Message: ${run.message}`);
53
+ }
54
+ }
55
+ export function showAnalytics(jobRef, options = {}) {
56
+ const days = options.days ?? 30;
57
+ const jobsRepo = new JobsRepository();
58
+ const analyticsService = new AnalyticsService();
59
+ printCommandScreen('Analytics');
60
+ if (jobRef) {
61
+ const job = jobsRepo.getByRef(jobRef);
62
+ if (!job) {
63
+ printError(`Job not found: ${jobRef}`);
64
+ return;
65
+ }
66
+ const view = analyticsService.getJobAnalytics(job.id, { days });
67
+ printSection(`Job Analytics: ${job.name} (${job.slug})`);
68
+ printKeyValue('Window', `Last ${view.windowDays} day(s)`);
69
+ printKeyValue('Runs in window', formatInteger(view.runCount));
70
+ printMetricsBlock(view.totals);
71
+ printSection('Subreddit Breakdown');
72
+ if (view.bySubreddit.length === 0) {
73
+ printWarning('No subreddit analytics yet for this job in the selected window.');
74
+ }
75
+ else {
76
+ view.bySubreddit.forEach((row) => {
77
+ printInfo(`r/${row.subreddit}`);
78
+ printKeyValue('New posts/comments', `${formatInteger(row.newPosts)}/${formatInteger(row.newComments)}`);
79
+ printKeyValue('Total tokens', formatInteger(row.metrics.totalTokens));
80
+ printKeyValue('Estimated cost', formatUsd(row.metrics.estimatedCostUsd));
81
+ printKeyValue('Avg posts/comments per day', `${formatFloat(row.metrics.avgNewPostsPerDay)}/${formatFloat(row.metrics.avgNewCommentsPerDay)}`);
82
+ printKeyValue('Avg tokens/day', formatFloat(row.metrics.avgTotalTokensPerDay));
83
+ printKeyValue('Avg cost/day', formatUsd(row.metrics.avgEstimatedCostUsdPerDay));
84
+ });
85
+ }
86
+ printSection('Recent Runs');
87
+ if (view.recentRuns.length === 0) {
88
+ printWarning('No runs found in the selected window.');
89
+ return;
90
+ }
91
+ view.recentRuns.forEach((run, index) => {
92
+ printRunCard(run);
93
+ if (index < view.recentRuns.length - 1) {
94
+ printMuted('');
95
+ }
96
+ });
97
+ return;
98
+ }
99
+ const view = analyticsService.getGlobalAnalytics({ days });
100
+ printSection('System Analytics');
101
+ printKeyValue('Window', `Last ${view.windowDays} day(s)`);
102
+ printKeyValue('Runs in window', formatInteger(view.runCount));
103
+ printMetricsBlock(view.totals);
104
+ printSection('Job Breakdown');
105
+ if (view.byJob.length === 0) {
106
+ printWarning('No job analytics yet in the selected window.');
107
+ }
108
+ else {
109
+ view.byJob.forEach((row) => {
110
+ printInfo(`${row.jobName} (${row.jobSlug})`);
111
+ printKeyValue('Job ID', row.jobId);
112
+ printKeyValue('New posts/comments', `${formatInteger(row.newPosts)}/${formatInteger(row.newComments)}`);
113
+ printKeyValue('Total tokens', formatInteger(row.metrics.totalTokens));
114
+ printKeyValue('Estimated cost', formatUsd(row.metrics.estimatedCostUsd));
115
+ printKeyValue('Avg posts/comments per day', `${formatFloat(row.metrics.avgNewPostsPerDay)}/${formatFloat(row.metrics.avgNewCommentsPerDay)}`);
116
+ printKeyValue('Avg tokens/day', formatFloat(row.metrics.avgTotalTokensPerDay));
117
+ printKeyValue('Avg cost/day', formatUsd(row.metrics.avgEstimatedCostUsdPerDay));
118
+ });
119
+ }
120
+ printSection('Subreddit Breakdown');
121
+ if (view.bySubreddit.length === 0) {
122
+ printWarning('No subreddit analytics yet in the selected window.');
123
+ }
124
+ else {
125
+ view.bySubreddit.forEach((row) => {
126
+ printInfo(`r/${row.subreddit}`);
127
+ printKeyValue('New posts/comments', `${formatInteger(row.newPosts)}/${formatInteger(row.newComments)}`);
128
+ printKeyValue('Total tokens', formatInteger(row.metrics.totalTokens));
129
+ printKeyValue('Estimated cost', formatUsd(row.metrics.estimatedCostUsd));
130
+ printKeyValue('Avg posts/comments per day', `${formatFloat(row.metrics.avgNewPostsPerDay)}/${formatFloat(row.metrics.avgNewCommentsPerDay)}`);
131
+ printKeyValue('Avg tokens/day', formatFloat(row.metrics.avgTotalTokensPerDay));
132
+ printKeyValue('Avg cost/day', formatUsd(row.metrics.avgEstimatedCostUsdPerDay));
133
+ });
134
+ }
135
+ printSection('Recent Runs');
136
+ if (view.recentRuns.length === 0) {
137
+ printWarning('No runs found in the selected window.');
138
+ return;
139
+ }
140
+ view.recentRuns.forEach((run, index) => {
141
+ printRunCard(run);
142
+ if (index < view.recentRuns.length - 1) {
143
+ printMuted('');
144
+ }
145
+ });
146
+ }
147
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../../src/cli/commands/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function daemonRun(): void;
2
+ export declare function daemonReload(): void;
3
+ export declare function daemonStart(): void;
4
+ export declare function daemonStop(): void;
5
+ export declare function daemonStatus(): void;
@@ -0,0 +1,85 @@
1
+ import fs from 'node:fs';
2
+ import { CronScheduler } from '../../services/scheduler/cronScheduler.js';
3
+ import { cleanupOldLogs } from '../../services/logging/logRotation.js';
4
+ import { ensureAppDirs } from '../../utils/paths.js';
5
+ import { ensureDaemonRunning, isDaemonRunning, requestDaemonReload } from '../../services/daemonControl.js';
6
+ import { printCommandScreen, printError, printInfo, printMuted, printSuccess, printWarning } from '../ui/consoleUi.js';
7
+ let scheduler = null;
8
+ export function daemonRun() {
9
+ ensureAppDirs();
10
+ cleanupOldLogs();
11
+ printCommandScreen('Daemon mode', 'Daemon');
12
+ scheduler = new CronScheduler();
13
+ scheduler.start();
14
+ process.on('SIGTERM', () => {
15
+ scheduler?.stop();
16
+ process.exit(0);
17
+ });
18
+ process.on('SIGINT', () => {
19
+ scheduler?.stop();
20
+ process.exit(0);
21
+ });
22
+ process.on('SIGUSR2', () => {
23
+ scheduler?.reload();
24
+ });
25
+ setInterval(() => {
26
+ // Keep event loop alive for cron tasks.
27
+ }, 60_000);
28
+ printSuccess('Snoopy daemon is running.');
29
+ printMuted('Press Ctrl+C to stop.');
30
+ }
31
+ export function daemonReload() {
32
+ printCommandScreen('Daemon control', 'Daemon Reload');
33
+ const result = requestDaemonReload();
34
+ if (!result.pid) {
35
+ printWarning('Daemon is not running.');
36
+ return;
37
+ }
38
+ if (!result.reloaded) {
39
+ printError(`Could not reload daemon schedules for pid ${result.pid}.`);
40
+ printMuted('If this persists, restart the daemon with snoopy daemon stop && snoopy daemon start.');
41
+ return;
42
+ }
43
+ printSuccess(`Reloaded daemon schedules (pid ${result.pid}).`);
44
+ }
45
+ export function daemonStart() {
46
+ printCommandScreen('Daemon control', 'Daemon Start');
47
+ const status = ensureDaemonRunning();
48
+ if (!status.started) {
49
+ printWarning(`Daemon already running${status.pid ? ` (pid ${status.pid})` : ''}.`);
50
+ return;
51
+ }
52
+ printSuccess(`Daemon started (pid ${status.pid}).`);
53
+ }
54
+ export function daemonStop() {
55
+ printCommandScreen('Daemon control', 'Daemon Stop');
56
+ const paths = ensureAppDirs();
57
+ if (!fs.existsSync(paths.pidFilePath)) {
58
+ printWarning('Daemon is not running.');
59
+ return;
60
+ }
61
+ const pid = Number(fs.readFileSync(paths.pidFilePath, 'utf8'));
62
+ try {
63
+ process.kill(pid, 'SIGTERM');
64
+ }
65
+ catch {
66
+ // Ignore if already dead.
67
+ }
68
+ fs.unlinkSync(paths.pidFilePath);
69
+ printSuccess('Daemon stopped.');
70
+ }
71
+ export function daemonStatus() {
72
+ printCommandScreen('Daemon control', 'Daemon Status');
73
+ const status = isDaemonRunning();
74
+ if (!status.pid) {
75
+ printInfo('Daemon status: stopped');
76
+ return;
77
+ }
78
+ if (status.running) {
79
+ printSuccess(`Daemon status: running (pid ${status.pid})`);
80
+ return;
81
+ }
82
+ printError(`Daemon status: stale pid file (pid ${status.pid})`);
83
+ printMuted('Run snoopy daemon stop to clear stale state.');
84
+ }
85
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../../../src/cli/commands/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runDoctor(): Promise<void>;
@@ -0,0 +1,106 @@
1
+ import fs from 'node:fs';
2
+ import { getDb } from '../../services/db/sqlite.js';
3
+ import { JobsRepository } from '../../services/db/repositories/jobsRepo.js';
4
+ import { RunsRepository } from '../../services/db/repositories/runsRepo.js';
5
+ import { extractErrorEntries, readRunLog } from '../../services/logging/logReader.js';
6
+ import { getOpenRouterApiKey } from '../../services/security/secretStore.js';
7
+ import { getStartupStatus } from '../../services/startup/index.js';
8
+ import { ensureAppDirs } from '../../utils/paths.js';
9
+ import { printCommandScreen, printError, printInfo, printKeyValue, printSection, printSuccess, printWarning } from '../ui/consoleUi.js';
10
+ import { formatRunDisplayTimestamp } from '../ui/time.js';
11
+ function getDaemonHealth() {
12
+ const paths = ensureAppDirs();
13
+ if (!fs.existsSync(paths.pidFilePath)) {
14
+ return { ok: false, details: 'Daemon not running (no pid file)' };
15
+ }
16
+ const pid = Number(fs.readFileSync(paths.pidFilePath, 'utf8'));
17
+ if (!Number.isFinite(pid)) {
18
+ return { ok: false, details: 'Invalid daemon pid file' };
19
+ }
20
+ try {
21
+ process.kill(pid, 0);
22
+ return { ok: true, details: `Daemon running (pid ${pid})` };
23
+ }
24
+ catch {
25
+ return { ok: false, details: `Stale daemon pid file (pid ${pid})` };
26
+ }
27
+ }
28
+ function isWithinLast24Hours(createdAt) {
29
+ const timestamp = Date.parse(createdAt);
30
+ if (Number.isNaN(timestamp)) {
31
+ return false;
32
+ }
33
+ return timestamp >= Date.now() - 24 * 60 * 60 * 1000;
34
+ }
35
+ export async function runDoctor() {
36
+ printCommandScreen('Diagnostics', 'Snoopy Doctor');
37
+ const paths = ensureAppDirs();
38
+ let dbOk = false;
39
+ let dbDetails = `DB file: ${paths.dbPath}`;
40
+ try {
41
+ const db = getDb();
42
+ db.prepare('SELECT 1').get();
43
+ dbOk = true;
44
+ dbDetails = `DB reachable at ${paths.dbPath}`;
45
+ }
46
+ catch (error) {
47
+ dbDetails = `DB error: ${String(error)}`;
48
+ }
49
+ const jobsRepo = new JobsRepository();
50
+ const runsRepo = new RunsRepository();
51
+ const jobs = jobsRepo.list();
52
+ const enabledJobs = jobs.filter((job) => job.enabled).length;
53
+ const apiKey = await getOpenRouterApiKey();
54
+ const startup = getStartupStatus();
55
+ const daemon = getDaemonHealth();
56
+ printKeyValue('Platform', process.platform);
57
+ printKeyValue('Node', process.version);
58
+ if (dbOk) {
59
+ printSuccess(`Database: ${dbDetails}`);
60
+ }
61
+ else {
62
+ printError(`Database: ${dbDetails}`);
63
+ }
64
+ if (apiKey) {
65
+ printSuccess('OpenRouter API key: configured');
66
+ }
67
+ else {
68
+ printWarning('OpenRouter API key: missing');
69
+ }
70
+ printInfo(`Jobs: ${jobs.length} total, ${enabledJobs} enabled`);
71
+ if (daemon.ok) {
72
+ printSuccess(`Daemon: ${daemon.details}`);
73
+ }
74
+ else {
75
+ printWarning(`Daemon: ${daemon.details}`);
76
+ }
77
+ printInfo(`Startup on reboot: ${startup.enabled ? 'enabled' : 'disabled'} via ${startup.method}`);
78
+ printInfo(`Startup details: ${startup.detail}`);
79
+ printSection('Recent Job Errors');
80
+ const recentProblemRuns = runsRepo
81
+ .latestWithJobNames(20)
82
+ .filter((run) => isWithinLast24Hours(run.createdAt))
83
+ .map((run) => {
84
+ const logContent = readRunLog(run.logFilePath);
85
+ const errorEntries = extractErrorEntries(logContent ?? '');
86
+ return {
87
+ run,
88
+ errorEntries
89
+ };
90
+ })
91
+ .filter(({ run, errorEntries }) => run.status === 'failed' || errorEntries.length > 0);
92
+ if (recentProblemRuns.length === 0) {
93
+ printSuccess('No recent job run failures or logged errors in the last 24 hours.');
94
+ return;
95
+ }
96
+ printWarning(`Found ${recentProblemRuns.length} recent run(s) with failures or logged errors.`);
97
+ recentProblemRuns.forEach(({ run, errorEntries }) => {
98
+ printWarning(`${formatRunDisplayTimestamp(run)} ${run.jobName ?? run.jobId} (${run.status})`);
99
+ printKeyValue('Run ID', run.id);
100
+ printKeyValue('Message', run.message ?? '-');
101
+ if (errorEntries.length > 0) {
102
+ printInfo(errorEntries[errorEntries.length - 1].split('\n')[0]);
103
+ }
104
+ });
105
+ }
106
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,SAAS,eAAe;IACtB,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,uBAAuB,GAAG,GAAG,EAAE,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,GAAG,GAAG,EAAE,CAAC;IACtE,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,kBAAkB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,SAAS,GAAG,YAAY,KAAK,CAAC,MAAM,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,GAAG,IAAI,CAAC;QACZ,SAAS,GAAG,mBAAmB,KAAK,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,SAAS,GAAG,aAAa,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAE7D,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IAEjC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC,IAAI,IAAI,EAAE,CAAC;QACT,YAAY,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,YAAY,CAAC,gCAAgC,CAAC,CAAC;IACjD,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,6BAA6B,CAAC,CAAC;IAC9C,CAAC;IAED,SAAS,CAAC,SAAS,IAAI,CAAC,MAAM,WAAW,WAAW,UAAU,CAAC,CAAC;IAEhE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,YAAY,CAAC,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS,CAAC,sBAAsB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAClG,SAAS,CAAC,oBAAoB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEhD,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAClC,MAAM,iBAAiB,GAAG,QAAQ;SAC/B,kBAAkB,CAAC,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;SACnD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACX,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,mBAAmB,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QAC3D,OAAO;YACL,GAAG;YACH,YAAY;SACb,CAAC;IACJ,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEzF,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,YAAY,CAAC,mEAAmE,CAAC,CAAC;QAClF,OAAO;IACT,CAAC;IAED,YAAY,CAAC,SAAS,iBAAiB,CAAC,MAAM,gDAAgD,CAAC,CAAC;IAChG,iBAAiB,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE;QAClD,YAAY,CAAC,GAAG,yBAAyB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9F,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,aAAa,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;QAC7C,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function showJobErrors(jobRef?: string, options?: {
2
+ hours?: number;
3
+ }): Promise<void>;