@nextclaw/openclaw-compat 0.3.34 → 0.3.36
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/dist/index.d.ts +80 -45
- package/dist/index.js +672 -46
- package/package.json +15 -15
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _nextclaw_core from '@nextclaw/core';
|
|
2
|
-
import { Config, ExtensionChannel, AgentEngineFactory } from '@nextclaw/core';
|
|
2
|
+
import { Config, ExtensionChannel, ProviderRuntimeResolution, AgentEngineFactory } from '@nextclaw/core';
|
|
3
3
|
import { NcpAgentRuntime } from '@nextclaw/ncp';
|
|
4
4
|
import { RuntimeFactoryParams } from '@nextclaw/ncp-toolkit';
|
|
5
5
|
|
|
@@ -328,6 +328,21 @@ type PluginReplyDispatchParams = {
|
|
|
328
328
|
};
|
|
329
329
|
type PluginRuntime = {
|
|
330
330
|
version: string;
|
|
331
|
+
agent: {
|
|
332
|
+
defaults: {
|
|
333
|
+
model: string;
|
|
334
|
+
workspace: string;
|
|
335
|
+
maxToolIterations: number;
|
|
336
|
+
};
|
|
337
|
+
resolveWorkspacePath: (workspace?: string) => string;
|
|
338
|
+
resolveProviderRuntime: (model?: string) => ProviderRuntimeResolution;
|
|
339
|
+
buildRuntimeUserPrompt: (params: {
|
|
340
|
+
workspace?: string;
|
|
341
|
+
sessionKey?: string;
|
|
342
|
+
metadata?: Record<string, unknown>;
|
|
343
|
+
userMessage: string;
|
|
344
|
+
}) => string;
|
|
345
|
+
};
|
|
331
346
|
config: {
|
|
332
347
|
loadConfig: () => Record<string, unknown>;
|
|
333
348
|
writeConfigFile: (next: Record<string, unknown>) => Promise<void>;
|
|
@@ -644,6 +659,69 @@ declare function installPluginFromPath(params: {
|
|
|
644
659
|
|
|
645
660
|
declare function buildPluginLoaderAliases(pluginRoot?: string): Record<string, string>;
|
|
646
661
|
|
|
662
|
+
type PluginRegisterRuntime = {
|
|
663
|
+
config: Config;
|
|
664
|
+
workspaceDir: string;
|
|
665
|
+
logger: PluginLogger;
|
|
666
|
+
registry: PluginRegistry;
|
|
667
|
+
toolNameOwners: Map<string, string>;
|
|
668
|
+
channelIdOwners: Map<string, string>;
|
|
669
|
+
providerIdOwners: Map<string, string>;
|
|
670
|
+
engineKindOwners: Map<string, string>;
|
|
671
|
+
ncpAgentRuntimeKindOwners: Map<string, string>;
|
|
672
|
+
resolvedToolNames: Set<string>;
|
|
673
|
+
reservedToolNames: Set<string>;
|
|
674
|
+
reservedChannelIds: Set<string>;
|
|
675
|
+
reservedProviderIds: Set<string>;
|
|
676
|
+
reservedEngineKinds: Set<string>;
|
|
677
|
+
reservedNcpAgentRuntimeKinds: Set<string>;
|
|
678
|
+
};
|
|
679
|
+
declare function createPluginRegisterRuntime(params: {
|
|
680
|
+
config: Config;
|
|
681
|
+
workspaceDir: string;
|
|
682
|
+
logger: PluginLogger;
|
|
683
|
+
registry: PluginRegistry;
|
|
684
|
+
reservedToolNames: Set<string>;
|
|
685
|
+
reservedChannelIds: Set<string>;
|
|
686
|
+
reservedProviderIds: Set<string>;
|
|
687
|
+
reservedEngineKinds: Set<string>;
|
|
688
|
+
reservedNcpAgentRuntimeKinds: Set<string>;
|
|
689
|
+
}): PluginRegisterRuntime;
|
|
690
|
+
declare function registerPluginWithApi(params: {
|
|
691
|
+
runtime: PluginRegisterRuntime;
|
|
692
|
+
record: PluginRecord;
|
|
693
|
+
pluginId: string;
|
|
694
|
+
source: string;
|
|
695
|
+
rootDir: string;
|
|
696
|
+
register: (api: OpenClawPluginApi) => void | Promise<void>;
|
|
697
|
+
pluginConfig?: Record<string, unknown>;
|
|
698
|
+
}): {
|
|
699
|
+
ok: true;
|
|
700
|
+
} | {
|
|
701
|
+
ok: false;
|
|
702
|
+
error: string;
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
type ProgressivePluginLoadOptions = {
|
|
706
|
+
config: Config;
|
|
707
|
+
workspaceDir?: string;
|
|
708
|
+
logger?: PluginLogger;
|
|
709
|
+
mode?: "full" | "validate";
|
|
710
|
+
excludeRoots?: string[];
|
|
711
|
+
reservedToolNames?: string[];
|
|
712
|
+
reservedChannelIds?: string[];
|
|
713
|
+
reservedProviderIds?: string[];
|
|
714
|
+
reservedEngineKinds?: string[];
|
|
715
|
+
reservedNcpAgentRuntimeKinds?: string[];
|
|
716
|
+
onPluginProcessed?: (params: {
|
|
717
|
+
loadedPluginCount: number;
|
|
718
|
+
pluginId?: string;
|
|
719
|
+
}) => void;
|
|
720
|
+
yieldToEventLoop?: () => Promise<void>;
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
declare function loadOpenClawPluginsProgressively(options: ProgressivePluginLoadOptions): Promise<PluginRegistry>;
|
|
724
|
+
|
|
647
725
|
type PluginLoadOptions = {
|
|
648
726
|
config: Config;
|
|
649
727
|
workspaceDir?: string;
|
|
@@ -724,49 +802,6 @@ declare function createPluginRuntime(params: {
|
|
|
724
802
|
config?: Config;
|
|
725
803
|
}): PluginRuntime;
|
|
726
804
|
|
|
727
|
-
type PluginRegisterRuntime = {
|
|
728
|
-
config: Config;
|
|
729
|
-
workspaceDir: string;
|
|
730
|
-
logger: PluginLogger;
|
|
731
|
-
registry: PluginRegistry;
|
|
732
|
-
toolNameOwners: Map<string, string>;
|
|
733
|
-
channelIdOwners: Map<string, string>;
|
|
734
|
-
providerIdOwners: Map<string, string>;
|
|
735
|
-
engineKindOwners: Map<string, string>;
|
|
736
|
-
ncpAgentRuntimeKindOwners: Map<string, string>;
|
|
737
|
-
resolvedToolNames: Set<string>;
|
|
738
|
-
reservedToolNames: Set<string>;
|
|
739
|
-
reservedChannelIds: Set<string>;
|
|
740
|
-
reservedProviderIds: Set<string>;
|
|
741
|
-
reservedEngineKinds: Set<string>;
|
|
742
|
-
reservedNcpAgentRuntimeKinds: Set<string>;
|
|
743
|
-
};
|
|
744
|
-
declare function createPluginRegisterRuntime(params: {
|
|
745
|
-
config: Config;
|
|
746
|
-
workspaceDir: string;
|
|
747
|
-
logger: PluginLogger;
|
|
748
|
-
registry: PluginRegistry;
|
|
749
|
-
reservedToolNames: Set<string>;
|
|
750
|
-
reservedChannelIds: Set<string>;
|
|
751
|
-
reservedProviderIds: Set<string>;
|
|
752
|
-
reservedEngineKinds: Set<string>;
|
|
753
|
-
reservedNcpAgentRuntimeKinds: Set<string>;
|
|
754
|
-
}): PluginRegisterRuntime;
|
|
755
|
-
declare function registerPluginWithApi(params: {
|
|
756
|
-
runtime: PluginRegisterRuntime;
|
|
757
|
-
record: PluginRecord;
|
|
758
|
-
pluginId: string;
|
|
759
|
-
source: string;
|
|
760
|
-
rootDir: string;
|
|
761
|
-
register: (api: OpenClawPluginApi) => void | Promise<void>;
|
|
762
|
-
pluginConfig?: Record<string, unknown>;
|
|
763
|
-
}): {
|
|
764
|
-
ok: true;
|
|
765
|
-
} | {
|
|
766
|
-
ok: false;
|
|
767
|
-
error: string;
|
|
768
|
-
};
|
|
769
|
-
|
|
770
805
|
declare function validateJsonSchemaValue(params: {
|
|
771
806
|
schema: Record<string, unknown>;
|
|
772
807
|
cacheKey: string;
|
|
@@ -838,4 +873,4 @@ declare function uninstallPlugin(params: {
|
|
|
838
873
|
extensionsDir?: string;
|
|
839
874
|
}): Promise<UninstallPluginResult>;
|
|
840
875
|
|
|
841
|
-
export { DEFAULT_ACCOUNT_ID, type InstallPluginResult, type NormalizedPluginsConfig, type OpenClawChannelAgentPrompt, type OpenClawChannelAuth, type OpenClawChannelAuthLoginResult, type OpenClawChannelAuthPollResult, type OpenClawChannelAuthStartResult, type OpenClawChannelConfigAdapter, type OpenClawChannelConfigSchema, type OpenClawChannelGateway, type OpenClawChannelGatewayStartContext, type OpenClawChannelPlugin, type OpenClawChannelSetup, type OpenClawPluginApi, type OpenClawPluginChannelRegistration, type OpenClawPluginConfigSchema, type OpenClawPluginDefinition, type OpenClawPluginEngineOptions, type OpenClawPluginModule, type OpenClawPluginNcpAgentRuntimeRegistration, type OpenClawPluginTool, type OpenClawPluginToolContext, type OpenClawPluginToolFactory, type OpenClawPluginToolOptions, type OpenClawProviderPlugin, PLUGIN_MANIFEST_FILENAME, PLUGIN_MANIFEST_FILENAMES, type PackageManifest, type PluginCandidate, type PluginChannelBinding, type PluginChannelGatewayHandle, type PluginChannelRegistration, type PluginConfigUiHint, type PluginDiagnostic, type PluginDiscoveryResult, type PluginEngineRegistration, type PluginInstallLogger, type PluginInstallSource, type PluginInstallUpdate, type PluginKind, type PluginLoadOptions, type PluginLogger, type PluginManifest, type PluginManifestLoadResult, type PluginManifestRecord, type PluginManifestRegistry, type PluginNcpAgentRuntimeRegistration, type PluginOrigin, type PluginProviderRegistration, type PluginRecord, type PluginRegisterRuntime, type PluginRegistry, type PluginReplyDispatchParams, type PluginRuntime, type PluginRuntimeBridge, type PluginStatusReport, type PluginToolRegistration, type PluginUiMetadata, type UninstallActions, type UninstallPluginResult, type _CompatOnly, __nextclawPluginSdkCompat, addPluginLoadPath, buildChannelConfigSchema, buildOauthProviderAuthResult, buildPluginLoaderAliases, buildPluginStatusReport, createNextclawBuiltinChannelPlugin, createPluginRegisterRuntime, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, discoverPluginStatusReport, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, mergePluginConfigView, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, registerPluginWithApi, removePluginFromConfig, resolveEnableState, resolvePluginChannelMessageToolHints, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, resolveUninstallDirectoryTargets, setPluginRuntimeBridge, sleep, startPluginChannelGateways, stopPluginChannelGateways, toPluginConfigView, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
|
|
876
|
+
export { DEFAULT_ACCOUNT_ID, type InstallPluginResult, type NormalizedPluginsConfig, type OpenClawChannelAgentPrompt, type OpenClawChannelAuth, type OpenClawChannelAuthLoginResult, type OpenClawChannelAuthPollResult, type OpenClawChannelAuthStartResult, type OpenClawChannelConfigAdapter, type OpenClawChannelConfigSchema, type OpenClawChannelGateway, type OpenClawChannelGatewayStartContext, type OpenClawChannelPlugin, type OpenClawChannelSetup, type OpenClawPluginApi, type OpenClawPluginChannelRegistration, type OpenClawPluginConfigSchema, type OpenClawPluginDefinition, type OpenClawPluginEngineOptions, type OpenClawPluginModule, type OpenClawPluginNcpAgentRuntimeRegistration, type OpenClawPluginTool, type OpenClawPluginToolContext, type OpenClawPluginToolFactory, type OpenClawPluginToolOptions, type OpenClawProviderPlugin, PLUGIN_MANIFEST_FILENAME, PLUGIN_MANIFEST_FILENAMES, type PackageManifest, type PluginCandidate, type PluginChannelBinding, type PluginChannelGatewayHandle, type PluginChannelRegistration, type PluginConfigUiHint, type PluginDiagnostic, type PluginDiscoveryResult, type PluginEngineRegistration, type PluginInstallLogger, type PluginInstallSource, type PluginInstallUpdate, type PluginKind, type PluginLoadOptions, type PluginLogger, type PluginManifest, type PluginManifestLoadResult, type PluginManifestRecord, type PluginManifestRegistry, type PluginNcpAgentRuntimeRegistration, type PluginOrigin, type PluginProviderRegistration, type PluginRecord, type PluginRegisterRuntime, type PluginRegistry, type PluginReplyDispatchParams, type PluginRuntime, type PluginRuntimeBridge, type PluginStatusReport, type PluginToolRegistration, type PluginUiMetadata, type ProgressivePluginLoadOptions, type UninstallActions, type UninstallPluginResult, type _CompatOnly, __nextclawPluginSdkCompat, addPluginLoadPath, buildChannelConfigSchema, buildOauthProviderAuthResult, buildPluginLoaderAliases, buildPluginStatusReport, createNextclawBuiltinChannelPlugin, createPluginRegisterRuntime, createPluginRuntime, disablePluginInConfig, discoverOpenClawPlugins, discoverPluginStatusReport, emptyPluginConfigSchema, enablePluginInConfig, getPackageManifestMetadata, getPluginChannelBindings, getPluginUiMetadataFromRegistry, installPluginFromArchive, installPluginFromDir, installPluginFromFile, installPluginFromNpmSpec, installPluginFromPath, loadOpenClawPlugins, loadOpenClawPluginsProgressively, loadPluginManifest, loadPluginManifestRegistry, loadPluginUiMetadata, mergePluginConfigView, normalizeAccountId, normalizePluginHttpPath, normalizePluginsConfig, recordPluginInstall, registerPluginWithApi, removePluginFromConfig, resolveEnableState, resolvePluginChannelMessageToolHints, resolvePluginInstallDir, resolvePluginManifestPath, resolveUninstallDirectoryTarget, resolveUninstallDirectoryTargets, setPluginRuntimeBridge, sleep, startPluginChannelGateways, stopPluginChannelGateways, toPluginConfigView, toPluginUiMetadata, uninstallPlugin, validateJsonSchemaValue };
|
package/dist/index.js
CHANGED
|
@@ -1204,10 +1204,10 @@ async function installPluginFromPath(params) {
|
|
|
1204
1204
|
}
|
|
1205
1205
|
|
|
1206
1206
|
// src/plugins/loader.ts
|
|
1207
|
-
import
|
|
1208
|
-
import
|
|
1209
|
-
import { createRequire as
|
|
1210
|
-
import { getWorkspacePathFromConfig } from "@nextclaw/core";
|
|
1207
|
+
import fs7 from "fs";
|
|
1208
|
+
import path8 from "path";
|
|
1209
|
+
import { createRequire as createRequire3 } from "module";
|
|
1210
|
+
import { getWorkspacePathFromConfig as getWorkspacePathFromConfig2 } from "@nextclaw/core";
|
|
1211
1211
|
|
|
1212
1212
|
// src/plugins/bundled-channel-plugin-packages.constants.ts
|
|
1213
1213
|
var BUNDLED_CHANNEL_PLUGIN_PACKAGES = [
|
|
@@ -1576,12 +1576,12 @@ function buildPluginLoaderAliases(pluginRoot) {
|
|
|
1576
1576
|
}
|
|
1577
1577
|
|
|
1578
1578
|
// src/plugins/bundled-plugin-loader.ts
|
|
1579
|
-
function resolveBundledPluginEntry(require2, packageName, diagnostics,
|
|
1579
|
+
function resolveBundledPluginEntry(require2, packageName, diagnostics, resolvePackageRootFromEntry4) {
|
|
1580
1580
|
try {
|
|
1581
1581
|
const entryFile = require2.resolve(packageName);
|
|
1582
1582
|
return {
|
|
1583
1583
|
entryFile,
|
|
1584
|
-
rootDir:
|
|
1584
|
+
rootDir: resolvePackageRootFromEntry4(entryFile)
|
|
1585
1585
|
};
|
|
1586
1586
|
} catch (err) {
|
|
1587
1587
|
diagnostics.push({
|
|
@@ -1611,9 +1611,9 @@ function formatAjvErrors(errors) {
|
|
|
1611
1611
|
return ["invalid config"];
|
|
1612
1612
|
}
|
|
1613
1613
|
return errors.map((error) => {
|
|
1614
|
-
const
|
|
1614
|
+
const path11 = error.instancePath?.replace(/^\//, "").replace(/\//g, ".") || "<root>";
|
|
1615
1615
|
const message = error.message ?? "invalid";
|
|
1616
|
-
return `${
|
|
1616
|
+
return `${path11}: ${message}`;
|
|
1617
1617
|
});
|
|
1618
1618
|
}
|
|
1619
1619
|
function validateJsonSchemaValue(params) {
|
|
@@ -1775,6 +1775,13 @@ function registerPluginNcpAgentRuntime(params) {
|
|
|
1775
1775
|
|
|
1776
1776
|
// src/plugins/runtime.ts
|
|
1777
1777
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1778
|
+
import {
|
|
1779
|
+
buildBootstrapAwareUserPrompt,
|
|
1780
|
+
getWorkspacePath,
|
|
1781
|
+
readRequestedSkillsFromMetadata,
|
|
1782
|
+
resolveProviderRuntime,
|
|
1783
|
+
SkillsLoader
|
|
1784
|
+
} from "@nextclaw/core";
|
|
1778
1785
|
import { getPackageVersion } from "@nextclaw/core";
|
|
1779
1786
|
import { MemoryGetTool, MemorySearchTool } from "@nextclaw/core";
|
|
1780
1787
|
|
|
@@ -2318,9 +2325,52 @@ async function dispatchReplyWithFallback(params) {
|
|
|
2318
2325
|
}
|
|
2319
2326
|
await bridge.dispatchReplyWithBufferedBlockDispatcher(params);
|
|
2320
2327
|
}
|
|
2328
|
+
function createRuntimeUserPromptBuilder(params) {
|
|
2329
|
+
const skillLoaders = /* @__PURE__ */ new Map();
|
|
2330
|
+
return ({ workspace, sessionKey, metadata, userMessage }) => {
|
|
2331
|
+
const resolvedWorkspace = getWorkspacePath(workspace ?? params.defaultWorkspace);
|
|
2332
|
+
let skills = skillLoaders.get(resolvedWorkspace);
|
|
2333
|
+
if (!skills) {
|
|
2334
|
+
skills = new SkillsLoader(resolvedWorkspace);
|
|
2335
|
+
skillLoaders.set(resolvedWorkspace, skills);
|
|
2336
|
+
}
|
|
2337
|
+
return buildBootstrapAwareUserPrompt({
|
|
2338
|
+
workspace: resolvedWorkspace,
|
|
2339
|
+
contextConfig: params.config?.agents?.context,
|
|
2340
|
+
sessionKey,
|
|
2341
|
+
skills,
|
|
2342
|
+
skillNames: readRequestedSkillsFromMetadata(metadata),
|
|
2343
|
+
userMessage
|
|
2344
|
+
});
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2321
2347
|
function createPluginRuntime(params) {
|
|
2348
|
+
const defaultModel = params.config?.agents?.defaults?.model ?? "";
|
|
2349
|
+
const maxToolIterations = Math.max(
|
|
2350
|
+
0,
|
|
2351
|
+
Math.trunc(params.config?.agents?.defaults?.maxToolIterations ?? 0)
|
|
2352
|
+
);
|
|
2353
|
+
const buildRuntimeUserPrompt = createRuntimeUserPromptBuilder({
|
|
2354
|
+
config: params.config,
|
|
2355
|
+
defaultWorkspace: params.workspace
|
|
2356
|
+
});
|
|
2322
2357
|
return {
|
|
2323
2358
|
version: getPackageVersion(),
|
|
2359
|
+
agent: {
|
|
2360
|
+
defaults: {
|
|
2361
|
+
model: defaultModel,
|
|
2362
|
+
workspace: params.workspace,
|
|
2363
|
+
maxToolIterations
|
|
2364
|
+
},
|
|
2365
|
+
resolveWorkspacePath: (workspace) => getWorkspacePath(workspace ?? params.workspace),
|
|
2366
|
+
resolveProviderRuntime: (model) => {
|
|
2367
|
+
if (!params.config) {
|
|
2368
|
+
throw new Error("plugin runtime agent.resolveProviderRuntime requires host config");
|
|
2369
|
+
}
|
|
2370
|
+
return resolveProviderRuntime(params.config, model);
|
|
2371
|
+
},
|
|
2372
|
+
buildRuntimeUserPrompt
|
|
2373
|
+
},
|
|
2324
2374
|
config: {
|
|
2325
2375
|
loadConfig: () => loadConfigWithFallback(params.config),
|
|
2326
2376
|
writeConfigFile: async (next) => writeConfigWithFallback(next)
|
|
@@ -2742,16 +2792,31 @@ function registerPluginWithApi(params) {
|
|
|
2742
2792
|
}
|
|
2743
2793
|
}
|
|
2744
2794
|
|
|
2745
|
-
// src/plugins/loader.ts
|
|
2795
|
+
// src/plugins/loader/progressive-bundled-plugin-loader.ts
|
|
2796
|
+
import { createRequire as createRequire2 } from "module";
|
|
2797
|
+
|
|
2798
|
+
// src/plugins/loader/progressive-plugin-loader-context.ts
|
|
2799
|
+
import fs6 from "fs";
|
|
2800
|
+
import path7 from "path";
|
|
2801
|
+
import { setImmediate as waitForNextTick } from "timers/promises";
|
|
2802
|
+
import { getWorkspacePathFromConfig } from "@nextclaw/core";
|
|
2746
2803
|
var defaultLogger2 = {
|
|
2747
2804
|
info: (message) => console.log(message),
|
|
2748
2805
|
warn: (message) => console.warn(message),
|
|
2749
2806
|
error: (message) => console.error(message),
|
|
2750
2807
|
debug: (message) => console.debug(message)
|
|
2751
2808
|
};
|
|
2809
|
+
var STARTUP_TRACE_ENABLED = process.env.NEXTCLAW_STARTUP_TRACE === "1";
|
|
2810
|
+
function logPluginStartupTrace(step, fields) {
|
|
2811
|
+
if (!STARTUP_TRACE_ENABLED) {
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
const suffix = Object.entries(fields ?? {}).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`).join(" ");
|
|
2815
|
+
console.log(`[startup-trace] ${step}${suffix ? ` ${suffix}` : ""}`);
|
|
2816
|
+
}
|
|
2752
2817
|
function resolvePackageRootFromEntry(entryFile) {
|
|
2753
2818
|
let cursor = path7.dirname(entryFile);
|
|
2754
|
-
for (let
|
|
2819
|
+
for (let index = 0; index < 8; index += 1) {
|
|
2755
2820
|
const candidate = path7.join(cursor, "package.json");
|
|
2756
2821
|
if (fs6.existsSync(candidate)) {
|
|
2757
2822
|
return cursor;
|
|
@@ -2780,14 +2845,552 @@ function resolvePluginModuleExport(moduleExport) {
|
|
|
2780
2845
|
}
|
|
2781
2846
|
return {};
|
|
2782
2847
|
}
|
|
2783
|
-
function
|
|
2848
|
+
function loadExternalPluginModule(candidateSource, pluginRoot) {
|
|
2849
|
+
const pluginJiti = createPluginJiti(buildPluginLoaderAliases(pluginRoot));
|
|
2850
|
+
return pluginJiti(candidateSource);
|
|
2851
|
+
}
|
|
2852
|
+
function createEmptyPluginRegistry() {
|
|
2853
|
+
return {
|
|
2854
|
+
plugins: [],
|
|
2855
|
+
tools: [],
|
|
2856
|
+
channels: [],
|
|
2857
|
+
providers: [],
|
|
2858
|
+
engines: [],
|
|
2859
|
+
ncpAgentRuntimes: [],
|
|
2860
|
+
diagnostics: [],
|
|
2861
|
+
resolvedTools: []
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
function createRegisterRuntimeFromOptions(options, registry, workspaceDir) {
|
|
2865
|
+
const logger = options.logger ?? defaultLogger2;
|
|
2866
|
+
return createPluginRegisterRuntime({
|
|
2867
|
+
config: options.config,
|
|
2868
|
+
workspaceDir,
|
|
2869
|
+
logger,
|
|
2870
|
+
registry,
|
|
2871
|
+
reservedToolNames: new Set(options.reservedToolNames ?? []),
|
|
2872
|
+
reservedChannelIds: new Set(options.reservedChannelIds ?? []),
|
|
2873
|
+
reservedProviderIds: new Set(options.reservedProviderIds ?? []),
|
|
2874
|
+
reservedEngineKinds: new Set((options.reservedEngineKinds ?? ["native"]).map((entry) => entry.toLowerCase())),
|
|
2875
|
+
reservedNcpAgentRuntimeKinds: new Set(
|
|
2876
|
+
(options.reservedNcpAgentRuntimeKinds ?? ["native"]).map((entry) => entry.toLowerCase())
|
|
2877
|
+
)
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
function createProgressivePluginLoadContext(options) {
|
|
2881
|
+
const workspaceDir = options.workspaceDir?.trim() || getWorkspacePathFromConfig(options.config);
|
|
2882
|
+
const normalizedConfig = normalizePluginsConfig(options.config.plugins);
|
|
2883
|
+
const registry = createEmptyPluginRegistry();
|
|
2884
|
+
return {
|
|
2885
|
+
options,
|
|
2886
|
+
workspaceDir,
|
|
2887
|
+
normalizedConfig,
|
|
2888
|
+
mode: options.mode ?? "full",
|
|
2889
|
+
registry,
|
|
2890
|
+
registerRuntime: createRegisterRuntimeFromOptions(options, registry, workspaceDir),
|
|
2891
|
+
tracker: {
|
|
2892
|
+
loadedPluginCount: 0,
|
|
2893
|
+
onPluginProcessed: options.onPluginProcessed,
|
|
2894
|
+
yieldToEventLoop: options.yieldToEventLoop ?? (() => waitForNextTick())
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
async function markPluginProcessed(tracker, pluginId) {
|
|
2899
|
+
tracker.loadedPluginCount += 1;
|
|
2900
|
+
tracker.onPluginProcessed?.({
|
|
2901
|
+
loadedPluginCount: tracker.loadedPluginCount,
|
|
2902
|
+
...pluginId ? { pluginId } : {}
|
|
2903
|
+
});
|
|
2904
|
+
await tracker.yieldToEventLoop();
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
// src/plugins/loader/progressive-bundled-plugin-loader.ts
|
|
2908
|
+
function pushBundledPluginLoadError(registry, entryFile, error) {
|
|
2909
|
+
registry.diagnostics.push({
|
|
2910
|
+
level: "error",
|
|
2911
|
+
source: entryFile,
|
|
2912
|
+
message: `failed to load bundled plugin: ${String(error)}`
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2915
|
+
function buildBundledPluginRecord(params) {
|
|
2916
|
+
return createPluginRecord({
|
|
2917
|
+
id: params.pluginId,
|
|
2918
|
+
name: params.definition?.name ?? params.pluginId,
|
|
2919
|
+
description: params.definition?.description,
|
|
2920
|
+
version: params.definition?.version,
|
|
2921
|
+
kind: params.definition?.kind,
|
|
2922
|
+
source: params.entryFile,
|
|
2923
|
+
origin: "bundled",
|
|
2924
|
+
workspaceDir: params.context.registerRuntime.workspaceDir,
|
|
2925
|
+
enabled: true,
|
|
2926
|
+
configSchema: Boolean(params.definition?.configSchema),
|
|
2927
|
+
configJsonSchema: params.definition?.configSchema
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
function disableBundledPluginRecord(record, reason) {
|
|
2931
|
+
record.status = "disabled";
|
|
2932
|
+
record.error = reason;
|
|
2933
|
+
return record;
|
|
2934
|
+
}
|
|
2935
|
+
function markBundledPluginError(params) {
|
|
2936
|
+
params.record.status = "error";
|
|
2937
|
+
params.record.error = params.message;
|
|
2938
|
+
params.registry.diagnostics.push({
|
|
2939
|
+
level: "error",
|
|
2940
|
+
pluginId: params.pluginId,
|
|
2941
|
+
source: params.entryFile,
|
|
2942
|
+
message: params.message
|
|
2943
|
+
});
|
|
2944
|
+
return params.record;
|
|
2945
|
+
}
|
|
2946
|
+
function finalizeBundledPluginRecord(params) {
|
|
2947
|
+
if (params.record) {
|
|
2948
|
+
params.context.registry.plugins.push(params.record);
|
|
2949
|
+
}
|
|
2950
|
+
logPluginStartupTrace("plugin.loader.bundled_plugin", {
|
|
2951
|
+
package: params.packageName,
|
|
2952
|
+
plugin_id: params.pluginId,
|
|
2953
|
+
duration_ms: Date.now() - params.packageStartedAt
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
function resolveBundledPluginRegistrationCandidate(params) {
|
|
2957
|
+
const resolvedEntry = resolveBundledPluginEntry(
|
|
2958
|
+
params.require,
|
|
2959
|
+
params.packageName,
|
|
2960
|
+
params.context.registry.diagnostics,
|
|
2961
|
+
resolvePackageRootFromEntry
|
|
2962
|
+
);
|
|
2963
|
+
if (!resolvedEntry) {
|
|
2964
|
+
return { done: true };
|
|
2965
|
+
}
|
|
2966
|
+
const { entryFile, rootDir } = resolvedEntry;
|
|
2967
|
+
let loadedModule;
|
|
2968
|
+
try {
|
|
2969
|
+
loadedModule = loadBundledPluginModule(entryFile, rootDir);
|
|
2970
|
+
} catch (error) {
|
|
2971
|
+
pushBundledPluginLoadError(params.context.registry, entryFile, error);
|
|
2972
|
+
return { done: true };
|
|
2973
|
+
}
|
|
2974
|
+
const resolved = resolvePluginModuleExport(loadedModule);
|
|
2975
|
+
const pluginId = typeof resolved.definition?.id === "string" ? resolved.definition.id.trim() : "";
|
|
2976
|
+
if (!pluginId) {
|
|
2977
|
+
params.context.registry.diagnostics.push({
|
|
2978
|
+
level: "error",
|
|
2979
|
+
source: entryFile,
|
|
2980
|
+
message: "bundled plugin definition missing id"
|
|
2981
|
+
});
|
|
2982
|
+
return { done: true };
|
|
2983
|
+
}
|
|
2984
|
+
const enableState = resolveEnableState(pluginId, params.context.normalizedConfig);
|
|
2985
|
+
const record = buildBundledPluginRecord({
|
|
2986
|
+
pluginId,
|
|
2987
|
+
definition: resolved.definition,
|
|
2988
|
+
entryFile,
|
|
2989
|
+
context: params.context
|
|
2990
|
+
});
|
|
2991
|
+
if (!enableState.enabled) {
|
|
2992
|
+
finalizeBundledPluginRecord({
|
|
2993
|
+
packageName: params.packageName,
|
|
2994
|
+
pluginId,
|
|
2995
|
+
record: disableBundledPluginRecord(record, enableState.reason ?? "disabled"),
|
|
2996
|
+
context: params.context,
|
|
2997
|
+
packageStartedAt: params.packageStartedAt
|
|
2998
|
+
});
|
|
2999
|
+
return { done: true, pluginId };
|
|
3000
|
+
}
|
|
3001
|
+
if (typeof resolved.register !== "function") {
|
|
3002
|
+
finalizeBundledPluginRecord({
|
|
3003
|
+
packageName: params.packageName,
|
|
3004
|
+
pluginId,
|
|
3005
|
+
record: markBundledPluginError({
|
|
3006
|
+
record,
|
|
3007
|
+
registry: params.context.registry,
|
|
3008
|
+
pluginId,
|
|
3009
|
+
entryFile,
|
|
3010
|
+
message: "plugin export missing register/activate"
|
|
3011
|
+
}),
|
|
3012
|
+
context: params.context,
|
|
3013
|
+
packageStartedAt: params.packageStartedAt
|
|
3014
|
+
});
|
|
3015
|
+
return { done: true, pluginId };
|
|
3016
|
+
}
|
|
3017
|
+
return {
|
|
3018
|
+
done: false,
|
|
3019
|
+
entryFile,
|
|
3020
|
+
rootDir,
|
|
3021
|
+
pluginId,
|
|
3022
|
+
record,
|
|
3023
|
+
register: resolved.register
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
async function processBundledPluginPackage(context, require2, packageName) {
|
|
3027
|
+
const packageStartedAt = Date.now();
|
|
3028
|
+
const candidate = resolveBundledPluginRegistrationCandidate({
|
|
3029
|
+
context,
|
|
3030
|
+
require: require2,
|
|
3031
|
+
packageName,
|
|
3032
|
+
packageStartedAt
|
|
3033
|
+
});
|
|
3034
|
+
if (candidate.done) {
|
|
3035
|
+
await markPluginProcessed(context.tracker, candidate.pluginId);
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
const result = registerPluginWithApi({
|
|
3039
|
+
runtime: context.registerRuntime,
|
|
3040
|
+
record: candidate.record,
|
|
3041
|
+
pluginId: candidate.pluginId,
|
|
3042
|
+
source: candidate.entryFile,
|
|
3043
|
+
rootDir: candidate.rootDir,
|
|
3044
|
+
register: candidate.register,
|
|
3045
|
+
pluginConfig: void 0
|
|
3046
|
+
});
|
|
3047
|
+
if (!result.ok) {
|
|
3048
|
+
markBundledPluginError({
|
|
3049
|
+
record: candidate.record,
|
|
3050
|
+
registry: context.registry,
|
|
3051
|
+
pluginId: candidate.pluginId,
|
|
3052
|
+
entryFile: candidate.entryFile,
|
|
3053
|
+
message: result.error
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
finalizeBundledPluginRecord({
|
|
3057
|
+
packageName,
|
|
3058
|
+
pluginId: candidate.pluginId,
|
|
3059
|
+
record: candidate.record,
|
|
3060
|
+
context,
|
|
3061
|
+
packageStartedAt
|
|
3062
|
+
});
|
|
3063
|
+
await markPluginProcessed(context.tracker, candidate.pluginId);
|
|
3064
|
+
}
|
|
3065
|
+
async function appendBundledChannelPluginsProgressively(context) {
|
|
2784
3066
|
const require2 = createRequire2(import.meta.url);
|
|
2785
3067
|
for (const packageName of BUNDLED_CHANNEL_PLUGIN_PACKAGES) {
|
|
3068
|
+
await processBundledPluginPackage(context, require2, packageName);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
// src/plugins/loader/progressive-external-plugin-loader.ts
|
|
3073
|
+
function createManifestPluginRecord(manifest, candidate, enabled) {
|
|
3074
|
+
return createPluginRecord({
|
|
3075
|
+
id: manifest.id,
|
|
3076
|
+
name: manifest.name ?? manifest.id,
|
|
3077
|
+
description: manifest.description,
|
|
3078
|
+
version: manifest.version,
|
|
3079
|
+
kind: manifest.kind,
|
|
3080
|
+
source: candidate.source,
|
|
3081
|
+
origin: candidate.origin,
|
|
3082
|
+
workspaceDir: candidate.workspaceDir,
|
|
3083
|
+
enabled,
|
|
3084
|
+
configSchema: Boolean(manifest.configSchema),
|
|
3085
|
+
configUiHints: manifest.configUiHints,
|
|
3086
|
+
configJsonSchema: manifest.configSchema
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
async function finalizeExternalPluginRecord(params) {
|
|
3090
|
+
params.context.registry.plugins.push(params.record);
|
|
3091
|
+
if (params.pluginId && params.seenIds && params.origin) {
|
|
3092
|
+
params.seenIds.set(params.pluginId, params.origin);
|
|
3093
|
+
}
|
|
3094
|
+
await markPluginProcessed(params.context.tracker, params.pluginId);
|
|
3095
|
+
}
|
|
3096
|
+
async function finalizeExternalPluginError(params) {
|
|
3097
|
+
params.record.status = "error";
|
|
3098
|
+
params.record.error = params.message;
|
|
3099
|
+
params.context.registry.diagnostics.push({
|
|
3100
|
+
level: "error",
|
|
3101
|
+
pluginId: params.pluginId,
|
|
3102
|
+
source: params.candidate.source,
|
|
3103
|
+
message: params.message
|
|
3104
|
+
});
|
|
3105
|
+
await finalizeExternalPluginRecord({
|
|
3106
|
+
context: params.context,
|
|
3107
|
+
record: params.record,
|
|
3108
|
+
pluginId: params.pluginId,
|
|
3109
|
+
seenIds: params.seenIds,
|
|
3110
|
+
origin: params.candidate.origin
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
async function finalizeDisabledExternalPlugin(params) {
|
|
3114
|
+
params.record.status = "disabled";
|
|
3115
|
+
params.record.error = params.reason;
|
|
3116
|
+
await finalizeExternalPluginRecord({
|
|
3117
|
+
context: params.context,
|
|
3118
|
+
record: params.record,
|
|
3119
|
+
pluginId: params.pluginId,
|
|
3120
|
+
seenIds: params.seenIds,
|
|
3121
|
+
origin: params.candidate.origin
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
function applyDefinitionMetadata(record, definition) {
|
|
3125
|
+
record.name = definition?.name ?? record.name;
|
|
3126
|
+
record.description = definition?.description ?? record.description;
|
|
3127
|
+
record.version = definition?.version ?? record.version;
|
|
3128
|
+
record.kind = definition?.kind ?? record.kind;
|
|
3129
|
+
}
|
|
3130
|
+
function loadExternalPluginDefinition(params) {
|
|
3131
|
+
const moduleLoadStartedAt = Date.now();
|
|
3132
|
+
const loadedModule = loadExternalPluginModule(params.candidate.source, params.candidate.rootDir);
|
|
3133
|
+
logPluginStartupTrace("plugin.loader.external_module_loaded", {
|
|
3134
|
+
plugin_id: params.pluginId,
|
|
3135
|
+
duration_ms: Date.now() - moduleLoadStartedAt,
|
|
3136
|
+
source: params.candidate.source
|
|
3137
|
+
});
|
|
3138
|
+
return resolvePluginModuleExport(loadedModule);
|
|
3139
|
+
}
|
|
3140
|
+
function pushDefinitionMismatchWarning(context, candidate, pluginId, definitionId) {
|
|
3141
|
+
if (!definitionId || definitionId === pluginId) {
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
context.registry.diagnostics.push({
|
|
3145
|
+
level: "warn",
|
|
3146
|
+
pluginId,
|
|
3147
|
+
source: candidate.source,
|
|
3148
|
+
message: `plugin id mismatch (manifest uses "${pluginId}", export uses "${definitionId}")`
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
async function loadAndRegisterExternalPlugin(params) {
|
|
3152
|
+
let resolved;
|
|
3153
|
+
try {
|
|
3154
|
+
resolved = loadExternalPluginDefinition({
|
|
3155
|
+
context: params.context,
|
|
3156
|
+
candidate: params.candidate,
|
|
3157
|
+
pluginId: params.pluginId
|
|
3158
|
+
});
|
|
3159
|
+
} catch (error) {
|
|
3160
|
+
await finalizeExternalPluginError({
|
|
3161
|
+
context: params.context,
|
|
3162
|
+
record: params.record,
|
|
3163
|
+
candidate: params.candidate,
|
|
3164
|
+
pluginId: params.pluginId,
|
|
3165
|
+
seenIds: params.seenIds,
|
|
3166
|
+
message: `failed to load plugin: ${String(error)}`
|
|
3167
|
+
});
|
|
3168
|
+
return;
|
|
3169
|
+
}
|
|
3170
|
+
pushDefinitionMismatchWarning(params.context, params.candidate, params.pluginId, resolved.definition?.id);
|
|
3171
|
+
applyDefinitionMetadata(params.record, resolved.definition);
|
|
3172
|
+
if (typeof resolved.register !== "function") {
|
|
3173
|
+
await finalizeExternalPluginError({
|
|
3174
|
+
context: params.context,
|
|
3175
|
+
record: params.record,
|
|
3176
|
+
candidate: params.candidate,
|
|
3177
|
+
pluginId: params.pluginId,
|
|
3178
|
+
seenIds: params.seenIds,
|
|
3179
|
+
message: "plugin export missing register/activate"
|
|
3180
|
+
});
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3183
|
+
const registerResult = registerPluginWithApi({
|
|
3184
|
+
runtime: params.context.registerRuntime,
|
|
3185
|
+
record: params.record,
|
|
3186
|
+
pluginId: params.pluginId,
|
|
3187
|
+
source: params.candidate.source,
|
|
3188
|
+
rootDir: params.candidate.rootDir,
|
|
3189
|
+
register: resolved.register,
|
|
3190
|
+
pluginConfig: params.validatedConfig.value
|
|
3191
|
+
});
|
|
3192
|
+
logPluginStartupTrace("plugin.loader.external_plugin_registered", {
|
|
3193
|
+
plugin_id: params.pluginId,
|
|
3194
|
+
duration_ms: Date.now() - params.candidateStartedAt,
|
|
3195
|
+
source: params.candidate.source
|
|
3196
|
+
});
|
|
3197
|
+
if (!registerResult.ok) {
|
|
3198
|
+
await finalizeExternalPluginError({
|
|
3199
|
+
context: params.context,
|
|
3200
|
+
record: params.record,
|
|
3201
|
+
candidate: params.candidate,
|
|
3202
|
+
pluginId: params.pluginId,
|
|
3203
|
+
seenIds: params.seenIds,
|
|
3204
|
+
message: registerResult.error
|
|
3205
|
+
});
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
await finalizeExternalPluginRecord({
|
|
3209
|
+
context: params.context,
|
|
3210
|
+
record: params.record,
|
|
3211
|
+
pluginId: params.pluginId,
|
|
3212
|
+
seenIds: params.seenIds,
|
|
3213
|
+
origin: params.candidate.origin
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
async function processExternalPluginCandidate(params) {
|
|
3217
|
+
const candidateStartedAt = Date.now();
|
|
3218
|
+
const pluginId = params.manifest.id;
|
|
3219
|
+
const existingOrigin = params.seenIds.get(pluginId);
|
|
3220
|
+
if (existingOrigin) {
|
|
3221
|
+
const overriddenRecord = createManifestPluginRecord(params.manifest, params.candidate, false);
|
|
3222
|
+
overriddenRecord.status = "disabled";
|
|
3223
|
+
overriddenRecord.error = `overridden by ${existingOrigin} plugin`;
|
|
3224
|
+
await finalizeExternalPluginRecord({
|
|
3225
|
+
context: params.context,
|
|
3226
|
+
record: overriddenRecord,
|
|
3227
|
+
pluginId
|
|
3228
|
+
});
|
|
3229
|
+
return;
|
|
3230
|
+
}
|
|
3231
|
+
const enableState = resolveEnableState(pluginId, params.context.normalizedConfig);
|
|
3232
|
+
const record = createManifestPluginRecord(params.manifest, params.candidate, enableState.enabled);
|
|
3233
|
+
if (!enableState.enabled) {
|
|
3234
|
+
await finalizeDisabledExternalPlugin({
|
|
3235
|
+
context: params.context,
|
|
3236
|
+
record,
|
|
3237
|
+
candidate: params.candidate,
|
|
3238
|
+
pluginId,
|
|
3239
|
+
seenIds: params.seenIds,
|
|
3240
|
+
reason: enableState.reason ?? "disabled"
|
|
3241
|
+
});
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
if (!params.manifest.configSchema) {
|
|
3245
|
+
await finalizeExternalPluginError({
|
|
3246
|
+
context: params.context,
|
|
3247
|
+
record,
|
|
3248
|
+
candidate: params.candidate,
|
|
3249
|
+
pluginId,
|
|
3250
|
+
seenIds: params.seenIds,
|
|
3251
|
+
message: "missing config schema"
|
|
3252
|
+
});
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
const validatedConfig = validatePluginConfig({
|
|
3256
|
+
schema: params.manifest.configSchema,
|
|
3257
|
+
cacheKey: params.manifest.schemaCacheKey,
|
|
3258
|
+
value: params.context.normalizedConfig.entries[pluginId]?.config
|
|
3259
|
+
});
|
|
3260
|
+
if (!validatedConfig.ok) {
|
|
3261
|
+
await finalizeExternalPluginError({
|
|
3262
|
+
context: params.context,
|
|
3263
|
+
record,
|
|
3264
|
+
candidate: params.candidate,
|
|
3265
|
+
pluginId,
|
|
3266
|
+
seenIds: params.seenIds,
|
|
3267
|
+
message: `invalid config: ${validatedConfig.errors.join(", ")}`
|
|
3268
|
+
});
|
|
3269
|
+
return;
|
|
3270
|
+
}
|
|
3271
|
+
if (params.context.mode === "validate") {
|
|
3272
|
+
await finalizeExternalPluginRecord({
|
|
3273
|
+
context: params.context,
|
|
3274
|
+
record,
|
|
3275
|
+
pluginId,
|
|
3276
|
+
seenIds: params.seenIds,
|
|
3277
|
+
origin: params.candidate.origin
|
|
3278
|
+
});
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
await loadAndRegisterExternalPlugin({
|
|
3282
|
+
context: params.context,
|
|
3283
|
+
candidate: params.candidate,
|
|
3284
|
+
record,
|
|
3285
|
+
pluginId,
|
|
3286
|
+
seenIds: params.seenIds,
|
|
3287
|
+
validatedConfig,
|
|
3288
|
+
candidateStartedAt
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
async function appendExternalPluginsProgressively(context) {
|
|
3292
|
+
const discovery = discoverOpenClawPlugins({
|
|
3293
|
+
config: context.options.config,
|
|
3294
|
+
workspaceDir: context.workspaceDir,
|
|
3295
|
+
extraPaths: context.normalizedConfig.loadPaths
|
|
3296
|
+
});
|
|
3297
|
+
const filteredCandidates = filterPluginCandidatesByExcludedRoots(discovery.candidates, context.options.excludeRoots ?? []);
|
|
3298
|
+
const manifestRegistry = loadPluginManifestRegistry({
|
|
3299
|
+
config: context.options.config,
|
|
3300
|
+
workspaceDir: context.workspaceDir,
|
|
3301
|
+
candidates: filteredCandidates,
|
|
3302
|
+
diagnostics: discovery.diagnostics
|
|
3303
|
+
});
|
|
3304
|
+
context.registry.diagnostics.push(...manifestRegistry.diagnostics);
|
|
3305
|
+
const manifestByRoot = new Map(manifestRegistry.plugins.map((entry) => [entry.rootDir, entry]));
|
|
3306
|
+
const seenIds = new Map(
|
|
3307
|
+
context.registry.plugins.map((entry) => [entry.id, entry.origin])
|
|
3308
|
+
);
|
|
3309
|
+
for (const candidate of filteredCandidates) {
|
|
3310
|
+
const manifest = manifestByRoot.get(candidate.rootDir);
|
|
3311
|
+
if (!manifest) {
|
|
3312
|
+
await markPluginProcessed(context.tracker);
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
await processExternalPluginCandidate({
|
|
3316
|
+
context,
|
|
3317
|
+
candidate,
|
|
3318
|
+
manifest,
|
|
3319
|
+
seenIds
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
// src/plugins/loader/progressive-plugin-loader.ts
|
|
3325
|
+
async function loadOpenClawPluginsProgressively(options) {
|
|
3326
|
+
const startedAt = Date.now();
|
|
3327
|
+
const context = createProgressivePluginLoadContext(options);
|
|
3328
|
+
await appendBundledChannelPluginsProgressively(context);
|
|
3329
|
+
if (process.env.NEXTCLAW_ENABLE_OPENCLAW_PLUGINS !== "0") {
|
|
3330
|
+
await appendExternalPluginsProgressively(context);
|
|
3331
|
+
}
|
|
3332
|
+
logPluginStartupTrace("plugin.loader.total", {
|
|
3333
|
+
duration_ms: Date.now() - startedAt,
|
|
3334
|
+
plugin_count: context.registry.plugins.length
|
|
3335
|
+
});
|
|
3336
|
+
return context.registry;
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
// src/plugins/loader.ts
|
|
3340
|
+
var defaultLogger3 = {
|
|
3341
|
+
info: (message) => console.log(message),
|
|
3342
|
+
warn: (message) => console.warn(message),
|
|
3343
|
+
error: (message) => console.error(message),
|
|
3344
|
+
debug: (message) => console.debug(message)
|
|
3345
|
+
};
|
|
3346
|
+
var STARTUP_TRACE_ENABLED2 = process.env.NEXTCLAW_STARTUP_TRACE === "1";
|
|
3347
|
+
function logPluginStartupTrace2(step, fields) {
|
|
3348
|
+
if (!STARTUP_TRACE_ENABLED2) {
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
const suffix = Object.entries(fields ?? {}).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`).join(" ");
|
|
3352
|
+
console.log(`[startup-trace] ${step}${suffix ? ` ${suffix}` : ""}`);
|
|
3353
|
+
}
|
|
3354
|
+
function resolvePackageRootFromEntry2(entryFile) {
|
|
3355
|
+
let cursor = path8.dirname(entryFile);
|
|
3356
|
+
for (let i = 0; i < 8; i += 1) {
|
|
3357
|
+
const candidate = path8.join(cursor, "package.json");
|
|
3358
|
+
if (fs7.existsSync(candidate)) {
|
|
3359
|
+
return cursor;
|
|
3360
|
+
}
|
|
3361
|
+
const parent = path8.dirname(cursor);
|
|
3362
|
+
if (parent === cursor) {
|
|
3363
|
+
break;
|
|
3364
|
+
}
|
|
3365
|
+
cursor = parent;
|
|
3366
|
+
}
|
|
3367
|
+
return path8.dirname(entryFile);
|
|
3368
|
+
}
|
|
3369
|
+
function resolvePluginModuleExport2(moduleExport) {
|
|
3370
|
+
const resolved = moduleExport && typeof moduleExport === "object" && "default" in moduleExport ? moduleExport.default : moduleExport;
|
|
3371
|
+
if (typeof resolved === "function") {
|
|
3372
|
+
return {
|
|
3373
|
+
register: resolved
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
if (resolved && typeof resolved === "object") {
|
|
3377
|
+
const definition = resolved;
|
|
3378
|
+
return {
|
|
3379
|
+
definition,
|
|
3380
|
+
register: definition.register ?? definition.activate
|
|
3381
|
+
};
|
|
3382
|
+
}
|
|
3383
|
+
return {};
|
|
3384
|
+
}
|
|
3385
|
+
function appendBundledChannelPlugins(params) {
|
|
3386
|
+
const require2 = createRequire3(import.meta.url);
|
|
3387
|
+
for (const packageName of BUNDLED_CHANNEL_PLUGIN_PACKAGES) {
|
|
3388
|
+
const packageStartedAt = Date.now();
|
|
2786
3389
|
const resolvedEntry = resolveBundledPluginEntry(
|
|
2787
3390
|
require2,
|
|
2788
3391
|
packageName,
|
|
2789
3392
|
params.registry.diagnostics,
|
|
2790
|
-
|
|
3393
|
+
resolvePackageRootFromEntry2
|
|
2791
3394
|
);
|
|
2792
3395
|
if (!resolvedEntry) {
|
|
2793
3396
|
continue;
|
|
@@ -2804,7 +3407,7 @@ function appendBundledChannelPlugins(params) {
|
|
|
2804
3407
|
});
|
|
2805
3408
|
continue;
|
|
2806
3409
|
}
|
|
2807
|
-
const resolved =
|
|
3410
|
+
const resolved = resolvePluginModuleExport2(moduleExport);
|
|
2808
3411
|
const definition = resolved.definition;
|
|
2809
3412
|
const register = resolved.register;
|
|
2810
3413
|
const pluginId = typeof definition?.id === "string" ? definition.id.trim() : "";
|
|
@@ -2869,16 +3472,22 @@ function appendBundledChannelPlugins(params) {
|
|
|
2869
3472
|
});
|
|
2870
3473
|
}
|
|
2871
3474
|
params.registry.plugins.push(record);
|
|
3475
|
+
logPluginStartupTrace2("plugin.loader.bundled_plugin", {
|
|
3476
|
+
package: packageName,
|
|
3477
|
+
plugin_id: pluginId,
|
|
3478
|
+
duration_ms: Date.now() - packageStartedAt
|
|
3479
|
+
});
|
|
2872
3480
|
}
|
|
2873
3481
|
}
|
|
2874
|
-
function
|
|
3482
|
+
function loadExternalPluginModule2(candidateSource, pluginRoot) {
|
|
2875
3483
|
const pluginJiti = createPluginJiti(buildPluginLoaderAliases(pluginRoot));
|
|
2876
3484
|
return pluginJiti(candidateSource);
|
|
2877
3485
|
}
|
|
2878
3486
|
function loadOpenClawPlugins(options) {
|
|
3487
|
+
const startedAt = Date.now();
|
|
2879
3488
|
const loadExternalPlugins = process.env.NEXTCLAW_ENABLE_OPENCLAW_PLUGINS !== "0";
|
|
2880
|
-
const logger = options.logger ??
|
|
2881
|
-
const workspaceDir = options.workspaceDir?.trim() ||
|
|
3489
|
+
const logger = options.logger ?? defaultLogger3;
|
|
3490
|
+
const workspaceDir = options.workspaceDir?.trim() || getWorkspacePathFromConfig2(options.config);
|
|
2882
3491
|
const normalized = normalizePluginsConfig(options.config.plugins);
|
|
2883
3492
|
const mode = options.mode ?? "full";
|
|
2884
3493
|
const registry = {
|
|
@@ -2935,6 +3544,7 @@ function loadOpenClawPlugins(options) {
|
|
|
2935
3544
|
registry.plugins.map((entry) => [entry.id, entry.origin])
|
|
2936
3545
|
);
|
|
2937
3546
|
for (const candidate of filteredCandidates) {
|
|
3547
|
+
const candidateStartedAt = Date.now();
|
|
2938
3548
|
const manifest = manifestByRoot.get(candidate.rootDir);
|
|
2939
3549
|
if (!manifest) {
|
|
2940
3550
|
continue;
|
|
@@ -3022,7 +3632,13 @@ function loadOpenClawPlugins(options) {
|
|
|
3022
3632
|
}
|
|
3023
3633
|
let moduleExport = null;
|
|
3024
3634
|
try {
|
|
3025
|
-
|
|
3635
|
+
const moduleLoadStartedAt = Date.now();
|
|
3636
|
+
moduleExport = loadExternalPluginModule2(candidate.source, candidate.rootDir);
|
|
3637
|
+
logPluginStartupTrace2("plugin.loader.external_module_loaded", {
|
|
3638
|
+
plugin_id: pluginId,
|
|
3639
|
+
duration_ms: Date.now() - moduleLoadStartedAt,
|
|
3640
|
+
source: candidate.source
|
|
3641
|
+
});
|
|
3026
3642
|
} catch (err) {
|
|
3027
3643
|
record.status = "error";
|
|
3028
3644
|
record.error = `failed to load plugin: ${String(err)}`;
|
|
@@ -3036,7 +3652,7 @@ function loadOpenClawPlugins(options) {
|
|
|
3036
3652
|
});
|
|
3037
3653
|
continue;
|
|
3038
3654
|
}
|
|
3039
|
-
const resolved =
|
|
3655
|
+
const resolved = resolvePluginModuleExport2(moduleExport);
|
|
3040
3656
|
const definition = resolved.definition;
|
|
3041
3657
|
const register = resolved.register;
|
|
3042
3658
|
if (definition?.id && definition.id !== pluginId) {
|
|
@@ -3073,6 +3689,11 @@ function loadOpenClawPlugins(options) {
|
|
|
3073
3689
|
register,
|
|
3074
3690
|
pluginConfig: validatedConfig.value
|
|
3075
3691
|
});
|
|
3692
|
+
logPluginStartupTrace2("plugin.loader.external_plugin_registered", {
|
|
3693
|
+
plugin_id: pluginId,
|
|
3694
|
+
duration_ms: Date.now() - candidateStartedAt,
|
|
3695
|
+
source: candidate.source
|
|
3696
|
+
});
|
|
3076
3697
|
if (!registerResult.ok) {
|
|
3077
3698
|
record.status = "error";
|
|
3078
3699
|
record.error = registerResult.error;
|
|
@@ -3089,15 +3710,19 @@ function loadOpenClawPlugins(options) {
|
|
|
3089
3710
|
registry.plugins.push(record);
|
|
3090
3711
|
seenIds.set(pluginId, candidate.origin);
|
|
3091
3712
|
}
|
|
3713
|
+
logPluginStartupTrace2("plugin.loader.total", {
|
|
3714
|
+
duration_ms: Date.now() - startedAt,
|
|
3715
|
+
plugin_count: registry.plugins.length
|
|
3716
|
+
});
|
|
3092
3717
|
return registry;
|
|
3093
3718
|
}
|
|
3094
3719
|
|
|
3095
3720
|
// src/plugins/status.ts
|
|
3096
|
-
import
|
|
3097
|
-
import { createRequire as
|
|
3098
|
-
import
|
|
3099
|
-
import { getWorkspacePathFromConfig as
|
|
3100
|
-
function
|
|
3721
|
+
import fs8 from "fs";
|
|
3722
|
+
import { createRequire as createRequire4 } from "module";
|
|
3723
|
+
import path9 from "path";
|
|
3724
|
+
import { getWorkspacePathFromConfig as getWorkspacePathFromConfig3 } from "@nextclaw/core";
|
|
3725
|
+
function createEmptyPluginRegistry2() {
|
|
3101
3726
|
return {
|
|
3102
3727
|
plugins: [],
|
|
3103
3728
|
tools: [],
|
|
@@ -3109,23 +3734,23 @@ function createEmptyPluginRegistry() {
|
|
|
3109
3734
|
resolvedTools: []
|
|
3110
3735
|
};
|
|
3111
3736
|
}
|
|
3112
|
-
function
|
|
3113
|
-
let cursor =
|
|
3737
|
+
function resolvePackageRootFromEntry3(entryFile) {
|
|
3738
|
+
let cursor = path9.dirname(entryFile);
|
|
3114
3739
|
for (let index = 0; index < 8; index += 1) {
|
|
3115
|
-
const candidate =
|
|
3116
|
-
if (
|
|
3740
|
+
const candidate = path9.join(cursor, "package.json");
|
|
3741
|
+
if (fs8.existsSync(candidate)) {
|
|
3117
3742
|
return cursor;
|
|
3118
3743
|
}
|
|
3119
|
-
const parent =
|
|
3744
|
+
const parent = path9.dirname(cursor);
|
|
3120
3745
|
if (parent === cursor) {
|
|
3121
3746
|
break;
|
|
3122
3747
|
}
|
|
3123
3748
|
cursor = parent;
|
|
3124
3749
|
}
|
|
3125
|
-
return
|
|
3750
|
+
return path9.dirname(entryFile);
|
|
3126
3751
|
}
|
|
3127
3752
|
function discoverBundledPluginCandidates(workspaceDir, diagnostics) {
|
|
3128
|
-
const require2 =
|
|
3753
|
+
const require2 = createRequire4(import.meta.url);
|
|
3129
3754
|
const candidates = [];
|
|
3130
3755
|
for (const packageName of BUNDLED_CHANNEL_PLUGIN_PACKAGES) {
|
|
3131
3756
|
try {
|
|
@@ -3133,7 +3758,7 @@ function discoverBundledPluginCandidates(workspaceDir, diagnostics) {
|
|
|
3133
3758
|
candidates.push({
|
|
3134
3759
|
idHint: packageName.split("/").pop() ?? packageName,
|
|
3135
3760
|
source: entryFile,
|
|
3136
|
-
rootDir:
|
|
3761
|
+
rootDir: resolvePackageRootFromEntry3(entryFile),
|
|
3137
3762
|
origin: "bundled",
|
|
3138
3763
|
workspaceDir,
|
|
3139
3764
|
packageName
|
|
@@ -3215,7 +3840,7 @@ function finalizeDiscoveredManifestRecord(params) {
|
|
|
3215
3840
|
};
|
|
3216
3841
|
}
|
|
3217
3842
|
function discoverPluginStatusReport(params) {
|
|
3218
|
-
const workspaceDir = params.workspaceDir?.trim() ||
|
|
3843
|
+
const workspaceDir = params.workspaceDir?.trim() || getWorkspacePathFromConfig3(params.config);
|
|
3219
3844
|
const normalized = normalizePluginsConfig(params.config.plugins);
|
|
3220
3845
|
const discovery = discoverOpenClawPlugins({
|
|
3221
3846
|
config: params.config,
|
|
@@ -3230,7 +3855,7 @@ function discoverPluginStatusReport(params) {
|
|
|
3230
3855
|
candidates: [...bundledCandidates, ...discovery.candidates],
|
|
3231
3856
|
diagnostics: [...bundledDiagnostics, ...discovery.diagnostics]
|
|
3232
3857
|
});
|
|
3233
|
-
const registry =
|
|
3858
|
+
const registry = createEmptyPluginRegistry2();
|
|
3234
3859
|
const seenIds = /* @__PURE__ */ new Map();
|
|
3235
3860
|
registry.diagnostics.push(...manifestRegistry.diagnostics);
|
|
3236
3861
|
for (const manifest of manifestRegistry.plugins) {
|
|
@@ -3256,7 +3881,7 @@ function discoverPluginStatusReport(params) {
|
|
|
3256
3881
|
};
|
|
3257
3882
|
}
|
|
3258
3883
|
function buildPluginStatusReport(params) {
|
|
3259
|
-
const workspaceDir = params.workspaceDir?.trim() ||
|
|
3884
|
+
const workspaceDir = params.workspaceDir?.trim() || getWorkspacePathFromConfig3(params.config);
|
|
3260
3885
|
const registry = loadOpenClawPlugins({
|
|
3261
3886
|
config: params.config,
|
|
3262
3887
|
workspaceDir,
|
|
@@ -3274,10 +3899,10 @@ function buildPluginStatusReport(params) {
|
|
|
3274
3899
|
}
|
|
3275
3900
|
|
|
3276
3901
|
// src/plugins/uninstall.ts
|
|
3277
|
-
import
|
|
3902
|
+
import fs9 from "fs/promises";
|
|
3278
3903
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
3279
|
-
import
|
|
3280
|
-
import { getWorkspacePathFromConfig as
|
|
3904
|
+
import path10 from "path";
|
|
3905
|
+
import { getWorkspacePathFromConfig as getWorkspacePathFromConfig4 } from "@nextclaw/core";
|
|
3281
3906
|
function isLinkedPathInstall(record) {
|
|
3282
3907
|
if (!record || record.source !== "path") {
|
|
3283
3908
|
return false;
|
|
@@ -3285,13 +3910,13 @@ function isLinkedPathInstall(record) {
|
|
|
3285
3910
|
if (!record.sourcePath || !record.installPath) {
|
|
3286
3911
|
return true;
|
|
3287
3912
|
}
|
|
3288
|
-
return
|
|
3913
|
+
return path10.resolve(record.sourcePath) === path10.resolve(record.installPath);
|
|
3289
3914
|
}
|
|
3290
3915
|
function pushUniquePath(targets, candidate) {
|
|
3291
3916
|
if (!candidate) {
|
|
3292
3917
|
return;
|
|
3293
3918
|
}
|
|
3294
|
-
const resolved =
|
|
3919
|
+
const resolved = path10.resolve(candidate);
|
|
3295
3920
|
if (!targets.includes(resolved)) {
|
|
3296
3921
|
targets.push(resolved);
|
|
3297
3922
|
}
|
|
@@ -3313,7 +3938,7 @@ function resolveUninstallDirectoryTarget(params) {
|
|
|
3313
3938
|
if (!configuredPath) {
|
|
3314
3939
|
return defaultPath;
|
|
3315
3940
|
}
|
|
3316
|
-
if (
|
|
3941
|
+
if (path10.resolve(configuredPath) === path10.resolve(defaultPath)) {
|
|
3317
3942
|
return configuredPath;
|
|
3318
3943
|
}
|
|
3319
3944
|
return defaultPath;
|
|
@@ -3333,8 +3958,8 @@ function resolveUninstallDirectoryTargets(params) {
|
|
|
3333
3958
|
})
|
|
3334
3959
|
);
|
|
3335
3960
|
pushUniquePath(targets, params.installRecord?.installPath);
|
|
3336
|
-
const workspaceDir =
|
|
3337
|
-
pushUniquePath(targets,
|
|
3961
|
+
const workspaceDir = getWorkspacePathFromConfig4(params.config);
|
|
3962
|
+
pushUniquePath(targets, path10.join(workspaceDir, ".nextclaw", "extensions", params.pluginId));
|
|
3338
3963
|
return targets;
|
|
3339
3964
|
}
|
|
3340
3965
|
function removePluginFromConfig(config, pluginId) {
|
|
@@ -3415,13 +4040,13 @@ function matchesPluginLoadPath(rawPath, pluginId) {
|
|
|
3415
4040
|
if (!normalizedPath) {
|
|
3416
4041
|
return false;
|
|
3417
4042
|
}
|
|
3418
|
-
const resolvedPath =
|
|
4043
|
+
const resolvedPath = path10.resolve(normalizedPath);
|
|
3419
4044
|
if (!existsSync2(resolvedPath)) {
|
|
3420
4045
|
return false;
|
|
3421
4046
|
}
|
|
3422
4047
|
const candidateRoot = (() => {
|
|
3423
4048
|
try {
|
|
3424
|
-
return statSync(resolvedPath).isDirectory() ? resolvedPath :
|
|
4049
|
+
return statSync(resolvedPath).isDirectory() ? resolvedPath : path10.dirname(resolvedPath);
|
|
3425
4050
|
} catch {
|
|
3426
4051
|
return null;
|
|
3427
4052
|
}
|
|
@@ -3455,9 +4080,9 @@ async function uninstallPlugin(params) {
|
|
|
3455
4080
|
extensionsDir
|
|
3456
4081
|
}) : [];
|
|
3457
4082
|
for (const deleteTarget of deleteTargets) {
|
|
3458
|
-
const existed = await
|
|
4083
|
+
const existed = await fs9.access(deleteTarget).then(() => true).catch(() => false);
|
|
3459
4084
|
try {
|
|
3460
|
-
await
|
|
4085
|
+
await fs9.rm(deleteTarget, { recursive: true, force: true });
|
|
3461
4086
|
actions.directory = actions.directory || existed;
|
|
3462
4087
|
} catch (error) {
|
|
3463
4088
|
warnings.push(
|
|
@@ -3500,6 +4125,7 @@ export {
|
|
|
3500
4125
|
installPluginFromNpmSpec,
|
|
3501
4126
|
installPluginFromPath,
|
|
3502
4127
|
loadOpenClawPlugins,
|
|
4128
|
+
loadOpenClawPluginsProgressively,
|
|
3503
4129
|
loadPluginManifest,
|
|
3504
4130
|
loadPluginManifestRegistry,
|
|
3505
4131
|
loadPluginUiMetadata,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/openclaw-compat",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.36",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenClaw plugin compatibility layer for NextClaw.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,21 +19,21 @@
|
|
|
19
19
|
"jiti": "^1.21.7",
|
|
20
20
|
"jszip": "^3.10.1",
|
|
21
21
|
"tar": "^7.4.3",
|
|
22
|
-
"@nextclaw/channel-plugin-dingtalk": "0.2.
|
|
23
|
-
"@nextclaw/channel-plugin-discord": "0.2.
|
|
24
|
-
"@nextclaw/channel-plugin-email": "0.2.
|
|
25
|
-
"@nextclaw/channel-plugin-
|
|
26
|
-
"@nextclaw/channel-plugin-
|
|
27
|
-
"@nextclaw/channel-plugin-
|
|
28
|
-
"@nextclaw/channel-plugin-
|
|
29
|
-
"@nextclaw/channel-plugin-
|
|
30
|
-
"@nextclaw/channel-
|
|
31
|
-
"@nextclaw/
|
|
32
|
-
"@nextclaw/
|
|
33
|
-
"@nextclaw/channel-runtime": "0.4.3",
|
|
34
|
-
"@nextclaw/core": "0.11.2",
|
|
22
|
+
"@nextclaw/channel-plugin-dingtalk": "0.2.18",
|
|
23
|
+
"@nextclaw/channel-plugin-discord": "0.2.18",
|
|
24
|
+
"@nextclaw/channel-plugin-email": "0.2.18",
|
|
25
|
+
"@nextclaw/channel-plugin-mochat": "0.2.18",
|
|
26
|
+
"@nextclaw/channel-plugin-qq": "0.2.18",
|
|
27
|
+
"@nextclaw/channel-plugin-slack": "0.2.18",
|
|
28
|
+
"@nextclaw/channel-plugin-telegram": "0.2.18",
|
|
29
|
+
"@nextclaw/channel-plugin-weixin": "0.1.11",
|
|
30
|
+
"@nextclaw/channel-runtime": "0.4.4",
|
|
31
|
+
"@nextclaw/core": "0.11.3",
|
|
32
|
+
"@nextclaw/ncp-toolkit": "0.4.4",
|
|
35
33
|
"@nextclaw/ncp": "0.4.0",
|
|
36
|
-
"@nextclaw/
|
|
34
|
+
"@nextclaw/channel-plugin-feishu": "0.2.21",
|
|
35
|
+
"@nextclaw/channel-plugin-whatsapp": "0.2.18",
|
|
36
|
+
"@nextclaw/channel-plugin-wecom": "0.2.18"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^20.17.6",
|