@memprofile/agent-node
Version:
Lightweight Node.js heap-snapshot agent for MemProfiler AI
153 lines (147 loc) • 4.46 kB
JavaScript
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/config.ts
import os from "os";
var cfg;
var init_config = __esm({
"src/config.ts"() {
"use strict";
cfg = {
apiKey: process.env.MEMPROF_API_KEY || "",
endpoint: process.env.MEMPROF_ENDPOINT || "https://api.memprofiler.io/v1/snapshots",
intervalSec: +(process.env.MEMPROF_INTERVAL || 3600),
thresholdPct: +(process.env.MEMPROF_THRESHOLD || 10),
host: process.env.MEMPROF_HOSTNAME || os.hostname(),
snapshotDir: process.env.MEMPROF_TMP || ".snapshots"
};
if (!cfg.apiKey) throw new Error("MEMPROF_API_KEY missing");
}
});
// src/snapshot.ts
import inspector from "inspector";
import fs from "fs/promises";
function takeSnapshot(file) {
return __async(this, null, function* () {
const session = new inspector.Session();
session.connect();
const chunks = [];
yield new Promise((res, rej) => {
session.post("HeapProfiler.enable", () => {
session.post("HeapProfiler.takeHeapSnapshot", {}, (err) => {
if (err) rej(err);
});
});
session.on("HeapProfiler.addHeapSnapshotChunk", (m) => chunks.push(m.params.chunk));
session.on("HeapProfiler.reportHeapSnapshotProgress", (m) => {
if (m.params.finished) {
session.disconnect();
res();
}
});
});
yield fs.writeFile(file, chunks.join(""));
});
}
var init_snapshot = __esm({
"src/snapshot.ts"() {
"use strict";
}
});
// src/compress.ts
import { gzip } from "zlib";
import { promisify } from "util";
function gzFile(input) {
return __async(this, null, function* () {
return gz(input, { level: 9 });
});
}
var gz;
var init_compress = __esm({
"src/compress.ts"() {
"use strict";
gz = promisify(gzip);
}
});
// src/upload.ts
import fetch from "node-fetch";
import { FormData } from "formdata-node";
function upload(buf) {
return __async(this, null, function* () {
const fd = new FormData();
fd.set("host", cfg.host);
fd.set("language", "node");
fd.append("snapshot", buf, "snap.gz");
const res = yield fetch(cfg.endpoint, {
method: "POST",
headers: { "x-api-key": cfg.apiKey },
body: fd
});
if (!res.ok) {
const text = yield res.text();
throw new Error(`Upload failed (${res.status}): ${text.slice(0, 200)}`);
}
});
}
var init_upload = __esm({
"src/upload.ts"() {
"use strict";
init_config();
}
});
// src/agent.ts
import fs2 from "fs/promises";
import path from "path";
var require_agent = __commonJS({
"src/agent.ts"(exports) {
init_config();
init_snapshot();
init_compress();
init_upload();
(() => __async(null, null, function* () {
yield fs2.mkdir(cfg.snapshotDir, { recursive: true });
function cycle() {
return __async(this, null, function* () {
const stamp = Date.now();
const raw = path.join(cfg.snapshotDir, `${stamp}.heapsnapshot`);
yield takeSnapshot(raw);
const buf = yield fs2.readFile(raw);
const gzBuf = yield gzFile(buf);
yield upload(gzBuf);
const files = (yield fs2.readdir(cfg.snapshotDir)).filter((f) => f.endsWith(".heapsnapshot"));
if (files.length > 3) for (const f of files.slice(0, files.length - 3))
yield fs2.rm(path.join(cfg.snapshotDir, f));
console.log(`[memprof] snapshot ${stamp} uploaded (${(buf.length / 1e6).toFixed(1)} MB)`);
});
}
setInterval(cycle, cfg.intervalSec * 1e3);
cycle();
}))();
}
});
export default require_agent();