@seqyuan/annovibe 0.8.12 → 0.8.14

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.
Files changed (205) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +8 -5
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/react-loadable-manifest.json +6 -6
  6. package/.next/required-server-files.js +1 -1
  7. package/.next/required-server-files.json +1 -1
  8. package/.next/routes-manifest.json +18 -0
  9. package/.next/server/app/_global-error/page.js +3 -3
  10. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  18. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/.next/server/app/_not-found/page.js +2 -2
  20. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/.next/server/app/_not-found.html +1 -1
  22. package/.next/server/app/_not-found.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  24. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  26. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  28. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  30. package/.next/server/app/api/agent/[id]/route.js +1 -1
  31. package/.next/server/app/api/agent/new/route.js +1 -1
  32. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  33. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  34. package/.next/server/app/api/auth/login/[provider]/route.js +2 -2
  35. package/.next/server/app/api/auth/login/route.js +1 -1
  36. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  37. package/.next/server/app/api/auth/providers/route.js +1 -1
  38. package/.next/server/app/api/auth/status/route.js +1 -1
  39. package/.next/server/app/api/default-cwd/route.js +1 -1
  40. package/.next/server/app/api/files/[...path]/route.js +2 -2
  41. package/.next/server/app/api/harness/route.js +1 -1
  42. package/.next/server/app/api/home/route.js +1 -1
  43. package/.next/server/app/api/internal/runtime/route.js +1 -0
  44. package/.next/server/app/api/internal/runtime/route.js.nft.json +1 -0
  45. package/.next/server/app/api/internal/runtime/route_client-reference-manifest.js +1 -0
  46. package/.next/server/app/api/models/route.js +1 -1
  47. package/.next/server/app/api/models-config/discover/route.js +1 -1
  48. package/.next/server/app/api/models-config/route.js +1 -1
  49. package/.next/server/app/api/models-config/test/route.js +1 -1
  50. package/.next/server/app/api/plot-kernels/route.js +1 -1
  51. package/.next/server/app/api/plot-kernels/status/route.js +1 -1
  52. package/.next/server/app/api/plot-kernels/stop/route.js +1 -1
  53. package/.next/server/app/api/projects/browse/route.js +1 -0
  54. package/.next/server/app/api/projects/browse/route.js.nft.json +1 -0
  55. package/.next/server/app/api/projects/browse/route_client-reference-manifest.js +1 -0
  56. package/.next/server/app/api/projects/route.js +3 -0
  57. package/.next/server/app/api/projects/route.js.nft.json +1 -0
  58. package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -0
  59. package/.next/server/app/api/reports/[id]/route.js +7 -4
  60. package/.next/server/app/api/search/route.js +2 -2
  61. package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
  62. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  63. package/.next/server/app/api/sessions/new/route.js +1 -1
  64. package/.next/server/app/api/sessions/route.js +2 -2
  65. package/.next/server/app/api/settings/route.js +1 -1
  66. package/.next/server/app/api/skills/install/route.js +1 -1
  67. package/.next/server/app/api/skills/route.js +2 -2
  68. package/.next/server/app/api/skills/search/route.js +1 -1
  69. package/.next/server/app/api/soul/route.js +1 -1
  70. package/.next/server/app/api/version/route.js +1 -1
  71. package/.next/server/app/favicon.ico/route.js +1 -1
  72. package/.next/server/app/index.html +1 -1
  73. package/.next/server/app/index.rsc +3 -3
  74. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  75. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  76. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  78. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/login/page.js +2 -2
  80. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app/login.html +1 -1
  82. package/.next/server/app/login.rsc +2 -2
  83. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  84. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  85. package/.next/server/app/login.segments/_index.segment.rsc +2 -2
  86. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  87. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  88. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  89. package/.next/server/app/page.js +13 -13
  90. package/.next/server/app/page.js.nft.json +1 -1
  91. package/.next/server/app/page_client-reference-manifest.js +1 -1
  92. package/.next/server/app-paths-manifest.json +8 -5
  93. package/.next/server/chunks/1688.js +45 -0
  94. package/.next/server/chunks/7601.js +54 -12
  95. package/.next/server/chunks/static/media/pdf.worker.min.c476e1a0.mjs +6 -0
  96. package/.next/server/middleware-build-manifest.js +1 -1
  97. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  98. package/.next/server/pages/404.html +1 -1
  99. package/.next/server/pages/500.html +1 -1
  100. package/.next/server/server-reference-manifest.json +1 -1
  101. package/.next/static/QvZYZknhDLA2wQJwpT60o/_buildManifest.js +1 -0
  102. package/.next/static/chunks/51fb665c.9f0b013f33bf4f6d.js +45 -0
  103. package/.next/static/chunks/8771-3e14b6810486df1f.js +1 -0
  104. package/.next/static/chunks/app/_global-error/page-7222ee8be0d45596.js +1 -0
  105. package/.next/static/chunks/app/api/agent/[id]/events/route-7222ee8be0d45596.js +1 -0
  106. package/.next/static/chunks/app/api/agent/[id]/route-7222ee8be0d45596.js +1 -0
  107. package/.next/static/chunks/app/api/agent/new/route-7222ee8be0d45596.js +1 -0
  108. package/.next/static/chunks/app/api/auth/all-providers/route-7222ee8be0d45596.js +1 -0
  109. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-7222ee8be0d45596.js +1 -0
  110. package/.next/static/chunks/app/api/auth/login/[provider]/route-7222ee8be0d45596.js +1 -0
  111. package/.next/static/chunks/app/api/auth/login/route-7222ee8be0d45596.js +1 -0
  112. package/.next/static/chunks/app/api/auth/logout/[provider]/route-7222ee8be0d45596.js +1 -0
  113. package/.next/static/chunks/app/api/auth/providers/route-7222ee8be0d45596.js +1 -0
  114. package/.next/static/chunks/app/api/auth/status/route-7222ee8be0d45596.js +1 -0
  115. package/.next/static/chunks/app/api/default-cwd/route-7222ee8be0d45596.js +1 -0
  116. package/.next/static/chunks/app/api/files/[...path]/route-7222ee8be0d45596.js +1 -0
  117. package/.next/static/chunks/app/api/harness/route-7222ee8be0d45596.js +1 -0
  118. package/.next/static/chunks/app/api/home/route-7222ee8be0d45596.js +1 -0
  119. package/.next/static/chunks/app/api/internal/runtime/route-7222ee8be0d45596.js +1 -0
  120. package/.next/static/chunks/app/api/models/route-7222ee8be0d45596.js +1 -0
  121. package/.next/static/chunks/app/api/models-config/discover/route-7222ee8be0d45596.js +1 -0
  122. package/.next/static/chunks/app/api/models-config/route-7222ee8be0d45596.js +1 -0
  123. package/.next/static/chunks/app/api/models-config/test/route-7222ee8be0d45596.js +1 -0
  124. package/.next/static/chunks/app/api/plot-kernels/route-7222ee8be0d45596.js +1 -0
  125. package/.next/static/chunks/app/api/plot-kernels/status/route-7222ee8be0d45596.js +1 -0
  126. package/.next/static/chunks/app/api/plot-kernels/stop/route-7222ee8be0d45596.js +1 -0
  127. package/.next/static/chunks/app/api/projects/browse/route-7222ee8be0d45596.js +1 -0
  128. package/.next/static/chunks/app/api/projects/route-7222ee8be0d45596.js +1 -0
  129. package/.next/static/chunks/app/api/reports/[id]/route-7222ee8be0d45596.js +1 -0
  130. package/.next/static/chunks/app/api/search/route-7222ee8be0d45596.js +1 -0
  131. package/.next/static/chunks/app/api/sessions/[id]/context/route-7222ee8be0d45596.js +1 -0
  132. package/.next/static/chunks/app/api/sessions/[id]/route-7222ee8be0d45596.js +1 -0
  133. package/.next/static/chunks/app/api/sessions/new/route-7222ee8be0d45596.js +1 -0
  134. package/.next/static/chunks/app/api/sessions/route-7222ee8be0d45596.js +1 -0
  135. package/.next/static/chunks/app/api/settings/route-7222ee8be0d45596.js +1 -0
  136. package/.next/static/chunks/app/api/skills/install/route-7222ee8be0d45596.js +1 -0
  137. package/.next/static/chunks/app/api/skills/route-7222ee8be0d45596.js +1 -0
  138. package/.next/static/chunks/app/api/skills/search/route-7222ee8be0d45596.js +1 -0
  139. package/.next/static/chunks/app/api/soul/route-7222ee8be0d45596.js +1 -0
  140. package/.next/static/chunks/app/api/version/route-7222ee8be0d45596.js +1 -0
  141. package/.next/static/chunks/app/page-e0ad11ee7003214a.js +270 -0
  142. package/.next/static/chunks/next/dist/client/components/builtin/app-error-7222ee8be0d45596.js +1 -0
  143. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-7222ee8be0d45596.js +1 -0
  144. package/.next/static/chunks/next/dist/client/components/builtin/not-found-7222ee8be0d45596.js +1 -0
  145. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-7222ee8be0d45596.js +1 -0
  146. package/.next/static/chunks/{webpack-5e677f60fa366b60.js → webpack-c223a77d0c5e1b9e.js} +1 -1
  147. package/.next/static/css/8026433d69e690e2.css +3 -0
  148. package/.next/static/media/pdf.worker.min.29aaf158.mjs +6 -0
  149. package/.next/trace +75 -74
  150. package/.next/trace-build +1 -1
  151. package/.next/types/app/api/internal/runtime/route.ts +351 -0
  152. package/.next/types/app/api/projects/browse/route.ts +351 -0
  153. package/.next/types/app/api/projects/route.ts +351 -0
  154. package/.next/types/routes.d.ts +4 -1
  155. package/.next/types/validator.ts +27 -0
  156. package/README.md +2 -0
  157. package/bin/pi-web.js +293 -16
  158. package/package.json +1 -1
  159. package/.next/server/chunks/7270.js +0 -45
  160. package/.next/server/chunks/static/media/pdf.worker.min.9df6854a.mjs +0 -6
  161. package/.next/static/chunks/7355-29eaa714eb390050.js +0 -1
  162. package/.next/static/chunks/9b0008ae.2918095672924572.js +0 -45
  163. package/.next/static/chunks/app/_global-error/page-3edd3886076a73cb.js +0 -1
  164. package/.next/static/chunks/app/api/agent/[id]/events/route-3edd3886076a73cb.js +0 -1
  165. package/.next/static/chunks/app/api/agent/[id]/route-3edd3886076a73cb.js +0 -1
  166. package/.next/static/chunks/app/api/agent/new/route-3edd3886076a73cb.js +0 -1
  167. package/.next/static/chunks/app/api/auth/all-providers/route-3edd3886076a73cb.js +0 -1
  168. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-3edd3886076a73cb.js +0 -1
  169. package/.next/static/chunks/app/api/auth/login/[provider]/route-3edd3886076a73cb.js +0 -1
  170. package/.next/static/chunks/app/api/auth/login/route-3edd3886076a73cb.js +0 -1
  171. package/.next/static/chunks/app/api/auth/logout/[provider]/route-3edd3886076a73cb.js +0 -1
  172. package/.next/static/chunks/app/api/auth/providers/route-3edd3886076a73cb.js +0 -1
  173. package/.next/static/chunks/app/api/auth/status/route-3edd3886076a73cb.js +0 -1
  174. package/.next/static/chunks/app/api/default-cwd/route-3edd3886076a73cb.js +0 -1
  175. package/.next/static/chunks/app/api/files/[...path]/route-3edd3886076a73cb.js +0 -1
  176. package/.next/static/chunks/app/api/harness/route-3edd3886076a73cb.js +0 -1
  177. package/.next/static/chunks/app/api/home/route-3edd3886076a73cb.js +0 -1
  178. package/.next/static/chunks/app/api/models/route-3edd3886076a73cb.js +0 -1
  179. package/.next/static/chunks/app/api/models-config/discover/route-3edd3886076a73cb.js +0 -1
  180. package/.next/static/chunks/app/api/models-config/route-3edd3886076a73cb.js +0 -1
  181. package/.next/static/chunks/app/api/models-config/test/route-3edd3886076a73cb.js +0 -1
  182. package/.next/static/chunks/app/api/plot-kernels/route-3edd3886076a73cb.js +0 -1
  183. package/.next/static/chunks/app/api/plot-kernels/status/route-3edd3886076a73cb.js +0 -1
  184. package/.next/static/chunks/app/api/plot-kernels/stop/route-3edd3886076a73cb.js +0 -1
  185. package/.next/static/chunks/app/api/reports/[id]/route-3edd3886076a73cb.js +0 -1
  186. package/.next/static/chunks/app/api/search/route-3edd3886076a73cb.js +0 -1
  187. package/.next/static/chunks/app/api/sessions/[id]/context/route-3edd3886076a73cb.js +0 -1
  188. package/.next/static/chunks/app/api/sessions/[id]/route-3edd3886076a73cb.js +0 -1
  189. package/.next/static/chunks/app/api/sessions/new/route-3edd3886076a73cb.js +0 -1
  190. package/.next/static/chunks/app/api/sessions/route-3edd3886076a73cb.js +0 -1
  191. package/.next/static/chunks/app/api/settings/route-3edd3886076a73cb.js +0 -1
  192. package/.next/static/chunks/app/api/skills/install/route-3edd3886076a73cb.js +0 -1
  193. package/.next/static/chunks/app/api/skills/route-3edd3886076a73cb.js +0 -1
  194. package/.next/static/chunks/app/api/skills/search/route-3edd3886076a73cb.js +0 -1
  195. package/.next/static/chunks/app/api/soul/route-3edd3886076a73cb.js +0 -1
  196. package/.next/static/chunks/app/api/version/route-3edd3886076a73cb.js +0 -1
  197. package/.next/static/chunks/app/page-dc03bbb33a1d438c.js +0 -270
  198. package/.next/static/chunks/next/dist/client/components/builtin/app-error-3edd3886076a73cb.js +0 -1
  199. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-3edd3886076a73cb.js +0 -1
  200. package/.next/static/chunks/next/dist/client/components/builtin/not-found-3edd3886076a73cb.js +0 -1
  201. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-3edd3886076a73cb.js +0 -1
  202. package/.next/static/css/d830cdea1c9a03c6.css +0 -3
  203. package/.next/static/ixA4-CDguO8ne_SD2W8sd/_buildManifest.js +0 -1
  204. package/.next/static/media/pdf.worker.min.5f98222a.mjs +0 -6
  205. /package/.next/static/{ixA4-CDguO8ne_SD2W8sd → QvZYZknhDLA2wQJwpT60o}/_ssgManifest.js +0 -0
