airloom 0.1.33 → 0.1.35
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/index.js +335 -56
- package/dist/viewer/assets/{browser-C_JHU_N8.js → browser-XJk_KmSg.js} +1 -1
- package/dist/viewer/assets/{index-D0JF99o3.js → index-DiSR_vmz.js} +1 -1
- package/dist/viewer/assets/{index-DwLYzSfq.js → index-RRG33VZY.js} +16 -17
- package/dist/viewer/index.html +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -652,15 +652,28 @@ var AnthropicAdapter = class {
|
|
|
652
652
|
messages: chatMsgs
|
|
653
653
|
};
|
|
654
654
|
if (systemMsg) body.system = systemMsg;
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
655
|
+
const controller = new AbortController();
|
|
656
|
+
const timeout = setTimeout(() => controller.abort(), 6e4);
|
|
657
|
+
let response;
|
|
658
|
+
try {
|
|
659
|
+
response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
660
|
+
method: "POST",
|
|
661
|
+
headers: {
|
|
662
|
+
"Content-Type": "application/json",
|
|
663
|
+
"x-api-key": this.apiKey,
|
|
664
|
+
"anthropic-version": "2023-06-01"
|
|
665
|
+
},
|
|
666
|
+
body: JSON.stringify(body),
|
|
667
|
+
signal: controller.signal
|
|
668
|
+
});
|
|
669
|
+
} catch (err) {
|
|
670
|
+
clearTimeout(timeout);
|
|
671
|
+
const msg = err.name === "AbortError" ? "Request timed out" : err.message;
|
|
672
|
+
stream.write(`[Error: ${msg}]`);
|
|
673
|
+
stream.end();
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
clearTimeout(timeout);
|
|
664
677
|
if (!response.ok) {
|
|
665
678
|
const error = await response.text();
|
|
666
679
|
stream.write(`[Error: ${response.status} ${error}]`);
|
|
@@ -714,14 +727,27 @@ var OpenAIAdapter = class {
|
|
|
714
727
|
this.baseUrl = config.baseUrl || "https://api.openai.com/v1";
|
|
715
728
|
}
|
|
716
729
|
async streamResponse(messages, stream) {
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
730
|
+
const controller = new AbortController();
|
|
731
|
+
const timeout = setTimeout(() => controller.abort(), 6e4);
|
|
732
|
+
let response;
|
|
733
|
+
try {
|
|
734
|
+
response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
735
|
+
method: "POST",
|
|
736
|
+
headers: {
|
|
737
|
+
"Content-Type": "application/json",
|
|
738
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
739
|
+
},
|
|
740
|
+
body: JSON.stringify({ model: this.model, stream: true, messages }),
|
|
741
|
+
signal: controller.signal
|
|
742
|
+
});
|
|
743
|
+
} catch (err) {
|
|
744
|
+
clearTimeout(timeout);
|
|
745
|
+
const msg = err.name === "AbortError" ? "Request timed out" : err.message;
|
|
746
|
+
stream.write(`[Error: ${msg}]`);
|
|
747
|
+
stream.end();
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
clearTimeout(timeout);
|
|
725
751
|
if (!response.ok) {
|
|
726
752
|
const error = await response.text();
|
|
727
753
|
stream.write(`[Error: ${response.status} ${error}]`);
|
|
@@ -850,7 +876,7 @@ function resolveExecutable(command, envPath = process.env.PATH ?? "") {
|
|
|
850
876
|
}
|
|
851
877
|
for (const dir of envPath.split(delimiter)) {
|
|
852
878
|
if (!dir) continue;
|
|
853
|
-
const candidate = join(dir.replace(/^~(?=$|\/)/, process.env.HOME
|
|
879
|
+
const candidate = join(dir.replace(/^~(?=$|\/)/, process.env.HOME || process.env.USERPROFILE || "~"), command);
|
|
854
880
|
if (existsSync(candidate)) return candidate;
|
|
855
881
|
}
|
|
856
882
|
return null;
|
|
@@ -1123,7 +1149,8 @@ function resolveExecutable2(command, envPath = process.env.PATH ?? "") {
|
|
|
1123
1149
|
}
|
|
1124
1150
|
for (const dir of envPath.split(delimiter2)) {
|
|
1125
1151
|
if (!dir) continue;
|
|
1126
|
-
const
|
|
1152
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
1153
|
+
const candidate = join3(dir.replace(/^~(?=$|\/)/, home), command);
|
|
1127
1154
|
if (existsSync2(candidate)) return candidate;
|
|
1128
1155
|
}
|
|
1129
1156
|
return null;
|
|
@@ -1138,14 +1165,16 @@ function parseCommand(command) {
|
|
|
1138
1165
|
function getDefaultTerminalCommand(explicitCommand) {
|
|
1139
1166
|
const configured = explicitCommand?.trim() || process.env.AIRLOOM_TERMINAL_COMMAND?.trim();
|
|
1140
1167
|
if (configured) return parseCommand(configured);
|
|
1168
|
+
const shell = process.env.SHELL;
|
|
1169
|
+
if (shell) {
|
|
1170
|
+
const name = basename(shell);
|
|
1171
|
+
if (name === "bash" || name === "zsh" || name === "sh") return { file: shell, args: ["-il"] };
|
|
1172
|
+
return { file: shell, args: ["-i"] };
|
|
1173
|
+
}
|
|
1141
1174
|
if (process.platform === "win32") {
|
|
1142
|
-
|
|
1143
|
-
return { file, args: [] };
|
|
1175
|
+
return { file: process.env.COMSPEC || "powershell.exe", args: [] };
|
|
1144
1176
|
}
|
|
1145
|
-
|
|
1146
|
-
const name = basename(shell);
|
|
1147
|
-
if (name === "bash" || name === "zsh" || name === "sh") return { file: shell, args: ["-il"] };
|
|
1148
|
-
return { file: shell, args: ["-i"] };
|
|
1177
|
+
return { file: "/bin/bash", args: ["-il"] };
|
|
1149
1178
|
}
|
|
1150
1179
|
var AdaptiveOutputBatcher = class {
|
|
1151
1180
|
constructor(onFlush, fastInterval = 16, slowInterval = 80, interactiveWindow = 250, maxBytes = 4096) {
|
|
@@ -1220,8 +1249,8 @@ var TerminalSession = class {
|
|
|
1220
1249
|
const file = resolveExecutable2(command.file) ?? command.file;
|
|
1221
1250
|
const cwd = process.cwd();
|
|
1222
1251
|
log(`[host] PTY spawn: ${file} ${command.args.join(" ")} (${this.cols}x${this.rows}) node=${process.version}`);
|
|
1223
|
-
const
|
|
1224
|
-
const spawnOpts = { name: "xterm-256color", cols: this.cols, rows: this.rows, cwd, env
|
|
1252
|
+
const env = { ...process.env, TERM: "xterm-256color" };
|
|
1253
|
+
const spawnOpts = { name: "xterm-256color", cols: this.cols, rows: this.rows, cwd, env };
|
|
1225
1254
|
let nodePty;
|
|
1226
1255
|
try {
|
|
1227
1256
|
nodePty = requireNodePty();
|
|
@@ -2238,13 +2267,213 @@ function loadOrCreateAblySessionToken() {
|
|
|
2238
2267
|
return ablySessionToken;
|
|
2239
2268
|
}
|
|
2240
2269
|
|
|
2270
|
+
// src/daemon.ts
|
|
2271
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync, readdirSync, openSync, closeSync } from "node:fs";
|
|
2272
|
+
import { homedir as homedir3 } from "node:os";
|
|
2273
|
+
import { join as join5 } from "node:path";
|
|
2274
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2275
|
+
var SESSIONS_DIR = join5(homedir3(), ".config", "airloom", "sessions");
|
|
2276
|
+
function ensureDir() {
|
|
2277
|
+
mkdirSync3(SESSIONS_DIR, { recursive: true });
|
|
2278
|
+
}
|
|
2279
|
+
function sessionPath(name) {
|
|
2280
|
+
return join5(SESSIONS_DIR, `${name}.json`);
|
|
2281
|
+
}
|
|
2282
|
+
function logFilePath(name) {
|
|
2283
|
+
return join5(SESSIONS_DIR, `${name}.log`);
|
|
2284
|
+
}
|
|
2285
|
+
function validateName(name) {
|
|
2286
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
2287
|
+
throw new Error(`Invalid session name "${name}". Use only letters, numbers, hyphens, and underscores.`);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
function readSession(name) {
|
|
2291
|
+
try {
|
|
2292
|
+
const raw = readFileSync3(sessionPath(name), "utf-8");
|
|
2293
|
+
const data = JSON.parse(raw);
|
|
2294
|
+
if (!data || typeof data.pid !== "number") return null;
|
|
2295
|
+
return data;
|
|
2296
|
+
} catch {
|
|
2297
|
+
return null;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
function writeSession(name, info) {
|
|
2301
|
+
ensureDir();
|
|
2302
|
+
writeFileSync3(sessionPath(name), JSON.stringify(info, null, 2) + "\n", { mode: 384 });
|
|
2303
|
+
}
|
|
2304
|
+
function removeSession(name) {
|
|
2305
|
+
try {
|
|
2306
|
+
unlinkSync(sessionPath(name));
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
function isAlive(pid) {
|
|
2311
|
+
try {
|
|
2312
|
+
process.kill(pid, 0);
|
|
2313
|
+
return true;
|
|
2314
|
+
} catch {
|
|
2315
|
+
return false;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
function listAllSessions() {
|
|
2319
|
+
ensureDir();
|
|
2320
|
+
let files;
|
|
2321
|
+
try {
|
|
2322
|
+
files = readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
2323
|
+
} catch {
|
|
2324
|
+
return [];
|
|
2325
|
+
}
|
|
2326
|
+
const results = [];
|
|
2327
|
+
for (const file of files) {
|
|
2328
|
+
const name = file.replace(/\.json$/, "");
|
|
2329
|
+
const info = readSession(name);
|
|
2330
|
+
if (!info) continue;
|
|
2331
|
+
if (!isAlive(info.pid)) {
|
|
2332
|
+
removeSession(name);
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
results.push({ name, info });
|
|
2336
|
+
}
|
|
2337
|
+
return results;
|
|
2338
|
+
}
|
|
2339
|
+
function formatAge(ms) {
|
|
2340
|
+
const sec = Math.floor(ms / 1e3);
|
|
2341
|
+
if (sec < 60) return `${sec}s ago`;
|
|
2342
|
+
const min = Math.floor(sec / 60);
|
|
2343
|
+
if (min < 60) return `${min}m ago`;
|
|
2344
|
+
const hr = Math.floor(min / 60);
|
|
2345
|
+
if (hr < 24) return `${hr}h ${min % 60}m ago`;
|
|
2346
|
+
return `${Math.floor(hr / 24)}d ago`;
|
|
2347
|
+
}
|
|
2348
|
+
async function handleStart(name, hostArgs) {
|
|
2349
|
+
validateName(name);
|
|
2350
|
+
const existing = readSession(name);
|
|
2351
|
+
if (existing && isAlive(existing.pid)) {
|
|
2352
|
+
console.error(`Session "${name}" is already running (PID ${existing.pid}, port ${existing.port})`);
|
|
2353
|
+
console.error(`Host UI: ${existing.controlUrl}`);
|
|
2354
|
+
process.exit(1);
|
|
2355
|
+
}
|
|
2356
|
+
if (existing) removeSession(name);
|
|
2357
|
+
ensureDir();
|
|
2358
|
+
const logFile = logFilePath(name);
|
|
2359
|
+
const logFd = openSync(logFile, "w");
|
|
2360
|
+
const child = spawn2(
|
|
2361
|
+
process.execPath,
|
|
2362
|
+
[...process.execArgv, process.argv[1], ...hostArgs, "--_daemon"],
|
|
2363
|
+
{
|
|
2364
|
+
detached: true,
|
|
2365
|
+
stdio: ["ignore", logFd, logFd, "ipc"],
|
|
2366
|
+
cwd: process.cwd(),
|
|
2367
|
+
env: process.env
|
|
2368
|
+
}
|
|
2369
|
+
);
|
|
2370
|
+
try {
|
|
2371
|
+
const info = await new Promise((resolve4, reject) => {
|
|
2372
|
+
const timer = setTimeout(() => {
|
|
2373
|
+
reject(new Error(`Timed out waiting for daemon to start (30s). Check log: ${logFile}`));
|
|
2374
|
+
}, 3e4);
|
|
2375
|
+
child.on("message", (msg) => {
|
|
2376
|
+
const m = msg;
|
|
2377
|
+
if (m.type === "ready") {
|
|
2378
|
+
clearTimeout(timer);
|
|
2379
|
+
resolve4({
|
|
2380
|
+
pid: child.pid,
|
|
2381
|
+
port: m.port,
|
|
2382
|
+
controlUrl: m.controlUrl,
|
|
2383
|
+
viewerUrl: m.viewerUrl,
|
|
2384
|
+
pairingCode: m.pairingCode,
|
|
2385
|
+
cwd: process.cwd(),
|
|
2386
|
+
startedAt: Date.now(),
|
|
2387
|
+
logFile
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
});
|
|
2391
|
+
child.on("error", (err) => {
|
|
2392
|
+
clearTimeout(timer);
|
|
2393
|
+
reject(err);
|
|
2394
|
+
});
|
|
2395
|
+
child.on("exit", (code) => {
|
|
2396
|
+
clearTimeout(timer);
|
|
2397
|
+
reject(new Error(`Daemon exited with code ${code}. Check log: ${logFile}`));
|
|
2398
|
+
});
|
|
2399
|
+
});
|
|
2400
|
+
writeSession(name, info);
|
|
2401
|
+
child.disconnect();
|
|
2402
|
+
child.unref();
|
|
2403
|
+
closeSync(logFd);
|
|
2404
|
+
console.log(`
|
|
2405
|
+
Airloom session "${name}" started (PID ${info.pid})
|
|
2406
|
+
`);
|
|
2407
|
+
console.log(`Pairing Code: ${info.pairingCode}`);
|
|
2408
|
+
console.log(`Viewer URL: ${info.viewerUrl}`);
|
|
2409
|
+
console.log(`Host UI: ${info.controlUrl}`);
|
|
2410
|
+
console.log(`Log: ${info.logFile}
|
|
2411
|
+
`);
|
|
2412
|
+
} catch (err) {
|
|
2413
|
+
closeSync(logFd);
|
|
2414
|
+
try {
|
|
2415
|
+
child.kill();
|
|
2416
|
+
} catch {
|
|
2417
|
+
}
|
|
2418
|
+
throw err;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
function handleStop(nameOrNull, all) {
|
|
2422
|
+
if (all) {
|
|
2423
|
+
const sessions = listAllSessions();
|
|
2424
|
+
if (sessions.length === 0) {
|
|
2425
|
+
console.log("No running sessions.");
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
for (const { name, info: info2 } of sessions) {
|
|
2429
|
+
try {
|
|
2430
|
+
process.kill(info2.pid, "SIGTERM");
|
|
2431
|
+
} catch {
|
|
2432
|
+
}
|
|
2433
|
+
removeSession(name);
|
|
2434
|
+
console.log(`Stopped "${name}" (PID ${info2.pid})`);
|
|
2435
|
+
}
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
const target = nameOrNull || "default";
|
|
2439
|
+
const info = readSession(target);
|
|
2440
|
+
if (!info) {
|
|
2441
|
+
console.error(`No session named "${target}" found.`);
|
|
2442
|
+
process.exit(1);
|
|
2443
|
+
}
|
|
2444
|
+
if (!isAlive(info.pid)) {
|
|
2445
|
+
removeSession(target);
|
|
2446
|
+
console.log(`Session "${target}" was not running (cleaned up stale entry).`);
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
try {
|
|
2450
|
+
process.kill(info.pid, "SIGTERM");
|
|
2451
|
+
} catch {
|
|
2452
|
+
}
|
|
2453
|
+
removeSession(target);
|
|
2454
|
+
console.log(`Stopped "${target}" (PID ${info.pid})`);
|
|
2455
|
+
}
|
|
2456
|
+
function handleList() {
|
|
2457
|
+
const sessions = listAllSessions();
|
|
2458
|
+
if (sessions.length === 0) {
|
|
2459
|
+
console.log("No running sessions.");
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
console.log("NAME PORT PID STARTED CWD");
|
|
2463
|
+
for (const { name, info } of sessions) {
|
|
2464
|
+
const age = formatAge(Date.now() - info.startedAt);
|
|
2465
|
+
console.log(
|
|
2466
|
+
name.padEnd(16) + String(info.port).padEnd(7) + String(info.pid).padEnd(9) + age.padEnd(17) + info.cwd
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2241
2471
|
// src/index.ts
|
|
2242
2472
|
var QRCode = null;
|
|
2243
2473
|
async function getQRCode() {
|
|
2244
2474
|
if (!QRCode) QRCode = await import("qrcode");
|
|
2245
2475
|
return QRCode;
|
|
2246
2476
|
}
|
|
2247
|
-
log("[host] Module loaded");
|
|
2248
2477
|
function parseArgs(argv) {
|
|
2249
2478
|
const args = {};
|
|
2250
2479
|
const rest = argv.slice(2);
|
|
@@ -2274,7 +2503,15 @@ function printHelp() {
|
|
|
2274
2503
|
Airloom \u2014 Run AI on your computer, control it from your phone.
|
|
2275
2504
|
|
|
2276
2505
|
Usage:
|
|
2277
|
-
airloom [options]
|
|
2506
|
+
airloom [options] Start in foreground (default)
|
|
2507
|
+
airloom start [options] Start as a background daemon
|
|
2508
|
+
airloom stop [name] Stop a background session
|
|
2509
|
+
airloom stop --all Stop all background sessions
|
|
2510
|
+
airloom list List running background sessions
|
|
2511
|
+
|
|
2512
|
+
Background options:
|
|
2513
|
+
--name <name> Session name (default: "default").
|
|
2514
|
+
Allows multiple independent sessions.
|
|
2278
2515
|
|
|
2279
2516
|
Options:
|
|
2280
2517
|
--cli <command> CLI command to use as the AI adapter.
|
|
@@ -2299,22 +2536,6 @@ Environment variables:
|
|
|
2299
2536
|
HOST_BIND Host bind address (default: 127.0.0.1).
|
|
2300
2537
|
`.trimStart());
|
|
2301
2538
|
}
|
|
2302
|
-
var cliArgs = parseArgs(process.argv);
|
|
2303
|
-
if (cliArgs.help) {
|
|
2304
|
-
printHelp();
|
|
2305
|
-
process.exit(0);
|
|
2306
|
-
}
|
|
2307
|
-
var IS_DEV = !process.env.VIEWER_URL && !new URL(import.meta.url).pathname.includes("node_modules");
|
|
2308
|
-
var env = parseHostEnv(cliArgs.port, IS_DEV);
|
|
2309
|
-
var VIEWER_URL = env.viewerUrl;
|
|
2310
|
-
var RELAY_URL = env.relayUrl;
|
|
2311
|
-
var ABLY_API_KEY = env.ablyApiKey;
|
|
2312
|
-
var ABLY_TOKEN_TTL = env.ablyTokenTtlMs;
|
|
2313
|
-
var HOST_PORT = env.hostPort;
|
|
2314
|
-
var HOST_BIND = env.hostBind;
|
|
2315
|
-
var useAbly = env.useAbly;
|
|
2316
|
-
var isDefaultKey = env.isDefaultAblyKey;
|
|
2317
|
-
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
2318
2539
|
function getLanIP() {
|
|
2319
2540
|
for (const ifaces of Object.values(networkInterfaces())) {
|
|
2320
2541
|
for (const iface of ifaces ?? []) {
|
|
@@ -2323,14 +2544,31 @@ function getLanIP() {
|
|
|
2323
2544
|
}
|
|
2324
2545
|
return void 0;
|
|
2325
2546
|
}
|
|
2326
|
-
function resolveViewerDir() {
|
|
2327
|
-
const prod = resolve3(
|
|
2547
|
+
function resolveViewerDir(base) {
|
|
2548
|
+
const prod = resolve3(base, "viewer");
|
|
2328
2549
|
if (existsSync4(prod)) return prod;
|
|
2329
|
-
const dev = resolve3(
|
|
2550
|
+
const dev = resolve3(base, "../../viewer/dist");
|
|
2330
2551
|
if (existsSync4(dev)) return dev;
|
|
2331
2552
|
return void 0;
|
|
2332
2553
|
}
|
|
2333
2554
|
async function main() {
|
|
2555
|
+
const cliArgs = parseArgs(process.argv);
|
|
2556
|
+
if (cliArgs.help) {
|
|
2557
|
+
printHelp();
|
|
2558
|
+
process.exit(0);
|
|
2559
|
+
}
|
|
2560
|
+
const IS_DEV = !process.env.VIEWER_URL && !new URL(import.meta.url).pathname.includes("node_modules");
|
|
2561
|
+
const env = parseHostEnv(cliArgs.port, IS_DEV);
|
|
2562
|
+
const VIEWER_URL = env.viewerUrl;
|
|
2563
|
+
const RELAY_URL = env.relayUrl;
|
|
2564
|
+
const ABLY_API_KEY = env.ablyApiKey;
|
|
2565
|
+
const ABLY_TOKEN_TTL = env.ablyTokenTtlMs;
|
|
2566
|
+
const HOST_PORT = env.hostPort;
|
|
2567
|
+
const HOST_BIND = env.hostBind;
|
|
2568
|
+
const useAbly = env.useAbly;
|
|
2569
|
+
const isDefaultKey = env.isDefaultAblyKey;
|
|
2570
|
+
const isDaemonChild = process.argv.includes("--_daemon");
|
|
2571
|
+
const __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
2334
2572
|
console.log("Airloom - Host");
|
|
2335
2573
|
console.log("==============\n");
|
|
2336
2574
|
if (useAbly) {
|
|
@@ -2482,7 +2720,7 @@ async function main() {
|
|
|
2482
2720
|
}
|
|
2483
2721
|
}
|
|
2484
2722
|
}
|
|
2485
|
-
const viewerDir = resolveViewerDir();
|
|
2723
|
+
const viewerDir = resolveViewerDir(__dirname);
|
|
2486
2724
|
if (viewerDir) {
|
|
2487
2725
|
log(`[host] Viewer files: ${viewerDir}`);
|
|
2488
2726
|
} else {
|
|
@@ -2522,16 +2760,20 @@ async function main() {
|
|
|
2522
2760
|
const controlBase = isLoopbackBind(HOST_BIND) ? `http://localhost:${port}` : lanBaseUrl;
|
|
2523
2761
|
const controlUrl = encodeControlUrl(controlBase, controlToken);
|
|
2524
2762
|
console.log(`Host UI: ${controlUrl}`);
|
|
2525
|
-
if (
|
|
2763
|
+
if (isDaemonChild && typeof process.send === "function") {
|
|
2764
|
+
process.send({ type: "ready", port, controlUrl, viewerUrl: qrTarget, pairingCode: displayCode });
|
|
2765
|
+
}
|
|
2766
|
+
if (isDaemonChild) {
|
|
2767
|
+
} else if (env.isSSH) {
|
|
2526
2768
|
if (isLoopbackBind(HOST_BIND)) {
|
|
2527
2769
|
console.log("\n (SSH session detected but server is bound to localhost \u2014 set HOST_BIND=0.0.0.0 to allow remote access)");
|
|
2528
2770
|
} else {
|
|
2529
2771
|
console.log("\n (SSH session \u2014 open the Host UI URL above in a browser on your local machine)");
|
|
2530
2772
|
}
|
|
2531
2773
|
} else {
|
|
2532
|
-
import("node:child_process").then(({
|
|
2774
|
+
import("node:child_process").then(({ execFile }) => {
|
|
2533
2775
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2534
|
-
|
|
2776
|
+
execFile(cmd, [controlUrl]);
|
|
2535
2777
|
}).catch(() => {
|
|
2536
2778
|
});
|
|
2537
2779
|
}
|
|
@@ -2598,7 +2840,44 @@ async function main() {
|
|
|
2598
2840
|
process.on("SIGINT", shutdown);
|
|
2599
2841
|
process.on("SIGTERM", shutdown);
|
|
2600
2842
|
}
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2843
|
+
var _cmd = process.argv[2];
|
|
2844
|
+
if (_cmd === "start" || _cmd === "stop" || _cmd === "list") {
|
|
2845
|
+
(async () => {
|
|
2846
|
+
const _args = process.argv.slice(3);
|
|
2847
|
+
if (_cmd === "list") {
|
|
2848
|
+
handleList();
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
if (_cmd === "stop") {
|
|
2852
|
+
let stopName = null;
|
|
2853
|
+
let stopAll = false;
|
|
2854
|
+
for (const a of _args) {
|
|
2855
|
+
if (a === "--all") stopAll = true;
|
|
2856
|
+
else if (!a.startsWith("-")) stopName = a;
|
|
2857
|
+
}
|
|
2858
|
+
handleStop(stopName, stopAll);
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
let startName = "default";
|
|
2862
|
+
const hostArgs = [];
|
|
2863
|
+
for (let i = 0; i < _args.length; i++) {
|
|
2864
|
+
const a = _args[i];
|
|
2865
|
+
if (a === "--name" && i + 1 < _args.length) {
|
|
2866
|
+
startName = _args[++i];
|
|
2867
|
+
} else if (a.startsWith("--name=")) {
|
|
2868
|
+
startName = a.slice(7);
|
|
2869
|
+
} else {
|
|
2870
|
+
hostArgs.push(a);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
await handleStart(startName, hostArgs);
|
|
2874
|
+
})().catch((err) => {
|
|
2875
|
+
console.error(err.message);
|
|
2876
|
+
process.exit(1);
|
|
2877
|
+
});
|
|
2878
|
+
} else {
|
|
2879
|
+
main().catch((err) => {
|
|
2880
|
+
logError("Fatal error:", err);
|
|
2881
|
+
process.exit(1);
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as c}from"./index-
|
|
1
|
+
import{g as c}from"./index-RRG33VZY.js";function f(t,i){for(var o=0;o<i.length;o++){const e=i[o];if(typeof e!="string"&&!Array.isArray(e)){for(const r in e)if(r!=="default"&&!(r in t)){const s=Object.getOwnPropertyDescriptor(e,r);s&&Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:()=>e[r]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}var n,a;function b(){return a||(a=1,n=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}),n}var u=b();const w=c(u),p=f({__proto__:null,default:w},[u]);export{p as b};
|