omniroute 3.2.6 → 3.2.7

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 (202) hide show
  1. package/README.ar.md +15 -13
  2. package/README.bg.md +3 -0
  3. package/README.cs.md +3 -0
  4. package/README.da.md +3 -0
  5. package/README.fi.md +3 -0
  6. package/README.he.md +40 -37
  7. package/README.hu.md +12 -9
  8. package/README.id.md +3 -0
  9. package/README.in.md +3 -0
  10. package/README.ja.md +3 -0
  11. package/README.ko.md +40 -37
  12. package/README.md +25 -22
  13. package/README.ms.md +18 -15
  14. package/README.nl.md +3 -0
  15. package/README.no.md +3 -0
  16. package/README.phi.md +3 -0
  17. package/README.pl.md +3 -0
  18. package/README.pt.md +3 -0
  19. package/README.ro.md +3 -0
  20. package/README.sk.md +3 -0
  21. package/README.sv.md +3 -0
  22. package/README.th.md +12 -9
  23. package/README.uk-UA.md +3 -0
  24. package/README.vi.md +3 -0
  25. package/app/.next/BUILD_ID +1 -1
  26. package/app/.next/build-manifest.json +2 -2
  27. package/app/.next/prerender-manifest.json +3 -3
  28. package/app/.next/server/app/(dashboard)/dashboard/a2a/page_client-reference-manifest.js +1 -1
  29. package/app/.next/server/app/(dashboard)/dashboard/agents/page_client-reference-manifest.js +1 -1
  30. package/app/.next/server/app/(dashboard)/dashboard/analytics/page_client-reference-manifest.js +1 -1
  31. package/app/.next/server/app/(dashboard)/dashboard/api-manager/page_client-reference-manifest.js +1 -1
  32. package/app/.next/server/app/(dashboard)/dashboard/audit-log/page_client-reference-manifest.js +1 -1
  33. package/app/.next/server/app/(dashboard)/dashboard/auto-combo/page_client-reference-manifest.js +1 -1
  34. package/app/.next/server/app/(dashboard)/dashboard/cache/page_client-reference-manifest.js +1 -1
  35. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  36. package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  37. package/app/.next/server/app/(dashboard)/dashboard/costs/page_client-reference-manifest.js +1 -1
  38. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  39. package/app/.next/server/app/(dashboard)/dashboard/health/page_client-reference-manifest.js +1 -1
  40. package/app/.next/server/app/(dashboard)/dashboard/limits/page_client-reference-manifest.js +1 -1
  41. package/app/.next/server/app/(dashboard)/dashboard/logs/page_client-reference-manifest.js +1 -1
  42. package/app/.next/server/app/(dashboard)/dashboard/mcp/page_client-reference-manifest.js +1 -1
  43. package/app/.next/server/app/(dashboard)/dashboard/media/page_client-reference-manifest.js +1 -1
  44. package/app/.next/server/app/(dashboard)/dashboard/onboarding/page_client-reference-manifest.js +1 -1
  45. package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  46. package/app/.next/server/app/(dashboard)/dashboard/playground/page_client-reference-manifest.js +1 -1
  47. package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  48. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  49. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  50. package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  51. package/app/.next/server/app/(dashboard)/dashboard/search-tools/page_client-reference-manifest.js +1 -1
  52. package/app/.next/server/app/(dashboard)/dashboard/settings/page_client-reference-manifest.js +1 -1
  53. package/app/.next/server/app/(dashboard)/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  54. package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  55. package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  56. package/app/.next/server/app/400/page_client-reference-manifest.js +1 -1
  57. package/app/.next/server/app/401/page_client-reference-manifest.js +1 -1
  58. package/app/.next/server/app/403/page_client-reference-manifest.js +1 -1
  59. package/app/.next/server/app/408/page_client-reference-manifest.js +1 -1
  60. package/app/.next/server/app/429/page_client-reference-manifest.js +1 -1
  61. package/app/.next/server/app/500/page_client-reference-manifest.js +1 -1
  62. package/app/.next/server/app/502/page_client-reference-manifest.js +1 -1
  63. package/app/.next/server/app/503/page_client-reference-manifest.js +1 -1
  64. package/app/.next/server/app/_global-error.html +2 -2
  65. package/app/.next/server/app/_global-error.rsc +1 -1
  66. package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  67. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  68. package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  69. package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  70. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  71. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  72. package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
  73. package/app/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  74. package/app/.next/server/app/forbidden/page_client-reference-manifest.js +1 -1
  75. package/app/.next/server/app/forgot-password/page_client-reference-manifest.js +1 -1
  76. package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
  77. package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
  78. package/app/.next/server/app/maintenance/page_client-reference-manifest.js +1 -1
  79. package/app/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  80. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  81. package/app/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
  82. package/app/.next/server/app/status/page_client-reference-manifest.js +1 -1
  83. package/app/.next/server/app/terms/page_client-reference-manifest.js +1 -1
  84. package/app/.next/server/chunks/[root-of-the-server]__0891af92._.js +3 -1
  85. package/app/.next/server/chunks/[root-of-the-server]__1f27646b._.js +1 -1
  86. package/app/.next/server/chunks/[root-of-the-server]__46e00e59._.js +1 -1
  87. package/app/.next/server/chunks/[root-of-the-server]__6e52619e._.js +1 -1
  88. package/app/.next/server/chunks/[root-of-the-server]__950558b8._.js +1 -1
  89. package/app/.next/server/chunks/[root-of-the-server]__a32d3818._.js +1 -1
  90. package/app/.next/server/chunks/[root-of-the-server]__d5a064d5._.js +2 -2
  91. package/app/.next/server/chunks/_05c48915._.js +1 -1
  92. package/app/.next/server/chunks/_06515a8a._.js +1 -1
  93. package/app/.next/server/chunks/_2115d8de._.js +1 -1
  94. package/app/.next/server/chunks/_3ac953eb._.js +1 -1
  95. package/app/.next/server/chunks/_4b8fd853._.js +1 -1
  96. package/app/.next/server/chunks/_68683848._.js +1 -1
  97. package/app/.next/server/chunks/_ee9b677b._.js +1 -1
  98. package/app/.next/server/chunks/ssr/[root-of-the-server]__9ef96d20._.js +1 -1
  99. package/app/.next/server/chunks/ssr/[root-of-the-server]__a6942102._.js +1 -1
  100. package/app/.next/server/chunks/ssr/src_app_(dashboard)_dashboard_3666f2bd._.js +1 -1
  101. package/app/.next/server/pages/500.html +2 -2
  102. package/app/.next/server/server-reference-manifest.js +1 -1
  103. package/app/.next/server/server-reference-manifest.json +1 -1
  104. package/app/.next/static/chunks/8a7e69530366d349.js +1 -0
  105. package/app/.next/static/chunks/ced99e799397dd8f.css +1 -0
  106. package/app/.next/static/chunks/{a505e70878b9cc4b.js → e0bc7c9ae396f60a.js} +1 -1
  107. package/app/CHANGELOG.md +6 -0
  108. package/app/README.ar.md +15 -13
  109. package/app/README.bg.md +3 -0
  110. package/app/README.cs.md +3 -0
  111. package/app/README.da.md +3 -0
  112. package/app/README.fi.md +3 -0
  113. package/app/README.he.md +40 -37
  114. package/app/README.hu.md +12 -9
  115. package/app/README.id.md +3 -0
  116. package/app/README.in.md +3 -0
  117. package/app/README.ja.md +3 -0
  118. package/app/README.ko.md +40 -37
  119. package/app/README.md +25 -22
  120. package/app/README.ms.md +18 -15
  121. package/app/README.nl.md +3 -0
  122. package/app/README.no.md +3 -0
  123. package/app/README.phi.md +3 -0
  124. package/app/README.pl.md +3 -0
  125. package/app/README.pt.md +3 -0
  126. package/app/README.ro.md +3 -0
  127. package/app/README.sk.md +3 -0
  128. package/app/README.sv.md +3 -0
  129. package/app/README.th.md +12 -9
  130. package/app/README.uk-UA.md +3 -0
  131. package/app/README.vi.md +3 -0
  132. package/app/docs/FEATURES.md +1 -1
  133. package/app/docs/i18n/ar/FEATURES.md +1 -1
  134. package/app/docs/i18n/ar/README.md +23 -20
  135. package/app/docs/i18n/bg/FEATURES.md +1 -1
  136. package/app/docs/i18n/bg/README.md +13 -10
  137. package/app/docs/i18n/cs/README.md +13 -10
  138. package/app/docs/i18n/da/FEATURES.md +1 -1
  139. package/app/docs/i18n/da/README.md +13 -10
  140. package/app/docs/i18n/de/FEATURES.md +1 -1
  141. package/app/docs/i18n/de/README.md +13 -10
  142. package/app/docs/i18n/es/FEATURES.md +1 -1
  143. package/app/docs/i18n/es/README.md +32 -34
  144. package/app/docs/i18n/fi/FEATURES.md +1 -1
  145. package/app/docs/i18n/fi/README.md +13 -10
  146. package/app/docs/i18n/fr/FEATURES.md +1 -1
  147. package/app/docs/i18n/fr/README.md +21 -23
  148. package/app/docs/i18n/he/FEATURES.md +1 -1
  149. package/app/docs/i18n/he/README.md +26 -23
  150. package/app/docs/i18n/hu/FEATURES.md +1 -1
  151. package/app/docs/i18n/hu/README.md +13 -10
  152. package/app/docs/i18n/id/FEATURES.md +1 -1
  153. package/app/docs/i18n/id/README.md +26 -23
  154. package/app/docs/i18n/in/FEATURES.md +1 -1
  155. package/app/docs/i18n/in/README.md +26 -23
  156. package/app/docs/i18n/it/FEATURES.md +1 -1
  157. package/app/docs/i18n/it/README.md +32 -34
  158. package/app/docs/i18n/ja/FEATURES.md +1 -1
  159. package/app/docs/i18n/ja/README.md +26 -23
  160. package/app/docs/i18n/ko/FEATURES.md +1 -1
  161. package/app/docs/i18n/ko/README.md +26 -23
  162. package/app/docs/i18n/ms/FEATURES.md +1 -1
  163. package/app/docs/i18n/ms/README.md +13 -10
  164. package/app/docs/i18n/nl/FEATURES.md +1 -1
  165. package/app/docs/i18n/nl/README.md +13 -10
  166. package/app/docs/i18n/no/FEATURES.md +1 -1
  167. package/app/docs/i18n/no/README.md +26 -23
  168. package/app/docs/i18n/phi/FEATURES.md +1 -1
  169. package/app/docs/i18n/phi/README.md +26 -23
  170. package/app/docs/i18n/pl/FEATURES.md +1 -1
  171. package/app/docs/i18n/pl/README.md +13 -10
  172. package/app/docs/i18n/pt/FEATURES.md +1 -1
  173. package/app/docs/i18n/pt/README.md +13 -10
  174. package/app/docs/i18n/pt-BR/FEATURES.md +1 -1
  175. package/app/docs/i18n/pt-BR/README.md +13 -10
  176. package/app/docs/i18n/ro/FEATURES.md +1 -1
  177. package/app/docs/i18n/ro/README.md +13 -10
  178. package/app/docs/i18n/ru/FEATURES.md +1 -1
  179. package/app/docs/i18n/ru/README.md +32 -34
  180. package/app/docs/i18n/sk/FEATURES.md +1 -1
  181. package/app/docs/i18n/sk/README.md +13 -10
  182. package/app/docs/i18n/sv/FEATURES.md +1 -1
  183. package/app/docs/i18n/sv/README.md +26 -23
  184. package/app/docs/i18n/th/FEATURES.md +1 -1
  185. package/app/docs/i18n/th/README.md +26 -23
  186. package/app/docs/i18n/uk-UA/FEATURES.md +1 -1
  187. package/app/docs/i18n/uk-UA/README.md +13 -10
  188. package/app/docs/i18n/vi/FEATURES.md +1 -1
  189. package/app/docs/i18n/vi/README.md +13 -10
  190. package/app/docs/i18n/zh-CN/FEATURES.md +1 -1
  191. package/app/docs/i18n/zh-CN/README.md +32 -34
  192. package/app/docs/openapi.yaml +1 -1
  193. package/app/package-lock.json +2 -2
  194. package/app/package.json +1 -1
  195. package/app/src/app/(dashboard)/dashboard/HomePageClient.tsx +217 -13
  196. package/app/src/app/api/system/version/route.ts +69 -22
  197. package/package.json +1 -1
  198. package/app/.next/static/chunks/2e25e28a804c74cb.css +0 -1
  199. package/app/.next/static/chunks/786bb016b73be366.js +0 -1
  200. /package/app/.next/static/{y0yR5YnNvU8dlWh7MSaaO → V1AhcZllvKyXSF7QBcufd}/_buildManifest.js +0 -0
  201. /package/app/.next/static/{y0yR5YnNvU8dlWh7MSaaO → V1AhcZllvKyXSF7QBcufd}/_clientMiddlewareManifest.json +0 -0
  202. /package/app/.next/static/{y0yR5YnNvU8dlWh7MSaaO → V1AhcZllvKyXSF7QBcufd}/_ssgManifest.js +0 -0
