pi-webapp 0.1.11 → 0.1.13

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/bin/pi-webapp.mjs CHANGED
@@ -131,26 +131,43 @@ async function serve(pi, args) {
131
131
  let runningUrl = null;
132
132
  let buffer = "";
133
133
  let killTimer;
134
+ let stopReason;
135
+ let forceStopRequested = false;
136
+ const instanceId = randomUUID();
137
+ const startedAt = performance.now();
138
+ const lifecycle = (event, fields = {}) => {
139
+ try {
140
+ console.error(JSON.stringify({ timestamp: new Date().toISOString(), event,
141
+ level: event === "pi_process_exit" && (!stopping || failed) ? "error" : "info",
142
+ instanceId, pid: process.pid, piPid: child.pid, uptimeMs: Math.round(performance.now() - startedAt), ...fields }));
143
+ } catch { /* Diagnostics must not interrupt process supervision. */ }
144
+ };
134
145
  const devWatchers = [];
135
146
  let devReloadTimer;
136
147
  let devReloadCount = 0;
137
148
  let devReloadCommand;
138
- const shutdown = () => {
149
+ const shutdown = (reason) => {
139
150
  if (stopping) return;
140
151
  stopping = true;
152
+ stopReason = reason;
153
+ lifecycle("launcher_stop_requested", { reason });
141
154
  child.stdin?.end();
142
- killTimer = setTimeout(() => child.kill("SIGTERM"), 5000);
155
+ killTimer = setTimeout(() => {
156
+ forceStopRequested = true;
157
+ lifecycle("pi_force_stop", { reason, signal: "SIGTERM" });
158
+ child.kill("SIGTERM");
159
+ }, 5000);
143
160
  killTimer.unref();
144
161
  };
145
162
  const fail = (message) => {
146
163
  if (failed || ready) return;
147
164
  failed = true;
148
165
  notifyParent({ type: "error", message });
149
- shutdown();
166
+ shutdown("startup_failure");
150
167
  };
151
168
  const startupTimer = setTimeout(() => fail(`Pi 启动超时。日志:${logPath}`), 60000);
152
169
  const stopTimer = setInterval(async () => {
153
- try { if ((await readFile(stopPath, "utf8")).trim() === nonce) shutdown(); }
170
+ try { if ((await readFile(stopPath, "utf8")).trim() === nonce) shutdown("stop_request"); }
154
171
  catch { /* No stop request. */ }
155
172
  if (stopping) return;
156
173
  try {
@@ -158,12 +175,12 @@ async function serve(pi, args) {
158
175
  const prepared = JSON.parse(await readFile(updatePath, "utf8"));
159
176
  if (request.nonce === nonce && request.launcher === prepared.launcher && existsSync(request.launcher)) {
160
177
  restartRequest = request;
161
- shutdown();
178
+ shutdown("restart_request");
162
179
  }
163
180
  } catch { /* No valid restart request. */ }
164
181
  }, 400);
165
- process.on("SIGTERM", shutdown);
166
- process.on("SIGINT", shutdown);
182
+ process.on("SIGTERM", () => shutdown("signal_sigterm"));
183
+ process.on("SIGINT", () => shutdown("signal_sigint"));
167
184
  child.stdout?.on("data", (chunk) => {
168
185
  if (failed) return;
169
186
  buffer += chunk.toString("utf8");
@@ -210,6 +227,7 @@ async function serve(pi, args) {
210
227
  if (closed || failed) return;
211
228
  ready = true;
212
229
  clearTimeout(startupTimer);
230
+ lifecycle("launcher_ready", { port: Number(url.port) });
213
231
  notifyParent({ type: "ready", pid: process.pid, url: url.href });
214
232
  for (const root of pluginRoots) {
215
233
  try {
@@ -233,8 +251,11 @@ async function serve(pi, args) {
233
251
  child.stdin?.write(JSON.stringify({ id: "list-commands", type: "get_commands" }) + "\n");
234
252
  await new Promise((resolveExit) => {
235
253
  child.once("error", (error) => { fail(`无法启动 Pi:${error.message}`); resolveExit(); });
236
- child.once("close", (code) => {
254
+ child.once("close", (code, signal) => {
237
255
  closed = true;
256
+ lifecycle("pi_process_exit", { ready, exitCode: code, signal,
257
+ reason: stopReason ?? (ready ? "unexpected_exit" : "startup_failure"), forceStopRequested,
258
+ port: runningUrl ? Number(new URL(runningUrl).port) : null });
238
259
  if (!ready) fail(`Pi 提前退出(代码 ${code ?? "未知"})。日志:${logPath}`);
239
260
  resolveExit();
240
261
  });