@solidnumber/cli 1.25.0 → 2.1.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 (42) hide show
  1. package/README.md +40 -1
  2. package/dist/commands/audit.js +61 -0
  3. package/dist/commands/audit.js.map +1 -1
  4. package/dist/commands/graph.d.ts.map +1 -1
  5. package/dist/commands/graph.js +17 -7
  6. package/dist/commands/graph.js.map +1 -1
  7. package/dist/commands/render.d.ts +28 -0
  8. package/dist/commands/render.d.ts.map +1 -0
  9. package/dist/commands/render.js +243 -0
  10. package/dist/commands/render.js.map +1 -0
  11. package/dist/commands/schema.js +57 -0
  12. package/dist/commands/schema.js.map +1 -1
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/api-client.d.ts.map +1 -1
  16. package/dist/lib/api-client.js +45 -0
  17. package/dist/lib/api-client.js.map +1 -1
  18. package/dist/lib/block-example-synth.d.ts +31 -0
  19. package/dist/lib/block-example-synth.d.ts.map +1 -0
  20. package/dist/lib/block-example-synth.js +148 -0
  21. package/dist/lib/block-example-synth.js.map +1 -0
  22. package/dist/lib/block-types.d.ts +28 -0
  23. package/dist/lib/block-types.d.ts.map +1 -0
  24. package/dist/lib/block-types.js +7 -0
  25. package/dist/lib/block-types.js.map +1 -0
  26. package/dist/lib/browser-install.d.ts +66 -0
  27. package/dist/lib/browser-install.d.ts.map +1 -0
  28. package/dist/lib/browser-install.js +178 -0
  29. package/dist/lib/browser-install.js.map +1 -0
  30. package/dist/lib/error-codes.d.ts +7 -3
  31. package/dist/lib/error-codes.d.ts.map +1 -1
  32. package/dist/lib/error-codes.js +12 -5
  33. package/dist/lib/error-codes.js.map +1 -1
  34. package/dist/lib/lighthouse-runner.d.ts +64 -0
  35. package/dist/lib/lighthouse-runner.d.ts.map +1 -0
  36. package/dist/lib/lighthouse-runner.js +168 -0
  37. package/dist/lib/lighthouse-runner.js.map +1 -0
  38. package/dist/lib/render-utils.d.ts +35 -0
  39. package/dist/lib/render-utils.d.ts.map +1 -0
  40. package/dist/lib/render-utils.js +78 -0
  41. package/dist/lib/render-utils.js.map +1 -0
  42. package/package.json +2 -1
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * Lighthouse driver for `solid audit a11y|perf|mobile` (A.5).
4
+ *
5
+ * Wraps the `lighthouse` npm package + the same Chromium that powers
6
+ * `solid render` (via `lib/browser-install.ts`). Lighthouse expects to
7
+ * connect to a Chrome with --remote-debugging-port; puppeteer-core
8
+ * gives us that for free.
9
+ *
10
+ * Pure plumbing: launches Chrome, runs lighthouse, returns the trimmed
11
+ * audit. No Commander, no UI — those live in `commands/audit.ts`.
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.extractIssues = extractIssues;
48
+ exports.runLighthouse = runLighthouse;
49
+ const browser_install_1 = require("./browser-install");
50
+ /** Map our short category names → Lighthouse's category IDs +
51
+ * configuration. `mobile` is special: it's not a separate category
52
+ * in Lighthouse — it's a form-factor toggle that runs the perf+a11y
53
+ * audit on a throttled mobile profile. */
54
+ function categoryConfig(cat) {
55
+ switch (cat) {
56
+ case 'a11y': return { id: 'accessibility', formFactor: 'desktop', label: 'Accessibility' };
57
+ case 'perf': return { id: 'performance', formFactor: 'desktop', label: 'Performance' };
58
+ case 'mobile': return { id: 'performance', formFactor: 'mobile', label: 'Mobile Performance' };
59
+ }
60
+ }
61
+ /** Map Lighthouse audit score → our 4-level severity bucket. */
62
+ function bucketSeverity(score) {
63
+ if (score === null)
64
+ return 'minor';
65
+ if (score >= 0.9)
66
+ return 'pass';
67
+ if (score >= 0.5)
68
+ return 'minor';
69
+ if (score >= 0.1)
70
+ return 'moderate';
71
+ return 'serious';
72
+ }
73
+ /**
74
+ * Trim a full Lighthouse report down to the issues that matter for
75
+ * the chosen category. Pure function — exported for unit testing.
76
+ */
77
+ function extractIssues(rawReport, categoryId) {
78
+ const audits = rawReport.audits || {};
79
+ const cat = (rawReport.categories || {})[categoryId];
80
+ if (!cat || !cat.auditRefs)
81
+ return [];
82
+ const issues = [];
83
+ for (const ref of cat.auditRefs) {
84
+ const audit = audits[ref.id];
85
+ if (!audit)
86
+ continue;
87
+ // Skip "manual" / "notApplicable" / "informative" — only score-bearing
88
+ if (audit.scoreDisplayMode && ['manual', 'notApplicable', 'informative'].includes(audit.scoreDisplayMode)) {
89
+ continue;
90
+ }
91
+ issues.push({
92
+ rule: audit.id || ref.id,
93
+ title: audit.title || ref.id,
94
+ description: audit.description || '',
95
+ score: audit.score ?? null,
96
+ severity: bucketSeverity(audit.score ?? null),
97
+ });
98
+ }
99
+ // Sort: serious → moderate → minor → pass (we keep `pass` so the
100
+ // caller can show "all green" if they want).
101
+ const order = { serious: 0, moderate: 1, minor: 2, pass: 3 };
102
+ issues.sort((a, b) => order[a.severity] - order[b.severity]);
103
+ return issues.slice(0, 50);
104
+ }
105
+ /**
106
+ * Run Lighthouse against a URL. Launches its own Chromium so the call
107
+ * is self-contained; auto-installs Chromium if needed.
108
+ *
109
+ * @throws if the URL doesn't load, Chrome can't launch, or Lighthouse
110
+ * returns no result.
111
+ */
112
+ async function runLighthouse(url, category, opts = {}) {
113
+ const config = categoryConfig(category);
114
+ // Ensure Chromium is available; same lib as `solid render`. If
115
+ // we're called in a non-interactive context (CI, agent) and the
116
+ // browser isn't cached, ensureChromium throws — caller surfaces it.
117
+ const browserResult = await (0, browser_install_1.ensureChromium)();
118
+ // Lazy-import puppeteer-core + lighthouse so unrelated commands
119
+ // pay zero cost.
120
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
121
+ const puppeteer = require('puppeteer-core');
122
+ // Lighthouse v13 ESM-only — use dynamic import.
123
+ const lighthouseModule = await Promise.resolve().then(() => __importStar(require('lighthouse')));
124
+ const lighthouse = (lighthouseModule.default ?? lighthouseModule);
125
+ const browser = await puppeteer.launch({
126
+ executablePath: browserResult.executablePath,
127
+ headless: true,
128
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
129
+ });
130
+ try {
131
+ // Lighthouse needs the CDP port. Puppeteer's wsEndpoint embeds it.
132
+ const wsEndpoint = browser.wsEndpoint();
133
+ const portMatch = wsEndpoint.match(/:(\d+)\//);
134
+ if (!portMatch) {
135
+ throw new Error('Could not extract CDP port from puppeteer wsEndpoint');
136
+ }
137
+ const port = parseInt(portMatch[1], 10);
138
+ const flags = {
139
+ port,
140
+ output: 'json',
141
+ logLevel: 'error',
142
+ onlyCategories: [config.id],
143
+ formFactor: config.formFactor,
144
+ // Lighthouse's "screenEmulation" picks defaults; specifying mobile
145
+ // flips screen + UA. Defaults are fine for a v1.
146
+ screenEmulation: config.formFactor === 'mobile' ? undefined : { disabled: true },
147
+ };
148
+ const result = await lighthouse(url, flags);
149
+ if (!result || !result.lhr) {
150
+ throw new Error('Lighthouse returned no result');
151
+ }
152
+ const lhr = result.lhr;
153
+ const catResult = (lhr.categories || {})[config.id];
154
+ const score = catResult && typeof catResult.score === 'number' ? Math.round(catResult.score * 100) : 0;
155
+ const issues = extractIssues(lhr, config.id);
156
+ return {
157
+ score,
158
+ category,
159
+ url,
160
+ issues,
161
+ raw: opts.keepRaw ? lhr : undefined,
162
+ };
163
+ }
164
+ finally {
165
+ await browser.close().catch(() => { });
166
+ }
167
+ }
168
+ //# sourceMappingURL=lighthouse-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lighthouse-runner.js","sourceRoot":"","sources":["../../src/lib/lighthouse-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Pure helpers for `solid render` (A.1).
3
+ *
4
+ * Lives in `lib/` (not `commands/render.ts`) so unit tests can import
5
+ * the pure functions without dragging in ora / puppeteer / commander
6
+ * — those are ESM-flaky under jest. Same module structure used
7
+ * everywhere else in the CLI: business logic → lib, wiring → commands.
8
+ */
9
+ export interface Viewport {
10
+ width: number;
11
+ height: number;
12
+ name: string;
13
+ }
14
+ export declare const NAMED_BREAKPOINTS: Record<string, Viewport>;
15
+ /**
16
+ * Parse a `--viewport WxH` value.
17
+ *
18
+ * @throws Error with a clear message on bad input.
19
+ */
20
+ export declare function parseViewport(spec: string): Viewport;
21
+ /**
22
+ * Resolve `--breakpoint <list>` (comma-separated names) to viewports.
23
+ * Empty input → desktop default.
24
+ */
25
+ export declare function resolveBreakpoints(spec: string | undefined): Viewport[];
26
+ /**
27
+ * Build the URL to render. Special-cases 'home' / '/' to the bare base.
28
+ */
29
+ export declare function buildRenderUrl(baseUrl: string, slug: string): string;
30
+ /**
31
+ * Output filename for a screenshot. Stable + predictable so agents can
32
+ * compute it before the command runs.
33
+ */
34
+ export declare function outputPath(outDir: string, slug: string, viewport: Viewport): string;
35
+ //# sourceMappingURL=render-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../src/lib/render-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAKtD,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAWpD;AAGD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,EAAE,CAWvE;AAGD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpE;AAGD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAOnF"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * Pure helpers for `solid render` (A.1).
4
+ *
5
+ * Lives in `lib/` (not `commands/render.ts`) so unit tests can import
6
+ * the pure functions without dragging in ora / puppeteer / commander
7
+ * — those are ESM-flaky under jest. Same module structure used
8
+ * everywhere else in the CLI: business logic → lib, wiring → commands.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.NAMED_BREAKPOINTS = void 0;
12
+ exports.parseViewport = parseViewport;
13
+ exports.resolveBreakpoints = resolveBreakpoints;
14
+ exports.buildRenderUrl = buildRenderUrl;
15
+ exports.outputPath = outputPath;
16
+ exports.NAMED_BREAKPOINTS = {
17
+ mobile: { width: 375, height: 667, name: 'mobile' },
18
+ tablet: { width: 768, height: 1024, name: 'tablet' },
19
+ desktop: { width: 1440, height: 900, name: 'desktop' },
20
+ full: { width: 1920, height: 1080, name: 'full' },
21
+ };
22
+ /**
23
+ * Parse a `--viewport WxH` value.
24
+ *
25
+ * @throws Error with a clear message on bad input.
26
+ */
27
+ function parseViewport(spec) {
28
+ const m = spec.trim().match(/^(\d+)x(\d+)$/i);
29
+ if (!m) {
30
+ throw new Error(`Invalid viewport '${spec}'. Use the form WIDTHxHEIGHT, e.g. 1280x720.`);
31
+ }
32
+ const width = parseInt(m[1], 10);
33
+ const height = parseInt(m[2], 10);
34
+ if (width < 100 || height < 100 || width > 8000 || height > 8000) {
35
+ throw new Error(`Viewport ${width}x${height} is out of range (100..8000 each axis).`);
36
+ }
37
+ return { width, height, name: `${width}x${height}` };
38
+ }
39
+ /**
40
+ * Resolve `--breakpoint <list>` (comma-separated names) to viewports.
41
+ * Empty input → desktop default.
42
+ */
43
+ function resolveBreakpoints(spec) {
44
+ if (!spec)
45
+ return [exports.NAMED_BREAKPOINTS.full];
46
+ const names = spec.split(',').map((s) => s.trim()).filter(Boolean);
47
+ return names.map((name) => {
48
+ const bp = exports.NAMED_BREAKPOINTS[name.toLowerCase()];
49
+ if (!bp) {
50
+ const valid = Object.keys(exports.NAMED_BREAKPOINTS).join(', ');
51
+ throw new Error(`Unknown breakpoint '${name}'. Valid: ${valid}.`);
52
+ }
53
+ return bp;
54
+ });
55
+ }
56
+ /**
57
+ * Build the URL to render. Special-cases 'home' / '/' to the bare base.
58
+ */
59
+ function buildRenderUrl(baseUrl, slug) {
60
+ const cleanBase = baseUrl.replace(/\/+$/, '');
61
+ if (slug === 'home' || slug === '/')
62
+ return cleanBase + '/';
63
+ const cleanSlug = slug.startsWith('/') ? slug : `/${slug}`;
64
+ return cleanBase + cleanSlug;
65
+ }
66
+ /**
67
+ * Output filename for a screenshot. Stable + predictable so agents can
68
+ * compute it before the command runs.
69
+ */
70
+ function outputPath(outDir, slug, viewport) {
71
+ // path.join is platform-aware; we keep this lib std-only so importers
72
+ // need to pass `path.join` through opts if they want strict separators.
73
+ // For our use case the simple template is enough.
74
+ const cleanSlug = slug.replace(/^\/+|\/+$/g, '').replace(/\//g, '_') || 'home';
75
+ const sep = outDir.endsWith('/') || outDir.endsWith('\\') ? '' : '/';
76
+ return `${outDir}${sep}${cleanSlug}-${viewport.width}x${viewport.height}.png`;
77
+ }
78
+ //# sourceMappingURL=render-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../../src/lib/render-utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidnumber/cli",
3
- "version": "1.25.0",
3
+ "version": "2.1.0",
4
4
  "description": "AI business infrastructure from the terminal — CRM, payments, voice AI, 116 agents, 52 industry templates. solid clone plumber → instant business. Also: programmatic TS SDK via @solidnumber/cli/sdk.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -88,6 +88,7 @@
88
88
  "inquirer": "^9.2.0",
89
89
  "jsonld": "^9.0.0",
90
90
  "ora": "^8.0.0",
91
+ "puppeteer-core": "^24.42.0",
91
92
  "update-notifier": "^7.3.1"
92
93
  },
93
94
  "optionalDependencies": {