@@ -26,6 +26,10 @@ export default function HomePageClient({ machineId }) {
26
26
 
27
27
  const [versionInfo, setVersionInfo] = useState<any>(null);
28
28
  const [updating, setUpdating] = useState(false);
29
+ const [updateSteps, setUpdateSteps] = useState<
30
+ Array<{ step: string; status: string; message: string }>
31
+ >([]);
32
+ const [updatePhase, setUpdatePhase] = useState<"idle" | "running" | "done" | "failed">("idle");
29
33
 
30
34
  useEffect(() => {
31
35
  if (typeof window !== "undefined") {
@@ -89,7 +93,6 @@ export default function HomePageClient({ machineId }) {
89
93
  const providerKeys = new Set([providerId, providerInfo.alias].filter(Boolean));
90
94
  const providerModels = models.filter((m) => providerKeys.has(m.provider));
91
95
 
92
- // Determine auth type
93
96
  const authType = FREE_PROVIDERS[providerId]
94
97
  ? "free"
95
98
  : OAUTH_PROVIDERS[providerId]
@@ -108,7 +111,6 @@ export default function HomePageClient({ machineId }) {
108
111
  });
109
112
  }, [providerConnections, models]);
110
113
 
111
- // Models for selected provider
112
114
  const selectedProviderModels = useMemo(() => {
113
115
  if (!selectedProvider) return [];
114
116
  const providerKeys = new Set(
@@ -135,24 +137,112 @@ export default function HomePageClient({ machineId }) {
135
137
  const handleUpdate = async () => {
136
138
  const notify = useNotificationStore.getState();
137
139
  setUpdating(true);
140
+ setUpdatePhase("running");
141
+ setUpdateSteps([]);
142
+
138
143
  try {
139
- notify.info(t("updateStarted") || "Update process started...");
140
144
  const res = await fetch("/api/system/version", { method: "POST" });
141
- const data = await res.json();
142
- if (res.ok && data.success) {
143
- notify.success(
144
- data.message || "Update initiated successfully. The system will restart shortly."
145
- );
146
- } else {
147
- notify.error(data.error || "Failed to start update.");
145
+
146
+ // If response is JSON (error/already up to date)
147
+ const contentType = res.headers.get("content-type") || "";
148
+ if (contentType.includes("application/json")) {
149
+ const data = await res.json();
150
+ if (!res.ok || !data.success) {
151
+ notify.error(data.error || "Failed to start update.");
152
+ setUpdating(false);
153
+ setUpdatePhase("idle");
154
+ return;
155
+ }
156
+ }
157
+
158
+ // SSE stream — read progress events
159
+ if (!res.body) {
160
+ notify.error("No response stream received.");
148
161
  setUpdating(false);
162
+ setUpdatePhase("idle");
163
+ return;
164
+ }
165
+
166
+ const reader = res.body.getReader();
167
+ const decoder = new TextDecoder();
168
+ let buffer = "";
169
+
170
+ while (true) {
171
+ const { done, value } = await reader.read();
172
+ if (done) break;
173
+ buffer += decoder.decode(value, { stream: true });
174
+
175
+ const lines = buffer.split("\n");
176
+ buffer = lines.pop() || "";
177
+
178
+ for (const line of lines) {
179
+ if (!line.startsWith("data: ")) continue;
180
+ try {
181
+ const event = JSON.parse(line.slice(6));
182
+
183
+ setUpdateSteps((prev) => {
184
+ // Replace existing step entry or add new one
185
+ const idx = prev.findIndex((s) => s.step === event.step);
186
+ if (idx >= 0) {
187
+ const next = [...prev];
188
+ next[idx] = event;
189
+ return next;
190
+ }
191
+ return [...prev, event];
192
+ });
193
+
194
+ if (event.step === "complete") {
195
+ setUpdatePhase("done");
196
+ notify.success(event.message || "Update complete!");
197
+ } else if (event.step === "error") {
198
+ setUpdatePhase("failed");
199
+ notify.error(event.message || "Update failed.");
200
+ setUpdating(false);
201
+ }
202
+ } catch {
203
+ // ignore parse errors
204
+ }
205
+ }
149
206
  }
150
207
  } catch {
151
- notify.error("Network error while trying to update.");
208
+ setUpdatePhase("failed");
209
+ setUpdateSteps((prev) => [
210
+ ...prev,
211
+ {
212
+ step: "error",
213
+ status: "failed",
214
+ message: "Network error — connection lost during update.",
215
+ },
216
+ ]);
152
217
  setUpdating(false);
153
218
  }
154
219
  };
155
220
 
221
+ // Auto-reload after successful update (service restarts, need new page)
222
+ useEffect(() => {
223
+ if (updatePhase !== "done") return;
224
+ const timer = setTimeout(() => {
225
+ window.location.reload();
226
+ }, 8000);
227
+ return () => clearTimeout(timer);
228
+ }, [updatePhase]);
229
+
230
+ const stepIcons: Record<string, string> = {
231
+ install: "download",
232
+ rebuild: "build",
233
+ restart: "restart_alt",
234
+ complete: "check_circle",
235
+ error: "error",
236
+ };
237
+
238
+ const stepLabels: Record<string, string> = {
239
+ install: "Install Package",
240
+ rebuild: "Rebuild Native Modules",
241
+ restart: "Restart Service",
242
+ complete: "Complete",
243
+ error: "Error",
244
+ };
245
+
156
246
  if (loading) {
157
247
  return (
158
248
  <div className="flex flex-col gap-8">
@@ -166,8 +256,122 @@ export default function HomePageClient({ machineId }) {
166
256
 
167
257
  return (
168
258
  <div className="flex flex-col gap-8">
259
+ {/* Update Progress Overlay */}
260
+ {updating && (
261
+ <div className="fixed inset-0 z-[999] bg-black/60 backdrop-blur-sm flex items-center justify-center p-4">
262
+ <div className="bg-bg-main border border-border rounded-2xl shadow-2xl max-w-md w-full p-6">
263
+ <div className="flex items-center gap-3 mb-5">
264
+ <span className="material-symbols-outlined text-primary text-[28px] animate-spin">
265
+ progress_activity
266
+ </span>
267
+ <div>
268
+ <h3 className="text-lg font-bold">
269
+ {updatePhase === "done"
270
+ ? "Update Complete!"
271
+ : updatePhase === "failed"
272
+ ? "Update Failed"
273
+ : "Updating OmniRoute..."}
274
+ </h3>
275
+ <p className="text-xs text-text-muted mt-0.5">
276
+ {updatePhase === "done"
277
+ ? "The page will reload automatically in a few seconds."
278
+ : updatePhase === "failed"
279
+ ? "Please try again or update manually via the CLI."
280
+ : "Do not close this page. The system will restart automatically."}
281
+ </p>
282
+ </div>
283
+ </div>
284
+
285
+ {/* Step list */}
286
+ <div className="flex flex-col gap-2">
287
+ {updateSteps
288
+ .filter((s) => s.step !== "complete" && s.step !== "error")
289
+ .map((s) => (
290
+ <div
291
+ key={s.step}
292
+ className={`flex items-center gap-3 px-3 py-2.5 rounded-lg border transition-all ${
293
+ s.status === "running"
294
+ ? "border-primary/40 bg-primary/5"
295
+ : s.status === "done"
296
+ ? "border-green-500/30 bg-green-500/5"
297
+ : s.status === "failed"
298
+ ? "border-red-500/30 bg-red-500/5"
299
+ : "border-border bg-bg-subtle"
300
+ }`}
301
+ >
302
+ {s.status === "running" ? (
303
+ <span className="material-symbols-outlined text-primary text-[18px] animate-spin">
304
+ progress_activity
305
+ </span>
306
+ ) : s.status === "done" ? (
307
+ <span className="material-symbols-outlined text-green-500 text-[18px]">
308
+ check_circle
309
+ </span>
310
+ ) : s.status === "failed" ? (
311
+ <span className="material-symbols-outlined text-red-500 text-[18px]">
312
+ error
313
+ </span>
314
+ ) : (
315
+ <span className="material-symbols-outlined text-yellow-500 text-[18px]">
316
+ warning
317
+ </span>
318
+ )}
319
+ <div className="min-w-0 flex-1">
320
+ <p className="text-sm font-medium">{stepLabels[s.step] || s.step}</p>
321
+ <p className="text-xs text-text-muted truncate">{s.message}</p>
322
+ </div>
323
+ </div>
324
+ ))}
325
+
326
+ {/* Error message */}
327
+ {updateSteps.find((s) => s.step === "error") && (
328
+ <div className="mt-1 px-3 py-2.5 rounded-lg border border-red-500/30 bg-red-500/5 text-red-500">
329
+ <p className="text-xs font-mono break-all">
330
+ {updateSteps.find((s) => s.step === "error")?.message}
331
+ </p>
332
+ </div>
333
+ )}
334
+
335
+ {/* Completion message */}
336
+ {updatePhase === "done" && (
337
+ <div className="mt-1 px-3 py-2.5 rounded-lg border border-green-500/30 bg-green-500/5">
338
+ <p className="text-sm font-semibold text-green-500 flex items-center gap-2">
339
+ <span className="material-symbols-outlined text-[18px]">check_circle</span>
340
+ {updateSteps.find((s) => s.step === "complete")?.message || "Update complete!"}
341
+ </p>
342
+ <p className="text-xs text-text-muted mt-1">Reloading page automatically...</p>
343
+ </div>
344
+ )}
345
+ </div>
346
+
347
+ {/* Actions */}
348
+ {(updatePhase === "failed" || updatePhase === "done") && (
349
+ <div className="flex gap-2 mt-4">
350
+ <Button
351
+ size="sm"
352
+ fullWidth
353
+ onClick={() => {
354
+ setUpdating(false);
355
+ setUpdatePhase("idle");
356
+ setUpdateSteps([]);
357
+ if (updatePhase === "done") window.location.reload();
358
+ }}
359
+ >
360
+ {updatePhase === "done" ? "Reload Now" : "Close"}
361
+ </Button>
362
+ {updatePhase === "failed" && (
363
+ <Button size="sm" variant="secondary" fullWidth onClick={handleUpdate}>
364
+ Retry
365
+ </Button>
366
+ )}
367
+ </div>
368
+ )}
369
+ </div>
370
+ </div>
371
+ )}
372
+
169
373
  {/* Update Notification Banner */}
170
- {versionInfo?.updateAvailable && (
374
+ {versionInfo?.updateAvailable && !updating && (
171
375
  <div className="bg-primary/10 border border-primary/20 text-primary px-5 py-4 rounded-xl flex items-center justify-between min-h-[64px]">
172
376
  <div className="flex items-center gap-4">
173
377
  <span className="material-symbols-outlined text-[24px]">system_update_alt</span>
@@ -185,7 +389,7 @@ export default function HomePageClient({ machineId }) {
185
389
  disabled={updating}
186
390
  className="shrink-0 ml-4 font-semibold"
187
391
  >
188
- {updating ? t("updating") || "Updating..." : t("updateNow") || "Update Now"}
392
+ {t("updateNow") || "Update Now"}
189
393
  </Button>
190
394
  </div>
191
395
  )}
@@ -30,7 +30,6 @@ async function getLatestNpmVersion(): Promise<string | null> {
30
30
  /** Current installed version from package.json */
31
31
  function getCurrentVersion(): string {
32
32
  try {
33
-
34
33
  return require("../../../../../package.json").version as string;
35
34
  } catch {
36
35
  return "unknown";
@@ -89,27 +88,75 @@ export async function POST(req: NextRequest) {
89
88
  });
90
89
  }
91
90
 
92
- // Run update in background client gets immediate acknowledgment
93
- const install = async () => {
94
- try {
95
- await execFileAsync("npm", ["install", "-g", `omniroute@${latest}`, "--ignore-scripts"], {
96
- timeout: 300000, // 5 minutes
97
- });
98
- // Restart PM2 — non-fatal if pm2 not available (Docker/manual setups)
99
- await execFileAsync("pm2", ["restart", "omniroute"]).catch(() => null);
100
- console.log(`[AutoUpdate] Successfully updated to v${latest}`);
101
- } catch (err) {
102
- console.error(`[AutoUpdate] Update failed:`, err);
103
- }
104
- };
105
-
106
- // Fire-and-forget
107
- install();
91
+ // Stream progress events so the frontend can show real-time status
92
+ const encoder = new TextEncoder();
93
+ const stream = new ReadableStream({
94
+ async start(controller) {
95
+ const send = (data: Record<string, unknown>) => {
96
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
97
+ };
108
98
 
109
- return NextResponse.json({
110
- success: true,
111
- message: `Update to v${latest} started. Restarting in ~30 seconds.`,
112
- from: current,
113
- to: latest,
99
+ try {
100
+ // Step 1: Install
101
+ send({ step: "install", status: "running", message: `Installing omniroute@${latest}...` });
102
+ await execFileAsync("npm", ["install", "-g", `omniroute@${latest}`, "--ignore-scripts"], {
103
+ timeout: 300000,
104
+ });
105
+ send({ step: "install", status: "done", message: `Installed omniroute@${latest}` });
106
+
107
+ // Step 2: Rebuild native modules (critical for better-sqlite3)
108
+ send({
109
+ step: "rebuild",
110
+ status: "running",
111
+ message: "Rebuilding native modules (better-sqlite3)...",
112
+ });
113
+ const globalRoot = (
114
+ await execFileAsync("npm", ["root", "-g"], { timeout: 10000 })
115
+ ).stdout.trim();
116
+ const omniPath = `${globalRoot}/omniroute/app`;
117
+ await execFileAsync("npm", ["rebuild", "better-sqlite3"], {
118
+ cwd: omniPath,
119
+ timeout: 120000,
120
+ });
121
+ send({ step: "rebuild", status: "done", message: "Native modules rebuilt" });
122
+
123
+ // Step 3: Restart PM2
124
+ send({ step: "restart", status: "running", message: "Restarting service via PM2..." });
125
+ try {
126
+ await execFileAsync("pm2", ["restart", "omniroute", "--update-env"], { timeout: 30000 });
127
+ send({ step: "restart", status: "done", message: "Service restarted" });
128
+ } catch {
129
+ // PM2 may not be available (Docker/manual setups)
130
+ send({
131
+ step: "restart",
132
+ status: "skipped",
133
+ message: "PM2 not available — manual restart needed",
134
+ });
135
+ }
136
+
137
+ send({
138
+ step: "complete",
139
+ status: "done",
140
+ from: current,
141
+ to: latest,
142
+ message: `Update to v${latest} complete!`,
143
+ });
144
+ console.log(`[AutoUpdate] Successfully updated to v${latest}`);
145
+ } catch (err: any) {
146
+ const errMsg = err?.stderr || err?.message || String(err);
147
+ send({ step: "error", status: "failed", message: errMsg });
148
+ console.error(`[AutoUpdate] Update failed:`, err);
149
+ } finally {
150
+ controller.close();
151
+ }
152
+ },
153
+ });
154
+
155
+ return new Response(stream, {
156
+ headers: {
157
+ "Content-Type": "text/event-stream",
158
+ "Cache-Control": "no-cache",
159
+ Connection: "keep-alive",
160
+ },
114
161
  });
115
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omniroute",
3
- "version": "3.2.6",
3
+ "version": "3.2.7",
4
4
  "description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
5
5
  "type": "module",
6
6
  "bin": {