@pbhamri/quartermaster-mcp 0.8.0 → 0.9.0
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 +14 -1
- package/bin/server.js +55 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -211,9 +211,22 @@ resources/
|
|
|
211
211
|
| `QM_REPO_ROOT` | `process.cwd()` | Override repo root for read-only access |
|
|
212
212
|
| `QM_CONNECT_DB` | `~/.quartermaster/connect-db.json` | Connect tracker path for `qm_personalize` |
|
|
213
213
|
| `QM_METRICS_FILE` | `~/.copilot/metrics/paved-path-events.jsonl` | Telemetry output path |
|
|
214
|
-
| `QM_METRICS_OPT_OUT` | (unset) | Set to `1` to disable telemetry |
|
|
214
|
+
| `QM_METRICS_OPT_OUT` | (unset) | Set to `1` to disable **all** telemetry (local file + beacon) |
|
|
215
|
+
| `QM_BEACON_URL` | (from package) | Override the usage-beacon endpoint |
|
|
216
|
+
| `QM_BEACON_OPT_OUT` | (unset) | Set to `1` to disable only the usage beacon |
|
|
215
217
|
| `QM_SELF_AGENT_DIR` | `~/.copilot/self-agent` | Self-agent skeleton directory |
|
|
216
218
|
|
|
219
|
+
### Telemetry & privacy
|
|
220
|
+
|
|
221
|
+
This package emits **one anonymized usage ping per machine per UTC day** so the
|
|
222
|
+
maintainer can count distinct PMs using it. The ping contains only a SHA-256
|
|
223
|
+
**machine hash** (`platform:arch:cpus:ram`) plus version, OS, and date —
|
|
224
|
+
**never** usernames, hostnames, IPs, file paths, repo names, or content.
|
|
225
|
+
|
|
226
|
+
Opt out any time with `QM_BEACON_OPT_OUT=1` (beacon only) or
|
|
227
|
+
`QM_METRICS_OPT_OUT=1` (everything). The beacon server is open-source in
|
|
228
|
+
[`beacon/`](beacon/README.md).
|
|
229
|
+
|
|
217
230
|
---
|
|
218
231
|
|
|
219
232
|
## Why MCP, not a script
|
package/bin/server.js
CHANGED
|
@@ -62,6 +62,60 @@ async function timed(type, baseData, fn) {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// ---- First-run-of-day usage beacon (privacy-preserving, opt-out) ----
|
|
66
|
+
// Sends ONE anonymized ping per machine per UTC day so the maintainer can
|
|
67
|
+
// count DISTINCT PMs who ran `npx @pbhamri/quartermaster-mcp`. Payload is
|
|
68
|
+
// machine hash (already non-PII: SHA-256 of platform:arch:cpus:ram) + version
|
|
69
|
+
// + OS + date. NO usernames, hostnames, paths, repo names, or content.
|
|
70
|
+
// Disable entirely with QM_BEACON_OPT_OUT=1 or QM_METRICS_OPT_OUT=1.
|
|
71
|
+
const BEACON_URL = process.env.QM_BEACON_URL || PKG.beaconUrl || "";
|
|
72
|
+
const BEACON_STATE_FILE = process.env.QM_BEACON_STATE
|
|
73
|
+
|| path.join(os.homedir(), ".quartermaster", "beacon-state.json");
|
|
74
|
+
|
|
75
|
+
function beaconOptedOut() {
|
|
76
|
+
return process.env.QM_BEACON_OPT_OUT === "1" || process.env.QM_METRICS_OPT_OUT === "1";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function alreadyBeaconedToday(today) {
|
|
80
|
+
try {
|
|
81
|
+
const s = JSON.parse(fs.readFileSync(BEACON_STATE_FILE, "utf8"));
|
|
82
|
+
return s.last_beacon_date === today;
|
|
83
|
+
} catch { return false; }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function markBeaconed(today) {
|
|
87
|
+
try {
|
|
88
|
+
fs.mkdirSync(path.dirname(BEACON_STATE_FILE), { recursive: true });
|
|
89
|
+
fs.writeFileSync(BEACON_STATE_FILE, JSON.stringify({ last_beacon_date: today, version: PKG.version }), "utf8");
|
|
90
|
+
} catch { /* non-fatal */ }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fire-and-forget. Never throws, never blocks server startup.
|
|
94
|
+
function sendBeacon() {
|
|
95
|
+
try {
|
|
96
|
+
if (beaconOptedOut() || !BEACON_URL) return;
|
|
97
|
+
if (typeof fetch !== "function") return; // Node < 18: skip silently
|
|
98
|
+
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD (UTC)
|
|
99
|
+
if (alreadyBeaconedToday(today)) return;
|
|
100
|
+
markBeaconed(today); // mark first so a slow/failed POST never double-sends
|
|
101
|
+
const payload = {
|
|
102
|
+
machine: machineId(),
|
|
103
|
+
version: PKG.version,
|
|
104
|
+
os: os.platform(),
|
|
105
|
+
arch: os.arch(),
|
|
106
|
+
date: today,
|
|
107
|
+
};
|
|
108
|
+
const ctrl = new AbortController();
|
|
109
|
+
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
110
|
+
fetch(BEACON_URL, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: { "content-type": "application/json" },
|
|
113
|
+
body: JSON.stringify(payload),
|
|
114
|
+
signal: ctrl.signal,
|
|
115
|
+
}).catch(() => {}).finally(() => clearTimeout(timer));
|
|
116
|
+
} catch { /* beacon must never break the server */ }
|
|
117
|
+
}
|
|
118
|
+
|
|
65
119
|
const readJson = (p) => JSON.parse(fs.readFileSync(p, "utf8"));
|
|
66
120
|
const writeText = (p, t) => { fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, t, "utf8"); };
|
|
67
121
|
const exists = (p) => fs.existsSync(p);
|
|
@@ -1089,4 +1143,5 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
|
1089
1143
|
|
|
1090
1144
|
await server.connect(new StdioServerTransport());
|
|
1091
1145
|
process.stderr.write(`quartermaster-mcp v${PKG.version} ready (stdio)\n`);
|
|
1146
|
+
sendBeacon(); // once/day anonymized usage ping; opt out via QM_BEACON_OPT_OUT=1
|
|
1092
1147
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pbhamri/quartermaster-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"beaconUrl": "",
|
|
4
5
|
"description": "MCP server that seeds any repo with a customizable PM kit. Any PM, any product — connect YOUR GitHub, ADO, Kusto, IcM. 19 tools: scaffolding, repo-read, onboarding, custom profile creation, self-agent auto-reply. Never ships personal data — profiles are templates you fill in.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|