@orangepro/orangepro-mcp 0.2.23 → 0.2.24
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.
|
@@ -13,7 +13,23 @@ const TEST_FRAMEWORK_DEPS = {
|
|
|
13
13
|
pytest: { layer: "unit" },
|
|
14
14
|
unittest: { layer: "unit" },
|
|
15
15
|
rspec: { layer: "unit" },
|
|
16
|
-
junit: { layer: "unit" }
|
|
16
|
+
junit: { layer: "unit" },
|
|
17
|
+
detox: { layer: "e2e" },
|
|
18
|
+
"@testing-library/react-native": { layer: "component" }
|
|
19
|
+
};
|
|
20
|
+
const RUNTIME_HINTS = {
|
|
21
|
+
"react-native": "React Native",
|
|
22
|
+
"expo": "Expo",
|
|
23
|
+
"next": "Next.js",
|
|
24
|
+
"nuxt": "Nuxt",
|
|
25
|
+
"express": "Express",
|
|
26
|
+
"fastify": "Fastify",
|
|
27
|
+
"@angular/core": "Angular",
|
|
28
|
+
"vue": "Vue",
|
|
29
|
+
"svelte": "Svelte",
|
|
30
|
+
"django": "Django",
|
|
31
|
+
"flask": "Flask",
|
|
32
|
+
"spring-boot": "Spring Boot",
|
|
17
33
|
};
|
|
18
34
|
const CONFIG_FRAMEWORK_HINTS = [
|
|
19
35
|
{ match: /^vitest\.config\.[tj]s$/, name: "vitest", layer: "unit" },
|
|
@@ -57,6 +73,13 @@ export function detectFromPackageJson(relPath, content) {
|
|
|
57
73
|
frameworks.push({ name: dep.replace(/^@playwright\/test$/, "playwright"), category: "test", test_layer: hit.layer, evidence_ref: relPath });
|
|
58
74
|
}
|
|
59
75
|
}
|
|
76
|
+
// Runtime framework detection (for display when no test framework found)
|
|
77
|
+
for (const dep of depNames) {
|
|
78
|
+
const hint = RUNTIME_HINTS[dep];
|
|
79
|
+
if (hint) {
|
|
80
|
+
frameworks.push({ name: hint, category: "runtime", evidence_ref: relPath });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
60
83
|
return { pkg, frameworks };
|
|
61
84
|
}
|
|
62
85
|
/** Detect test frameworks from non-npm manifests (pyproject.toml, requirements). */
|
package/dist/local/operations.js
CHANGED
|
@@ -2,6 +2,7 @@ import { execFileSync, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { pingTelemetry } from "./telemetry.js";
|
|
5
6
|
import { LOCAL_GRAPH_SCHEMA_VERSION } from "./graph/ontology.js";
|
|
6
7
|
import { systemClock } from "./util/time.js";
|
|
7
8
|
import { redactSecrets } from "./util/redact.js";
|
|
@@ -1510,6 +1511,21 @@ export async function opStart(root, opts = {}, deps = defaultDeps()) {
|
|
|
1510
1511
|
suppressProgress: true
|
|
1511
1512
|
}, deps);
|
|
1512
1513
|
const warnings = [...analyze.warnings];
|
|
1514
|
+
let dominantLang = "unknown";
|
|
1515
|
+
try {
|
|
1516
|
+
const g = loadGraph(root);
|
|
1517
|
+
const lc = {};
|
|
1518
|
+
for (const n of g.nodes) {
|
|
1519
|
+
const lang = n.kind === "File" && typeof n.properties.language === "string" ? n.properties.language : null;
|
|
1520
|
+
if (lang)
|
|
1521
|
+
lc[lang] = (lc[lang] ?? 0) + 1;
|
|
1522
|
+
}
|
|
1523
|
+
const top = Object.entries(lc).sort((a, b) => b[1] - a[1]);
|
|
1524
|
+
if (top.length > 0)
|
|
1525
|
+
dominantLang = top[0][0];
|
|
1526
|
+
}
|
|
1527
|
+
catch { /* telemetry is best-effort */ }
|
|
1528
|
+
pingTelemetry({ fileCount: scope.files, language: dominantLang });
|
|
1513
1529
|
reportProgress("start: deterministic graph is ready", { current: 4, total: 8 });
|
|
1514
1530
|
const staticSnapshot = writeStartStaticSnapshot(root, opts.baseRef, warnings);
|
|
1515
1531
|
const providerConfigured = deps.aiProvider !== undefined || resolveProviderConfig(providerEnv, providerOpts) !== null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous usage telemetry for OrangePro MCP.
|
|
3
|
+
*
|
|
4
|
+
* Sends one lightweight ping per run with: version, detected language,
|
|
5
|
+
* file count bucket, OS, and node version. No code, no file names,
|
|
6
|
+
* no repo name, no identity, no IP stored.
|
|
7
|
+
*
|
|
8
|
+
* Disable: set DO_NOT_TRACK=1 or ORANGEPRO_NO_TELEMETRY=1
|
|
9
|
+
*/
|
|
10
|
+
import https from "https";
|
|
11
|
+
import { readFileSync } from "fs";
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
const ENDPOINT_HOST = "telemetry.orangepro.ai";
|
|
14
|
+
const ENDPOINT_PATH = "/v1/ping";
|
|
15
|
+
const TIMEOUT_MS = 3000;
|
|
16
|
+
function getVersion() {
|
|
17
|
+
try {
|
|
18
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf8"));
|
|
19
|
+
return pkg.version || "unknown";
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "unknown";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function fileBucket(count) {
|
|
26
|
+
if (count < 50)
|
|
27
|
+
return "1-49";
|
|
28
|
+
if (count < 200)
|
|
29
|
+
return "50-199";
|
|
30
|
+
if (count < 500)
|
|
31
|
+
return "200-499";
|
|
32
|
+
if (count < 1000)
|
|
33
|
+
return "500-999";
|
|
34
|
+
if (count < 5000)
|
|
35
|
+
return "1000-4999";
|
|
36
|
+
if (count < 10000)
|
|
37
|
+
return "5000-9999";
|
|
38
|
+
return "10000+";
|
|
39
|
+
}
|
|
40
|
+
export function pingTelemetry(payload) {
|
|
41
|
+
// Respect DO_NOT_TRACK standard and custom env var
|
|
42
|
+
if (process.env.DO_NOT_TRACK === "1" ||
|
|
43
|
+
process.env.ORANGEPRO_NO_TELEMETRY === "1") {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const data = JSON.stringify({
|
|
47
|
+
event: "run",
|
|
48
|
+
v: getVersion(),
|
|
49
|
+
lang: payload.language,
|
|
50
|
+
files: fileBucket(payload.fileCount),
|
|
51
|
+
os: process.platform,
|
|
52
|
+
node: process.version,
|
|
53
|
+
ts: new Date().toISOString(),
|
|
54
|
+
});
|
|
55
|
+
try {
|
|
56
|
+
const req = https.request({
|
|
57
|
+
hostname: ENDPOINT_HOST,
|
|
58
|
+
path: ENDPOINT_PATH,
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
"Content-Length": Buffer.byteLength(data),
|
|
63
|
+
},
|
|
64
|
+
timeout: TIMEOUT_MS,
|
|
65
|
+
}, () => { } // ignore response
|
|
66
|
+
);
|
|
67
|
+
req.on("error", () => { }); // silent fail — never block the user
|
|
68
|
+
req.on("timeout", () => req.destroy());
|
|
69
|
+
req.write(data);
|
|
70
|
+
req.end();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Never throw — telemetry must never affect the user experience
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -840,11 +840,18 @@ function riskRows(risks, graph) {
|
|
|
840
840
|
});
|
|
841
841
|
}
|
|
842
842
|
function frameworkLabel(graph) {
|
|
843
|
-
const
|
|
843
|
+
const allFrameworks = graph.nodes
|
|
844
844
|
.filter((n) => n.kind === "Framework")
|
|
845
845
|
.map((n) => n.title || n.external_id.replace(/^framework:/, ""))
|
|
846
846
|
.sort();
|
|
847
|
-
|
|
847
|
+
if (allFrameworks.length)
|
|
848
|
+
return allFrameworks.slice(0, 4).join(", ");
|
|
849
|
+
// Fallback: check for runtime framework nodes
|
|
850
|
+
const runtimeNodes = graph.nodes
|
|
851
|
+
.filter((n) => n.kind === "Package" && /react.native|expo|next|angular|vue|express/i.test(n.title || n.external_id))
|
|
852
|
+
.map((n) => n.title || n.external_id)
|
|
853
|
+
.sort();
|
|
854
|
+
return runtimeNodes.length ? runtimeNodes.slice(0, 3).join(", ") : "Unknown framework";
|
|
848
855
|
}
|
|
849
856
|
export function buildBehaviorReportData(graph, ledger, opts = {}) {
|
|
850
857
|
const { rows } = buildRtm(graph, ledger);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangepro/orangepro-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.24",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OrangePro (`opro`) — a local-first, BYOK CLI + MCP server that builds an evidence graph from a local checkout, ingests runtime coverage, and generates grounded tests. Metadata-only exports; no source upload; generated tests stay local.",
|
|
6
6
|
"license": "MIT",
|