laxy-verify 1.1.0 → 1.1.2

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 (3) hide show
  1. package/README.md +212 -0
  2. package/dist/auth.js +44 -33
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -5,9 +5,221 @@ Frontend quality gate for AI-generated code. Build + Lighthouse verification wit
5
5
  ```bash
6
6
  npx laxy-verify --init # Auto-detect framework, generate config
7
7
  npx laxy-verify . # Run verification
8
+ npx laxy-verify login # Log in for Pro/Pro+ features
8
9
  npx laxy-verify --badge # Show shields.io badge
9
10
  ```
10
11
 
12
+ ## Quick Start
13
+
14
+ ### 1. Initialize
15
+
16
+ ```bash
17
+ cd your-project
18
+ npx laxy-verify --init
19
+ ```
20
+
21
+ This generates `.laxy.yml` and `.github/workflows/laxy-verify.yml` automatically by detecting your framework and package manager.
22
+
23
+ ### 2. Run Locally
24
+
25
+ ```bash
26
+ npx laxy-verify .
27
+ ```
28
+
29
+ ### 3. Add to CI
30
+
31
+ Push the generated workflow file. Every PR gets a quality gate with grade comment and status check.
32
+
33
+ ## Grades
34
+
35
+ | Grade | Requirement |
36
+ |-------|------------|
37
+ | **Gold** | Build passes + all Lighthouse thresholds pass + all viewports pass (Pro+ only) |
38
+ | **Silver** | Build passes + Lighthouse exceeds all thresholds |
39
+ | **Bronze** | Build passes (Lighthouse not run or below threshold) |
40
+ | **Unverified** | Build failed |
41
+
42
+ `fail_on` controls the minimum acceptable grade. Default: `bronze`.
43
+
44
+ > **Gold grade** is only achievable with a Pro+ account. It requires multi-viewport Lighthouse (desktop/tablet/mobile) to all pass in addition to the standard thresholds.
45
+
46
+ ## Pro / Pro+ Features
47
+
48
+ Log in to unlock paid plan features:
49
+
50
+ ```bash
51
+ npx laxy-verify login # Log in with your laxy-blue.vercel.app account
52
+ npx laxy-verify whoami # Check current login status
53
+ npx laxy-verify logout # Remove saved credentials
54
+ ```
55
+
56
+ | Feature | Free | Pro | Pro+ |
57
+ |---------|------|-----|------|
58
+ | Build verification | ✅ | ✅ | ✅ |
59
+ | Lighthouse (1 run) | ✅ | ✅ | ✅ |
60
+ | Silver grade | ✅ | ✅ | ✅ |
61
+ | Bronze grade | ✅ | ✅ | ✅ |
62
+ | Lighthouse (3 runs, averaged) | ❌ | ✅ | ✅ |
63
+ | Gold grade eligibility | ❌ | ✅ | ✅ |
64
+ | Verbose failure output | ❌ | ✅ | ✅ |
65
+ | Multi-viewport Lighthouse (desktop/tablet/mobile) | ❌ | ❌ | ✅ |
66
+ | Failure analysis report | ❌ | ❌ | ✅ |
67
+ | Fast Lane (priority queue) | ❌ | ❌ | ✅ |
68
+
69
+ ### Login
70
+
71
+ ```bash
72
+ npx laxy-verify login
73
+ # Enter your email and password for laxy-blue.vercel.app
74
+ ```
75
+
76
+ Credentials are stored in `~/.laxy/credentials.json` (chmod 600). Token expires after 30 days.
77
+
78
+ For CI environments, set the `LAXY_TOKEN` environment variable instead of logging in interactively:
79
+
80
+ ```yaml
81
+ env:
82
+ LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
83
+ ```
84
+
85
+ ### Multi-viewport (Pro+)
86
+
87
+ ```bash
88
+ npx laxy-verify --multi-viewport .
89
+ ```
90
+
91
+ Runs Lighthouse on three viewports: desktop, tablet (1024×768), and mobile. Gold grade requires all three to pass the configured thresholds.
92
+
93
+ ## Configuration
94
+
95
+ All fields optional in `.laxy.yml`:
96
+
97
+ ```yaml
98
+ framework: "auto" # auto | nextjs | vite | cra | sveltekit
99
+ build_command: "" # default: auto-detected from package.json
100
+ dev_command: "" # default: auto-detected
101
+ package_manager: "auto" # auto | npm | pnpm | yarn | bun
102
+ port: 3000 # dev server port
103
+ build_timeout: 300 # seconds (default 5m)
104
+ dev_timeout: 60 # seconds for dev server start (90 in CI mode)
105
+ lighthouse_runs: 1 # @lhci/cli runs (CI mode auto-sets to 3)
106
+
107
+ thresholds:
108
+ performance: 70 # CI mode applies -10 offset (effective: 60)
109
+ accessibility: 85
110
+ seo: 80
111
+ best_practices: 80
112
+
113
+ fail_on: "bronze" # unverified | bronze | silver | gold
114
+ # unverified = never fail (informational only)
115
+ ```
116
+
117
+ **fail_on vs build failure:** Build failure always produces grade `Unverified` and exit code 1, regardless of `fail_on`. `fail_on: unverified` means informational only (always exit 0).
118
+
119
+ **--ci flag:** Lowers Performance threshold by 10, sets `lighthouse_runs=3` (when not explicitly set), and increases `dev_timeout` to 90s. Auto-set when `CI=true` env var exists.
120
+
121
+ ## CLI Options
122
+
123
+ ```
124
+ npx laxy-verify [project-dir] Default: current directory
125
+
126
+ Options:
127
+ --format console | json Output format (default: console)
128
+ --ci CI mode: -10 Performance, runs=3
129
+ --config <path> Path to .laxy.yml
130
+ --fail-on unverified|bronze|silver|gold Override fail_on
131
+ --skip-lighthouse Build-only verification (max Bronze)
132
+ --badge Show shields.io badge (reads .laxy-result.json)
133
+ --init Generate .laxy.yml + GitHub workflow
134
+ --multi-viewport Pro+: run Lighthouse on desktop/tablet/mobile
135
+
136
+ Subcommands:
137
+ login [email] Log in to unlock Pro/Pro+ features
138
+ logout Remove saved credentials
139
+ whoami Show current login status
140
+ ```
141
+
142
+ ## exit codes
143
+
144
+ | Code | Meaning |
145
+ |------|--------|
146
+ | 0 | Grade meets or exceeds `fail_on` threshold |
147
+ | 1 | Grade worse than `fail_on`, or build failed |
148
+ | 2 | Configuration error (no package.json, invalid YAML, etc.) |
149
+
150
+ ## GitHub Action
151
+
152
+ ```yaml
153
+ - uses: psungmin24/laxy-verify@v1
154
+ with:
155
+ github-token: ${{ secrets.GITHUB_TOKEN }}
156
+ fail-on: silver # optional, default: bronze
157
+ config: .laxy.yml # optional
158
+ ```
159
+
160
+ Posts a PR comment with grade and Lighthouse scores, and sets a commit status check.
161
+
162
+ For Pro/Pro+ features in CI, add `LAXY_TOKEN` to your repository secrets:
163
+
164
+ ```yaml
165
+ - uses: psungmin24/laxy-verify@v1
166
+ with:
167
+ github-token: ${{ secrets.GITHUB_TOKEN }}
168
+ fail-on: gold # Gold requires Pro+ account
169
+ env:
170
+ LAXY_TOKEN: ${{ secrets.LAXY_TOKEN }}
171
+ ```
172
+
173
+ ## Limitations (v1)
174
+
175
+ - **Monorepos:** Not supported. Run `npx laxy-verify apps/web` for the app subdirectory.
176
+ - **Lighthouse accuracy:** Scores are measured in dev mode (`npm run dev`). Production scores are typically higher.
177
+ - **Fork PRs:** Comments and status checks are not posted on PRs from forks (GitHub security restriction).
178
+ - **Yarn Berry:** May require manual `package_manager` configuration if using Corepack.
179
+ - **Gold grade:** Requires Pro+ plan and `--multi-viewport` flag. All three viewports must pass Lighthouse thresholds.
180
+
181
+ ## Expected CI Timing
182
+
183
+ - Build: 15-30s
184
+ - Dev server start: 5-20s
185
+ - Lighthouse (1 run): ~15s
186
+ - Lighthouse (3 runs CI): ~45s
187
+ - Multi-viewport (Pro+, 3 viewports): ~60s
188
+ - **Total: 35-95s per PR**
189
+
190
+ ## .laxy-result.json
191
+
192
+ Written after every run:
193
+
194
+ ```json
195
+ {
196
+ "grade": "Silver",
197
+ "timestamp": "2026-04-07T10:30:00Z",
198
+ "build": { "success": true, "durationMs": 12300, "errors": [] },
199
+ "lighthouse": { "performance": 87, "accessibility": 92, "seo": 88, "bestPractices": 90, "runs": 1 },
200
+ "thresholds": { "performance": 70, "accessibility": 85, "seo": 80, "bestPractices": 80 },
201
+ "ciMode": false,
202
+ "framework": "nextjs",
203
+ "exitCode": 0,
204
+ "config_fail_on": "bronze"
205
+ }
206
+ ```
207
+
208
+ Use `npx laxy-verify --badge` to output a shields.io badge markdown from this file.
209
+
210
+ ## Badge
211
+
212
+ ```bash
213
+ npx laxy-verify --badge
214
+ ```
215
+
216
+ Output: `![Laxy Verify: Silver](https://img.shields.io/badge/laxy_verify-silver-brightgreen)`
217
+
218
+ ## License
219
+
220
+ MIT
221
+
222
+
11
223
  ## Quick Start
