pubblue 0.5.0 → 0.6.1
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/{chunk-YI45G6AG.js → chunk-BBJOOZHS.js} +159 -242
- package/dist/chunk-WXNNDR4T.js +1313 -0
- package/dist/index.js +236 -526
- package/dist/tunnel-daemon-BR5XKNEA.js +7 -0
- package/dist/tunnel-daemon-entry.js +12 -24
- package/package.json +3 -3
- package/dist/chunk-5GSMS3YU.js +0 -776
- package/dist/chunk-PFZT7M3E.js +0 -114
- package/dist/tunnel-bridge-entry.d.ts +0 -2
- package/dist/tunnel-bridge-entry.js +0 -703
- package/dist/tunnel-daemon-QN6TVUX6.js +0 -8
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ipcCall
|
|
3
|
-
} from "./chunk-PFZT7M3E.js";
|
|
4
|
-
|
|
5
1
|
// src/lib/cli-error.ts
|
|
6
2
|
import { CommanderError } from "commander";
|
|
7
3
|
var CliError = class extends Error {
|
|
@@ -124,35 +120,90 @@ var PubApiClient = class {
|
|
|
124
120
|
method: "DELETE"
|
|
125
121
|
});
|
|
126
122
|
}
|
|
127
|
-
// --
|
|
128
|
-
async
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
// -- Agent presence -------------------------------------------------------
|
|
124
|
+
async goOnline() {
|
|
125
|
+
await this.request("/api/v1/agent/online", { method: "POST" });
|
|
126
|
+
}
|
|
127
|
+
async heartbeat() {
|
|
128
|
+
await this.request("/api/v1/agent/heartbeat", { method: "POST" });
|
|
129
|
+
}
|
|
130
|
+
async goOffline() {
|
|
131
|
+
await this.request("/api/v1/agent/offline", { method: "POST" });
|
|
132
|
+
}
|
|
133
|
+
// -- Agent live management ------------------------------------------------
|
|
134
|
+
async getPendingLive() {
|
|
135
|
+
const data = await this.request("/api/v1/agent/live");
|
|
136
|
+
return data.live;
|
|
137
|
+
}
|
|
138
|
+
async signalAnswer(opts) {
|
|
139
|
+
await this.request("/api/v1/agent/live/signal", {
|
|
140
|
+
method: "PATCH",
|
|
131
141
|
body: JSON.stringify(opts)
|
|
132
142
|
});
|
|
133
143
|
}
|
|
144
|
+
async closeActiveLive() {
|
|
145
|
+
await this.request("/api/v1/agent/live", { method: "DELETE" });
|
|
146
|
+
}
|
|
147
|
+
// -- Per-slug live info ---------------------------------------------------
|
|
134
148
|
async getLive(slug) {
|
|
135
149
|
const data = await this.request(
|
|
136
150
|
`/api/v1/pubs/${encodeURIComponent(slug)}/live`
|
|
137
151
|
);
|
|
138
152
|
return data.live;
|
|
139
153
|
}
|
|
140
|
-
async signal(slug, opts) {
|
|
141
|
-
await this.request(`/api/v1/pubs/${encodeURIComponent(slug)}/live/signal`, {
|
|
142
|
-
method: "PATCH",
|
|
143
|
-
body: JSON.stringify(opts)
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
async closeLive(slug) {
|
|
147
|
-
await this.request(`/api/v1/pubs/${encodeURIComponent(slug)}/live`, {
|
|
148
|
-
method: "DELETE"
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
154
|
};
|
|
152
155
|
|
|
156
|
+
// ../shared/bridge-protocol-core.ts
|
|
157
|
+
var CONTROL_CHANNEL = "_control";
|
|
158
|
+
var CHANNELS = {
|
|
159
|
+
CHAT: "chat",
|
|
160
|
+
CANVAS: "canvas",
|
|
161
|
+
AUDIO: "audio",
|
|
162
|
+
MEDIA: "media",
|
|
163
|
+
FILE: "file"
|
|
164
|
+
};
|
|
165
|
+
var idCounter = 0;
|
|
166
|
+
function generateMessageId() {
|
|
167
|
+
const ts = Date.now().toString(36);
|
|
168
|
+
const seq = (idCounter++).toString(36);
|
|
169
|
+
const rand = Math.random().toString(36).slice(2, 6);
|
|
170
|
+
return `${ts}-${seq}-${rand}`;
|
|
171
|
+
}
|
|
172
|
+
function encodeMessage(msg) {
|
|
173
|
+
return JSON.stringify(msg);
|
|
174
|
+
}
|
|
175
|
+
function decodeMessage(raw) {
|
|
176
|
+
try {
|
|
177
|
+
const parsed = JSON.parse(raw);
|
|
178
|
+
if (parsed && typeof parsed.id === "string" && typeof parsed.type === "string") {
|
|
179
|
+
return parsed;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
} catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function makeEventMessage(event, meta) {
|
|
187
|
+
return { id: generateMessageId(), type: "event", data: event, meta };
|
|
188
|
+
}
|
|
189
|
+
function makeAckMessage(messageId, channel) {
|
|
190
|
+
return makeEventMessage("ack", { messageId, channel, receivedAt: Date.now() });
|
|
191
|
+
}
|
|
192
|
+
function parseAckMessage(msg) {
|
|
193
|
+
if (msg.type !== "event" || msg.data !== "ack" || !msg.meta) return null;
|
|
194
|
+
const messageId = typeof msg.meta.messageId === "string" ? msg.meta.messageId : null;
|
|
195
|
+
const channel = typeof msg.meta.channel === "string" ? msg.meta.channel : null;
|
|
196
|
+
if (!messageId || !channel) return null;
|
|
197
|
+
const receivedAt = typeof msg.meta.receivedAt === "number" ? msg.meta.receivedAt : void 0;
|
|
198
|
+
return { messageId, channel, receivedAt };
|
|
199
|
+
}
|
|
200
|
+
function shouldAcknowledgeMessage(channel, msg) {
|
|
201
|
+
return channel !== CONTROL_CHANNEL && parseAckMessage(msg) === null;
|
|
202
|
+
}
|
|
203
|
+
|
|
153
204
|
// src/commands/tunnel-helpers.ts
|
|
154
|
-
import { fork } from "child_process";
|
|
155
205
|
import * as fs2 from "fs";
|
|
206
|
+
import { homedir as homedir2 } from "os";
|
|
156
207
|
import * as path2 from "path";
|
|
157
208
|
|
|
158
209
|
// src/lib/config.ts
|
|
@@ -215,6 +266,58 @@ function getTelegramMiniAppUrl(slug) {
|
|
|
215
266
|
return `https://t.me/${saved.telegram.botUsername}?startapp=${slug}`;
|
|
216
267
|
}
|
|
217
268
|
|
|
269
|
+
// src/lib/tunnel-ipc.ts
|
|
270
|
+
import * as net from "net";
|
|
271
|
+
function getAgentSocketPath() {
|
|
272
|
+
return "/tmp/pubblue-agent.sock";
|
|
273
|
+
}
|
|
274
|
+
async function ipcCall(socketPath, request) {
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
let settled = false;
|
|
277
|
+
let timeoutId = null;
|
|
278
|
+
const finish = (fn) => {
|
|
279
|
+
if (settled) return;
|
|
280
|
+
settled = true;
|
|
281
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
282
|
+
fn();
|
|
283
|
+
};
|
|
284
|
+
const client = net.createConnection(socketPath, () => {
|
|
285
|
+
client.write(`${JSON.stringify(request)}
|
|
286
|
+
`);
|
|
287
|
+
});
|
|
288
|
+
let data = "";
|
|
289
|
+
client.on("data", (chunk) => {
|
|
290
|
+
data += chunk.toString();
|
|
291
|
+
const newlineIdx = data.indexOf("\n");
|
|
292
|
+
if (newlineIdx !== -1) {
|
|
293
|
+
const line = data.slice(0, newlineIdx);
|
|
294
|
+
client.end();
|
|
295
|
+
try {
|
|
296
|
+
finish(() => resolve(JSON.parse(line)));
|
|
297
|
+
} catch {
|
|
298
|
+
finish(() => reject(new Error("Invalid response from daemon")));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
client.on("error", (err) => {
|
|
303
|
+
if (err.code === "ECONNREFUSED" || err.code === "ENOENT") {
|
|
304
|
+
finish(() => reject(new Error("Daemon not running.")));
|
|
305
|
+
} else {
|
|
306
|
+
finish(() => reject(err));
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
client.on("end", () => {
|
|
310
|
+
if (!data.includes("\n")) {
|
|
311
|
+
finish(() => reject(new Error("Daemon closed connection unexpectedly")));
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
timeoutId = setTimeout(() => {
|
|
315
|
+
client.destroy();
|
|
316
|
+
finish(() => reject(new Error("Daemon request timed out")));
|
|
317
|
+
}, 1e4);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
218
321
|
// src/commands/tunnel-helpers.ts
|
|
219
322
|
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
220
323
|
".txt",
|
|
@@ -280,12 +383,6 @@ function liveInfoPath(slug) {
|
|
|
280
383
|
function liveLogPath(slug) {
|
|
281
384
|
return path2.join(liveInfoDir(), `${slug}.log`);
|
|
282
385
|
}
|
|
283
|
-
function bridgeInfoPath(slug) {
|
|
284
|
-
return path2.join(liveInfoDir(), `${slug}.bridge.json`);
|
|
285
|
-
}
|
|
286
|
-
function bridgeLogPath(slug) {
|
|
287
|
-
return path2.join(liveInfoDir(), `${slug}.bridge.log`);
|
|
288
|
-
}
|
|
289
386
|
function createApiClient(configOverride) {
|
|
290
387
|
const config = configOverride || getConfig();
|
|
291
388
|
return new PubApiClient(config.baseUrl, config.apiKey);
|
|
@@ -293,31 +390,27 @@ function createApiClient(configOverride) {
|
|
|
293
390
|
function buildBridgeProcessEnv(bridgeConfig) {
|
|
294
391
|
const env = { ...process.env };
|
|
295
392
|
const setIfMissing = (key, value) => {
|
|
296
|
-
if (value === void 0
|
|
393
|
+
if (value === void 0) return;
|
|
297
394
|
const current = env[key];
|
|
298
395
|
if (typeof current === "string" && current.length > 0) return;
|
|
299
396
|
env[key] = String(value);
|
|
300
397
|
};
|
|
301
398
|
setIfMissing("PUBBLUE_PROJECT_ROOT", process.cwd());
|
|
399
|
+
setIfMissing("OPENCLAW_HOME", homedir2());
|
|
302
400
|
if (!bridgeConfig) return env;
|
|
303
401
|
setIfMissing("OPENCLAW_PATH", bridgeConfig.openclawPath);
|
|
304
402
|
setIfMissing("OPENCLAW_SESSION_ID", bridgeConfig.sessionId);
|
|
305
403
|
setIfMissing("OPENCLAW_THREAD_ID", bridgeConfig.threadId);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
404
|
+
setIfMissing("OPENCLAW_CANVAS_REMINDER_EVERY", bridgeConfig.canvasReminderEvery);
|
|
405
|
+
setIfMissing(
|
|
406
|
+
"OPENCLAW_DELIVER",
|
|
407
|
+
bridgeConfig.deliver === void 0 ? void 0 : bridgeConfig.deliver ? "1" : "0"
|
|
408
|
+
);
|
|
312
409
|
setIfMissing("OPENCLAW_DELIVER_CHANNEL", bridgeConfig.deliverChannel);
|
|
313
410
|
setIfMissing("OPENCLAW_REPLY_TO", bridgeConfig.replyTo);
|
|
314
|
-
|
|
315
|
-
setIfMissing("OPENCLAW_DELIVER_TIMEOUT_MS", bridgeConfig.deliverTimeoutMs);
|
|
316
|
-
}
|
|
411
|
+
setIfMissing("OPENCLAW_DELIVER_TIMEOUT_MS", bridgeConfig.deliverTimeoutMs);
|
|
317
412
|
setIfMissing("OPENCLAW_ATTACHMENT_DIR", bridgeConfig.attachmentDir);
|
|
318
|
-
|
|
319
|
-
setIfMissing("OPENCLAW_ATTACHMENT_MAX_BYTES", bridgeConfig.attachmentMaxBytes);
|
|
320
|
-
}
|
|
413
|
+
setIfMissing("OPENCLAW_ATTACHMENT_MAX_BYTES", bridgeConfig.attachmentMaxBytes);
|
|
321
414
|
return env;
|
|
322
415
|
}
|
|
323
416
|
async function ensureNodeDatachannelAvailable() {
|
|
@@ -343,7 +436,7 @@ function readDaemonProcessInfo(slug) {
|
|
|
343
436
|
try {
|
|
344
437
|
const info = JSON.parse(fs2.readFileSync(infoPath, "utf-8"));
|
|
345
438
|
if (!Number.isFinite(info.pid)) throw new Error("invalid daemon pid");
|
|
346
|
-
|
|
439
|
+
if (!isProcessAlive(info.pid)) throw new Error("process not alive");
|
|
347
440
|
return info;
|
|
348
441
|
} catch {
|
|
349
442
|
try {
|
|
@@ -353,30 +446,6 @@ function readDaemonProcessInfo(slug) {
|
|
|
353
446
|
return null;
|
|
354
447
|
}
|
|
355
448
|
}
|
|
356
|
-
function readBridgeProcessInfo(slug) {
|
|
357
|
-
const infoPath = bridgeInfoPath(slug);
|
|
358
|
-
if (!fs2.existsSync(infoPath)) return null;
|
|
359
|
-
try {
|
|
360
|
-
return JSON.parse(fs2.readFileSync(infoPath, "utf-8"));
|
|
361
|
-
} catch {
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
function isBridgeRunning(slug) {
|
|
366
|
-
const infoPath = bridgeInfoPath(slug);
|
|
367
|
-
if (!fs2.existsSync(infoPath)) return false;
|
|
368
|
-
try {
|
|
369
|
-
const info = JSON.parse(fs2.readFileSync(infoPath, "utf-8"));
|
|
370
|
-
process.kill(info.pid, 0);
|
|
371
|
-
return true;
|
|
372
|
-
} catch {
|
|
373
|
-
try {
|
|
374
|
-
fs2.unlinkSync(infoPath);
|
|
375
|
-
} catch {
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
449
|
function latestCliVersionPath() {
|
|
381
450
|
return path2.join(liveInfoDir(), "cli-version.txt");
|
|
382
451
|
}
|
|
@@ -413,19 +482,6 @@ async function waitForProcessExit(pid, timeoutMs) {
|
|
|
413
482
|
}
|
|
414
483
|
return !isProcessAlive(pid);
|
|
415
484
|
}
|
|
416
|
-
async function stopBridge(slug) {
|
|
417
|
-
const bridge = readBridgeProcessInfo(slug);
|
|
418
|
-
if (!bridge || !Number.isFinite(bridge.pid)) return null;
|
|
419
|
-
if (!isProcessAlive(bridge.pid)) return null;
|
|
420
|
-
try {
|
|
421
|
-
process.kill(bridge.pid, "SIGTERM");
|
|
422
|
-
} catch (error) {
|
|
423
|
-
return `bridge ${bridge.pid}: failed to send SIGTERM (${error instanceof Error ? error.message : String(error)})`;
|
|
424
|
-
}
|
|
425
|
-
const stopped = await waitForProcessExit(bridge.pid, 6e3);
|
|
426
|
-
if (!stopped) return `bridge ${bridge.pid}: did not exit after SIGTERM`;
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
485
|
async function stopDaemonForLive(info) {
|
|
430
486
|
const pid = info.pid;
|
|
431
487
|
if (!Number.isFinite(pid) || !isProcessAlive(pid)) return null;
|
|
@@ -451,15 +507,12 @@ async function stopDaemonForLive(info) {
|
|
|
451
507
|
if (!stopped) return `daemon ${pid}: did not exit after stop request`;
|
|
452
508
|
return null;
|
|
453
509
|
}
|
|
454
|
-
async function stopOtherDaemons(
|
|
510
|
+
async function stopOtherDaemons() {
|
|
455
511
|
const dir = liveInfoDir();
|
|
456
|
-
const entries = fs2.readdirSync(dir).filter((name) => name.endsWith(".json")
|
|
512
|
+
const entries = fs2.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
457
513
|
const failures = [];
|
|
458
514
|
for (const entry of entries) {
|
|
459
515
|
const slug = entry.replace(/\.json$/, "");
|
|
460
|
-
if (exceptSlug && slug === exceptSlug) continue;
|
|
461
|
-
const bridgeError = await stopBridge(slug);
|
|
462
|
-
if (bridgeError) failures.push(`[${slug}] ${bridgeError}`);
|
|
463
516
|
const info = readDaemonProcessInfo(slug);
|
|
464
517
|
if (!info) continue;
|
|
465
518
|
const daemonError = await stopDaemonForLive(info);
|
|
@@ -468,23 +521,17 @@ async function stopOtherDaemons(exceptSlug) {
|
|
|
468
521
|
if (failures.length > 0) {
|
|
469
522
|
throw new Error(
|
|
470
523
|
[
|
|
471
|
-
"Critical: failed to stop previous live daemon
|
|
524
|
+
"Critical: failed to stop previous live daemon processes.",
|
|
472
525
|
"Starting a new daemon now would leak resources and increase bandwidth usage.",
|
|
473
526
|
...failures
|
|
474
527
|
].join("\n")
|
|
475
528
|
);
|
|
476
529
|
}
|
|
477
530
|
}
|
|
478
|
-
function buildBridgeForkStdio(logFd) {
|
|
479
|
-
return ["ignore", logFd, logFd, "ipc"];
|
|
480
|
-
}
|
|
481
531
|
function getFollowReadDelayMs(disconnected, consecutiveFailures) {
|
|
482
532
|
if (!disconnected) return 1e3;
|
|
483
533
|
return Math.min(5e3, 1e3 * 2 ** Math.min(consecutiveFailures, 3));
|
|
484
534
|
}
|
|
485
|
-
function resolveSlugSelection(slugArg, slugOpt) {
|
|
486
|
-
return slugOpt || slugArg;
|
|
487
|
-
}
|
|
488
535
|
function buildDaemonForkStdio(logFd) {
|
|
489
536
|
return ["ignore", logFd, logFd, "ipc"];
|
|
490
537
|
}
|
|
@@ -502,9 +549,8 @@ function parseBridgeMode(raw) {
|
|
|
502
549
|
}
|
|
503
550
|
throw new Error(`--bridge must be one of: openclaw, none. Received: ${raw}`);
|
|
504
551
|
}
|
|
505
|
-
function
|
|
506
|
-
|
|
507
|
-
return daemonCliVersion.trim() !== currentCliVersion;
|
|
552
|
+
function resolveBridgeMode(opts) {
|
|
553
|
+
return parseBridgeMode(opts.bridge || (opts.foreground ? "none" : "openclaw"));
|
|
508
554
|
}
|
|
509
555
|
function messageContainsPong(payload) {
|
|
510
556
|
if (!payload || typeof payload !== "object") return false;
|
|
@@ -514,14 +560,6 @@ function messageContainsPong(payload) {
|
|
|
514
560
|
const data = message.data;
|
|
515
561
|
return type === "text" && typeof data === "string" && data.trim().toLowerCase() === "pong";
|
|
516
562
|
}
|
|
517
|
-
function getPublicUrl(slug) {
|
|
518
|
-
const base = process.env.PUBBLUE_PUBLIC_URL || "https://pub.blue";
|
|
519
|
-
return `${base.replace(/\/$/, "")}/p/${slug}`;
|
|
520
|
-
}
|
|
521
|
-
function pickReusableLive(pubs, nowMs = Date.now()) {
|
|
522
|
-
const active = pubs.filter((p) => p.live?.status === "active" && p.live.expiresAt > nowMs).sort((a, b) => b.createdAt - a.createdAt);
|
|
523
|
-
return active[0] ?? null;
|
|
524
|
-
}
|
|
525
563
|
function readLogTail(logPath, maxChars = 4e3) {
|
|
526
564
|
if (!fs2.existsSync(logPath)) return null;
|
|
527
565
|
try {
|
|
@@ -541,27 +579,18 @@ function formatApiError(error) {
|
|
|
541
579
|
}
|
|
542
580
|
return error instanceof Error ? error.message : String(error);
|
|
543
581
|
}
|
|
544
|
-
async function cleanupLiveOnStartFailure(apiClient, target) {
|
|
545
|
-
if (!target.createdNew) return;
|
|
546
|
-
try {
|
|
547
|
-
await apiClient.closeLive(target.slug);
|
|
548
|
-
} catch (closeError) {
|
|
549
|
-
console.error(`Failed to clean up live for ${target.slug}: ${formatApiError(closeError)}`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
582
|
async function resolveActiveSlug() {
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
583
|
+
const socketPath = getAgentSocketPath();
|
|
584
|
+
let response;
|
|
585
|
+
try {
|
|
586
|
+
response = await ipcCall(socketPath, { method: "active-slug", params: {} });
|
|
587
|
+
} catch {
|
|
588
|
+
failCli("No active daemon. Run `pubblue start` first.");
|
|
559
589
|
}
|
|
560
|
-
if (
|
|
561
|
-
|
|
590
|
+
if (response.ok && typeof response.slug === "string" && response.slug.length > 0) {
|
|
591
|
+
return response.slug;
|
|
562
592
|
}
|
|
563
|
-
|
|
564
|
-
failCli(`Multiple active lives: ${active.join(", ")}. Specify one with --slug.`);
|
|
593
|
+
failCli("Daemon is running but no live is active. Wait for browser to initiate live.");
|
|
565
594
|
}
|
|
566
595
|
function waitForDaemonReady({
|
|
567
596
|
child,
|
|
@@ -603,113 +632,6 @@ function waitForDaemonReady({
|
|
|
603
632
|
}, timeoutMs);
|
|
604
633
|
});
|
|
605
634
|
}
|
|
606
|
-
async function waitForAgentOffer(params) {
|
|
607
|
-
const startedAt = Date.now();
|
|
608
|
-
let lastError = null;
|
|
609
|
-
while (Date.now() - startedAt < params.timeoutMs) {
|
|
610
|
-
try {
|
|
611
|
-
const session = await params.apiClient.getLive(params.slug);
|
|
612
|
-
if (typeof session.agentOffer === "string" && session.agentOffer.length > 0) {
|
|
613
|
-
return { ok: true };
|
|
614
|
-
}
|
|
615
|
-
} catch (error) {
|
|
616
|
-
lastError = formatApiError(error);
|
|
617
|
-
}
|
|
618
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
619
|
-
}
|
|
620
|
-
return {
|
|
621
|
-
ok: false,
|
|
622
|
-
reason: lastError ? `agent offer was not published in time (last API error: ${lastError})` : "agent offer was not published in time"
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
async function ensureBridgeReady(params) {
|
|
626
|
-
if (params.bridgeMode === "none") {
|
|
627
|
-
return { ok: true };
|
|
628
|
-
}
|
|
629
|
-
const infoPath = bridgeInfoPath(params.slug);
|
|
630
|
-
if (isBridgeRunning(params.slug)) {
|
|
631
|
-
return waitForBridgeReady({
|
|
632
|
-
infoPath,
|
|
633
|
-
slug: params.slug,
|
|
634
|
-
timeoutMs: params.timeoutMs
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
const bridgeScript = path2.join(import.meta.dirname, "tunnel-bridge-entry.js");
|
|
638
|
-
const logPath = bridgeLogPath(params.slug);
|
|
639
|
-
const logFd = fs2.openSync(logPath, "a");
|
|
640
|
-
const child = fork(bridgeScript, [], {
|
|
641
|
-
detached: true,
|
|
642
|
-
stdio: buildBridgeForkStdio(logFd),
|
|
643
|
-
env: {
|
|
644
|
-
...params.bridgeProcessEnv,
|
|
645
|
-
PUBBLUE_BRIDGE_MODE: params.bridgeMode,
|
|
646
|
-
PUBBLUE_BRIDGE_SLUG: params.slug,
|
|
647
|
-
PUBBLUE_BRIDGE_SOCKET: params.socketPath,
|
|
648
|
-
PUBBLUE_BRIDGE_INFO: infoPath
|
|
649
|
-
}
|
|
650
|
-
});
|
|
651
|
-
fs2.closeSync(logFd);
|
|
652
|
-
if (child.connected) {
|
|
653
|
-
child.disconnect();
|
|
654
|
-
}
|
|
655
|
-
child.unref();
|
|
656
|
-
return waitForBridgeReady({
|
|
657
|
-
child,
|
|
658
|
-
infoPath,
|
|
659
|
-
slug: params.slug,
|
|
660
|
-
timeoutMs: params.timeoutMs
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
function waitForBridgeReady({
|
|
664
|
-
child,
|
|
665
|
-
infoPath,
|
|
666
|
-
slug,
|
|
667
|
-
timeoutMs
|
|
668
|
-
}) {
|
|
669
|
-
return new Promise((resolve) => {
|
|
670
|
-
let settled = false;
|
|
671
|
-
let lastState;
|
|
672
|
-
let lastError;
|
|
673
|
-
const done = (result) => {
|
|
674
|
-
if (settled) return;
|
|
675
|
-
settled = true;
|
|
676
|
-
clearInterval(poll);
|
|
677
|
-
clearTimeout(timeout);
|
|
678
|
-
if (child) {
|
|
679
|
-
child.off("exit", onExit);
|
|
680
|
-
}
|
|
681
|
-
resolve(result);
|
|
682
|
-
};
|
|
683
|
-
const onExit = (code, signal) => {
|
|
684
|
-
const suffix = signal ? ` (signal ${signal})` : "";
|
|
685
|
-
done({ ok: false, reason: `bridge exited with code ${code ?? 0}${suffix}` });
|
|
686
|
-
};
|
|
687
|
-
if (child) {
|
|
688
|
-
child.on("exit", onExit);
|
|
689
|
-
}
|
|
690
|
-
const poll = setInterval(() => {
|
|
691
|
-
if (!fs2.existsSync(infoPath)) return;
|
|
692
|
-
const info = readBridgeProcessInfo(slug);
|
|
693
|
-
if (!info) return;
|
|
694
|
-
lastState = info.status;
|
|
695
|
-
lastError = info.lastError;
|
|
696
|
-
if (info.status === "ready" && isBridgeRunning(slug)) {
|
|
697
|
-
done({ ok: true });
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
if (info.status === "error") {
|
|
701
|
-
done({
|
|
702
|
-
ok: false,
|
|
703
|
-
reason: info.lastError ? `bridge reported startup error: ${info.lastError}` : "bridge reported startup error"
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
}, 120);
|
|
707
|
-
const timeout = setTimeout(() => {
|
|
708
|
-
const reason = lastError && lastError.length > 0 ? `timed out after ${timeoutMs}ms waiting for bridge readiness (last error: ${lastError})` : `timed out after ${timeoutMs}ms waiting for bridge readiness (state: ${lastState || "unknown"})`;
|
|
709
|
-
done({ ok: false, reason });
|
|
710
|
-
}, timeoutMs);
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
635
|
|
|
714
636
|
export {
|
|
715
637
|
failCli,
|
|
@@ -720,40 +642,35 @@ export {
|
|
|
720
642
|
getTelegramMiniAppUrl,
|
|
721
643
|
PubApiError,
|
|
722
644
|
PubApiClient,
|
|
645
|
+
CONTROL_CHANNEL,
|
|
646
|
+
CHANNELS,
|
|
647
|
+
generateMessageId,
|
|
648
|
+
encodeMessage,
|
|
649
|
+
decodeMessage,
|
|
650
|
+
makeAckMessage,
|
|
651
|
+
parseAckMessage,
|
|
652
|
+
shouldAcknowledgeMessage,
|
|
653
|
+
getAgentSocketPath,
|
|
654
|
+
ipcCall,
|
|
723
655
|
TEXT_FILE_EXTENSIONS,
|
|
724
656
|
getMimeType,
|
|
725
657
|
liveInfoPath,
|
|
726
658
|
liveLogPath,
|
|
727
|
-
bridgeInfoPath,
|
|
728
|
-
bridgeLogPath,
|
|
729
659
|
createApiClient,
|
|
730
660
|
buildBridgeProcessEnv,
|
|
731
661
|
ensureNodeDatachannelAvailable,
|
|
732
662
|
isDaemonRunning,
|
|
733
|
-
readDaemonProcessInfo,
|
|
734
|
-
readBridgeProcessInfo,
|
|
735
|
-
isBridgeRunning,
|
|
736
663
|
latestCliVersionPath,
|
|
737
664
|
readLatestCliVersion,
|
|
738
665
|
writeLatestCliVersion,
|
|
739
|
-
waitForProcessExit,
|
|
740
|
-
stopBridge,
|
|
741
666
|
stopOtherDaemons,
|
|
742
|
-
buildBridgeForkStdio,
|
|
743
667
|
getFollowReadDelayMs,
|
|
744
|
-
resolveSlugSelection,
|
|
745
668
|
buildDaemonForkStdio,
|
|
746
669
|
parsePositiveIntegerOption,
|
|
747
|
-
|
|
748
|
-
shouldRestartDaemonForCliUpgrade,
|
|
670
|
+
resolveBridgeMode,
|
|
749
671
|
messageContainsPong,
|
|
750
|
-
getPublicUrl,
|
|
751
|
-
pickReusableLive,
|
|
752
672
|
readLogTail,
|
|
753
673
|
formatApiError,
|
|
754
|
-
cleanupLiveOnStartFailure,
|
|
755
674
|
resolveActiveSlug,
|
|
756
|
-
waitForDaemonReady
|
|
757
|
-
waitForAgentOffer,
|
|
758
|
-
ensureBridgeReady
|
|
675
|
+
waitForDaemonReady
|
|
759
676
|
};
|