@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.
- package/bin/gb.js +2 -0
- package/dist/commands/init.d.ts +19 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +842 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +75 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/patch.d.ts +8 -0
- package/dist/commands/patch.d.ts.map +1 -0
- package/dist/commands/patch.js +73 -0
- package/dist/commands/patch.js.map +1 -0
- package/dist/commands/run.d.ts +26 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +1093 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/scan.d.ts +6 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +62 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/spec_split.d.ts +5 -0
- package/dist/commands/spec_split.d.ts.map +1 -0
- package/dist/commands/spec_split.js +56 -0
- package/dist/commands/spec_split.js.map +1 -0
- package/dist/commands/sync.d.ts +13 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +252 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/trigger.d.ts +13 -0
- package/dist/commands/trigger.d.ts.map +1 -0
- package/dist/commands/trigger.js +213 -0
- package/dist/commands/trigger.js.map +1 -0
- package/dist/commands/validate_metadata.d.ts +6 -0
- package/dist/commands/validate_metadata.d.ts.map +1 -0
- package/dist/commands/validate_metadata.js +103 -0
- package/dist/commands/validate_metadata.js.map +1 -0
- package/dist/commands/worker.d.ts +14 -0
- package/dist/commands/worker.d.ts.map +1 -0
- package/dist/commands/worker.js +10 -0
- package/dist/commands/worker.js.map +1 -0
- package/dist/lib/auth.d.ts +39 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +82 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/ci.d.ts +12 -0
- package/dist/lib/ci.d.ts.map +1 -0
- package/dist/lib/ci.js +42 -0
- package/dist/lib/ci.js.map +1 -0
- package/dist/lib/config.d.ts +116 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +264 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +6 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +6 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/env.d.ts +19 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +40 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/git.d.ts +8 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +68 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/openapi_patch.d.ts +23 -0
- package/dist/lib/openapi_patch.d.ts.map +1 -0
- package/dist/lib/openapi_patch.js +232 -0
- package/dist/lib/openapi_patch.js.map +1 -0
- package/dist/lib/openapi_split.d.ts +16 -0
- package/dist/lib/openapi_split.d.ts.map +1 -0
- package/dist/lib/openapi_split.js +188 -0
- package/dist/lib/openapi_split.js.map +1 -0
- package/dist/lib/upload.d.ts +44 -0
- package/dist/lib/upload.d.ts.map +1 -0
- package/dist/lib/upload.js +297 -0
- package/dist/lib/upload.js.map +1 -0
- package/dist/main.d.ts +8 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +319 -0
- package/dist/main.js.map +1 -0
- package/dist/metadata.d.ts +17 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +61 -0
- package/dist/metadata.js.map +1 -0
- package/dist/update_check.d.ts +14 -0
- package/dist/update_check.d.ts.map +1 -0
- package/dist/update_check.js +130 -0
- package/dist/update_check.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +34 -0
- package/templates/AI-INSTRUCTIONS.md +163 -0
- package/templates/README.md +226 -0
- package/templates/claude-skill-glubean-test.md +382 -0
- package/templates/data/create-user.json +14 -0
- package/templates/data/endpoints.csv +5 -0
- package/templates/data/scenarios.yaml +19 -0
- package/templates/data/search-examples.json +14 -0
- package/templates/data/users.json +17 -0
- package/templates/data-driven.test.ts.tpl +118 -0
- package/templates/demo.test.result.json +398 -0
- package/templates/demo.test.ts.tpl +226 -0
- package/templates/explore-api.test.result.json +79 -0
- package/templates/minimal/README.md +42 -0
- package/templates/minimal-api.test.ts.tpl +42 -0
- package/templates/minimal-auth.test.ts.tpl +45 -0
- package/templates/minimal-search.test.ts.tpl +34 -0
- package/templates/openapi.sample.json +97 -0
- package/templates/pick.test.result.json +165 -0
- 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 @@
|
|
|
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
|