brakit 0.10.0 → 0.10.1
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/dist/api.d.ts +6 -5
- package/dist/api.js +23 -15
- package/dist/bin/brakit.js +120 -25
- package/dist/dashboard-client.global.js +470 -424
- package/dist/dashboard.html +558 -472
- package/dist/mcp/server.js +98 -22
- package/dist/runtime/index.js +361 -269
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ interface TracedRequest {
|
|
|
20
20
|
}
|
|
21
21
|
type RequestListener = (req: TracedRequest) => void;
|
|
22
22
|
|
|
23
|
-
type Framework = "nextjs" | "remix" | "nuxt" | "vite" | "astro" | "flask" | "fastapi" | "django" | "custom" | "unknown";
|
|
23
|
+
type Framework = "nextjs" | "remix" | "nuxt" | "vite" | "astro" | "express" | "fastify" | "koa" | "hono" | "nestjs" | "hapi" | "adonis" | "sails" | "flask" | "fastapi" | "django" | "custom" | "unknown";
|
|
24
24
|
interface DetectedProject {
|
|
25
25
|
framework: Framework;
|
|
26
26
|
devCommand: string;
|
|
@@ -227,10 +227,11 @@ declare class IssueStore {
|
|
|
227
227
|
stop(): void;
|
|
228
228
|
upsert(issue: Issue, source: IssueSource): StatefulIssue;
|
|
229
229
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
230
|
+
* Evidence-based reconciliation: for each active issue whose endpoint had
|
|
231
|
+
* traffic but the issue was NOT re-detected, increment cleanHitsSinceLastSeen.
|
|
232
|
+
* After CLEAN_HITS_FOR_RESOLUTION consecutive clean cycles, auto-resolve.
|
|
233
|
+
* Issues on endpoints with no recent traffic are marked stale after STALE_ISSUE_TTL_MS.
|
|
234
|
+
* Resolved and stale issues are pruned after their respective TTLs expire.
|
|
234
235
|
*/
|
|
235
236
|
reconcile(currentIssueIds: Set<string>, activeEndpoints: Set<string>): void;
|
|
236
237
|
transition(issueId: string, state: IssueState): boolean;
|
package/dist/api.js
CHANGED
|
@@ -232,7 +232,7 @@ var IssueStore = class {
|
|
|
232
232
|
existing.occurrences++;
|
|
233
233
|
existing.issue = issue;
|
|
234
234
|
existing.cleanHitsSinceLastSeen = 0;
|
|
235
|
-
if (existing.state === "resolved" || existing.state === "stale") {
|
|
235
|
+
if (existing.aiStatus !== "wont_fix" && (existing.state === "resolved" || existing.state === "stale")) {
|
|
236
236
|
existing.state = "regressed";
|
|
237
237
|
existing.resolvedAt = null;
|
|
238
238
|
}
|
|
@@ -258,10 +258,11 @@ var IssueStore = class {
|
|
|
258
258
|
return stateful;
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
261
|
+
* Evidence-based reconciliation: for each active issue whose endpoint had
|
|
262
|
+
* traffic but the issue was NOT re-detected, increment cleanHitsSinceLastSeen.
|
|
263
|
+
* After CLEAN_HITS_FOR_RESOLUTION consecutive clean cycles, auto-resolve.
|
|
264
|
+
* Issues on endpoints with no recent traffic are marked stale after STALE_ISSUE_TTL_MS.
|
|
265
|
+
* Resolved and stale issues are pruned after their respective TTLs expire.
|
|
265
266
|
*/
|
|
266
267
|
reconcile(currentIssueIds, activeEndpoints) {
|
|
267
268
|
const now = Date.now();
|
|
@@ -391,11 +392,22 @@ import { readFile as readFile3, readdir } from "fs/promises";
|
|
|
391
392
|
import { existsSync as existsSync4 } from "fs";
|
|
392
393
|
import { join as join2, relative } from "path";
|
|
393
394
|
var FRAMEWORKS = [
|
|
395
|
+
// Meta-frameworks first (they bundle Express/Vite internally)
|
|
394
396
|
{ name: "nextjs", dep: "next", devCmd: "next dev", bin: "next", defaultPort: 3e3, devArgs: ["dev", "--port"] },
|
|
395
397
|
{ name: "remix", dep: "@remix-run/dev", devCmd: "remix dev", bin: "remix", defaultPort: 3e3, devArgs: ["dev"] },
|
|
396
398
|
{ name: "nuxt", dep: "nuxt", devCmd: "nuxt dev", bin: "nuxt", defaultPort: 3e3, devArgs: ["dev", "--port"] },
|
|
397
|
-
{ name: "
|
|
398
|
-
{ name: "
|
|
399
|
+
{ name: "astro", dep: "astro", devCmd: "astro dev", bin: "astro", defaultPort: 4321, devArgs: ["dev", "--port"] },
|
|
400
|
+
{ name: "nestjs", dep: "@nestjs/core", devCmd: "nest start", bin: "nest", defaultPort: 3e3, devArgs: ["--watch"] },
|
|
401
|
+
{ name: "adonis", dep: "@adonisjs/core", devCmd: "node ace serve", bin: "ace", defaultPort: 3333, devArgs: ["serve", "--watch"] },
|
|
402
|
+
{ name: "sails", dep: "sails", devCmd: "sails lift", bin: "sails", defaultPort: 1337, devArgs: ["lift"] },
|
|
403
|
+
// Server frameworks
|
|
404
|
+
{ name: "hono", dep: "hono", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
405
|
+
{ name: "fastify", dep: "fastify", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
406
|
+
{ name: "koa", dep: "koa", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
407
|
+
{ name: "hapi", dep: "@hapi/hapi", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
408
|
+
{ name: "express", dep: "express", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
409
|
+
// Bundlers (last — likely used alongside a framework above)
|
|
410
|
+
{ name: "vite", dep: "vite", devCmd: "vite", bin: "vite", defaultPort: 5173, devArgs: ["--port"] }
|
|
399
411
|
];
|
|
400
412
|
async function detectProject(rootDir) {
|
|
401
413
|
const pkgPath = join2(rootDir, "package.json");
|
|
@@ -1170,14 +1182,10 @@ var DASHBOARD_API_GRAPH = `${DASHBOARD_PREFIX}/api/graph`;
|
|
|
1170
1182
|
var VALID_TABS_TUPLE = [
|
|
1171
1183
|
"overview",
|
|
1172
1184
|
"actions",
|
|
1173
|
-
"
|
|
1174
|
-
"fetches",
|
|
1175
|
-
"queries",
|
|
1176
|
-
"errors",
|
|
1177
|
-
"logs",
|
|
1185
|
+
"insights",
|
|
1178
1186
|
"performance",
|
|
1179
|
-
"
|
|
1180
|
-
"
|
|
1187
|
+
"graph",
|
|
1188
|
+
"explorer"
|
|
1181
1189
|
];
|
|
1182
1190
|
var VALID_TABS = new Set(VALID_TABS_TUPLE);
|
|
1183
1191
|
|
|
@@ -2411,7 +2419,7 @@ var AnalysisEngine = class {
|
|
|
2411
2419
|
};
|
|
2412
2420
|
|
|
2413
2421
|
// src/index.ts
|
|
2414
|
-
var VERSION = "0.10.
|
|
2422
|
+
var VERSION = "0.10.1";
|
|
2415
2423
|
export {
|
|
2416
2424
|
AdapterRegistry,
|
|
2417
2425
|
AnalysisEngine,
|
package/dist/bin/brakit.js
CHANGED
|
@@ -99,14 +99,10 @@ var init_labels = __esm({
|
|
|
99
99
|
VALID_TABS_TUPLE = [
|
|
100
100
|
"overview",
|
|
101
101
|
"actions",
|
|
102
|
-
"
|
|
103
|
-
"fetches",
|
|
104
|
-
"queries",
|
|
105
|
-
"errors",
|
|
106
|
-
"logs",
|
|
102
|
+
"insights",
|
|
107
103
|
"performance",
|
|
108
|
-
"
|
|
109
|
-
"
|
|
104
|
+
"graph",
|
|
105
|
+
"explorer"
|
|
110
106
|
];
|
|
111
107
|
VALID_TABS = new Set(VALID_TABS_TUPLE);
|
|
112
108
|
POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -140,7 +136,7 @@ var init_features = __esm({
|
|
|
140
136
|
MAX_TIMELINE_EVENTS = 20;
|
|
141
137
|
MAX_RESOLVED_DISPLAY = 5;
|
|
142
138
|
ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
|
|
143
|
-
MCP_SERVER_VERSION = "0.10.
|
|
139
|
+
MCP_SERVER_VERSION = "0.10.1";
|
|
144
140
|
RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
145
141
|
PORT_MIN = 1;
|
|
146
142
|
PORT_MAX = 65535;
|
|
@@ -366,10 +362,27 @@ async function enrichFindings(client) {
|
|
|
366
362
|
if (reqData.requests.length > 0) {
|
|
367
363
|
const req = reqData.requests[0];
|
|
368
364
|
if (req.id) {
|
|
369
|
-
const activity = await
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
365
|
+
const [activity, queries, fetches] = await Promise.all([
|
|
366
|
+
client.getActivity(req.id),
|
|
367
|
+
client.getQueries(req.id),
|
|
368
|
+
client.getFetches(req.id)
|
|
369
|
+
]);
|
|
370
|
+
const lines = [`Request took ${req.durationMs}ms.`];
|
|
371
|
+
if (queries.entries.length > 0) {
|
|
372
|
+
lines.push(`DB Queries (${queries.entries.length}):`);
|
|
373
|
+
for (const q of queries.entries.slice(0, 5)) {
|
|
374
|
+
const sql = q.sql ?? `${q.operation ?? ""} ${q.table ?? q.model ?? ""}`;
|
|
375
|
+
lines.push(` [${q.durationMs}ms] ${sql}`);
|
|
376
|
+
}
|
|
377
|
+
if (queries.entries.length > 5) lines.push(` ... and ${queries.entries.length - 5} more`);
|
|
378
|
+
}
|
|
379
|
+
if (fetches.entries.length > 0) {
|
|
380
|
+
lines.push(`Fetches (${fetches.entries.length}):`);
|
|
381
|
+
for (const f of fetches.entries.slice(0, 3)) {
|
|
382
|
+
lines.push(` [${f.durationMs}ms] ${f.method} ${f.url} \u2192 ${f.statusCode}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return lines.join("\n");
|
|
373
386
|
}
|
|
374
387
|
}
|
|
375
388
|
} catch {
|
|
@@ -488,6 +501,9 @@ var init_get_findings = __esm({
|
|
|
488
501
|
return { content: [{ type: "text", text: `Invalid state "${state}". Use: open, fixing, resolved, stale, regressed.` }], isError: true };
|
|
489
502
|
}
|
|
490
503
|
let findings = await enrichFindings(client);
|
|
504
|
+
if (!state) {
|
|
505
|
+
findings = findings.filter((f) => f.aiStatus !== "wont_fix");
|
|
506
|
+
}
|
|
491
507
|
if (severity) {
|
|
492
508
|
findings = findings.filter((f) => f.severity === severity);
|
|
493
509
|
}
|
|
@@ -499,21 +515,25 @@ var init_get_findings = __esm({
|
|
|
499
515
|
if (findings.length === 0) {
|
|
500
516
|
return { content: [{ type: "text", text: "No findings detected. The application looks healthy." }] };
|
|
501
517
|
}
|
|
502
|
-
const lines = [
|
|
503
|
-
`
|
|
518
|
+
const lines = [
|
|
519
|
+
`Found ${findings.length} issue(s). Full context included below \u2014 no need to call get_request_detail separately.`,
|
|
520
|
+
`After fixing, call report_fixes ONCE with all results.
|
|
521
|
+
`
|
|
522
|
+
];
|
|
504
523
|
for (const f of findings) {
|
|
505
524
|
lines.push(`[${f.severity.toUpperCase()}] ${f.title}`);
|
|
506
525
|
lines.push(` ID: ${f.findingId}`);
|
|
507
526
|
lines.push(` Endpoint: ${f.endpoint}`);
|
|
508
527
|
lines.push(` Issue: ${f.description}`);
|
|
509
|
-
if (f.context)
|
|
528
|
+
if (f.context) {
|
|
529
|
+
for (const ctxLine of f.context.split("\n")) {
|
|
530
|
+
lines.push(` ${ctxLine}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
510
533
|
lines.push(` Fix: ${f.hint}`);
|
|
511
534
|
if (f.aiStatus === "fixed") {
|
|
512
535
|
lines.push(` AI Status: fixed (awaiting verification)`);
|
|
513
536
|
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
514
|
-
} else if (f.aiStatus === "wont_fix") {
|
|
515
|
-
lines.push(` AI Status: won't fix`);
|
|
516
|
-
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
517
537
|
}
|
|
518
538
|
lines.push("");
|
|
519
539
|
}
|
|
@@ -901,6 +921,69 @@ var init_report_fix = __esm({
|
|
|
901
921
|
}
|
|
902
922
|
});
|
|
903
923
|
|
|
924
|
+
// src/mcp/tools/report-fixes.ts
|
|
925
|
+
var reportFixes;
|
|
926
|
+
var init_report_fixes = __esm({
|
|
927
|
+
"src/mcp/tools/report-fixes.ts"() {
|
|
928
|
+
"use strict";
|
|
929
|
+
init_type_guards();
|
|
930
|
+
reportFixes = {
|
|
931
|
+
name: "report_fixes",
|
|
932
|
+
description: "Report results for multiple findings in a single call. Use this instead of calling report_fix repeatedly \u2014 it's faster and requires only one confirmation. Pass a JSON array string where each item has finding_id, status ('fixed' or 'wont_fix'), and summary.",
|
|
933
|
+
inputSchema: {
|
|
934
|
+
type: "object",
|
|
935
|
+
properties: {
|
|
936
|
+
fixes: {
|
|
937
|
+
type: "string",
|
|
938
|
+
description: 'JSON array of fix reports. Example: [{"finding_id":"abc123","status":"fixed","summary":"Added input validation"}]'
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
required: ["fixes"]
|
|
942
|
+
},
|
|
943
|
+
async handler(client, args) {
|
|
944
|
+
let fixes;
|
|
945
|
+
try {
|
|
946
|
+
const raw = typeof args.fixes === "string" ? JSON.parse(args.fixes) : args.fixes;
|
|
947
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
948
|
+
return { content: [{ type: "text", text: "fixes must be a non-empty JSON array." }], isError: true };
|
|
949
|
+
}
|
|
950
|
+
fixes = raw.filter(
|
|
951
|
+
(item) => typeof item === "object" && item !== null && typeof item.finding_id === "string" && typeof item.status === "string" && typeof item.summary === "string"
|
|
952
|
+
);
|
|
953
|
+
if (fixes.length === 0) {
|
|
954
|
+
return { content: [{ type: "text", text: "No valid fix entries found. Each entry needs finding_id, status, and summary." }], isError: true };
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
return { content: [{ type: "text", text: "fixes must be valid JSON." }], isError: true };
|
|
958
|
+
}
|
|
959
|
+
const results = [];
|
|
960
|
+
let errors = 0;
|
|
961
|
+
for (const fix of fixes) {
|
|
962
|
+
if (!fix.finding_id || !isValidAiFixStatus(fix.status) || !fix.summary) {
|
|
963
|
+
results.push(`\u2717 Invalid entry: ${fix.finding_id || "missing ID"}`);
|
|
964
|
+
errors++;
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const ok = await client.reportFix(fix.finding_id, fix.status, fix.summary);
|
|
968
|
+
if (ok) {
|
|
969
|
+
const label = fix.status === "fixed" ? "fixed" : "won't fix";
|
|
970
|
+
results.push(`\u2713 ${fix.finding_id} \u2192 ${label}`);
|
|
971
|
+
} else {
|
|
972
|
+
results.push(`\u2717 ${fix.finding_id} \u2192 not found`);
|
|
973
|
+
errors++;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const errorSuffix = errors > 0 ? ` (${errors} error${errors !== 1 ? "s" : ""})` : "";
|
|
977
|
+
const summary = `Processed ${fixes.length} finding(s)${errorSuffix}. Dashboard updated.`;
|
|
978
|
+
return {
|
|
979
|
+
content: [{ type: "text", text: summary + "\n\n" + results.join("\n") }],
|
|
980
|
+
...errors > 0 ? { isError: true } : {}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
|
|
904
987
|
// src/mcp/tools/index.ts
|
|
905
988
|
function getToolDefinitions() {
|
|
906
989
|
return [...TOOL_MAP.values()].map((t) => ({
|
|
@@ -930,8 +1013,9 @@ var init_tools = __esm({
|
|
|
930
1013
|
init_get_report();
|
|
931
1014
|
init_clear_findings();
|
|
932
1015
|
init_report_fix();
|
|
1016
|
+
init_report_fixes();
|
|
933
1017
|
TOOL_MAP = new Map(
|
|
934
|
-
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings, reportFix].map((t) => [t.name, t])
|
|
1018
|
+
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings, reportFix, reportFixes].map((t) => [t.name, t])
|
|
935
1019
|
);
|
|
936
1020
|
}
|
|
937
1021
|
});
|
|
@@ -941,7 +1025,7 @@ var SERVER_INSTRUCTIONS, PROMPTS, PROMPT_MESSAGES;
|
|
|
941
1025
|
var init_prompts = __esm({
|
|
942
1026
|
"src/mcp/prompts.ts"() {
|
|
943
1027
|
"use strict";
|
|
944
|
-
SERVER_INSTRUCTIONS = "Brakit is a dev tool that monitors your running app for security vulnerabilities, performance issues, and query problems. IMPORTANT: After fixing
|
|
1028
|
+
SERVER_INSTRUCTIONS = "Brakit is a dev tool that monitors your running app for security vulnerabilities, performance issues, and query problems. IMPORTANT: After fixing brakit findings, call report_fixes (batch) with all results at once instead of calling report_fix individually. This is faster and requires only one confirmation.";
|
|
945
1029
|
PROMPTS = [
|
|
946
1030
|
{
|
|
947
1031
|
name: "check-app",
|
|
@@ -953,8 +1037,8 @@ var init_prompts = __esm({
|
|
|
953
1037
|
}
|
|
954
1038
|
];
|
|
955
1039
|
PROMPT_MESSAGES = {
|
|
956
|
-
"check-app": "Check my running app for security and performance issues using brakit. First get all findings, then get the endpoint summary. For any critical or warning findings, get the request detail to understand the root cause. Give me a clear report of what's wrong and offer to fix each issue. After fixing
|
|
957
|
-
"fix-findings": "Get all open brakit findings. For each finding:\n1. Get the request detail to understand the exact issue\n2. Find the source code responsible and fix it\n3.
|
|
1040
|
+
"check-app": "Check my running app for security and performance issues using brakit. First get all findings, then get the endpoint summary. For any critical or warning findings, get the request detail to understand the root cause. Give me a clear report of what's wrong and offer to fix each issue. After fixing issues, call report_fixes once with all results.",
|
|
1041
|
+
"fix-findings": "Get all open brakit findings. For each finding:\n1. Get the request detail to understand the exact issue\n2. Find the source code responsible and fix it\n3. Track the finding_id, status ('fixed' or 'wont_fix'), and a brief summary\n\nAfter processing ALL findings, call report_fixes ONCE with the full array of results. Do not call report_fix individually for each finding.\n\nAfter reporting, ask me to re-trigger the endpoints so brakit can verify the fixes."
|
|
958
1042
|
};
|
|
959
1043
|
}
|
|
960
1044
|
});
|
|
@@ -1123,11 +1207,22 @@ import { readFile as readFile3, readdir } from "fs/promises";
|
|
|
1123
1207
|
import { existsSync as existsSync4 } from "fs";
|
|
1124
1208
|
import { join as join2, relative } from "path";
|
|
1125
1209
|
var FRAMEWORKS = [
|
|
1210
|
+
// Meta-frameworks first (they bundle Express/Vite internally)
|
|
1126
1211
|
{ name: "nextjs", dep: "next", devCmd: "next dev", bin: "next", defaultPort: 3e3, devArgs: ["dev", "--port"] },
|
|
1127
1212
|
{ name: "remix", dep: "@remix-run/dev", devCmd: "remix dev", bin: "remix", defaultPort: 3e3, devArgs: ["dev"] },
|
|
1128
1213
|
{ name: "nuxt", dep: "nuxt", devCmd: "nuxt dev", bin: "nuxt", defaultPort: 3e3, devArgs: ["dev", "--port"] },
|
|
1129
|
-
{ name: "
|
|
1130
|
-
{ name: "
|
|
1214
|
+
{ name: "astro", dep: "astro", devCmd: "astro dev", bin: "astro", defaultPort: 4321, devArgs: ["dev", "--port"] },
|
|
1215
|
+
{ name: "nestjs", dep: "@nestjs/core", devCmd: "nest start", bin: "nest", defaultPort: 3e3, devArgs: ["--watch"] },
|
|
1216
|
+
{ name: "adonis", dep: "@adonisjs/core", devCmd: "node ace serve", bin: "ace", defaultPort: 3333, devArgs: ["serve", "--watch"] },
|
|
1217
|
+
{ name: "sails", dep: "sails", devCmd: "sails lift", bin: "sails", defaultPort: 1337, devArgs: ["lift"] },
|
|
1218
|
+
// Server frameworks
|
|
1219
|
+
{ name: "hono", dep: "hono", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
1220
|
+
{ name: "fastify", dep: "fastify", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
1221
|
+
{ name: "koa", dep: "koa", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
1222
|
+
{ name: "hapi", dep: "@hapi/hapi", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
1223
|
+
{ name: "express", dep: "express", devCmd: "node", bin: "node", defaultPort: 3e3 },
|
|
1224
|
+
// Bundlers (last — likely used alongside a framework above)
|
|
1225
|
+
{ name: "vite", dep: "vite", devCmd: "vite", bin: "vite", defaultPort: 5173, devArgs: ["--port"] }
|
|
1131
1226
|
];
|
|
1132
1227
|
async function detectProject(rootDir) {
|
|
1133
1228
|
const pkgPath = join2(rootDir, "package.json");
|
|
@@ -1910,7 +2005,7 @@ init_constants();
|
|
|
1910
2005
|
init_endpoint();
|
|
1911
2006
|
|
|
1912
2007
|
// src/index.ts
|
|
1913
|
-
var VERSION = "0.10.
|
|
2008
|
+
var VERSION = "0.10.1";
|
|
1914
2009
|
|
|
1915
2010
|
// src/cli/commands/install.ts
|
|
1916
2011
|
init_constants();
|