mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-19 19:40:49 +08:00
适配 Bailian WebParser 兼容接入并完善 sidecar 文档。
新增 WebParser 兼容工具与 MCP health/tools-sync 兼容模式,让 WebParser/sse 可注册到 agent 并暴露明确 schema;同时修复 mcp argv 环境变量展开并补齐微信 sidecar 纯本地化安装与文档说明。 Made-with: Cursor
This commit is contained in:
parent
329fb2239a
commit
df91434936
16 changed files with 640 additions and 56 deletions
|
|
@ -1,24 +1,76 @@
|
|||
import { spawn } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
function resolveLocalOpenclawBin(): string {
|
||||
// Prefer local openclaw installed in the sidecar runtime (no global CLI required).
|
||||
// Windows: node_modules/.bin/openclaw.cmd
|
||||
return path.join(process.cwd(), "node_modules", ".bin", process.platform === "win32" ? "openclaw.cmd" : "openclaw");
|
||||
const STATE_DIR = (process.env.OCLAW_STATE_DIR || path.resolve(process.cwd(), "state")).trim();
|
||||
|
||||
function resolvePluginRoot(): string {
|
||||
const configured = String(process.env.OCLAW_WEIXIN_PLUGIN_ROOT || "").trim();
|
||||
return configured || path.join(process.cwd(), "node_modules", "@tencent-weixin", "openclaw-weixin");
|
||||
}
|
||||
|
||||
function run(): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const openclawBin = resolveLocalOpenclawBin();
|
||||
const child = spawn(openclawBin, ["channels", "login", "--channel", "openclaw-weixin"], {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
child.on("error", reject);
|
||||
child.on("exit", (code) => resolve(code ?? 1));
|
||||
async function importFromPluginSrc(relativePath: string): Promise<any> {
|
||||
const pluginRoot = resolvePluginRoot();
|
||||
const srcRoot = path.join(pluginRoot, "src");
|
||||
const fullPath = path.join(srcRoot, relativePath);
|
||||
return import(pathToFileURL(fullPath).href);
|
||||
}
|
||||
|
||||
async function run(): Promise<void> {
|
||||
// Force the plugin's state-dir resolver away from ~/.openclaw
|
||||
process.env.OPENCLAW_STATE_DIR = STATE_DIR;
|
||||
|
||||
const loginQr = await importFromPluginSrc(path.join("auth", "login-qr.ts"));
|
||||
const accounts = await importFromPluginSrc(path.join("auth", "accounts.ts"));
|
||||
|
||||
const startWeixinLoginWithQr = loginQr.startWeixinLoginWithQr as ((opts: any) => Promise<any>) | undefined;
|
||||
const waitForWeixinLogin = loginQr.waitForWeixinLogin as ((opts: any) => Promise<any>) | undefined;
|
||||
const displayQRCode = loginQr.displayQRCode as ((qrcodeUrl: string) => Promise<void>) | undefined;
|
||||
const registerWeixinAccountId = accounts.registerWeixinAccountId as ((accountId: string) => void) | undefined;
|
||||
const saveWeixinAccount = accounts.saveWeixinAccount as ((accountId: string, update: any) => void) | undefined;
|
||||
|
||||
if (!startWeixinLoginWithQr || !waitForWeixinLogin || !displayQRCode) {
|
||||
throw new Error("weixin plugin login-qr module missing exports");
|
||||
}
|
||||
if (!registerWeixinAccountId || !saveWeixinAccount) {
|
||||
throw new Error("weixin plugin accounts module missing exports");
|
||||
}
|
||||
|
||||
const apiBaseUrl = "https://ilinkai.weixin.qq.com";
|
||||
const botType = process.env.OCLAW_WEIXIN_BOT_TYPE?.trim();
|
||||
|
||||
const start = await startWeixinLoginWithQr({
|
||||
apiBaseUrl,
|
||||
botType,
|
||||
});
|
||||
if (!start.qrcodeUrl) {
|
||||
throw new Error(start.message || "failed to start login");
|
||||
}
|
||||
|
||||
process.stdout.write(`${start.message}\n`);
|
||||
await displayQRCode(start.qrcodeUrl);
|
||||
|
||||
const result = await waitForWeixinLogin({
|
||||
sessionKey: start.sessionKey,
|
||||
apiBaseUrl,
|
||||
botType,
|
||||
});
|
||||
|
||||
process.stdout.write(`${result.message}\n`);
|
||||
if (!result.connected || !result.accountId || !result.botToken) {
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
registerWeixinAccountId(result.accountId);
|
||||
saveWeixinAccount(result.accountId, {
|
||||
token: result.botToken,
|
||||
baseUrl: result.baseUrl,
|
||||
userId: result.userId,
|
||||
});
|
||||
process.stdout.write(`saved account=${result.accountId} into ${STATE_DIR}\n`);
|
||||
}
|
||||
|
||||
void run().then((code) => {
|
||||
process.exitCode = code;
|
||||
void run().catch((err) => {
|
||||
process.stderr.write(`weixin login failed: ${String(err)}\n`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
|
|
@ -89,13 +88,9 @@ function sleep(ms: number): Promise<void> {
|
|||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function homeOpenclawPath(...parts: string[]): string {
|
||||
return path.join(os.homedir(), ".openclaw", ...parts);
|
||||
}
|
||||
|
||||
function resolvePluginRoot(): string {
|
||||
const configured = String(process.env.OCLAW_WEIXIN_PLUGIN_ROOT || "").trim();
|
||||
return configured || homeOpenclawPath("extensions", "openclaw-weixin");
|
||||
return configured || path.join(process.cwd(), "node_modules", "@tencent-weixin", "openclaw-weixin");
|
||||
}
|
||||
|
||||
async function loadOfficialModules(): Promise<OfficialModules> {
|
||||
|
|
@ -130,19 +125,34 @@ async function loadOfficialModules(): Promise<OfficialModules> {
|
|||
return officialModulesPromise;
|
||||
}
|
||||
|
||||
function resolveAccount(): { accountId: string; token: string; cloudBaseUrl: string } {
|
||||
const ids = readJsonFile<string[]>(homeOpenclawPath("openclaw-weixin", "accounts.json")) || [];
|
||||
async function resolveAccount(): Promise<{ accountId: string; token: string; cloudBaseUrl: string }> {
|
||||
if (!String(process.env.OPENCLAW_STATE_DIR || "").trim()) {
|
||||
process.env.OPENCLAW_STATE_DIR = STATE_DIR;
|
||||
}
|
||||
const pluginRoot = resolvePluginRoot();
|
||||
const srcRoot = path.join(pluginRoot, "src");
|
||||
const importTs = async (relativePath: string): Promise<any> => {
|
||||
const fullPath = path.join(srcRoot, relativePath);
|
||||
return import(pathToFileURL(fullPath).href);
|
||||
};
|
||||
const accountsMod = await importTs(path.join("auth", "accounts.ts"));
|
||||
const listIds = accountsMod.listIndexedWeixinAccountIds as (() => string[]) | undefined;
|
||||
const loadAcc = accountsMod.loadWeixinAccount as ((accountId: string) => any) | undefined;
|
||||
if (!listIds || !loadAcc) {
|
||||
throw new Error("weixin plugin accounts module missing exports");
|
||||
}
|
||||
const ids = listIds() || [];
|
||||
const accountId = String(ids[0] || "").trim();
|
||||
if (!accountId) {
|
||||
throw new Error("no weixin account id found; run login first");
|
||||
}
|
||||
const account = readJsonFile<Json>(homeOpenclawPath("openclaw-weixin", "accounts", `${accountId}.json`)) || {};
|
||||
const token = String(account.token || "").trim();
|
||||
const data = loadAcc(accountId) || {};
|
||||
const token = String(data.token || "").trim();
|
||||
if (!token) {
|
||||
throw new Error(`missing token for account ${accountId}; run login again`);
|
||||
}
|
||||
const envCloud = String(process.env.OCLAW_WEIXIN_CLOUD_BASE_URL || "").trim();
|
||||
const cfgCloud = String(account.baseUrl || "").trim();
|
||||
const cfgCloud = String(data.baseUrl || "").trim();
|
||||
const cloudBaseUrl = (envCloud || cfgCloud || "https://ilinkai.weixin.qq.com").trim();
|
||||
return { accountId, token, cloudBaseUrl };
|
||||
}
|
||||
|
|
@ -424,7 +434,7 @@ async function main(): Promise<void> {
|
|||
state.user_context_tokens && typeof state.user_context_tokens === "object"
|
||||
? (state.user_context_tokens as TokenMap)
|
||||
: {};
|
||||
const { accountId, token, cloudBaseUrl } = resolveAccount();
|
||||
const { accountId, token, cloudBaseUrl } = await resolveAccount();
|
||||
const modules = await loadOfficialModules();
|
||||
modules.restoreContextTokens(accountId);
|
||||
log(`official runner started account=${accountId} cloud=${cloudBaseUrl} local=${LOCAL_BASE_URL}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue