Fix graceful shutdown, Improve setup UI

This commit is contained in:
Arjun Komath
2026-01-31 23:40:23 +11:00
parent 785fef9087
commit 116e6718ab
3 changed files with 75 additions and 15 deletions
+29 -8
View File
@@ -27,6 +27,7 @@
pairingChannel: '',
pairingCode: '',
showPairingModal: false,
isReady: false,
get currentGroup() {
return this.authGroups.find(g => g.value === this.selectedGroup);
@@ -50,10 +51,9 @@
this.status = 'Loading...';
try {
const j = await this.httpJson('/setup/api/status');
const ver = j.openclawVersion ? (' | ' + j.openclawVersion) : '';
this.configured = j.configured;
this.openclawVersion = j.openclawVersion || '';
this.status = (j.configured ? 'Configured - open /openclaw' : 'Not configured - run setup below') + ver;
this.status = j.configured ? 'Configured - open /openclaw' : 'Not configured - run setup below';
this.authGroups = j.authGroups || [];
if (this.authGroups.length > 0) {
this.selectedGroup = this.authGroups[0].value;
@@ -62,8 +62,10 @@
if (j.channelsAddHelp && j.channelsAddHelp.indexOf('telegram') === -1) {
this.log += '\nNote: this openclaw build does not list telegram in `channels add --help`. Telegram auto-add will be skipped.\n';
}
this.isReady = true;
} catch (e) {
this.status = 'Error: ' + String(e);
this.isReady = true;
}
},
@@ -165,18 +167,37 @@
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>
</head>
<body x-data="setupApp()">
<div class="logo">
<svg viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="lobster-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff4d4d"/>
<stop offset="100%" stop-color="#991b1b"/>
</linearGradient>
</defs>
<path d="M60 10 C30 10 15 35 15 55 C15 75 30 95 45 100 L45 110 L55 110 L55 100 C55 100 60 102 65 100 L65 110 L75 110 L75 100 C90 95 105 75 105 55 C105 35 90 10 60 10Z" fill="url(#lobster-gradient)"/>
<path d="M20 45 C5 40 0 50 5 60 C10 70 20 65 25 55 C28 48 25 45 20 45Z" fill="url(#lobster-gradient)"/>
<path d="M100 45 C115 40 120 50 115 60 C110 70 100 65 95 55 C92 48 95 45 100 45Z" fill="url(#lobster-gradient)"/>
<path d="M45 15 Q35 5 30 8" stroke="#ff4d4d" stroke-width="3" stroke-linecap="round"/>
<path d="M75 15 Q85 5 90 8" stroke="#ff4d4d" stroke-width="3" stroke-linecap="round"/>
<circle cx="45" cy="35" r="6" fill="#050810"/>
<circle cx="75" cy="35" r="6" fill="#050810"/>
<circle cx="46" cy="34" r="2.5" fill="#00e5cc"/>
<circle cx="76" cy="34" r="2.5" fill="#00e5cc"/>
</svg>
</div>
<h1>OpenClaw Setup</h1>
<p class="muted">This wizard configures OpenClaw by running the same onboarding command it uses in the terminal, but from the browser.</p>
<p class="muted subtitle">This wizard configures OpenClaw by running the same onboarding command it uses in the terminal, but from the browser.</p>
<div class="card">
<h2>Status</h2>
<h2>Status <span x-show="openclawVersion" x-text="openclawVersion" class="muted" style="font-size: 0.75em; font-weight: normal;"></span></h2>
<div x-text="status">Loading...</div>
<div style="margin-top: 0.75rem">
<div x-show="configured" style="margin-top: 0.75rem">
<a href="/openclaw" target="_blank">Open OpenClaw UI</a>
</div>
</div>
<div class="card">
<div class="card" x-show="isReady">
<h2>1) Model/auth provider</h2>
<p class="muted">Matches the groups shown in the terminal onboarding.</p>
<label>Provider group</label>
@@ -210,7 +231,7 @@
</select>
</div>
<div class="card">
<div class="card" x-show="isReady">
<h2>2) Optional: Channels</h2>
<p class="muted">You can also add channels later inside OpenClaw, but this helps you get messaging working immediately.</p>
@@ -234,7 +255,7 @@
<input x-model="slackAppToken" type="password" placeholder="xapp-..." />
</div>
<div class="card">
<div class="card" x-show="isReady">
<h2>3) Run onboarding</h2>
<button @click="runSetup()" :disabled="loading">Run setup</button>
<button @click="openPairingModal()" class="secondary-button" :disabled="loading">Approve pairing</button>
+19
View File
@@ -54,6 +54,17 @@ body {
transition: background 0.2s, color 0.2s;
}
.logo {
display: flex;
justify-content: center;
margin-bottom: 0.5rem;
}
.logo svg {
width: 72px;
height: 72px;
}
.card {
border: 1px solid var(--border-color);
border-radius: 12px;
@@ -167,3 +178,11 @@ h1, h2, h3 {
color: var(--text-primary);
}
h1 {
text-align: center;
}
.subtitle {
text-align: center;
}
+27 -7
View File
@@ -805,12 +805,32 @@ server.on("upgrade", async (req, socket, head) => {
proxy.ws(req, socket, head, { target: GATEWAY_TARGET });
});
process.on("SIGTERM", () => {
console.log("[wrapper] received SIGTERM, shutting down");
try {
if (gatewayProc) gatewayProc.kill("SIGTERM");
} catch (err) {
console.warn(`[wrapper] error killing gateway: ${err.message}`);
async function gracefulShutdown(signal) {
console.log(`[wrapper] received ${signal}, shutting down`);
if (setupRateLimiter.cleanupInterval) {
clearInterval(setupRateLimiter.cleanupInterval);
}
server.close();
if (gatewayProc) {
try {
gatewayProc.kill("SIGTERM");
await Promise.race([
new Promise((resolve) => gatewayProc.on("exit", resolve)),
new Promise((resolve) => setTimeout(resolve, 2000)),
]);
if (gatewayProc && !gatewayProc.killed) {
gatewayProc.kill("SIGKILL");
}
} catch (err) {
console.warn(`[wrapper] error killing gateway: ${err.message}`);
}
}
process.exit(0);
});
}
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));