@hasna/skills 0.1.18 → 0.1.20

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 (234) hide show
  1. package/README.md +108 -372
  2. package/bin/index.js +10455 -9282
  3. package/bin/mcp.js +4793 -3402
  4. package/dist/cli/commands/completion.d.ts +5 -0
  5. package/dist/cli/commands/create-sync-config.d.ts +5 -0
  6. package/dist/cli/commands/diagnostic.d.ts +5 -0
  7. package/dist/cli/commands/init.d.ts +5 -0
  8. package/dist/cli/commands/install.d.ts +5 -0
  9. package/dist/cli/commands/introspect.d.ts +5 -0
  10. package/dist/cli/commands/list.d.ts +5 -0
  11. package/dist/cli/commands/runtime.d.ts +5 -0
  12. package/dist/cli/commands/schedule.d.ts +5 -0
  13. package/dist/index.js +197 -97
  14. package/dist/lib/config.d.ts +1 -1
  15. package/dist/lib/registry.d.ts +1 -11
  16. package/dist/lib/scheduler.d.ts +1 -1
  17. package/dist/lib/scheduler.test.d.ts +4 -0
  18. package/dist/lib/search.d.ts +17 -0
  19. package/package.json +3 -3
  20. package/skills/skill-commitpush/SKILL.md +57 -0
  21. package/skills/skill-commitpush/package.json +34 -0
  22. package/skills/skill-commitpush/src/index.ts +34 -0
  23. package/skills/skill-commitpush/tsconfig.json +17 -0
  24. package/skills/skill-commitpushpr/SKILL.md +55 -0
  25. package/skills/skill-commitpushpr/package.json +34 -0
  26. package/skills/skill-commitpushpr/src/index.ts +34 -0
  27. package/skills/skill-commitpushpr/tsconfig.json +17 -0
  28. package/skills/skill-monitor/SKILL.md +69 -0
  29. package/skills/skill-monitor/package.json +34 -0
  30. package/skills/skill-monitor/src/index.ts +34 -0
  31. package/skills/skill-monitor/tsconfig.json +17 -0
  32. package/skills/skill-read-csv/SKILL.md +62 -0
  33. package/skills/skill-read-csv/package.json +38 -0
  34. package/skills/skill-read-csv/src/index.ts +331 -0
  35. package/skills/skill-read-csv/tsconfig.json +17 -0
  36. package/skills/skill-read-excel/SKILL.md +64 -0
  37. package/skills/skill-read-excel/package.json +37 -0
  38. package/skills/skill-read-excel/src/index.ts +253 -0
  39. package/skills/skill-read-excel/tsconfig.json +17 -0
  40. package/skills/skill-read-image/SKILL.md +47 -0
  41. package/skills/skill-read-image/package.json +34 -0
  42. package/skills/skill-read-image/src/index.ts +264 -0
  43. package/skills/skill-read-image/tsconfig.json +17 -0
  44. package/skills/skill-read-pdf/SKILL.md +52 -0
  45. package/skills/skill-read-pdf/package.json +37 -0
  46. package/skills/skill-read-pdf/src/index.ts +376 -0
  47. package/skills/skill-read-pdf/tsconfig.json +17 -0
  48. package/skills/skill-tmux-session/SKILL.md +109 -0
  49. package/skills/skill-tmux-session/package.json +34 -0
  50. package/skills/skill-tmux-session/src/index.ts +34 -0
  51. package/skills/skill-tmux-session/tsconfig.json +17 -0
  52. package/skills/skill-academic-journal-matcher/bin/cli.ts +0 -34
  53. package/skills/skill-action-item-router/bin/cli.ts +0 -34
  54. package/skills/skill-ad-creative-generator/bin/cli.ts +0 -34
  55. package/skills/skill-advanced-math/bin/cli.ts +0 -34
  56. package/skills/skill-analyze-data/bin/cli.ts +0 -19
  57. package/skills/skill-anomaly-investigator/bin/cli.ts +0 -34
  58. package/skills/skill-api-test-suite/bin/cli.ts +0 -34
  59. package/skills/skill-apidocs/bin/cli.ts +0 -87
  60. package/skills/skill-audio-cleanup-lab/bin/cli.ts +0 -6
  61. package/skills/skill-audiobook-chapter-proofer/bin/cli.ts +0 -34
  62. package/skills/skill-banner-ad-suite/bin/cli.ts +0 -34
  63. package/skills/skill-benchmark-finder/bin/cli.ts +0 -34
  64. package/skills/skill-bio-sequence-tool/bin/cli.ts +0 -34
  65. package/skills/skill-blog-topic-cluster/bin/cli.ts +0 -34
  66. package/skills/skill-brand-style-guide/bin/cli.ts +0 -19
  67. package/skills/skill-brand-voice-audit/bin/cli.ts +0 -34
  68. package/skills/skill-budget-variance-analyzer/bin/cli.ts +0 -6
  69. package/skills/skill-businessactivity/bin/cli.ts +0 -28
  70. package/skills/skill-calendar-events/bin/cli.ts +0 -34
  71. package/skills/skill-campaign-metric-brief/bin/cli.ts +0 -34
  72. package/skills/skill-campaign-moodboard/bin/cli.ts +0 -34
  73. package/skills/skill-caption-style-stylist/bin/cli.ts +0 -34
  74. package/skills/skill-chemistry-calculator/bin/cli.ts +0 -34
  75. package/skills/skill-churn-risk-notifier/bin/cli.ts +0 -34
  76. package/skills/skill-citation-formatter/bin/cli.ts +0 -34
  77. package/skills/skill-classroom-newsletter-kit/bin/cli.ts +0 -34
  78. package/skills/skill-color-palette-harmonizer/bin/cli.ts +0 -34
  79. package/skills/skill-competitor-ad-analyzer/bin/cli.ts +0 -34
  80. package/skills/skill-compliance-copy-check/bin/cli.ts +0 -34
  81. package/skills/skill-compliance-report-pack/bin/cli.ts +0 -34
  82. package/skills/skill-compress-video/bin/cli.ts +0 -19
  83. package/skills/skill-consolelog/bin/cli.ts +0 -884
  84. package/skills/skill-contract-plainlanguage/bin/cli.ts +0 -34
  85. package/skills/skill-copytone-translator/bin/cli.ts +0 -34
  86. package/skills/skill-create-blog-article/bin/cli.ts +0 -34
  87. package/skills/skill-create-ebook/bin/cli.ts +0 -34
  88. package/skills/skill-crm-note-enhancer/bin/cli.ts +0 -34
  89. package/skills/skill-customer-journey-mapper/bin/cli.ts +0 -34
  90. package/skills/skill-dashboard-builder/bin/cli.ts +0 -34
  91. package/skills/skill-dashboard-narrator/bin/cli.ts +0 -34
  92. package/skills/skill-data-anonymizer/bin/cli.ts +0 -34
  93. package/skills/skill-database-explorer/bin/cli.ts +0 -34
  94. package/skills/skill-dataset-health-check/bin/cli.ts +0 -34
  95. package/skills/skill-decision-journal/bin/cli.ts +0 -34
  96. package/skills/skill-delegation-brief-writer/bin/cli.ts +0 -34
  97. package/skills/skill-destination-briefing/bin/cli.ts +0 -34
  98. package/skills/skill-diff-viewer/bin/cli.ts +0 -34
  99. package/skills/skill-domainpurchase/bin/cli.ts +0 -683
  100. package/skills/skill-domainsearch/bin/cli.ts +0 -410
  101. package/skills/skill-educational-resource-finder/bin/cli.ts +0 -34
  102. package/skills/skill-email-campaign/bin/cli.ts +0 -34
  103. package/skills/skill-exam-readiness-check/bin/cli.ts +0 -34
  104. package/skills/skill-experiment-power-calculator/bin/cli.ts +0 -34
  105. package/skills/skill-extract-audio/bin/cli.ts +0 -19
  106. package/skills/skill-extract-frames/bin/cli.ts +0 -34
  107. package/skills/skill-extract-invoice/bin/cli.ts +0 -34
  108. package/skills/skill-family-activity-curator/bin/cli.ts +0 -34
  109. package/skills/skill-faq-packager/bin/cli.ts +0 -34
  110. package/skills/skill-feedback-survey-designer/bin/cli.ts +0 -34
  111. package/skills/skill-field-trip-planner/bin/cli.ts +0 -34
  112. package/skills/skill-file-organizer/bin/cli.ts +0 -34
  113. package/skills/skill-folder-tree/bin/cli.ts +0 -34
  114. package/skills/skill-forecast-scenario-lab/bin/cli.ts +0 -34
  115. package/skills/skill-form-filler/bin/cli.ts +0 -34
  116. package/skills/skill-generate-api-client/bin/cli.ts +0 -34
  117. package/skills/skill-generate-book-cover/bin/cli.ts +0 -34
  118. package/skills/skill-generate-chart/bin/cli.ts +0 -34
  119. package/skills/skill-generate-diagram/bin/cli.ts +0 -34
  120. package/skills/skill-generate-dockerfile/bin/cli.ts +0 -34
  121. package/skills/skill-generate-documentation/bin/cli.ts +0 -34
  122. package/skills/skill-generate-docx/bin/cli.ts +0 -6
  123. package/skills/skill-generate-env/bin/cli.ts +0 -34
  124. package/skills/skill-generate-excel/bin/cli.ts +0 -34
  125. package/skills/skill-generate-favicon/bin/cli.ts +0 -34
  126. package/skills/skill-generate-mock-data/bin/cli.ts +0 -34
  127. package/skills/skill-generate-pdf/bin/cli.ts +0 -6
  128. package/skills/skill-generate-pr-description/bin/cli.ts +0 -34
  129. package/skills/skill-generate-presentation/bin/cli.ts +0 -34
  130. package/skills/skill-generate-qrcode/bin/cli.ts +0 -34
  131. package/skills/skill-generate-regex/bin/cli.ts +0 -34
  132. package/skills/skill-generate-resume/bin/cli.ts +0 -34
  133. package/skills/skill-generate-sitemap/bin/cli.ts +0 -34
  134. package/skills/skill-generate-social-posts/bin/cli.ts +0 -34
  135. package/skills/skill-generate-sql/bin/cli.ts +0 -34
  136. package/skills/skill-gif-maker/bin/cli.ts +0 -34
  137. package/skills/skill-github-manager/bin/cli.ts +0 -34
  138. package/skills/skill-gmail/bin/cli.ts +0 -34
  139. package/skills/skill-goal-quarterly-roadmap/bin/cli.ts +0 -34
  140. package/skills/skill-grant-application-drafter/bin/cli.ts +0 -34
  141. package/skills/skill-grocery-basket-optimizer/bin/cli.ts +0 -34
  142. package/skills/skill-guest-communication-suite/bin/cli.ts +0 -34
  143. package/skills/skill-habit-reflection-digest/bin/cli.ts +0 -34
  144. package/skills/skill-highlight-reel-generator/bin/cli.ts +0 -34
  145. package/skills/skill-homework-feedback-coach/bin/cli.ts +0 -34
  146. package/skills/skill-household-maintenance-mgr/bin/cli.ts +0 -34
  147. package/skills/skill-http-server/bin/cli.ts +0 -34
  148. package/skills/skill-implementation-agent/bin/cli.ts +0 -34
  149. package/skills/skill-implementation-plan/bin/cli.ts +0 -34
  150. package/skills/skill-implementation-todo/bin/cli.ts +0 -34
  151. package/skills/skill-inbox-priority-planner/bin/cli.ts +0 -34
  152. package/skills/skill-invoice/bin/cli.ts +0 -20
  153. package/skills/skill-invoice-dispute-helper/bin/cli.ts +0 -34
  154. package/skills/skill-itinerary-architect/bin/cli.ts +0 -34
  155. package/skills/skill-jingle-composer/bin/cli.ts +0 -34
  156. package/skills/skill-kpi-digest-generator/bin/cli.ts +0 -34
  157. package/skills/skill-lab-notebook-formatter/bin/cli.ts +0 -34
  158. package/skills/skill-landing-page-copy/bin/cli.ts +0 -34
  159. package/skills/skill-latex-table-generator/bin/cli.ts +0 -34
  160. package/skills/skill-learning-style-profiler/bin/cli.ts +0 -34
  161. package/skills/skill-lesson-plan-customizer/bin/cli.ts +0 -34
  162. package/skills/skill-livestream-runofshow/bin/cli.ts +0 -34
  163. package/skills/skill-longform-structurer/bin/cli.ts +0 -34
  164. package/skills/skill-lorem-generator/bin/cli.ts +0 -34
  165. package/skills/skill-managehook/bin/cli.ts +0 -241
  166. package/skills/skill-managemcp/bin/cli.ts +0 -241
  167. package/skills/skill-manageskill/bin/cli.ts +0 -241
  168. package/skills/skill-markdown-validator/bin/cli.ts +0 -34
  169. package/skills/skill-mcp-builder/bin/cli.ts +0 -34
  170. package/skills/skill-meal-plan-designer/bin/cli.ts +0 -34
  171. package/skills/skill-meeting-insight-summarizer/bin/cli.ts +0 -34
  172. package/skills/skill-merge-pdfs/bin/cli.ts +0 -34
  173. package/skills/skill-microcopy-generator/bin/cli.ts +0 -34
  174. package/skills/skill-mindfulness-prompt-cache/bin/cli.ts +0 -34
  175. package/skills/skill-notion-manager/bin/cli.ts +0 -34
  176. package/skills/skill-onboarding-sequence-builder/bin/cli.ts +0 -34
  177. package/skills/skill-onsite-ops-checklist/bin/cli.ts +0 -34
  178. package/skills/skill-outreach-cadence-designer/bin/cli.ts +0 -34
  179. package/skills/skill-packaging-concept-studio/bin/cli.ts +0 -34
  180. package/skills/skill-packing-plan-pro/bin/cli.ts +0 -34
  181. package/skills/skill-parent-teacher-brief/bin/cli.ts +0 -34
  182. package/skills/skill-partner-kit-assembler/bin/cli.ts +0 -34
  183. package/skills/skill-payroll-change-prepper/bin/cli.ts +0 -34
  184. package/skills/skill-persona-based-adwriter/bin/cli.ts +0 -34
  185. package/skills/skill-persona-generator/bin/cli.ts +0 -34
  186. package/skills/skill-personal-daily-ops/bin/cli.ts +0 -34
  187. package/skills/skill-pet-care-scheduler/bin/cli.ts +0 -34
  188. package/skills/skill-podcast-show-notes/bin/cli.ts +0 -34
  189. package/skills/skill-presentation-theme-maker/bin/cli.ts +0 -34
  190. package/skills/skill-press-release-drafter/bin/cli.ts +0 -34
  191. package/skills/skill-print-collateral-designer/bin/cli.ts +0 -34
  192. package/skills/skill-procurement-scorecard/bin/cli.ts +0 -34
  193. package/skills/skill-product-demo-script/bin/cli.ts +0 -34
  194. package/skills/skill-product-mockup/bin/cli.ts +0 -34
  195. package/skills/skill-project-retro-companion/bin/cli.ts +0 -34
  196. package/skills/skill-proposal-redline-advisor/bin/cli.ts +0 -34
  197. package/skills/skill-regex-tester/bin/cli.ts +0 -34
  198. package/skills/skill-remove-background/bin/cli.ts +0 -34
  199. package/skills/skill-risk-disclosure-kit/bin/cli.ts +0 -34
  200. package/skills/skill-roi-comparison-tool/bin/cli.ts +0 -34
  201. package/skills/skill-sales-call-recapper/bin/cli.ts +0 -34
  202. package/skills/skill-salescopy/bin/cli.ts +0 -20
  203. package/skills/skill-scaffold-project/bin/cli.ts +0 -34
  204. package/skills/skill-scholarship-tracker/bin/cli.ts +0 -34
  205. package/skills/skill-scientific-figure-check/bin/cli.ts +0 -34
  206. package/skills/skill-seating-chart-maker/bin/cli.ts +0 -34
  207. package/skills/skill-security-audit/bin/cli.ts +0 -34
  208. package/skills/skill-seo-brief-builder/bin/cli.ts +0 -34
  209. package/skills/skill-slack-assistant/bin/cli.ts +0 -34
  210. package/skills/skill-sleep-routine-analyzer/bin/cli.ts +0 -34
  211. package/skills/skill-social-media-kit/bin/cli.ts +0 -34
  212. package/skills/skill-split-pdf/bin/cli.ts +0 -34
  213. package/skills/skill-sponsorship-proposal-lab/bin/cli.ts +0 -34
  214. package/skills/skill-spreadsheet-cleanroom/bin/cli.ts +0 -34
  215. package/skills/skill-statistical-test-selector/bin/cli.ts +0 -34
  216. package/skills/skill-stress-relief-playbook/bin/cli.ts +0 -34
  217. package/skills/skill-study-guide-builder/bin/cli.ts +0 -34
  218. package/skills/skill-subscription-spend-watcher/bin/cli.ts +0 -34
  219. package/skills/skill-subtitle/bin/cli.ts +0 -20
  220. package/skills/skill-survey-insight-extractor/bin/cli.ts +0 -34
  221. package/skills/skill-terraform-generator/bin/cli.ts +0 -34
  222. package/skills/skill-testimonial-graphics/bin/cli.ts +0 -34
  223. package/skills/skill-timesheet/bin/cli.ts +0 -47
  224. package/skills/skill-travel-budget-balancer/bin/cli.ts +0 -34
  225. package/skills/skill-validate-config/bin/cli.ts +0 -34
  226. package/skills/skill-video-cut-suggester/bin/cli.ts +0 -34
  227. package/skills/skill-video-downloader/bin/cli.ts +0 -34
  228. package/skills/skill-video-thumbnail/bin/cli.ts +0 -34
  229. package/skills/skill-voiceover-casting-assistant/bin/cli.ts +0 -34
  230. package/skills/skill-watermark/bin/cli.ts +0 -34
  231. package/skills/skill-webcrawling/bin/cli.ts +0 -21
  232. package/skills/skill-webinar-script-coach/bin/cli.ts +0 -34
  233. package/skills/skill-wellness-progress-reporter/bin/cli.ts +0 -34
  234. package/skills/skill-workout-cycle-planner/bin/cli.ts +0 -34
