@glubean/cli 0.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 (113) hide show
  1. package/bin/gb.js +2 -0
  2. package/dist/commands/init.d.ts +19 -0
  3. package/dist/commands/init.d.ts.map +1 -0
  4. package/dist/commands/init.js +842 -0
  5. package/dist/commands/init.js.map +1 -0
  6. package/dist/commands/login.d.ts +10 -0
  7. package/dist/commands/login.d.ts.map +1 -0
  8. package/dist/commands/login.js +75 -0
  9. package/dist/commands/login.js.map +1 -0
  10. package/dist/commands/patch.d.ts +8 -0
  11. package/dist/commands/patch.d.ts.map +1 -0
  12. package/dist/commands/patch.js +73 -0
  13. package/dist/commands/patch.js.map +1 -0
  14. package/dist/commands/run.d.ts +26 -0
  15. package/dist/commands/run.d.ts.map +1 -0
  16. package/dist/commands/run.js +1093 -0
  17. package/dist/commands/run.js.map +1 -0
  18. package/dist/commands/scan.d.ts +6 -0
  19. package/dist/commands/scan.d.ts.map +1 -0
  20. package/dist/commands/scan.js +62 -0
  21. package/dist/commands/scan.js.map +1 -0
  22. package/dist/commands/spec_split.d.ts +5 -0
  23. package/dist/commands/spec_split.d.ts.map +1 -0
  24. package/dist/commands/spec_split.js +56 -0
  25. package/dist/commands/spec_split.js.map +1 -0
  26. package/dist/commands/sync.d.ts +13 -0
  27. package/dist/commands/sync.d.ts.map +1 -0
  28. package/dist/commands/sync.js +252 -0
  29. package/dist/commands/sync.js.map +1 -0
  30. package/dist/commands/trigger.d.ts +13 -0
  31. package/dist/commands/trigger.d.ts.map +1 -0
  32. package/dist/commands/trigger.js +213 -0
  33. package/dist/commands/trigger.js.map +1 -0
  34. package/dist/commands/validate_metadata.d.ts +6 -0
  35. package/dist/commands/validate_metadata.d.ts.map +1 -0
  36. package/dist/commands/validate_metadata.js +103 -0
  37. package/dist/commands/validate_metadata.js.map +1 -0
  38. package/dist/commands/worker.d.ts +14 -0
  39. package/dist/commands/worker.d.ts.map +1 -0
  40. package/dist/commands/worker.js +10 -0
  41. package/dist/commands/worker.js.map +1 -0
  42. package/dist/lib/auth.d.ts +39 -0
  43. package/dist/lib/auth.d.ts.map +1 -0
  44. package/dist/lib/auth.js +82 -0
  45. package/dist/lib/auth.js.map +1 -0
  46. package/dist/lib/ci.d.ts +12 -0
  47. package/dist/lib/ci.d.ts.map +1 -0
  48. package/dist/lib/ci.js +42 -0
  49. package/dist/lib/ci.js.map +1 -0
  50. package/dist/lib/config.d.ts +116 -0
  51. package/dist/lib/config.d.ts.map +1 -0
  52. package/dist/lib/config.js +264 -0
  53. package/dist/lib/config.js.map +1 -0
  54. package/dist/lib/constants.d.ts +6 -0
  55. package/dist/lib/constants.d.ts.map +1 -0
  56. package/dist/lib/constants.js +6 -0
  57. package/dist/lib/constants.js.map +1 -0
  58. package/dist/lib/env.d.ts +19 -0
  59. package/dist/lib/env.d.ts.map +1 -0
  60. package/dist/lib/env.js +40 -0
  61. package/dist/lib/env.js.map +1 -0
  62. package/dist/lib/git.d.ts +8 -0
  63. package/dist/lib/git.d.ts.map +1 -0
  64. package/dist/lib/git.js +68 -0
  65. package/dist/lib/git.js.map +1 -0
  66. package/dist/lib/openapi_patch.d.ts +23 -0
  67. package/dist/lib/openapi_patch.d.ts.map +1 -0
  68. package/dist/lib/openapi_patch.js +232 -0
  69. package/dist/lib/openapi_patch.js.map +1 -0
  70. package/dist/lib/openapi_split.d.ts +16 -0
  71. package/dist/lib/openapi_split.d.ts.map +1 -0
  72. package/dist/lib/openapi_split.js +188 -0
  73. package/dist/lib/openapi_split.js.map +1 -0
  74. package/dist/lib/upload.d.ts +44 -0
  75. package/dist/lib/upload.d.ts.map +1 -0
  76. package/dist/lib/upload.js +297 -0
  77. package/dist/lib/upload.js.map +1 -0
  78. package/dist/main.d.ts +8 -0
  79. package/dist/main.d.ts.map +1 -0
  80. package/dist/main.js +319 -0
  81. package/dist/main.js.map +1 -0
  82. package/dist/metadata.d.ts +17 -0
  83. package/dist/metadata.d.ts.map +1 -0
  84. package/dist/metadata.js +61 -0
  85. package/dist/metadata.js.map +1 -0
  86. package/dist/update_check.d.ts +14 -0
  87. package/dist/update_check.d.ts.map +1 -0
  88. package/dist/update_check.js +130 -0
  89. package/dist/update_check.js.map +1 -0
  90. package/dist/version.d.ts +5 -0
  91. package/dist/version.d.ts.map +1 -0
  92. package/dist/version.js +11 -0
  93. package/dist/version.js.map +1 -0
  94. package/package.json +34 -0
  95. package/templates/AI-INSTRUCTIONS.md +163 -0
  96. package/templates/README.md +226 -0
  97. package/templates/claude-skill-glubean-test.md +382 -0
  98. package/templates/data/create-user.json +14 -0
  99. package/templates/data/endpoints.csv +5 -0
  100. package/templates/data/scenarios.yaml +19 -0
  101. package/templates/data/search-examples.json +14 -0
  102. package/templates/data/users.json +17 -0
  103. package/templates/data-driven.test.ts.tpl +118 -0
  104. package/templates/demo.test.result.json +398 -0
  105. package/templates/demo.test.ts.tpl +226 -0
  106. package/templates/explore-api.test.result.json +79 -0
  107. package/templates/minimal/README.md +42 -0
  108. package/templates/minimal-api.test.ts.tpl +42 -0
  109. package/templates/minimal-auth.test.ts.tpl +45 -0
  110. package/templates/minimal-search.test.ts.tpl +34 -0
  111. package/templates/openapi.sample.json +97 -0
  112. package/templates/pick.test.result.json +165 -0
  113. package/templates/pick.test.ts.tpl +126 -0
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Upload run results and artifacts to Glubean Cloud.
3
+ *
4
+ * Upload flow:
5
+ * 1. POST results JSON to /open/v1/cli-runs → { runId, url }
6
+ * 2. If artifact files exist, build a single zip (manifest.json + files/):
7
+ * a. zip < 512KB → POST multipart to /artifacts/upload (inline, 1 request)
8
+ * b. zip ≥ 512KB → POST /artifacts/upload { size } → PUT zip to signed URL
9
+ * → POST /artifacts/upload/complete (3 requests)
10
+ */
11
+ import { readdir, stat, readFile, mkdir, copyFile, writeFile, rm, mkdtemp } from "node:fs/promises";
12
+ import { basename, extname, join, relative } from "node:path";
13
+ import { tmpdir } from "node:os";
14
+ import { execFile as execFileCb } from "node:child_process";
15
+ import { promisify } from "node:util";
16
+ import { CLI_VERSION } from "../version.js";
17
+ import { detectCiContext } from "./ci.js";
18
+ const execFileAsync = promisify(execFileCb);
19
+ const colors = {
20
+ reset: "\x1b[0m",
21
+ green: "\x1b[32m",
22
+ red: "\x1b[31m",
23
+ dim: "\x1b[2m",
24
+ yellow: "\x1b[33m",
25
+ };
26
+ const RESULTS_TIMEOUT_MS = 5_000;
27
+ const ARTIFACT_TIMEOUT_MS = 30_000;
28
+ const INLINE_THRESHOLD = 512 * 1024; // 512KB
29
+ const MAX_RETRIES = 1;
30
+ const RETRY_DELAY_MS = 1_000;
31
+ async function fetchWithRetry(url, init, retries = MAX_RETRIES) {
32
+ const { timeoutMs, ...fetchInit } = init;
33
+ for (let attempt = 0; attempt <= retries; attempt++) {
34
+ const controller = new AbortController();
35
+ const timeout = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
36
+ try {
37
+ const resp = await fetch(url, { ...fetchInit, signal: controller.signal });
38
+ if (timeout)
39
+ clearTimeout(timeout);
40
+ if (resp.status >= 500 && attempt < retries) {
41
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
42
+ continue;
43
+ }
44
+ return resp;
45
+ }
46
+ catch (err) {
47
+ if (timeout)
48
+ clearTimeout(timeout);
49
+ if (attempt < retries) {
50
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
51
+ continue;
52
+ }
53
+ throw err;
54
+ }
55
+ }
56
+ throw new Error("fetchWithRetry exhausted");
57
+ }
58
+ function extToMime(ext) {
59
+ const map = {
60
+ ".png": "image/png",
61
+ ".jpg": "image/jpeg",
62
+ ".jpeg": "image/jpeg",
63
+ ".gif": "image/gif",
64
+ ".webp": "image/webp",
65
+ ".html": "text/html",
66
+ ".json": "application/json",
67
+ ".jsonl": "application/x-ndjson",
68
+ ".har": "application/json",
69
+ ".csv": "text/csv",
70
+ ".txt": "text/plain",
71
+ ".log": "text/plain",
72
+ ".xml": "application/xml",
73
+ };
74
+ return map[ext.toLowerCase()] ?? "application/octet-stream";
75
+ }
76
+ function extToArtifactType(ext) {
77
+ const map = {
78
+ ".png": "screenshot",
79
+ ".jpg": "screenshot",
80
+ ".jpeg": "screenshot",
81
+ ".gif": "screenshot",
82
+ ".webp": "screenshot",
83
+ ".html": "html",
84
+ ".har": "har",
85
+ ".json": "data",
86
+ ".jsonl": "data",
87
+ ".csv": "data",
88
+ ".txt": "log",
89
+ ".log": "log",
90
+ ".xml": "report",
91
+ };
92
+ return map[ext.toLowerCase()] ?? "other";
93
+ }
94
+ /** Recursively walk a directory and collect file paths */
95
+ async function walkDir(dir) {
96
+ const files = [];
97
+ try {
98
+ const entries = await readdir(dir, { withFileTypes: true });
99
+ for (const entry of entries) {
100
+ const full = join(dir, entry.name);
101
+ if (entry.isFile()) {
102
+ files.push(full);
103
+ }
104
+ else if (entry.isDirectory()) {
105
+ files.push(...await walkDir(full));
106
+ }
107
+ }
108
+ }
109
+ catch {
110
+ // Directory doesn't exist
111
+ }
112
+ return files;
113
+ }
114
+ /**
115
+ * Upload run results and optionally artifacts to Glubean Cloud.
116
+ * All operations are best-effort — failures print a warning but never throw.
117
+ */
118
+ export async function uploadToCloud(resultPayload, options) {
119
+ const { apiUrl, token, projectId, rootDir } = options;
120
+ const ci = detectCiContext();
121
+ // ── Step 1: Upload results JSON ──
122
+ const body = {
123
+ projectId,
124
+ source: ci.source,
125
+ clientVersion: CLI_VERSION,
126
+ environment: options.envFile ? basename(options.envFile, extname(options.envFile)) : undefined,
127
+ gitRef: ci.gitRef,
128
+ commitSha: ci.commitSha,
129
+ runUrl: ci.runUrl,
130
+ runAt: resultPayload.runAt,
131
+ target: resultPayload.target,
132
+ files: resultPayload.files,
133
+ nodeVersion: process.versions.node,
134
+ os: process.platform,
135
+ summary: resultPayload.summary,
136
+ tests: resultPayload.tests,
137
+ };
138
+ let runId;
139
+ let runUrl;
140
+ try {
141
+ const resp = await fetchWithRetry(`${apiUrl}/open/v1/cli-runs`, {
142
+ method: "POST",
143
+ headers: {
144
+ "Content-Type": "application/json",
145
+ Authorization: `Bearer ${token}`,
146
+ },
147
+ body: JSON.stringify(body),
148
+ timeoutMs: RESULTS_TIMEOUT_MS,
149
+ });
150
+ if (!resp.ok) {
151
+ const errText = await resp.text();
152
+ console.log(`${colors.yellow}Upload failed (${resp.status}): ${errText}${colors.reset}`);
153
+ return;
154
+ }
155
+ const result = await resp.json();
156
+ runId = result.runId;
157
+ runUrl = result.url;
158
+ console.log(`${colors.green}Results uploaded${colors.reset} ${colors.dim}→ ${runUrl}${colors.reset}`);
159
+ }
160
+ catch (err) {
161
+ if (err instanceof DOMException && err.name === "AbortError") {
162
+ console.log(`${colors.yellow}Upload timed out${colors.reset}`);
163
+ }
164
+ else {
165
+ console.log(`${colors.yellow}Upload failed: ${err instanceof Error ? err.message : err}${colors.reset}`);
166
+ }
167
+ return;
168
+ }
169
+ // ── Step 2: Upload artifacts (if any) ──
170
+ const artifactDirs = [
171
+ join(rootDir, ".glubean", "artifacts"),
172
+ join(rootDir, ".glubean", "screenshots"),
173
+ ];
174
+ const files = [];
175
+ for (const dir of artifactDirs) {
176
+ const dirFiles = await walkDir(dir);
177
+ for (const filePath of dirFiles) {
178
+ files.push({
179
+ path: filePath,
180
+ relativeName: relative(join(rootDir, ".glubean"), filePath),
181
+ });
182
+ }
183
+ }
184
+ if (files.length === 0)
185
+ return;
186
+ let tmpDir;
187
+ try {
188
+ // Build manifest
189
+ const manifest = [];
190
+ for (const file of files) {
191
+ const s = await stat(file.path);
192
+ const ext = extname(file.relativeName);
193
+ manifest.push({
194
+ name: file.relativeName,
195
+ artifactType: extToArtifactType(ext),
196
+ mimeType: extToMime(ext),
197
+ sizeBytes: s.size,
198
+ });
199
+ }
200
+ // Stage files + manifest into temp dir, then zip
201
+ tmpDir = await mkdtemp(join(tmpdir(), "glubean-artifacts-"));
202
+ const stagingDir = join(tmpDir, "staging");
203
+ const filesDir = join(stagingDir, "files");
204
+ await mkdir(filesDir, { recursive: true });
205
+ for (const file of files) {
206
+ const destPath = join(filesDir, file.relativeName);
207
+ const destDir = destPath.substring(0, destPath.lastIndexOf("/"));
208
+ await mkdir(destDir, { recursive: true });
209
+ await copyFile(file.path, destPath);
210
+ }
211
+ await writeFile(join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
212
+ const zipPath = join(tmpDir, "artifacts.zip");
213
+ try {
214
+ await execFileAsync("zip", ["-r", zipPath, "."], { cwd: stagingDir });
215
+ }
216
+ catch {
217
+ console.log(`${colors.yellow}Failed to create artifact archive${colors.reset}`);
218
+ return;
219
+ }
220
+ const zipData = await readFile(zipPath);
221
+ const zipSize = zipData.byteLength;
222
+ if (zipSize < INLINE_THRESHOLD) {
223
+ await uploadArtifactsInline(apiUrl, token, runId, zipData);
224
+ }
225
+ else {
226
+ await uploadArtifactsPresigned(apiUrl, token, runId, zipData, zipSize);
227
+ }
228
+ const totalSize = manifest.reduce((sum, e) => sum + e.sizeBytes, 0);
229
+ const sizeStr = totalSize > 1024 * 1024
230
+ ? `${(totalSize / 1024 / 1024).toFixed(1)} MB`
231
+ : `${(totalSize / 1024).toFixed(1)} KB`;
232
+ console.log(`${colors.green}Artifacts uploaded${colors.reset} ${colors.dim}(${files.length} files, ${sizeStr})${colors.reset}`);
233
+ }
234
+ catch (err) {
235
+ if (err instanceof DOMException && err.name === "AbortError") {
236
+ console.log(`${colors.yellow}Artifact upload timed out${colors.reset}`);
237
+ }
238
+ else {
239
+ console.log(`${colors.yellow}Artifact upload failed: ${err instanceof Error ? err.message : err}${colors.reset}`);
240
+ }
241
+ }
242
+ finally {
243
+ if (tmpDir) {
244
+ await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
245
+ }
246
+ }
247
+ }
248
+ async function uploadArtifactsInline(apiUrl, token, runId, zipData) {
249
+ const form = new FormData();
250
+ form.append("archive", new Blob([zipData.buffer], { type: "application/zip" }), "artifacts.zip");
251
+ const resp = await fetchWithRetry(`${apiUrl}/open/v1/cli-runs/${runId}/artifacts/upload`, {
252
+ method: "POST",
253
+ headers: { Authorization: `Bearer ${token}` },
254
+ body: form,
255
+ timeoutMs: ARTIFACT_TIMEOUT_MS,
256
+ });
257
+ if (!resp.ok) {
258
+ const errText = await resp.text();
259
+ console.log(`${colors.yellow}Artifact upload failed (${resp.status}): ${errText}${colors.reset}`);
260
+ }
261
+ }
262
+ async function uploadArtifactsPresigned(apiUrl, token, runId, zipData, zipSize) {
263
+ const form = new FormData();
264
+ form.append("size", String(zipSize));
265
+ const urlResp = await fetchWithRetry(`${apiUrl}/open/v1/cli-runs/${runId}/artifacts/upload`, {
266
+ method: "POST",
267
+ headers: { Authorization: `Bearer ${token}` },
268
+ body: form,
269
+ timeoutMs: RESULTS_TIMEOUT_MS,
270
+ });
271
+ if (!urlResp.ok) {
272
+ const errText = await urlResp.text();
273
+ console.log(`${colors.yellow}Artifact upload URL request failed (${urlResp.status}): ${errText}${colors.reset}`);
274
+ return;
275
+ }
276
+ const { signedUrl, archiveKey } = await urlResp.json();
277
+ const putResp = await fetchWithRetry(signedUrl, {
278
+ method: "PUT",
279
+ headers: { "Content-Type": "application/zip" },
280
+ body: zipData,
281
+ timeoutMs: ARTIFACT_TIMEOUT_MS,
282
+ });
283
+ if (!putResp.ok) {
284
+ console.log(`${colors.yellow}Artifact upload failed (${putResp.status})${colors.reset}`);
285
+ return;
286
+ }
287
+ await fetchWithRetry(`${apiUrl}/open/v1/cli-runs/${runId}/artifacts/upload/complete`, {
288
+ method: "POST",
289
+ headers: {
290
+ "Content-Type": "application/json",
291
+ Authorization: `Bearer ${token}`,
292
+ },
293
+ body: JSON.stringify({ archiveKey }),
294
+ timeoutMs: RESULTS_TIMEOUT_MS,
295
+ });
296
+ }
297
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/lib/upload.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Glubean CLI - Main entry point
3
+ *
4
+ * Uses Commander.js for structured command handling with automatic help
5
+ * generation, argument validation, and shell completions.
6
+ */
7
+ export { CLI_VERSION } from "./version.js";
8
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgVH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
package/dist/main.js ADDED
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Glubean CLI - Main entry point
3
+ *
4
+ * Uses Commander.js for structured command handling with automatic help
5
+ * generation, argument validation, and shell completions.
6
+ */
7
+ // Support running from outside workspace (e.g. shell alias with GLUBEAN_CWD)
8
+ const _cwd = process.env["GLUBEAN_CWD"];
9
+ if (_cwd)
10
+ process.chdir(_cwd);
11
+ import { Command } from "commander";
12
+ import { CLI_VERSION } from "./version.js";
13
+ import { loadConfig } from "./lib/config.js";
14
+ import { initCommand } from "./commands/init.js";
15
+ import { runCommand } from "./commands/run.js";
16
+ import { scanCommand } from "./commands/scan.js";
17
+ import { syncCommand } from "./commands/sync.js";
18
+ import { triggerCommand } from "./commands/trigger.js";
19
+ import { validateMetadataCommand } from "./commands/validate_metadata.js";
20
+ import { loginCommand } from "./commands/login.js";
21
+ import { patchCommand } from "./commands/patch.js";
22
+ import { specSplitCommand } from "./commands/spec_split.js";
23
+ import { workerCommand } from "./commands/worker.js";
24
+ import { abortUpdateCheck, checkForUpdates } from "./update_check.js";
25
+ const program = new Command();
26
+ program
27
+ .name("glubean")
28
+ .alias("gb")
29
+ .version(CLI_VERSION)
30
+ .description("Glubean CLI - Run and sync API tests from the command line")
31
+ .option("--no-update-check", "Skip update check");
32
+ // ─────────────────────────────────────────────────────────────────────────────
33
+ // init command
34
+ // ─────────────────────────────────────────────────────────────────────────────
35
+ program
36
+ .command("init")
37
+ .description("Initialize a new test project (interactive wizard)")
38
+ .option("--minimal", "Scaffold minimal explore-only project (GET, POST, pick)")
39
+ .option("--hooks", "Install git hooks (pre-commit, pre-push)")
40
+ .option("--github-actions", "Scaffold GitHub Actions workflow")
41
+ .option("--base-url <url>", "API base URL for .env")
42
+ .option("--no-interactive", "Disable prompts (use with flags)")
43
+ .option("--overwrite", "Overwrite existing files (dangerous)")
44
+ .option("--overwrite-hooks", "Overwrite existing .git/hooks files")
45
+ .option("--overwrite-actions", "Overwrite GitHub Actions workflow")
46
+ .action(async (options) => {
47
+ await initCommand({
48
+ minimal: options.minimal,
49
+ hooks: options.hooks,
50
+ githubActions: options.githubActions,
51
+ baseUrl: options.baseUrl,
52
+ interactive: options.interactive,
53
+ overwrite: options.overwrite,
54
+ overwriteHooks: options.overwriteHooks,
55
+ overwriteActions: options.overwriteActions,
56
+ });
57
+ });
58
+ // ─────────────────────────────────────────────────────────────────────────────
59
+ // run command
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+ program
62
+ .command("run [target]")
63
+ .description("Run tests from a file, directory, or glob pattern (defaults to testDir)")
64
+ .option("--explore", "Run explore tests (from exploreDir instead of testDir)")
65
+ .option("-f, --filter <pattern>", "Run only tests matching pattern (name or id substring)")
66
+ .option("-t, --tag <tag>", "Run only tests with matching tag (comma-separated or repeatable)", collect, [])
67
+ .option("--tag-mode <mode>", 'Tag match logic: "or" (any tag) or "and" (all tags)', "or")
68
+ .option("--env-file <path>", "Path to .env file", ".env")
69
+ .option("-l, --log-file", "Write logs to file (<testfile>.log)")
70
+ .option("--pretty", "Pretty-print JSON in log file (2-space indent)")
71
+ .option("--verbose", "Show all output (traces, assertions) in console")
72
+ .option("--fail-fast", "Stop on first test failure")
73
+ .option("--fail-after <count>", "Stop after N test failures")
74
+ .option("--result-json [path]", "Write structured results to .result.json (or custom path)")
75
+ .option("--emit-full-trace", "Include full request/response headers and bodies in HTTP traces")
76
+ .option("--config <paths>", "Config file(s), comma-separated or repeatable", collect, [])
77
+ .option("--pick <keys>", "Select specific test.pick example(s) by key (comma-separated)")
78
+ .option("--inspect-brk [port]", "Enable V8 Inspector for debugging (pauses until debugger attaches)")
79
+ .option("--reporter <format>", 'Output format: "junit" or "junit:/path/to/output.xml"')
80
+ .option("--trace-limit <count>", "Max trace files to keep per test (default: 20)")
81
+ .option("--ci", "CI mode: enables --fail-fast and --reporter junit")
82
+ .option("--upload", "Upload run results and artifacts to Glubean Cloud")
83
+ .option("--project <id>", "Glubean Cloud project ID (or GLUBEAN_PROJECT_ID env)")
84
+ .option("--token <token>", "Auth token for cloud upload (or GLUBEAN_TOKEN env)")
85
+ .option("--api-url <url>", "Glubean API server URL")
86
+ .action(async (target, options) => {
87
+ // Flatten --config values
88
+ const configFiles = options.config && options.config.length > 0
89
+ ? options.config.flatMap((v) => v.split(",").map((s) => s.trim()).filter(Boolean))
90
+ : undefined;
91
+ // Resolve default target from config when not explicitly provided
92
+ let resolvedTarget = target;
93
+ if (!resolvedTarget) {
94
+ const config = await loadConfig(process.cwd(), configFiles);
95
+ resolvedTarget = options.explore ? config.run.exploreDir : config.run.testDir;
96
+ }
97
+ // --ci implies --fail-fast and --reporter junit
98
+ const isCi = options.ci === true;
99
+ const failFast = options.failFast || isCi;
100
+ let reporter = options.reporter;
101
+ let reporterPath;
102
+ if (!reporter && isCi) {
103
+ reporter = "junit";
104
+ }
105
+ if (reporter && reporter.startsWith("junit:")) {
106
+ reporterPath = reporter.slice("junit:".length);
107
+ reporter = "junit";
108
+ }
109
+ const resultJson = options.resultJson;
110
+ await runCommand(resolvedTarget, {
111
+ filter: options.filter,
112
+ pick: options.pick,
113
+ tags: options.tag?.flatMap((t) => t.split(",").map((s) => s.trim()).filter(Boolean)),
114
+ tagMode: options.tagMode,
115
+ envFile: options.envFile,
116
+ logFile: options.logFile,
117
+ pretty: options.pretty,
118
+ verbose: options.verbose,
119
+ failFast,
120
+ failAfter: options.failAfter ? parseInt(options.failAfter, 10) : undefined,
121
+ resultJson,
122
+ emitFullTrace: options.emitFullTrace,
123
+ configFiles,
124
+ inspectBrk: options.inspectBrk,
125
+ reporter,
126
+ reporterPath,
127
+ traceLimit: options.traceLimit ? parseInt(options.traceLimit, 10) : undefined,
128
+ upload: options.upload,
129
+ project: options.project,
130
+ token: options.token,
131
+ apiUrl: options.apiUrl,
132
+ });
133
+ });
134
+ // ─────────────────────────────────────────────────────────────────────────────
135
+ // scan command
136
+ // ─────────────────────────────────────────────────────────────────────────────
137
+ program
138
+ .command("scan")
139
+ .description("Generate metadata.json from a directory")
140
+ .option("-d, --dir <path>", "Directory to scan", ".")
141
+ .option("--out <path>", "Output path for metadata.json")
142
+ .action(async (options) => {
143
+ await scanCommand({
144
+ dir: options.dir,
145
+ output: options.out,
146
+ });
147
+ });
148
+ // ─────────────────────────────────────────────────────────────────────────────
149
+ // validate-metadata command
150
+ // ─────────────────────────────────────────────────────────────────────────────
151
+ program
152
+ .command("validate-metadata")
153
+ .description("Validate metadata.json against local files")
154
+ .option("-d, --dir <path>", "Project root", ".")
155
+ .option("--metadata <path>", "Path to metadata.json")
156
+ .action(async (options) => {
157
+ await validateMetadataCommand({
158
+ dir: options.dir,
159
+ metadata: options.metadata,
160
+ });
161
+ });
162
+ // ─────────────────────────────────────────────────────────────────────────────
163
+ // sync command
164
+ // ─────────────────────────────────────────────────────────────────────────────
165
+ program
166
+ .command("sync")
167
+ .description("Sync tests to Glubean Cloud")
168
+ .option("-p, --project <id>", "Target project ID (required)")
169
+ .option("-t, --tag <version>", "Version tag (default: timestamp)")
170
+ .option("-d, --dir <path>", "Directory to scan", ".")
171
+ .option("--api-url <url>", "API server URL")
172
+ .option("--token <token>", "Auth token (or GLUBEAN_TOKEN env)")
173
+ .option("--dry-run", "Generate bundle without uploading")
174
+ .action(async (options) => {
175
+ await syncCommand({
176
+ project: options.project,
177
+ version: options.tag,
178
+ dir: options.dir,
179
+ apiUrl: options.apiUrl,
180
+ token: options.token,
181
+ dryRun: options.dryRun,
182
+ });
183
+ });
184
+ // ─────────────────────────────────────────────────────────────────────────────
185
+ // trigger command
186
+ // ─────────────────────────────────────────────────────────────────────────────
187
+ program
188
+ .command("trigger")
189
+ .description("Trigger a remote run on Glubean Cloud")
190
+ .option("-p, --project <id>", "Target project ID (required)")
191
+ .option("-b, --bundle <id>", "Bundle ID (uses latest if not specified)")
192
+ .option("-j, --job <id>", "Job ID")
193
+ .option("-F, --follow", "Tail logs until run completes")
194
+ .option("--api-url <url>", "API server URL")
195
+ .option("--token <token>", "Auth token (or GLUBEAN_TOKEN env)")
196
+ .action(async (options) => {
197
+ await triggerCommand({
198
+ project: options.project,
199
+ bundle: options.bundle,
200
+ job: options.job,
201
+ apiUrl: options.apiUrl,
202
+ token: options.token,
203
+ follow: options.follow,
204
+ });
205
+ });
206
+ // ─────────────────────────────────────────────────────────────────────────────
207
+ // login command
208
+ // ─────────────────────────────────────────────────────────────────────────────
209
+ program
210
+ .command("login")
211
+ .description("Authenticate with Glubean Cloud")
212
+ .option("--token <token>", "Auth token (skip interactive prompt)")
213
+ .option("--project <id>", "Default project ID")
214
+ .option("--api-url <url>", "API server URL")
215
+ .action(async (options) => {
216
+ await loginCommand({
217
+ token: options.token,
218
+ project: options.project,
219
+ apiUrl: options.apiUrl,
220
+ });
221
+ });
222
+ // ─────────────────────────────────────────────────────────────────────────────
223
+ // patch command
224
+ // ─────────────────────────────────────────────────────────────────────────────
225
+ program
226
+ .command("patch <spec>")
227
+ .description("Merge an OpenAPI spec with its .patch.yaml and write the complete spec")
228
+ .option("--patch <file>", "Path to patch file (auto-discovered if omitted)")
229
+ .option("-o, --output <file>", "Output file path (default: <name>.patched.json)")
230
+ .option("--stdout", "Write to stdout instead of file")
231
+ .option("--format <fmt>", 'Output format: "json" or "yaml" (default: same as input)')
232
+ .action(async (spec, options) => {
233
+ await patchCommand(spec, {
234
+ patch: options.patch,
235
+ output: options.output,
236
+ stdout: options.stdout,
237
+ format: options.format,
238
+ });
239
+ });
240
+ // ─────────────────────────────────────────────────────────────────────────────
241
+ // spec command (with subcommands)
242
+ // ─────────────────────────────────────────────────────────────────────────────
243
+ const specCmd = program
244
+ .command("spec")
245
+ .description("OpenAPI spec tools");
246
+ specCmd
247
+ .command("split <spec>")
248
+ .description("Dereference $refs and split spec into per-endpoint files for AI")
249
+ .option("-o, --output <dir>", "Output directory (default: <name>-endpoints/ next to spec)")
250
+ .action(async (spec, options) => {
251
+ await specSplitCommand(spec, { output: options.output });
252
+ });
253
+ // ─────────────────────────────────────────────────────────────────────────────
254
+ // worker command (with subcommands)
255
+ // ─────────────────────────────────────────────────────────────────────────────
256
+ const workerCmd = program
257
+ .command("worker")
258
+ .description("Self-hosted worker management");
259
+ workerCmd
260
+ .command("start")
261
+ .description("Start worker instance(s)")
262
+ .option("-n, --instances <count>", "Number of instances (or 'auto')", "1")
263
+ .option("--config <path>", "Worker config file (JSON)")
264
+ .option("--api-url <url>", "Control plane URL")
265
+ .option("--token <token>", "Worker token (or GLUBEAN_WORKER_TOKEN env)")
266
+ .option("--log-level <level>", "Log level")
267
+ .option("--worker-id <id>", "Base worker ID (auto-generated if not set)")
268
+ .action(async (options) => {
269
+ let instances;
270
+ if (options.instances === "auto") {
271
+ instances = "auto";
272
+ }
273
+ else {
274
+ const parsed = parseInt(options.instances, 10);
275
+ if (!isNaN(parsed) && parsed >= 1) {
276
+ instances = parsed;
277
+ }
278
+ }
279
+ await workerCommand("start", {
280
+ instances,
281
+ config: options.config,
282
+ apiUrl: options.apiUrl,
283
+ token: options.token,
284
+ logLevel: options.logLevel,
285
+ workerId: options.workerId,
286
+ });
287
+ });
288
+ // ─────────────────────────────────────────────────────────────────────────────
289
+ // Helpers
290
+ // ─────────────────────────────────────────────────────────────────────────────
291
+ /** Collect repeated options into an array (Commander.js pattern) */
292
+ function collect(value, previous) {
293
+ return previous.concat([value]);
294
+ }
295
+ // ─────────────────────────────────────────────────────────────────────────────
296
+ // Main entry point
297
+ // ─────────────────────────────────────────────────────────────────────────────
298
+ // Check for updates (non-blocking)
299
+ if (!process.argv.includes("--no-update-check")) {
300
+ checkForUpdates(CLI_VERSION).catch(() => { });
301
+ }
302
+ try {
303
+ await program.parseAsync(process.argv);
304
+ }
305
+ catch (error) {
306
+ if (error instanceof Error) {
307
+ console.error(`Error: ${error.message}`);
308
+ }
309
+ else {
310
+ console.error("An unexpected error occurred");
311
+ }
312
+ process.exit(1);
313
+ }
314
+ finally {
315
+ abortUpdateCheck();
316
+ }
317
+ // Export CLI version for programmatic access
318
+ export { CLI_VERSION } from "./version.js";
319
+ //# sourceMappingURL=main.js.map