package/bin/pi-web.js CHANGED
@@ -12,6 +12,10 @@ const fs = require("fs");
12
12
  // eslint-disable-next-line @typescript-eslint/no-require-imports
13
13
  const os = require("os");
14
14
  // eslint-disable-next-line @typescript-eslint/no-require-imports
15
+ const http = require("http");
16
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
17
+ const net = require("net");
18
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
15
19
  const readline = require("readline");
16
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
17
21
  const { parseArgs } = require("util");
@@ -19,6 +23,8 @@ const { parseArgs } = require("util");
19
23
  const bcrypt = require("bcryptjs");
20
24
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
25
  const https = require("https");
26
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
27
+ const { randomBytes } = require("crypto");
22
28
 
23
29
  const pkgDir = path.join(__dirname, "..");
24
30
  const nextDir = path.join(pkgDir, ".next");
@@ -35,6 +41,12 @@ const STOP_TERM_TIMEOUT_MS = 6000;
35
41
  const STOP_KILL_TIMEOUT_MS = 2000;
36
42
  const PROCESS_TITLE = "annovibe";
37
43
  const NEXT_PROCESS_TITLE = "annovibe-next";
44
+ const AUTO_RESTART_ENABLED = !/^(0|false|no)$/i.test(process.env.ANNOVIBE_AUTO_RESTART ?? "1");
45
+ const AUTO_RESTART_POLL_MS = parsePositiveInt(process.env.ANNOVIBE_AUTO_RESTART_POLL_MS, 60_000);
46
+ const AUTO_RESTART_IDLE_GRACE_MS = parsePositiveInt(process.env.ANNOVIBE_AUTO_RESTART_IDLE_GRACE_MS, 10_000);
47
+ const AUTO_RESTART_MAX_WAIT_MS = parsePositiveInt(process.env.ANNOVIBE_AUTO_RESTART_MAX_WAIT_MS, 30 * 60_000);
48
+ const AUTO_RESTART_WAIT_FOR_PORT_MS = parsePositiveInt(process.env.ANNOVIBE_AUTO_RESTART_WAIT_FOR_PORT_MS, 30_000);
49
+ const INTERNAL_RUNTIME_TIMEOUT_MS = 3000;
38
50
 