@@ -1,884 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { Command } from "commander";
3
- import { createTables } from "../src/db/schema";
4
- import { ensureInstalled } from "../src/lib/installer";
5
-
6
- // Ensure service directory exists on startup
7
- ensureInstalled();
8
- import {
9
- createApp,
10
- getAllApps,
11
- getAppById,
12
- getAppByName,
13
- updateApp,
14
- deleteApp,
15
- createPage,
16
- getPagesByAppId,
17
- getPageByAppAndPath,
18
- deletePage,
19
- createScan,
20
- getScans,
21
- getScanById,
22
- updateScan,
23
- getConsoleLogs,
24
- countConsoleLogs,
25
- createConsoleLogsBatch,
26
- getLogsByLevel,
27
- getLastScanForApp,
28
- createScreenshot,
29
- getScreenshotsByScanId,
30
- } from "../src/db/queries";
31
- import { getDb, closeConnection } from "../src/db/index";
32
- import { ConsoleMonitor, getMonitor, shutdownMonitor } from "../src/lib/monitor";
33
- import {
34
- loadConfig,
35
- saveConfig,
36
- getConfigPath,
37
- isHeadless,
38
- getDefaultTimeout,
39
- } from "../src/lib/config";
40
- import {
41
- startWatching,
42
- stopWatching,
43
- getWatchStatus,
44
- stopAllWatchers,
45
- } from "../src/lib/watcher";
46
- import {
47
- install,
48
- uninstall,
49
- isInstalled,
50
- logScanStart,
51
- logScanComplete,
52
- logWatchStart,
53
- logWatchStop,
54
- logError,
55
- ensureSnapshotsDir,
56
- generateScreenshotFilename,
57
- getScreenshotPath,
58
- getSnapshotsDir,
59
- } from "../src/lib/installer";
60
- import type { ConsoleLogLevel, CreateConsoleLogInput } from "../src/lib/types";
61
-
62
- // Helper for Commander option parsing (parseInt needs explicit radix)
63
- const parseIntOption = (val: string) => parseInt(val, 10);
64
-
65
- const program = new Command();
66
-
67
- program
68
- .name("service-consolelog")
69
- .description("Monitor console logs from web applications using Playwright")
70
- .version("1.0.0");
71
-
72
- // ============ INIT/INSTALL COMMANDS ============
73
-
74
- program
75
- .command("init")
76
- .description("Initialize service-consolelog in the current project")
77
- .option("-f, --force", "Force reinstall even if already installed")
78
- .action((options) => {
79
- const result = install({ force: options.force });
80
- if (result.success) {
81
- console.log(`āœ“ ${result.message}`);
82
- console.log(` Created: .service-consolelog/README.md`);
83
- console.log(` Created: .service-consolelog/service-consolelog.log`);
84
- console.log(` Created: .service-consolelog/snapshots/`);
85
- } else {
86
- console.log(`⚠ ${result.message}`);
87
- }
88
- });
89
-
90
- program
91
- .command("uninit")
92
- .description("Remove service-consolelog from the current project")
93
- .action(() => {
94
- const result = uninstall();
95
- if (result.success) {
96
- console.log(`āœ“ ${result.message}`);
97
- } else {
98
- console.log(`⚠ ${result.message}`);
99
- }
100
- });
101
-
102
- program
103
- .command("status")
104
- .description("Check installation status")
105
- .action(() => {
106
- if (isInstalled()) {
107
- console.log("āœ“ Installed in current project");
108
- } else {
109
- console.log("āœ— Not installed. Run 'service-consolelog init' to set up.");
110
- }
111
- });
112
-
113
- // ============ APP COMMANDS ============
114
-
115
- const appCmd = program.command("app").description("Manage monitored applications");
116
-
117
- appCmd
118
- .command("add")
119
- .description("Add a new app to monitor")
120
- .requiredOption("-n, --name <name>", "App name")
121
- .requiredOption("-p, --port <port>", "App port", parseIntOption)
122
- .requiredOption("-u, --url <url>", "Base URL")
123
- .option("-d, --description <desc>", "Description")
124
- .action(async (options) => {
125
- try {
126
- await ensureDb();
127
- const app = await createApp({
128
- name: options.name,
129
- port: options.port,
130
- base_url: options.url,
131
- description: options.description,
132
- });
133
- console.log(`āœ“ Created app "${app.name}" (ID: ${app.id})`);
134
- } catch (error) {
135
- const err = error as Error;
136
- if (err.message.includes("unique") || err.message.includes("duplicate")) {
137
- console.error(`āœ— App "${options.name}" already exists`);
138
- } else {
139
- console.error(`āœ— Error: ${err.message}`);
140
- }
141
- process.exit(1);
142
- }
143
- });
144
-
145
- appCmd
146
- .command("list")
147
- .description("List all apps")
148
- .option("-a, --active-only", "Show only active apps")
149
- .action(async (options) => {
150
- await ensureDb();
151
- const apps = await getAllApps(options.activeOnly);
152
-
153
- if (apps.length === 0) {
154
- console.log("No apps found. Add one with: service-consolelog app add");
155
- return;
156
- }
157
-
158
- console.log("\nApps:\n");
159
- for (const app of apps) {
160
- const status = app.active ? "ā—" : "ā—‹";
161
- console.log(` ${status} ${app.name} (ID: ${app.id})`);
162
- console.log(` URL: ${app.base_url}`);
163
- console.log(` Port: ${app.port}`);
164
- if (app.description) {
165
- console.log(` Description: ${app.description}`);
166
- }
167
- console.log();
168
- }
169
- });
170
-
171
- appCmd
172
- .command("get <idOrName>")
173
- .description("Get app details")
174
- .action(async (idOrName) => {
175
- await ensureDb();
176
- const app = await resolveApp(idOrName);
177
-
178
- if (!app) {
179
- console.error(`āœ— App "${idOrName}" not found`);
180
- process.exit(1);
181
- }
182
-
183
- console.log(`\nApp: ${app.name}\n`);
184
- console.log(` ID: ${app.id}`);
185
- console.log(` URL: ${app.base_url}`);
186
- console.log(` Port: ${app.port}`);
187
- console.log(` Active: ${app.active ? "Yes" : "No"}`);
188
- if (app.description) {
189
- console.log(` Description: ${app.description}`);
190
- }
191
- console.log(` Created: ${app.created_at}`);
192
- console.log(` Updated: ${app.updated_at}`);
193
-
194
- const pages = await getPagesByAppId(app.id);
195
- console.log(` Pages: ${pages.length}`);
196
- });
197
-
198
- appCmd
199
- .command("update <idOrName>")
200
- .description("Update an app")
201
- .option("-n, --name <name>", "New name")
202
- .option("-p, --port <port>", "New port", parseIntOption)
203
- .option("-u, --url <url>", "New base URL")
204
- .option("-d, --description <desc>", "New description")
205
- .option("--active <bool>", "Set active status", (v) => v === "true")
206
- .action(async (idOrName, options) => {
207
- await ensureDb();
208
- const app = await resolveApp(idOrName);
209
-
210
- if (!app) {
211
- console.error(`āœ— App "${idOrName}" not found`);
212
- process.exit(1);
213
- }
214
-
215
- const updated = await updateApp(app.id, {
216
- name: options.name,
217
- port: options.port,
218
- base_url: options.url,
219
- description: options.description,
220
- active: options.active,
221
- });
222
-
223
- console.log(`āœ“ Updated app "${updated?.name}"`);
224
- });
225
-
226
- appCmd
227
- .command("remove <idOrName>")
228
- .description("Remove an app")
229
- .action(async (idOrName) => {
230
- await ensureDb();
231
- const app = await resolveApp(idOrName);
232
-
233
- if (!app) {
234
- console.error(`āœ— App "${idOrName}" not found`);
235
- process.exit(1);
236
- }
237
-
238
- await deleteApp(app.id);
239
- console.log(`āœ“ Removed app "${app.name}"`);
240
- });
241
-
242
- // ============ PAGE COMMANDS ============
243
-
244
- const pageCmd = program.command("page").description("Manage monitored pages");
245
-
246
- pageCmd
247
- .command("add")
248
- .description("Add a page to monitor")
249
- .requiredOption("-a, --app <app>", "App name or ID")
250
- .requiredOption("-p, --path <path>", "Page path (e.g., '/', '/dashboard')")
251
- .option("-n, --name <name>", "Page name")
252
- .option("-w, --wait-for <selector>", "CSS selector to wait for")
253
- .option("-t, --timeout <ms>", "Timeout in milliseconds", parseIntOption)
254
- .action(async (options) => {
255
- await ensureDb();
256
- const app = await resolveApp(options.app);
257
-
258
- if (!app) {
259
- console.error(`āœ— App "${options.app}" not found`);
260
- process.exit(1);
261
- }
262
-
263
- try {
264
- const page = await createPage({
265
- app_id: app.id,
266
- path: options.path,
267
- name: options.name,
268
- wait_for: options.waitFor,
269
- timeout: options.timeout,
270
- });
271
- console.log(`āœ“ Added page "${page.path}" to "${app.name}" (ID: ${page.id})`);
272
- } catch (error) {
273
- const err = error as Error;
274
- if (err.message.includes("unique") || err.message.includes("duplicate")) {
275
- console.error(`āœ— Page "${options.path}" already exists for "${app.name}"`);
276
- } else {
277
- console.error(`āœ— Error: ${err.message}`);
278
- }
279
- process.exit(1);
280
- }
281
- });
282
-
283
- pageCmd
284
- .command("list")
285
- .description("List pages for an app")
286
- .requiredOption("-a, --app <app>", "App name or ID")
287
- .option("--active-only", "Show only active pages")
288
- .action(async (options) => {
289
- await ensureDb();
290
- const app = await resolveApp(options.app);
291
-
292
- if (!app) {
293
- console.error(`āœ— App "${options.app}" not found`);
294
- process.exit(1);
295
- }
296
-
297
- const pages = await getPagesByAppId(app.id, options.activeOnly);
298
-
299
- if (pages.length === 0) {
300
- console.log(`No pages for "${app.name}". Add one with: service-consolelog page add`);
301
- return;
302
- }
303
-
304
- console.log(`\nPages for "${app.name}":\n`);
305
- for (const page of pages) {
306
- const status = page.active ? "ā—" : "ā—‹";
307
- console.log(` ${status} ${page.path} (ID: ${page.id})`);
308
- if (page.name) {
309
- console.log(` Name: ${page.name}`);
310
- }
311
- if (page.wait_for) {
312
- console.log(` Wait for: ${page.wait_for}`);
313
- }
314
- console.log(` Timeout: ${page.timeout}ms`);
315
- console.log();
316
- }
317
- });
318
-
319
- pageCmd
320
- .command("remove <id>")
321
- .description("Remove a page")
322
- .action(async (id) => {
323
- await ensureDb();
324
- const success = await deletePage(parseInt(id, 10));
325
-
326
- if (success) {
327
- console.log(`āœ“ Removed page ID ${id}`);
328
- } else {
329
- console.error(`āœ— Page ID ${id} not found`);
330
- process.exit(1);
331
- }
332
- });
333
-
334
- // ============ SCAN COMMANDS ============
335
-
336
- program
337
- .command("scan")
338
- .description("Scan pages for console logs")
339
- .option("-a, --app <app>", "App name or ID")
340
- .option("--all", "Scan all active apps")
341
- .option("-p, --pages <paths...>", "Specific paths to scan")
342
- .option("--headless <bool>", "Run headless", (v) => v !== "false")
343
- .action(async (options) => {
344
- await ensureDb();
345
-
346
- if (!options.app && !options.all) {
347
- console.error("āœ— Specify --app or --all");
348
- process.exit(1);
349
- }
350
-
351
- const headless = options.headless ?? isHeadless();
352
- const monitor = getMonitor(headless);
353
- await monitor.start();
354
-
355
- try {
356
- if (options.all) {
357
- const apps = await getAllApps(true);
358
- for (const app of apps) {
359
- await scanApp(app.id, monitor, options.pages);
360
- }
361
- } else {
362
- const app = await resolveApp(options.app);
363
- if (!app) {
364
- console.error(`āœ— App "${options.app}" not found`);
365
- process.exit(1);
366
- }
367
- await scanApp(app.id, monitor, options.pages);
368
- }
369
- } finally {
370
- await shutdownMonitor();
371
- }
372
- });
373
-
374
- // ============ LOG COMMANDS ============
375
-
376
- program
377
- .command("logs")
378
- .description("Query console logs")
379
- .requiredOption("-a, --app <app>", "App name or ID")
380
- .option("-l, --level <level>", "Filter by level (error, warn, info, log, debug)")
381
- .option("-s, --scan <id>", "Filter by scan ID", parseIntOption)
382
- .option("--since <date>", "Filter logs since date (ISO format)")
383
- .option("--until <date>", "Filter logs until date (ISO format)")
384
- .option("--limit <n>", "Limit results", parseIntOption, 50)
385
- .option("--offset <n>", "Offset results", parseIntOption, 0)
386
- .action(async (options) => {
387
- await ensureDb();
388
- const app = await resolveApp(options.app);
389
-
390
- if (!app) {
391
- console.error(`āœ— App "${options.app}" not found`);
392
- process.exit(1);
393
- }
394
-
395
- const logs = await getConsoleLogs({
396
- app_id: app.id,
397
- level: options.level as ConsoleLogLevel,
398
- scan_id: isNaN(options.scan) ? undefined : options.scan,
399
- since: options.since,
400
- until: options.until,
401
- limit: isNaN(options.limit) ? 50 : options.limit,
402
- offset: isNaN(options.offset) ? 0 : options.offset,
403
- });
404
-
405
- if (logs.length === 0) {
406
- console.log("No logs found matching criteria.");
407
- return;
408
- }
409
-
410
- console.log(`\nLogs for "${app.name}" (${logs.length} results):\n`);
411
-
412
- for (const log of logs) {
413
- const levelIcon = getLevelIcon(log.level);
414
- console.log(`${levelIcon} [${log.timestamp}] ${log.level.toUpperCase()}`);
415
- console.log(` ${log.message}`);
416
- if (log.source_url) {
417
- console.log(` at ${log.source_url}:${log.line_number || "?"}:${log.column_number || "?"}`);
418
- }
419
- if (log.stack_trace) {
420
- console.log(` Stack: ${log.stack_trace.split("\n")[0]}...`);
421
- }
422
- console.log();
423
- }
424
- });
425
-
426
- program
427
- .command("history")
428
- .description("View scan history")
429
- .requiredOption("-a, --app <app>", "App name or ID")
430
- .option("--limit <n>", "Limit results", parseIntOption, 10)
431
- .action(async (options) => {
432
- await ensureDb();
433
- const app = await resolveApp(options.app);
434
-
435
- if (!app) {
436
- console.error(`āœ— App "${options.app}" not found`);
437
- process.exit(1);
438
- }
439
-
440
- const scans = await getScans({ app_id: app.id, limit: options.limit });
441
-
442
- if (scans.length === 0) {
443
- console.log(`No scan history for "${app.name}".`);
444
- return;
445
- }
446
-
447
- console.log(`\nScan history for "${app.name}":\n`);
448
-
449
- for (const scan of scans) {
450
- const statusIcon =
451
- scan.status === "completed" ? "āœ“" : scan.status === "failed" ? "āœ—" : "ā—Œ";
452
- console.log(
453
- ` ${statusIcon} Scan #${scan.id} - ${scan.status.toUpperCase()}`
454
- );
455
- console.log(` Started: ${scan.started_at}`);
456
- if (scan.completed_at) {
457
- console.log(` Completed: ${scan.completed_at}`);
458
- }
459
- console.log(
460
- ` Pages: ${scan.pages_scanned} | Errors: ${scan.errors_found}`
461
- );
462
- console.log();
463
- }
464
- });
465
-
466
- // ============ EXPORT COMMAND ============
467
-
468
- program
469
- .command("export")
470
- .description("Export logs to file")
471
- .requiredOption("-a, --app <app>", "App name or ID")
472
- .requiredOption("-o, --output <path>", "Output file path")
473
- .option("-f, --format <format>", "Format: json or csv", "json")
474
- .option("-l, --level <level>", "Filter by level")
475
- .option("--since <date>", "Filter logs since date")
476
- .option("--until <date>", "Filter logs until date")
477
- .action(async (options) => {
478
- await ensureDb();
479
- const app = await resolveApp(options.app);
480
-
481
- if (!app) {
482
- console.error(`āœ— App "${options.app}" not found`);
483
- process.exit(1);
484
- }
485
-
486
- const logs = await getConsoleLogs({
487
- app_id: app.id,
488
- level: options.level as ConsoleLogLevel,
489
- since: options.since,
490
- until: options.until,
491
- });
492
-
493
- let content: string;
494
- if (options.format === "csv") {
495
- const headers = [
496
- "id",
497
- "scan_id",
498
- "page_id",
499
- "level",
500
- "message",
501
- "source_url",
502
- "line_number",
503
- "column_number",
504
- "timestamp",
505
- ];
506
- const rows = logs.map((l) =>
507
- [
508
- l.id,
509
- l.scan_id,
510
- l.page_id,
511
- l.level,
512
- `"${l.message.replace(/"/g, '""')}"`,
513
- l.source_url || "",
514
- l.line_number || "",
515
- l.column_number || "",
516
- l.timestamp,
517
- ].join(",")
518
- );
519
- content = [headers.join(","), ...rows].join("\n");
520
- } else {
521
- content = JSON.stringify(logs, null, 2);
522
- }
523
-
524
- Bun.write(options.output, content);
525
- console.log(`āœ“ Exported ${logs.length} logs to ${options.output}`);
526
- });
527
-
528
- // ============ WATCH COMMANDS ============
529
-
530
- const watchCmd = program.command("watch").description("Continuous monitoring");
531
-
532
- watchCmd
533
- .command("start")
534
- .description("Start watching an app")
535
- .requiredOption("-a, --app <app>", "App name or ID")
536
- .option("-i, --interval <min>", "Interval in minutes", parseIntOption, 5)
537
- .action(async (options) => {
538
- await ensureDb();
539
- const app = await resolveApp(options.app);
540
-
541
- if (!app) {
542
- console.error(`āœ— App "${options.app}" not found`);
543
- process.exit(1);
544
- }
545
-
546
- const intervalMs = options.interval * 60 * 1000;
547
-
548
- try {
549
- const started = await startWatching(app.id, intervalMs);
550
- if (started) {
551
- console.log(`āœ“ Started watching "${app.name}" (interval: ${options.interval}min)`);
552
- logWatchStart(app.name, intervalMs);
553
-
554
- // Keep process alive
555
- console.log("Press Ctrl+C to stop watching.\n");
556
- process.on("SIGINT", async () => {
557
- console.log("\nStopping...");
558
- await stopAllWatchers();
559
- logWatchStop(app.name);
560
- process.exit(0);
561
- });
562
-
563
- // Keep the process running
564
- await new Promise(() => {});
565
- } else {
566
- console.log(`Already watching "${app.name}"`);
567
- }
568
- } catch (error) {
569
- const err = error as Error;
570
- console.error(`āœ— Error: ${err.message}`);
571
- process.exit(1);
572
- }
573
- });
574
-
575
- watchCmd
576
- .command("stop")
577
- .description("Stop watching an app")
578
- .requiredOption("-a, --app <app>", "App name or ID")
579
- .action(async (options) => {
580
- await ensureDb();
581
- const app = await resolveApp(options.app);
582
-
583
- if (!app) {
584
- console.error(`āœ— App "${options.app}" not found`);
585
- process.exit(1);
586
- }
587
-
588
- const stopped = stopWatching(app.id);
589
- if (stopped) {
590
- console.log(`āœ“ Stopped watching "${app.name}"`);
591
- logWatchStop(app.name);
592
- } else {
593
- console.log(`Not watching "${app.name}"`);
594
- }
595
- });
596
-
597
- watchCmd
598
- .command("status")
599
- .description("Check watch status")
600
- .action(async () => {
601
- const statuses = await getWatchStatus();
602
-
603
- if (statuses.length === 0) {
604
- console.log("No apps being watched.");
605
- return;
606
- }
607
-
608
- console.log("\nActive watchers:\n");
609
- for (const status of statuses) {
610
- const intervalMin = (status.intervalMs || 0) / 60000;
611
- console.log(` ā— ${status.appName} (ID: ${status.appId})`);
612
- console.log(` Interval: ${intervalMin}min`);
613
- if (status.lastScan) {
614
- console.log(` Last scan: ${status.lastScan}`);
615
- }
616
- console.log();
617
- }
618
- });
619
-
620
- // ============ CONFIG COMMANDS ============
621
-
622
- program
623
- .command("config")
624
- .description("Manage configuration")
625
- .option("--show", "Show current config")
626
- .option("--path", "Show config file path")
627
- .option("--set-headless <bool>", "Set headless mode", (v) => v === "true")
628
- .option("--set-timeout <ms>", "Set default timeout", parseIntOption)
629
- .option("--set-db-path <path>", "Set database path")
630
- .option("--set-watch-interval <ms>", "Set watch interval", parseIntOption)
631
- .action((options) => {
632
- if (options.path) {
633
- console.log(getConfigPath());
634
- return;
635
- }
636
-
637
- if (options.show) {
638
- const config = loadConfig();
639
- console.log("\nConfiguration:\n");
640
- console.log(` Database path: ${config.databasePath}`);
641
- console.log(` Headless: ${config.headless}`);
642
- console.log(` Default timeout: ${config.defaultTimeout}ms`);
643
- console.log(` Watch interval: ${config.watchInterval}ms`);
644
- console.log(` Log level: ${config.logLevel}`);
645
- console.log(`\n Config file: ${getConfigPath()}`);
646
- return;
647
- }
648
-
649
- const updates: Record<string, unknown> = {};
650
- if (options.setHeadless !== undefined) {
651
- updates.headless = options.setHeadless;
652
- }
653
- if (options.setTimeout !== undefined) {
654
- updates.defaultTimeout = options.setTimeout;
655
- }
656
- if (options.setDbPath !== undefined) {
657
- updates.databasePath = options.setDbPath;
658
- }
659
- if (options.setWatchInterval !== undefined) {
660
- updates.watchInterval = options.setWatchInterval;
661
- }
662
-
663
- if (Object.keys(updates).length > 0) {
664
- saveConfig(updates);
665
- console.log("āœ“ Configuration updated");
666
- } else {
667
- console.log("No changes. Use --show to view config or --set-* to modify.");
668
- }
669
- });
670
-
671
- // ============ SNAPSHOTS COMMAND ============
672
-
673
- program
674
- .command("snapshots")
675
- .description("List snapshots from a scan")
676
- .option("-s, --scan <id>", "Scan ID", parseIntOption)
677
- .option("-a, --app <app>", "App name or ID (shows latest scan)")
678
- .action(async (options) => {
679
- await ensureDb();
680
-
681
- let scanId: number | undefined = options.scan;
682
-
683
- if (!scanId && options.app) {
684
- const app = await resolveApp(options.app);
685
- if (!app) {
686
- console.error(`āœ— App "${options.app}" not found`);
687
- process.exit(1);
688
- }
689
- const lastScan = await getLastScanForApp(app.id);
690
- if (!lastScan) {
691
- console.log(`No scans found for "${app.name}"`);
692
- return;
693
- }
694
- scanId = lastScan.id;
695
- }
696
-
697
- if (!scanId) {
698
- console.error("āœ— Specify --scan or --app");
699
- process.exit(1);
700
- }
701
-
702
- const screenshots = await getScreenshotsByScanId(scanId);
703
-
704
- if (screenshots.length === 0) {
705
- console.log("No screenshots found for this scan.");
706
- return;
707
- }
708
-
709
- console.log(`\nSnapshots for scan #${scanId}:\n`);
710
- for (const ss of screenshots) {
711
- console.log(` šŸ“ø ${ss.filename}`);
712
- console.log(` Path: ${ss.filepath}`);
713
- console.log(` Created: ${ss.created_at}`);
714
- console.log();
715
- }
716
-
717
- console.log(`Total: ${screenshots.length} screenshots`);
718
- console.log(`\nTo view a screenshot, open the path in your file viewer.`);
719
- });
720
-
721
- // ============ SERVER COMMAND ============
722
-
723
- program
724
- .command("server")
725
- .description("Start the HTTP API server")
726
- .option("-p, --port <port>", "Server port", parseIntOption, 3100)
727
- .action(async (options) => {
728
- await ensureDb();
729
- console.log(`Starting server on port ${options.port}...`);
730
- console.log(`See: http://localhost:${options.port}/health`);
731
-
732
- // Import and start server
733
- const { startServer } = await import("../src/server/index");
734
- startServer(options.port);
735
- });
736
-
737
- // ============ HELPER FUNCTIONS ============
738
-
739
- async function ensureDb(): Promise<void> {
740
- getDb();
741
- await createTables();
742
- }
743
-
744
- async function resolveApp(idOrName: string) {
745
- const id = parseInt(idOrName, 10);
746
- if (!isNaN(id)) {
747
- return await getAppById(id);
748
- }
749
- return await getAppByName(idOrName);
750
- }
751
-
752
- async function scanApp(
753
- appId: number,
754
- monitor: ConsoleMonitor,
755
- specificPaths?: string[]
756
- ): Promise<void> {
757
- const app = await getAppById(appId);
758
- if (!app) {
759
- console.error(`āœ— App ID ${appId} not found`);
760
- return;
761
- }
762
-
763
- let pages = await getPagesByAppId(appId, true);
764
-
765
- if (specificPaths && specificPaths.length > 0) {
766
- pages = pages.filter((p) => specificPaths.includes(p.path));
767
- }
768
-
769
- if (pages.length === 0) {
770
- console.log(`No pages to scan for "${app.name}"`);
771
- return;
772
- }
773
-
774
- // Ensure snapshots directory exists
775
- const snapshotsDir = ensureSnapshotsDir();
776
-
777
- console.log(`\nScanning "${app.name}" (${pages.length} pages)...\n`);
778
- logScanStart(app.name, pages.length);
779
-
780
- const scan = await createScan({ app_id: appId });
781
- let totalErrors = 0;
782
- let pagesScanned = 0;
783
- const screenshotPaths: string[] = [];
784
-
785
- for (const page of pages) {
786
- const url = `${app.base_url}${page.path}`;
787
- process.stdout.write(` Scanning ${page.path}... `);
788
-
789
- // Generate screenshot filename
790
- const screenshotFilename = generateScreenshotFilename(page.name, page.path);
791
- const screenshotFullPath = getScreenshotPath(screenshotFilename);
792
-
793
- try {
794
- const result = await monitor.scanPage(url, {
795
- timeout: page.timeout,
796
- waitFor: page.wait_for || undefined,
797
- captureScreenshot: true,
798
- screenshotDir: screenshotFullPath,
799
- });
800
-
801
- const { logs, screenshotPath } = result;
802
-
803
- // Save screenshot record if captured
804
- if (screenshotPath) {
805
- await createScreenshot({
806
- scan_id: scan.id,
807
- page_id: page.id,
808
- filename: screenshotFilename,
809
- filepath: screenshotPath,
810
- });
811
- screenshotPaths.push(screenshotPath);
812
- }
813
-
814
- if (logs.length > 0) {
815
- const logInputs: CreateConsoleLogInput[] = logs.map((log) => ({
816
- scan_id: scan.id,
817
- page_id: page.id,
818
- level: log.level as ConsoleLogLevel,
819
- message: log.message,
820
- source_url: log.sourceUrl,
821
- line_number: log.lineNumber,
822
- column_number: log.columnNumber,
823
- stack_trace: log.stackTrace,
824
- }));
825
-
826
- await createConsoleLogsBatch(logInputs);
827
-
828
- const errorCount = logs.filter((l) => l.level === "error").length;
829
- const warnCount = logs.filter((l) => l.level === "warn").length;
830
- totalErrors += errorCount;
831
-
832
- console.log(`${logs.length} logs (${errorCount} errors, ${warnCount} warnings)`);
833
- } else {
834
- console.log("clean");
835
- }
836
-
837
- if (screenshotPath) {
838
- console.log(` šŸ“ø ${screenshotPath}`);
839
- }
840
-
841
- pagesScanned++;
842
- } catch (error) {
843
- const err = error as Error;
844
- console.log(`failed: ${err.message}`);
845
- logError(`Failed to scan ${url}: ${err.message}`);
846
- }
847
- }
848
-
849
- await updateScan(scan.id, {
850
- status: "completed",
851
- pages_scanned: pagesScanned,
852
- errors_found: totalErrors,
853
- completed_at: new Date().toISOString(),
854
- });
855
-
856
- console.log(`\nāœ“ Scan complete: ${pagesScanned} pages, ${totalErrors} errors`);
857
- if (screenshotPaths.length > 0) {
858
- console.log(`šŸ“ø Screenshots saved to: ${getSnapshotsDir()}`);
859
- console.log(`\nScreenshot paths:`);
860
- for (const path of screenshotPaths) {
861
- console.log(` ${path}`);
862
- }
863
- }
864
- console.log();
865
- logScanComplete(app.name, pagesScanned, totalErrors);
866
- }
867
-
868
- function getLevelIcon(level: string): string {
869
- switch (level) {
870
- case "error":
871
- return "āŒ";
872
- case "warn":
873
- return "āš ļø";
874
- case "info":
875
- return "ā„¹ļø";
876
- case "debug":
877
- return "šŸ”";
878
- default:
879
- return "šŸ“";
880
- }
881
- }
882
-
883
- // Run CLI
884
- program.parse(process.argv);