mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 13:52:09 +03:00
* chore(release): v3.7.4 — version bump, openapi and changelog sync * fix: preserve previous_response_id and conversation_id fields on empty input array (#1729) * fix: bypass UI validation block for optional API keys and fix string fallback typing (#1721) * fix(proxy): disable HTTP keep-alive and pipelining in Undici proxy dispatcher to prevent socket hang up * feat(proxy): implement bulk proxy import via pipe-delimited parser with update-or-create logic * docs: update changelog for v3.7.4 fixes and proxy features * test: update responses store expectations for empty input arrays * feat(pwa): add fullscreen installable PWA with manifest, service worker, and cross-platform app icons. (#1728) Integrated into release/v3.7.4 * Fix image provider validation and Stability image requests (#1726) Integrated into release/v3.7.4 * docs: add PR 1726 and PR 1728 to v3.7.4 changelog * fix(security): replace insecure Math.random with crypto.getRandomValues for fallback UUID generation * fix(migrations): intercept 007 migration to use IF NOT EXISTS logic on fresh installs Fixes #1733 * test: fix typescript compilation errors in unit tests * fix(db): reconcile legacy reasoning cache migration * chore(release): bump to v3.7.4 — changelog, docs, version sync * fix(cc-compatible): preserve Claude Code system skeleton (#1740) Integrated into release/v3.7.4 * docs(changelog): update for PR #1740 merge * docs(changelog): include workflow updates * fix(db): reconcile legacy reasoning cache migration (#1734) Integrated into release/v3.7.4 * Add endpoint tunnel visibility settings (#1743) Integrated into release/v3.7.4 * Normalize max reasoning effort for Codex routing (#1744) Integrated into release/v3.7.4 * Fix Claude Code gateway config helper (#1745) Integrated into release/v3.7.4 * Refresh CLI fingerprint provider profiles (#1746) Integrated into release/v3.7.4 * Integrated into release/v3.7.4 (PR #1742) * docs(changelog): update for PRs 1742-1746 --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Yash Ghule <y.ghule77@gmail.com> Co-authored-by: backryun <bakryun0718@proton.me> Co-authored-by: dhaern <manker_lol@hotmail.com> Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Duncan L <leungd@gmail.com>
187 lines
5.7 KiB
TypeScript
187 lines
5.7 KiB
TypeScript
import { execFileSync, execSync } from "child_process";
|
|
import { existsSync, readFileSync } from "fs";
|
|
|
|
/**
|
|
* Get raw machine ID using OS-specific methods.
|
|
*
|
|
* We use try/catch waterfall: try each OS method and fall through
|
|
* to the next on failure. Platform checks are INSIDE try blocks so they
|
|
* run at RUNTIME (not build time), avoiding Next.js SWC dead-code elimination.
|
|
*
|
|
* On Linux: skips Windows (REG.exe) and macOS (ioreg) strategies entirely.
|
|
*/
|
|
function getMachineIdRaw(): string {
|
|
// Strategy 1: Windows — REG.exe query for MachineGuid
|
|
try {
|
|
if (process.platform !== "win32") {
|
|
throw new Error("Not Windows");
|
|
}
|
|
const sysRoot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
|
|
const regPath = `${sysRoot}\\System32\\REG.exe`;
|
|
if (existsSync(/* turbopackIgnore: true */ regPath)) {
|
|
const output = execFileSync(
|
|
regPath,
|
|
["QUERY", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
{ encoding: "utf8", timeout: 5000 }
|
|
);
|
|
const id = output
|
|
.split("REG_SZ")[1]
|
|
?.replace(/\r+|\n+|\s+/gi, "")
|
|
?.toLowerCase();
|
|
if (id && id.length > 8) return id;
|
|
}
|
|
} catch {
|
|
// Not Windows or REG.exe failed — continue
|
|
}
|
|
|
|
// Strategy 2: macOS — ioreg IOPlatformUUID
|
|
try {
|
|
if (process.platform !== "darwin") {
|
|
throw new Error("Not macOS");
|
|
}
|
|
const output = execSync("ioreg -rd1 -c IOPlatformExpertDevice", {
|
|
encoding: "utf8",
|
|
timeout: 5000,
|
|
});
|
|
if (output.includes("IOPlatformUUID")) {
|
|
const id = output
|
|
.split("IOPlatformUUID")[1]
|
|
?.split("\n")[0]
|
|
?.replace(/=|\s+|"/gi, "")
|
|
?.toLowerCase();
|
|
if (id && id.length > 8) return id;
|
|
}
|
|
} catch {
|
|
// Not macOS or ioreg not available — continue
|
|
}
|
|
|
|
// Strategy 3: Linux — read machine-id files directly (no `head` or pipe)
|
|
try {
|
|
for (const filePath of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
try {
|
|
const content = readFileSync(/* turbopackIgnore: true */ filePath, "utf8")
|
|
.trim()
|
|
.toLowerCase();
|
|
if (content.length > 8) return content;
|
|
} catch {
|
|
// Try the next candidate file
|
|
}
|
|
}
|
|
} catch {
|
|
// Files not readable — continue
|
|
}
|
|
|
|
// Strategy 4: Hostname fallback (works on all platforms)
|
|
try {
|
|
const hostname = execSync("hostname", { encoding: "utf8", timeout: 5000 });
|
|
const id = hostname.trim().toLowerCase();
|
|
if (id) return id;
|
|
} catch {
|
|
// hostname failed — continue
|
|
}
|
|
|
|
// Strategy 5: Node.js os.hostname() (no exec needed)
|
|
try {
|
|
const os = require("os");
|
|
return os.hostname().toLowerCase();
|
|
} catch {
|
|
// Final fallback
|
|
}
|
|
|
|
return "unknown-machine";
|
|
}
|
|
|
|
/**
|
|
* Get consistent machine ID using native registry/OS query with salt
|
|
* This ensures the same physical machine gets the same ID across runs
|
|
*
|
|
* @param {string} salt - Optional salt to use (defaults to environment variable)
|
|
* @returns {Promise<string>} Machine ID (16-character base32)
|
|
*/
|
|
export async function getConsistentMachineId(salt = null) {
|
|
const saltValue = salt || process.env.MACHINE_ID_SALT || "endpoint-proxy-salt";
|
|
try {
|
|
const rawMachineId = getMachineIdRaw();
|
|
// Create consistent ID using salt
|
|
const crypto = await import("crypto");
|
|
const hashedMachineId = crypto
|
|
.createHash("sha256")
|
|
.update(rawMachineId + saltValue)
|
|
.digest("hex");
|
|
// Return only first 16 characters for brevity
|
|
return hashedMachineId.substring(0, 16);
|
|
} catch (error) {
|
|
console.log("Error getting machine ID:", error);
|
|
// Fallback to random ID if node-machine-id fails
|
|
try {
|
|
const cryptoFallback = await import("crypto");
|
|
return cryptoFallback.randomUUID();
|
|
} catch {
|
|
if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.randomUUID) {
|
|
return globalThis.crypto.randomUUID();
|
|
}
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
let r = 0;
|
|
if (
|
|
typeof globalThis !== "undefined" &&
|
|
globalThis.crypto &&
|
|
globalThis.crypto.getRandomValues
|
|
) {
|
|
const arr = new Uint8Array(1);
|
|
globalThis.crypto.getRandomValues(arr);
|
|
r = arr[0] % 16;
|
|
} else {
|
|
r = (Date.now() % 16) | 0;
|
|
}
|
|
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get raw machine ID without hashing (for debugging purposes)
|
|
* @returns {Promise<string>} Raw machine ID
|
|
*/
|
|
export async function getRawMachineId() {
|
|
try {
|
|
return getMachineIdRaw();
|
|
} catch (error) {
|
|
console.log("Error getting raw machine ID:", error);
|
|
// Fallback to random ID if node-machine-id fails
|
|
try {
|
|
const cryptoFallback = await import("crypto");
|
|
return cryptoFallback.randomUUID();
|
|
} catch {
|
|
if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.randomUUID) {
|
|
return globalThis.crypto.randomUUID();
|
|
}
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
let r = 0;
|
|
if (
|
|
typeof globalThis !== "undefined" &&
|
|
globalThis.crypto &&
|
|
globalThis.crypto.getRandomValues
|
|
) {
|
|
const arr = new Uint8Array(1);
|
|
globalThis.crypto.getRandomValues(arr);
|
|
r = arr[0] % 16;
|
|
} else {
|
|
r = (Date.now() % 16) | 0;
|
|
}
|
|
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if we're running in browser or server environment
|
|
* @returns {boolean} True if in browser, false if in server
|
|
*/
|
|
export function isBrowser() {
|
|
return typeof window !== "undefined";
|
|
}
|