12
224
 
13
225
  ### 1. Initialize
package/dist/auth.js CHANGED
@@ -52,7 +52,7 @@ const os = __importStar(require("node:os"));
52
52
  const readline = __importStar(require("node:readline"));
53
53
  const CREDENTIALS_DIR = path.join(os.homedir(), ".laxy");
54
54
  const CREDENTIALS_PATH = path.join(CREDENTIALS_DIR, "credentials.json");
55
- exports.LAXY_API_URL = process.env.LAXY_API_URL ?? "https://laxy.io";
55
+ exports.LAXY_API_URL = process.env.LAXY_API_URL ?? "https://laxy-blue.vercel.app";
56
56
  function loadToken() {
57
57
  // 환경변수 우선
58
58
  const envToken = process.env.LAXY_TOKEN;
@@ -121,48 +121,48 @@ function whoami() {
121
121
  console.log(" 인증 정보를 읽을 수 없습니다.");
122
122
  }
123
123
  }
124
- /** readline으로 비밀번호 입력 (터미널 에코 숨김) */
125
- function promptPassword(prompt) {
124
+ /** readline으로 입력 */
125
+ function askQuestion(prompt, mute = false) {
126
126
  return new Promise((resolve) => {
127
127
  const rl = readline.createInterface({
128
128
  input: process.stdin,
129
- output: process.stdout,
130
- terminal: true,
129
+ output: mute ? undefined : process.stdout,
130
+ terminal: false,
131
131
  });
132
- process.stdout.write(prompt);
133
- // 에코 끄기 (Unix 계열만, Windows는 readline이 처리)
134
- if (process.stdin.isTTY) {
135
- // @ts-ignore — private API
136
- process.stdin._handle?.setRawMode?.(true);
132
+ if (mute) {
133
+ process.stdout.write(prompt);
137
134
  }
138
- let password = "";
139
- process.stdin.on("data", (chunk) => {
140
- const char = chunk.toString("utf-8");
141
- if (char === "\n" || char === "\r" || char === "\u0004") {
135
+ // readline.question 대신 line 이벤트 사용 (Windows 호환성)
136
+ let answered = false;
137
+ rl.question(mute ? "" : prompt, (ans) => {
138
+ if (answered)
139
+ return;
140
+ answered = true;
141
+ if (mute)
142
142
  process.stdout.write("\n");
143
- rl.close();
144
- resolve(password);
145
- }
146
- else if (char === "\u0008" || char === "\u007f") {
147
- password = password.slice(0, -1);
148
- }
149
- else {
150
- password += char;
151
- }
143
+ rl.close();
144
+ rl.removeAllListeners();
145
+ resolve(ans.trim());
152
146
  });
147
+ // 비밀번호 모드에서 라인 이벤트 수동 처리 (일부 Windows 터미널 호환)
148
+ if (mute) {
149
+ rl.on("line", (line) => {
150
+ if (answered)
151
+ return;
152
+ answered = true;
153
+ process.stdout.write("\n");
154
+ rl.close();
155
+ rl.removeAllListeners();
156
+ resolve(line.trim());
157
+ });
158
+ }
153
159
  });
154
160
  }
155
161
  async function login(emailArg) {
156
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
157
- const askEmail = () => {
158
- if (emailArg) {
159
- rl.close();
160
- return Promise.resolve(emailArg);
161
- }
162
- return new Promise((res) => rl.question(" 이메일: ", (ans) => { rl.close(); res(ans.trim()); }));
163
- };
164
- const email = await askEmail();
165
- const password = await promptPassword(" 비밀번호: ");
162
+ const email = emailArg
163
+ ? emailArg.trim()
164
+ : await askQuestion(" 이메일: ");
165
+ const password = await askQuestion(" 비밀번호: ", true);
166
166
  console.log("\n 로그인 중...");
167
167
  let res;
168
168
  try {
@@ -176,6 +176,17 @@ async function login(emailArg) {
176
176
  console.error(` ❌ 서버에 연결할 수 없습니다. (${exports.LAXY_API_URL})`);
177
177
  process.exit(1);
178
178
  }
179
+ // HTML(404/500 에러 페이지) 응답 감지
180
+ const contentType = res.headers.get("content-type") ?? "";
181
+ if (!contentType.includes("application/json")) {
182
+ const body = await res.text();
183
+ const preview = body.slice(0, 200).replace(/\n/g, " ");
184
+ console.error(` ❌ 서버가 JSON이 아닌 응답을 반환했습니다. (HTTP ${res.status})`);
185
+ console.error(` URL: ${exports.LAXY_API_URL}/api/cli-auth`);
186
+ console.error(` 응답 미리보기: ${preview}`);
187
+ console.error(` 해결 방법: Vercel 환경변수(CLI_JWT_SECRET, SUPABASE_SERVICE_ROLE_KEY)가 설정됐는지 확인하세요.`);
188
+ process.exit(1);
189
+ }
179
190
  const data = (await res.json());
180
191
  if (!res.ok || !data.token) {
181
192
  console.error(` ❌ ${data.error ?? "로그인에 실패했습니다."}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laxy-verify",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Frontend quality gate: build + Lighthouse verification",
5
5
  "type": "commonjs",
6
6
  "bin": {