39
51
  // Resolve next's CLI entry
40
52
  let nextBin;
@@ -79,10 +91,132 @@ function getUrl(host, listenPort) {
79
91
  return `http://${host ?? "localhost"}:${listenPort}`;
80
92
  }
81
93
 
94
+ function getServerArgs() {
95
+ return [getCommandPath(), "__server", "--port", String(port), ...(hostname ? ["--hostname", hostname] : [])];
96
+ }
97
+
98
+ function getServerCommand() {
99
+ return `${getCommandPath()} __server --port ${port}${hostname ? ` --hostname ${hostname}` : ""}`;
100
+ }
101
+
82
102
  function nowIso() {
83
103
  return new Date().toISOString();
84
104
  }
85
105
 
106
+ function parsePositiveInt(value, fallback) {
107
+ const n = Number(value);
108
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
109
+ }
110
+
111
+ function sleep(ms) {
112
+ return new Promise((resolve) => setTimeout(resolve, ms));
113
+ }
114
+
115
+ function readPackageVersion(packageDir) {
116
+ try {
117
+ const installedPkg = JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf8"));
118
+ return typeof installedPkg.version === "string" ? installedPkg.version : null;
119
+ } catch {
120
+ return null;
121
+ }
122
+ }
123
+
124
+ function getInstalledPackageDir() {
125
+ try {
126
+ return path.resolve(path.dirname(fs.realpathSync(getCommandPath())), "..");
127
+ } catch {
128
+ return pkgDir;
129
+ }
130
+ }
131
+
132
+ function readInstalledVersion() {
133
+ const dirs = [getInstalledPackageDir(), pkgDir];
134
+ for (const dir of dirs) {
135
+ const version = readPackageVersion(dir);
136
+ if (version) return version;
137
+ }
138
+ return null;
139
+ }
140
+
141
+ function getRuntimeConnectHost(host) {
142
+ if (!host || host === "0.0.0.0") return "127.0.0.1";
143
+ if (host === "::" || host === "[::]") return "::1";
144
+ return String(host).replace(/^\[(.*)\]$/, "$1");
145
+ }
146
+
147
+ function canBindPort(listenPort, host) {
148
+ return new Promise((resolve) => {
149
+ const server = net.createServer();
150
+ let settled = false;
151
+ const done = (available) => {
152
+ if (settled) return;
153
+ settled = true;
154
+ resolve(available);
155
+ };
156
+ server.unref();
157
+ server.once("error", () => done(false));
158
+ server.once("listening", () => {
159
+ server.close(() => done(true));
160
+ });
161
+ server.listen(Number(listenPort), host ? String(host) : undefined);
162
+ });
163
+ }
164
+
165
+ async function waitForPortAvailable(listenPort, host, timeoutMs) {
166
+ const startedAt = Date.now();
167
+ while (Date.now() - startedAt < timeoutMs) {
168
+ if (await canBindPort(listenPort, host)) return true;
169
+ await sleep(250);
170
+ }
171
+ return canBindPort(listenPort, host);
172
+ }
173
+
174
+ function fetchRuntimeStatus(internalToken) {
175
+ return new Promise((resolve) => {
176
+ let settled = false;
177
+ const finish = (status) => {
178
+ if (settled) return;
179
+ settled = true;
180
+ resolve(status);
181
+ };
182
+ const req = http.request({
183
+ hostname: getRuntimeConnectHost(hostname),
184
+ port: Number(port),
185
+ path: "/api/internal/runtime",
186
+ method: "GET",
187
+ timeout: INTERNAL_RUNTIME_TIMEOUT_MS,
188
+ headers: {
189
+ "x-annovibe-internal-token": internalToken,
190
+ },
191
+ }, (res) => {
192
+ if (res.statusCode !== 200) {
193
+ res.resume();
194
+ finish(null);
195
+ return;
196
+ }
197
+ let data = "";
198
+ res.setEncoding("utf8");
199
+ res.on("data", (chunk) => {
200
+ data += chunk;
201
+ if (data.length > 1024 * 1024) req.destroy();
202
+ });
203
+ res.on("end", () => {
204
+ try {
205
+ finish(JSON.parse(data));
206
+ } catch {
207
+ finish(null);
208
+ }
209
+ });
210
+ });
211
+ req.on("error", () => finish(null));
212
+ req.on("timeout", () => {
213
+ req.destroy();
214
+ finish(null);
215
+ });
216
+ req.end();
217
+ });
218
+ }
219
+
86
220
  function setStableProcessTitle(title) {
87
221
  process.title = title;
88
222
  try {
@@ -338,6 +472,10 @@ function renderState(state, json = false) {
338
472
  supervisorPid: supervisorAlive ? state.supervisorPid : null,
339
473
  nextPid: nextAlive ? state.nextPid : null,
340
474
  startedAt: supervisorAlive ? state.startedAt ?? null : null,
475
+ restartPending: !!state?.restartPending,
476
+ installedVersion: state?.installedVersion ?? null,
477
+ restartDetectedAt: state?.restartDetectedAt ?? null,
478
+ restartStartedAt: state?.restartStartedAt ?? null,
341
479
  logFile,
342
480
  stateFile,
343
481
  };
@@ -357,6 +495,9 @@ function renderState(state, json = false) {
357
495
  `supervisor pid: ${payload.supervisorPid ?? "not running"}`,
358
496
  `next pid: ${payload.nextPid ?? "unknown"}`,
359
497
  `started: ${payload.startedAt ?? "unknown"}`,
498
+ ...(payload.restartPending ? [
499
+ `restart: pending${payload.installedVersion ? ` for v${payload.installedVersion}` : ""}`,
500
+ ] : []),
360
501
  `log: ${payload.logFile}`,
361
502
  ].join("\n");
362
503
  }
@@ -433,17 +574,7 @@ async function startManagedServer({ quiet = false } = {}) {
433
574
  removeState();
434
575
  }
435
576
  ensureAgentDir();
436
- const fd = fs.openSync(logFile, "a");
437
- const child = spawn(process.execPath, [getCommandPath(), "__server", "--port", String(port), ...(hostname ? ["--hostname", hostname] : [])], {
438
- cwd: pkgDir,
439
- detached: true,
440
- stdio: ["ignore", fd, fd],
441
- env: {
442
- ...process.env,
443
- ANNOVIBE_MANAGED: "1",
444
- },
445
- });
446
- fs.closeSync(fd);
577
+ const child = spawnDetachedServer();
447
578
  if (!child.pid) {
448
579
  throw new Error("failed to start annovibe server");
449
580
  }
@@ -451,7 +582,7 @@ async function startManagedServer({ quiet = false } = {}) {
451
582
  const state = {
452
583
  schemaVersion: 1,
453
584
  version: VERSION,
454
- command: `${getCommandPath()} __server --port ${port}${hostname ? ` --hostname ${hostname}` : ""}`,
585
+ command: getServerCommand(),
455
586
  supervisorPid: child.pid,
456
587
  nextPid: null,
457
588
  port: String(port),
@@ -470,6 +601,23 @@ async function startManagedServer({ quiet = false } = {}) {
470
601
  return 0;
471
602
  }
472
603
 
604
+ function spawnDetachedServer(extraEnv = {}) {
605
+ ensureAgentDir();
606
+ const fd = fs.openSync(logFile, "a");
607
+ const child = spawn(process.execPath, getServerArgs(), {
608
+ cwd: fs.existsSync(getInstalledPackageDir()) ? getInstalledPackageDir() : pkgDir,
609
+ detached: true,
610
+ stdio: ["ignore", fd, fd],
611
+ env: {
612
+ ...process.env,
613
+ ANNOVIBE_MANAGED: "1",
614
+ ...extraEnv,
615
+ },
616
+ });
617
+ fs.closeSync(fd);
618
+ return child;
619
+ }
620
+
473
621
  if (cliArgs.version) {
474
622
  console.log(VERSION);
475
623
  process.exit(0);
@@ -500,6 +648,7 @@ Options:
500
648
  Environment:
501
649
  PORT Server port
502
650
  ANNOVIBE_PASSWORD Set password on first start
651
+ ANNOVIBE_AUTO_RESTART Set 0/false/no to disable idle auto-restart after update
503
652
  PI_CODING_AGENT_DIR Custom pi agent data directory
504
653
  `);
505
654
  process.exit(0);
@@ -718,6 +867,115 @@ function openBrowser(url) {
718
867
  openChild.unref();
719
868
  }
720
869
 
870
+ function startAutoRestartWatcher({ child, internalToken, writeCurrentState, markRestarting }) {
871
+ if (!AUTO_RESTART_ENABLED) return () => {};
872
+
873
+ let pending = null;
874
+ let idleSince = null;
875
+ let restarting = false;
876
+ let lastWaitLogAt = 0;
877
+
878
+ const poll = async () => {
879
+ if (restarting) return;
880
+ const installedVersion = readInstalledVersion();
881
+ if (!installedVersion || installedVersion === VERSION) {
882
+ if (pending) {
883
+ pending = null;
884
+ idleSince = null;
885
+ writeCurrentState({ restartPending: false, installedVersion: null, restartDetectedAt: null, restartStartedAt: null });
886
+ }
887
+ return;
888
+ }
889
+
890
+ if (!pending || pending.installedVersion !== installedVersion) {
891
+ pending = {
892
+ installedVersion,
893
+ detectedAt: nowIso(),
894
+ detectedAtMs: Date.now(),
895
+ };
896
+ idleSince = null;
897
+ lastWaitLogAt = 0;
898
+ console.log(`annovibe auto-restart: detected installed version ${installedVersion}; current runtime is ${VERSION}`);
899
+ writeCurrentState({
900
+ restartPending: true,
901
+ installedVersion,
902
+ restartDetectedAt: pending.detectedAt,
903
+ });
904
+ }
905
+
906
+ const status = await fetchRuntimeStatus(internalToken);
907
+ if (!status || typeof status.busy !== "boolean") {
908
+ idleSince = null;
909
+ if (Date.now() - lastWaitLogAt >= 5 * 60_000) {
910
+ lastWaitLogAt = Date.now();
911
+ console.log("annovibe auto-restart: waiting for runtime status before restarting");
912
+ }
913
+ return;
914
+ }
915
+
916
+ if (status.busy) {
917
+ idleSince = null;
918
+ const waitedMs = Date.now() - pending.detectedAtMs;
919
+ if (Date.now() - lastWaitLogAt >= 5 * 60_000) {
920
+ lastWaitLogAt = Date.now();
921
+ const busyCount = Array.isArray(status.busySessions) ? status.busySessions.length : 0;
922
+ const suffix = waitedMs >= AUTO_RESTART_MAX_WAIT_MS ? "; max wait reached, still waiting for graceful idle" : "";
923
+ console.log(`annovibe auto-restart: update pending, waiting for ${busyCount} busy session${busyCount === 1 ? "" : "s"}${suffix}`);
924
+ }
925
+ return;
926
+ }
927
+
928
+ idleSince ??= Date.now();
929
+ if (Date.now() - idleSince < AUTO_RESTART_IDLE_GRACE_MS) return;
930
+
931
+ restarting = true;
932
+ let replacement;
933
+ try {
934
+ replacement = spawnDetachedServer({
935
+ ANNOVIBE_NO_OPEN: "1",
936
+ ANNOVIBE_WAIT_FOR_PORT: "1",
937
+ });
938
+ } catch (error) {
939
+ console.error(`annovibe auto-restart: failed to start replacement supervisor: ${error instanceof Error ? error.message : String(error)}`);
940
+ restarting = false;
941
+ return;
942
+ }
943
+ if (!replacement.pid) {
944
+ console.error("annovibe auto-restart: failed to start replacement supervisor");
945
+ restarting = false;
946
+ return;
947
+ }
948
+ markRestarting();
949
+ writeCurrentState({
950
+ restartPending: true,
951
+ installedVersion: pending.installedVersion,
952
+ restartDetectedAt: pending.detectedAt,
953
+ restartStartedAt: nowIso(),
954
+ });
955
+ console.log(`annovibe auto-restart: installed ${VERSION} -> ${pending.installedVersion}; restarting now`);
956
+ replacement.unref();
957
+ console.log(`annovibe auto-restart: replacement supervisor pid ${replacement.pid}`);
958
+
959
+ if (!(await terminatePid(child.pid))) {
960
+ console.error(`annovibe auto-restart: failed to stop old next pid ${child.pid}`);
961
+ restarting = false;
962
+ markRestarting(false);
963
+ return;
964
+ }
965
+ process.exit(0);
966
+ };
967
+
968
+ const timer = setInterval(() => {
969
+ poll().catch((error) => {
970
+ console.error(`annovibe auto-restart: ${error instanceof Error ? error.message : String(error)}`);
971
+ });
972
+ }, AUTO_RESTART_POLL_MS);
973
+ timer.unref();
974
+
975
+ poll().catch(() => {});
976
+ return () => clearInterval(timer);
977
+ }
978
+
721
979
  async function runServerProcess() {
722
980
  setStableProcessTitle(PROCESS_TITLE);
723
981
  await maybeSetupAuth();
@@ -728,26 +986,37 @@ async function runServerProcess() {
728
986
 
729
987
  checkVersion();
730
988
 
989
+ if (process.env.ANNOVIBE_WAIT_FOR_PORT === "1") {
990
+ const available = await waitForPortAvailable(port, hostname, AUTO_RESTART_WAIT_FOR_PORT_MS);
991
+ if (!available) {
992
+ console.error(`annovibe server: port ${port} was not released within ${AUTO_RESTART_WAIT_FOR_PORT_MS}ms`);
993
+ process.exit(1);
994
+ }
995
+ }
996
+
731
997
  const url = getUrl(hostname, port);
732
998
  const nextArgs = [getCommandPath(), "__next", "--port", String(port)];
733
999
  if (hostname) nextArgs.push("--hostname", hostname);
1000
+ const internalToken = process.env.ANNOVIBE_INTERNAL_TOKEN || randomBytes(24).toString("hex");
734
1001
 
735
1002
  const child = spawn(process.execPath, nextArgs, {
736
1003
  cwd: pkgDir,
737
1004
  stdio: ["ignore", "pipe", "pipe"],
738
1005
  env: {
739
1006
  ...process.env,
1007
+ ANNOVIBE_INTERNAL_TOKEN: internalToken,
740
1008
  ANNOVIBE_SUPERVISOR_PID: String(process.pid),
741
1009
  ANNOVIBE_NEXT_WRAPPER: "1",
742
1010
  },
743
1011
  });
1012
+ let restarting = false;
744
1013
 
745
1014
  const writeCurrentState = (patch = {}) => {
746
1015
  const current = readState() ?? {};
747
1016
  writeState({
748
1017
  schemaVersion: 1,
749
1018
  version: VERSION,
750
- command: `${getCommandPath()} __server --port ${port}${hostname ? ` --hostname ${hostname}` : ""}`,
1019
+ command: getServerCommand(),
751
1020
  supervisorPid: process.pid,
752
1021
  nextPid: child.pid ?? null,
753
1022
  port: String(port),
@@ -761,6 +1030,12 @@ async function runServerProcess() {
761
1030
  };
762
1031
 
763
1032
  writeCurrentState();
1033
+ const stopAutoRestartWatcher = startAutoRestartWatcher({
1034
+ child,
1035
+ internalToken,
1036
+ writeCurrentState,
1037
+ markRestarting: (value = true) => { restarting = value; },
1038
+ });
764
1039
 
765
1040
  let browserOpened = false;
766
1041
  child.stdout.on("data", (chunk) => {
@@ -768,7 +1043,7 @@ async function runServerProcess() {
768
1043
  process.stdout.write(text);
769
1044
  if (!browserOpened && text.includes("Ready")) {
770
1045
  browserOpened = true;
771
- openBrowser(url);
1046
+ if (process.env.ANNOVIBE_NO_OPEN !== "1") openBrowser(url);
772
1047
  console.log(`\n annovibe running at ${url}\n`);
773
1048
  writeCurrentState({ readyAt: nowIso() });
774
1049
  }
@@ -776,15 +1051,17 @@ async function runServerProcess() {
776
1051
  child.stderr.on("data", (chunk) => process.stderr.write(chunk));
777
1052
 
778
1053
  const shutdown = async () => {
1054
+ stopAutoRestartWatcher();
779
1055
  await terminatePid(child.pid);
780
- removeState();
1056
+ if (!restarting) removeState();
781
1057
  process.exit(0);
782
1058
  };
783
1059
  process.on("SIGTERM", () => { void shutdown(); });
784
1060
  process.on("SIGINT", () => { void shutdown(); });
785
1061
 
786
1062
  child.on("exit", (code) => {
787
- removeState();
1063
+ stopAutoRestartWatcher();
1064
+ if (!restarting) removeState();
788
1065
  process.exit(code ?? 0);
789
1066
  });
790
1067
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seqyuan/annovibe",
3
- "version": "0.8.12",
3
+ "version": "0.8.14",
4
4
  "description": "AI-native bioinformatics workspace by Annoroad",
5
5
  "license": "MIT",
6
6
  "bin": {