@vibgrate/cli 1.0.1 → 1.0.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/README.md +46 -7
- package/dist/{baseline-35XRSRAD.js → baseline-5SAUNH2V.js} +2 -2
- package/dist/{chunk-NTRKEIKP.js → chunk-GG5AUF7X.js} +1 -1
- package/dist/{chunk-VMNBKARQ.js → chunk-XZ4NRZMT.js} +435 -112
- package/dist/cli.js +31 -164
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,22 +4,22 @@ import {
|
|
|
4
4
|
} from "./chunk-VXZT34Y5.js";
|
|
5
5
|
import {
|
|
6
6
|
baselineCommand
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GG5AUF7X.js";
|
|
8
8
|
import {
|
|
9
9
|
VERSION,
|
|
10
|
+
dsnCommand,
|
|
10
11
|
ensureDir,
|
|
11
12
|
formatText,
|
|
12
13
|
pathExists,
|
|
14
|
+
pushCommand,
|
|
13
15
|
readJsonFile,
|
|
14
|
-
readTextFile,
|
|
15
16
|
scanCommand,
|
|
16
|
-
writeDefaultConfig
|
|
17
|
-
|
|
18
|
-
} from "./chunk-VMNBKARQ.js";
|
|
17
|
+
writeDefaultConfig
|
|
18
|
+
} from "./chunk-XZ4NRZMT.js";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
|
-
import { Command as
|
|
22
|
-
import
|
|
21
|
+
import { Command as Command4 } from "commander";
|
|
22
|
+
import chalk4 from "chalk";
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
25
|
import * as path from "path";
|
|
@@ -38,7 +38,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
38
38
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
39
39
|
}
|
|
40
40
|
if (opts.baseline) {
|
|
41
|
-
const { runBaseline } = await import("./baseline-
|
|
41
|
+
const { runBaseline } = await import("./baseline-5SAUNH2V.js");
|
|
42
42
|
await runBaseline(rootDir);
|
|
43
43
|
}
|
|
44
44
|
console.log("");
|
|
@@ -74,153 +74,20 @@ var reportCommand = new Command2("report").description("Generate a drift report
|
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
// src/commands/dsn.ts
|
|
78
|
-
import * as crypto from "crypto";
|
|
79
|
-
import * as path3 from "path";
|
|
80
|
-
import { Command as Command3 } from "commander";
|
|
81
|
-
import chalk3 from "chalk";
|
|
82
|
-
var REGION_HOSTS = {
|
|
83
|
-
us: "us.ingest.vibgrate.com",
|
|
84
|
-
eu: "eu.ingest.vibgrate.com"
|
|
85
|
-
};
|
|
86
|
-
function resolveIngestHost(region, ingest) {
|
|
87
|
-
if (ingest) {
|
|
88
|
-
try {
|
|
89
|
-
return new URL(ingest).host;
|
|
90
|
-
} catch {
|
|
91
|
-
throw new Error(`Invalid ingest URL: ${ingest}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const r = (region ?? "us").toLowerCase();
|
|
95
|
-
const host = REGION_HOSTS[r];
|
|
96
|
-
if (!host) {
|
|
97
|
-
throw new Error(`Unknown region "${r}". Supported: ${Object.keys(REGION_HOSTS).join(", ")}`);
|
|
98
|
-
}
|
|
99
|
-
return host;
|
|
100
|
-
}
|
|
101
|
-
var dsnCommand = new Command3("dsn").description("Manage DSN tokens");
|
|
102
|
-
dsnCommand.command("create").description("Create a new DSN token").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", "Data residency region (us, eu)", "us").requiredOption("--workspace <id>", "Workspace ID").option("--write <path>", "Write DSN to file").action(async (opts) => {
|
|
103
|
-
const keyId = crypto.randomBytes(8).toString("hex");
|
|
104
|
-
const secret = crypto.randomBytes(32).toString("hex");
|
|
105
|
-
let ingestHost;
|
|
106
|
-
try {
|
|
107
|
-
ingestHost = resolveIngestHost(opts.region, opts.ingest);
|
|
108
|
-
} catch (e) {
|
|
109
|
-
console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
const dsn = `vibgrate+https://${keyId}:${secret}@${ingestHost}/${opts.workspace}`;
|
|
113
|
-
console.log(chalk3.green("\u2714") + " DSN created");
|
|
114
|
-
console.log("");
|
|
115
|
-
console.log(chalk3.bold("DSN:"));
|
|
116
|
-
console.log(` ${dsn}`);
|
|
117
|
-
console.log("");
|
|
118
|
-
console.log(chalk3.bold("Key ID:"));
|
|
119
|
-
console.log(` ${keyId}`);
|
|
120
|
-
console.log("");
|
|
121
|
-
console.log(chalk3.dim("Set this as VIBGRATE_DSN in your CI environment."));
|
|
122
|
-
console.log(chalk3.dim("The secret must be registered on your Vibgrate ingest API."));
|
|
123
|
-
if (opts.write) {
|
|
124
|
-
const writePath = path3.resolve(opts.write);
|
|
125
|
-
await writeTextFile(writePath, dsn + "\n");
|
|
126
|
-
console.log("");
|
|
127
|
-
console.log(chalk3.green("\u2714") + ` DSN written to ${opts.write}`);
|
|
128
|
-
console.log(chalk3.yellow("\u26A0") + " Add this file to .gitignore!");
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// src/commands/push.ts
|
|
133
|
-
import * as crypto2 from "crypto";
|
|
134
|
-
import * as path4 from "path";
|
|
135
|
-
import { Command as Command4 } from "commander";
|
|
136
|
-
import chalk4 from "chalk";
|
|
137
|
-
function parseDsn(dsn) {
|
|
138
|
-
const match = dsn.match(/^vibgrate\+https:\/\/([^:]+):([^@]+)@([^/]+)\/(.+)$/);
|
|
139
|
-
if (!match) return null;
|
|
140
|
-
return {
|
|
141
|
-
keyId: match[1],
|
|
142
|
-
secret: match[2],
|
|
143
|
-
host: match[3],
|
|
144
|
-
workspaceId: match[4]
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
function computeHmac(body, secret) {
|
|
148
|
-
return crypto2.createHmac("sha256", secret).update(body).digest("base64");
|
|
149
|
-
}
|
|
150
|
-
var pushCommand = new Command4("push").description("Push scan results to Vibgrate API").option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region (us, eu)").option("--file <file>", "Scan artifact file", ".vibgrate/scan_result.json").option("--strict", "Fail on upload errors").action(async (opts) => {
|
|
151
|
-
const dsn = opts.dsn || process.env.VIBGRATE_DSN;
|
|
152
|
-
if (!dsn) {
|
|
153
|
-
console.error(chalk4.red("No DSN provided."));
|
|
154
|
-
console.error(chalk4.dim("Set VIBGRATE_DSN environment variable or use --dsn flag."));
|
|
155
|
-
if (opts.strict) process.exit(1);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const parsed = parseDsn(dsn);
|
|
159
|
-
if (!parsed) {
|
|
160
|
-
console.error(chalk4.red("Invalid DSN format."));
|
|
161
|
-
console.error(chalk4.dim("Expected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>"));
|
|
162
|
-
if (opts.strict) process.exit(1);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
const filePath = path4.resolve(opts.file);
|
|
166
|
-
if (!await pathExists(filePath)) {
|
|
167
|
-
console.error(chalk4.red(`Scan artifact not found: ${filePath}`));
|
|
168
|
-
console.error(chalk4.dim('Run "vibgrate scan" first.'));
|
|
169
|
-
if (opts.strict) process.exit(1);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const body = await readTextFile(filePath);
|
|
173
|
-
const timestamp = String(Date.now());
|
|
174
|
-
const hmac = computeHmac(body, parsed.secret);
|
|
175
|
-
let host = parsed.host;
|
|
176
|
-
if (opts.region) {
|
|
177
|
-
try {
|
|
178
|
-
host = resolveIngestHost(opts.region);
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
181
|
-
if (opts.strict) process.exit(1);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const url = `https://${host}/v1/ingest/scan`;
|
|
186
|
-
console.log(chalk4.dim(`Uploading to ${host}...`));
|
|
187
|
-
try {
|
|
188
|
-
const response = await fetch(url, {
|
|
189
|
-
method: "POST",
|
|
190
|
-
headers: {
|
|
191
|
-
"Content-Type": "application/json",
|
|
192
|
-
"X-Vibgrate-Timestamp": timestamp,
|
|
193
|
-
"Authorization": `VibgrateDSN ${parsed.keyId}:${hmac}`
|
|
194
|
-
},
|
|
195
|
-
body
|
|
196
|
-
});
|
|
197
|
-
if (!response.ok) {
|
|
198
|
-
const text = await response.text();
|
|
199
|
-
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
200
|
-
}
|
|
201
|
-
const result = await response.json();
|
|
202
|
-
console.log(chalk4.green("\u2714") + ` Uploaded successfully (${result.ingestId ?? "ok"})`);
|
|
203
|
-
} catch (e) {
|
|
204
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
205
|
-
console.error(chalk4.red(`Upload failed: ${msg}`));
|
|
206
|
-
if (opts.strict) process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
|
|
210
77
|
// src/commands/update.ts
|
|
211
78
|
import { execSync } from "child_process";
|
|
212
|
-
import * as
|
|
213
|
-
import { Command as
|
|
214
|
-
import
|
|
79
|
+
import * as path4 from "path";
|
|
80
|
+
import { Command as Command3 } from "commander";
|
|
81
|
+
import chalk3 from "chalk";
|
|
215
82
|
|
|
216
83
|
// src/utils/update-check.ts
|
|
217
84
|
import * as fs from "fs/promises";
|
|
218
|
-
import * as
|
|
85
|
+
import * as path3 from "path";
|
|
219
86
|
import * as os from "os";
|
|
220
87
|
import semver from "semver";
|
|
221
88
|
var REGISTRY_URL = "https://registry.npmjs.org/@vibgrate%2fcli/latest";
|
|
222
|
-
var CACHE_DIR =
|
|
223
|
-
var CACHE_FILE =
|
|
89
|
+
var CACHE_DIR = path3.join(os.homedir(), ".vibgrate");
|
|
90
|
+
var CACHE_FILE = path3.join(CACHE_DIR, "update-check.json");
|
|
224
91
|
var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1e3;
|
|
225
92
|
async function checkForUpdate() {
|
|
226
93
|
try {
|
|
@@ -292,9 +159,9 @@ async function writeCache(data) {
|
|
|
292
159
|
|
|
293
160
|
// src/commands/update.ts
|
|
294
161
|
async function detectPackageManager(cwd) {
|
|
295
|
-
if (await pathExists(
|
|
296
|
-
if (await pathExists(
|
|
297
|
-
if (await pathExists(
|
|
162
|
+
if (await pathExists(path4.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
163
|
+
if (await pathExists(path4.join(cwd, "bun.lockb"))) return "bun";
|
|
164
|
+
if (await pathExists(path4.join(cwd, "yarn.lock"))) return "yarn";
|
|
298
165
|
return "npm";
|
|
299
166
|
}
|
|
300
167
|
function getInstallCommand(pm, pkg, version, isDev) {
|
|
@@ -313,7 +180,7 @@ function getInstallCommand(pm, pkg, version, isDev) {
|
|
|
313
180
|
}
|
|
314
181
|
async function isDevDependency(cwd) {
|
|
315
182
|
try {
|
|
316
|
-
const pkgPath =
|
|
183
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
317
184
|
const raw = await (await import("fs/promises")).readFile(pkgPath, "utf-8");
|
|
318
185
|
const pkg = JSON.parse(raw);
|
|
319
186
|
return Boolean(pkg.devDependencies?.["@vibgrate/cli"]);
|
|
@@ -321,40 +188,40 @@ async function isDevDependency(cwd) {
|
|
|
321
188
|
return true;
|
|
322
189
|
}
|
|
323
190
|
}
|
|
324
|
-
var updateCommand = new
|
|
325
|
-
console.log(
|
|
326
|
-
console.log(
|
|
191
|
+
var updateCommand = new Command3("update").description("Update vibgrate to the latest version").option("--check", "Only check for updates, do not install").option("--pm <manager>", "Package manager to use (npm, pnpm, yarn, bun)").action(async (opts) => {
|
|
192
|
+
console.log(chalk3.dim(`Current version: ${VERSION}`));
|
|
193
|
+
console.log(chalk3.dim("Checking npm registry..."));
|
|
327
194
|
const latest = await fetchLatestVersion();
|
|
328
195
|
if (!latest) {
|
|
329
|
-
console.error(
|
|
196
|
+
console.error(chalk3.red("Could not reach the npm registry. Check your network connection."));
|
|
330
197
|
process.exit(1);
|
|
331
198
|
}
|
|
332
199
|
const semver2 = await import("semver");
|
|
333
200
|
if (!semver2.gt(latest, VERSION)) {
|
|
334
|
-
console.log(
|
|
201
|
+
console.log(chalk3.green("\u2714") + ` You are on the latest version (${VERSION}).`);
|
|
335
202
|
return;
|
|
336
203
|
}
|
|
337
|
-
console.log(
|
|
204
|
+
console.log(chalk3.yellow(`Update available: ${VERSION} \u2192 ${latest}`));
|
|
338
205
|
if (opts.check) {
|
|
339
|
-
console.log(
|
|
206
|
+
console.log(chalk3.dim('Run "vibgrate update" to install.'));
|
|
340
207
|
return;
|
|
341
208
|
}
|
|
342
209
|
const cwd = process.cwd();
|
|
343
210
|
const pm = opts.pm || await detectPackageManager(cwd);
|
|
344
211
|
const isDev = await isDevDependency(cwd);
|
|
345
212
|
const cmd = getInstallCommand(pm, "@vibgrate/cli", latest, isDev);
|
|
346
|
-
console.log(
|
|
213
|
+
console.log(chalk3.dim(`Using ${pm}: ${cmd}`));
|
|
347
214
|
try {
|
|
348
215
|
execSync(cmd, { cwd, stdio: "inherit" });
|
|
349
|
-
console.log(
|
|
216
|
+
console.log(chalk3.green("\u2714") + ` Updated to @vibgrate/cli@${latest}`);
|
|
350
217
|
} catch {
|
|
351
|
-
console.error(
|
|
218
|
+
console.error(chalk3.red(`Update failed. Run manually: ${cmd}`));
|
|
352
219
|
process.exit(1);
|
|
353
220
|
}
|
|
354
221
|
});
|
|
355
222
|
|
|
356
223
|
// src/cli.ts
|
|
357
|
-
var program = new
|
|
224
|
+
var program = new Command4();
|
|
358
225
|
program.name("vibgrate").description("Continuous Upgrade Drift Intelligence for Node & .NET").version(VERSION);
|
|
359
226
|
program.addCommand(initCommand);
|
|
360
227
|
program.addCommand(scanCommand);
|
|
@@ -367,8 +234,8 @@ program.parseAsync().then(async () => {
|
|
|
367
234
|
const update = await checkForUpdate();
|
|
368
235
|
if (update?.updateAvailable) {
|
|
369
236
|
console.error("");
|
|
370
|
-
console.error(
|
|
371
|
-
console.error(
|
|
237
|
+
console.error(chalk4.yellow(` Update available: ${update.current} \u2192 ${update.latest}`));
|
|
238
|
+
console.error(chalk4.dim(' Run "vibgrate update" to install the latest version.'));
|
|
372
239
|
console.error("");
|
|
373
240
|
}
|
|
374
241
|
}).catch((err) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ interface ProjectScan {
|
|
|
21
21
|
type: ProjectType;
|
|
22
22
|
path: string;
|
|
23
23
|
name: string;
|
|
24
|
+
/** Deterministic project ID: SHA-256 hash of `${path}:${name}:${workspaceId}` */
|
|
25
|
+
projectId?: string;
|
|
24
26
|
runtime?: string;
|
|
25
27
|
runtimeLatest?: string;
|
|
26
28
|
runtimeMajorsBehind?: number;
|
|
@@ -33,6 +35,8 @@ interface ProjectScan {
|
|
|
33
35
|
twoPlusBehind: number;
|
|
34
36
|
unknown: number;
|
|
35
37
|
};
|
|
38
|
+
/** Individual project drift score (computed per-project, then aggregated into artifact.drift) */
|
|
39
|
+
drift?: DriftScore;
|
|
36
40
|
}
|
|
37
41
|
interface DriftScore {
|
|
38
42
|
score: number;
|
|
@@ -84,6 +88,13 @@ interface ScanOptions {
|
|
|
84
88
|
baseline?: string;
|
|
85
89
|
changedOnly?: boolean;
|
|
86
90
|
concurrency: number;
|
|
91
|
+
/** Auto-push after scan. If set, artifact is uploaded using this DSN (or VIBGRATE_DSN env). */
|
|
92
|
+
push?: boolean;
|
|
93
|
+
dsn?: string;
|
|
94
|
+
/** Override data residency region for push */
|
|
95
|
+
region?: string;
|
|
96
|
+
/** Fail on push errors (like --strict on push command) */
|
|
97
|
+
strict?: boolean;
|
|
87
98
|
}
|
|
88
99
|
interface ScannerToggle {
|
|
89
100
|
enabled: boolean;
|
package/dist/index.js
CHANGED