newpr 0.5.5 → 0.5.6
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/package.json
CHANGED
|
@@ -256,43 +256,43 @@ export function AppShell({
|
|
|
256
256
|
</button>
|
|
257
257
|
</div>
|
|
258
258
|
|
|
259
|
-
{update.needsUpdate && (
|
|
259
|
+
{(update.needsUpdate || update.restarting) && (
|
|
260
260
|
<div className="shrink-0 mx-2 mt-2 rounded-lg border bg-blue-500/5 px-3 py-2.5">
|
|
261
|
-
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
261
|
+
{update.restarting ? (
|
|
262
|
+
<div className="flex items-center gap-2">
|
|
263
|
+
<Loader2 className="h-3 w-3 animate-spin text-blue-500 shrink-0" />
|
|
264
|
+
<span className="text-[11px] text-blue-600 dark:text-blue-400">
|
|
265
|
+
Restarting...
|
|
266
|
+
</span>
|
|
267
|
+
</div>
|
|
268
|
+
) : (
|
|
269
|
+
<>
|
|
270
|
+
<div className="flex items-center gap-2 mb-2">
|
|
271
|
+
<Download className="h-3 w-3 text-blue-500 shrink-0" />
|
|
272
|
+
<span className="text-[11px] font-medium text-blue-600 dark:text-blue-400">
|
|
273
|
+
v{update.latest} available
|
|
274
|
+
</span>
|
|
275
|
+
</div>
|
|
276
|
+
<button
|
|
277
|
+
type="button"
|
|
278
|
+
disabled={update.updating}
|
|
279
|
+
onClick={update.doUpdate}
|
|
280
|
+
className="w-full flex items-center justify-center gap-1.5 rounded-md bg-blue-500 hover:bg-blue-600 text-white text-[11px] font-medium py-1.5 transition-colors disabled:opacity-50"
|
|
281
|
+
>
|
|
282
|
+
{update.updating ? (
|
|
283
|
+
<><Loader2 className="h-3 w-3 animate-spin" /> Updating...</>
|
|
284
|
+
) : (
|
|
285
|
+
<><Download className="h-3 w-3" /> Update & restart</>
|
|
286
|
+
)}
|
|
287
|
+
</button>
|
|
288
|
+
{update.error && (
|
|
289
|
+
<p className="text-[10px] text-red-500 mt-1.5">{update.error}</p>
|
|
290
|
+
)}
|
|
291
|
+
</>
|
|
281
292
|
)}
|
|
282
293
|
</div>
|
|
283
294
|
)}
|
|
284
295
|
|
|
285
|
-
{update.updated && (
|
|
286
|
-
<div className="shrink-0 mx-2 mt-2 rounded-lg border border-green-500/20 bg-green-500/5 px-3 py-2.5">
|
|
287
|
-
<div className="flex items-center gap-2">
|
|
288
|
-
<Check className="h-3 w-3 text-green-500 shrink-0" />
|
|
289
|
-
<span className="text-[11px] text-green-600 dark:text-green-400">
|
|
290
|
-
Updated! Restart newpr to apply.
|
|
291
|
-
</span>
|
|
292
|
-
</div>
|
|
293
|
-
</div>
|
|
294
|
-
)}
|
|
295
|
-
|
|
296
296
|
<SessionList
|
|
297
297
|
sessions={sessions}
|
|
298
298
|
activeSessionId={activeSessionId}
|
|
@@ -6,10 +6,22 @@ interface UpdateState {
|
|
|
6
6
|
current: string;
|
|
7
7
|
latest: string;
|
|
8
8
|
updating: boolean;
|
|
9
|
-
|
|
9
|
+
restarting: boolean;
|
|
10
10
|
error: string | null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
async function waitForServer(maxWait = 30000): Promise<boolean> {
|
|
14
|
+
const start = Date.now();
|
|
15
|
+
while (Date.now() - start < maxWait) {
|
|
16
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch("/api/features", { signal: AbortSignal.timeout(2000) });
|
|
19
|
+
if (res.ok) return true;
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export function useUpdateCheck() {
|
|
14
26
|
const [state, setState] = useState<UpdateState>({
|
|
15
27
|
checking: true,
|
|
@@ -17,35 +29,49 @@ export function useUpdateCheck() {
|
|
|
17
29
|
current: "",
|
|
18
30
|
latest: "",
|
|
19
31
|
updating: false,
|
|
20
|
-
|
|
32
|
+
restarting: false,
|
|
21
33
|
error: null,
|
|
22
34
|
});
|
|
23
35
|
|
|
24
36
|
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const check = () => {
|
|
38
|
+
fetch("/api/update-check")
|
|
39
|
+
.then((r) => r.json())
|
|
40
|
+
.then((data) => {
|
|
41
|
+
const d = data as { current: string; latest: string; needsUpdate: boolean };
|
|
42
|
+
setState((s) => ({
|
|
43
|
+
...s,
|
|
44
|
+
checking: false,
|
|
45
|
+
current: d.current,
|
|
46
|
+
latest: d.latest,
|
|
47
|
+
needsUpdate: s.restarting ? s.needsUpdate : d.needsUpdate,
|
|
48
|
+
}));
|
|
49
|
+
})
|
|
50
|
+
.catch(() => setState((s) => ({ ...s, checking: false })));
|
|
51
|
+
};
|
|
52
|
+
check();
|
|
53
|
+
const interval = setInterval(check, 60 * 60 * 1000);
|
|
54
|
+
return () => clearInterval(interval);
|
|
38
55
|
}, []);
|
|
39
56
|
|
|
40
57
|
const doUpdate = useCallback(async () => {
|
|
41
58
|
setState((s) => ({ ...s, updating: true, error: null }));
|
|
42
59
|
try {
|
|
43
60
|
const res = await fetch("/api/update", { method: "POST" });
|
|
44
|
-
const data = await res.json() as { ok: boolean; error?: string };
|
|
45
|
-
if (data.ok) {
|
|
46
|
-
setState((s) => ({ ...s, updating: false, updated: true, needsUpdate: false }));
|
|
47
|
-
} else {
|
|
61
|
+
const data = await res.json() as { ok: boolean; restarting?: boolean; error?: string };
|
|
62
|
+
if (!data.ok) {
|
|
48
63
|
setState((s) => ({ ...s, updating: false, error: data.error ?? "Update failed" }));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (data.restarting) {
|
|
68
|
+
setState((s) => ({ ...s, updating: false, restarting: true }));
|
|
69
|
+
const up = await waitForServer();
|
|
70
|
+
if (up) {
|
|
71
|
+
window.location.reload();
|
|
72
|
+
} else {
|
|
73
|
+
setState((s) => ({ ...s, restarting: false, error: "Server did not come back up. Try restarting manually." }));
|
|
74
|
+
}
|
|
49
75
|
}
|
|
50
76
|
} catch (err) {
|
|
51
77
|
setState((s) => ({ ...s, updating: false, error: err instanceof Error ? err.message : String(err) }));
|
package/src/web/server/routes.ts
CHANGED
|
@@ -675,7 +675,18 @@ Before posting an inline comment, ALWAYS call \`get_file_diff\` first to find th
|
|
|
675
675
|
if (exitCode !== 0) {
|
|
676
676
|
return json({ ok: false, error: stderr.trim() || stdout.trim() }, 500);
|
|
677
677
|
}
|
|
678
|
-
|
|
678
|
+
|
|
679
|
+
setTimeout(() => {
|
|
680
|
+
Bun.spawn(["newpr", ...process.argv.slice(2)], {
|
|
681
|
+
cwd: process.cwd(),
|
|
682
|
+
stdin: "inherit",
|
|
683
|
+
stdout: "inherit",
|
|
684
|
+
stderr: "inherit",
|
|
685
|
+
});
|
|
686
|
+
setTimeout(() => process.exit(0), 500);
|
|
687
|
+
}, 1000);
|
|
688
|
+
|
|
689
|
+
return json({ ok: true, restarting: true });
|
|
679
690
|
} catch (err) {
|
|
680
691
|
return json({ ok: false, error: err instanceof Error ? err.message : String(err) }, 500);
|
|
681
692
|
}
|