

























@@ -1,40 +1,41 @@
11import { rm } from "node:fs/promises";
22import os from "node:os";
33import path from "node:path";
4+import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
45import {
56normalizeLowercaseStringOrEmpty,
67normalizeOptionalString,
78} from "openclaw/plugin-sdk/text-runtime";
8-import {
9- clearDeviceBootstrapTokens,
10- definePluginEntry,
11- issueDeviceBootstrapToken,
12- listDevicePairing,
13- PAIRING_SETUP_BOOTSTRAP_PROFILE,
14- renderQrPngDataUrl,
15- writeQrPngTempFile,
16- revokeDeviceBootstrapToken,
17- resolveGatewayBindUrl,
18- resolveGatewayPort,
19- resolvePreferredOpenClawTmpDir,
20-runPluginCommandWithTimeout,
21-resolveTailnetHostWithRunner,
22- type OpenClawPluginApi,
23-} from "./api.js";
24-import {
25-armPairNotifyOnce,
26-formatPendingRequests,
27- handleNotifyCommand,
28- registerPairingNotifierService,
29-} from "./notify.js";
30-import {
31-approvePendingPairingRequest,
32- selectPendingApprovalRequest,
33-} from "./pair-command-approve.js";
34-import {
35-buildMissingPairingScopeReply,
36-resolvePairingCommandAuthState,
37-} from "./pair-command-auth.js";
9+10+type DevicePairApiModule = typeof import("./api.js");
11+type NotifyModule = typeof import("./notify.js");
12+type PairCommandApproveModule = typeof import("./pair-command-approve.js");
13+type PairCommandAuthModule = typeof import("./pair-command-auth.js");
14+15+let devicePairApiModulePromise: Promise<DevicePairApiModule> | undefined;
16+let notifyModulePromise: Promise<NotifyModule> | undefined;
17+let pairCommandApproveModulePromise: Promise<PairCommandApproveModule> | undefined;
18+let pairCommandAuthModulePromise: Promise<PairCommandAuthModule> | undefined;
19+20+function loadDevicePairApiModule(): Promise<DevicePairApiModule> {
21+devicePairApiModulePromise ??= import("./api.js");
22+return devicePairApiModulePromise;
23+}
24+25+function loadNotifyModule(): Promise<NotifyModule> {
26+notifyModulePromise ??= import("./notify.js");
27+return notifyModulePromise;
28+}
29+30+function loadPairCommandApproveModule(): Promise<PairCommandApproveModule> {
31+ pairCommandApproveModulePromise ??= import("./pair-command-approve.js");
32+return pairCommandApproveModulePromise;
33+}
34+35+function loadPairCommandAuthModule(): Promise<PairCommandAuthModule> {
36+pairCommandAuthModulePromise ??= import("./pair-command-auth.js");
37+return pairCommandAuthModulePromise;
38+}
38393940function formatDurationMinutes(expiresAtMs: number): string {
4041const msRemaining = Math.max(0, expiresAtMs - Date.now());
@@ -254,6 +255,8 @@ function pickTailnetIPv4(): string | null {
254255}
255256256257async function resolveTailnetHost(): Promise<string | null> {
258+const { resolveTailnetHostWithRunner, runPluginCommandWithTimeout } =
259+await loadDevicePairApiModule();
257260return await resolveTailnetHostWithRunner((argv, opts) =>
258261runPluginCommandWithTimeout({
259262 argv,
@@ -307,6 +310,7 @@ function resolveRequiredAuthLabel(
307310}
308311309312async function resolveGatewayUrl(api: OpenClawPluginApi): Promise<ResolveUrlResult> {
313+const { resolveGatewayBindUrl, resolveGatewayPort } = await loadDevicePairApiModule();
310314const cfg = api.config;
311315const pluginCfg = (api.pluginConfig ?? {}) as DevicePairPluginConfig;
312316const scheme = resolveScheme(cfg);
@@ -511,6 +515,8 @@ function issuesPairSetupCode(action: string): boolean {
511515}
512516513517async function issueSetupPayload(url: string): Promise<SetupPayload> {
518+const { issueDeviceBootstrapToken, PAIRING_SETUP_BOOTSTRAP_PROFILE } =
519+await loadDevicePairApiModule();
514520const issuedBootstrap = await issueDeviceBootstrapToken({
515521profile: PAIRING_SETUP_BOOTSTRAP_PROFILE,
516522});
@@ -558,7 +564,19 @@ export default definePluginEntry({
558564name: "Device Pair",
559565description: "QR/bootstrap pairing helpers for OpenClaw devices",
560566register(api: OpenClawPluginApi) {
561-registerPairingNotifierService(api);
567+let notifierService: ReturnType<NotifyModule["createPairingNotifierService"]> | undefined;
568+api.registerService({
569+id: "device-pair-notifier",
570+start: async (ctx) => {
571+const { createPairingNotifierService } = await loadNotifyModule();
572+notifierService = createPairingNotifierService(api);
573+await notifierService.start(ctx);
574+},
575+stop: async (ctx) => {
576+await notifierService?.stop?.(ctx);
577+notifierService = undefined;
578+},
579+});
562580563581api.registerCommand({
564582name: "pair",
@@ -571,6 +589,8 @@ export default definePluginEntry({
571589const gatewayClientScopes = Array.isArray(ctx.gatewayClientScopes)
572590 ? ctx.gatewayClientScopes
573591 : undefined;
592+const { buildMissingPairingScopeReply, resolvePairingCommandAuthState } =
593+await loadPairCommandAuthModule();
574594const authState = resolvePairingCommandAuthState({
575595channel: ctx.channel,
576596 gatewayClientScopes,
@@ -582,12 +602,17 @@ export default definePluginEntry({
582602);
583603584604if (action === "status" || action === "pending") {
605+const [{ listDevicePairing }, { formatPendingRequests }] = await Promise.all([
606+loadDevicePairApiModule(),
607+loadNotifyModule(),
608+]);
585609const list = await listDevicePairing();
586610return { text: formatPendingRequests(list.pending) };
587611}
588612589613if (action === "notify") {
590614const notifyAction = normalizeLowercaseStringOrEmpty(tokens[1]) || "status";
615+const { handleNotifyCommand } = await loadNotifyModule();
591616return await handleNotifyCommand({
592617 api,
593618 ctx,
@@ -599,6 +624,10 @@ export default definePluginEntry({
599624if (authState.isMissingInternalPairingPrivilege) {
600625return buildMissingPairingScopeReply();
601626}
627+const [
628+{ listDevicePairing },
629+{ approvePendingPairingRequest, selectPendingApprovalRequest },
630+] = await Promise.all([loadDevicePairApiModule(), loadPairCommandApproveModule()]);
602631const list = await listDevicePairing();
603632const selected = selectPendingApprovalRequest({
604633pending: list.pending,
@@ -621,6 +650,7 @@ export default definePluginEntry({
621650if (authState.isMissingInternalPairingPrivilege) {
622651return buildMissingPairingScopeReply();
623652}
653+const { clearDeviceBootstrapTokens } = await loadDevicePairApiModule();
624654const cleared = await clearDeviceBootstrapTokens();
625655return {
626656text:
@@ -651,6 +681,7 @@ export default definePluginEntry({
651681652682if (channel === "telegram" && target) {
653683try {
684+const { armPairNotifyOnce } = await loadNotifyModule();
654685autoNotifyArmed = await armPairNotifyOnce({ api, ctx });
655686} catch (err) {
656687api.logger.warn?.(
@@ -672,6 +703,8 @@ export default definePluginEntry({
672703if (target && canSendQrPngToChannel(channel)) {
673704let qrFilePath: string | undefined;
674705try {
706+const { resolvePreferredOpenClawTmpDir, writeQrPngTempFile } =
707+await loadDevicePairApiModule();
675708qrFilePath = (
676709await writeQrPngTempFile(setupCode, {
677710tmpRoot: resolvePreferredOpenClawTmpDir(),
@@ -697,6 +730,7 @@ export default definePluginEntry({
697730};
698731}
699732} catch (err) {
733+const { revokeDeviceBootstrapToken } = await loadDevicePairApiModule();
700734api.logger.warn?.(
701735`device-pair: QR image send failed channel=${channel}, falling back (${(err as Error)?.message ?? err})`,
702736);
@@ -716,8 +750,10 @@ export default definePluginEntry({
716750if (channel === "webchat") {
717751let qrDataUrl: string;
718752try {
753+const { renderQrPngDataUrl } = await loadDevicePairApiModule();
719754qrDataUrl = await renderQrPngDataUrl(setupCode);
720755} catch (err) {
756+const { revokeDeviceBootstrapToken } = await loadDevicePairApiModule();
721757api.logger.warn?.(
722758`device-pair: webchat QR render failed, falling back (${(err as Error)?.message ?? err})`,
723759);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。