error-mom 0.2.0 → 0.3.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/cli.js +159 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Command } from "commander";
|
|
|
10
10
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
12
|
import { z } from "zod";
|
|
13
|
-
var VERSION = "0.
|
|
13
|
+
var VERSION = "0.3.1";
|
|
14
14
|
var CONFIG_DIR = join(homedir(), ".error-mom");
|
|
15
15
|
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
16
16
|
var program = new Command().name("error-mom").description("Query and operate a self-hosted Error Mom incident desk").version(VERSION);
|
|
@@ -64,6 +64,17 @@ program.command("resolve").description("Resolve one issue and record the release
|
|
|
64
64
|
)
|
|
65
65
|
);
|
|
66
66
|
});
|
|
67
|
+
program.command("delete-project").description("Permanently delete one project and all of its issues and history").argument("<project-id>").action(async (projectId) => {
|
|
68
|
+
const config = await loadConfig();
|
|
69
|
+
print(
|
|
70
|
+
await request(
|
|
71
|
+
config.server,
|
|
72
|
+
config.adminToken,
|
|
73
|
+
`/api/v1/projects/${encodeURIComponent(projectId)}`,
|
|
74
|
+
{ method: "DELETE" }
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
});
|
|
67
78
|
program.command("doctor").description("Verify collector health, credentials, and optional project ingestion").option("--project-key <key>", "Send and verify a synthetic event with this write-only key").action(async (options) => {
|
|
68
79
|
const config = await loadConfig();
|
|
69
80
|
const health = await request(config.server, void 0, "/api/health");
|
|
@@ -112,17 +123,18 @@ program.command("init").description("Create/select a project, install the SDK, a
|
|
|
112
123
|
const setupPath = await writeSetup(framework);
|
|
113
124
|
await writeFile(
|
|
114
125
|
join(process.cwd(), ".error-mom.json"),
|
|
115
|
-
`${JSON.stringify({ server: config.server, projectId: project.id, projectName: project.name, framework }, null, 2)}
|
|
126
|
+
`${JSON.stringify({ server: config.server, projectId: project.id, projectName: project.name, framework: framework.id }, null, 2)}
|
|
116
127
|
`
|
|
117
128
|
);
|
|
118
129
|
await appendEnvironment(framework, config.server, project.ingestKey);
|
|
119
130
|
print({
|
|
120
131
|
installed: !options.skipInstall,
|
|
121
132
|
project: { id: project.id, name: project.name, slug: project.slug },
|
|
122
|
-
framework,
|
|
133
|
+
framework: framework.id,
|
|
123
134
|
setupFile: setupPath,
|
|
124
135
|
verified: false,
|
|
125
|
-
|
|
136
|
+
wiring: framework.wiring,
|
|
137
|
+
nextAction: `Wire it up: ${framework.wiring} For handlers where a framework catches errors itself (queue/cron jobs, webhooks, MCP tools), wrap each with errorMom.wrap(fn, { culprit: "<name>" }). Then run error-mom doctor --project-key <key>.`
|
|
126
138
|
});
|
|
127
139
|
});
|
|
128
140
|
program.command("mcp").description("Run Error Mom tools over MCP stdio for coding agents").action(async () => {
|
|
@@ -193,6 +205,21 @@ async function runMcpServer() {
|
|
|
193
205
|
)
|
|
194
206
|
)
|
|
195
207
|
);
|
|
208
|
+
server.registerTool(
|
|
209
|
+
"delete_project",
|
|
210
|
+
{
|
|
211
|
+
description: "Permanently delete a project and all of its issues, samples, and history. Irreversible.",
|
|
212
|
+
inputSchema: { projectId: z.string().min(1) }
|
|
213
|
+
},
|
|
214
|
+
async ({ projectId }) => toolResult(
|
|
215
|
+
await request(
|
|
216
|
+
config.server,
|
|
217
|
+
config.adminToken,
|
|
218
|
+
`/api/v1/projects/${encodeURIComponent(projectId)}`,
|
|
219
|
+
{ method: "DELETE" }
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
);
|
|
196
223
|
await server.connect(new StdioServerTransport());
|
|
197
224
|
}
|
|
198
225
|
function toolResult(value) {
|
|
@@ -243,14 +270,125 @@ async function readPackageJson(cwd) {
|
|
|
243
270
|
);
|
|
244
271
|
}
|
|
245
272
|
}
|
|
273
|
+
var FRAMEWORKS = [
|
|
274
|
+
[
|
|
275
|
+
"next",
|
|
276
|
+
{
|
|
277
|
+
id: "next",
|
|
278
|
+
envStyle: "next",
|
|
279
|
+
wiring: "Import the setup file from the root layout for client errors; the generated instrumentation.ts already reports server errors (API routes, SSR, server actions)."
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
"@tauri-apps/api",
|
|
284
|
+
{
|
|
285
|
+
id: "tauri",
|
|
286
|
+
envStyle: "vite",
|
|
287
|
+
wiring: "Import the setup file first in the webview entry (main.tsx or equivalent). If the app spawns Node sidecar processes, also call initErrorMom from @kenkaiiii/error-mom/node at each sidecar entry so backend/LLM errors are reported too. Rust-side panics are not captured."
|
|
288
|
+
}
|
|
289
|
+
],
|
|
290
|
+
[
|
|
291
|
+
"electron",
|
|
292
|
+
{
|
|
293
|
+
id: "electron",
|
|
294
|
+
envStyle: "node",
|
|
295
|
+
wiring: "Call initErrorMom from @kenkaiiii/error-mom/node at the TOP of the main process entry, and import @kenkaiiii/error-mom (browser build) first in each renderer entry. Both report to the same project."
|
|
296
|
+
}
|
|
297
|
+
],
|
|
298
|
+
[
|
|
299
|
+
"astro",
|
|
300
|
+
{
|
|
301
|
+
id: "astro",
|
|
302
|
+
envStyle: "vite",
|
|
303
|
+
wiring: "Load the setup file as a client script in the base layout so it runs on every page. For SSR/API routes, add src/middleware.ts that wraps next() with errorMom.wrap using @kenkaiiii/error-mom/node."
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
[
|
|
307
|
+
"@sveltejs/kit",
|
|
308
|
+
{
|
|
309
|
+
id: "sveltekit",
|
|
310
|
+
envStyle: "vite",
|
|
311
|
+
wiring: "Import the setup file in hooks.client.ts and export handleError from BOTH hooks.client.ts and hooks.server.ts calling errorMom.captureError(error); the server hook uses @kenkaiiii/error-mom/node. SvelteKit catches load/endpoint errors, so handleError is its official reporting hook."
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
[
|
|
315
|
+
"nuxt",
|
|
316
|
+
{
|
|
317
|
+
id: "nuxt",
|
|
318
|
+
envStyle: "vite",
|
|
319
|
+
wiring: "Create a client plugin (plugins/error-mom.client.ts) importing the setup file, and a nitro plugin hooking 'error' with @kenkaiiii/error-mom/node for server-side errors."
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
[
|
|
323
|
+
"@remix-run/react",
|
|
324
|
+
{
|
|
325
|
+
id: "remix",
|
|
326
|
+
envStyle: "node",
|
|
327
|
+
wiring: "Import the setup file in entry.client.tsx, and export handleError from entry.server.tsx calling errorMom.captureError(error). Remix catches loader/action errors and handleError is its official reporting hook."
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
[
|
|
331
|
+
"@angular/core",
|
|
332
|
+
{
|
|
333
|
+
id: "angular",
|
|
334
|
+
envStyle: "node",
|
|
335
|
+
wiring: "Import the setup file in main.ts and provide a custom ErrorHandler that calls errorMom.captureError(error). Angular catches component errors in its own handler."
|
|
336
|
+
}
|
|
337
|
+
],
|
|
338
|
+
[
|
|
339
|
+
"express",
|
|
340
|
+
{
|
|
341
|
+
id: "express",
|
|
342
|
+
envStyle: "node",
|
|
343
|
+
wiring: "Import the setup file at the TOP of the server entry, and add a final error middleware: (err, req, res, next) => { errorMom.captureError(err, { culprit: `${req.method} ${req.path}` }); next(err); }. Express catches route errors, so the middleware is where they surface."
|
|
344
|
+
}
|
|
345
|
+
],
|
|
346
|
+
[
|
|
347
|
+
"fastify",
|
|
348
|
+
{
|
|
349
|
+
id: "fastify",
|
|
350
|
+
envStyle: "node",
|
|
351
|
+
wiring: "Import the setup file at the TOP of the server entry, and call errorMom.captureError(error) inside setErrorHandler before replying. Fastify catches handler errors there."
|
|
352
|
+
}
|
|
353
|
+
],
|
|
354
|
+
[
|
|
355
|
+
"hono",
|
|
356
|
+
{
|
|
357
|
+
id: "hono",
|
|
358
|
+
envStyle: "node",
|
|
359
|
+
wiring: "Import the setup file at the TOP of the server entry, and call errorMom.captureError(err) inside app.onError. Hono catches handler errors there."
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
[
|
|
363
|
+
"@nestjs/core",
|
|
364
|
+
{
|
|
365
|
+
id: "nestjs",
|
|
366
|
+
envStyle: "node",
|
|
367
|
+
wiring: "Import the setup file at the TOP of main.ts, and add a global exception filter that calls errorMom.captureError(exception) before delegating to the base filter."
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
[
|
|
371
|
+
"vite",
|
|
372
|
+
{
|
|
373
|
+
id: "vite",
|
|
374
|
+
envStyle: "vite",
|
|
375
|
+
wiring: "Import the setup file first in the app entry (main.tsx or equivalent)."
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
];
|
|
246
379
|
function detectFramework(packageJson) {
|
|
247
380
|
const dependencies = {
|
|
248
381
|
...packageJson.dependencies ?? {},
|
|
249
382
|
...packageJson.devDependencies ?? {}
|
|
250
383
|
};
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
384
|
+
for (const [dependency, info] of FRAMEWORKS) {
|
|
385
|
+
if (dependencies[dependency]) return info;
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
id: "node",
|
|
389
|
+
envStyle: "node",
|
|
390
|
+
wiring: "Import the setup file at the TOP of the main entry point, before anything else."
|
|
391
|
+
};
|
|
254
392
|
}
|
|
255
393
|
function detectPackageManager(cwd) {
|
|
256
394
|
if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
@@ -271,25 +409,24 @@ function installSdk(packageManager) {
|
|
|
271
409
|
async function writeSetup(framework) {
|
|
272
410
|
const sourceDirectory = existsSync(join(process.cwd(), "src")) ? "src" : ".";
|
|
273
411
|
const relativePath = join(sourceDirectory, "error-mom.ts");
|
|
274
|
-
if (framework === "next") await writeNextInstrumentation(sourceDirectory);
|
|
275
|
-
const environment = framework === "next" ? "process.env.NEXT_PUBLIC_" : framework === "vite" ? "import.meta.env.VITE_" : "process.env.";
|
|
276
|
-
const moduleName = framework === "node" ? "@kenkaiiii/error-mom/node" : "@kenkaiiii/error-mom";
|
|
277
|
-
const contents = `${framework === "next" ? '"use client";\n\n' : ""}import { initErrorMom } from "${moduleName}";
|
|
412
|
+
if (framework.id === "next") await writeNextInstrumentation(sourceDirectory);
|
|
413
|
+
const environment = framework.envStyle === "next" ? "process.env.NEXT_PUBLIC_" : framework.envStyle === "vite" ? "import.meta.env.VITE_" : "process.env.";
|
|
414
|
+
const moduleName = framework.envStyle === "node" ? "@kenkaiiii/error-mom/node" : "@kenkaiiii/error-mom";
|
|
415
|
+
const contents = `${framework.id === "next" ? '"use client";\n\n' : ""}import { initErrorMom } from "${moduleName}";
|
|
278
416
|
|
|
279
417
|
const server = ${environment}ERROR_MOM_SERVER;
|
|
280
418
|
const projectKey = ${environment}ERROR_MOM_PROJECT_KEY;
|
|
281
419
|
const release = ${environment}ERROR_MOM_RELEASE;
|
|
282
420
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
421
|
+
export const errorMom =
|
|
422
|
+
server && projectKey
|
|
423
|
+
? initErrorMom({
|
|
424
|
+
server,
|
|
425
|
+
projectKey,
|
|
426
|
+
environment: ${environment}ERROR_MOM_ENVIRONMENT ?? "production",
|
|
427
|
+
...(release ? { release } : {}),
|
|
428
|
+
})
|
|
429
|
+
: undefined;
|
|
293
430
|
`;
|
|
294
431
|
await writeFile(join(process.cwd(), relativePath), contents);
|
|
295
432
|
return relativePath;
|
|
@@ -324,7 +461,7 @@ export const onRequestError: Instrumentation.onRequestError = async (
|
|
|
324
461
|
await writeFile(file, contents);
|
|
325
462
|
}
|
|
326
463
|
async function appendEnvironment(framework, server, key) {
|
|
327
|
-
const prefix = framework === "next" ? "NEXT_PUBLIC_" : framework === "vite" ? "VITE_" : "";
|
|
464
|
+
const prefix = framework.envStyle === "next" ? "NEXT_PUBLIC_" : framework.envStyle === "vite" ? "VITE_" : "";
|
|
328
465
|
const file = join(process.cwd(), ".env.local");
|
|
329
466
|
const existing = existsSync(file) ? await readFile(file, "utf8") : "";
|
|
330
467
|
const lines = [
|