feat(webchat): auto-start at root
This commit is contained in:
@@ -14,7 +14,7 @@ The macOS menu bar app opens the gateway’s loopback web chat server in a WKWeb
|
||||
- WK logs: navigation lifecycle, readyState, js location, and JS errors/unhandled rejections are mirrored to OSLog for easier diagnosis.
|
||||
|
||||
## How it’s wired
|
||||
- Assets: `apps/macos/Sources/Clawdis/Resources/WebChat/` contains the `pi-web-ui` dist plus a local import map pointing at bundled vendor modules and a tiny `pi-ai` stub. Everything is served from the gateway at `/webchat/*`.
|
||||
- Assets: `apps/macos/Sources/Clawdis/Resources/WebChat/` contains the `pi-web-ui` dist plus a local import map pointing at bundled vendor modules and a tiny `pi-ai` stub. Everything is served from the gateway at `/` (legacy `/webchat/*` still works).
|
||||
- Bridge: none. The web UI calls `/webchat/rpc` directly; Swift no longer proxies messages. RPC is handled in-process inside the gateway (no CLI spawn/PATH dependency).
|
||||
- Session: always primary; multiple transports (WhatsApp/Telegram/Desktop) share the same session key so context is unified.
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@ Updated: 2025-12-09
|
||||
- `webchat.gatewayPort` config can point at a non-default Gateway port if needed.
|
||||
|
||||
## Endpoints
|
||||
- `GET /webchat/info?session=<key>` → `{ port, sessionId, initialMessages, basePath }` plus history from the Gateway session store.
|
||||
- `GET /webchat/*` → static assets.
|
||||
- `POST /webchat/rpc` → proxies a chat/agent action through the Gateway connection and returns `{ ok, payloads?, error? }`.
|
||||
- UI is now served at the root: `http://127.0.0.1:<port>/` (legacy `/webchat/` still works).
|
||||
- `GET /webchat/info?session=<key>` (alias `/info`) → `{ port, sessionId, initialMessages, basePath }` plus history from the Gateway session store.
|
||||
- `GET /` (or `/webchat/*`) → static assets.
|
||||
- `POST /webchat/rpc` (alias `/rpc`) → proxies a chat/agent action through the Gateway connection and returns `{ ok, payloads?, error? }`.
|
||||
|
||||
## How it connects
|
||||
- On startup, the WebChat server dials the Gateway WebSocket and performs the mandatory `hello` handshake; the `hello-ok` snapshot seeds presence + health immediately.
|
||||
|
||||
@@ -56,7 +56,7 @@ describe("cli program", () => {
|
||||
await program.parseAsync(["webchat", "--json"], { from: "user" });
|
||||
expect(startWebChatServer).toHaveBeenCalled();
|
||||
expect(runtime.log).toHaveBeenCalledWith(
|
||||
JSON.stringify({ port: 18788, basePath: "/webchat/", host: "127.0.0.1" }),
|
||||
JSON.stringify({ port: 18788, basePath: "/", host: "127.0.0.1" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,10 @@ import { loginWeb, logoutWeb } from "../provider-web.js";
|
||||
import { runRpcLoop } from "../rpc/loop.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import { startWebChatServer } from "../webchat/server.js";
|
||||
import {
|
||||
ensureWebChatServerFromConfig,
|
||||
startWebChatServer,
|
||||
} from "../webchat/server.js";
|
||||
import { createDefaultDeps } from "./deps.js";
|
||||
import {
|
||||
forceFreePort,
|
||||
@@ -282,6 +285,22 @@ Examples:
|
||||
}
|
||||
try {
|
||||
await startGatewayServer(port);
|
||||
try {
|
||||
const webchat = await ensureWebChatServerFromConfig({
|
||||
gatewayUrl: `ws://127.0.0.1:${port}`,
|
||||
});
|
||||
if (webchat) {
|
||||
defaultRuntime.log(
|
||||
info(
|
||||
`webchat listening on http://127.0.0.1:${webchat.port}/`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.log(info("webchat disabled via config"));
|
||||
}
|
||||
} catch (webchatErr) {
|
||||
defaultRuntime.error(`WebChat failed to start: ${String(webchatErr)}`);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof GatewayLockError) {
|
||||
defaultRuntime.error(`Gateway failed to start: ${err.message}`);
|
||||
@@ -588,14 +607,14 @@ Shows token usage per session when the agent reports it; set inbound.reply.agent
|
||||
const server = await startWebChatServer(port);
|
||||
const payload = {
|
||||
port: server.port,
|
||||
basePath: "/webchat/",
|
||||
basePath: "/",
|
||||
host: "127.0.0.1",
|
||||
};
|
||||
if (opts.json) {
|
||||
defaultRuntime.log(JSON.stringify(payload));
|
||||
} else {
|
||||
defaultRuntime.log(
|
||||
info(`webchat listening on http://127.0.0.1:${server.port}/webchat/`),
|
||||
info(`webchat listening on http://127.0.0.1:${server.port}/`),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,9 @@ vi.mock("../commands/health.js", () => ({
|
||||
vi.mock("../commands/status.js", () => ({
|
||||
getStatusSummary: vi.fn().mockResolvedValue({ ok: true }),
|
||||
}));
|
||||
vi.mock("../webchat/server.js", () => ({
|
||||
ensureWebChatServerFromConfig: vi.fn().mockResolvedValue(null),
|
||||
}));
|
||||
vi.mock("../web/outbound.js", () => ({
|
||||
sendMessageWhatsApp: vi
|
||||
.fn()
|
||||
|
||||
@@ -22,6 +22,7 @@ import { defaultRuntime } from "../runtime.js";
|
||||
import { monitorTelegramProvider } from "../telegram/monitor.js";
|
||||
import { sendMessageTelegram } from "../telegram/send.js";
|
||||
import { sendMessageWhatsApp } from "../web/outbound.js";
|
||||
import { ensureWebChatServerFromConfig } from "../webchat/server.js";
|
||||
import {
|
||||
ErrorCodes,
|
||||
type ErrorShape,
|
||||
@@ -730,6 +731,13 @@ export async function startGatewayServer(port = 18789): Promise<GatewayServer> {
|
||||
);
|
||||
defaultRuntime.log(`gateway log file: ${getResolvedLoggerSettings().file}`);
|
||||
|
||||
// Start loopback WebChat server (unless disabled via config).
|
||||
void ensureWebChatServerFromConfig({
|
||||
gatewayUrl: `ws://127.0.0.1:${port}`,
|
||||
}).catch((err) => {
|
||||
logError(`gateway: webchat failed to start: ${String(err)}`);
|
||||
});
|
||||
|
||||
// Launch configured providers (WhatsApp Web, Telegram) so gateway replies via the
|
||||
// surface the message came from. Tests can opt out via CLAWDIS_SKIP_PROVIDERS.
|
||||
if (process.env.CLAWDIS_SKIP_PROVIDERS !== "1") {
|
||||
|
||||
@@ -557,12 +557,14 @@ export function __broadcastGatewayEventForTests(
|
||||
broadcastAll({ type: "gateway-event", event, payload });
|
||||
}
|
||||
|
||||
export async function ensureWebChatServerFromConfig() {
|
||||
export async function ensureWebChatServerFromConfig(opts?: {
|
||||
gatewayUrl?: string;
|
||||
}) {
|
||||
const cfg = loadConfig();
|
||||
if (cfg.webchat?.enabled === false) return null;
|
||||
const port = cfg.webchat?.port ?? WEBCHAT_DEFAULT_PORT;
|
||||
try {
|
||||
return await startWebChatServer(port);
|
||||
return await startWebChatServer(port, opts?.gatewayUrl);
|
||||
} catch (err) {
|
||||
logDebug(`webchat server failed to start: ${String(err)}`);
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user