@clawos-dev/clawd 0.2.105 → 0.2.106-beta.202.2cc5279
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/dist/cli.cjs +98 -15
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -19257,8 +19257,8 @@ var require_websocket = __commonJS({
|
|
|
19257
19257
|
"use strict";
|
|
19258
19258
|
var EventEmitter2 = require("events");
|
|
19259
19259
|
var https = require("https");
|
|
19260
|
-
var
|
|
19261
|
-
var
|
|
19260
|
+
var http3 = require("http");
|
|
19261
|
+
var net3 = require("net");
|
|
19262
19262
|
var tls = require("tls");
|
|
19263
19263
|
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
19264
19264
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
@@ -19791,7 +19791,7 @@ var require_websocket = __commonJS({
|
|
|
19791
19791
|
}
|
|
19792
19792
|
const defaultPort = isSecure ? 443 : 80;
|
|
19793
19793
|
const key = randomBytes(16).toString("base64");
|
|
19794
|
-
const request = isSecure ? https.request :
|
|
19794
|
+
const request = isSecure ? https.request : http3.request;
|
|
19795
19795
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
19796
19796
|
let perMessageDeflate;
|
|
19797
19797
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -19992,12 +19992,12 @@ var require_websocket = __commonJS({
|
|
|
19992
19992
|
}
|
|
19993
19993
|
function netConnect(options) {
|
|
19994
19994
|
options.path = options.socketPath;
|
|
19995
|
-
return
|
|
19995
|
+
return net3.connect(options);
|
|
19996
19996
|
}
|
|
19997
19997
|
function tlsConnect(options) {
|
|
19998
19998
|
options.path = void 0;
|
|
19999
19999
|
if (!options.servername && options.servername !== "") {
|
|
20000
|
-
options.servername =
|
|
20000
|
+
options.servername = net3.isIP(options.host) ? "" : options.host;
|
|
20001
20001
|
}
|
|
20002
20002
|
return tls.connect(options);
|
|
20003
20003
|
}
|
|
@@ -20285,7 +20285,7 @@ var require_websocket_server = __commonJS({
|
|
|
20285
20285
|
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
|
|
20286
20286
|
"use strict";
|
|
20287
20287
|
var EventEmitter2 = require("events");
|
|
20288
|
-
var
|
|
20288
|
+
var http3 = require("http");
|
|
20289
20289
|
var { Duplex } = require("stream");
|
|
20290
20290
|
var { createHash: createHash2 } = require("crypto");
|
|
20291
20291
|
var extension2 = require_extension();
|
|
@@ -20360,8 +20360,8 @@ var require_websocket_server = __commonJS({
|
|
|
20360
20360
|
);
|
|
20361
20361
|
}
|
|
20362
20362
|
if (options.port != null) {
|
|
20363
|
-
this._server =
|
|
20364
|
-
const body =
|
|
20363
|
+
this._server = http3.createServer((req, res) => {
|
|
20364
|
+
const body = http3.STATUS_CODES[426];
|
|
20365
20365
|
res.writeHead(426, {
|
|
20366
20366
|
"Content-Length": body.length,
|
|
20367
20367
|
"Content-Type": "text/plain"
|
|
@@ -20648,7 +20648,7 @@ var require_websocket_server = __commonJS({
|
|
|
20648
20648
|
this.destroy();
|
|
20649
20649
|
}
|
|
20650
20650
|
function abortHandshake(socket, code, message, headers) {
|
|
20651
|
-
message = message ||
|
|
20651
|
+
message = message || http3.STATUS_CODES[code];
|
|
20652
20652
|
headers = {
|
|
20653
20653
|
Connection: "close",
|
|
20654
20654
|
"Content-Type": "text/html",
|
|
@@ -20657,7 +20657,7 @@ var require_websocket_server = __commonJS({
|
|
|
20657
20657
|
};
|
|
20658
20658
|
socket.once("finish", socket.destroy);
|
|
20659
20659
|
socket.end(
|
|
20660
|
-
`HTTP/1.1 ${code} ${
|
|
20660
|
+
`HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
|
|
20661
20661
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
20662
20662
|
);
|
|
20663
20663
|
}
|
|
@@ -30870,6 +30870,8 @@ function resolveConfig(opts) {
|
|
|
30870
30870
|
const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
|
|
30871
30871
|
const rawMode = env.CLAWD_CC_MODE;
|
|
30872
30872
|
const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
|
|
30873
|
+
const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
|
|
30874
|
+
const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
|
|
30873
30875
|
return {
|
|
30874
30876
|
port,
|
|
30875
30877
|
host,
|
|
@@ -30881,7 +30883,8 @@ function resolveConfig(opts) {
|
|
|
30881
30883
|
authToken,
|
|
30882
30884
|
noTunnelPersist,
|
|
30883
30885
|
frpcBinary,
|
|
30884
|
-
mode
|
|
30886
|
+
mode,
|
|
30887
|
+
previewPorts
|
|
30885
30888
|
};
|
|
30886
30889
|
}
|
|
30887
30890
|
var HELP_TEXT = `clawd [options]
|
|
@@ -36252,7 +36255,58 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
36252
36255
|
var wrapper_default = import_websocket.default;
|
|
36253
36256
|
|
|
36254
36257
|
// src/transport/local-ws-server.ts
|
|
36258
|
+
var import_node_http2 = __toESM(require("http"), 1);
|
|
36259
|
+
|
|
36260
|
+
// src/transport/preview-proxy.ts
|
|
36255
36261
|
var import_node_http = __toESM(require("http"), 1);
|
|
36262
|
+
var import_node_net = __toESM(require("net"), 1);
|
|
36263
|
+
var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
|
|
36264
|
+
function matchPreviewPort(pathname) {
|
|
36265
|
+
const m2 = pathname.match(PREVIEW_RE);
|
|
36266
|
+
return m2 ? Number(m2[1]) : null;
|
|
36267
|
+
}
|
|
36268
|
+
function isPreviewPortAllowed(port, cfg) {
|
|
36269
|
+
return cfg.allowedPorts.includes(port);
|
|
36270
|
+
}
|
|
36271
|
+
function proxyPreviewHttp(req, res, port) {
|
|
36272
|
+
return new Promise((resolve6) => {
|
|
36273
|
+
const upstream = import_node_http.default.request(
|
|
36274
|
+
{ host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
|
|
36275
|
+
(upRes) => {
|
|
36276
|
+
res.writeHead(upRes.statusCode || 502, upRes.headers);
|
|
36277
|
+
upRes.pipe(res);
|
|
36278
|
+
upRes.on("end", () => resolve6());
|
|
36279
|
+
}
|
|
36280
|
+
);
|
|
36281
|
+
upstream.on("error", (e) => {
|
|
36282
|
+
if (!res.headersSent) {
|
|
36283
|
+
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
36284
|
+
}
|
|
36285
|
+
res.end(`preview upstream error: ${e.message}`);
|
|
36286
|
+
resolve6();
|
|
36287
|
+
});
|
|
36288
|
+
req.pipe(upstream);
|
|
36289
|
+
});
|
|
36290
|
+
}
|
|
36291
|
+
function proxyPreviewUpgrade(req, socket, head, port) {
|
|
36292
|
+
const upstream = import_node_net.default.connect(port, "localhost", () => {
|
|
36293
|
+
let raw = `${req.method} ${req.url} HTTP/1.1\r
|
|
36294
|
+
`;
|
|
36295
|
+
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
36296
|
+
raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
|
|
36297
|
+
`;
|
|
36298
|
+
}
|
|
36299
|
+
raw += "\r\n";
|
|
36300
|
+
upstream.write(raw);
|
|
36301
|
+
if (head && head.length) upstream.write(head);
|
|
36302
|
+
upstream.pipe(socket);
|
|
36303
|
+
socket.pipe(upstream);
|
|
36304
|
+
});
|
|
36305
|
+
upstream.on("error", () => socket.destroy());
|
|
36306
|
+
socket.on("error", () => upstream.destroy());
|
|
36307
|
+
}
|
|
36308
|
+
|
|
36309
|
+
// src/transport/local-ws-server.ts
|
|
36256
36310
|
var LocalWsServer = class {
|
|
36257
36311
|
constructor(opts) {
|
|
36258
36312
|
this.opts = opts;
|
|
@@ -36272,9 +36326,25 @@ var LocalWsServer = class {
|
|
|
36272
36326
|
async start() {
|
|
36273
36327
|
const host = this.opts.host ?? "127.0.0.1";
|
|
36274
36328
|
await new Promise((resolve6, reject) => {
|
|
36275
|
-
const httpServer =
|
|
36329
|
+
const httpServer = import_node_http2.default.createServer((req, res) => this.handleHttpRequest(req, res));
|
|
36276
36330
|
const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
|
|
36277
36331
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
36332
|
+
if (req.url?.startsWith("/preview/")) {
|
|
36333
|
+
const pathname = (() => {
|
|
36334
|
+
try {
|
|
36335
|
+
return new URL(req.url, "http://localhost").pathname;
|
|
36336
|
+
} catch {
|
|
36337
|
+
return "/";
|
|
36338
|
+
}
|
|
36339
|
+
})();
|
|
36340
|
+
const port = matchPreviewPort(pathname);
|
|
36341
|
+
if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
|
|
36342
|
+
proxyPreviewUpgrade(req, socket, head, port);
|
|
36343
|
+
} else {
|
|
36344
|
+
socket.destroy();
|
|
36345
|
+
}
|
|
36346
|
+
return;
|
|
36347
|
+
}
|
|
36278
36348
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
36279
36349
|
this.routeConnection(ws, req);
|
|
36280
36350
|
});
|
|
@@ -37922,6 +37992,15 @@ function createHttpRouter(deps) {
|
|
|
37922
37992
|
});
|
|
37923
37993
|
return true;
|
|
37924
37994
|
}
|
|
37995
|
+
if (url.pathname.startsWith("/preview/")) {
|
|
37996
|
+
const port = matchPreviewPort(url.pathname);
|
|
37997
|
+
if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
|
|
37998
|
+
await proxyPreviewHttp(req, res, port);
|
|
37999
|
+
return true;
|
|
38000
|
+
}
|
|
38001
|
+
sendJson(res, 403, { code: "PREVIEW_PORT_FORBIDDEN", message: "preview port not allowed" });
|
|
38002
|
+
return true;
|
|
38003
|
+
}
|
|
37925
38004
|
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
|
|
37926
38005
|
return false;
|
|
37927
38006
|
}
|
|
@@ -41518,7 +41597,7 @@ var import_node_path41 = __toESM(require("path"), 1);
|
|
|
41518
41597
|
var import_promises10 = require("timers/promises");
|
|
41519
41598
|
|
|
41520
41599
|
// src/extension/port-allocator.ts
|
|
41521
|
-
var
|
|
41600
|
+
var import_node_net2 = __toESM(require("net"), 1);
|
|
41522
41601
|
var PortExhaustedError = class extends Error {
|
|
41523
41602
|
constructor(min, max) {
|
|
41524
41603
|
super(`no free port in [${min},${max}]`);
|
|
@@ -41531,7 +41610,7 @@ var PortExhaustedError = class extends Error {
|
|
|
41531
41610
|
};
|
|
41532
41611
|
function probe(port) {
|
|
41533
41612
|
return new Promise((resolve6) => {
|
|
41534
|
-
const srv =
|
|
41613
|
+
const srv = import_node_net2.default.createServer();
|
|
41535
41614
|
srv.once("error", () => resolve6(false));
|
|
41536
41615
|
srv.once("listening", () => {
|
|
41537
41616
|
srv.close(() => resolve6(true));
|
|
@@ -42185,7 +42264,9 @@ async function startDaemon(config) {
|
|
|
42185
42264
|
getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
|
|
42186
42265
|
},
|
|
42187
42266
|
// POST /extensions/import lands in ~/.clawd/extensions/<id>/
|
|
42188
|
-
extensionsRoot: () => extensionsRoot()
|
|
42267
|
+
extensionsRoot: () => extensionsRoot(),
|
|
42268
|
+
// app-builder build 模式:/preview/<port>/ 反代 dev server 的端口白名单(见 spec §三.2)
|
|
42269
|
+
previewPorts: config.previewPorts ?? void 0
|
|
42189
42270
|
});
|
|
42190
42271
|
wsServer = new LocalWsServer({
|
|
42191
42272
|
host: config.host,
|
|
@@ -42224,6 +42305,8 @@ async function startDaemon(config) {
|
|
|
42224
42305
|
},
|
|
42225
42306
|
// file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
|
|
42226
42307
|
httpRequestHandler: httpRouter,
|
|
42308
|
+
// app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
|
|
42309
|
+
previewPorts: config.previewPorts ?? void 0,
|
|
42227
42310
|
// 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
|
|
42228
42311
|
// daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
|
|
42229
42312
|
// 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
|
package/package.json
CHANGED