diff --git a/nerve.yaml b/nerve.yaml index 65df5c2..289f4eb 100644 --- a/nerve.yaml +++ b/nerve.yaml @@ -2,19 +2,24 @@ senses: linux-system-health: group: system + interval: 30s throttle: 10s timeout: 15s - grace_period: null hermes-gateway-health: group: system + interval: 2m throttle: 30s timeout: 30s - grace_period: null hermes-session-message-stats: group: hermes + interval: 15m throttle: 30s timeout: 60s - grace_period: null + worker-process-metrics: + group: system + interval: 1m + throttle: 15s + timeout: 5s workflows: sense-generator: @@ -23,26 +28,6 @@ workflows: workflow-generator: concurrency: 1 overflow: drop - pr-summarizer: - concurrency: 1 - overflow: drop - pr-code-reviewer: - concurrency: 1 - overflow: drop - hello-world: - concurrency: 1 - overflow: drop gitea-issue-solver: concurrency: 1 overflow: drop - -reflexes: - - kind: sense - sense: linux-system-health - interval: 30s - - kind: sense - sense: hermes-gateway-health - interval: 2m - - kind: sense - sense: hermes-session-message-stats - interval: 15m diff --git a/senses/hermes-gateway-health/index.js b/senses/hermes-gateway-health/index.js index dde4bb2..36bf98b 100644 --- a/senses/hermes-gateway-health/index.js +++ b/senses/hermes-gateway-health/index.js @@ -4,6 +4,69 @@ import { hermesGatewayHealth } from "./schema.ts"; /** Keep subprocess deadlines slightly under typical sense timeout (30s). */ const EXEC_TIMEOUT_MS = 25_000; +/** HTTP probe stays below EXEC_TIMEOUT_MS and sense timeout (30s). */ +const HTTP_TIMEOUT_MS = Math.min(23_000, EXEC_TIMEOUT_MS - 2000); + +const HTTP_ERROR_MAX_LEN = 256; + +function gatewayProbeUrl() { + const u = + process.env.HERMES_GATEWAY_HEALTH_URL ?? + process.env.NERVE_HERMES_GATEWAY_URL ?? + ""; + return String(u).trim(); +} + +function truncateHttpError(err) { + const raw = + err && typeof err === "object" && "code" in err && err.code + ? String(err.code) + : String(err?.message ?? err ?? "error"); + const s = raw.trim() || "error"; + return s.length > HTTP_ERROR_MAX_LEN ? s.slice(0, HTTP_ERROR_MAX_LEN) : s; +} + +/** + * GET the gateway URL; success = HTTP 200–399. + * URL must be set via HERMES_GATEWAY_HEALTH_URL or NERVE_HERMES_GATEWAY_URL. + */ +async function probeGatewayHttp(url) { + if (!url) { + return { + httpOk: 0, + httpStatusCode: 0, + httpLatencyMs: 0, + httpError: "missing_url", + }; + } + const t0 = Date.now(); + try { + const signal = AbortSignal.timeout(HTTP_TIMEOUT_MS); + const res = await fetch(url, { + method: "GET", + signal, + redirect: "follow", + }); + const httpLatencyMs = Date.now() - t0; + const code = res.status; + const ok = code >= 200 && code < 400; + return { + httpOk: ok ? 1 : 0, + httpStatusCode: code, + httpLatencyMs, + httpError: ok ? "" : truncateHttpError({ message: `HTTP ${code}` }), + }; + } catch (err) { + const httpLatencyMs = Date.now() - t0; + return { + httpOk: 0, + httpStatusCode: 0, + httpLatencyMs, + httpError: truncateHttpError(err), + }; + } +} + /** * When `ps` lacks `etimes` (wall-clock seconds since start), parse `etime` * ([[dd-]hh:]mm:ss) into seconds. See ps(1) `etime` field description. @@ -293,6 +356,23 @@ export async function compute(db, _peers) { } } + let httpOk = 0; + let httpStatusCode = 0; + let httpLatencyMs = 0; + let httpError = ""; + try { + const h = await probeGatewayHttp(gatewayProbeUrl()); + httpOk = h.httpOk; + httpStatusCode = h.httpStatusCode; + httpLatencyMs = h.httpLatencyMs; + httpError = h.httpError; + } catch { + httpOk = 0; + httpStatusCode = 0; + httpLatencyMs = 0; + httpError = "probe_failed"; + } + const storedMainPid = mainPid > 0 ? mainPid : 0; const row = { @@ -304,6 +384,10 @@ export async function compute(db, _peers) { uptimeSec: alive ? uptimeSec : 0, activeSessions, childProcessCount: alive ? childProcessCount : 0, + httpOk, + httpStatusCode, + httpLatencyMs, + httpError, }; await db.insert(hermesGatewayHealth).values(row); @@ -317,5 +401,9 @@ export async function compute(db, _peers) { uptimeSec: row.uptimeSec, activeSessions: row.activeSessions, childProcessCount: row.childProcessCount, + httpOk: row.httpOk, + httpStatusCode: row.httpStatusCode, + httpLatencyMs: row.httpLatencyMs, + httpError: row.httpError, }; } diff --git a/senses/hermes-gateway-health/migrations/0002_add_http_probe.sql b/senses/hermes-gateway-health/migrations/0002_add_http_probe.sql new file mode 100644 index 0000000..a5787b9 --- /dev/null +++ b/senses/hermes-gateway-health/migrations/0002_add_http_probe.sql @@ -0,0 +1,7 @@ +-- Migration: 0002_add_http_probe +-- HTTP reachability columns for hermes-gateway-health sense. + +ALTER TABLE hermes_gateway_health ADD COLUMN http_ok INTEGER NOT NULL DEFAULT 0; +ALTER TABLE hermes_gateway_health ADD COLUMN http_status_code INTEGER NOT NULL DEFAULT 0; +ALTER TABLE hermes_gateway_health ADD COLUMN http_latency_ms INTEGER NOT NULL DEFAULT 0; +ALTER TABLE hermes_gateway_health ADD COLUMN http_error TEXT NOT NULL DEFAULT ''; diff --git a/senses/hermes-gateway-health/schema.ts b/senses/hermes-gateway-health/schema.ts index 66469f2..1ca1ba0 100644 --- a/senses/hermes-gateway-health/schema.ts +++ b/senses/hermes-gateway-health/schema.ts @@ -1,4 +1,4 @@ -import { integer, real, sqliteTable } from "drizzle-orm/sqlite-core"; +import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core"; export const hermesGatewayHealth = sqliteTable("hermes_gateway_health", { id: integer("id").primaryKey({ autoIncrement: true }), @@ -10,4 +10,8 @@ export const hermesGatewayHealth = sqliteTable("hermes_gateway_health", { uptimeSec: integer("uptime_sec").notNull(), activeSessions: integer("active_sessions").notNull(), childProcessCount: integer("child_process_count").notNull(), + httpOk: integer("http_ok").notNull(), + httpStatusCode: integer("http_status_code").notNull(), + httpLatencyMs: integer("http_latency_ms").notNull(), + httpError: text("http_error").notNull(), }); diff --git a/senses/worker-process-metrics/index.js b/senses/worker-process-metrics/index.js new file mode 100644 index 0000000..59fc8aa --- /dev/null +++ b/senses/worker-process-metrics/index.js @@ -0,0 +1,35 @@ +import { workerProcessMetrics } from "./schema.ts"; + +function round2(n) { + return Math.round(n * 100) / 100; +} + +export async function compute(db, _peers) { + const ts = Date.now(); + const pid = process.pid; + const uptimeSec = process.uptime(); + const m = process.memoryUsage(); + const heapUsedMB = round2(m.heapUsed / 1024 / 1024); + const rssMB = round2(m.rss / 1024 / 1024); + const externalMB = round2(m.external / 1024 / 1024); + + const row = { + ts, + pid, + uptimeSec, + heapUsedMB, + rssMB, + externalMB, + }; + + await db.insert(workerProcessMetrics).values(row); + + return { + ts: row.ts, + pid: row.pid, + uptimeSec: row.uptimeSec, + heapUsedMB: row.heapUsedMB, + rssMB: row.rssMB, + externalMB: row.externalMB, + }; +} diff --git a/senses/worker-process-metrics/migrations/0001_init.sql b/senses/worker-process-metrics/migrations/0001_init.sql new file mode 100644 index 0000000..989f19d --- /dev/null +++ b/senses/worker-process-metrics/migrations/0001_init.sql @@ -0,0 +1,11 @@ +-- Migration: 0001_init +-- Creates the worker_process_metrics table for worker-process-metrics sense. + +CREATE TABLE IF NOT EXISTS worker_process_metrics ( + ts INTEGER PRIMARY KEY, + pid INTEGER NOT NULL, + uptime_sec REAL NOT NULL, + heap_used_mb REAL NOT NULL, + rss_mb REAL NOT NULL, + external_mb REAL NOT NULL +); diff --git a/senses/worker-process-metrics/schema.ts b/senses/worker-process-metrics/schema.ts new file mode 100644 index 0000000..59045f2 --- /dev/null +++ b/senses/worker-process-metrics/schema.ts @@ -0,0 +1,10 @@ +import { integer, real, sqliteTable } from "drizzle-orm/sqlite-core"; + +export const workerProcessMetrics = sqliteTable("worker_process_metrics", { + ts: integer("ts").primaryKey(), + pid: integer("pid").notNull(), + uptimeSec: real("uptime_sec").notNull(), + heapUsedMB: real("heap_used_mb").notNull(), + rssMB: real("rss_mb").notNull(), + externalMB: real("external_mb").notNull(), +}); diff --git a/workflows/sense-generator/.gitignore b/workflows/sense-generator/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/workflows/sense-generator/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/workflows/sense-generator/package.json b/workflows/sense-generator/package.json index 9e4cb60..95ead85 100644 --- a/workflows/sense-generator/package.json +++ b/workflows/sense-generator/package.json @@ -3,6 +3,9 @@ "version": "0.0.1", "private": true, "type": "module", + "scripts": { + "build": "esbuild index.ts --bundle --platform=node --format=esm --outdir=dist --packages=external" + }, "dependencies": { "@uncaged/nerve-core": "latest", "@uncaged/nerve-workflow-utils": "latest", @@ -10,6 +13,7 @@ }, "devDependencies": { "@types/node": "^22.0.0", + "esbuild": "^0.27.0", "typescript": "^5.7.0" }, "pnpm": { diff --git a/workflows/sense-generator/pnpm-lock.yaml b/workflows/sense-generator/pnpm-lock.yaml index 237146e..2f15cb9 100644 --- a/workflows/sense-generator/pnpm-lock.yaml +++ b/workflows/sense-generator/pnpm-lock.yaml @@ -26,15 +26,179 @@ importers: '@types/node': specifier: ^22.0.0 version: 22.19.17 + esbuild: + specifier: ^0.27.0 + version: 0.27.7 typescript: specifier: ^5.7.0 version: 5.9.3 packages: + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@types/node@22.19.17': resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -48,10 +212,117 @@ packages: snapshots: + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + '@types/node@22.19.17': dependencies: undici-types: 6.21.0 + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + typescript@5.9.3: {} undici-types@6.21.0: {} diff --git a/workflows/sense-generator/tsconfig.json b/workflows/sense-generator/tsconfig.json index fc00159..1ae98b5 100644 --- a/workflows/sense-generator/tsconfig.json +++ b/workflows/sense-generator/tsconfig.json @@ -6,7 +6,8 @@ "moduleResolution": "NodeNext", "strict": true, "skipLibCheck": true, - "noEmit": true, + "noEmit": false, + "declaration": false, "types": ["node"] }, "include": ["./**/*.ts"]