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.
Files changed (2) hide show
  1. package/dist/index.mjs +98 -3
  2. 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.220";
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
- runDisplayCmd("scrot", ["-o", target]);
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.220",
3
+ "version": "0.2.222",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {