@gecho-ai/gecho-bridge 1.1.26 → 1.1.27
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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/mcp-client.cjs +13 -5
- package/dist/server.cjs +292 -57
- package/distribution-skills/tiktok-influencer/SKILL.md +360 -0
- package/distribution-skills/tiktok-influencer/_meta.json +5 -0
- package/distribution-skills/tiktok-insight/SKILL.md +391 -0
- package/distribution-skills/tiktok-insight/_meta.json +5 -0
- package/distribution-skills/tiktok-video-search/SKILL.md +358 -0
- package/distribution-skills/tiktok-video-search/_meta.json +5 -0
- package/mcp-client.js +14 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/server.js +315 -64
- package/skills/tiktok-search/_meta.json +1 -1
package/dist/mcp-client.cjs
CHANGED
|
@@ -33854,7 +33854,7 @@ var require_package = __commonJS({
|
|
|
33854
33854
|
"package.json"(exports2, module2) {
|
|
33855
33855
|
module2.exports = {
|
|
33856
33856
|
name: "@gecho-ai/gecho-bridge",
|
|
33857
|
-
version: "1.1.
|
|
33857
|
+
version: "1.1.27",
|
|
33858
33858
|
description: "TikTok Search and Insight tools for OpenClaw, connecting LLMs to TikTok via Chrome Extension.",
|
|
33859
33859
|
main: "mcp-client.js",
|
|
33860
33860
|
bin: {
|
|
@@ -33868,6 +33868,7 @@ var require_package = __commonJS({
|
|
|
33868
33868
|
".mcp.json",
|
|
33869
33869
|
"openclaw.plugin.json",
|
|
33870
33870
|
"skills",
|
|
33871
|
+
"distribution-skills",
|
|
33871
33872
|
"README.md",
|
|
33872
33873
|
"README.zh.md",
|
|
33873
33874
|
"qywx.jpg"
|
|
@@ -34017,12 +34018,19 @@ function requestShutdown() {
|
|
|
34017
34018
|
});
|
|
34018
34019
|
}
|
|
34019
34020
|
function startServiceDetached() {
|
|
34020
|
-
const
|
|
34021
|
-
|
|
34022
|
-
|
|
34023
|
-
//
|
|
34021
|
+
const isWindows = process.platform === "win32";
|
|
34022
|
+
const child = spawn(process.execPath, [SERVICE_PATH], {
|
|
34023
|
+
// On Windows, detached creates a separate process group. It is unnecessary
|
|
34024
|
+
// after unref() and has proved unreliable for the browser launched by this
|
|
34025
|
+
// service when the parent is a desktop MCP host. Keep the Unix behaviour.
|
|
34026
|
+
detached: !isWindows,
|
|
34027
|
+
stdio: "ignore",
|
|
34028
|
+
windowsHide: true,
|
|
34029
|
+
// Make this equivalent to running `node server.js` from the package.
|
|
34030
|
+
cwd: __dirname
|
|
34024
34031
|
});
|
|
34025
34032
|
child.unref();
|
|
34033
|
+
return child;
|
|
34026
34034
|
}
|
|
34027
34035
|
async function waitForServiceDown() {
|
|
34028
34036
|
let retries = 10;
|
package/dist/server.cjs
CHANGED
|
@@ -3640,7 +3640,7 @@ var require_package = __commonJS({
|
|
|
3640
3640
|
"package.json"(exports2, module2) {
|
|
3641
3641
|
module2.exports = {
|
|
3642
3642
|
name: "@gecho-ai/gecho-bridge",
|
|
3643
|
-
version: "1.1.
|
|
3643
|
+
version: "1.1.27",
|
|
3644
3644
|
description: "TikTok Search and Insight tools for OpenClaw, connecting LLMs to TikTok via Chrome Extension.",
|
|
3645
3645
|
main: "mcp-client.js",
|
|
3646
3646
|
bin: {
|
|
@@ -3654,6 +3654,7 @@ var require_package = __commonJS({
|
|
|
3654
3654
|
".mcp.json",
|
|
3655
3655
|
"openclaw.plugin.json",
|
|
3656
3656
|
"skills",
|
|
3657
|
+
"distribution-skills",
|
|
3657
3658
|
"README.md",
|
|
3658
3659
|
"README.zh.md",
|
|
3659
3660
|
"qywx.jpg"
|
|
@@ -3699,9 +3700,10 @@ var { WebSocketServer } = require_ws();
|
|
|
3699
3700
|
var http = require("http");
|
|
3700
3701
|
var fs = require("fs");
|
|
3701
3702
|
var path = require("path");
|
|
3703
|
+
var { spawn } = require("child_process");
|
|
3702
3704
|
var packageJson = require_package();
|
|
3703
|
-
var WS_PORT = 18792;
|
|
3704
|
-
var HTTP_PORT = 18793;
|
|
3705
|
+
var WS_PORT = Number(process["env"].GECHO_WS_PORT || 18792);
|
|
3706
|
+
var HTTP_PORT = Number(process["env"].GECHO_HTTP_PORT || 18793);
|
|
3705
3707
|
var SERVICE_PROTOCOL_VERSION = 2;
|
|
3706
3708
|
var SERVICE_STARTED_AT = Date.now();
|
|
3707
3709
|
var DEFAULT_DATA_DIR = path.join(__dirname, "data");
|
|
@@ -3709,10 +3711,19 @@ var JOBS_DIR = process["env"].GECHO_DATA_DIR || DEFAULT_DATA_DIR;
|
|
|
3709
3711
|
var JOBS_STORE_PATH = path.join(JOBS_DIR, ".async_jobs.json");
|
|
3710
3712
|
var JOB_DETAILS_DIR = path.join(JOBS_DIR, "jobs");
|
|
3711
3713
|
var REQUEST_INDEX_PATH = path.join(JOBS_DIR, ".async_request_index.json");
|
|
3714
|
+
var BROWSER_CONNECTION_PATH = path.join(JOBS_DIR, ".browser_connection.json");
|
|
3712
3715
|
var JOB_TTL_MS = Number(process["env"].GECHO_JOB_TTL_MS || 3 * 24 * 60 * 60 * 1e3);
|
|
3713
3716
|
var MAX_PERSISTED_JOBS = Number(process["env"].GECHO_MAX_PERSISTED_JOBS || 2e3);
|
|
3714
3717
|
var CLEANUP_INTERVAL_MS = Number(process["env"].GECHO_CLEANUP_INTERVAL_MS || 10 * 60 * 1e3);
|
|
3718
|
+
var EXTENSION_CONNECT_TIMEOUT_MS = Math.max(1e3, Number(process["env"].GECHO_EXTENSION_CONNECT_TIMEOUT_MS || 3e4));
|
|
3719
|
+
var EXTENSION_CONNECT_POLL_MS = 500;
|
|
3720
|
+
var AUTO_LAUNCH_BROWSER = process["env"].GECHO_AUTO_LAUNCH_BROWSER !== "0";
|
|
3721
|
+
var AUTO_LAUNCH_BROWSER_DRY_RUN = process["env"].GECHO_AUTO_LAUNCH_BROWSER_DRY_RUN === "1";
|
|
3722
|
+
var AUTO_LAUNCH_BROWSER_COOLDOWN_MS = Math.max(0, Number(process["env"].GECHO_AUTO_LAUNCH_BROWSER_COOLDOWN_MS || 1e4));
|
|
3715
3723
|
var extensionSocket = null;
|
|
3724
|
+
var lastBrowserConnection = null;
|
|
3725
|
+
var browserLaunchPromise = null;
|
|
3726
|
+
var lastBrowserLaunchAt = 0;
|
|
3716
3727
|
var pendingRequests = /* @__PURE__ */ new Map();
|
|
3717
3728
|
var requestIdCounter = 1;
|
|
3718
3729
|
var shuttingDown = false;
|
|
@@ -3738,6 +3749,27 @@ function ensureJobDetailsDirReady() {
|
|
|
3738
3749
|
function getJobDetailPath(jobId) {
|
|
3739
3750
|
return path.join(JOB_DETAILS_DIR, `${jobId}.json`);
|
|
3740
3751
|
}
|
|
3752
|
+
function writeFileAtomic(targetPath, content) {
|
|
3753
|
+
const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
3754
|
+
fs.writeFileSync(tmpPath, content, "utf8");
|
|
3755
|
+
try {
|
|
3756
|
+
fs.renameSync(tmpPath, targetPath);
|
|
3757
|
+
} catch (e) {
|
|
3758
|
+
if (e && (e.code === "EEXIST" || e.code === "EPERM")) {
|
|
3759
|
+
try {
|
|
3760
|
+
fs.unlinkSync(targetPath);
|
|
3761
|
+
} catch (_ignore) {
|
|
3762
|
+
}
|
|
3763
|
+
fs.renameSync(tmpPath, targetPath);
|
|
3764
|
+
} else {
|
|
3765
|
+
try {
|
|
3766
|
+
fs.unlinkSync(tmpPath);
|
|
3767
|
+
} catch (_ignore) {
|
|
3768
|
+
}
|
|
3769
|
+
throw e;
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3741
3773
|
function enqueueWrite(task, label = "write_task") {
|
|
3742
3774
|
writeQueue = writeQueue.then(async () => {
|
|
3743
3775
|
await task();
|
|
@@ -3768,6 +3800,235 @@ async function writeFileAtomicAsync(targetPath, content) {
|
|
|
3768
3800
|
}
|
|
3769
3801
|
}
|
|
3770
3802
|
}
|
|
3803
|
+
function isExtensionConnected() {
|
|
3804
|
+
return !!extensionSocket && extensionSocket.readyState === 1;
|
|
3805
|
+
}
|
|
3806
|
+
function normalizeBrowserName(value) {
|
|
3807
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
3808
|
+
if (normalized === "edge" || normalized === "microsoft-edge" || normalized === "msedge") return "edge";
|
|
3809
|
+
if (normalized === "chrome" || normalized === "google-chrome") return "chrome";
|
|
3810
|
+
return "";
|
|
3811
|
+
}
|
|
3812
|
+
function detectBrowserFromUserAgent(userAgent) {
|
|
3813
|
+
const value = String(userAgent || "");
|
|
3814
|
+
if (/\bEdg\//i.test(value)) return "edge";
|
|
3815
|
+
if (/\bChrome\//i.test(value) && !/\b(?:OPR|Edg)\//i.test(value)) return "chrome";
|
|
3816
|
+
return "";
|
|
3817
|
+
}
|
|
3818
|
+
function loadLastBrowserConnection() {
|
|
3819
|
+
try {
|
|
3820
|
+
if (!fs.existsSync(BROWSER_CONNECTION_PATH)) return null;
|
|
3821
|
+
const parsed = JSON.parse(fs.readFileSync(BROWSER_CONNECTION_PATH, "utf8") || "{}");
|
|
3822
|
+
const browser = normalizeBrowserName(parsed?.browser);
|
|
3823
|
+
if (!browser) return null;
|
|
3824
|
+
return {
|
|
3825
|
+
browser,
|
|
3826
|
+
detectedAt: Number(parsed.detectedAt || 0) || 0
|
|
3827
|
+
};
|
|
3828
|
+
} catch (e) {
|
|
3829
|
+
console.warn(`Failed to load last browser connection: ${e.message}`);
|
|
3830
|
+
return null;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
function persistLastBrowserConnection(browser) {
|
|
3834
|
+
const normalized = normalizeBrowserName(browser);
|
|
3835
|
+
if (!normalized) return;
|
|
3836
|
+
lastBrowserConnection = { browser: normalized, detectedAt: Date.now() };
|
|
3837
|
+
try {
|
|
3838
|
+
ensureJobsDirReady();
|
|
3839
|
+
writeFileAtomic(BROWSER_CONNECTION_PATH, JSON.stringify(lastBrowserConnection));
|
|
3840
|
+
} catch (e) {
|
|
3841
|
+
console.warn(`Failed to persist last browser connection: ${e.message}`);
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
function findFirstExistingPath(paths) {
|
|
3845
|
+
return paths.find((candidate) => {
|
|
3846
|
+
try {
|
|
3847
|
+
return !!candidate && fs.existsSync(candidate);
|
|
3848
|
+
} catch (_e) {
|
|
3849
|
+
return false;
|
|
3850
|
+
}
|
|
3851
|
+
}) || "";
|
|
3852
|
+
}
|
|
3853
|
+
function findFirstCommand(commands) {
|
|
3854
|
+
const pathEntries = String(process["env"].PATH || "").split(path.delimiter).filter(Boolean);
|
|
3855
|
+
const extensions = process.platform === "win32" ? String(process["env"].PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";") : [""];
|
|
3856
|
+
for (const command of commands) {
|
|
3857
|
+
for (const entry of pathEntries) {
|
|
3858
|
+
for (const extension of extensions) {
|
|
3859
|
+
const executable = path.join(entry, process.platform === "win32" ? `${command}${extension}` : command);
|
|
3860
|
+
if (fs.existsSync(executable)) return executable;
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
return "";
|
|
3865
|
+
}
|
|
3866
|
+
function getWindowsBrowserExecutable(browser) {
|
|
3867
|
+
const normalized = normalizeBrowserName(browser);
|
|
3868
|
+
const executable = normalized === "edge" ? "msedge.exe" : "chrome.exe";
|
|
3869
|
+
const vendorPath = normalized === "edge" ? ["Microsoft", "Edge", "Application"] : ["Google", "Chrome", "Application"];
|
|
3870
|
+
const roots = [
|
|
3871
|
+
process["env"].LOCALAPPDATA,
|
|
3872
|
+
process["env"].PROGRAMFILES,
|
|
3873
|
+
process["env"]["PROGRAMFILES(X86)"]
|
|
3874
|
+
].filter(Boolean);
|
|
3875
|
+
return findFirstExistingPath(roots.map((root) => path.join(root, ...vendorPath, executable)));
|
|
3876
|
+
}
|
|
3877
|
+
function isBrowserInstalled(browser) {
|
|
3878
|
+
const normalized = normalizeBrowserName(browser);
|
|
3879
|
+
if (!normalized) return false;
|
|
3880
|
+
if (process.platform === "darwin") {
|
|
3881
|
+
const appName = normalized === "edge" ? "Microsoft Edge.app" : "Google Chrome.app";
|
|
3882
|
+
return !!findFirstExistingPath([
|
|
3883
|
+
path.join("/Applications", appName),
|
|
3884
|
+
path.join("/System/Applications", appName)
|
|
3885
|
+
]);
|
|
3886
|
+
}
|
|
3887
|
+
if (process.platform === "win32") {
|
|
3888
|
+
return !!getWindowsBrowserExecutable(normalized);
|
|
3889
|
+
}
|
|
3890
|
+
return !!findFirstCommand(
|
|
3891
|
+
normalized === "edge" ? ["microsoft-edge", "microsoft-edge-stable"] : ["google-chrome", "google-chrome-stable"]
|
|
3892
|
+
);
|
|
3893
|
+
}
|
|
3894
|
+
function getBrowserToLaunch() {
|
|
3895
|
+
const configured = normalizeBrowserName(process["env"].GECHO_BROWSER);
|
|
3896
|
+
if (configured) return configured;
|
|
3897
|
+
if (lastBrowserConnection?.browser) return lastBrowserConnection.browser;
|
|
3898
|
+
if (isBrowserInstalled("chrome")) return "chrome";
|
|
3899
|
+
if (isBrowserInstalled("edge")) return "edge";
|
|
3900
|
+
return "";
|
|
3901
|
+
}
|
|
3902
|
+
function getBrowserLaunchSpec(browser) {
|
|
3903
|
+
const normalized = normalizeBrowserName(browser);
|
|
3904
|
+
const appName = normalized === "edge" ? "Microsoft Edge" : "Google Chrome";
|
|
3905
|
+
if (!normalized) return null;
|
|
3906
|
+
if (process.platform === "darwin") {
|
|
3907
|
+
return {
|
|
3908
|
+
command: "open",
|
|
3909
|
+
args: ["-a", appName, "--args", "--new-window", "about:blank"]
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
if (process.platform === "win32") {
|
|
3913
|
+
const browserPath = getWindowsBrowserExecutable(normalized);
|
|
3914
|
+
if (browserPath) {
|
|
3915
|
+
return {
|
|
3916
|
+
command: browserPath,
|
|
3917
|
+
args: ["--new-window", "about:blank"]
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
const executable2 = normalized === "edge" ? "msedge" : "chrome";
|
|
3921
|
+
return {
|
|
3922
|
+
command: "cmd.exe",
|
|
3923
|
+
args: ["/d", "/s", "/c", "start", "", executable2, "--new-window", "about:blank"]
|
|
3924
|
+
};
|
|
3925
|
+
}
|
|
3926
|
+
const executable = findFirstCommand(
|
|
3927
|
+
normalized === "edge" ? ["microsoft-edge", "microsoft-edge-stable"] : ["google-chrome", "google-chrome-stable"]
|
|
3928
|
+
) || (normalized === "edge" ? "microsoft-edge" : "google-chrome");
|
|
3929
|
+
return {
|
|
3930
|
+
command: executable,
|
|
3931
|
+
args: ["--new-window", "about:blank"]
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3934
|
+
async function launchBrowserForExtension() {
|
|
3935
|
+
if (!AUTO_LAUNCH_BROWSER) {
|
|
3936
|
+
return { attempted: false, launched: false, reason: "disabled" };
|
|
3937
|
+
}
|
|
3938
|
+
const browser = getBrowserToLaunch();
|
|
3939
|
+
const spec = getBrowserLaunchSpec(browser);
|
|
3940
|
+
if (!spec) {
|
|
3941
|
+
return { attempted: false, launched: false, reason: "browser_unknown" };
|
|
3942
|
+
}
|
|
3943
|
+
if (browserLaunchPromise) return browserLaunchPromise;
|
|
3944
|
+
if (Date.now() - lastBrowserLaunchAt < AUTO_LAUNCH_BROWSER_COOLDOWN_MS) {
|
|
3945
|
+
return { attempted: false, launched: false, browser, reason: "cooldown" };
|
|
3946
|
+
}
|
|
3947
|
+
lastBrowserLaunchAt = Date.now();
|
|
3948
|
+
browserLaunchPromise = new Promise((resolve) => {
|
|
3949
|
+
if (AUTO_LAUNCH_BROWSER_DRY_RUN) {
|
|
3950
|
+
console.log(`\u{1F9EA} Browser launch dry run: ${browser}`);
|
|
3951
|
+
resolve({ attempted: true, launched: true, browser, dryRun: true });
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
let child;
|
|
3955
|
+
try {
|
|
3956
|
+
child = spawn(spec.command, spec.args, {
|
|
3957
|
+
// A detached Windows process starts in a separate process group. That
|
|
3958
|
+
// extra isolation is not needed for a GUI browser after unref(), and
|
|
3959
|
+
// can prevent an extension service worker from reconnecting when the
|
|
3960
|
+
// bridge itself was started by a desktop MCP host.
|
|
3961
|
+
detached: process.platform !== "win32",
|
|
3962
|
+
stdio: "ignore",
|
|
3963
|
+
windowsHide: true
|
|
3964
|
+
});
|
|
3965
|
+
} catch (e) {
|
|
3966
|
+
resolve({ attempted: true, launched: false, browser, reason: e.message });
|
|
3967
|
+
return;
|
|
3968
|
+
}
|
|
3969
|
+
child.once("error", (e) => {
|
|
3970
|
+
resolve({ attempted: true, launched: false, browser, reason: e.message });
|
|
3971
|
+
});
|
|
3972
|
+
child.once("spawn", () => {
|
|
3973
|
+
child.unref();
|
|
3974
|
+
console.log(`\u{1F310} Started ${browser} while waiting for the extension connection`);
|
|
3975
|
+
resolve({ attempted: true, launched: true, browser });
|
|
3976
|
+
});
|
|
3977
|
+
});
|
|
3978
|
+
try {
|
|
3979
|
+
return await browserLaunchPromise;
|
|
3980
|
+
} finally {
|
|
3981
|
+
browserLaunchPromise = null;
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
function waitForExtensionConnection(timeoutMs = EXTENSION_CONNECT_TIMEOUT_MS) {
|
|
3985
|
+
return new Promise((resolve) => {
|
|
3986
|
+
if (isExtensionConnected()) return resolve(true);
|
|
3987
|
+
let waited = 0;
|
|
3988
|
+
const checkTimer = setInterval(() => {
|
|
3989
|
+
waited += EXTENSION_CONNECT_POLL_MS;
|
|
3990
|
+
if (isExtensionConnected()) {
|
|
3991
|
+
clearInterval(checkTimer);
|
|
3992
|
+
resolve(true);
|
|
3993
|
+
} else if (waited >= timeoutMs) {
|
|
3994
|
+
clearInterval(checkTimer);
|
|
3995
|
+
resolve(false);
|
|
3996
|
+
}
|
|
3997
|
+
}, EXTENSION_CONNECT_POLL_MS);
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
async function ensureExtensionConnection(action) {
|
|
4001
|
+
if (isExtensionConnected()) {
|
|
4002
|
+
return { connected: true, launch: { attempted: false, launched: false, reason: "already_connected" } };
|
|
4003
|
+
}
|
|
4004
|
+
const launch = await launchBrowserForExtension();
|
|
4005
|
+
console.log(
|
|
4006
|
+
`\u23F3 Extension not connected yet. Waiting up to ${EXTENSION_CONNECT_TIMEOUT_MS / 1e3}s (action: ${action}, browser: ${launch.browser || "unknown"})`
|
|
4007
|
+
);
|
|
4008
|
+
const connected = await waitForExtensionConnection();
|
|
4009
|
+
return { connected, launch };
|
|
4010
|
+
}
|
|
4011
|
+
function extensionConnectionError(connection) {
|
|
4012
|
+
const launch = connection?.launch || {};
|
|
4013
|
+
const details = [];
|
|
4014
|
+
if (launch.reason === "browser_unknown") {
|
|
4015
|
+
details.push("Bridge has not identified a browser yet. Open Edge or Chrome with the Gecho extension once, then retry.");
|
|
4016
|
+
} else if (launch.reason === "disabled") {
|
|
4017
|
+
details.push("Automatic browser launch is disabled (GECHO_AUTO_LAUNCH_BROWSER=0).");
|
|
4018
|
+
} else if (launch.reason === "cooldown") {
|
|
4019
|
+
details.push(`${launch.browser} was started recently; it did not reconnect in time.`);
|
|
4020
|
+
} else if (launch.attempted && !launch.launched) {
|
|
4021
|
+
details.push(`Failed to start ${launch.browser || "the saved browser"}: ${launch.reason || "unknown error"}`);
|
|
4022
|
+
} else if (launch.attempted) {
|
|
4023
|
+
details.push(`${launch.browser} was started, but its extension did not connect in time.`);
|
|
4024
|
+
}
|
|
4025
|
+
return [
|
|
4026
|
+
"Extension not connected.",
|
|
4027
|
+
...details,
|
|
4028
|
+
"Check that the Gecho extension is installed, enabled, and signed in for that browser profile."
|
|
4029
|
+
].join(" ");
|
|
4030
|
+
}
|
|
4031
|
+
lastBrowserConnection = loadLastBrowserConnection();
|
|
3771
4032
|
function sanitizeJobForDisk(job = {}) {
|
|
3772
4033
|
const copy = { ...job };
|
|
3773
4034
|
delete copy.data;
|
|
@@ -4197,12 +4458,13 @@ function hydrateJobWithDataFromFile(job) {
|
|
|
4197
4458
|
return job;
|
|
4198
4459
|
}
|
|
4199
4460
|
}
|
|
4200
|
-
function runAsyncAttempt({ jobId, action, params, payload, attempt }) {
|
|
4201
|
-
|
|
4461
|
+
async function runAsyncAttempt({ jobId, action, params, payload, attempt }) {
|
|
4462
|
+
const connection = await ensureExtensionConnection(action);
|
|
4463
|
+
if (!connection.connected) {
|
|
4202
4464
|
updateJob(jobId, {
|
|
4203
4465
|
status: "error",
|
|
4204
4466
|
stage: "extension_disconnected",
|
|
4205
|
-
error:
|
|
4467
|
+
error: extensionConnectionError(connection)
|
|
4206
4468
|
});
|
|
4207
4469
|
return;
|
|
4208
4470
|
}
|
|
@@ -4291,8 +4553,10 @@ function gracefulShutdown(reason) {
|
|
|
4291
4553
|
}
|
|
4292
4554
|
}
|
|
4293
4555
|
var wss = new WebSocketServer({ port: WS_PORT, host: "127.0.0.1" });
|
|
4294
|
-
wss.on("connection", (ws) => {
|
|
4295
|
-
|
|
4556
|
+
wss.on("connection", (ws, request) => {
|
|
4557
|
+
const browser = detectBrowserFromUserAgent(request?.headers?.["user-agent"]);
|
|
4558
|
+
if (browser) persistLastBrowserConnection(browser);
|
|
4559
|
+
console.log(`\u2705 Browser extension connected to Service Layer${browser ? ` (${browser})` : ""}`);
|
|
4296
4560
|
extensionSocket = ws;
|
|
4297
4561
|
ws.on("message", (message) => {
|
|
4298
4562
|
try {
|
|
@@ -4349,8 +4613,11 @@ wss.on("connection", (ws) => {
|
|
|
4349
4613
|
}
|
|
4350
4614
|
});
|
|
4351
4615
|
ws.on("close", () => {
|
|
4352
|
-
console.log("\u274C
|
|
4353
|
-
extensionSocket = null;
|
|
4616
|
+
console.log("\u274C Browser extension disconnected");
|
|
4617
|
+
if (extensionSocket === ws) extensionSocket = null;
|
|
4618
|
+
});
|
|
4619
|
+
ws.on("error", (error) => {
|
|
4620
|
+
console.warn(`Browser extension WebSocket error: ${error.message}`);
|
|
4354
4621
|
});
|
|
4355
4622
|
});
|
|
4356
4623
|
loadAsyncJobsFromDisk();
|
|
@@ -4378,7 +4645,13 @@ var server = http.createServer(async (req, res) => {
|
|
|
4378
4645
|
servicePath: __filename,
|
|
4379
4646
|
startedAt: SERVICE_STARTED_AT,
|
|
4380
4647
|
sourceMtimeMs: getServiceSourceMtimeMs(),
|
|
4381
|
-
dataDir: JOBS_DIR
|
|
4648
|
+
dataDir: JOBS_DIR,
|
|
4649
|
+
extensionConnected: isExtensionConnected(),
|
|
4650
|
+
lastConnectedBrowser: lastBrowserConnection?.browser || null,
|
|
4651
|
+
autoBrowserLaunch: {
|
|
4652
|
+
enabled: AUTO_LAUNCH_BROWSER,
|
|
4653
|
+
browser: getBrowserToLaunch() || null
|
|
4654
|
+
}
|
|
4382
4655
|
}));
|
|
4383
4656
|
}
|
|
4384
4657
|
if (req.method === "POST" && req.url === "/shutdown") {
|
|
@@ -4431,29 +4704,10 @@ var server = http.createServer(async (req, res) => {
|
|
|
4431
4704
|
res.statusCode = 400;
|
|
4432
4705
|
return res.end(JSON.stringify({ error: "Missing action" }));
|
|
4433
4706
|
}
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
let waited = 0;
|
|
4439
|
-
await new Promise((resolve) => {
|
|
4440
|
-
const checkTimer = setInterval(() => {
|
|
4441
|
-
waited += checkIntervalMs;
|
|
4442
|
-
if (extensionSocket && extensionSocket.readyState === 1) {
|
|
4443
|
-
clearInterval(checkTimer);
|
|
4444
|
-
resolve();
|
|
4445
|
-
} else if (waited >= maxWaitMs) {
|
|
4446
|
-
clearInterval(checkTimer);
|
|
4447
|
-
resolve();
|
|
4448
|
-
}
|
|
4449
|
-
}, checkIntervalMs);
|
|
4450
|
-
});
|
|
4451
|
-
if (!extensionSocket || extensionSocket.readyState !== 1) {
|
|
4452
|
-
res.statusCode = 503;
|
|
4453
|
-
return res.end(JSON.stringify({
|
|
4454
|
-
error: "Extension not connected. Chrome \u63D2\u4EF6\u672A\u8FDE\u63A5\u3002\n\u8BF7\u68C0\u67E5\uFF1A\n1. Chrome \u662F\u5426\u4FDD\u6301\u6253\u5F00\u72B6\u6001\n2. Gecho TikTok Bridge \u63D2\u4EF6\u662F\u5426\u4ECD\u5728\u8FD0\u884C\uFF08\u6269\u5C55\u56FE\u6807\u662F\u5426\u4EAE\u7740\uFF09\n3. TikTok tab \u662F\u5426\u6D3B\u8DC3\uFF08\u6700\u597D\u5728 tiktok.com \u9875\u9762\u4E0A\uFF09\n\u5982\u679C\u63D2\u4EF6\u521A\u542F\u52A8\uFF0C\u8BF7\u7B49\u5F85\u51E0\u79D2\u540E\u518D\u8BD5\u3002"
|
|
4455
|
-
}));
|
|
4456
|
-
}
|
|
4707
|
+
const connection = await ensureExtensionConnection(action);
|
|
4708
|
+
if (!connection.connected) {
|
|
4709
|
+
res.statusCode = 503;
|
|
4710
|
+
return res.end(JSON.stringify({ error: extensionConnectionError(connection) }));
|
|
4457
4711
|
}
|
|
4458
4712
|
const jobId = `job-${Date.now()}-${requestIdCounter++}`;
|
|
4459
4713
|
console.log(`\u{1F680} Dispatching ASYNC action: [${action}], jobId: ${jobId}`);
|
|
@@ -4509,29 +4763,10 @@ var server = http.createServer(async (req, res) => {
|
|
|
4509
4763
|
res.statusCode = 400;
|
|
4510
4764
|
return res.end(JSON.stringify({ error: "Missing action" }));
|
|
4511
4765
|
}
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
let waited = 0;
|
|
4517
|
-
await new Promise((resolve) => {
|
|
4518
|
-
const checkTimer = setInterval(() => {
|
|
4519
|
-
waited += checkIntervalMs;
|
|
4520
|
-
if (extensionSocket && extensionSocket.readyState === 1) {
|
|
4521
|
-
clearInterval(checkTimer);
|
|
4522
|
-
resolve();
|
|
4523
|
-
} else if (waited >= maxWaitMs) {
|
|
4524
|
-
clearInterval(checkTimer);
|
|
4525
|
-
resolve();
|
|
4526
|
-
}
|
|
4527
|
-
}, checkIntervalMs);
|
|
4528
|
-
});
|
|
4529
|
-
if (!extensionSocket || extensionSocket.readyState !== 1) {
|
|
4530
|
-
res.statusCode = 503;
|
|
4531
|
-
return res.end(JSON.stringify({
|
|
4532
|
-
error: "Extension not connected. Chrome \u63D2\u4EF6\u672A\u8FDE\u63A5\u3002\n\u8BF7\u68C0\u67E5\uFF1A\n1. Chrome \u662F\u5426\u4FDD\u6301\u6253\u5F00\u72B6\u6001\n2. Gecho TikTok Bridge \u63D2\u4EF6\u662F\u5426\u4ECD\u5728\u8FD0\u884C\uFF08\u6269\u5C55\u56FE\u6807\u662F\u5426\u4EAE\u7740\uFF09\n3. TikTok tab \u662F\u5426\u6D3B\u8DC3\uFF08\u6700\u597D\u5728 tiktok.com \u9875\u9762\u4E0A\uFF09\n\u5982\u679C\u63D2\u4EF6\u521A\u542F\u52A8\uFF0C\u8BF7\u7B49\u5F85\u51E0\u79D2\u540E\u518D\u8BD5\u3002"
|
|
4533
|
-
}));
|
|
4534
|
-
}
|
|
4766
|
+
const connection = await ensureExtensionConnection(action);
|
|
4767
|
+
if (!connection.connected) {
|
|
4768
|
+
res.statusCode = 503;
|
|
4769
|
+
return res.end(JSON.stringify({ error: extensionConnectionError(connection) }));
|
|
4535
4770
|
}
|
|
4536
4771
|
console.log(`\u{1F680} Dispatching action: [${action}]`);
|
|
4537
4772
|
const requestId = `svc-${Date.now()}-${requestIdCounter++}`;
|