replicas-cli 0.2.220 → 0.2.222
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.mjs +98 -3
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -8933,7 +8933,7 @@ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
|
8933
8933
|
var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
|
|
8934
8934
|
|
|
8935
8935
|
// ../shared/src/cli-version.ts
|
|
8936
|
-
var CLI_VERSION = "0.2.
|
|
8936
|
+
var CLI_VERSION = "0.2.222";
|
|
8937
8937
|
|
|
8938
8938
|
// ../shared/src/engine/environment.ts
|
|
8939
8939
|
var DESKTOP_NOVNC_PORT = 6080;
|
|
@@ -13096,7 +13096,7 @@ async function mediaListCommand(options) {
|
|
|
13096
13096
|
|
|
13097
13097
|
// src/commands/computer.ts
|
|
13098
13098
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
13099
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
13099
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, rmSync, writeFileSync } from "fs";
|
|
13100
13100
|
import { dirname, isAbsolute, resolve } from "path";
|
|
13101
13101
|
import chalk19 from "chalk";
|
|
13102
13102
|
var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
|
|
@@ -13180,10 +13180,105 @@ async function computerStatusCommand() {
|
|
|
13180
13180
|
console.log(` ${chalk19.dim("preview: not yet registered (engine registers it at startup)")}`);
|
|
13181
13181
|
}
|
|
13182
13182
|
}
|
|
13183
|
+
var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
13184
|
+
function readPngDimensions(filePath) {
|
|
13185
|
+
const fd = openSync(filePath, "r");
|
|
13186
|
+
try {
|
|
13187
|
+
const buf = Buffer.alloc(24);
|
|
13188
|
+
const bytesRead = readSync(fd, buf, 0, 24, 0);
|
|
13189
|
+
if (bytesRead < 24 || !buf.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
13190
|
+
fail(`${filePath} is not a valid PNG (read ${bytesRead}/24 bytes)`);
|
|
13191
|
+
}
|
|
13192
|
+
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
|
|
13193
|
+
} finally {
|
|
13194
|
+
closeSync(fd);
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
13197
|
+
function brandSvgPath() {
|
|
13198
|
+
const dir = process.env.REPLICAS_DESKTOP_TEMPLATES || "/usr/local/share/replicas/desktop";
|
|
13199
|
+
return `${dir}/brand-wallpaper.svg`;
|
|
13200
|
+
}
|
|
13201
|
+
function loadBrandSvg(canvasW, canvasH) {
|
|
13202
|
+
const path6 = brandSvgPath();
|
|
13203
|
+
if (!existsSync(path6)) {
|
|
13204
|
+
fail(
|
|
13205
|
+
`Brand wallpaper SVG missing at ${path6}. The workspace image is out of date \u2014 \`desktop/brand-wallpaper.svg\` must be installed at $REPLICAS_DESKTOP_TEMPLATES.`
|
|
13206
|
+
);
|
|
13207
|
+
}
|
|
13208
|
+
return readFileSync(path6, "utf8").replace(/(<svg[^>]*\s)width="\d+"/, `$1width="${canvasW}"`).replace(/(<svg[^>]*\s)height="\d+"/, `$1height="${canvasH}"`);
|
|
13209
|
+
}
|
|
13210
|
+
var BRAND_PAD_FRACTION = 0.06;
|
|
13211
|
+
var SCREENSHOT_CORNER_FRACTION = 0.022;
|
|
13212
|
+
var SHADOW_SIGMA_FRACTION = 0.022;
|
|
13213
|
+
var SHADOW_OFFSET_Y_FRACTION = 0.013;
|
|
13214
|
+
var SHADOW_ALPHA = 0.6;
|
|
13215
|
+
var SCREENSHOT_MASK_TEMPLATE = `<svg width="__W__" height="__H__" xmlns="http://www.w3.org/2000/svg"><rect width="__W__" height="__H__" rx="__R__" ry="__R__" fill="white"/></svg>`;
|
|
13216
|
+
var SHADOW_MASK_TEMPLATE = `<svg width="__SW__" height="__SH__" xmlns="http://www.w3.org/2000/svg"><rect x="__M__" y="__M__" width="__W__" height="__H__" rx="__R__" ry="__R__" fill="white"/></svg>`;
|
|
13183
13217
|
async function computerScreenshotCommand(path6) {
|
|
13184
13218
|
const target = resolvePath(path6);
|
|
13185
13219
|
mkdirSync(dirname(target), { recursive: true });
|
|
13186
|
-
|
|
13220
|
+
const stamp = `${process.pid}-${Date.now()}`;
|
|
13221
|
+
const rawPath = `/tmp/replicas-screenshot-${stamp}.raw.png`;
|
|
13222
|
+
const svgPath = `/tmp/replicas-screenshot-${stamp}.brand.svg`;
|
|
13223
|
+
const maskPath = `/tmp/replicas-screenshot-${stamp}.mask.svg`;
|
|
13224
|
+
const shadowPath = `/tmp/replicas-screenshot-${stamp}.shadow.svg`;
|
|
13225
|
+
try {
|
|
13226
|
+
runDisplayCmd("scrot", ["-o", rawPath]);
|
|
13227
|
+
const { width, height } = readPngDimensions(rawPath);
|
|
13228
|
+
const padX = Math.round(width * BRAND_PAD_FRACTION);
|
|
13229
|
+
const padY = Math.round(height * BRAND_PAD_FRACTION);
|
|
13230
|
+
const canvasW = width + padX * 2;
|
|
13231
|
+
const canvasH = height + padY * 2;
|
|
13232
|
+
const minDim = Math.min(width, height);
|
|
13233
|
+
const cornerR = Math.round(minDim * SCREENSHOT_CORNER_FRACTION);
|
|
13234
|
+
const shadowSigma = Math.max(12, Math.round(minDim * SHADOW_SIGMA_FRACTION));
|
|
13235
|
+
const shadowOffsetY = Math.round(minDim * SHADOW_OFFSET_Y_FRACTION);
|
|
13236
|
+
const shadowMargin = shadowSigma * 3;
|
|
13237
|
+
const shadowW = width + shadowMargin * 2;
|
|
13238
|
+
const shadowH = height + shadowMargin * 2;
|
|
13239
|
+
writeFileSync(svgPath, loadBrandSvg(canvasW, canvasH));
|
|
13240
|
+
writeFileSync(
|
|
13241
|
+
maskPath,
|
|
13242
|
+
SCREENSHOT_MASK_TEMPLATE.replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
|
|
13243
|
+
);
|
|
13244
|
+
writeFileSync(
|
|
13245
|
+
shadowPath,
|
|
13246
|
+
SHADOW_MASK_TEMPLATE.replace(/__SW__/g, String(shadowW)).replace(/__SH__/g, String(shadowH)).replace(/__M__/g, String(shadowMargin)).replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
|
|
13247
|
+
);
|
|
13248
|
+
const r = spawnSync(
|
|
13249
|
+
"ffmpeg",
|
|
13250
|
+
[
|
|
13251
|
+
"-y",
|
|
13252
|
+
"-hide_banner",
|
|
13253
|
+
"-loglevel",
|
|
13254
|
+
"error",
|
|
13255
|
+
"-i",
|
|
13256
|
+
svgPath,
|
|
13257
|
+
"-i",
|
|
13258
|
+
rawPath,
|
|
13259
|
+
"-i",
|
|
13260
|
+
maskPath,
|
|
13261
|
+
"-i",
|
|
13262
|
+
shadowPath,
|
|
13263
|
+
"-filter_complex",
|
|
13264
|
+
`[2:v]format=rgba,alphaextract[mask];[3:v]format=rgba,colorchannelmixer=rr=0:gg=0:bb=0:aa=${SHADOW_ALPHA},gblur=sigma=${shadowSigma}[shadow];[1:v]format=rgba[scr];[scr][mask]alphamerge[rounded];[0:v]format=rgba[bg];[bg][shadow]overlay=${padX - shadowMargin}:${padY - shadowMargin + shadowOffsetY}:format=auto[bg_shadow];[bg_shadow][rounded]overlay=${padX}:${padY}:format=auto`,
|
|
13265
|
+
"-frames:v",
|
|
13266
|
+
"1",
|
|
13267
|
+
"-update",
|
|
13268
|
+
"1",
|
|
13269
|
+
target
|
|
13270
|
+
],
|
|
13271
|
+
{ stdio: "pipe" }
|
|
13272
|
+
);
|
|
13273
|
+
if (r.status !== 0) {
|
|
13274
|
+
fail(`ffmpeg branding failed: ${r.stderr?.toString().trim() || `exit ${r.status}`}`);
|
|
13275
|
+
}
|
|
13276
|
+
} finally {
|
|
13277
|
+
rmSync(rawPath, { force: true });
|
|
13278
|
+
rmSync(svgPath, { force: true });
|
|
13279
|
+
rmSync(maskPath, { force: true });
|
|
13280
|
+
rmSync(shadowPath, { force: true });
|
|
13281
|
+
}
|
|
13187
13282
|
console.log(target);
|
|
13188
13283
|
}
|
|
13189
13284
|
async function computerClickCommand(xStr, yStr, options) {
|