Merge remote-tracking branch 'public-gh/master'
* public-gh/master: fix(ui): wrap failed run card actions on mobile feat(codex): add gpt-5.4 to codex_local model list persist paperclip data in a named volume add support to `cursor` and `opencode` in containerized instances force `@types/node@24` in the server Add artifact-check to fail fast on broken builds remove an insecure default auth secret expose `PAPERCLIP_ALLOWED_HOSTNAMES` in compose files wait for a health db update lock file update typing to node v24 from v20 add missing `openclaw` adapter from deps stage fix incorrect pkg scope update docker base image move docker into `authenticated` deployment mode
This commit is contained in:
@@ -57,7 +57,9 @@
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/node": "^24.6.0",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@types/ws": "^8.18.1",
|
||||
"supertest": "^7.0.0",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.7.3",
|
||||
|
||||
@@ -444,7 +444,7 @@ const app = await createApp(db as any, {
|
||||
betterAuthHandler,
|
||||
resolveSession,
|
||||
});
|
||||
const server = createServer(app);
|
||||
const server = createServer(app as unknown as Parameters<typeof createServer>[0]);
|
||||
const listenPort = await detectPort(config.port);
|
||||
|
||||
if (listenPort !== config.port) {
|
||||
|
||||
@@ -1,15 +1,45 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import type { IncomingMessage, Server as HttpServer } from "node:http";
|
||||
import { createRequire } from "node:module";
|
||||
import type { Duplex } from "node:stream";
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import type { Db } from "@paperclipai/db";
|
||||
import { agentApiKeys, companyMemberships, instanceUserRoles } from "@paperclipai/db";
|
||||
import type { DeploymentMode } from "@paperclipai/shared";
|
||||
import { WebSocket, WebSocketServer } from "ws";
|
||||
import type { BetterAuthSessionResult } from "../auth/better-auth.js";
|
||||
import { logger } from "../middleware/logger.js";
|
||||
import { subscribeCompanyLiveEvents } from "../services/live-events.js";
|
||||
|
||||
interface WsSocket {
|
||||
readyState: number;
|
||||
ping(): void;
|
||||
send(data: string): void;
|
||||
terminate(): void;
|
||||
close(code?: number, reason?: string): void;
|
||||
on(event: "pong", listener: () => void): void;
|
||||
on(event: "close", listener: () => void): void;
|
||||
on(event: "error", listener: (err: Error) => void): void;
|
||||
}
|
||||
|
||||
interface WsServer {
|
||||
clients: Set<WsSocket>;
|
||||
on(event: "connection", listener: (socket: WsSocket, req: IncomingMessage) => void): void;
|
||||
on(event: "close", listener: () => void): void;
|
||||
handleUpgrade(
|
||||
req: IncomingMessage,
|
||||
socket: Duplex,
|
||||
head: Buffer,
|
||||
callback: (ws: WsSocket) => void,
|
||||
): void;
|
||||
emit(event: "connection", ws: WsSocket, req: IncomingMessage): boolean;
|
||||
}
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { WebSocket, WebSocketServer } = require("ws") as {
|
||||
WebSocket: { OPEN: number };
|
||||
WebSocketServer: new (opts: { noServer: boolean }) => WsServer;
|
||||
};
|
||||
|
||||
interface UpgradeContext {
|
||||
companyId: string;
|
||||
actorType: "board" | "agent";
|
||||
@@ -154,8 +184,8 @@ export function setupLiveEventsWebSocketServer(
|
||||
},
|
||||
) {
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
const cleanupByClient = new Map<WebSocket, () => void>();
|
||||
const aliveByClient = new Map<WebSocket, boolean>();
|
||||
const cleanupByClient = new Map<WsSocket, () => void>();
|
||||
const aliveByClient = new Map<WsSocket, boolean>();
|
||||
|
||||
const pingInterval = setInterval(() => {
|
||||
for (const socket of wss.clients) {
|
||||
@@ -168,7 +198,7 @@ export function setupLiveEventsWebSocketServer(
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
wss.on("connection", (socket, req) => {
|
||||
wss.on("connection", (socket: WsSocket, req: IncomingMessage) => {
|
||||
const context = (req as IncomingMessageWithContext).paperclipUpgradeContext;
|
||||
if (!context) {
|
||||
socket.close(1008, "missing context");
|
||||
@@ -194,7 +224,7 @@ export function setupLiveEventsWebSocketServer(
|
||||
aliveByClient.delete(socket);
|
||||
});
|
||||
|
||||
socket.on("error", (err) => {
|
||||
socket.on("error", (err: Error) => {
|
||||
logger.warn({ err, companyId: context.companyId }, "live websocket client error");
|
||||
});
|
||||
});
|
||||
@@ -229,7 +259,7 @@ export function setupLiveEventsWebSocketServer(
|
||||
const reqWithContext = req as IncomingMessageWithContext;
|
||||
reqWithContext.paperclipUpgradeContext = context;
|
||||
|
||||
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||
wss.handleUpgrade(req, socket, head, (ws: WsSocket) => {
|
||||
wss.emit("connection", ws, reqWithContext);
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user