فهرست منبع

feat(cli): add heap snapshot signal

Dax Raad 20 ساعت پیش
والد
کامیت
613c570a3b
3فایلهای تغییر یافته به همراه51 افزوده شده و 7 حذف شده
  1. 4 1
      packages/cli/bin/opencode2.cjs
  2. 37 0
      packages/cli/src/heap.ts
  3. 10 6
      packages/cli/src/index.ts

+ 4 - 1
packages/cli/bin/opencode2.cjs

@@ -5,7 +5,10 @@ const fs = require("fs")
 const path = require("path")
 const os = require("os")
 
-const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
+const forwardedSignals =
+  process.platform === "win32"
+    ? ["SIGINT", "SIGTERM", "SIGHUP"]
+    : ["SIGINT", "SIGTERM", "SIGHUP", "SIGUSR1"]
 
 function run(target) {
   const child = childProcess.spawn(target, process.argv.slice(2), { stdio: "inherit" })

+ 37 - 0
packages/cli/src/heap.ts

@@ -0,0 +1,37 @@
+import { Global } from "@opencode-ai/util/global"
+import { Effect, Queue } from "effect"
+import path from "node:path"
+
+export const listen = Effect.gen(function* () {
+  const global = yield* Global.Service
+  if (process.platform === "win32") return
+  const signals = yield* Queue.dropping<void>(1)
+  yield* Effect.acquireRelease(
+    Effect.sync(() => {
+      const handler = () => Queue.offerUnsafe(signals, undefined)
+      process.on("SIGUSR1", handler)
+      return handler
+    }),
+    (handler) => Effect.sync(() => process.off("SIGUSR1", handler)),
+  )
+  yield* Queue.take(signals).pipe(
+    Effect.andThen(
+      Effect.suspend(() => {
+        const file = path.join(
+          global.log,
+          `heap-${process.pid}-${new Date().toISOString().replace(/[:.]/g, "")}.heapsnapshot`,
+        )
+        return Effect.gen(function* () {
+          yield* Effect.logInfo("writing heap snapshot", { path: file })
+          const { writeHeapSnapshot } = yield* Effect.tryPromise(() => import("node:v8"))
+          yield* Effect.try(() => writeHeapSnapshot(file))
+          yield* Effect.logInfo("heap snapshot written", { path: file })
+        }).pipe(Effect.catchCause((cause) => Effect.logError("failed to write heap snapshot", { path: file, cause })))
+      }),
+    ),
+    Effect.forever,
+    Effect.forkScoped({ startImmediately: true }),
+  )
+})
+
+export * as Heap from "./heap"

+ 10 - 6
packages/cli/src/index.ts

@@ -12,6 +12,7 @@ import { Global } from "@opencode-ai/util/global"
 import { AppProcess } from "@opencode-ai/util/process"
 import { Config } from "./config"
 import { Npm } from "@opencode-ai/util/npm"
+import { Heap } from "./heap"
 
 const Handlers = Runtime.handlers(Commands, {
   $: () => import("./commands/handlers/default"),
@@ -54,13 +55,16 @@ const Handlers = Runtime.handlers(Commands, {
   serve: () => import("./commands/handlers/serve"),
 })
 
-Effect.logInfo("cli starting", {
-  version: OPENCODE_VERSION,
-  channel: OPENCODE_CHANNEL,
-  local: OPENCODE_LOCAL,
-  args: process.argv.slice(2),
+Effect.gen(function* () {
+  yield* Heap.listen
+  yield* Effect.logInfo("cli starting", {
+    version: OPENCODE_VERSION,
+    channel: OPENCODE_CHANNEL,
+    local: OPENCODE_LOCAL,
+    args: process.argv.slice(2),
+  })
+  return yield* Runtime.run(Commands, Handlers, { version: OPENCODE_VERSION })
 }).pipe(
-  Effect.flatMap(() => Runtime.run(Commands, Handlers, { version: OPENCODE_VERSION })),
   Effect.annotateLogs({ role: "cli" }),
   Effect.provide(Config.layer),
   Effect.provide(Updater.layer),