@deskthing/cli
Version:
An emulator for the DeskThing Server
1,213 lines (1,180 loc) • 40.9 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// package.json
var require_package = __commonJS({
"package.json"(exports, module) {
module.exports = {
name: "@deskthing/cli",
version: "0.11.12",
description: "An emulator for the DeskThing Server",
keywords: [
"Emulator",
"DeskThing",
"Development",
"App",
"Development",
"DeskThing",
"Development"
],
exports: {
".": {
import: "./dist/index.js",
types: "./dist/index.d.ts"
}
},
homepage: "https://github.com/itsriprod/deskthing-emulator#readme",
bugs: {
url: "https://github.com/itsriprod/deskthing-emulator/issues"
},
repository: {
type: "git",
url: "git+https://github.com/itsriprod/deskthing-emulator.git"
},
license: "ISC",
author: "Riprod",
type: "module",
main: "./dist/index.js",
bin: {
deskthing: "dist/cli.js"
},
scripts: {
build: "npm run build:emulator-client && npm run build:emulator && npm run build:cli && npm run build:emulator-thread && npm run build:index",
"build:cli": "esbuild src/cli.ts --bundle --platform=node --outdir=dist --format=esm --packages=external",
"build:emulator": "esbuild src/emulator/index.ts --bundle --platform=node --outdir=dist/emulator --format=esm --external:ws",
"build:emulator-thread": "esbuild src/emulator/server/serverProcess.ts --bundle --platform=node --outdir=dist/emulator --format=esm --external:ts-node",
"build:emulator-client": "vite build",
dev: "vite",
"build:index": "esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --packages=external && tsc src/index.ts --declaration --emitDeclarationOnly --outDir dist --skipLibCheck --isolatedModules --module ESNext",
typecheck: "tsc --noEmit"
},
dependencies: {
"@tailwindcss/vite": "^4.1.10",
"@vitejs/plugin-legacy": "^6.0.2",
react: "^19.0.0",
"react-dom": "^19.0.0",
tailwindcss: "^4.1.10",
vite: "^6.2.2",
ws: "^8.18.0",
yargs: "^17.7.2",
"zip-lib": "^1.0.5",
zustand: "^5.0.5"
},
devDependencies: {
"@deskthing/types": "^0.11.16",
"@types/node": "^22.13.17",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@types/ws": "^8.5.14",
"@types/yargs": "^17.0.33",
"@vitejs/plugin-react": "^4.3.4",
esbuild: "^0.25.1",
"import-meta-resolve": "^4.1.0",
typescript: "^5.7.3"
},
peerDependencies: {
tsm: "^2.3.0"
}
};
}
});
// src/cli.ts
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { fileURLToPath } from "url";
import { dirname, join as join9 } from "path";
import { execSync } from "child_process";
// src/package/package.ts
import { join as join2, resolve } from "path";
import zl from "zip-lib";
import { readdir, stat, cp, rm, mkdir } from "fs/promises";
// src/package/config.ts
import { join } from "path";
import { readFileSync } from "fs";
var loadConfigs = () => {
const packageJsonPath = join(process.cwd(), "package.json");
const manifestJsonPath = join(process.cwd(), "deskthing/manifest.json");
let packageJson;
let manifestJson;
try {
packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
} catch (error) {
throw new Error(`\x1B[31mFailed to load package.json: ${error.message}\x1B[0m`);
}
try {
manifestJson = JSON.parse(readFileSync(manifestJsonPath, "utf8"));
} catch (error) {
try {
console.log("\x1B[33m\u274C Failed to load manifest.json from deskthing/manifest.json, trying public/manifest.json\x1B[0m");
const oldmanifestJsonPath = join(process.cwd(), "public/manifest.json");
manifestJson = JSON.parse(readFileSync(oldmanifestJsonPath, "utf8"));
console.log("\x1B[32m\u2705 Successfully loaded manifest.json from public/manifest.json\x1B[0m");
} catch (err2) {
throw new Error(`\x1B[31mFailed to load manifest.json from both locations: ${error.message}, ${err2.message}\x1B[0m`);
}
}
return {
packageJson,
manifestJson
};
};
// src/package/package.ts
import { build as buildEsbuild } from "esbuild";
import { build as buildVite } from "vite";
import viteLegacyPlugin from "@vitejs/plugin-legacy";
import { exec } from "child_process";
async function buildServer() {
await buildEsbuild({
entryPoints: ["server/index.ts"],
bundle: true,
platform: "node",
outfile: "dist/server/index.js",
target: "ESNext",
format: "esm",
external: ["node:*", "fs", "path", "child_process"],
resolveExtensions: [".ts", ".js"],
sourcemap: true,
banner: {
js: `
// ESM shims for Node.js built-in modules
import { createRequire as DeskThingCreateRequire } from 'module';
import { fileURLToPath as DeskThingFileURLToPath } from 'url';
import { dirname as DeskThingDirname } from 'node:path';
const require = DeskThingCreateRequire(import.meta.url);
const __filename = DeskThingFileURLToPath(import.meta.url);
const __dirname = DeskThingDirname(__filename);
`
}
});
}
async function buildWorkers() {
try {
await stat("server/workers");
} catch (e) {
console.warn("\x1B[35mUnable to find workers file\x1B[0m");
console.warn("\x1B[90m(Can be ignored if you do not have workers)\x1B[0m");
return;
}
try {
const workersDir = "server/workers";
const files = await readdir(workersDir);
const tsFiles = files.filter((file) => file.endsWith(".ts")).map((file) => join2(workersDir, file));
if (tsFiles.length === 0) {
console.warn("\x1B[35mNo TypeScript files found in workers directory\x1B[0m");
return;
}
await buildEsbuild({
entryPoints: tsFiles,
bundle: true,
platform: "node",
outdir: "dist/server/workers",
target: "ESNext",
format: "esm",
resolveExtensions: [".ts", ".js"],
sourcemap: true,
banner: {
js: `
// ESM shims for Node.js built-in modules
import { createRequire as DeskThingCreateRequire } from 'module';
import { fileURLToPath as DeskThingFileURLToPath } from 'url';
import { dirname as DeskThingDirname } from 'node:path';
const require = DeskThingCreateRequire(import.meta.url);
const __filename = DeskThingFileURLToPath(import.meta.url);
const __dirname = DeskThingDirname(__filename);
`
}
});
} catch (error) {
console.error("\x1B[31mError building workers:\x1B[0m", error);
}
}
var buildPostinstall = async () => {
try {
await stat("postinstall");
} catch (e) {
console.warn("\x1B[35mUnable to find postinstall file\x1B[0m");
console.warn("\x1B[90m(Can be ignored if you do not have a postinstall script)\x1B[0m");
return;
}
try {
await buildEsbuild({
entryPoints: ["postinstall/index.ts"],
bundle: true,
platform: "node",
target: "ESNext",
format: "esm",
resolveExtensions: [".ts", ".js"],
sourcemap: false,
outfile: "dist/postinstall.mjs",
// ensure that it is a module
minify: true
});
} catch (error) {
console.error("\x1B[31mError building postinstall:\x1B[0m", error);
}
};
async function buildClient() {
await buildVite({
configFile: "vite.config.ts",
base: "./",
plugins: [viteLegacyPlugin({
targets: ["Chrome 69"]
})],
build: {
outDir: "dist/client",
rollupOptions: {
output: {
assetFileNames: "[name]-[hash][extname]",
chunkFileNames: "[name]-[hash].js",
entryFileNames: "[name]-[hash].js"
}
}
}
});
}
async function copyDeskThing() {
const deskthingPath = resolve("deskthing");
const publicPath = resolve("public");
const distFile = resolve("dist");
const manifestFile = join2(deskthingPath, "manifest.json");
const oldmanifestFile = join2(publicPath, "manifest.json");
if (await stat(manifestFile).catch(() => false)) {
await cp(deskthingPath, join2(distFile), { recursive: true });
} else if (await stat(oldmanifestFile).catch(() => false)) {
console.log(
"Using old manifest.json. Please move this to /deskthing or run `deskthing update`"
);
await cp(publicPath, join2(distFile), { recursive: true });
} else {
throw new Error("No manifest.json found in either /deskthing or /public");
}
}
async function addFilesToArchive(archive, folderPath, baseFolder = "") {
const exists = await stat(folderPath).catch(() => false);
if (!exists) return;
const files = await readdir(folderPath);
for (const file of files) {
const filePath = join2(folderPath, file);
const stats = await stat(filePath);
if (stats.isDirectory()) {
await addFilesToArchive(archive, filePath, join2(baseFolder, file));
} else {
await archive.addFile(filePath, join2(baseFolder, file));
}
}
}
async function createPackage() {
const { packageJson, manifestJson } = loadConfigs();
const packageName = packageJson.name;
const version = manifestJson.version || packageJson.version;
const distPath = resolve("dist");
const outputFile = join2(distPath, `${packageName}-v${version}.zip`);
console.log("\x1B[36m%s\x1B[0m", "Zipping to " + outputFile);
const archive = new zl.Zip();
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Adding files to archive...");
await addFilesToArchive(archive, distPath);
console.log("\x1B[33m%s\x1B[0m", "\u{1F4DD} Writing archive to file...");
await archive.archive(outputFile);
console.log("\x1B[32m%s\x1B[0m", "\u2705 Archive written successfully!");
}
async function clean() {
const distPath = resolve("dist");
try {
await mkdir(distPath, { recursive: true });
const files = await readdir(distPath);
for (const file of files) {
const filePath = join2(distPath, file);
await rm(filePath, { recursive: true, force: true });
}
} catch (error) {
await mkdir(distPath, { recursive: true });
}
}
async function ensureNpmBuilt() {
const nodeModulesPath = resolve("node_modules");
const nodeModulesExists = await stat(nodeModulesPath).catch(() => false);
if (!nodeModulesExists) {
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Running npm install...");
await new Promise((resolve4, reject) => {
exec("npm install", (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve4(stdout);
});
});
} else {
console.log("\x1B[32m%s\x1B[0m", "\u2705 NPM is already built!");
}
}
async function buildAll() {
console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Clearing dist folder...");
await clean();
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Ensuring NPM has been built...");
await ensureNpmBuilt();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Client...");
await buildClient();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Server...");
await buildServer();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Workers...");
await buildWorkers();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Postinstall Script...");
await buildPostinstall();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Copying Manifest...");
await copyDeskThing();
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Creating package...");
await createPackage();
console.log("\x1B[32m%s\x1B[0m", "\u2705 Build completed successfully!");
}
// src/package/releaseMeta.ts
import { createHash } from "crypto";
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
import { join as join4 } from "path";
import { writeFile } from "fs/promises";
// src/utils/filePaths.ts
import { existsSync } from "node:fs";
import { join as join3 } from "node:path";
var getReleaseFilePath = async (fileId) => {
const distPath = join3(process.cwd(), "dist");
const defaultPath = join3(distPath, "latest.json");
const appSpecificPath = join3(distPath, `${fileId}.json`);
return existsSync(defaultPath) ? appSpecificPath : defaultPath;
};
// src/utils/validateRepoUrl.ts
async function validateRepoUrl(url) {
try {
const parsedUrl = new URL(url);
if (parsedUrl.hostname === "github.com") {
const response2 = await fetch(url, { method: "HEAD" });
return response2.ok;
}
const response = await fetch(url, { method: "HEAD" });
return response.ok;
} catch (error) {
return false;
}
}
// src/package/releaseMeta.ts
var getLatestReleasesFromGithubURLs = (releaseAssetId, potentialUrls) => {
for (const url of potentialUrls) {
try {
const githubMatch = url.match(/github\.com\/([^\/]+)\/([^\/]+)/);
if (githubMatch) {
const [, owner, repo] = githubMatch;
const cleanRepo = repo.replace(".git", "");
return `https://github.com/${owner}/${cleanRepo}/releases/latest/download/${releaseAssetId}`;
}
} catch (error) {
console.error("\x1B[90m%s\x1B[0m", "Error processing github URL:", error);
}
}
return "";
};
var generateRelease = async () => {
console.log("\x1B[33m%s\x1B[0m", "Reading package.json and manifest.json...");
const { packageJson, manifestJson } = loadConfigs();
console.log("\x1B[33m%s\x1B[0m", "Calculating package hash...");
const packageName = packageJson.name;
const version = manifestJson.version || packageJson.version;
const distPath = join4(process.cwd(), "dist");
const zipPath = join4(distPath, `${packageName}-v${version}.zip`);
const iconPath = join4(distPath, "icons", `${manifestJson.id}.svg`);
const iconPathAlt = join4(distPath, "images", `${manifestJson.id}.svg`);
const iconFilePath = existsSync2(iconPath) ? iconPath : existsSync2(iconPathAlt) ? iconPathAlt : null;
const icon = iconFilePath ? `data:image/svg+xml;base64,${readFileSync2(iconFilePath, "base64")}` : "";
if (icon) {
console.log("\x1B[33m%s\x1B[0m", "Icon found, adding to release metadata...");
}
const updateUrl = getLatestReleasesFromGithubURLs(
`${packageName}-v${version}.zip`,
[
manifestJson.repository,
packageJson.repository?.url
]
);
let fileHash = "";
let fileSize = -1;
try {
const fileBuffer = readFileSync2(zipPath);
const hashSum = createHash("sha512");
hashSum.update(fileBuffer);
fileHash = hashSum.digest("hex");
fileSize = fileBuffer.length;
} catch (err) {
console.warn("\x1B[33m%s\x1B[0m", "Could not generate hash for package file");
}
console.log("\x1B[33m%s\x1B[0m", "Generating release metadata...");
const isValidUrl = await validateRepoUrl(manifestJson.repository);
if (isValidUrl) {
console.log("\x1B[32m%s\x1B[0m", "\u2705 Url is valid");
} else {
console.error("\x1B[31m%s\x1B[0m", `\u26A0\uFE0F Url ${manifestJson.repository} is not valid! You may need to provide a valid GitHub repository URL in manifest.json. If you ignore, DeskThing will be unable to download your app`);
}
const release = {
meta_version: "0.11.8",
// latest version of the release
meta_type: "app",
repository: manifestJson.repository || packageJson.repository?.url,
icon,
size: fileSize,
appManifest: manifestJson,
updateUrl,
hash: fileHash,
hashAlgorithm: "sha512",
updatedAt: Date.now(),
createdAt: Date.now(),
downloads: 0
};
return release;
};
async function createReleaseFile() {
console.log("\x1B[33m%s\x1B[0m", "Creating release file...");
try {
const release = await generateRelease();
const releaseFilePath = await getReleaseFilePath(release.appManifest.id);
await writeFile(releaseFilePath, JSON.stringify(release, null, 2));
console.log("\x1B[32m%s\x1B[0m", "Release file created successfully");
} catch (err) {
console.error("\x1B[31m%s\x1B[0m", "Failed to create release file:", err);
}
}
// src/package/index.ts
var packageApp = async ({ guided, noRelease, onlyRelease }) => {
if (guided) {
console.log("Guided mode is not implemented yet.");
return;
}
if (!onlyRelease) {
await buildAll();
} else {
console.log("\x1B[34m%s\x1B[0m", "\u23ED\uFE0F Skipping build step.");
}
if (!noRelease) {
await createReleaseFile();
} else {
console.log("\x1B[34m%s\x1B[0m", "\u23ED\uFE0F Skipping release file creation step.");
}
};
// src/packageClient/package.ts
import { join as join6, resolve as resolve2 } from "path";
import zl2 from "zip-lib";
import { readdir as readdir2, stat as stat2, rm as rm2, mkdir as mkdir2 } from "fs/promises";
// src/packageClient/config.ts
import { join as join5 } from "path";
import { readFileSync as readFileSync3 } from "fs";
var loadConfigs2 = () => {
const packageJsonPath = join5(process.cwd(), "package.json");
const manifestJsPath = join5(process.cwd(), "public/manifest.json");
let packageJson;
let manifestJson;
try {
packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
} catch (error) {
throw new Error(`\x1B[31mFailed to load package.json: ${error.message}\x1B[0m`);
}
try {
const manifestContent = readFileSync3(manifestJsPath, "utf8");
manifestJson = JSON.parse(manifestContent);
} catch (error) {
throw new Error(`\x1B[33m\u274C Failed to load manifest.json from ${manifestJsPath}. Does it exist?\x1B[0m`);
}
return {
packageJson,
manifestJson
};
};
// src/packageClient/package.ts
import { build as buildVite2 } from "vite";
async function buildClient2() {
await buildVite2({
configFile: "vite.config.ts",
base: "./"
});
}
async function addFilesToArchive2(archive, folderPath, baseFolder = "") {
const exists = await stat2(folderPath).catch(() => false);
if (!exists) return;
const files = await readdir2(folderPath);
for (const file of files) {
const filePath = join6(folderPath, file);
const stats = await stat2(filePath);
if (stats.isDirectory()) {
await addFilesToArchive2(archive, filePath, join6(baseFolder, file));
} else {
await archive.addFile(filePath, join6(baseFolder, file));
}
}
}
async function createPackage2() {
const { packageJson, manifestJson } = loadConfigs2();
const packageName = packageJson.name;
const version = (manifestJson.version || packageJson.version).replaceAll(
"v",
""
);
const distPath = resolve2("dist");
const distExists = await stat2(distPath).catch(() => false);
if (!distExists) {
await mkdir2(distPath, { recursive: true });
}
const outputFile = join6(distPath, `${packageName}-v${version}.zip`);
console.log("\x1B[36m%s\x1B[0m", "Zipping to " + outputFile);
const archive = new zl2.Zip();
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Adding files to archive...");
await addFilesToArchive2(archive, distPath);
console.log("\x1B[33m%s\x1B[0m", "\u{1F4DD} Writing archive to file...");
await archive.archive(outputFile);
console.log("\x1B[32m%s\x1B[0m", "\u2705 Archive written successfully!");
}
async function clean2() {
const distPath = resolve2("dist");
const files = await readdir2(distPath);
for (const file of files) {
const filePath = join6(distPath, file);
await rm2(filePath, { recursive: true, force: true });
}
}
async function buildAll2() {
console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Clearing dist folder...");
await clean2();
console.log("\x1B[33m%s\x1B[0m", "\u{1F3D7}\uFE0F Building Client...");
await buildClient2();
console.log("\x1B[33m%s\x1B[0m", "\u{1F4E6} Creating package...");
await createPackage2();
console.log("\x1B[32m%s\x1B[0m", "\u2705 Build completed successfully!");
}
// src/packageClient/releaseMeta.ts
import { createHash as createHash2 } from "crypto";
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
import { join as join7 } from "path";
// src/packageClient/sanitizeData.ts
var sanitizeClient = async () => {
console.log("\x1B[33m%s\x1B[0m", "\u{1F9F9} Client Sanitization not yet implemented...");
console.log("\x1B[32m%s\x1B[0m", "\u2705 DeskThing sanitization completed successfully!");
};
// src/packageClient/releaseMeta.ts
var getLatestReleasesFromGithubURLs2 = (releaseAssetId, potentialUrls) => {
for (const url of potentialUrls) {
try {
const githubMatch = url.match(/github\.com\/([^\/]+)\/([^\/]+)/);
if (githubMatch) {
const [, owner, repo] = githubMatch;
const cleanRepo = repo.replace(".git", "");
return `https://github.com/${owner}/${cleanRepo}/releases/latest/download/${releaseAssetId}`;
}
} catch (error) {
console.error("\x1B[90m%s\x1B[0m", "Error processing github URL:", error);
}
}
return "";
};
var generateRelease2 = async () => {
console.log("\x1B[33m%s\x1B[0m", "Reading package.json and manifest.json...");
const { packageJson, manifestJson } = loadConfigs2();
console.log("\x1B[33m%s\x1B[0m", "Calculating package hash...");
const packageName = packageJson.name;
const version = (manifestJson.version || packageJson.version).replaceAll(
"v",
""
);
const distPath = join7(process.cwd(), "dist");
const zipPath = join7(distPath, `${packageName}-v${version}.zip`);
const iconPath = join7(distPath, "icons", `${manifestJson.id}.svg`);
const iconPathAlt = join7(distPath, "images", `${manifestJson.id}.svg`);
const iconFilePath = existsSync3(iconPath) ? iconPath : existsSync3(iconPathAlt) ? iconPathAlt : null;
const icon = iconFilePath ? `data:image/svg+xml;base64,${readFileSync4(iconFilePath, "base64")}` : "";
if (icon) {
console.log(
"\x1B[33m%s\x1B[0m",
"Icon found, adding to release metadata..."
);
}
const updateUrl = getLatestReleasesFromGithubURLs2(
`${packageName}-v${version}.zip`,
[manifestJson.repository, packageJson.repository]
);
let fileHash = "";
let fileSize = -1;
try {
const fileBuffer = readFileSync4(zipPath);
const hashSum = createHash2("sha512");
hashSum.update(fileBuffer);
fileHash = hashSum.digest("hex");
fileSize = fileBuffer.length;
} catch (err) {
console.warn(
"\x1B[33m%s\x1B[0m",
"Could not generate hash for package file"
);
}
console.log("\x1B[33m%s\x1B[0m", "Generating release metadata...");
console.log("\x1B[33m%s\x1B[0m", "Validating Url...");
const isValidUrl = await validateRepoUrl(manifestJson.repository);
if (isValidUrl) {
console.log("\x1B[32m%s\x1B[0m", "\u2705 Url is valid");
} else {
console.error("\x1B[31m%s\x1B[0m", "\u26A0\uFE0F Url is not valid! You may need to provide a valid GitHub repository URL in manifest.json. If you ignore, DeskThing will be unable to download your app");
}
const release = {
meta_version: "0.11.8",
meta_type: "client",
repository: manifestJson.repository || packageJson.repository?.url,
icon,
clientManifest: manifestJson,
updateUrl,
hash: fileHash,
hashAlgorithm: "sha512",
size: fileSize,
updatedAt: Date.now(),
createdAt: Date.now(),
downloads: 0
};
return release;
};
async function createReleaseFile2() {
console.log("\x1B[33m%s\x1B[0m", "Creating release file...");
try {
await sanitizeClient();
const release = await generateRelease2();
const releaseFilePath = await getReleaseFilePath(release.clientManifest.id);
writeFileSync2(releaseFilePath, JSON.stringify(release, null, 2));
console.log("\x1B[32m%s\x1B[0m", "Release file created successfully");
} catch (err) {
console.error("\x1B[31m%s\x1B[0m", "Failed to create release file:", err);
}
}
// src/packageClient/index.ts
var packageClient = async () => {
await buildAll2();
await createReleaseFile2();
};
// src/cli.ts
import { stat as stat4, writeFile as writeFile2 } from "fs/promises";
// src/config/deskthing.config.ts
import { join as join8, resolve as resolve3 } from "path";
import { createRequire } from "module";
import { readFile } from "fs/promises";
import { pathToFileURL } from "url";
import { existsSync as existsSync4 } from "fs";
var defaultConfig = {
development: {
logging: {
level: "info",
prefix: "[DeskThing Server]"
},
client: {
logging: {
level: "info",
prefix: "[DeskThing Client]",
enableRemoteLogging: true
},
clientPort: 3e3,
viteLocation: "http://localhost",
vitePort: 5173,
linkPort: 8080
},
server: {
editCooldownMs: 1e3,
refreshInterval: 0
}
}
};
async function loadTsConfig(path, debug = false) {
try {
const require2 = createRequire(import.meta.url);
try {
require2("ts-node/register");
} catch (e) {
if (debug) console.log(`ts-node not available, continuing anyway...`);
}
try {
const configModule = require2(path);
return configModule.default || configModule;
} catch (e) {
if (debug)
console.error(
"\x1B[91mError loading TypeScript config:",
path,
e,
"\x1B[0m"
);
throw e;
}
} catch (error) {
if (debug) console.error("\x1B[91mError loading config:", error, "\x1B[0m");
throw error;
}
}
var manuallyParseConfig = async (path, debug = false) => {
try {
if (debug)
console.log(
`(debug mode enabled) Manually loading config from ${path} file and parsing manually (may cause errors)`
);
const fileContent = await readFile(path, "utf-8");
const configMatch = fileContent.match(/defineConfig\s*\(\s*({[\s\S]*?})\s*\)/);
if (!configMatch) {
if (debug)
console.log("Could not find config definition in file using regex");
const objectMatch = fileContent.match(/\{[\s\S]*development[\s\S]*\}/);
if (objectMatch) {
try {
let jsonStr = objectMatch[0].replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/'/g, '"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, "");
jsonStr = jsonStr.replace(
/process\.env\.[A-Z_]+/g,
'"ENV_PLACEHOLDER"'
);
const config = JSON.parse(jsonStr);
if (debug)
console.log("Successfully extracted config using fallback method");
return config;
} catch (e) {
if (debug) console.log("Failed to parse extracted object:", e);
}
}
throw new Error("Could not find or parse config definition in file");
}
try {
const configStr = configMatch[1]?.trim() || configMatch[2]?.trim();
if (debug)
console.log("Found config string, attempting to evaluate safely");
if (debug)
console.log(configStr);
const cleanConfigStr = configStr?.replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, "");
const config = JSON.parse(cleanConfigStr);
return config;
} catch (parseError) {
if (debug)
console.error("\x1B[91mError parsing config:", parseError, "\x1B[0m");
throw parseError;
}
} catch (e) {
if (debug)
console.error(
"\x1B[91m(debug mode enabled) Error loading config:",
e,
"\x1B[0m"
);
throw e;
}
};
async function parseTypeScriptConfig(filePath, debug = false) {
try {
const content = await readFile(filePath, "utf-8");
const configMatch = content.match(/defineConfig\s*\(\s*({[\s\S]*?})\s*\)/);
if (!configMatch || !configMatch[1]) {
return null;
}
let configStr = configMatch[1].replace(/process\.env\.[A-Z_]+/g, '"ENV_VARIABLE"').replace(/,(\s*[}\]])/g, "$1").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
try {
const fn = new Function(`return ${configStr}`);
return fn();
} catch (evalError) {
if (debug)
console.log("Failed to evaluate config as JavaScript:", evalError);
try {
configStr = configStr.replace(/'/g, '"');
return JSON.parse(configStr);
} catch (jsonError) {
if (debug)
console.log("Failed to parse as JSON:", jsonError);
return null;
}
}
} catch (error) {
if (debug)
console.log("Error reading or parsing config file:", error);
return null;
}
}
var directConfigImport = async (path, debug = false) => {
try {
if (debug)
console.log(
`(debug mode enabled) Loading config from ${path} file...`
);
const configModule = await import(`${path}`);
if (debug) console.log(`Config loaded successfully from ${path}`);
return configModule.default || configModule;
} catch (error) {
if (debug) console.log("Direct import failed:", error);
throw error;
}
};
var directConfigImportUrl = async (path, debug = false) => {
try {
const tsConfigPath = pathToFileURL(path).href;
if (debug)
console.log(
`(debug mode enabled) Loading config from ${tsConfigPath} file...`
);
const configModule = await import(tsConfigPath);
if (debug) console.log(`Config loaded successfully from ${tsConfigPath}`);
return configModule.default || configModule;
} catch (error) {
if (debug) console.log("Direct import failed:", error);
throw error;
}
};
var getConfigFromFile = async (debug = false) => {
try {
let rootUrl = resolve3(process.cwd(), "deskthing.config.ts");
if (!existsSync4(rootUrl)) {
if (debug)
console.log(
"deskthing.config.ts not found, trying deskthing.config.js"
);
rootUrl = resolve3(process.cwd(), "deskthing.config.js");
if (!existsSync4(rootUrl)) {
throw new Error("No config file found (tried both TS and JS files)");
}
}
try {
const config = await directConfigImport(rootUrl, debug);
if (config) {
if (debug) console.log("Config loaded successfully from TS file");
return config;
}
} catch (importError) {
if (debug)
console.log(`Direct import failed, trying alternative method...`);
}
try {
const config = await directConfigImportUrl(rootUrl, debug);
if (config) {
if (debug) console.log("Config loaded successfully from TS file");
return config;
}
} catch (importError) {
if (debug)
console.log(`Direct import failed, trying alternative method...`);
}
try {
const config = await loadTsConfig(rootUrl, debug);
if (config) {
if (debug) console.log("Config loaded successfully from TS file");
return config;
}
} catch (e) {
if (debug)
console.error("\x1B[91mError loading TS config:", e, "\x1B[0m");
}
if (debug) console.log("Trying to parse config manually...");
try {
const config = await manuallyParseConfig(rootUrl, debug);
return config;
} catch (e) {
if (debug)
console.error("\x1B[91mError parsing config manually:", e, "\x1B[0m");
}
if (debug) console.log("Trying to parse config and run as js...");
try {
const config = await parseTypeScriptConfig(rootUrl, debug);
return config;
} catch (error) {
if (debug) console.log("Error running as js:", error);
}
} catch (e) {
if (debug)
console.error(
"\x1B[91m(debug mode) Error loading config. Does it exist? :",
e,
"\x1B[0m"
);
return defaultConfig;
}
};
var DeskThingConfig = defaultConfig;
function isObject(item) {
return item && typeof item === "object" && !Array.isArray(item);
}
function deepmerge(target, source) {
if (!isObject(target) || !isObject(source)) {
return source;
}
const output = { ...target };
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key]) && isObject(target[key])) {
output[key] = deepmerge(target[key], source[key]);
} else {
output[key] = source[key];
}
}
}
return output;
}
var initConfig = async (options = {
silent: false,
debug: false
}) => {
try {
const userConfig = await getConfigFromFile(options.debug);
DeskThingConfig = deepmerge(defaultConfig, userConfig || {});
if (!options.silent || options.debug) {
if (userConfig) {
console.log(`
\x1B[32m\u2705 Config Loaded\x1B[0m
`);
} else {
console.log(`
\x1B[32m\u2705 No Config Found, Using Default\x1B[0m`);
if (options.debug) {
console.log(
`\x1B[3m\x1B[90mPath Checked: ${join8(
process.cwd(),
"deskthing.config.ts"
)}\x1B[0m
`
);
}
}
}
} catch (e) {
console.warn("\x1B[93mWarning: Error loading config, using defaults\x1B[0m");
if (options.debug)
console.error("\x1B[91mError loading config:", e, "\x1B[0m");
DeskThingConfig = defaultConfig;
}
};
var serverConfig = {
...defaultConfig
};
// src/cli.ts
await initConfig({ silent: true });
var __dirname = dirname(fileURLToPath(import.meta.url));
var thisPackage = await Promise.resolve().then(() => __toESM(require_package(), 1)).catch(() => ({ default: { version: "0.0.0" } })).then((module) => module?.default) || { version: "0.0.0" };
console.log(`
------------------------------------------------
_ _ _ _ \x1B[92m_\x1B[0m
| | | | | | | | \x1B[92m(_)\x1B[0m
__| | ___ ___| | _| |_| |__ _ _ __ __ _
/ _\` |/ _ \\/ __| |/ / __| '_ \\| | '_ \\ / _\` |
| (_| | __/\\__ \\ <| |_| | | | | | | | (_| |
\\__,_|\\___||___/_|\\_\\\\__|_| |_|_|_| |_|\\__, |
__/ |
|___/
Version ${thisPackage?.version || "0.11.0"}`);
yargs(hideBin(process.argv)).scriptName("deskthing").command(
"dev",
"Start development server",
(yargs2) => {
return yargs2.option("debug", {
type: "boolean",
default: false,
description: "Enable debug mode"
});
},
async (argv) => {
console.log(
`------- \x1B[1mdev\x1B[0m -- init -- update -- package --------`
);
console.log("\n\n\x1B[1m\u{1F680} Starting development server...\x1B[0m\n\n");
const tsmPath = join9(process.cwd(), "node_modules", "tsm");
try {
await stat4(tsmPath);
console.log("\x1B[1mtsm is installed\x1B[0m\n");
} catch (e) {
console.log("\n\x1B[1mInstalling tsm dependency...\x1B[0m\n");
execSync("npm install tsm --no-save --legacy-peer-deps", {
stdio: "inherit"
});
}
const indexPath = join9(__dirname, "./emulator/index.js");
const fileUrl = `file://${indexPath.replace(/\\/g, "/")}`;
const { startDevelopment } = await import(fileUrl);
await startDevelopment({ debug: argv.debug });
}
).command(
"update",
"Update dependencies and configurations",
(yargs2) => {
return yargs2.option("force", {
type: "boolean",
default: false,
description: "Force update all dependencies"
}).option("no-overwrite", {
type: "boolean",
default: false,
description: "Do not overwrite existing files"
}).option("debug", {
type: "boolean",
default: false,
description: "Add verbose debugging"
}).option("silent", {
type: "boolean",
default: false,
description: "Make it so there is no output to the console"
});
},
async (argv) => {
if (!argv.silent)
console.log(
`------- dev -- init -- \x1B[1mupdate\x1B[0m -- package --------`
);
if (!argv.silent)
console.log("Updating dependencies and configurations...");
const args = ["create-deskthing@latest", "--update"];
if (argv["no-overwrite"]) {
if (!argv.silent) console.log("\x1B[32m\u2713 No Overwrite Enabled\x1B[0m");
args.push("--no-overwrite");
}
if (argv.force) {
if (!argv.silent) console.log("\x1B[32m\u2713 Force Enabled\x1B[0m");
args.push("--force");
}
if (argv.debug) {
if (!argv.silent) console.log("\x1B[32m\u2713 Debug Enabled\x1B[0m");
args.push("--debug");
}
if (argv.silent) {
console.log("\x1B[32m\u2713 Silent Enabled\x1B[0m");
args.push("--silent");
}
execSync(`npx ${args.join(" ")}`, { stdio: "inherit" });
}
).command(
"package",
"Package and zip up your app. Also generates needed manifest files",
(yargs2) => {
return yargs2.option("guided", {
type: "boolean",
default: false,
description: "Force update all dependencies"
}).option("no-release", {
type: "boolean",
default: false,
description: "Skip generating release metadata files"
}).option("only-release", {
type: "boolean",
default: false,
description: "Only generate release metadata files"
});
},
async (argv) => {
console.log(
`------- dev -- init -- update -- \x1B[1mpackage\x1B[0m --------`
);
console.log("Packaging app...");
await packageApp({
guided: argv.guided,
noRelease: argv.noRelease,
onlyRelease: argv.onlyRelease
});
}
).command(
"package-client",
"Package and zip up your client. Also generates needed manifest files",
(yargs2) => {
return yargs2;
},
async () => {
console.log(`------- dev -- init -- update -- package --------
`);
console.log(
`------- \x1B[1mpackaging client\x1B[0m --------
`
);
await packageClient();
}
).command(
"init",
"Setup the DeskThing template",
(yargs2) => {
return yargs2;
},
async () => {
console.log(
`------- dev -- \x1B[1minit\x1B[0m -- update -- package --------`
);
console.log("Setting up the DeskThing template...");
execSync("npm create deskthing@latest --create", { stdio: "inherit" });
}
).command("$0", "Show available commands", () => {
console.log(`------- dev -- init -- update -- package --------`);
console.log("Available commands:");
console.log(" dev Start development server");
console.log(" update Update dependencies and configurations");
console.log(" package Package and zip up your app");
console.log(" init Setup the DeskThing template");
console.log(
"\nRun `deskthing <command> --help` for more information about a command."
);
}).command(
"init-config",
"Create a typed configuration file",
(yargs2) => {
return yargs2;
},
async () => {
console.log(
`
------- \x1B[1mCreating typed configuration file\x1B[0m --------
`
);
const configTemplate = `
// @ts-check
// version ${thisPackage?.version || "0.11.0"}
import { defineConfig } from '@deskthing/cli';
export default defineConfig({
development: {
logging: {
level: "info",
prefix: "[DeskThing Server]",
},
client: {
logging: {
level: "info",
prefix: "[DeskThing Client]",
enableRemoteLogging: true,
},
clientPort: 3000,
viteLocation: "http://localhost",
vitePort: 5173,
linkPort: 8080,
},
server: {
editCooldownMs: 1000,
},
}
});
`;
const configPath = join9(process.cwd(), "deskthing.config.js");
try {
await writeFile2(configPath, configTemplate);
console.log(`
\x1B[32m\u2705 File created at
${configPath}\x1B[0m
`);
} catch (error) {
console.error(
"\x1B[31m\u274C Failed to create configuration file:\x1B[0m",
error
);
}
}
).parse();