mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 03:03:12 +00:00
refactor(plugin-runtime): remove plugin-specific core seams
This commit is contained in:
parent
4846ebce12
commit
f59d0eac68
79 changed files with 1062 additions and 2628 deletions
|
|
@ -127,7 +127,7 @@
|
|||
"exportName": "ChannelConfiguredBindingConversationRef",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 659,
|
||||
"line": 675,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
"exportName": "ChannelConfiguredBindingMatch",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 664,
|
||||
"line": 680,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -145,7 +145,7 @@
|
|||
"exportName": "ChannelConfiguredBindingProvider",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 680,
|
||||
"line": 696,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 271,
|
||||
"line": 275,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -172,7 +172,7 @@
|
|||
"exportName": "ChannelMessageActionAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 556,
|
||||
"line": 560,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
"exportName": "ChannelMessageActionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 520,
|
||||
"line": 524,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1240,7 +1240,7 @@
|
|||
"exportName": "BlockStreamingCoalesceSchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 341,
|
||||
"line": 414,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1249,7 +1249,7 @@
|
|||
"exportName": "ContextVisibilityModeSchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 339,
|
||||
"line": 412,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1267,7 +1267,7 @@
|
|||
"exportName": "DmConfigSchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 294,
|
||||
"line": 367,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1276,7 +1276,7 @@
|
|||
"exportName": "DmPolicySchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 338,
|
||||
"line": 411,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1294,7 +1294,7 @@
|
|||
"exportName": "GroupPolicySchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 336,
|
||||
"line": 409,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1312,7 +1312,7 @@
|
|||
"exportName": "MarkdownConfigSchema",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 374,
|
||||
"line": 447,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1330,7 +1330,7 @@
|
|||
"exportName": "ReplyRuntimeConfigSchemaShape",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 349,
|
||||
"line": 422,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1339,7 +1339,7 @@
|
|||
"exportName": "requireOpenAllowFrom",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 489,
|
||||
"line": 562,
|
||||
"path": "src/config/zod-schema.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1404,7 +1404,7 @@
|
|||
"exportName": "BaseProbeResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 599,
|
||||
"line": 603,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1413,7 +1413,7 @@
|
|||
"exportName": "BaseTokenResolution",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 605,
|
||||
"line": 609,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1440,7 +1440,7 @@
|
|||
"exportName": "ChannelApprovalAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 597,
|
||||
"line": 613,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1449,7 +1449,7 @@
|
|||
"exportName": "ChannelApprovalCapability",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 592,
|
||||
"line": 602,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1458,7 +1458,7 @@
|
|||
"exportName": "ChannelCommandConversationContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 668,
|
||||
"line": 684,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1467,7 +1467,7 @@
|
|||
"exportName": "ChannelDirectoryAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 454,
|
||||
"line": 459,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1485,7 +1485,7 @@
|
|||
"exportName": "ChannelMessageActionAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 556,
|
||||
"line": 560,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1494,7 +1494,7 @@
|
|||
"exportName": "ChannelMessageActionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 520,
|
||||
"line": 524,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1941,7 +1941,7 @@
|
|||
"exportName": "BaseProbeResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 599,
|
||||
"line": 603,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1950,7 +1950,7 @@
|
|||
"exportName": "BaseTokenResolution",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 605,
|
||||
"line": 609,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -1986,7 +1986,7 @@
|
|||
"exportName": "ChannelAgentPromptAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 493,
|
||||
"line": 497,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2013,7 +2013,7 @@
|
|||
"exportName": "ChannelAllowlistAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 603,
|
||||
"line": 619,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2022,7 +2022,7 @@
|
|||
"exportName": "ChannelApprovalAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 597,
|
||||
"line": 613,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2031,7 +2031,7 @@
|
|||
"exportName": "ChannelApprovalCapability",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 592,
|
||||
"line": 602,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2058,7 +2058,7 @@
|
|||
"exportName": "ChannelAuthAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 395,
|
||||
"line": 399,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2103,7 +2103,7 @@
|
|||
"exportName": "ChannelCommandAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 492,
|
||||
"line": 497,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2112,7 +2112,7 @@
|
|||
"exportName": "ChannelCommandConversationContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 668,
|
||||
"line": 684,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2130,7 +2130,7 @@
|
|||
"exportName": "ChannelConfiguredBindingConversationRef",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 659,
|
||||
"line": 675,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2139,7 +2139,7 @@
|
|||
"exportName": "ChannelConfiguredBindingMatch",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 664,
|
||||
"line": 680,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2148,7 +2148,7 @@
|
|||
"exportName": "ChannelConfiguredBindingProvider",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 680,
|
||||
"line": 696,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2157,7 +2157,7 @@
|
|||
"exportName": "ChannelConversationBindingSupport",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 696,
|
||||
"line": 712,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2166,7 +2166,7 @@
|
|||
"exportName": "ChannelDirectoryAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 454,
|
||||
"line": 459,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2175,7 +2175,7 @@
|
|||
"exportName": "ChannelDirectoryEntry",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 507,
|
||||
"line": 511,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2184,7 +2184,7 @@
|
|||
"exportName": "ChannelDirectoryEntryKind",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 505,
|
||||
"line": 509,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2193,7 +2193,7 @@
|
|||
"exportName": "ChannelElevatedAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 485,
|
||||
"line": 490,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2202,7 +2202,7 @@
|
|||
"exportName": "ChannelGatewayAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 379,
|
||||
"line": 383,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2211,7 +2211,7 @@
|
|||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 271,
|
||||
"line": 275,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2220,7 +2220,7 @@
|
|||
"exportName": "ChannelGroupAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 129,
|
||||
"line": 133,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2238,7 +2238,7 @@
|
|||
"exportName": "ChannelHeartbeatAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 420,
|
||||
"line": 425,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2265,7 +2265,7 @@
|
|||
"exportName": "ChannelLifecycleAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 497,
|
||||
"line": 502,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2274,7 +2274,7 @@
|
|||
"exportName": "ChannelLoginWithQrStartResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 350,
|
||||
"line": 354,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2283,7 +2283,7 @@
|
|||
"exportName": "ChannelLoginWithQrWaitResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 355,
|
||||
"line": 359,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2292,7 +2292,7 @@
|
|||
"exportName": "ChannelLogoutContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 360,
|
||||
"line": 364,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2301,7 +2301,7 @@
|
|||
"exportName": "ChannelLogoutResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 344,
|
||||
"line": 348,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2328,7 +2328,7 @@
|
|||
"exportName": "ChannelMessageActionAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 556,
|
||||
"line": 560,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2337,7 +2337,7 @@
|
|||
"exportName": "ChannelMessageActionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 520,
|
||||
"line": 524,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2409,7 +2409,7 @@
|
|||
"exportName": "ChannelOutboundAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 175,
|
||||
"line": 179,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2418,7 +2418,7 @@
|
|||
"exportName": "ChannelOutboundContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 135,
|
||||
"line": 139,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2427,7 +2427,7 @@
|
|||
"exportName": "ChannelOutboundPayloadHint",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 160,
|
||||
"line": 164,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2445,7 +2445,7 @@
|
|||
"exportName": "ChannelOutboundTargetRef",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 164,
|
||||
"line": 168,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2454,7 +2454,7 @@
|
|||
"exportName": "ChannelPairingAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 368,
|
||||
"line": 372,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2472,7 +2472,7 @@
|
|||
"exportName": "ChannelPollContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 587,
|
||||
"line": 591,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2481,7 +2481,7 @@
|
|||
"exportName": "ChannelPollResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 578,
|
||||
"line": 582,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2490,7 +2490,7 @@
|
|||
"exportName": "ChannelResolveKind",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 465,
|
||||
"line": 470,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2499,7 +2499,7 @@
|
|||
"exportName": "ChannelResolverAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 475,
|
||||
"line": 480,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2508,7 +2508,7 @@
|
|||
"exportName": "ChannelResolveResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 467,
|
||||
"line": 472,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2517,7 +2517,7 @@
|
|||
"exportName": "ChannelSecurityAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 727,
|
||||
"line": 743,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2562,7 +2562,7 @@
|
|||
"exportName": "ChannelStatusAdapter",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 217,
|
||||
"line": 221,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -2625,7 +2625,7 @@
|
|||
"exportName": "ChannelToolSend",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 549,
|
||||
"line": 553,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -3237,11 +3237,11 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function resolveStoredModelOverride(params: { sessionEntry?: SessionEntry | undefined; sessionStore?: Record<string, SessionEntry> | undefined; sessionKey?: string | undefined; parentSessionKey?: string | undefined; }): StoredModelOverride | null;",
|
||||
"declaration": "export function resolveStoredModelOverride(params: { sessionEntry?: SessionEntry | undefined; sessionStore?: Record<string, SessionEntry> | undefined; sessionKey?: string | undefined; parentSessionKey?: string | undefined; defaultProvider: string; }): StoredModelOverride | null;",
|
||||
"exportName": "resolveStoredModelOverride",
|
||||
"kind": "function",
|
||||
"source": {
|
||||
"line": 156,
|
||||
"line": 145,
|
||||
"path": "src/auto-reply/reply/model-selection.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -3502,7 +3502,7 @@
|
|||
"exportName": "StoredModelOverride",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 123,
|
||||
"line": 124,
|
||||
"path": "src/auto-reply/reply/model-selection.ts"
|
||||
}
|
||||
}
|
||||
|
|
@ -3954,7 +3954,7 @@
|
|||
"exportName": "ChannelMessageActionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 520,
|
||||
"line": 524,
|
||||
"path": "src/channels/plugins/types.core.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -5308,7 +5308,7 @@
|
|||
"exportName": "ModelApi",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 16,
|
||||
"line": 17,
|
||||
"path": "src/config/types.models.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -5317,7 +5317,7 @@
|
|||
"exportName": "ModelDefinitionConfig",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 49,
|
||||
"line": 50,
|
||||
"path": "src/config/types.models.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -5326,7 +5326,7 @@
|
|||
"exportName": "ModelProviderConfig",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 67,
|
||||
"line": 68,
|
||||
"path": "src/config/types.models.ts"
|
||||
}
|
||||
},
|
||||
|
|
@ -6099,7 +6099,7 @@
|
|||
"exportName": "ChannelGatewayContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 271,
|
||||
"line": 275,
|
||||
"path": "src/channels/plugins/types.adapters.ts"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -30,10 +30,12 @@ import {
|
|||
} from "./accounts.js";
|
||||
import { getDiscordApprovalCapability } from "./approval-native.js";
|
||||
import { auditDiscordChannelPermissions, collectDiscordAuditChannelIds } from "./audit.js";
|
||||
import { discordMessageActions } from "./channel-actions.js";
|
||||
import {
|
||||
listDiscordDirectoryGroupsFromConfig,
|
||||
listDiscordDirectoryPeersFromConfig,
|
||||
} from "./directory-config.js";
|
||||
import { listDiscordDirectoryGroupsLive, listDiscordDirectoryPeersLive } from "./directory-live.js";
|
||||
import { shouldSuppressLocalDiscordExecApprovalPrompt } from "./exec-approvals.js";
|
||||
import {
|
||||
resolveDiscordGroupRequireMention,
|
||||
|
|
@ -46,10 +48,10 @@ import {
|
|||
} from "./normalize.js";
|
||||
import { resolveDiscordOutboundSessionRoute } from "./outbound-session-route.js";
|
||||
import type { DiscordProbe } from "./probe.js";
|
||||
import { resolveDiscordChannelAllowlist } from "./resolve-channels.js";
|
||||
import { resolveDiscordUserAllowlist } from "./resolve-users.js";
|
||||
import {
|
||||
buildTokenChannelStatusSummary,
|
||||
type ChannelMessageActionAdapter,
|
||||
type ChannelPlugin,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
getChatChannelMeta,
|
||||
|
|
@ -59,16 +61,15 @@ import {
|
|||
type OpenClawConfig,
|
||||
} from "./runtime-api.js";
|
||||
import { getDiscordRuntime } from "./runtime.js";
|
||||
import { fetchChannelPermissionsDiscord } from "./send.js";
|
||||
import { fetchChannelPermissionsDiscord, sendMessageDiscord, sendPollDiscord } from "./send.js";
|
||||
import { normalizeExplicitDiscordSessionKey } from "./session-key-normalization.js";
|
||||
import { discordSetupAdapter } from "./setup-core.js";
|
||||
import { createDiscordPluginBase, discordConfigAdapter } from "./shared.js";
|
||||
import { collectDiscordStatusIssues } from "./status-issues.js";
|
||||
import { parseDiscordTarget } from "./targets.js";
|
||||
import { DiscordUiContainer } from "./ui.js";
|
||||
|
||||
type DiscordSendFn = ReturnType<
|
||||
typeof getDiscordRuntime
|
||||
>["channel"]["discord"]["sendMessageDiscord"];
|
||||
type DiscordSendFn = typeof sendMessageDiscord;
|
||||
|
||||
let discordProviderRuntimePromise:
|
||||
| Promise<typeof import("./monitor/provider.runtime.js")>
|
||||
|
|
@ -128,22 +129,6 @@ function formatDiscordIntents(intents?: {
|
|||
].join(" ");
|
||||
}
|
||||
|
||||
const discordMessageActions: ChannelMessageActionAdapter = {
|
||||
describeMessageTool: (ctx) =>
|
||||
getDiscordRuntime().channel.discord.messageActions?.describeMessageTool?.(ctx) ?? null,
|
||||
extractToolSend: (ctx) =>
|
||||
getDiscordRuntime().channel.discord.messageActions?.extractToolSend?.(ctx) ?? null,
|
||||
handleAction: async (ctx) => {
|
||||
const ma = getDiscordRuntime().channel.discord.messageActions;
|
||||
if (!ma?.handleAction) {
|
||||
throw new Error("Discord message actions not available");
|
||||
}
|
||||
return ma.handleAction(ctx);
|
||||
},
|
||||
requiresTrustedRequesterSender: ({ action, toolContext }) =>
|
||||
Boolean(toolContext && (action === "timeout" || action === "kick" || action === "ban")),
|
||||
};
|
||||
|
||||
function buildDiscordCrossContextComponents(params: {
|
||||
originLabel: string;
|
||||
message: string;
|
||||
|
|
@ -329,6 +314,8 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
},
|
||||
messaging: {
|
||||
normalizeTarget: normalizeDiscordMessagingTarget,
|
||||
normalizeExplicitSessionKey: ({ sessionKey, ctx }) =>
|
||||
normalizeExplicitDiscordSessionKey(sessionKey, ctx),
|
||||
resolveSessionTarget: ({ id }) => normalizeDiscordMessagingTarget(`channel:${id}`),
|
||||
parseExplicitTarget: ({ raw }) => parseDiscordExplicitTarget(raw),
|
||||
inferTargetChatType: ({ to }) => parseDiscordExplicitTarget(to)?.chatType,
|
||||
|
|
@ -344,7 +331,10 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
listPeers: async (params) => listDiscordDirectoryPeersFromConfig(params),
|
||||
listGroups: async (params) => listDiscordDirectoryGroupsFromConfig(params),
|
||||
...createRuntimeDirectoryLiveAdapter({
|
||||
getRuntime: () => getDiscordRuntime().channel.discord,
|
||||
getRuntime: () => ({
|
||||
listDirectoryGroupsLive: listDiscordDirectoryGroupsLive,
|
||||
listDirectoryPeersLive: listDiscordDirectoryPeersLive,
|
||||
}),
|
||||
listPeersLive: (runtime) => runtime.listDirectoryPeersLive,
|
||||
listGroupsLive: (runtime) => runtime.listDirectoryGroupsLive,
|
||||
}),
|
||||
|
|
@ -358,10 +348,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
inputs,
|
||||
missingTokenNote: "missing Discord token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getDiscordRuntime().channel.discord.resolveChannelAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
resolveDiscordChannelAllowlist({ token, entries: inputs }),
|
||||
mapResolved: (entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
|
|
@ -379,10 +366,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
inputs,
|
||||
missingTokenNote: "missing Discord token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getDiscordRuntime().channel.discord.resolveUserAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
resolveDiscordUserAllowlist({ token, entries: inputs }),
|
||||
mapResolved: (entry) => ({
|
||||
input: entry.input,
|
||||
resolved: entry.resolved,
|
||||
|
|
@ -638,7 +622,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
message: PAIRING_APPROVED_MESSAGE,
|
||||
normalizeAllowEntry: createPairingPrefixStripper(/^(discord|user):/i),
|
||||
notify: async ({ id, message }) => {
|
||||
await getDiscordRuntime().channel.discord.sendMessageDiscord(`user:${id}`, message);
|
||||
await sendMessageDiscord(`user:${id}`, message);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -666,9 +650,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
attachedResults: {
|
||||
channel: "discord",
|
||||
sendText: async ({ cfg, to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
const send = resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ?? sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
|
|
@ -688,9 +670,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
replyToId,
|
||||
silent,
|
||||
}) => {
|
||||
const send =
|
||||
resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ??
|
||||
getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
const send = resolveOutboundSendDep<DiscordSendFn>(deps, "discord") ?? sendMessageDiscord;
|
||||
return await send(to, text, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
|
|
@ -702,7 +682,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
|||
});
|
||||
},
|
||||
sendPoll: async ({ cfg, to, poll, accountId, silent }) =>
|
||||
await getDiscordRuntime().channel.discord.sendPollDiscord(to, poll, {
|
||||
await sendPollDiscord(to, poll, {
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
silent: silent ?? undefined,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } {
|
|||
|
||||
const runtime = {
|
||||
config: { writeConfigFile },
|
||||
channel: { line: { resolveLineAccount } },
|
||||
} as unknown as PluginRuntime;
|
||||
|
||||
return { runtime, mocks: { writeConfigFile, resolveLineAccount } };
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ import { createRestrictSendersChannelSecurity } from "openclaw/plugin-sdk/channe
|
|||
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
||||
import { type ChannelPlugin, type ResolvedLineAccount } from "../api.js";
|
||||
import { resolveLineAccount } from "./accounts.js";
|
||||
import { lineChannelPluginCommon } from "./channel-shared.js";
|
||||
import { lineGatewayAdapter } from "./gateway.js";
|
||||
import { resolveLineGroupRequireMention } from "./group-policy.js";
|
||||
import { lineOutboundAdapter } from "./outbound.js";
|
||||
import { getLineRuntime } from "./runtime.js";
|
||||
import { pushMessageLine } from "./send.js";
|
||||
import { lineSetupAdapter } from "./setup-core.js";
|
||||
import { lineSetupWizard } from "./setup-surface.js";
|
||||
import { lineStatusAdapter } from "./status.js";
|
||||
|
|
@ -157,12 +158,11 @@ export const linePlugin: ChannelPlugin<ResolvedLineAccount> = createChatChannelP
|
|||
message: "OpenClaw: your access has been approved.",
|
||||
normalizeAllowEntry: createPairingPrefixStripper(/^line:(?:user:)?/i),
|
||||
notify: async ({ cfg, id, message }) => {
|
||||
const line = getLineRuntime().channel.line;
|
||||
const account = line.resolveLineAccount({ cfg });
|
||||
const account = resolveLineAccount({ cfg });
|
||||
if (!account.channelAccessToken) {
|
||||
throw new Error("LINE channel access token not configured");
|
||||
}
|
||||
await line.pushMessageLine(id, message, {
|
||||
await pushMessageLine(id, message, {
|
||||
accountId: account.accountId,
|
||||
channelAccessToken: account.channelAccessToken,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import {
|
|||
type OpenClawConfig,
|
||||
type ResolvedLineAccount,
|
||||
} from "../api.js";
|
||||
import { resolveLineAccount } from "./accounts.js";
|
||||
import { monitorLineProvider } from "./monitor.js";
|
||||
import { probeLineBot } from "./probe.js";
|
||||
import { getLineRuntime } from "./runtime.js";
|
||||
|
||||
export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>["gateway"]> = {
|
||||
|
|
@ -26,7 +29,7 @@ export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>[
|
|||
|
||||
let lineBotLabel = "";
|
||||
try {
|
||||
const probe = await getLineRuntime().channel.line.probeLineBot(token, 2500);
|
||||
const probe = await probeLineBot(token, 2500);
|
||||
const displayName = probe.ok ? probe.bot?.displayName?.trim() : null;
|
||||
if (displayName) {
|
||||
lineBotLabel = ` (${displayName})`;
|
||||
|
|
@ -39,7 +42,7 @@ export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>[
|
|||
|
||||
ctx.log?.info(`[${account.accountId}] starting LINE provider${lineBotLabel}`);
|
||||
|
||||
return await getLineRuntime().channel.line.monitorLineProvider({
|
||||
return await monitorLineProvider({
|
||||
channelAccessToken: token,
|
||||
channelSecret: secret,
|
||||
accountId: account.accountId,
|
||||
|
|
@ -106,7 +109,7 @@ export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>[
|
|||
await getLineRuntime().config.writeConfigFile(nextCfg);
|
||||
}
|
||||
|
||||
const resolved = getLineRuntime().channel.line.resolveLineAccount({
|
||||
const resolved = resolveLineAccount({
|
||||
cfg: changed ? nextCfg : cfg,
|
||||
accountId,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,17 @@ import {
|
|||
} from "../api.js";
|
||||
import { resolveLineOutboundMedia, type LineOutboundMediaResolved } from "./outbound-media.js";
|
||||
import { getLineRuntime } from "./runtime.js";
|
||||
import {
|
||||
createQuickReplyItems,
|
||||
pushFlexMessage,
|
||||
pushLocationMessage,
|
||||
pushMessageLine,
|
||||
pushMessagesLine,
|
||||
pushTemplateMessage,
|
||||
pushTextMessageWithQuickReplies,
|
||||
sendMessageLine,
|
||||
} from "./send.js";
|
||||
import { buildTemplateMessageFromPayload } from "./template-messages.js";
|
||||
|
||||
type LineChannelDataWithMedia = LineChannelData & {
|
||||
mediaKind?: "image" | "video" | "audio";
|
||||
|
|
@ -77,14 +88,13 @@ export const lineOutboundAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>
|
|||
sendPayload: async ({ to, payload, accountId, cfg }) => {
|
||||
const runtime = getLineRuntime();
|
||||
const lineData = (payload.channelData?.line as LineChannelDataWithMedia | undefined) ?? {};
|
||||
const sendText = runtime.channel.line.pushMessageLine;
|
||||
const sendBatch = runtime.channel.line.pushMessagesLine;
|
||||
const sendFlex = runtime.channel.line.pushFlexMessage;
|
||||
const sendTemplate = runtime.channel.line.pushTemplateMessage;
|
||||
const sendLocation = runtime.channel.line.pushLocationMessage;
|
||||
const sendQuickReplies = runtime.channel.line.pushTextMessageWithQuickReplies;
|
||||
const buildTemplate = runtime.channel.line.buildTemplateMessageFromPayload;
|
||||
const createQuickReplyItems = runtime.channel.line.createQuickReplyItems;
|
||||
const sendText = pushMessageLine;
|
||||
const sendBatch = pushMessagesLine;
|
||||
const sendFlex = pushFlexMessage;
|
||||
const sendTemplate = pushTemplateMessage;
|
||||
const sendLocation = pushLocationMessage;
|
||||
const sendQuickReplies = pushTextMessageWithQuickReplies;
|
||||
const buildTemplate = buildTemplateMessageFromPayload;
|
||||
|
||||
let lastResult: { messageId: string; chatId: string } | null = null;
|
||||
const quickReplies = lineData.quickReplies ?? [];
|
||||
|
|
@ -129,7 +139,7 @@ export const lineOutboundAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>
|
|||
continue;
|
||||
}
|
||||
if (!useLineSpecificMedia) {
|
||||
lastResult = await runtime.channel.line.sendMessageLine(to, "", {
|
||||
lastResult = await sendMessageLine(to, "", {
|
||||
verbose: false,
|
||||
mediaUrl: trimmed,
|
||||
cfg,
|
||||
|
|
@ -143,7 +153,7 @@ export const lineOutboundAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>
|
|||
durationMs: lineData.durationMs,
|
||||
trackingId: lineData.trackingId,
|
||||
});
|
||||
lastResult = await runtime.channel.line.sendMessageLine(to, "", {
|
||||
lastResult = await sendMessageLine(to, "", {
|
||||
verbose: false,
|
||||
mediaUrl: resolved.mediaUrl,
|
||||
mediaKind: resolved.mediaKind,
|
||||
|
|
@ -294,8 +304,8 @@ export const lineOutboundAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>
|
|||
channel: "line",
|
||||
sendText: async ({ cfg, to, text, accountId }) => {
|
||||
const runtime = getLineRuntime();
|
||||
const sendText = runtime.channel.line.pushMessageLine;
|
||||
const sendFlex = runtime.channel.line.pushFlexMessage;
|
||||
const sendText = pushMessageLine;
|
||||
const sendFlex = pushFlexMessage;
|
||||
const processed = processLineMessage(text);
|
||||
let result: { messageId: string; chatId: string };
|
||||
if (processed.text.trim()) {
|
||||
|
|
@ -318,7 +328,7 @@ export const lineOutboundAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>
|
|||
return result;
|
||||
},
|
||||
sendMedia: async ({ cfg, to, text, mediaUrl, accountId }) =>
|
||||
await getLineRuntime().channel.line.sendMessageLine(to, text, {
|
||||
await sendMessageLine(to, text, {
|
||||
verbose: false,
|
||||
mediaUrl,
|
||||
cfg,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { resolveSignalAccount, type ResolvedSignalAccount } from "./accounts.js"
|
|||
import { signalApprovalAuth } from "./approval-auth.js";
|
||||
import { markdownToSignalTextChunks } from "./format.js";
|
||||
import { signalMessageActions } from "./message-actions.js";
|
||||
import { monitorSignalProvider } from "./monitor.js";
|
||||
import { looksLikeSignalTargetId, normalizeSignalMessagingTarget } from "./normalize.js";
|
||||
import { resolveSignalOutboundTarget } from "./outbound-session.js";
|
||||
import { probeSignal, type SignalProbe } from "./probe.js";
|
||||
|
|
@ -29,7 +30,7 @@ import {
|
|||
resolveChannelMediaMaxBytes,
|
||||
type ChannelPlugin,
|
||||
} from "./runtime-api.js";
|
||||
import { getSignalRuntime } from "./runtime.js";
|
||||
import { sendMessageSignal } from "./send.js";
|
||||
import { signalSetupAdapter } from "./setup-core.js";
|
||||
import {
|
||||
signalConfigAdapter,
|
||||
|
|
@ -37,16 +38,14 @@ import {
|
|||
signalSecurityAdapter,
|
||||
signalSetupWizard,
|
||||
} from "./shared.js";
|
||||
type SignalSendFn = ReturnType<typeof getSignalRuntime>["channel"]["signal"]["sendMessageSignal"];
|
||||
type SignalSendFn = typeof sendMessageSignal;
|
||||
|
||||
function resolveSignalSendContext(params: {
|
||||
cfg: Parameters<typeof resolveSignalAccount>[0]["cfg"];
|
||||
accountId?: string;
|
||||
deps?: { [channelId: string]: unknown };
|
||||
}) {
|
||||
const send =
|
||||
resolveOutboundSendDep<SignalSendFn>(params.deps, "signal") ??
|
||||
getSignalRuntime().channel.signal.sendMessageSignal;
|
||||
const send = resolveOutboundSendDep<SignalSendFn>(params.deps, "signal") ?? sendMessageSignal;
|
||||
const maxBytes = resolveChannelMediaMaxBytes({
|
||||
cfg: params.cfg,
|
||||
resolveChannelLimitMb: ({ cfg, accountId }) =>
|
||||
|
|
@ -299,7 +298,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount, SignalProbe> =
|
|||
});
|
||||
ctx.log?.info(`[${account.accountId}] starting provider (${account.baseUrl})`);
|
||||
// Lazy import: the monitor pulls the reply pipeline; avoid ESM init cycles.
|
||||
return getSignalRuntime().channel.signal.monitorSignalProvider({
|
||||
return monitorSignalProvider({
|
||||
accountId: account.accountId,
|
||||
config: ctx.cfg,
|
||||
runtime: ctx.runtime,
|
||||
|
|
@ -315,7 +314,7 @@ export const signalPlugin: ChannelPlugin<ResolvedSignalAccount, SignalProbe> =
|
|||
message: PAIRING_APPROVED_MESSAGE,
|
||||
normalizeAllowEntry: createPairingPrefixStripper(/^signal:/i),
|
||||
notify: async ({ id, message }) => {
|
||||
await getSignalRuntime().channel.signal.sendMessageSignal(id, message);
|
||||
await sendMessageSignal(id, message);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { slackPlugin } from "./src/channel.js";
|
||||
import { registerSlackPluginHttpRoutes } from "./src/http/plugin-routes.js";
|
||||
import { setSlackRuntime } from "./src/runtime.js";
|
||||
|
||||
export { slackPlugin } from "./src/channel.js";
|
||||
|
|
@ -11,4 +12,5 @@ export default defineChannelPluginEntry({
|
|||
description: "Slack channel plugin",
|
||||
plugin: slackPlugin,
|
||||
setRuntime: setSlackRuntime,
|
||||
registerFull: registerSlackPluginHttpRoutes,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {
|
|||
resolveSlackReplyToMode,
|
||||
type ResolvedSlackAccount,
|
||||
} from "./accounts.js";
|
||||
import type { SlackActionContext } from "./action-runtime.js";
|
||||
import { handleSlackAction, type SlackActionContext } from "./action-runtime.js";
|
||||
import { resolveSlackAutoThreadId } from "./action-threading.js";
|
||||
import { slackApprovalCapability } from "./approval-native.js";
|
||||
import { createSlackActions } from "./channel-actions.js";
|
||||
|
|
@ -45,13 +45,16 @@ import {
|
|||
listSlackDirectoryGroupsFromConfig,
|
||||
listSlackDirectoryPeersFromConfig,
|
||||
} from "./directory-config.js";
|
||||
import { listSlackDirectoryGroupsLive, listSlackDirectoryPeersLive } from "./directory-live.js";
|
||||
import { shouldSuppressLocalSlackExecApprovalPrompt } from "./exec-approvals.js";
|
||||
import { resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy } from "./group-policy.js";
|
||||
import { isSlackInteractiveRepliesEnabled } from "./interactive-replies.js";
|
||||
import { SLACK_TEXT_LIMIT } from "./limits.js";
|
||||
import { monitorSlackProvider } from "./monitor.js";
|
||||
import { slackOutbound } from "./outbound-adapter.js";
|
||||
import { probeSlack, type SlackProbe } from "./probe.js";
|
||||
import { resolveSlackReplyBlocks } from "./reply-blocks.js";
|
||||
import { resolveSlackChannelAllowlist } from "./resolve-channels.js";
|
||||
import { resolveSlackUserAllowlist } from "./resolve-users.js";
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
|
|
@ -65,6 +68,7 @@ import {
|
|||
} from "./runtime-api.js";
|
||||
import { getSlackRuntime } from "./runtime.js";
|
||||
import { fetchSlackScopes } from "./scopes.js";
|
||||
import { sendMessageSlack } from "./send.js";
|
||||
import { slackSetupAdapter } from "./setup-core.js";
|
||||
import { slackSetupWizard } from "./setup-surface.js";
|
||||
import {
|
||||
|
|
@ -89,14 +93,7 @@ const resolveSlackDmPolicy = createScopedDmSecurityResolver<ResolvedSlackAccount
|
|||
});
|
||||
|
||||
function resolveSlackProbe() {
|
||||
try {
|
||||
return getSlackRuntime().channel.slack.probeSlack;
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === "Slack runtime not initialized") {
|
||||
return probeSlack;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return probeSlack;
|
||||
}
|
||||
|
||||
// Select the appropriate Slack token for read/write operations.
|
||||
|
|
@ -116,7 +113,7 @@ function getTokenForOperation(
|
|||
return botToken ?? userToken;
|
||||
}
|
||||
|
||||
type SlackSendFn = ReturnType<typeof getSlackRuntime>["channel"]["slack"]["sendMessageSlack"];
|
||||
type SlackSendFn = typeof sendMessageSlack;
|
||||
|
||||
function resolveSlackSendContext(params: {
|
||||
cfg: Parameters<typeof resolveSlackAccount>[0]["cfg"];
|
||||
|
|
@ -125,9 +122,7 @@ function resolveSlackSendContext(params: {
|
|||
replyToId?: string | number | null;
|
||||
threadId?: string | number | null;
|
||||
}) {
|
||||
const send =
|
||||
resolveOutboundSendDep<SlackSendFn>(params.deps, "slack") ??
|
||||
getSlackRuntime().channel.slack.sendMessageSlack;
|
||||
const send = resolveOutboundSendDep<SlackSendFn>(params.deps, "slack") ?? sendMessageSlack;
|
||||
const account = resolveSlackAccount({ cfg: params.cfg, accountId: params.accountId });
|
||||
const token = getTokenForOperation(account, "write");
|
||||
const botToken = account.botToken?.trim();
|
||||
|
|
@ -323,7 +318,10 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
|||
listPeers: async (params) => listSlackDirectoryPeersFromConfig(params),
|
||||
listGroups: async (params) => listSlackDirectoryGroupsFromConfig(params),
|
||||
...createRuntimeDirectoryLiveAdapter({
|
||||
getRuntime: () => getSlackRuntime().channel.slack,
|
||||
getRuntime: () => ({
|
||||
listDirectoryGroupsLive: listSlackDirectoryGroupsLive,
|
||||
listDirectoryPeersLive: listSlackDirectoryPeersLive,
|
||||
}),
|
||||
listPeersLive: (runtime) => runtime.listDirectoryPeersLive,
|
||||
listGroupsLive: (runtime) => runtime.listDirectoryGroupsLive,
|
||||
}),
|
||||
|
|
@ -349,10 +347,7 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
|||
inputs,
|
||||
missingTokenNote: "missing Slack token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getSlackRuntime().channel.slack.resolveChannelAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
resolveSlackChannelAllowlist({ token, entries: inputs }),
|
||||
mapResolved: (entry) =>
|
||||
toResolvedTarget(entry, entry.archived ? "archived" : undefined),
|
||||
});
|
||||
|
|
@ -362,17 +357,14 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
|||
inputs,
|
||||
missingTokenNote: "missing Slack token",
|
||||
resolveWithToken: ({ token, inputs }) =>
|
||||
getSlackRuntime().channel.slack.resolveUserAllowlist({
|
||||
token,
|
||||
entries: inputs,
|
||||
}),
|
||||
resolveSlackUserAllowlist({ token, entries: inputs }),
|
||||
mapResolved: (entry) => toResolvedTarget(entry, entry.note),
|
||||
});
|
||||
},
|
||||
},
|
||||
actions: createSlackActions(SLACK_CHANNEL, {
|
||||
invoke: async (action, cfg, toolContext) =>
|
||||
await getSlackRuntime().channel.slack.handleSlackAction(
|
||||
await handleSlackAction(
|
||||
action,
|
||||
cfg as OpenClawConfig,
|
||||
toolContext as SlackActionContext | undefined,
|
||||
|
|
@ -450,7 +442,7 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
|||
const botToken = account.botToken?.trim();
|
||||
const appToken = account.appToken?.trim();
|
||||
ctx.log?.info(`[${account.accountId}] starting provider`);
|
||||
return getSlackRuntime().channel.slack.monitorSlackProvider({
|
||||
return monitorSlackProvider({
|
||||
botToken: botToken ?? "",
|
||||
appToken: appToken ?? "",
|
||||
accountId: account.accountId,
|
||||
|
|
@ -480,11 +472,11 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
|
|||
const botToken = account.botToken?.trim();
|
||||
const tokenOverride = token && token !== botToken ? token : undefined;
|
||||
if (tokenOverride) {
|
||||
await getSlackRuntime().channel.slack.sendMessageSlack(`user:${id}`, message, {
|
||||
await sendMessageSlack(`user:${id}`, message, {
|
||||
token: tokenOverride,
|
||||
});
|
||||
} else {
|
||||
await getSlackRuntime().channel.slack.sendMessageSlack(`user:${id}`, message);
|
||||
await sendMessageSlack(`user:${id}`, message);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
23
extensions/slack/src/http/plugin-routes.ts
Normal file
23
extensions/slack/src/http/plugin-routes.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { listSlackAccountIds, resolveSlackAccount } from "../accounts.js";
|
||||
import { handleSlackHttpRequest, normalizeSlackWebhookPath } from "./registry.js";
|
||||
|
||||
export function registerSlackPluginHttpRoutes(api: OpenClawPluginApi): void {
|
||||
const accountIds = new Set<string>([DEFAULT_ACCOUNT_ID, ...listSlackAccountIds(api.config)]);
|
||||
const registeredPaths = new Set<string>();
|
||||
for (const accountId of accountIds) {
|
||||
const account = resolveSlackAccount({ cfg: api.config, accountId });
|
||||
registeredPaths.add(normalizeSlackWebhookPath(account.config.webhookPath));
|
||||
}
|
||||
if (registeredPaths.size === 0) {
|
||||
registeredPaths.add(normalizeSlackWebhookPath());
|
||||
}
|
||||
for (const path of registeredPaths) {
|
||||
api.registerHttpRoute({
|
||||
path,
|
||||
auth: "plugin",
|
||||
handler: async (req, res) => await handleSlackHttpRequest(req, res),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import {
|
|||
createApproverRestrictedNativeApprovalCapability,
|
||||
splitChannelApprovalCapability,
|
||||
} from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type { ChannelApprovalCapability } from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import { listTelegramAccountIds } from "./accounts.js";
|
||||
|
|
@ -84,7 +85,7 @@ const resolveTelegramApproverDmTargets = createChannelApproverDmTargetResolver({
|
|||
mapApprover: (approver) => ({ to: approver }),
|
||||
});
|
||||
|
||||
export const telegramApprovalCapability = createApproverRestrictedNativeApprovalCapability({
|
||||
const telegramNativeApprovalCapability = createApproverRestrictedNativeApprovalCapability({
|
||||
channel: "telegram",
|
||||
channelLabel: "Telegram",
|
||||
listAccountIds: listTelegramAccountIds,
|
||||
|
|
@ -105,6 +106,32 @@ export const telegramApprovalCapability = createApproverRestrictedNativeApproval
|
|||
resolveApproverDmTargets: resolveTelegramApproverDmTargets,
|
||||
});
|
||||
|
||||
const resolveTelegramApproveCommandBehavior: NonNullable<
|
||||
ChannelApprovalCapability["resolveApproveCommandBehavior"]
|
||||
> = ({ cfg, accountId, senderId, approvalKind }) => {
|
||||
if (approvalKind !== "exec") {
|
||||
return undefined;
|
||||
}
|
||||
if (isTelegramExecApprovalClientEnabled({ cfg, accountId })) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
isTelegramExecApprovalAuthorizedSender({ cfg, accountId, senderId }) &&
|
||||
!isTelegramExecApprovalApprover({ cfg, accountId, senderId })
|
||||
) {
|
||||
return { kind: "ignore" };
|
||||
}
|
||||
return {
|
||||
kind: "reply",
|
||||
text: "❌ Telegram exec approvals are not enabled for this bot account.",
|
||||
};
|
||||
};
|
||||
|
||||
export const telegramApprovalCapability: ChannelApprovalCapability = {
|
||||
...telegramNativeApprovalCapability,
|
||||
resolveApproveCommandBehavior: resolveTelegramApproveCommandBehavior,
|
||||
};
|
||||
|
||||
export const telegramNativeApprovalAdapter = splitChannelApprovalCapability(
|
||||
telegramApprovalCapability,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
listWhatsAppAccountIds,
|
||||
resolveDefaultWhatsAppAccountId,
|
||||
resolveWhatsAppAccount,
|
||||
hasAnyWhatsAppAuth,
|
||||
type ResolvedWhatsAppAccount,
|
||||
} from "./accounts.js";
|
||||
import { WhatsAppChannelConfigSchema } from "./config-schema.js";
|
||||
|
|
@ -141,6 +142,7 @@ export function createWhatsAppPluginBase(params: {
|
|||
isEnabled: (account, cfg) => account.enabled && cfg.web?.enabled !== false,
|
||||
disabledReason: () => "disabled",
|
||||
isConfigured: params.isConfigured,
|
||||
hasPersistedAuthState: ({ cfg }) => hasAnyWhatsAppAuth(cfg),
|
||||
unconfiguredReason: () => "not linked",
|
||||
describeAccount: (account) =>
|
||||
describeAccountSnapshot({
|
||||
|
|
|
|||
|
|
@ -40,154 +40,6 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
|
|||
"resolveAnthropicVertexRegionFromBaseUrl",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "discord-account",
|
||||
source: pluginSource("discord", "api.js"),
|
||||
exports: ["resolveDiscordAccount", "ResolvedDiscordAccount"],
|
||||
typeExports: ["ResolvedDiscordAccount"],
|
||||
},
|
||||
{
|
||||
subpath: "discord-runtime-surface",
|
||||
source: pluginSource("discord", "runtime-api.js"),
|
||||
// Runtime entrypoints should be blocked until the owning plugin is active.
|
||||
loadPolicy: "activated",
|
||||
exports: [
|
||||
"addRoleDiscord",
|
||||
"auditDiscordChannelPermissions",
|
||||
"banMemberDiscord",
|
||||
"collectDiscordAuditChannelIds",
|
||||
"createChannelDiscord",
|
||||
"createScheduledEventDiscord",
|
||||
"createThreadDiscord",
|
||||
"deleteChannelDiscord",
|
||||
"deleteMessageDiscord",
|
||||
"discordMessageActions",
|
||||
"editChannelDiscord",
|
||||
"editDiscordComponentMessage",
|
||||
"editMessageDiscord",
|
||||
"fetchChannelInfoDiscord",
|
||||
"fetchChannelPermissionsDiscord",
|
||||
"fetchMemberInfoDiscord",
|
||||
"fetchMessageDiscord",
|
||||
"fetchReactionsDiscord",
|
||||
"fetchRoleInfoDiscord",
|
||||
"fetchVoiceStatusDiscord",
|
||||
"getGateway",
|
||||
"getPresence",
|
||||
"hasAnyGuildPermissionDiscord",
|
||||
"kickMemberDiscord",
|
||||
"listDiscordDirectoryGroupsLive",
|
||||
"listDiscordDirectoryPeersLive",
|
||||
"listGuildChannelsDiscord",
|
||||
"listGuildEmojisDiscord",
|
||||
"listPinsDiscord",
|
||||
"listScheduledEventsDiscord",
|
||||
"listThreadsDiscord",
|
||||
"monitorDiscordProvider",
|
||||
"moveChannelDiscord",
|
||||
"pinMessageDiscord",
|
||||
"probeDiscord",
|
||||
"reactMessageDiscord",
|
||||
"readMessagesDiscord",
|
||||
"registerBuiltDiscordComponentMessage",
|
||||
"removeChannelPermissionDiscord",
|
||||
"removeOwnReactionsDiscord",
|
||||
"removeReactionDiscord",
|
||||
"removeRoleDiscord",
|
||||
"resolveDiscordChannelAllowlist",
|
||||
"resolveDiscordOutboundSessionRoute",
|
||||
"resolveDiscordUserAllowlist",
|
||||
"searchMessagesDiscord",
|
||||
"sendDiscordComponentMessage",
|
||||
"sendMessageDiscord",
|
||||
"sendPollDiscord",
|
||||
"sendStickerDiscord",
|
||||
"sendTypingDiscord",
|
||||
"sendVoiceMessageDiscord",
|
||||
"setChannelPermissionDiscord",
|
||||
"timeoutMemberDiscord",
|
||||
"unpinMessageDiscord",
|
||||
"uploadEmojiDiscord",
|
||||
"uploadStickerDiscord",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "discord-session-key",
|
||||
source: pluginSource("discord", "session-key-api.js"),
|
||||
exports: ["normalizeExplicitDiscordSessionKey"],
|
||||
},
|
||||
{
|
||||
subpath: "discord-surface",
|
||||
source: pluginSource("discord", "api.js"),
|
||||
exports: [
|
||||
"buildDiscordComponentMessage",
|
||||
"collectDiscordStatusIssues",
|
||||
"createDiscordActionGate",
|
||||
"DiscordComponentMessageSpec",
|
||||
"DiscordSendComponents",
|
||||
"DiscordSendEmbeds",
|
||||
"DiscordSendResult",
|
||||
"handleDiscordMessageAction",
|
||||
"inspectDiscordAccount",
|
||||
"isDiscordExecApprovalApprover",
|
||||
"isDiscordExecApprovalClientEnabled",
|
||||
"InspectedDiscordAccount",
|
||||
"listDiscordAccountIds",
|
||||
"listDiscordDirectoryGroupsFromConfig",
|
||||
"listDiscordDirectoryPeersFromConfig",
|
||||
"looksLikeDiscordTargetId",
|
||||
"normalizeDiscordMessagingTarget",
|
||||
"normalizeDiscordOutboundTarget",
|
||||
"readDiscordComponentSpec",
|
||||
"ResolvedDiscordAccount",
|
||||
"resolveDefaultDiscordAccountId",
|
||||
"resolveDiscordAccount",
|
||||
"resolveDiscordChannelId",
|
||||
"resolveDiscordRuntimeGroupPolicy",
|
||||
"resolveDiscordGroupRequireMention",
|
||||
"resolveDiscordGroupToolPolicy",
|
||||
],
|
||||
typeExports: [
|
||||
"DiscordComponentMessageSpec",
|
||||
"DiscordProbe",
|
||||
"DiscordSendComponents",
|
||||
"DiscordSendEmbeds",
|
||||
"DiscordSendResult",
|
||||
"DiscordTokenResolution",
|
||||
"InspectedDiscordAccount",
|
||||
"ResolvedDiscordAccount",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "discord-thread-bindings",
|
||||
source: pluginSource("discord", "runtime-api.js"),
|
||||
loadPolicy: "activated",
|
||||
directExports: {
|
||||
unbindThreadBindingsBySessionKey: "./discord-maintenance.js",
|
||||
},
|
||||
exports: [
|
||||
"autoBindSpawnedDiscordSubagent",
|
||||
"createThreadBindingManager",
|
||||
"getThreadBindingManager",
|
||||
"listThreadBindingsBySessionKey",
|
||||
"resolveThreadBindingIdleTimeoutMs",
|
||||
"resolveThreadBindingInactivityExpiresAt",
|
||||
"resolveThreadBindingMaxAgeExpiresAt",
|
||||
"resolveThreadBindingMaxAgeMs",
|
||||
"setThreadBindingIdleTimeoutBySessionKey",
|
||||
"setThreadBindingMaxAgeBySessionKey",
|
||||
"ThreadBindingManager",
|
||||
"ThreadBindingRecord",
|
||||
"ThreadBindingTargetKind",
|
||||
"unbindThreadBindingsBySessionKey",
|
||||
],
|
||||
typeExports: ["ThreadBindingManager", "ThreadBindingRecord", "ThreadBindingTargetKind"],
|
||||
},
|
||||
{
|
||||
subpath: "discord-timeouts",
|
||||
source: pluginSource("discord", "timeouts.js"),
|
||||
exports: ["DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS", "DISCORD_DEFAULT_LISTENER_TIMEOUT_MS"],
|
||||
},
|
||||
{
|
||||
subpath: "anthropic-cli",
|
||||
source: pluginSource("anthropic", "api.js"),
|
||||
|
|
@ -836,32 +688,6 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
|
|||
source: pluginSource("qianfan", "api.js"),
|
||||
exports: ["QIANFAN_BASE_URL", "QIANFAN_DEFAULT_MODEL_ID", "buildQianfanProvider"],
|
||||
},
|
||||
{
|
||||
subpath: "signal-account",
|
||||
source: pluginSource("signal", "api.js"),
|
||||
exports: ["resolveSignalAccount", "ResolvedSignalAccount"],
|
||||
typeExports: ["ResolvedSignalAccount"],
|
||||
},
|
||||
{
|
||||
subpath: "signal-surface",
|
||||
source: pluginSource("signal", "api.js"),
|
||||
exports: [
|
||||
"isSignalSenderAllowed",
|
||||
"listEnabledSignalAccounts",
|
||||
"listSignalAccountIds",
|
||||
"monitorSignalProvider",
|
||||
"probeSignal",
|
||||
"removeReactionSignal",
|
||||
"ResolvedSignalAccount",
|
||||
"resolveDefaultSignalAccountId",
|
||||
"resolveSignalReactionLevel",
|
||||
"sendMessageSignal",
|
||||
"sendReactionSignal",
|
||||
"signalMessageActions",
|
||||
"SignalSender",
|
||||
],
|
||||
typeExports: ["ResolvedSignalAccount", "SignalProbe", "SignalSender"],
|
||||
},
|
||||
{
|
||||
subpath: "provider-reasoning",
|
||||
source: pluginSource("ollama", "api.js"),
|
||||
|
|
@ -938,84 +764,6 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
|
|||
"SYNTHETIC_MODEL_CATALOG",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "slack-target-parser",
|
||||
source: pluginSource("slack", "api.js"),
|
||||
exports: ["parseSlackTarget", "resolveSlackChannelId"],
|
||||
},
|
||||
{
|
||||
subpath: "slack-account",
|
||||
source: pluginSource("slack", "api.js"),
|
||||
exports: ["resolveSlackAccount", "ResolvedSlackAccount"],
|
||||
typeExports: ["ResolvedSlackAccount"],
|
||||
},
|
||||
{
|
||||
subpath: "slack-runtime-surface",
|
||||
source: pluginSource("slack", "runtime-api.js"),
|
||||
loadPolicy: "activated",
|
||||
exports: [
|
||||
"handleSlackAction",
|
||||
"listSlackDirectoryGroupsLive",
|
||||
"listSlackDirectoryPeersLive",
|
||||
"monitorSlackProvider",
|
||||
"probeSlack",
|
||||
"resolveSlackChannelAllowlist",
|
||||
"resolveSlackUserAllowlist",
|
||||
"sendMessageSlack",
|
||||
"SlackActionContext",
|
||||
],
|
||||
typeExports: ["SlackActionContext"],
|
||||
},
|
||||
{
|
||||
subpath: "slack-surface",
|
||||
source: pluginSource("slack", "api.js"),
|
||||
functionExports: [
|
||||
"listSlackAccountIds",
|
||||
"listSlackDirectoryGroupsFromConfig",
|
||||
"listSlackDirectoryPeersFromConfig",
|
||||
"resolveDefaultSlackAccountId",
|
||||
"resolveSlackRuntimeGroupPolicy",
|
||||
],
|
||||
exports: [
|
||||
"buildSlackThreadingToolContext",
|
||||
"createSlackWebClient",
|
||||
"deleteSlackMessage",
|
||||
"downloadSlackFile",
|
||||
"editSlackMessage",
|
||||
"extractSlackToolSend",
|
||||
"getSlackMemberInfo",
|
||||
"handleSlackHttpRequest",
|
||||
"inspectSlackAccount",
|
||||
"InspectedSlackAccount",
|
||||
"isSlackInteractiveRepliesEnabled",
|
||||
"listEnabledSlackAccounts",
|
||||
"listSlackAccountIds",
|
||||
"listSlackDirectoryGroupsFromConfig",
|
||||
"listSlackDirectoryPeersFromConfig",
|
||||
"listSlackEmojis",
|
||||
"listSlackMessageActions",
|
||||
"listSlackPins",
|
||||
"listSlackReactions",
|
||||
"normalizeAllowListLower",
|
||||
"parseSlackBlocksInput",
|
||||
"recordSlackThreadParticipation",
|
||||
"resolveDefaultSlackAccountId",
|
||||
"resolveSlackAutoThreadId",
|
||||
"resolveSlackGroupRequireMention",
|
||||
"resolveSlackRuntimeGroupPolicy",
|
||||
"resolveSlackGroupToolPolicy",
|
||||
"resolveSlackReplyToMode",
|
||||
"ResolvedSlackAccount",
|
||||
"sendSlackMessage",
|
||||
"pinSlackMessage",
|
||||
"reactSlackMessage",
|
||||
"readSlackMessages",
|
||||
"removeOwnSlackReactions",
|
||||
"removeSlackReaction",
|
||||
"unpinSlackMessage",
|
||||
],
|
||||
typeExports: ["InspectedSlackAccount", "ResolvedSlackAccount", "SlackProbe"],
|
||||
},
|
||||
{
|
||||
subpath: "together",
|
||||
source: pluginSource("together", "api.js"),
|
||||
|
|
@ -1040,71 +788,6 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
|
|||
"VENICE_MODEL_CATALOG",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "telegram-account",
|
||||
source: pluginSource("telegram", "api.js"),
|
||||
exports: ["resolveTelegramAccount", "ResolvedTelegramAccount"],
|
||||
typeExports: ["ResolvedTelegramAccount"],
|
||||
},
|
||||
{
|
||||
subpath: "telegram-allow-from",
|
||||
source: pluginSource("telegram", "api.js"),
|
||||
exports: ["isNumericTelegramUserId", "normalizeTelegramAllowFromEntry"],
|
||||
},
|
||||
{
|
||||
subpath: "telegram-surface",
|
||||
source: pluginSource("telegram", "api.js"),
|
||||
exports: [
|
||||
"buildBrowseProvidersButton",
|
||||
"buildModelsKeyboard",
|
||||
"buildProviderKeyboard",
|
||||
"buildTelegramGroupPeerId",
|
||||
"calculateTotalPages",
|
||||
"createTelegramActionGate",
|
||||
"fetchTelegramChatId",
|
||||
"getCacheStats",
|
||||
"getModelsPageSize",
|
||||
"inspectTelegramAccount",
|
||||
"InspectedTelegramAccount",
|
||||
"isTelegramExecApprovalApprover",
|
||||
"isTelegramExecApprovalAuthorizedSender",
|
||||
"isTelegramExecApprovalClientEnabled",
|
||||
"isTelegramExecApprovalTargetRecipient",
|
||||
"listTelegramAccountIds",
|
||||
"listTelegramDirectoryGroupsFromConfig",
|
||||
"listTelegramDirectoryPeersFromConfig",
|
||||
"looksLikeTelegramTargetId",
|
||||
"lookupTelegramChatId",
|
||||
"normalizeTelegramMessagingTarget",
|
||||
"parseTelegramReplyToMessageId",
|
||||
"parseTelegramTarget",
|
||||
"parseTelegramThreadId",
|
||||
"ProviderInfo",
|
||||
"ResolvedTelegramAccount",
|
||||
"resolveTelegramAutoThreadId",
|
||||
"resolveTelegramGroupRequireMention",
|
||||
"resolveTelegramGroupToolPolicy",
|
||||
"resolveTelegramInlineButtonsScope",
|
||||
"resolveTelegramPollActionGateState",
|
||||
"resolveTelegramReactionLevel",
|
||||
"resolveTelegramTargetChatType",
|
||||
"searchStickers",
|
||||
"sendTelegramPayloadMessages",
|
||||
"StickerMetadata",
|
||||
"TelegramButtonStyle",
|
||||
"TelegramInlineButtons",
|
||||
],
|
||||
typeExports: [
|
||||
"InspectedTelegramAccount",
|
||||
"ProviderInfo",
|
||||
"ResolvedTelegramAccount",
|
||||
"StickerMetadata",
|
||||
"TelegramButtonStyle",
|
||||
"TelegramInlineButtons",
|
||||
"TelegramProbe",
|
||||
"TelegramTokenResolution",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "vercel-ai-gateway",
|
||||
source: pluginSource("vercel-ai-gateway", "api.js"),
|
||||
|
|
@ -1193,36 +876,6 @@ export const GENERATED_PLUGIN_SDK_FACADES = [
|
|||
"ZAI_GLOBAL_BASE_URL",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "whatsapp-targets",
|
||||
source: pluginSource("whatsapp", "api.js"),
|
||||
exports: ["isWhatsAppGroupJid", "isWhatsAppUserTarget", "normalizeWhatsAppTarget"],
|
||||
},
|
||||
{
|
||||
subpath: "whatsapp-surface",
|
||||
source: pluginSource("whatsapp", "api.js"),
|
||||
exportSources: {
|
||||
DEFAULT_WEB_MEDIA_BYTES: pluginSource("whatsapp", "constants.js"),
|
||||
},
|
||||
exports: [
|
||||
"DEFAULT_WEB_MEDIA_BYTES",
|
||||
"hasAnyWhatsAppAuth",
|
||||
"listEnabledWhatsAppAccounts",
|
||||
"listWhatsAppDirectoryGroupsFromConfig",
|
||||
"listWhatsAppDirectoryPeersFromConfig",
|
||||
"resolveWhatsAppAccount",
|
||||
"resolveWhatsAppGroupRequireMention",
|
||||
"resolveWhatsAppGroupToolPolicy",
|
||||
"resolveWhatsAppOutboundTarget",
|
||||
"whatsappAccessControlTesting",
|
||||
],
|
||||
typeExports: [
|
||||
"WebChannelStatus",
|
||||
"WebInboundMessage",
|
||||
"WebListenerCloseReason",
|
||||
"WebMonitorTuning",
|
||||
],
|
||||
},
|
||||
{
|
||||
subpath: "zalo-setup",
|
||||
source: pluginSource("zalo", "api.js"),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const acquireSessionWriteLockMock = vi.hoisted(() =>
|
|||
|
||||
vi.mock("../session-write-lock.js", () => ({
|
||||
acquireSessionWriteLock: (params: unknown) => acquireSessionWriteLockMock(params),
|
||||
resolveSessionLockMaxHoldFromTimeout: () => 1,
|
||||
}));
|
||||
|
||||
let truncateToolResultText: typeof import("./tool-result-truncation.js").truncateToolResultText;
|
||||
|
|
@ -28,6 +29,7 @@ async function loadFreshToolResultTruncationModuleForTest() {
|
|||
vi.resetModules();
|
||||
vi.doMock("../session-write-lock.js", () => ({
|
||||
acquireSessionWriteLock: (params: unknown) => acquireSessionWriteLockMock(params),
|
||||
resolveSessionLockMaxHoldFromTimeout: () => 1,
|
||||
}));
|
||||
({ onSessionTranscriptUpdate } = await import("../../sessions/transcript-events.js"));
|
||||
({
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const acquireSessionWriteLockMock = vi.hoisted(() =>
|
|||
|
||||
vi.mock("../session-write-lock.js", () => ({
|
||||
acquireSessionWriteLock: (params: unknown) => acquireSessionWriteLockMock(params),
|
||||
resolveSessionLockMaxHoldFromTimeout: () => 1,
|
||||
}));
|
||||
|
||||
let rewriteTranscriptEntriesInSessionFile: typeof import("./transcript-rewrite.js").rewriteTranscriptEntriesInSessionFile;
|
||||
|
|
@ -20,6 +21,7 @@ async function loadFreshTranscriptRewriteModuleForTest() {
|
|||
vi.resetModules();
|
||||
vi.doMock("../session-write-lock.js", () => ({
|
||||
acquireSessionWriteLock: (params: unknown) => acquireSessionWriteLockMock(params),
|
||||
resolveSessionLockMaxHoldFromTimeout: () => 1,
|
||||
}));
|
||||
({ onSessionTranscriptUpdate } = await import("../../sessions/transcript-events.js"));
|
||||
({ installSessionToolResultGuard } = await import("../session-tool-result-guard.js"));
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export function installReplyRuntimeMocks(mocks: ReplyRuntimeMocks) {
|
|||
listSkillCommandsForWorkspace: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
webAuthExists: mocks.webAuthExists,
|
||||
getWebAuthAgeMs: mocks.getWebAuthAgeMs,
|
||||
readWebSelfId: mocks.readWebSelfId,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
isTelegramExecApprovalAuthorizedSender,
|
||||
isTelegramExecApprovalClientEnabled,
|
||||
} from "../../../extensions/telegram/api.js";
|
||||
getChannelPlugin,
|
||||
resolveChannelApprovalCapability,
|
||||
} from "../../channels/plugins/index.js";
|
||||
import { callGateway } from "../../gateway/call.js";
|
||||
import { ErrorCodes } from "../../gateway/protocol/index.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
|
|
@ -77,17 +77,6 @@ function buildResolvedByLabel(params: Parameters<CommandHandler>[0]): string {
|
|||
return `${channel}:${sender}`;
|
||||
}
|
||||
|
||||
function isAuthorizedTelegramExecSender(params: Parameters<CommandHandler>[0]): boolean {
|
||||
if (params.command.channel !== "telegram") {
|
||||
return false;
|
||||
}
|
||||
return isTelegramExecApprovalAuthorizedSender({
|
||||
cfg: params.cfg,
|
||||
accountId: params.ctx.AccountId,
|
||||
senderId: params.command.senderId,
|
||||
});
|
||||
}
|
||||
|
||||
function readErrorCode(value: unknown): string | null {
|
||||
return typeof value === "string" && value.trim() ? value : null;
|
||||
}
|
||||
|
|
@ -178,7 +167,21 @@ export const handleApproveCommand: CommandHandler = async (params, allowTextComm
|
|||
}
|
||||
|
||||
const isPluginId = parsed.id.startsWith("plugin:");
|
||||
const telegramExecAuthorizedSender = isAuthorizedTelegramExecSender(params);
|
||||
const approvalCapability = resolveChannelApprovalCapability(
|
||||
getChannelPlugin(params.command.channel),
|
||||
);
|
||||
const approveCommandBehavior = approvalCapability?.resolveApproveCommandBehavior?.({
|
||||
cfg: params.cfg,
|
||||
accountId: params.ctx.AccountId,
|
||||
senderId: params.command.senderId,
|
||||
approvalKind: isPluginId ? "plugin" : "exec",
|
||||
});
|
||||
if (approveCommandBehavior?.kind === "ignore") {
|
||||
return { shouldContinue: false };
|
||||
}
|
||||
if (approveCommandBehavior?.kind === "reply") {
|
||||
return { shouldContinue: false, reply: { text: approveCommandBehavior.text } };
|
||||
}
|
||||
const execApprovalAuthorization = resolveApprovalCommandAuthorization({
|
||||
cfg: params.cfg,
|
||||
channel: params.command.channel,
|
||||
|
|
@ -203,18 +206,6 @@ export const handleApproveCommand: CommandHandler = async (params, allowTextComm
|
|||
return { shouldContinue: false };
|
||||
}
|
||||
|
||||
if (
|
||||
params.command.channel === "telegram" &&
|
||||
!isPluginId &&
|
||||
!telegramExecAuthorizedSender &&
|
||||
!isTelegramExecApprovalClientEnabled({ cfg: params.cfg, accountId: params.ctx.AccountId })
|
||||
) {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
reply: { text: "❌ Telegram exec approvals are not enabled for this bot account." },
|
||||
};
|
||||
}
|
||||
|
||||
const missingScope = requireGatewayClientScopeForInternalChannel(params, {
|
||||
label: "/approve",
|
||||
allowedScopes: ["operator.approvals", "operator.admin"],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { resolveFastModeState } from "../../agents/fast-mode.js";
|
||||
import {
|
||||
setChannelConversationBindingIdleTimeoutBySessionKey,
|
||||
setChannelConversationBindingMaxAgeBySessionKey,
|
||||
} from "../../channels/plugins/conversation-bindings.js";
|
||||
import { formatThreadBindingDurationLabel } from "../../channels/thread-bindings-messages.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { isRestartEnabled } from "../../config/commands.js";
|
||||
|
|
@ -7,7 +11,6 @@ import { getSessionBindingService } from "../../infra/outbound/session-binding-s
|
|||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
import { scheduleGatewaySigusr1Restart, triggerOpenClawRestart } from "../../infra/restart.js";
|
||||
import { loadCostUsageSummary, loadSessionCostSummary } from "../../infra/session-cost-usage.js";
|
||||
import { createPluginRuntime } from "../../plugins/runtime/index.js";
|
||||
import { formatTokenCount, formatUsd } from "../../utils/usage-format.js";
|
||||
import { parseActivationCommand } from "../group-activation.js";
|
||||
import { parseSendPolicyCommand } from "../send-policy.js";
|
||||
|
|
@ -32,12 +35,6 @@ const SESSION_COMMAND_PREFIX = "/session";
|
|||
const SESSION_DURATION_OFF_VALUES = new Set(["off", "disable", "disabled", "none", "0"]);
|
||||
const SESSION_ACTION_IDLE = "idle";
|
||||
const SESSION_ACTION_MAX_AGE = "max-age";
|
||||
let cachedChannelRuntime: ReturnType<typeof createPluginRuntime>["channel"] | undefined;
|
||||
|
||||
function getChannelRuntime() {
|
||||
cachedChannelRuntime ??= createPluginRuntime().channel;
|
||||
return cachedChannelRuntime;
|
||||
}
|
||||
|
||||
function resolveSessionCommandUsage() {
|
||||
return "Usage: /session idle <duration|off> | /session max-age <duration|off> (example: /session idle 24h)";
|
||||
|
|
@ -462,20 +459,14 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
|||
})
|
||||
: undefined;
|
||||
const telegramConversationId = onTelegram ? resolveTelegramConversationId(params) : undefined;
|
||||
const channelRuntime = getChannelRuntime();
|
||||
|
||||
const discordManager = onDiscord
|
||||
? channelRuntime.discord.threadBindings.getManager(accountId)
|
||||
: null;
|
||||
if (onDiscord && !discordManager) {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
reply: { text: "⚠️ Discord thread bindings are unavailable for this account." },
|
||||
};
|
||||
}
|
||||
|
||||
const discordBinding =
|
||||
onDiscord && threadId ? discordManager?.getByThreadId(threadId) : undefined;
|
||||
onDiscord && threadId
|
||||
? sessionBindingService.resolveByConversation({
|
||||
channel: "discord",
|
||||
accountId,
|
||||
conversationId: threadId,
|
||||
})
|
||||
: null;
|
||||
const telegramBinding =
|
||||
onTelegram && telegramConversationId
|
||||
? sessionBindingService.resolveByConversation({
|
||||
|
|
@ -538,39 +529,18 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
|||
};
|
||||
}
|
||||
|
||||
const idleTimeoutMs = onDiscord
|
||||
? channelRuntime.discord.threadBindings.resolveIdleTimeoutMs({
|
||||
record: discordBinding!,
|
||||
defaultIdleTimeoutMs: discordManager!.getIdleTimeoutMs(),
|
||||
})
|
||||
: resolveSessionBindingDurationMs(
|
||||
(onMatrix ? matrixBinding : telegramBinding)!,
|
||||
"idleTimeoutMs",
|
||||
24 * 60 * 60 * 1000,
|
||||
);
|
||||
const idleExpiresAt = onDiscord
|
||||
? channelRuntime.discord.threadBindings.resolveInactivityExpiresAt({
|
||||
record: discordBinding!,
|
||||
defaultIdleTimeoutMs: discordManager!.getIdleTimeoutMs(),
|
||||
})
|
||||
: idleTimeoutMs > 0
|
||||
? resolveSessionBindingLastActivityAt((onMatrix ? matrixBinding : telegramBinding)!) +
|
||||
idleTimeoutMs
|
||||
: undefined;
|
||||
const maxAgeMs = onDiscord
|
||||
? channelRuntime.discord.threadBindings.resolveMaxAgeMs({
|
||||
record: discordBinding!,
|
||||
defaultMaxAgeMs: discordManager!.getMaxAgeMs(),
|
||||
})
|
||||
: resolveSessionBindingDurationMs((onMatrix ? matrixBinding : telegramBinding)!, "maxAgeMs", 0);
|
||||
const maxAgeExpiresAt = onDiscord
|
||||
? channelRuntime.discord.threadBindings.resolveMaxAgeExpiresAt({
|
||||
record: discordBinding!,
|
||||
defaultMaxAgeMs: discordManager!.getMaxAgeMs(),
|
||||
})
|
||||
: maxAgeMs > 0
|
||||
? (onMatrix ? matrixBinding : telegramBinding)!.boundAt + maxAgeMs
|
||||
const activeBinding = (onDiscord ? discordBinding : onMatrix ? matrixBinding : telegramBinding)!;
|
||||
const idleTimeoutMs = resolveSessionBindingDurationMs(
|
||||
activeBinding,
|
||||
"idleTimeoutMs",
|
||||
24 * 60 * 60 * 1000,
|
||||
);
|
||||
const idleExpiresAt =
|
||||
idleTimeoutMs > 0
|
||||
? resolveSessionBindingLastActivityAt(activeBinding) + idleTimeoutMs
|
||||
: undefined;
|
||||
const maxAgeMs = resolveSessionBindingDurationMs(activeBinding, "maxAgeMs", 0);
|
||||
const maxAgeExpiresAt = maxAgeMs > 0 ? activeBinding.boundAt + maxAgeMs : undefined;
|
||||
|
||||
const durationArgRaw = tokens.slice(1).join("");
|
||||
if (!durationArgRaw) {
|
||||
|
|
@ -612,9 +582,7 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
|||
}
|
||||
|
||||
const senderId = params.command.senderId?.trim() || "";
|
||||
const boundBy = onDiscord
|
||||
? discordBinding!.boundBy
|
||||
: resolveSessionBindingBoundBy((onMatrix ? matrixBinding : telegramBinding)!);
|
||||
const boundBy = resolveSessionBindingBoundBy(activeBinding);
|
||||
if (boundBy && boundBy !== "system" && senderId && senderId !== boundBy) {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
|
|
@ -638,47 +606,21 @@ export const handleSessionCommand: CommandHandler = async (params, allowTextComm
|
|||
};
|
||||
}
|
||||
|
||||
const updatedBindings = (() => {
|
||||
if (onDiscord) {
|
||||
return action === SESSION_ACTION_IDLE
|
||||
? channelRuntime.discord.threadBindings.setIdleTimeoutBySessionKey({
|
||||
targetSessionKey: discordBinding!.targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs: durationMs,
|
||||
})
|
||||
: channelRuntime.discord.threadBindings.setMaxAgeBySessionKey({
|
||||
targetSessionKey: discordBinding!.targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs: durationMs,
|
||||
});
|
||||
}
|
||||
if (onMatrix) {
|
||||
return action === SESSION_ACTION_IDLE
|
||||
? channelRuntime.matrix.threadBindings.setIdleTimeoutBySessionKey({
|
||||
targetSessionKey: matrixBinding!.targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs: durationMs,
|
||||
})
|
||||
: channelRuntime.matrix.threadBindings.setMaxAgeBySessionKey({
|
||||
targetSessionKey: matrixBinding!.targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs: durationMs,
|
||||
});
|
||||
}
|
||||
return action === SESSION_ACTION_IDLE
|
||||
? channelRuntime.threadBindings.setIdleTimeoutBySessionKey({
|
||||
channelId: "telegram",
|
||||
targetSessionKey: telegramBinding!.targetSessionKey,
|
||||
const channelId = onDiscord ? "discord" : onMatrix ? "matrix" : "telegram";
|
||||
const updatedBindings =
|
||||
action === SESSION_ACTION_IDLE
|
||||
? setChannelConversationBindingIdleTimeoutBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey: activeBinding.targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs: durationMs,
|
||||
})
|
||||
: channelRuntime.threadBindings.setMaxAgeBySessionKey({
|
||||
channelId: "telegram",
|
||||
targetSessionKey: telegramBinding!.targetSessionKey,
|
||||
: setChannelConversationBindingMaxAgeBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey: activeBinding.targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs: durationMs,
|
||||
});
|
||||
})();
|
||||
if (updatedBindings.length === 0) {
|
||||
return {
|
||||
shouldContinue: false,
|
||||
|
|
|
|||
|
|
@ -491,6 +491,26 @@ const telegramCommandTestPlugin: ChannelPlugin = {
|
|||
formatAllowFrom: normalizeTelegramAllowFromEntries,
|
||||
}),
|
||||
auth: telegramNativeApprovalAdapter.auth,
|
||||
approvalCapability: {
|
||||
resolveApproveCommandBehavior: ({ cfg, accountId, senderId, approvalKind }) => {
|
||||
if (approvalKind !== "exec") {
|
||||
return undefined;
|
||||
}
|
||||
if (isTelegramExecApprovalClientEnabled({ cfg, accountId })) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
isTelegramExecApprovalAuthorizedSender({ cfg, accountId, senderId }) &&
|
||||
!getTelegramExecApprovalApprovers({ cfg, accountId }).includes(senderId?.trim() ?? "")
|
||||
) {
|
||||
return { kind: "ignore" } as const;
|
||||
}
|
||||
return {
|
||||
kind: "reply",
|
||||
text: "❌ Telegram exec approvals are not enabled for this bot account.",
|
||||
} as const;
|
||||
},
|
||||
},
|
||||
pairing: {
|
||||
idLabel: "telegramUserId",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ async function loadFreshFollowupRunnerModuleForTest() {
|
|||
acquireSessionWriteLock: vi.fn(async () => ({
|
||||
release: async () => {},
|
||||
})),
|
||||
resolveSessionLockMaxHoldFromTimeout: vi.fn(() => 1),
|
||||
}));
|
||||
vi.doMock("../../agents/pi-embedded.js", () => ({
|
||||
abortEmbeddedPiRun: vi.fn(async () => false),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { initSessionState } from "./session.js";
|
|||
// Perf: session-store locks are exercised elsewhere; most session tests don't need FS lock files.
|
||||
vi.mock("../../agents/session-write-lock.js", () => ({
|
||||
acquireSessionWriteLock: async () => ({ release: async () => {} }),
|
||||
resolveSessionLockMaxHoldFromTimeout: () => 1,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/model-catalog.js", () => ({
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Barrel exports for the web channel pieces. Splitting the original 900+ line
|
||||
// module keeps responsibilities small and testable.
|
||||
import { resolveWaWebAuthDir } from "./plugins/runtime/runtime-web-channel-boundary.js";
|
||||
import { resolveWebChannelAuthDir } from "./plugins/runtime/runtime-web-channel-plugin.js";
|
||||
|
||||
export { HEARTBEAT_PROMPT } from "./auto-reply/heartbeat.js";
|
||||
export { HEARTBEAT_TOKEN } from "./auto-reply/tokens.js";
|
||||
export { loadWebMedia, optimizeImageToJpeg } from "./media/web-media.js";
|
||||
export {
|
||||
createWaSocket,
|
||||
createWebChannelSocket as createWaSocket,
|
||||
extractMediaPlaceholder,
|
||||
extractText,
|
||||
formatError,
|
||||
|
|
@ -19,19 +19,19 @@ export {
|
|||
pickWebChannel,
|
||||
resolveHeartbeatRecipients,
|
||||
runWebHeartbeatOnce,
|
||||
sendMessageWhatsApp,
|
||||
sendReactionWhatsApp,
|
||||
waitForWaConnection,
|
||||
sendWebChannelMessage as sendMessageWhatsApp,
|
||||
sendWebChannelReaction as sendReactionWhatsApp,
|
||||
waitForWebChannelConnection as waitForWaConnection,
|
||||
webAuthExists,
|
||||
} from "./plugins/runtime/runtime-web-channel-boundary.js";
|
||||
} from "./plugins/runtime/runtime-web-channel-plugin.js";
|
||||
|
||||
// Keep the historic constant surface available, but resolve it through the
|
||||
// plugin boundary only when a caller actually coerces the value to string.
|
||||
class LazyWhatsAppAuthDir {
|
||||
// web-channel plugin boundary only when a caller actually coerces the value to string.
|
||||
class LazyWebChannelAuthDir {
|
||||
#value: string | null = null;
|
||||
|
||||
#read(): string {
|
||||
this.#value ??= resolveWaWebAuthDir();
|
||||
this.#value ??= resolveWebChannelAuthDir();
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
|
|
@ -48,4 +48,4 @@ class LazyWhatsAppAuthDir {
|
|||
}
|
||||
}
|
||||
|
||||
export const WA_WEB_AUTH_DIR = new LazyWhatsAppAuthDir() as unknown as string;
|
||||
export const WA_WEB_AUTH_DIR = new LazyWebChannelAuthDir() as unknown as string;
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ describe("normalizeChatType", () => {
|
|||
|
||||
describe("WA_WEB_AUTH_DIR", () => {
|
||||
afterEach(() => {
|
||||
vi.doUnmock("../plugins/runtime/runtime-web-channel-boundary.js");
|
||||
vi.doUnmock("../plugins/runtime/runtime-web-channel-plugin.js");
|
||||
});
|
||||
|
||||
it("resolves lazily and caches across the legacy and channels/web entrypoints", async () => {
|
||||
const resolveWaWebAuthDir = vi.fn(() => "/tmp/openclaw-whatsapp-auth");
|
||||
const resolveWebChannelAuthDir = vi.fn(() => "/tmp/openclaw-whatsapp-auth");
|
||||
|
||||
vi.resetModules();
|
||||
vi.doMock("../plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
createWaSocket: vi.fn(),
|
||||
vi.doMock("../plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
createWebChannelSocket: vi.fn(),
|
||||
extractMediaPlaceholder: vi.fn(),
|
||||
extractText: vi.fn(),
|
||||
formatError: vi.fn(),
|
||||
|
|
@ -49,20 +49,20 @@ describe("WA_WEB_AUTH_DIR", () => {
|
|||
monitorWebInbox: vi.fn(),
|
||||
pickWebChannel: vi.fn(),
|
||||
resolveHeartbeatRecipients: vi.fn(),
|
||||
resolveWaWebAuthDir,
|
||||
resolveWebChannelAuthDir,
|
||||
runWebHeartbeatOnce: vi.fn(),
|
||||
sendMessageWhatsApp: vi.fn(),
|
||||
sendReactionWhatsApp: vi.fn(),
|
||||
waitForWaConnection: vi.fn(),
|
||||
sendWebChannelMessage: vi.fn(),
|
||||
sendWebChannelReaction: vi.fn(),
|
||||
waitForWebChannelConnection: vi.fn(),
|
||||
webAuthExists: vi.fn(),
|
||||
}));
|
||||
|
||||
const channelWeb = await import("../channel-web.js");
|
||||
const webEntry = await import("./web/index.js");
|
||||
|
||||
expect(resolveWaWebAuthDir).not.toHaveBeenCalled();
|
||||
expect(resolveWebChannelAuthDir).not.toHaveBeenCalled();
|
||||
expect(String(channelWeb.WA_WEB_AUTH_DIR)).toBe("/tmp/openclaw-whatsapp-auth");
|
||||
expect(String(webEntry.WA_WEB_AUTH_DIR)).toBe("/tmp/openclaw-whatsapp-auth");
|
||||
expect(resolveWaWebAuthDir).toHaveBeenCalledTimes(1);
|
||||
expect(resolveWebChannelAuthDir).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ function buildApprovalCapabilityFromLegacyPlugin(
|
|||
): ChannelApprovalCapability | undefined {
|
||||
const authorizeActorAction = plugin?.auth?.authorizeActorAction;
|
||||
const getActionAvailabilityState = plugin?.auth?.getActionAvailabilityState;
|
||||
const resolveApproveCommandBehavior = plugin?.auth?.resolveApproveCommandBehavior;
|
||||
const approvals = plugin?.approvals;
|
||||
if (
|
||||
!authorizeActorAction &&
|
||||
!getActionAvailabilityState &&
|
||||
!resolveApproveCommandBehavior &&
|
||||
!approvals?.delivery &&
|
||||
!approvals?.render &&
|
||||
!approvals?.native
|
||||
|
|
@ -18,6 +20,7 @@ function buildApprovalCapabilityFromLegacyPlugin(
|
|||
return {
|
||||
authorizeActorAction,
|
||||
getActionAvailabilityState,
|
||||
resolveApproveCommandBehavior,
|
||||
delivery: approvals?.delivery,
|
||||
render: approvals?.render,
|
||||
native: approvals?.native,
|
||||
|
|
@ -39,6 +42,8 @@ export function resolveChannelApprovalCapability(
|
|||
authorizeActorAction: capability.authorizeActorAction ?? legacyCapability.authorizeActorAction,
|
||||
getActionAvailabilityState:
|
||||
capability.getActionAvailabilityState ?? legacyCapability.getActionAvailabilityState,
|
||||
resolveApproveCommandBehavior:
|
||||
capability.resolveApproveCommandBehavior ?? legacyCapability.resolveApproveCommandBehavior,
|
||||
delivery: capability.delivery ?? legacyCapability.delivery,
|
||||
render: capability.render ?? legacyCapability.render,
|
||||
native: capability.native ?? legacyCapability.native,
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ export type ChannelConfigAdapter<ResolvedAccount> = {
|
|||
accountId?: string | null;
|
||||
allowFrom: Array<string | number>;
|
||||
}) => string[];
|
||||
hasPersistedAuthState?: (params: { cfg: OpenClawConfig; env?: NodeJS.ProcessEnv }) => boolean;
|
||||
resolveDefaultTo?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
|
|
@ -415,6 +416,7 @@ export type ChannelAuthAdapter = {
|
|||
accountId?: string | null;
|
||||
action: "approve";
|
||||
}) => ChannelActionAvailabilityState;
|
||||
resolveApproveCommandBehavior?: ChannelApprovalCapability["resolveApproveCommandBehavior"];
|
||||
};
|
||||
|
||||
export type ChannelHeartbeatAdapter = {
|
||||
|
|
@ -520,6 +522,11 @@ export type ChannelApprovalDeliveryAdapter = {
|
|||
|
||||
export type ChannelApprovalKind = "exec" | "plugin";
|
||||
|
||||
export type ChannelApproveCommandBehavior =
|
||||
| { kind: "allow" }
|
||||
| { kind: "ignore" }
|
||||
| { kind: "reply"; text: string };
|
||||
|
||||
export type ChannelApprovalNativeSurface = "origin" | "approver-dm";
|
||||
|
||||
export type ChannelApprovalNativeTarget = {
|
||||
|
|
@ -592,6 +599,12 @@ export type ChannelApprovalRenderAdapter = {
|
|||
export type ChannelApprovalCapability = ChannelApprovalAdapter & {
|
||||
authorizeActorAction?: ChannelAuthAdapter["authorizeActorAction"];
|
||||
getActionAvailabilityState?: ChannelAuthAdapter["getActionAvailabilityState"];
|
||||
resolveApproveCommandBehavior?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
senderId?: string | null;
|
||||
approvalKind: ChannelApprovalKind;
|
||||
}) => ChannelApproveCommandBehavior | undefined;
|
||||
};
|
||||
|
||||
export type ChannelApprovalAdapter = {
|
||||
|
|
|
|||
|
|
@ -397,6 +397,10 @@ export type ChannelThreadingToolContext = {
|
|||
/** Channel-owned messaging helpers for target parsing, routing, and payload shaping. */
|
||||
export type ChannelMessagingAdapter = {
|
||||
normalizeTarget?: (raw: string) => string | undefined;
|
||||
normalizeExplicitSessionKey?: (params: {
|
||||
sessionKey: string;
|
||||
ctx: MsgContext;
|
||||
}) => string | undefined;
|
||||
/**
|
||||
* Canonical plugin-owned session conversation grammar.
|
||||
* Use this when the provider encodes thread or scoped-conversation semantics
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
import { inspectDiscordAccount as inspectDiscordAccountImpl } from "../../extensions/discord/api.js";
|
||||
|
||||
export type { InspectedDiscordAccount } from "../../extensions/discord/api.js";
|
||||
|
||||
type InspectDiscordAccount = typeof import("../../extensions/discord/api.js").inspectDiscordAccount;
|
||||
|
||||
export function inspectDiscordAccount(
|
||||
...args: Parameters<InspectDiscordAccount>
|
||||
): ReturnType<InspectDiscordAccount> {
|
||||
return inspectDiscordAccountImpl(...args);
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { inspectSlackAccount as inspectSlackAccountImpl } from "../../extensions/slack/api.js";
|
||||
|
||||
export type { InspectedSlackAccount } from "../../extensions/slack/api.js";
|
||||
|
||||
type InspectSlackAccount = typeof import("../../extensions/slack/api.js").inspectSlackAccount;
|
||||
|
||||
export function inspectSlackAccount(
|
||||
...args: Parameters<InspectSlackAccount>
|
||||
): ReturnType<InspectSlackAccount> {
|
||||
return inspectSlackAccountImpl(...args);
|
||||
}
|
||||
|
|
@ -19,15 +19,6 @@ const sendFns = vi.hoisted(() => ({
|
|||
imessage: vi.fn(async () => ({ messageId: "i1", chatId: "imessage:1" })),
|
||||
}));
|
||||
|
||||
const whatsappBoundaryLoads = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-boundary.js", async (importOriginal) => {
|
||||
whatsappBoundaryLoads();
|
||||
return await importOriginal<
|
||||
typeof import("../plugins/runtime/runtime-web-channel-boundary.js")
|
||||
>();
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/whatsapp.js", () => {
|
||||
moduleLoads.whatsapp();
|
||||
return { runtimeSend: { sendMessage: sendFns.whatsapp } };
|
||||
|
|
@ -112,13 +103,4 @@ describe("createDefaultDeps", () => {
|
|||
expect(moduleLoads.discord).toHaveBeenCalledTimes(1);
|
||||
expect(sendFns.discord).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("does not import the whatsapp runtime boundary on deps module load", async () => {
|
||||
await importFreshModule<typeof import("./deps.js")>(
|
||||
import.meta.url,
|
||||
"./deps.js?scope=no-whatsapp-runtime-on-import",
|
||||
);
|
||||
|
||||
expect(whatsappBoundaryLoads).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { sendMessageDiscord as sendMessageDiscordImpl } from "../../../extensions/discord/runtime-api.js";
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../../extensions/discord/runtime-api.js").sendMessageDiscord;
|
||||
};
|
||||
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageDiscordImpl,
|
||||
} satisfies RuntimeSend;
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "discord",
|
||||
exportName: "sendMessageDiscord",
|
||||
missingLabel: "Discord plugin runtime",
|
||||
});
|
||||
|
|
|
|||
37
src/cli/send-runtime/plugin-boundary-send.ts
Normal file
37
src/cli/send-runtime/plugin-boundary-send.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { createCachedPluginBoundaryModuleLoader } from "../../plugins/runtime/runtime-plugin-boundary.js";
|
||||
|
||||
type RuntimeSendModule = Record<string, unknown>;
|
||||
|
||||
export type RuntimeSend = {
|
||||
sendMessage: (...args: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
|
||||
function resolveRuntimeExport(
|
||||
module: RuntimeSendModule | null,
|
||||
pluginId: string,
|
||||
exportName: string,
|
||||
): (...args: unknown[]) => Promise<unknown> {
|
||||
const candidate = module?.[exportName];
|
||||
if (typeof candidate !== "function") {
|
||||
throw new Error(`${pluginId} plugin runtime is unavailable: missing export '${exportName}'`);
|
||||
}
|
||||
return candidate as (...args: unknown[]) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export function createPluginBoundaryRuntimeSend(params: {
|
||||
pluginId: string;
|
||||
exportName: string;
|
||||
missingLabel: string;
|
||||
}): RuntimeSend {
|
||||
const loadRuntimeModuleSync = createCachedPluginBoundaryModuleLoader<RuntimeSendModule>({
|
||||
pluginId: params.pluginId,
|
||||
entryBaseName: "runtime-api",
|
||||
required: true,
|
||||
missingLabel: params.missingLabel,
|
||||
});
|
||||
|
||||
return {
|
||||
sendMessage: (...args) =>
|
||||
resolveRuntimeExport(loadRuntimeModuleSync(), params.pluginId, params.exportName)(...args),
|
||||
};
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import { sendMessageSignal as sendMessageSignalImpl } from "../../../extensions/signal/runtime-api.js";
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../../extensions/signal/runtime-api.js").sendMessageSignal;
|
||||
};
|
||||
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageSignalImpl,
|
||||
} satisfies RuntimeSend;
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "signal",
|
||||
exportName: "sendMessageSignal",
|
||||
missingLabel: "Signal plugin runtime",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { sendMessageSlack as sendMessageSlackImpl } from "../../../extensions/slack/runtime-api.js";
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../../extensions/slack/runtime-api.js").sendMessageSlack;
|
||||
};
|
||||
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageSlackImpl,
|
||||
} satisfies RuntimeSend;
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "slack",
|
||||
exportName: "sendMessageSlack",
|
||||
missingLabel: "Slack plugin runtime",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { sendMessageWhatsApp as sendMessageWhatsAppImpl } from "../../plugins/runtime/runtime-web-channel-boundary.js";
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../plugins/runtime/runtime-web-channel-boundary.js").sendMessageWhatsApp;
|
||||
};
|
||||
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageWhatsAppImpl,
|
||||
} satisfies RuntimeSend;
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "whatsapp",
|
||||
exportName: "sendMessageWhatsApp",
|
||||
missingLabel: "WhatsApp plugin runtime",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ async function loadFreshHealthModulesForTest() {
|
|||
recordSessionMetaFromInbound: vi.fn().mockResolvedValue(undefined),
|
||||
updateLastRoute: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
vi.doMock("../plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
vi.doMock("../plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
webAuthExists: vi.fn(async () => true),
|
||||
getWebAuthAgeMs: vi.fn(() => 1234),
|
||||
readWebSelfId: vi.fn(() => ({ e164: null, jid: null })),
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ vi.mock("../channels/plugins/index.js", () => ({
|
|||
},
|
||||
] as unknown,
|
||||
}));
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
webAuthExists: mocks.webAuthExists,
|
||||
getWebAuthAgeMs: mocks.getWebAuthAgeMs,
|
||||
readWebSelfId: mocks.readWebSelfId,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { hasAnyWhatsAppAuth } from "../../extensions/whatsapp/auth-presence.js";
|
||||
import { hasMeaningfulChannelConfig } from "../channels/config-presence.js";
|
||||
import { getChannelPlugin } from "../channels/plugins/index.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import type { OpenClawConfig } from "./config.js";
|
||||
|
||||
|
|
@ -127,17 +127,6 @@ function isStructuredChannelConfigured(
|
|||
return hasMeaningfulChannelConfig(entry);
|
||||
}
|
||||
|
||||
function isWhatsAppConfigured(cfg: OpenClawConfig): boolean {
|
||||
if (hasAnyWhatsAppAuth(cfg)) {
|
||||
return true;
|
||||
}
|
||||
const entry = resolveChannelConfig(cfg, "whatsapp");
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
return hasMeaningfulChannelConfig(entry);
|
||||
}
|
||||
|
||||
function isGenericChannelConfigured(cfg: OpenClawConfig, channelId: string): boolean {
|
||||
const entry = resolveChannelConfig(cfg, channelId);
|
||||
return hasMeaningfulChannelConfig(entry);
|
||||
|
|
@ -148,8 +137,12 @@ export function isChannelConfigured(
|
|||
channelId: string,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): boolean {
|
||||
if (channelId === "whatsapp") {
|
||||
return isWhatsAppConfigured(cfg);
|
||||
const pluginConfigured = getChannelPlugin(channelId)?.config.hasPersistedAuthState?.({
|
||||
cfg,
|
||||
env,
|
||||
});
|
||||
if (pluginConfigured) {
|
||||
return true;
|
||||
}
|
||||
const spec = STRUCTURED_CHANNEL_CONFIG_SPECS[channelId];
|
||||
if (spec) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import type { MsgContext } from "../../auto-reply/templating.js";
|
||||
import type { ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import {
|
||||
createChannelTestPluginBase,
|
||||
createTestRegistry,
|
||||
} from "../../test-utils/channel-plugins.js";
|
||||
import { normalizeExplicitSessionKey } from "./explicit-session-key-normalization.js";
|
||||
|
||||
function makeCtx(overrides: Partial<MsgContext>): MsgContext {
|
||||
|
|
@ -11,6 +17,54 @@ function makeCtx(overrides: Partial<MsgContext>): MsgContext {
|
|||
} as MsgContext;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const discordPlugin: ChannelPlugin = {
|
||||
...createChannelTestPluginBase({
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
docsPath: "/channels/discord",
|
||||
}),
|
||||
messaging: {
|
||||
normalizeExplicitSessionKey: ({ sessionKey, ctx }) => {
|
||||
const normalizedChatType = ctx.ChatType?.trim().toLowerCase();
|
||||
let normalized = sessionKey.trim().toLowerCase();
|
||||
if (normalizedChatType !== "direct" && normalizedChatType !== "dm") {
|
||||
return normalized;
|
||||
}
|
||||
normalized = normalized.replace(/^(discord:)dm:/, "$1direct:");
|
||||
normalized = normalized.replace(/^(agent:[^:]+:discord:)dm:/, "$1direct:");
|
||||
const match = normalized.match(/^((?:agent:[^:]+:)?)discord:channel:([^:]+)$/);
|
||||
if (!match) {
|
||||
return normalized;
|
||||
}
|
||||
const from = (ctx.From ?? "").trim().toLowerCase();
|
||||
const senderId = (ctx.SenderId ?? "").trim().toLowerCase();
|
||||
const fromDiscordId =
|
||||
from.startsWith("discord:") && !from.includes(":channel:") && !from.includes(":group:")
|
||||
? from.slice("discord:".length)
|
||||
: "";
|
||||
const directId = senderId || fromDiscordId;
|
||||
return directId && directId === match[2]
|
||||
? `${match[1]}discord:direct:${match[2]}`
|
||||
: normalized;
|
||||
},
|
||||
},
|
||||
};
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "discord",
|
||||
plugin: discordPlugin,
|
||||
source: "test",
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetPluginRuntimeStateForTest();
|
||||
});
|
||||
|
||||
describe("normalizeExplicitSessionKey", () => {
|
||||
it("dispatches discord keys through the provider normalizer", () => {
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -1,50 +1,44 @@
|
|||
import { normalizeExplicitDiscordSessionKey } from "../../../extensions/discord/session-key-api.js";
|
||||
import type { MsgContext } from "../../auto-reply/templating.js";
|
||||
import { getChannelPlugin, listChannelPlugins } from "../../channels/plugins/index.js";
|
||||
import { normalizeMessageChannel } from "../../utils/message-channel.js";
|
||||
|
||||
type ExplicitSessionKeyNormalizer = (sessionKey: string, ctx: MsgContext) => string;
|
||||
type ExplicitSessionKeyNormalizerEntry = {
|
||||
provider: string;
|
||||
normalize: ExplicitSessionKeyNormalizer;
|
||||
matches: (params: {
|
||||
sessionKey: string;
|
||||
provider?: string;
|
||||
surface?: string;
|
||||
from: string;
|
||||
}) => boolean;
|
||||
};
|
||||
|
||||
const EXPLICIT_SESSION_KEY_NORMALIZERS: ExplicitSessionKeyNormalizerEntry[] = [
|
||||
{
|
||||
provider: "discord",
|
||||
normalize: normalizeExplicitDiscordSessionKey,
|
||||
matches: ({ sessionKey, provider, surface, from }) =>
|
||||
surface === "discord" ||
|
||||
provider === "discord" ||
|
||||
from.startsWith("discord:") ||
|
||||
sessionKey.startsWith("discord:") ||
|
||||
sessionKey.includes(":discord:"),
|
||||
},
|
||||
];
|
||||
|
||||
function resolveExplicitSessionKeyNormalizer(
|
||||
function resolveExplicitSessionKeyNormalizerCandidates(
|
||||
sessionKey: string,
|
||||
ctx: Pick<MsgContext, "From" | "Provider" | "Surface">,
|
||||
): ExplicitSessionKeyNormalizer | undefined {
|
||||
): string[] {
|
||||
const normalizedProvider = ctx.Provider?.trim().toLowerCase();
|
||||
const normalizedSurface = ctx.Surface?.trim().toLowerCase();
|
||||
const normalizedFrom = (ctx.From ?? "").trim().toLowerCase();
|
||||
return EXPLICIT_SESSION_KEY_NORMALIZERS.find((entry) =>
|
||||
entry.matches({
|
||||
sessionKey,
|
||||
provider: normalizedProvider,
|
||||
surface: normalizedSurface,
|
||||
from: normalizedFrom,
|
||||
}),
|
||||
)?.normalize;
|
||||
const candidates = new Set<string>();
|
||||
const maybeAdd = (value?: string | null) => {
|
||||
const normalized = normalizeMessageChannel(value);
|
||||
if (normalized) {
|
||||
candidates.add(normalized);
|
||||
}
|
||||
};
|
||||
maybeAdd(normalizedSurface);
|
||||
maybeAdd(normalizedProvider);
|
||||
maybeAdd(normalizedFrom.split(":", 1)[0]);
|
||||
for (const plugin of listChannelPlugins()) {
|
||||
const pluginId = normalizeMessageChannel(plugin.id);
|
||||
if (!pluginId) {
|
||||
continue;
|
||||
}
|
||||
if (sessionKey.startsWith(`${pluginId}:`) || sessionKey.includes(`:${pluginId}:`)) {
|
||||
candidates.add(pluginId);
|
||||
}
|
||||
}
|
||||
return [...candidates];
|
||||
}
|
||||
|
||||
export function normalizeExplicitSessionKey(sessionKey: string, ctx: MsgContext): string {
|
||||
const normalized = sessionKey.trim().toLowerCase();
|
||||
const normalize = resolveExplicitSessionKeyNormalizer(normalized, ctx);
|
||||
return normalize ? normalize(normalized, ctx) : normalized;
|
||||
for (const channelId of resolveExplicitSessionKeyNormalizerCandidates(normalized, ctx)) {
|
||||
const normalize = getChannelPlugin(channelId)?.messaging?.normalizeExplicitSessionKey;
|
||||
const next = normalize?.({ sessionKey: normalized, ctx });
|
||||
if (typeof next === "string" && next.trim()) {
|
||||
return next.trim().toLowerCase();
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import type { MsgContext } from "../../auto-reply/templating.js";
|
||||
import type { ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import {
|
||||
createChannelTestPluginBase,
|
||||
createTestRegistry,
|
||||
} from "../../test-utils/channel-plugins.js";
|
||||
import { resolveSessionKey } from "./session-key.js";
|
||||
|
||||
function makeCtx(overrides: Partial<MsgContext>): MsgContext {
|
||||
|
|
@ -11,6 +17,54 @@ function makeCtx(overrides: Partial<MsgContext>): MsgContext {
|
|||
} as MsgContext;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
const discordPlugin: ChannelPlugin = {
|
||||
...createChannelTestPluginBase({
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
docsPath: "/channels/discord",
|
||||
}),
|
||||
messaging: {
|
||||
normalizeExplicitSessionKey: ({ sessionKey, ctx }) => {
|
||||
const normalizedChatType = ctx.ChatType?.trim().toLowerCase();
|
||||
let normalized = sessionKey.trim().toLowerCase();
|
||||
if (normalizedChatType !== "direct" && normalizedChatType !== "dm") {
|
||||
return normalized;
|
||||
}
|
||||
normalized = normalized.replace(/^(discord:)dm:/, "$1direct:");
|
||||
normalized = normalized.replace(/^(agent:[^:]+:discord:)dm:/, "$1direct:");
|
||||
const match = normalized.match(/^((?:agent:[^:]+:)?)discord:channel:([^:]+)$/);
|
||||
if (!match) {
|
||||
return normalized;
|
||||
}
|
||||
const from = (ctx.From ?? "").trim().toLowerCase();
|
||||
const senderId = (ctx.SenderId ?? "").trim().toLowerCase();
|
||||
const fromDiscordId =
|
||||
from.startsWith("discord:") && !from.includes(":channel:") && !from.includes(":group:")
|
||||
? from.slice("discord:".length)
|
||||
: "";
|
||||
const directId = senderId || fromDiscordId;
|
||||
return directId && directId === match[2]
|
||||
? `${match[1]}discord:direct:${match[2]}`
|
||||
: normalized;
|
||||
},
|
||||
},
|
||||
};
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([
|
||||
{
|
||||
pluginId: "discord",
|
||||
plugin: discordPlugin,
|
||||
source: "test",
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetPluginRuntimeStateForTest();
|
||||
});
|
||||
|
||||
describe("resolveSessionKey", () => {
|
||||
describe("Discord DM session key normalization", () => {
|
||||
it("passes through correct discord:direct keys unchanged", () => {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
|
||||
const whatsappAccountMocks = vi.hoisted(() => ({
|
||||
resolveWhatsAppAccount: vi.fn<() => { allowFrom: string[] }>(() => ({ allowFrom: [] })),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/sessions.js", () => ({
|
||||
loadSessionStore: vi.fn().mockReturnValue({}),
|
||||
resolveAgentMainSessionKey: vi.fn().mockReturnValue("agent:test:main"),
|
||||
|
|
@ -28,16 +24,11 @@ vi.mock("../../pairing/pairing-store.js", () => ({
|
|||
readChannelAllowFromStoreSync: vi.fn(() => []),
|
||||
}));
|
||||
|
||||
vi.mock("../../../extensions/whatsapp/api.js", () => ({
|
||||
resolveWhatsAppAccount: whatsappAccountMocks.resolveWhatsAppAccount,
|
||||
}));
|
||||
|
||||
const mockedModuleIds = [
|
||||
"../../config/sessions.js",
|
||||
"../../infra/outbound/channel-selection.js",
|
||||
"../../infra/outbound/target-resolver.js",
|
||||
"../../pairing/pairing-store.js",
|
||||
"../../../extensions/whatsapp/api.js",
|
||||
];
|
||||
|
||||
import { loadSessionStore } from "../../config/sessions.js";
|
||||
|
|
@ -65,6 +56,22 @@ function createStubOutbound(label: string): ChannelOutboundAdapter {
|
|||
};
|
||||
}
|
||||
|
||||
function createAllowlistAwareStubOutbound(label: string): ChannelOutboundAdapter {
|
||||
return {
|
||||
deliveryMode: "gateway",
|
||||
resolveTarget: ({ to, allowFrom }) => {
|
||||
const trimmed = typeof to === "string" ? to.trim() : "";
|
||||
if (!trimmed) {
|
||||
return { ok: false, error: new Error(`${label} requires target`) };
|
||||
}
|
||||
if (allowFrom && allowFrom.length > 0 && !allowFrom.includes(trimmed)) {
|
||||
return { ok: false, error: new Error(`${label} target blocked`) };
|
||||
}
|
||||
return { ok: true, to: trimmed };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetPluginRuntimeStateForTest();
|
||||
setActivePluginRegistry(
|
||||
|
|
@ -79,10 +86,18 @@ beforeEach(() => {
|
|||
},
|
||||
{
|
||||
pluginId: "whatsapp",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "whatsapp",
|
||||
outbound: createStubOutbound("WhatsApp"),
|
||||
}),
|
||||
plugin: {
|
||||
...createOutboundTestPlugin({
|
||||
id: "whatsapp",
|
||||
outbound: createAllowlistAwareStubOutbound("WhatsApp"),
|
||||
}),
|
||||
config: {
|
||||
listAccountIds: () => [],
|
||||
resolveAccount: () => ({}),
|
||||
resolveAllowFrom: ({ cfg }: { cfg: OpenClawConfig }) =>
|
||||
(cfg.channels?.whatsapp as { allowFrom?: string[] } | undefined)?.allowFrom,
|
||||
},
|
||||
},
|
||||
source: "test",
|
||||
},
|
||||
]),
|
||||
|
|
@ -142,10 +157,6 @@ function setLastSessionEntry(params: {
|
|||
});
|
||||
}
|
||||
|
||||
function setWhatsAppAllowFrom(allowFrom: string[]) {
|
||||
vi.mocked(whatsappAccountMocks.resolveWhatsAppAccount).mockReturnValue({ allowFrom });
|
||||
}
|
||||
|
||||
function setStoredWhatsAppAllowFrom(allowFrom: string[]) {
|
||||
vi.mocked(readChannelAllowFromStoreSync).mockReturnValue(allowFrom);
|
||||
}
|
||||
|
|
@ -176,10 +187,9 @@ describe("resolveDeliveryTarget", () => {
|
|||
lastChannel: "whatsapp",
|
||||
lastTo: "+15550000099",
|
||||
});
|
||||
setWhatsAppAllowFrom([]);
|
||||
setStoredWhatsAppAllowFrom(["+15550000001"]);
|
||||
|
||||
const cfg = makeCfg({ bindings: [] });
|
||||
const cfg = makeCfg({ bindings: [], channels: { whatsapp: { allowFrom: [] } } });
|
||||
const result = await resolveLastTarget(cfg);
|
||||
|
||||
expect(result.channel).toBe("whatsapp");
|
||||
|
|
@ -192,10 +202,9 @@ describe("resolveDeliveryTarget", () => {
|
|||
lastChannel: "whatsapp",
|
||||
lastTo: "+15550000099",
|
||||
});
|
||||
setWhatsAppAllowFrom([]);
|
||||
setStoredWhatsAppAllowFrom(["+15550000001"]);
|
||||
|
||||
const cfg = makeCfg({ bindings: [] });
|
||||
const cfg = makeCfg({ bindings: [], channels: { whatsapp: { allowFrom: [] } } });
|
||||
const result = await resolveDeliveryTarget(cfg, AGENT_ID, {
|
||||
channel: "whatsapp",
|
||||
to: "+15550000099",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { resolveWhatsAppAccount } from "../../../extensions/whatsapp/api.js";
|
||||
import { normalizeWhatsAppTarget } from "../../channels/plugins/normalize/whatsapp.js";
|
||||
import { getChannelPlugin } from "../../channels/plugins/index.js";
|
||||
import type { ChannelId } from "../../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import {
|
||||
|
|
@ -15,6 +14,7 @@ import {
|
|||
resolveSessionDeliveryTarget,
|
||||
} from "../../infra/outbound/targets.js";
|
||||
import { readChannelAllowFromStoreSync } from "../../pairing/pairing-store.js";
|
||||
import { mapAllowFromEntries } from "../../plugin-sdk/channel-config-helpers.js";
|
||||
import { buildChannelAccountBindings } from "../../routing/bindings.js";
|
||||
import { normalizeAccountId, normalizeAgentId } from "../../routing/session-key.js";
|
||||
|
||||
|
|
@ -152,26 +152,32 @@ export async function resolveDeliveryTarget(
|
|||
};
|
||||
}
|
||||
|
||||
let allowFromOverride: string[] | undefined;
|
||||
if (channel === "whatsapp") {
|
||||
const resolvedAccountId = normalizeAccountId(accountId);
|
||||
const configuredAllowFromRaw =
|
||||
resolveWhatsAppAccount({ cfg, accountId: resolvedAccountId }).allowFrom ?? [];
|
||||
const configuredAllowFrom = configuredAllowFromRaw
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter((entry) => entry && entry !== "*")
|
||||
.map((entry) => normalizeWhatsAppTarget(entry))
|
||||
.filter((entry): entry is string => Boolean(entry));
|
||||
const storeAllowFrom = readChannelAllowFromStoreSync("whatsapp", process.env, resolvedAccountId)
|
||||
.map((entry) => normalizeWhatsAppTarget(entry))
|
||||
.filter((entry): entry is string => Boolean(entry));
|
||||
allowFromOverride = [...new Set([...configuredAllowFrom, ...storeAllowFrom])];
|
||||
const channelPlugin = getChannelPlugin(channel);
|
||||
const resolvedAccountId = normalizeAccountId(accountId);
|
||||
const configuredAllowFromRaw = channelPlugin?.config.resolveAllowFrom?.({
|
||||
cfg,
|
||||
accountId: resolvedAccountId,
|
||||
});
|
||||
const configuredAllowFrom = configuredAllowFromRaw
|
||||
? mapAllowFromEntries(configuredAllowFromRaw)
|
||||
: [];
|
||||
const storeAllowFrom = mapAllowFromEntries(
|
||||
readChannelAllowFromStoreSync(channel, process.env, resolvedAccountId),
|
||||
);
|
||||
const allowFromOverride = [...new Set([...configuredAllowFrom, ...storeAllowFrom])];
|
||||
const effectiveAllowFrom = mode === "implicit" ? allowFromOverride : undefined;
|
||||
|
||||
if (toCandidate && mode === "implicit" && allowFromOverride.length > 0) {
|
||||
const normalizedCurrentTarget = normalizeWhatsAppTarget(toCandidate);
|
||||
if (!normalizedCurrentTarget || !allowFromOverride.includes(normalizedCurrentTarget)) {
|
||||
toCandidate = allowFromOverride[0];
|
||||
}
|
||||
if (toCandidate && mode === "implicit" && allowFromOverride.length > 0) {
|
||||
const currentTargetResolution = resolveOutboundTarget({
|
||||
channel,
|
||||
to: toCandidate,
|
||||
cfg,
|
||||
accountId,
|
||||
mode,
|
||||
allowFrom: effectiveAllowFrom,
|
||||
});
|
||||
if (!currentTargetResolution.ok) {
|
||||
toCandidate = allowFromOverride[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +187,7 @@ export async function resolveDeliveryTarget(
|
|||
cfg,
|
||||
accountId,
|
||||
mode,
|
||||
allowFrom: allowFromOverride,
|
||||
allowFrom: effectiveAllowFrom,
|
||||
});
|
||||
if (!docked.ok) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
import { createServer as createHttpsServer } from "node:https";
|
||||
import type { TlsOptions } from "node:tls";
|
||||
import type { WebSocketServer } from "ws";
|
||||
import { handleSlackHttpRequest } from "../../extensions/slack/api.js";
|
||||
import { resolveAgentAvatar } from "../agents/identity-avatar.js";
|
||||
import { CANVAS_WS_PATH, handleA2uiHttpRequest } from "../canvas-host/a2ui.js";
|
||||
import type { CanvasHostHandler } from "../canvas-host/server.js";
|
||||
|
|
@ -325,9 +324,8 @@ export async function runGatewayHttpRequestStages(
|
|||
} catch (err) {
|
||||
// Log and skip the failing stage so subsequent stages (control-ui,
|
||||
// gateway-probes, etc.) remain reachable. A common trigger is a
|
||||
// bundled-plugin facade that fails to load because an optional
|
||||
// dependency is missing (e.g. @slack/bolt after the lazy-facade
|
||||
// refactor).
|
||||
// plugin-owned route/runtime code can still fail to load when an
|
||||
// optional dependency is missing. Keep later stages reachable.
|
||||
console.error(`[gateway-http] stage "${stage.name}" threw — skipping:`, err);
|
||||
}
|
||||
}
|
||||
|
|
@ -898,10 +896,6 @@ export function createGatewayHttpServer(opts: {
|
|||
rateLimiter,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "slack",
|
||||
run: () => handleSlackHttpRequest(req, res),
|
||||
},
|
||||
];
|
||||
if (openResponsesEnabled) {
|
||||
requestStages.push({
|
||||
|
|
|
|||
|
|
@ -142,21 +142,16 @@ vi.mock("../plugins/hook-runner-global.js", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("../plugins/runtime/runtime-discord.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../plugins/runtime/runtime-discord.js")>();
|
||||
vi.mock("../infra/outbound/session-binding-service.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../infra/outbound/session-binding-service.js")>();
|
||||
return {
|
||||
...actual,
|
||||
createRuntimeDiscord: () => {
|
||||
const runtime = actual.createRuntimeDiscord();
|
||||
return {
|
||||
...runtime,
|
||||
threadBindings: {
|
||||
...runtime.threadBindings,
|
||||
unbindBySessionKey: (params: unknown) =>
|
||||
threadBindingMocks.unbindThreadBindingsBySessionKey(params),
|
||||
},
|
||||
};
|
||||
},
|
||||
getSessionBindingService: () => ({
|
||||
...actual.getSessionBindingService(),
|
||||
unbind: async (params: unknown) =>
|
||||
threadBindingMocks.unbindThreadBindingsBySessionKey(params),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -1862,9 +1857,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:discord:group:dev",
|
||||
targetKind: "acp",
|
||||
reason: "session-delete",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2022,9 +2015,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:subagent:worker",
|
||||
targetKind: "subagent",
|
||||
reason: "session-delete",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2053,9 +2044,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:subagent:worker",
|
||||
targetKind: "subagent",
|
||||
reason: "session-delete",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2083,9 +2072,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:subagent:worker",
|
||||
targetKind: "subagent",
|
||||
reason: "session-delete",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2140,9 +2127,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:main",
|
||||
targetKind: "acp",
|
||||
reason: "session-reset",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2308,9 +2293,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:subagent:worker",
|
||||
targetKind: "subagent",
|
||||
reason: "session-reset",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
@ -2338,9 +2321,7 @@ describe("gateway server sessions", () => {
|
|||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledTimes(1);
|
||||
expect(threadBindingMocks.unbindThreadBindingsBySessionKey).toHaveBeenCalledWith({
|
||||
targetSessionKey: "agent:main:main",
|
||||
targetKind: "acp",
|
||||
reason: "session-reset",
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
ws.close();
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import {
|
|||
import { resolveSessionFilePath, resolveSessionFilePathOptions } from "../config/sessions/paths.js";
|
||||
import { logVerbose } from "../globals.js";
|
||||
import { createInternalHookEvent, triggerInternalHook } from "../hooks/internal-hooks.js";
|
||||
import { getSessionBindingService } from "../infra/outbound/session-binding-service.js";
|
||||
import { closeTrackedBrowserTabsForSessions } from "../plugin-sdk/browser-maintenance.js";
|
||||
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
||||
import { createPluginRuntime } from "../plugins/runtime/index.js";
|
||||
import {
|
||||
isSubagentSessionKey,
|
||||
normalizeAgentId,
|
||||
|
|
@ -44,12 +44,6 @@ import {
|
|||
} from "./session-utils.js";
|
||||
|
||||
const ACP_RUNTIME_CLEANUP_TIMEOUT_MS = 15_000;
|
||||
let cachedChannelRuntime: ReturnType<typeof createPluginRuntime>["channel"] | undefined;
|
||||
|
||||
function getChannelRuntime() {
|
||||
cachedChannelRuntime ??= createPluginRuntime().channel;
|
||||
return cachedChannelRuntime;
|
||||
}
|
||||
|
||||
function stripRuntimeModelState(entry?: SessionEntry): SessionEntry | undefined {
|
||||
if (!entry) {
|
||||
|
|
@ -164,12 +158,9 @@ export async function emitSessionUnboundLifecycleEvent(params: {
|
|||
emitHooks?: boolean;
|
||||
}) {
|
||||
const targetKind = isSubagentSessionKey(params.targetSessionKey) ? "subagent" : "acp";
|
||||
const channelRuntime = getChannelRuntime();
|
||||
channelRuntime.discord.threadBindings.unbindBySessionKey({
|
||||
await getSessionBindingService().unbind({
|
||||
targetSessionKey: params.targetSessionKey,
|
||||
targetKind,
|
||||
reason: params.reason,
|
||||
sendFarewell: true,
|
||||
});
|
||||
|
||||
if (params.emitHooks === false) {
|
||||
|
|
|
|||
|
|
@ -821,12 +821,12 @@ vi.mock("../plugins/loader.js", async () => {
|
|||
loadOpenClawPlugins: () => pluginRegistryState.registry,
|
||||
};
|
||||
});
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
sendMessageWhatsApp: (...args: unknown[]) =>
|
||||
vi.mock("../plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
sendWebChannelMessage: (...args: unknown[]) =>
|
||||
(hoisted.sendWhatsAppMock as (...args: unknown[]) => unknown)(...args),
|
||||
}));
|
||||
vi.mock("/src/plugins/runtime/runtime-web-channel-boundary.js", () => ({
|
||||
sendMessageWhatsApp: (...args: unknown[]) =>
|
||||
vi.mock("/src/plugins/runtime/runtime-web-channel-plugin.js", () => ({
|
||||
sendWebChannelMessage: (...args: unknown[]) =>
|
||||
(hoisted.sendWhatsAppMock as (...args: unknown[]) => unknown)(...args),
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -18,75 +18,6 @@ export interface PluginSdkFacadeTypeMap {
|
|||
};
|
||||
types: {};
|
||||
};
|
||||
"discord-account": {
|
||||
module: typeof import("@openclaw/discord/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ResolvedDiscordAccount: import("@openclaw/discord/api.js").ResolvedDiscordAccount;
|
||||
};
|
||||
};
|
||||
"discord-runtime-surface": {
|
||||
module: typeof import("@openclaw/discord/runtime-api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/runtime-api.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"discord-session-key": {
|
||||
module: typeof import("@openclaw/discord/session-key-api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/session-key-api.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"discord-surface": {
|
||||
module: typeof import("@openclaw/discord/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
DiscordComponentMessageSpec: import("@openclaw/discord/api.js").DiscordComponentMessageSpec;
|
||||
DiscordProbe: import("@openclaw/discord/api.js").DiscordProbe;
|
||||
DiscordSendComponents: import("@openclaw/discord/api.js").DiscordSendComponents;
|
||||
DiscordSendEmbeds: import("@openclaw/discord/api.js").DiscordSendEmbeds;
|
||||
DiscordSendResult: import("@openclaw/discord/api.js").DiscordSendResult;
|
||||
DiscordTokenResolution: import("@openclaw/discord/api.js").DiscordTokenResolution;
|
||||
InspectedDiscordAccount: import("@openclaw/discord/api.js").InspectedDiscordAccount;
|
||||
ResolvedDiscordAccount: import("@openclaw/discord/api.js").ResolvedDiscordAccount;
|
||||
};
|
||||
};
|
||||
"discord-thread-bindings": {
|
||||
module: typeof import("@openclaw/discord/runtime-api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/runtime-api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ThreadBindingManager: import("@openclaw/discord/runtime-api.js").ThreadBindingManager;
|
||||
ThreadBindingRecord: import("@openclaw/discord/runtime-api.js").ThreadBindingRecord;
|
||||
ThreadBindingTargetKind: import("@openclaw/discord/runtime-api.js").ThreadBindingTargetKind;
|
||||
};
|
||||
};
|
||||
"discord-timeouts": {
|
||||
module: typeof import("@openclaw/discord/timeouts.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/discord/timeouts.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"anthropic-cli": {
|
||||
module: typeof import("@openclaw/anthropic/api.js");
|
||||
sourceModules: {
|
||||
|
|
@ -510,30 +441,6 @@ export interface PluginSdkFacadeTypeMap {
|
|||
};
|
||||
types: {};
|
||||
};
|
||||
"signal-account": {
|
||||
module: typeof import("@openclaw/signal/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/signal/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ResolvedSignalAccount: import("@openclaw/signal/api.js").ResolvedSignalAccount;
|
||||
};
|
||||
};
|
||||
"signal-surface": {
|
||||
module: typeof import("@openclaw/signal/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/signal/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ResolvedSignalAccount: import("@openclaw/signal/api.js").ResolvedSignalAccount;
|
||||
SignalProbe: import("@openclaw/signal/api.js").SignalProbe;
|
||||
SignalSender: import("@openclaw/signal/api.js").SignalSender;
|
||||
};
|
||||
};
|
||||
"provider-reasoning": {
|
||||
module: typeof import("@openclaw/ollama/api.js");
|
||||
sourceModules: {
|
||||
|
|
@ -578,50 +485,6 @@ export interface PluginSdkFacadeTypeMap {
|
|||
};
|
||||
types: {};
|
||||
};
|
||||
"slack-target-parser": {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"slack-account": {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ResolvedSlackAccount: import("@openclaw/slack/api.js").ResolvedSlackAccount;
|
||||
};
|
||||
};
|
||||
"slack-runtime-surface": {
|
||||
module: typeof import("@openclaw/slack/runtime-api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/slack/runtime-api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
SlackActionContext: import("@openclaw/slack/runtime-api.js").SlackActionContext;
|
||||
};
|
||||
};
|
||||
"slack-surface": {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/slack/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
InspectedSlackAccount: import("@openclaw/slack/api.js").InspectedSlackAccount;
|
||||
ResolvedSlackAccount: import("@openclaw/slack/api.js").ResolvedSlackAccount;
|
||||
SlackProbe: import("@openclaw/slack/api.js").SlackProbe;
|
||||
};
|
||||
};
|
||||
together: {
|
||||
module: typeof import("@openclaw/together/api.js");
|
||||
sourceModules: {
|
||||
|
|
@ -640,44 +503,6 @@ export interface PluginSdkFacadeTypeMap {
|
|||
};
|
||||
types: {};
|
||||
};
|
||||
"telegram-account": {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
ResolvedTelegramAccount: import("@openclaw/telegram/api.js").ResolvedTelegramAccount;
|
||||
};
|
||||
};
|
||||
"telegram-allow-from": {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"telegram-surface": {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/telegram/api.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
InspectedTelegramAccount: import("@openclaw/telegram/api.js").InspectedTelegramAccount;
|
||||
ProviderInfo: import("@openclaw/telegram/api.js").ProviderInfo;
|
||||
ResolvedTelegramAccount: import("@openclaw/telegram/api.js").ResolvedTelegramAccount;
|
||||
StickerMetadata: import("@openclaw/telegram/api.js").StickerMetadata;
|
||||
TelegramButtonStyle: import("@openclaw/telegram/api.js").TelegramButtonStyle;
|
||||
TelegramInlineButtons: import("@openclaw/telegram/api.js").TelegramInlineButtons;
|
||||
TelegramProbe: import("@openclaw/telegram/api.js").TelegramProbe;
|
||||
TelegramTokenResolution: import("@openclaw/telegram/api.js").TelegramTokenResolution;
|
||||
};
|
||||
};
|
||||
"vercel-ai-gateway": {
|
||||
module: typeof import("@openclaw/vercel-ai-gateway/api.js");
|
||||
sourceModules: {
|
||||
|
|
@ -732,32 +557,6 @@ export interface PluginSdkFacadeTypeMap {
|
|||
};
|
||||
types: {};
|
||||
};
|
||||
"whatsapp-targets": {
|
||||
module: typeof import("@openclaw/whatsapp/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/whatsapp/api.js");
|
||||
};
|
||||
};
|
||||
types: {};
|
||||
};
|
||||
"whatsapp-surface": {
|
||||
module: typeof import("@openclaw/whatsapp/api.js");
|
||||
sourceModules: {
|
||||
source1: {
|
||||
module: typeof import("@openclaw/whatsapp/api.js");
|
||||
};
|
||||
source2: {
|
||||
module: typeof import("@openclaw/whatsapp/constants.js");
|
||||
};
|
||||
};
|
||||
types: {
|
||||
WebChannelStatus: import("@openclaw/whatsapp/api.js").WebChannelStatus;
|
||||
WebInboundMessage: import("@openclaw/whatsapp/api.js").WebInboundMessage;
|
||||
WebListenerCloseReason: import("@openclaw/whatsapp/api.js").WebListenerCloseReason;
|
||||
WebMonitorTuning: import("@openclaw/whatsapp/api.js").WebMonitorTuning;
|
||||
};
|
||||
};
|
||||
"zalo-setup": {
|
||||
module: typeof import("@openclaw/zalo/api.js");
|
||||
sourceModules: {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ describe("tsdown config", () => {
|
|||
"index",
|
||||
"commands/status.summary.runtime",
|
||||
"plugins/provider-runtime.runtime",
|
||||
"plugins/runtime/runtime-line.contract",
|
||||
"plugins/runtime/index",
|
||||
"plugin-sdk/compat",
|
||||
"plugin-sdk/index",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const lazyRuntimeSpecifiers = [
|
|||
"./cli/prompt.js",
|
||||
"./infra/binaries.js",
|
||||
"./process/exec.js",
|
||||
"./plugins/runtime/runtime-web-channel-boundary.js",
|
||||
"./plugins/runtime/runtime-web-channel-plugin.js",
|
||||
] as const;
|
||||
|
||||
function readLibraryModuleImports() {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
handlePortError,
|
||||
PortInUseError,
|
||||
} from "./infra/ports.js";
|
||||
import type { monitorWebChannel as monitorWebChannelRuntime } from "./plugins/runtime/runtime-web-channel-boundary.js";
|
||||
import type { monitorWebChannel as monitorWebChannelRuntime } from "./plugins/runtime/runtime-web-channel-plugin.js";
|
||||
import type {
|
||||
runCommandWithTimeout as runCommandWithTimeoutRuntime,
|
||||
runExec as runExecRuntime,
|
||||
|
|
@ -32,8 +32,8 @@ let replyRuntimePromise: Promise<typeof import("./auto-reply/reply.runtime.js")>
|
|||
let promptRuntimePromise: Promise<typeof import("./cli/prompt.js")> | null = null;
|
||||
let binariesRuntimePromise: Promise<typeof import("./infra/binaries.js")> | null = null;
|
||||
let execRuntimePromise: Promise<typeof import("./process/exec.js")> | null = null;
|
||||
let whatsappRuntimePromise: Promise<
|
||||
typeof import("./plugins/runtime/runtime-web-channel-boundary.js")
|
||||
let webChannelRuntimePromise: Promise<
|
||||
typeof import("./plugins/runtime/runtime-web-channel-plugin.js")
|
||||
> | null = null;
|
||||
|
||||
function loadReplyRuntime() {
|
||||
|
|
@ -56,9 +56,9 @@ function loadExecRuntime() {
|
|||
return execRuntimePromise;
|
||||
}
|
||||
|
||||
function loadWhatsAppRuntime() {
|
||||
whatsappRuntimePromise ??= import("./plugins/runtime/runtime-web-channel-boundary.js");
|
||||
return whatsappRuntimePromise;
|
||||
function loadWebChannelRuntime() {
|
||||
webChannelRuntimePromise ??= import("./plugins/runtime/runtime-web-channel-plugin.js");
|
||||
return webChannelRuntimePromise;
|
||||
}
|
||||
|
||||
export const getReplyFromConfig: GetReplyFromConfig = async (...args) =>
|
||||
|
|
@ -71,7 +71,7 @@ export const runExec: RunExec = async (...args) => (await loadExecRuntime()).run
|
|||
export const runCommandWithTimeout: RunCommandWithTimeout = async (...args) =>
|
||||
(await loadExecRuntime()).runCommandWithTimeout(...args);
|
||||
export const monitorWebChannel: MonitorWebChannel = async (...args) =>
|
||||
(await loadWhatsAppRuntime()).monitorWebChannel(...args);
|
||||
(await loadWebChannelRuntime()).monitorWebChannel(...args);
|
||||
|
||||
export {
|
||||
assertWebChannel,
|
||||
|
|
|
|||
|
|
@ -45,12 +45,6 @@ describe("plugin activation boundary", () => {
|
|||
}>
|
||||
| undefined;
|
||||
let browserAmbientImportsPromise: Promise<void> | undefined;
|
||||
let discordMaintenancePromise:
|
||||
| Promise<{
|
||||
unbindThreadBindingsBySessionKey: typeof import("./plugin-sdk/discord-maintenance.js").unbindThreadBindingsBySessionKey;
|
||||
}>
|
||||
| undefined;
|
||||
|
||||
function importAmbientModules() {
|
||||
ambientImportsPromise ??= Promise.all([
|
||||
import("./agents/cli-session.js"),
|
||||
|
|
@ -110,13 +104,6 @@ describe("plugin activation boundary", () => {
|
|||
return browserAmbientImportsPromise;
|
||||
}
|
||||
|
||||
function importDiscordMaintenance() {
|
||||
discordMaintenancePromise ??= import("./plugin-sdk/discord-maintenance.js").then((module) => ({
|
||||
unbindThreadBindingsBySessionKey: module.unbindThreadBindingsBySessionKey,
|
||||
}));
|
||||
return discordMaintenancePromise;
|
||||
}
|
||||
|
||||
it("does not load bundled provider plugins on ambient command imports", async () => {
|
||||
await importAmbientModules();
|
||||
|
||||
|
|
@ -187,17 +174,16 @@ describe("plugin activation boundary", () => {
|
|||
expect(loadBundledPluginPublicSurfaceModuleSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps discord cleanup helpers cold when discord is disabled", async () => {
|
||||
const discord = await importDiscordMaintenance();
|
||||
it("keeps generic session-binding cleanup helpers cold when plugins are disabled", async () => {
|
||||
const { getSessionBindingService } =
|
||||
await import("./infra/outbound/session-binding-service.js");
|
||||
|
||||
expect(
|
||||
discord.unbindThreadBindingsBySessionKey({
|
||||
await expect(
|
||||
getSessionBindingService().unbind({
|
||||
targetSessionKey: "agent:main:test",
|
||||
targetKind: "acp",
|
||||
reason: "session-reset",
|
||||
sendFarewell: true,
|
||||
}),
|
||||
).toEqual([]);
|
||||
).resolves.toEqual([]);
|
||||
expect(loadBundledPluginPublicSurfaceModuleSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -169,11 +169,13 @@ export function createApproverRestrictedNativeApprovalAdapter(
|
|||
export function createChannelApprovalCapability(params: {
|
||||
authorizeActorAction?: ChannelApprovalCapability["authorizeActorAction"];
|
||||
getActionAvailabilityState?: ChannelApprovalCapability["getActionAvailabilityState"];
|
||||
resolveApproveCommandBehavior?: ChannelApprovalCapability["resolveApproveCommandBehavior"];
|
||||
approvals?: Pick<ChannelApprovalCapability, "delivery" | "render" | "native">;
|
||||
}): ChannelApprovalCapability {
|
||||
return {
|
||||
authorizeActorAction: params.authorizeActorAction,
|
||||
getActionAvailabilityState: params.getActionAvailabilityState,
|
||||
resolveApproveCommandBehavior: params.resolveApproveCommandBehavior,
|
||||
delivery: params.approvals?.delivery,
|
||||
render: params.approvals?.render,
|
||||
native: params.approvals?.native,
|
||||
|
|
@ -184,6 +186,7 @@ export function splitChannelApprovalCapability(capability: ChannelApprovalCapabi
|
|||
auth: {
|
||||
authorizeActorAction?: ChannelApprovalCapability["authorizeActorAction"];
|
||||
getActionAvailabilityState?: ChannelApprovalCapability["getActionAvailabilityState"];
|
||||
resolveApproveCommandBehavior?: ChannelApprovalCapability["resolveApproveCommandBehavior"];
|
||||
};
|
||||
delivery: ChannelApprovalCapability["delivery"];
|
||||
render: ChannelApprovalCapability["render"];
|
||||
|
|
@ -193,6 +196,7 @@ export function splitChannelApprovalCapability(capability: ChannelApprovalCapabi
|
|||
auth: {
|
||||
authorizeActorAction: capability.authorizeActorAction,
|
||||
getActionAvailabilityState: capability.getActionAvailabilityState,
|
||||
resolveApproveCommandBehavior: capability.resolveApproveCommandBehavior,
|
||||
},
|
||||
delivery: capability.delivery,
|
||||
render: capability.render,
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import { tryLoadActivatedBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js";
|
||||
|
||||
type DiscordThreadBindingsModule = typeof import("../../extensions/discord/runtime-api.js");
|
||||
|
||||
export const unbindThreadBindingsBySessionKey: DiscordThreadBindingsModule["unbindThreadBindingsBySessionKey"] =
|
||||
((...args) => {
|
||||
// Session cleanup always attempts Discord thread unbinds, even when Discord is disabled.
|
||||
// Keep that path a no-op unless the Discord runtime is actually active.
|
||||
const unbindThreadBindings = tryLoadActivatedBundledPluginPublicSurfaceModuleSync<
|
||||
Pick<DiscordThreadBindingsModule, "unbindThreadBindingsBySessionKey">
|
||||
>({
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
})?.unbindThreadBindingsBySessionKey;
|
||||
return typeof unbindThreadBindings === "function" ? unbindThreadBindings(...args) : [];
|
||||
}) as DiscordThreadBindingsModule["unbindThreadBindingsBySessionKey"];
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
import {
|
||||
createLazyFacadeObjectValue,
|
||||
loadBundledPluginPublicSurfaceModuleSync,
|
||||
} from "./facade-runtime.js";
|
||||
|
||||
type DiscordRuntimeModule = typeof import("../../extensions/discord/runtime-api.js");
|
||||
|
||||
type DiscordRuntimeSurface = Pick<
|
||||
DiscordRuntimeModule,
|
||||
| "auditDiscordChannelPermissions"
|
||||
| "createThreadDiscord"
|
||||
| "deleteMessageDiscord"
|
||||
| "discordMessageActions"
|
||||
| "editChannelDiscord"
|
||||
| "editMessageDiscord"
|
||||
| "getThreadBindingManager"
|
||||
| "listDiscordDirectoryGroupsLive"
|
||||
| "listDiscordDirectoryPeersLive"
|
||||
| "monitorDiscordProvider"
|
||||
| "pinMessageDiscord"
|
||||
| "probeDiscord"
|
||||
| "resolveDiscordChannelAllowlist"
|
||||
| "resolveDiscordUserAllowlist"
|
||||
| "resolveThreadBindingIdleTimeoutMs"
|
||||
| "resolveThreadBindingInactivityExpiresAt"
|
||||
| "resolveThreadBindingMaxAgeExpiresAt"
|
||||
| "resolveThreadBindingMaxAgeMs"
|
||||
| "sendDiscordComponentMessage"
|
||||
| "sendMessageDiscord"
|
||||
| "sendPollDiscord"
|
||||
| "sendTypingDiscord"
|
||||
| "setThreadBindingIdleTimeoutBySessionKey"
|
||||
| "setThreadBindingMaxAgeBySessionKey"
|
||||
| "unbindThreadBindingsBySessionKey"
|
||||
| "unpinMessageDiscord"
|
||||
>;
|
||||
|
||||
function loadDiscordRuntimeSurface(): DiscordRuntimeSurface {
|
||||
return loadBundledPluginPublicSurfaceModuleSync<DiscordRuntimeSurface>({
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
});
|
||||
}
|
||||
|
||||
export const discordMessageActions: DiscordRuntimeModule["discordMessageActions"] =
|
||||
createLazyFacadeObjectValue(() => loadDiscordRuntimeSurface().discordMessageActions);
|
||||
|
||||
export const auditDiscordChannelPermissions: DiscordRuntimeModule["auditDiscordChannelPermissions"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().auditDiscordChannelPermissions(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["auditDiscordChannelPermissions"];
|
||||
|
||||
export const createThreadDiscord: DiscordRuntimeModule["createThreadDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().createThreadDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["createThreadDiscord"];
|
||||
|
||||
export const deleteMessageDiscord: DiscordRuntimeModule["deleteMessageDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().deleteMessageDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["deleteMessageDiscord"];
|
||||
|
||||
export const editChannelDiscord: DiscordRuntimeModule["editChannelDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().editChannelDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["editChannelDiscord"];
|
||||
|
||||
export const editMessageDiscord: DiscordRuntimeModule["editMessageDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().editMessageDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["editMessageDiscord"];
|
||||
|
||||
export const getThreadBindingManager: DiscordRuntimeModule["getThreadBindingManager"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadDiscordRuntimeSurface().getThreadBindingManager(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["getThreadBindingManager"];
|
||||
|
||||
export const listDiscordDirectoryGroupsLive: DiscordRuntimeModule["listDiscordDirectoryGroupsLive"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().listDiscordDirectoryGroupsLive(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["listDiscordDirectoryGroupsLive"];
|
||||
|
||||
export const listDiscordDirectoryPeersLive: DiscordRuntimeModule["listDiscordDirectoryPeersLive"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().listDiscordDirectoryPeersLive(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["listDiscordDirectoryPeersLive"];
|
||||
|
||||
export const monitorDiscordProvider: DiscordRuntimeModule["monitorDiscordProvider"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().monitorDiscordProvider(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["monitorDiscordProvider"];
|
||||
|
||||
export const pinMessageDiscord: DiscordRuntimeModule["pinMessageDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().pinMessageDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["pinMessageDiscord"];
|
||||
|
||||
export const probeDiscord: DiscordRuntimeModule["probeDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().probeDiscord(...args)) as DiscordRuntimeModule["probeDiscord"];
|
||||
|
||||
export const resolveDiscordChannelAllowlist: DiscordRuntimeModule["resolveDiscordChannelAllowlist"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().resolveDiscordChannelAllowlist(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveDiscordChannelAllowlist"];
|
||||
|
||||
export const resolveDiscordUserAllowlist: DiscordRuntimeModule["resolveDiscordUserAllowlist"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadDiscordRuntimeSurface().resolveDiscordUserAllowlist(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveDiscordUserAllowlist"];
|
||||
|
||||
export const resolveThreadBindingIdleTimeoutMs: DiscordRuntimeModule["resolveThreadBindingIdleTimeoutMs"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().resolveThreadBindingIdleTimeoutMs(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveThreadBindingIdleTimeoutMs"];
|
||||
|
||||
export const resolveThreadBindingInactivityExpiresAt: DiscordRuntimeModule["resolveThreadBindingInactivityExpiresAt"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().resolveThreadBindingInactivityExpiresAt(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveThreadBindingInactivityExpiresAt"];
|
||||
|
||||
export const resolveThreadBindingMaxAgeExpiresAt: DiscordRuntimeModule["resolveThreadBindingMaxAgeExpiresAt"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().resolveThreadBindingMaxAgeExpiresAt(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveThreadBindingMaxAgeExpiresAt"];
|
||||
|
||||
export const resolveThreadBindingMaxAgeMs: DiscordRuntimeModule["resolveThreadBindingMaxAgeMs"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadDiscordRuntimeSurface().resolveThreadBindingMaxAgeMs(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["resolveThreadBindingMaxAgeMs"];
|
||||
|
||||
export const sendDiscordComponentMessage: DiscordRuntimeModule["sendDiscordComponentMessage"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadDiscordRuntimeSurface().sendDiscordComponentMessage(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["sendDiscordComponentMessage"];
|
||||
|
||||
export const sendMessageDiscord: DiscordRuntimeModule["sendMessageDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().sendMessageDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["sendMessageDiscord"];
|
||||
|
||||
export const sendPollDiscord: DiscordRuntimeModule["sendPollDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().sendPollDiscord(...args)) as DiscordRuntimeModule["sendPollDiscord"];
|
||||
|
||||
export const sendTypingDiscord: DiscordRuntimeModule["sendTypingDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().sendTypingDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["sendTypingDiscord"];
|
||||
|
||||
export const setThreadBindingIdleTimeoutBySessionKey: DiscordRuntimeModule["setThreadBindingIdleTimeoutBySessionKey"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().setThreadBindingIdleTimeoutBySessionKey(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["setThreadBindingIdleTimeoutBySessionKey"];
|
||||
|
||||
export const setThreadBindingMaxAgeBySessionKey: DiscordRuntimeModule["setThreadBindingMaxAgeBySessionKey"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().setThreadBindingMaxAgeBySessionKey(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["setThreadBindingMaxAgeBySessionKey"];
|
||||
|
||||
export const unbindThreadBindingsBySessionKey: DiscordRuntimeModule["unbindThreadBindingsBySessionKey"] =
|
||||
((...args) =>
|
||||
loadDiscordRuntimeSurface().unbindThreadBindingsBySessionKey(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["unbindThreadBindingsBySessionKey"];
|
||||
|
||||
export const unpinMessageDiscord: DiscordRuntimeModule["unpinMessageDiscord"] = ((...args) =>
|
||||
loadDiscordRuntimeSurface().unpinMessageDiscord(
|
||||
...args,
|
||||
)) as DiscordRuntimeModule["unpinMessageDiscord"];
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import type { DiscordSendResult } from "../../extensions/discord/api.js";
|
||||
import type { OutboundMediaAccess } from "../media/load-options.js";
|
||||
import { attachChannelToResult } from "./channel-send-result.js";
|
||||
|
||||
type DiscordSendOptionInput = {
|
||||
replyToId?: string | null;
|
||||
accountId?: string | null;
|
||||
silent?: boolean;
|
||||
};
|
||||
|
||||
type DiscordSendMediaOptionInput = DiscordSendOptionInput & {
|
||||
mediaUrl?: string;
|
||||
mediaAccess?: OutboundMediaAccess;
|
||||
mediaLocalRoots?: readonly string[];
|
||||
mediaReadFile?: (filePath: string) => Promise<Buffer>;
|
||||
};
|
||||
|
||||
/** Build the common Discord send options from SDK-level reply payload fields. */
|
||||
export function buildDiscordSendOptions(input: DiscordSendOptionInput) {
|
||||
return {
|
||||
verbose: false,
|
||||
replyTo: input.replyToId ?? undefined,
|
||||
accountId: input.accountId ?? undefined,
|
||||
silent: input.silent ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** Extend the base Discord send options with media-specific fields. */
|
||||
export function buildDiscordSendMediaOptions(input: DiscordSendMediaOptionInput) {
|
||||
return {
|
||||
...buildDiscordSendOptions(input),
|
||||
mediaUrl: input.mediaUrl,
|
||||
mediaAccess: input.mediaAccess,
|
||||
mediaLocalRoots: input.mediaLocalRoots,
|
||||
mediaReadFile: input.mediaReadFile,
|
||||
};
|
||||
}
|
||||
|
||||
/** Stamp raw Discord send results with the channel id expected by shared outbound flows. */
|
||||
export function tagDiscordChannelResult(result: DiscordSendResult) {
|
||||
return attachChannelToResult("discord", result);
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
createLazyFacadeObjectValue,
|
||||
loadBundledPluginPublicSurfaceModuleSync,
|
||||
} from "./facade-runtime.js";
|
||||
|
||||
type SignalRuntimeModule = typeof import("../../extensions/signal/runtime-api.js");
|
||||
|
||||
type SignalRuntimeSurface = Pick<
|
||||
SignalRuntimeModule,
|
||||
"monitorSignalProvider" | "probeSignal" | "sendMessageSignal" | "signalMessageActions"
|
||||
>;
|
||||
|
||||
function loadSignalRuntimeSurface(): SignalRuntimeSurface {
|
||||
return loadBundledPluginPublicSurfaceModuleSync<SignalRuntimeSurface>({
|
||||
dirName: "signal",
|
||||
artifactBasename: "runtime-api.js",
|
||||
});
|
||||
}
|
||||
|
||||
export const signalMessageActions: SignalRuntimeModule["signalMessageActions"] =
|
||||
createLazyFacadeObjectValue(() => loadSignalRuntimeSurface().signalMessageActions);
|
||||
|
||||
export const monitorSignalProvider: SignalRuntimeModule["monitorSignalProvider"] = ((...args) =>
|
||||
loadSignalRuntimeSurface().monitorSignalProvider(
|
||||
...args,
|
||||
)) as SignalRuntimeModule["monitorSignalProvider"];
|
||||
|
||||
export const probeSignal: SignalRuntimeModule["probeSignal"] = ((...args) =>
|
||||
loadSignalRuntimeSurface().probeSignal(...args)) as SignalRuntimeModule["probeSignal"];
|
||||
|
||||
export const sendMessageSignal: SignalRuntimeModule["sendMessageSignal"] = ((...args) =>
|
||||
loadSignalRuntimeSurface().sendMessageSignal(
|
||||
...args,
|
||||
)) as SignalRuntimeModule["sendMessageSignal"];
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js";
|
||||
|
||||
type SlackRuntimeModule = typeof import("../../extensions/slack/runtime-api.js");
|
||||
|
||||
type SlackRuntimeSurface = Pick<
|
||||
SlackRuntimeModule,
|
||||
| "handleSlackAction"
|
||||
| "listSlackDirectoryGroupsLive"
|
||||
| "listSlackDirectoryPeersLive"
|
||||
| "monitorSlackProvider"
|
||||
| "probeSlack"
|
||||
| "resolveSlackChannelAllowlist"
|
||||
| "resolveSlackUserAllowlist"
|
||||
| "sendMessageSlack"
|
||||
>;
|
||||
|
||||
function loadSlackRuntimeSurface(): SlackRuntimeSurface {
|
||||
return loadBundledPluginPublicSurfaceModuleSync<SlackRuntimeSurface>({
|
||||
dirName: "slack",
|
||||
artifactBasename: "runtime-api.js",
|
||||
});
|
||||
}
|
||||
|
||||
export const handleSlackAction: SlackRuntimeModule["handleSlackAction"] = ((...args) =>
|
||||
loadSlackRuntimeSurface().handleSlackAction(...args)) as SlackRuntimeModule["handleSlackAction"];
|
||||
|
||||
export const listSlackDirectoryGroupsLive: SlackRuntimeModule["listSlackDirectoryGroupsLive"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadSlackRuntimeSurface().listSlackDirectoryGroupsLive(
|
||||
...args,
|
||||
)) as SlackRuntimeModule["listSlackDirectoryGroupsLive"];
|
||||
|
||||
export const listSlackDirectoryPeersLive: SlackRuntimeModule["listSlackDirectoryPeersLive"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadSlackRuntimeSurface().listSlackDirectoryPeersLive(
|
||||
...args,
|
||||
)) as SlackRuntimeModule["listSlackDirectoryPeersLive"];
|
||||
|
||||
export const monitorSlackProvider: SlackRuntimeModule["monitorSlackProvider"] = ((...args) =>
|
||||
loadSlackRuntimeSurface().monitorSlackProvider(
|
||||
...args,
|
||||
)) as SlackRuntimeModule["monitorSlackProvider"];
|
||||
|
||||
export const probeSlack: SlackRuntimeModule["probeSlack"] = ((...args) =>
|
||||
loadSlackRuntimeSurface().probeSlack(...args)) as SlackRuntimeModule["probeSlack"];
|
||||
|
||||
export const resolveSlackChannelAllowlist: SlackRuntimeModule["resolveSlackChannelAllowlist"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadSlackRuntimeSurface().resolveSlackChannelAllowlist(
|
||||
...args,
|
||||
)) as SlackRuntimeModule["resolveSlackChannelAllowlist"];
|
||||
|
||||
export const resolveSlackUserAllowlist: SlackRuntimeModule["resolveSlackUserAllowlist"] = ((
|
||||
...args
|
||||
) =>
|
||||
loadSlackRuntimeSurface().resolveSlackUserAllowlist(
|
||||
...args,
|
||||
)) as SlackRuntimeModule["resolveSlackUserAllowlist"];
|
||||
|
||||
export const sendMessageSlack: SlackRuntimeModule["sendMessageSlack"] = ((...args) =>
|
||||
loadSlackRuntimeSurface().sendMessageSlack(...args)) as SlackRuntimeModule["sendMessageSlack"];
|
||||
|
|
@ -375,7 +375,7 @@ describe("plugin-sdk subpath exports", () => {
|
|||
/plugins\/runtime\/runtime-(?:discord|imessage|line|signal|slack|telegram|whatsapp)(?:[-.][^"']*)?\.js/u,
|
||||
exclude: [
|
||||
"src/plugins/runtime/runtime-plugin-boundary.ts",
|
||||
"src/plugins/runtime/runtime-web-channel-boundary.ts",
|
||||
"src/plugins/runtime/runtime-web-channel-plugin.ts",
|
||||
],
|
||||
excludeFilesMatching: [/\.test\.ts$/u, /\.test-harness\.ts$/u],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,17 +29,17 @@ describe("loadSiblingRuntimeModuleSync", () => {
|
|||
it("loads a sibling runtime module from the caller directory", () => {
|
||||
const root = createTempDir();
|
||||
const moduleUrl = pathToFileURL(
|
||||
path.join(root, "src", "plugins", "runtime", "runtime-line.js"),
|
||||
path.join(root, "src", "plugins", "runtime", "runtime.js"),
|
||||
).href;
|
||||
|
||||
writeFile(
|
||||
path.join(root, "src", "plugins", "runtime", "runtime-line.contract.js"),
|
||||
path.join(root, "src", "plugins", "runtime", "runtime.contract.js"),
|
||||
"module.exports = { runtimeLine: { source: 'sibling' } };",
|
||||
);
|
||||
|
||||
const loaded = loadSiblingRuntimeModuleSync<{ runtimeLine: { source: string } }>({
|
||||
moduleUrl,
|
||||
relativeBase: "./runtime-line.contract",
|
||||
relativeBase: "./runtime.contract",
|
||||
});
|
||||
|
||||
expect(loaded.runtimeLine.source).toBe("sibling");
|
||||
|
|
@ -50,13 +50,13 @@ describe("loadSiblingRuntimeModuleSync", () => {
|
|||
const moduleUrl = pathToFileURL(path.join(root, "dist", "runtime-9DLN_Ai5.js")).href;
|
||||
|
||||
writeFile(
|
||||
path.join(root, "dist", "plugins", "runtime", "runtime-line.contract.js"),
|
||||
path.join(root, "dist", "plugins", "runtime", "runtime.contract.js"),
|
||||
"module.exports = { runtimeLine: { source: 'dist-runtime' } };",
|
||||
);
|
||||
|
||||
const loaded = loadSiblingRuntimeModuleSync<{ runtimeLine: { source: string } }>({
|
||||
moduleUrl,
|
||||
relativeBase: "./runtime-line.contract",
|
||||
relativeBase: "./runtime.contract",
|
||||
});
|
||||
|
||||
expect(loaded.runtimeLine.source).toBe("dist-runtime");
|
||||
|
|
@ -69,8 +69,8 @@ describe("loadSiblingRuntimeModuleSync", () => {
|
|||
expect(() =>
|
||||
loadSiblingRuntimeModuleSync({
|
||||
moduleUrl,
|
||||
relativeBase: "./runtime-line.contract",
|
||||
relativeBase: "./runtime.contract",
|
||||
}),
|
||||
).toThrow("Unable to resolve runtime module ./runtime-line.contract");
|
||||
).toThrow("Unable to resolve runtime module ./runtime.contract");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -61,17 +61,7 @@ import {
|
|||
readChannelAllowFromStore,
|
||||
upsertChannelPairingRequest,
|
||||
} from "../../pairing/pairing-store.js";
|
||||
import {
|
||||
setThreadBindingIdleTimeoutBySessionKey,
|
||||
setThreadBindingMaxAgeBySessionKey,
|
||||
} from "../../plugin-sdk/discord-runtime-surface.js";
|
||||
import { buildAgentSessionKey, resolveAgentRoute } from "../../routing/resolve-route.js";
|
||||
import { defineCachedValue } from "./runtime-cache.js";
|
||||
import { createRuntimeDiscord } from "./runtime-discord.js";
|
||||
import { createRuntimeLine } from "./runtime-line.js";
|
||||
import { createRuntimeMatrix } from "./runtime-matrix.js";
|
||||
import { createRuntimeSignal } from "./runtime-signal.js";
|
||||
import { createRuntimeSlack } from "./runtime-slack.js";
|
||||
import type { PluginRuntime } from "./types.js";
|
||||
|
||||
export function createRuntimeChannel(): PluginRuntime["channel"] {
|
||||
|
|
@ -161,63 +151,22 @@ export function createRuntimeChannel(): PluginRuntime["channel"] {
|
|||
loadAdapter: loadChannelOutboundAdapter,
|
||||
},
|
||||
threadBindings: {
|
||||
setIdleTimeoutBySessionKey: ({ channelId, targetSessionKey, accountId, idleTimeoutMs }) => {
|
||||
switch (channelId) {
|
||||
case "discord":
|
||||
return setThreadBindingIdleTimeoutBySessionKey({
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs,
|
||||
});
|
||||
case "matrix":
|
||||
return setChannelConversationBindingIdleTimeoutBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId: accountId ?? "",
|
||||
idleTimeoutMs,
|
||||
});
|
||||
case "telegram":
|
||||
return setChannelConversationBindingIdleTimeoutBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs,
|
||||
});
|
||||
}
|
||||
},
|
||||
setMaxAgeBySessionKey: ({ channelId, targetSessionKey, accountId, maxAgeMs }) => {
|
||||
switch (channelId) {
|
||||
case "discord":
|
||||
return setThreadBindingMaxAgeBySessionKey({
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs,
|
||||
});
|
||||
case "matrix":
|
||||
return setChannelConversationBindingMaxAgeBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId: accountId ?? "",
|
||||
maxAgeMs,
|
||||
});
|
||||
case "telegram":
|
||||
return setChannelConversationBindingMaxAgeBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs,
|
||||
});
|
||||
}
|
||||
},
|
||||
setIdleTimeoutBySessionKey: ({ channelId, targetSessionKey, accountId, idleTimeoutMs }) =>
|
||||
setChannelConversationBindingIdleTimeoutBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
idleTimeoutMs,
|
||||
}),
|
||||
setMaxAgeBySessionKey: ({ channelId, targetSessionKey, accountId, maxAgeMs }) =>
|
||||
setChannelConversationBindingMaxAgeBySessionKey({
|
||||
channelId,
|
||||
targetSessionKey,
|
||||
accountId,
|
||||
maxAgeMs,
|
||||
}),
|
||||
},
|
||||
} satisfies Omit<PluginRuntime["channel"], "discord" | "slack" | "matrix" | "signal" | "line"> &
|
||||
Partial<Pick<PluginRuntime["channel"], "discord" | "slack" | "matrix" | "signal" | "line">>;
|
||||
|
||||
defineCachedValue(channelRuntime, "discord", createRuntimeDiscord);
|
||||
defineCachedValue(channelRuntime, "slack", createRuntimeSlack);
|
||||
defineCachedValue(channelRuntime, "matrix", createRuntimeMatrix);
|
||||
defineCachedValue(channelRuntime, "signal", createRuntimeSignal);
|
||||
defineCachedValue(channelRuntime, "line", createRuntimeLine);
|
||||
} satisfies PluginRuntime["channel"];
|
||||
|
||||
return channelRuntime as PluginRuntime["channel"];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import {
|
||||
auditDiscordChannelPermissions as auditDiscordChannelPermissionsImpl,
|
||||
listDiscordDirectoryGroupsLive as listDiscordDirectoryGroupsLiveImpl,
|
||||
listDiscordDirectoryPeersLive as listDiscordDirectoryPeersLiveImpl,
|
||||
monitorDiscordProvider as monitorDiscordProviderImpl,
|
||||
probeDiscord as probeDiscordImpl,
|
||||
resolveDiscordChannelAllowlist as resolveDiscordChannelAllowlistImpl,
|
||||
resolveDiscordUserAllowlist as resolveDiscordUserAllowlistImpl,
|
||||
createThreadDiscord as createThreadDiscordImpl,
|
||||
deleteMessageDiscord as deleteMessageDiscordImpl,
|
||||
editChannelDiscord as editChannelDiscordImpl,
|
||||
editMessageDiscord as editMessageDiscordImpl,
|
||||
pinMessageDiscord as pinMessageDiscordImpl,
|
||||
sendDiscordComponentMessage as sendDiscordComponentMessageImpl,
|
||||
sendMessageDiscord as sendMessageDiscordImpl,
|
||||
sendPollDiscord as sendPollDiscordImpl,
|
||||
sendTypingDiscord as sendTypingDiscordImpl,
|
||||
unpinMessageDiscord as unpinMessageDiscordImpl,
|
||||
} from "../../plugin-sdk/discord-runtime-surface.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
type RuntimeDiscordOps = Pick<
|
||||
PluginRuntimeChannel["discord"],
|
||||
| "auditChannelPermissions"
|
||||
| "listDirectoryGroupsLive"
|
||||
| "listDirectoryPeersLive"
|
||||
| "probeDiscord"
|
||||
| "resolveChannelAllowlist"
|
||||
| "resolveUserAllowlist"
|
||||
| "sendComponentMessage"
|
||||
| "sendMessageDiscord"
|
||||
| "sendPollDiscord"
|
||||
| "monitorDiscordProvider"
|
||||
> & {
|
||||
typing: Pick<PluginRuntimeChannel["discord"]["typing"], "pulse">;
|
||||
conversationActions: Pick<
|
||||
PluginRuntimeChannel["discord"]["conversationActions"],
|
||||
"editMessage" | "deleteMessage" | "pinMessage" | "unpinMessage" | "createThread" | "editChannel"
|
||||
>;
|
||||
};
|
||||
|
||||
export const runtimeDiscordOps = {
|
||||
auditChannelPermissions: auditDiscordChannelPermissionsImpl,
|
||||
listDirectoryGroupsLive: listDiscordDirectoryGroupsLiveImpl,
|
||||
listDirectoryPeersLive: listDiscordDirectoryPeersLiveImpl,
|
||||
probeDiscord: probeDiscordImpl,
|
||||
resolveChannelAllowlist: resolveDiscordChannelAllowlistImpl,
|
||||
resolveUserAllowlist: resolveDiscordUserAllowlistImpl,
|
||||
sendComponentMessage: sendDiscordComponentMessageImpl,
|
||||
sendMessageDiscord: sendMessageDiscordImpl,
|
||||
sendPollDiscord: sendPollDiscordImpl,
|
||||
monitorDiscordProvider: monitorDiscordProviderImpl,
|
||||
typing: {
|
||||
pulse: sendTypingDiscordImpl,
|
||||
},
|
||||
conversationActions: {
|
||||
editMessage: editMessageDiscordImpl,
|
||||
deleteMessage: deleteMessageDiscordImpl,
|
||||
pinMessage: pinMessageDiscordImpl,
|
||||
unpinMessage: unpinMessageDiscordImpl,
|
||||
createThread: createThreadDiscordImpl,
|
||||
editChannel: editChannelDiscordImpl,
|
||||
},
|
||||
} satisfies RuntimeDiscordOps;
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import {
|
||||
discordMessageActions,
|
||||
getThreadBindingManager,
|
||||
resolveThreadBindingIdleTimeoutMs,
|
||||
resolveThreadBindingInactivityExpiresAt,
|
||||
resolveThreadBindingMaxAgeExpiresAt,
|
||||
resolveThreadBindingMaxAgeMs,
|
||||
setThreadBindingIdleTimeoutBySessionKey,
|
||||
setThreadBindingMaxAgeBySessionKey,
|
||||
unbindThreadBindingsBySessionKey,
|
||||
} from "../../plugin-sdk/discord-runtime-surface.js";
|
||||
import {
|
||||
createLazyRuntimeMethodBinder,
|
||||
createLazyRuntimeSurface,
|
||||
} from "../../shared/lazy-runtime.js";
|
||||
import { createDiscordTypingLease } from "./runtime-discord-typing.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
const loadRuntimeDiscordOps = createLazyRuntimeSurface(
|
||||
() => import("./runtime-discord-ops.runtime.js"),
|
||||
({ runtimeDiscordOps }) => runtimeDiscordOps,
|
||||
);
|
||||
|
||||
const bindDiscordRuntimeMethod = createLazyRuntimeMethodBinder(loadRuntimeDiscordOps);
|
||||
|
||||
const auditChannelPermissionsLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.auditChannelPermissions,
|
||||
);
|
||||
const listDirectoryGroupsLiveLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.listDirectoryGroupsLive,
|
||||
);
|
||||
const listDirectoryPeersLiveLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.listDirectoryPeersLive,
|
||||
);
|
||||
const probeDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.probeDiscord,
|
||||
);
|
||||
const resolveChannelAllowlistLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.resolveChannelAllowlist,
|
||||
);
|
||||
const resolveUserAllowlistLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.resolveUserAllowlist,
|
||||
);
|
||||
const sendComponentMessageLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.sendComponentMessage,
|
||||
);
|
||||
const sendMessageDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.sendMessageDiscord,
|
||||
);
|
||||
const sendPollDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.sendPollDiscord,
|
||||
);
|
||||
const monitorDiscordProviderLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.monitorDiscordProvider,
|
||||
);
|
||||
const sendTypingDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.typing.pulse,
|
||||
);
|
||||
const editMessageDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.editMessage,
|
||||
);
|
||||
const deleteMessageDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.deleteMessage,
|
||||
);
|
||||
const pinMessageDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.pinMessage,
|
||||
);
|
||||
const unpinMessageDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.unpinMessage,
|
||||
);
|
||||
const createThreadDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.createThread,
|
||||
);
|
||||
const editChannelDiscordLazy = bindDiscordRuntimeMethod(
|
||||
(runtimeDiscordOps) => runtimeDiscordOps.conversationActions.editChannel,
|
||||
);
|
||||
|
||||
export function createRuntimeDiscord(): PluginRuntimeChannel["discord"] {
|
||||
return {
|
||||
messageActions: discordMessageActions,
|
||||
auditChannelPermissions: auditChannelPermissionsLazy,
|
||||
listDirectoryGroupsLive: listDirectoryGroupsLiveLazy,
|
||||
listDirectoryPeersLive: listDirectoryPeersLiveLazy,
|
||||
probeDiscord: probeDiscordLazy,
|
||||
resolveChannelAllowlist: resolveChannelAllowlistLazy,
|
||||
resolveUserAllowlist: resolveUserAllowlistLazy,
|
||||
sendComponentMessage: sendComponentMessageLazy,
|
||||
sendMessageDiscord: sendMessageDiscordLazy,
|
||||
sendPollDiscord: sendPollDiscordLazy,
|
||||
monitorDiscordProvider: monitorDiscordProviderLazy,
|
||||
threadBindings: {
|
||||
getManager: getThreadBindingManager,
|
||||
resolveIdleTimeoutMs: resolveThreadBindingIdleTimeoutMs,
|
||||
resolveInactivityExpiresAt: resolveThreadBindingInactivityExpiresAt,
|
||||
resolveMaxAgeMs: resolveThreadBindingMaxAgeMs,
|
||||
resolveMaxAgeExpiresAt: resolveThreadBindingMaxAgeExpiresAt,
|
||||
setIdleTimeoutBySessionKey: setThreadBindingIdleTimeoutBySessionKey,
|
||||
setMaxAgeBySessionKey: setThreadBindingMaxAgeBySessionKey,
|
||||
unbindBySessionKey: unbindThreadBindingsBySessionKey,
|
||||
},
|
||||
typing: {
|
||||
pulse: sendTypingDiscordLazy,
|
||||
start: async ({ channelId, accountId, cfg, intervalMs }) =>
|
||||
await createDiscordTypingLease({
|
||||
channelId,
|
||||
accountId,
|
||||
cfg,
|
||||
intervalMs,
|
||||
pulse: async ({ channelId, accountId, cfg }) =>
|
||||
void (await sendTypingDiscordLazy(channelId, { accountId, cfg })),
|
||||
}),
|
||||
},
|
||||
conversationActions: {
|
||||
editMessage: editMessageDiscordLazy,
|
||||
deleteMessage: deleteMessageDiscordLazy,
|
||||
pinMessage: pinMessageDiscordLazy,
|
||||
unpinMessage: unpinMessageDiscordLazy,
|
||||
createThread: createThreadDiscordLazy,
|
||||
editChannel: editChannelDiscordLazy,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import {
|
||||
buildTemplateMessageFromPayload,
|
||||
createQuickReplyItems,
|
||||
monitorLineProvider,
|
||||
probeLineBot,
|
||||
pushFlexMessage,
|
||||
pushLocationMessage,
|
||||
pushMessageLine,
|
||||
pushMessagesLine,
|
||||
pushTemplateMessage,
|
||||
pushTextMessageWithQuickReplies,
|
||||
sendMessageLine,
|
||||
} from "../../plugin-sdk/line-runtime.js";
|
||||
import {
|
||||
listLineAccountIds,
|
||||
normalizeAccountId,
|
||||
resolveDefaultLineAccountId,
|
||||
resolveLineAccount,
|
||||
} from "../../plugin-sdk/line.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
export const runtimeLine = {
|
||||
listLineAccountIds,
|
||||
resolveDefaultLineAccountId,
|
||||
resolveLineAccount,
|
||||
normalizeAccountId,
|
||||
probeLineBot,
|
||||
sendMessageLine,
|
||||
pushMessageLine,
|
||||
pushMessagesLine,
|
||||
pushFlexMessage,
|
||||
pushTemplateMessage,
|
||||
pushLocationMessage,
|
||||
pushTextMessageWithQuickReplies,
|
||||
createQuickReplyItems,
|
||||
buildTemplateMessageFromPayload,
|
||||
monitorLineProvider,
|
||||
} satisfies PluginRuntimeChannel["line"];
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
export {
|
||||
monitorLineProvider,
|
||||
probeLineBot,
|
||||
pushFlexMessage,
|
||||
pushLocationMessage,
|
||||
pushMessageLine,
|
||||
pushMessagesLine,
|
||||
pushTemplateMessage,
|
||||
pushTextMessageWithQuickReplies,
|
||||
sendMessageLine,
|
||||
} from "../../plugin-sdk/line-runtime.js";
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import { loadSiblingRuntimeModuleSync } from "./local-runtime-module.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
type RuntimeLineModule = {
|
||||
runtimeLine: PluginRuntimeChannel["line"];
|
||||
};
|
||||
|
||||
let cachedRuntimeLineModule: RuntimeLineModule | null = null;
|
||||
|
||||
function loadRuntimeLineModule(): RuntimeLineModule {
|
||||
cachedRuntimeLineModule ??= loadSiblingRuntimeModuleSync<RuntimeLineModule>({
|
||||
moduleUrl: import.meta.url,
|
||||
relativeBase: "./runtime-line.contract",
|
||||
});
|
||||
return cachedRuntimeLineModule;
|
||||
}
|
||||
|
||||
export function createRuntimeLine(): PluginRuntimeChannel["line"] {
|
||||
return {
|
||||
listLineAccountIds: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.listLineAccountIds(...args),
|
||||
resolveDefaultLineAccountId: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.resolveDefaultLineAccountId(...args),
|
||||
resolveLineAccount: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.resolveLineAccount(...args),
|
||||
normalizeAccountId: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.normalizeAccountId(...args),
|
||||
probeLineBot: (...args) => loadRuntimeLineModule().runtimeLine.probeLineBot(...args),
|
||||
sendMessageLine: (...args) => loadRuntimeLineModule().runtimeLine.sendMessageLine(...args),
|
||||
pushMessageLine: (...args) => loadRuntimeLineModule().runtimeLine.pushMessageLine(...args),
|
||||
pushMessagesLine: (...args) => loadRuntimeLineModule().runtimeLine.pushMessagesLine(...args),
|
||||
pushFlexMessage: (...args) => loadRuntimeLineModule().runtimeLine.pushFlexMessage(...args),
|
||||
pushTemplateMessage: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.pushTemplateMessage(...args),
|
||||
pushLocationMessage: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.pushLocationMessage(...args),
|
||||
pushTextMessageWithQuickReplies: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.pushTextMessageWithQuickReplies(...args),
|
||||
createQuickReplyItems: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.createQuickReplyItems(...args),
|
||||
buildTemplateMessageFromPayload: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.buildTemplateMessageFromPayload(...args),
|
||||
monitorLineProvider: (...args) =>
|
||||
loadRuntimeLineModule().runtimeLine.monitorLineProvider(...args),
|
||||
};
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
import { createJiti } from "jiti";
|
||||
import type { MatrixRuntimeBoundaryModule } from "./runtime-matrix-surface.js";
|
||||
import {
|
||||
loadPluginBoundaryModuleWithJiti,
|
||||
resolvePluginRuntimeModulePath,
|
||||
resolvePluginRuntimeRecord,
|
||||
} from "./runtime-plugin-boundary.js";
|
||||
|
||||
const MATRIX_PLUGIN_ID = "matrix";
|
||||
|
||||
type MatrixPluginRecord = {
|
||||
rootDir?: string;
|
||||
source: string;
|
||||
};
|
||||
|
||||
let cachedModulePath: string | null = null;
|
||||
let cachedModule: MatrixRuntimeBoundaryModule | null = null;
|
||||
|
||||
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
function resolveMatrixPluginRecord(): MatrixPluginRecord | null {
|
||||
return resolvePluginRuntimeRecord(MATRIX_PLUGIN_ID) as MatrixPluginRecord | null;
|
||||
}
|
||||
|
||||
function resolveMatrixRuntimeModulePath(record: MatrixPluginRecord): string | null {
|
||||
return resolvePluginRuntimeModulePath(record, "runtime-api");
|
||||
}
|
||||
|
||||
function loadMatrixModule(): MatrixRuntimeBoundaryModule | null {
|
||||
const record = resolveMatrixPluginRecord();
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
const modulePath = resolveMatrixRuntimeModulePath(record);
|
||||
if (!modulePath) {
|
||||
return null;
|
||||
}
|
||||
if (cachedModule && cachedModulePath === modulePath) {
|
||||
return cachedModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<MatrixRuntimeBoundaryModule>(
|
||||
modulePath,
|
||||
jitiLoaders,
|
||||
);
|
||||
cachedModulePath = modulePath;
|
||||
cachedModule = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
export function setMatrixThreadBindingIdleTimeoutBySessionKey(
|
||||
...args: Parameters<MatrixRuntimeBoundaryModule["setMatrixThreadBindingIdleTimeoutBySessionKey"]>
|
||||
): ReturnType<MatrixRuntimeBoundaryModule["setMatrixThreadBindingIdleTimeoutBySessionKey"]> {
|
||||
const fn = loadMatrixModule()?.setMatrixThreadBindingIdleTimeoutBySessionKey;
|
||||
if (typeof fn !== "function") {
|
||||
return [];
|
||||
}
|
||||
return fn(...args);
|
||||
}
|
||||
|
||||
export function setMatrixThreadBindingMaxAgeBySessionKey(
|
||||
...args: Parameters<MatrixRuntimeBoundaryModule["setMatrixThreadBindingMaxAgeBySessionKey"]>
|
||||
): ReturnType<MatrixRuntimeBoundaryModule["setMatrixThreadBindingMaxAgeBySessionKey"]> {
|
||||
const fn = loadMatrixModule()?.setMatrixThreadBindingMaxAgeBySessionKey;
|
||||
if (typeof fn !== "function") {
|
||||
return [];
|
||||
}
|
||||
return fn(...args);
|
||||
}
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
// Narrow plugin-sdk surface for the bundled Matrix plugin.
|
||||
// Keep this list additive and scoped to the runtime contract only.
|
||||
|
||||
import { createOptionalChannelSetupSurface } from "../../plugin-sdk/channel-setup.js";
|
||||
|
||||
export {
|
||||
createActionGate,
|
||||
jsonResult,
|
||||
readNumberParam,
|
||||
readReactionParams,
|
||||
readStringArrayParam,
|
||||
readStringParam,
|
||||
} from "../../agents/tools/common.js";
|
||||
export type { ReplyPayload } from "../../auto-reply/types.js";
|
||||
export { resolveAckReaction } from "../../agents/identity.js";
|
||||
export {
|
||||
compileAllowlist,
|
||||
resolveCompiledAllowlistMatch,
|
||||
resolveAllowlistCandidates,
|
||||
resolveAllowlistMatchByCandidates,
|
||||
} from "../../channels/allowlist-match.js";
|
||||
export {
|
||||
addAllowlistUserEntriesFromConfigEntry,
|
||||
buildAllowlistResolutionSummary,
|
||||
canonicalizeAllowlistWithResolvedIds,
|
||||
mergeAllowlist,
|
||||
patchAllowlistUsersInConfigEntries,
|
||||
summarizeMapping,
|
||||
} from "../../channels/allowlists/resolve-utils.js";
|
||||
export { ensureConfiguredAcpBindingReady } from "../../acp/persistent-bindings.lifecycle.js";
|
||||
export { resolveConfiguredAcpBindingRecord } from "../../acp/persistent-bindings.resolve.js";
|
||||
export { resolveControlCommandGate } from "../../channels/command-gating.js";
|
||||
export type { NormalizedLocation } from "../../channels/location.js";
|
||||
export { formatLocationText, toLocationContext } from "../../channels/location.js";
|
||||
export { logInboundDrop, logTypingFailure } from "../../channels/logging.js";
|
||||
export type { AllowlistMatch } from "../../channels/plugins/allowlist-match.js";
|
||||
export { formatAllowlistMatchMeta } from "../../channels/plugins/allowlist-match.js";
|
||||
export {
|
||||
buildChannelKeyCandidates,
|
||||
resolveChannelEntryMatch,
|
||||
} from "../../channels/plugins/channel-config.js";
|
||||
export { createAccountListHelpers } from "../../channels/plugins/account-helpers.js";
|
||||
export {
|
||||
deleteAccountFromConfigSection,
|
||||
setAccountEnabledInConfigSection,
|
||||
} from "../../channels/plugins/config-helpers.js";
|
||||
export { buildChannelConfigSchema } from "../../channels/plugins/config-schema.js";
|
||||
export { formatPairingApproveHint } from "../../channels/plugins/helpers.js";
|
||||
export {
|
||||
buildSingleChannelSecretPromptState,
|
||||
addWildcardAllowFrom,
|
||||
mergeAllowFromEntries,
|
||||
promptAccountId,
|
||||
promptSingleChannelSecretInput,
|
||||
setTopLevelChannelGroupPolicy,
|
||||
} from "../../channels/plugins/setup-wizard-helpers.js";
|
||||
export { promptChannelAccessConfig } from "../../channels/plugins/setup-group-access.js";
|
||||
export { PAIRING_APPROVED_MESSAGE } from "../../channels/plugins/pairing-message.js";
|
||||
export {
|
||||
applyAccountNameToChannelSection,
|
||||
moveSingleAccountChannelSectionToDefaultAccount,
|
||||
} from "../../channels/plugins/setup-helpers.js";
|
||||
export type {
|
||||
BaseProbeResult,
|
||||
ChannelDirectoryEntry,
|
||||
ChannelGroupContext,
|
||||
ChannelMessageActionAdapter,
|
||||
ChannelMessageActionContext,
|
||||
ChannelMessageActionName,
|
||||
ChannelMessageToolDiscovery,
|
||||
ChannelMessageToolSchemaContribution,
|
||||
ChannelOutboundAdapter,
|
||||
ChannelResolveKind,
|
||||
ChannelResolveResult,
|
||||
ChannelSetupInput,
|
||||
ChannelToolSend,
|
||||
} from "../../channels/plugins/types.js";
|
||||
export type { ChannelPlugin } from "../../channels/plugins/types.plugin.js";
|
||||
export { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
|
||||
export { resolveThreadBindingFarewellText } from "../../channels/thread-bindings-messages.js";
|
||||
export {
|
||||
resolveThreadBindingIdleTimeoutMsForChannel,
|
||||
resolveThreadBindingMaxAgeMsForChannel,
|
||||
} from "../../channels/thread-bindings-policy.js";
|
||||
export {
|
||||
setMatrixThreadBindingIdleTimeoutBySessionKey,
|
||||
setMatrixThreadBindingMaxAgeBySessionKey,
|
||||
} from "../../plugin-sdk/matrix-thread-bindings.js";
|
||||
export { createTypingCallbacks } from "../../channels/typing.js";
|
||||
export { createChannelReplyPipeline } from "../../plugin-sdk/channel-reply-pipeline.js";
|
||||
export type { OpenClawConfig } from "../../config/config.js";
|
||||
export {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
resolveAllowlistProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../../config/runtime-group-policy.js";
|
||||
export type {
|
||||
DmPolicy,
|
||||
GroupPolicy,
|
||||
GroupToolPolicyConfig,
|
||||
MarkdownTableMode,
|
||||
} from "../../config/types.js";
|
||||
export type { SecretInput } from "../../plugin-sdk/secret-input.js";
|
||||
export {
|
||||
buildSecretInputSchema,
|
||||
hasConfiguredSecretInput,
|
||||
normalizeResolvedSecretInputString,
|
||||
normalizeSecretInputString,
|
||||
} from "../../plugin-sdk/secret-input.js";
|
||||
export { ToolPolicySchema } from "../../config/zod-schema.agent-runtime.js";
|
||||
export { MarkdownConfigSchema } from "../../config/zod-schema.core.js";
|
||||
export { formatZonedTimestamp } from "../../infra/format-time/format-datetime.js";
|
||||
export { fetchWithSsrFGuard } from "../../infra/net/fetch-guard.js";
|
||||
export { maybeCreateMatrixMigrationSnapshot } from "../../infra/matrix-migration-snapshot.js";
|
||||
export {
|
||||
getSessionBindingService,
|
||||
registerSessionBindingAdapter,
|
||||
unregisterSessionBindingAdapter,
|
||||
} from "../../infra/outbound/session-binding-service.js";
|
||||
export { resolveOutboundSendDep } from "../../infra/outbound/send-deps.js";
|
||||
export type {
|
||||
BindingTargetKind,
|
||||
SessionBindingRecord,
|
||||
} from "../../infra/outbound/session-binding-service.js";
|
||||
export { isPrivateOrLoopbackHost } from "../../gateway/net.js";
|
||||
export { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
|
||||
export { emptyPluginConfigSchema } from "../config-schema.js";
|
||||
export type { PluginRuntime, RuntimeLogger } from "./types.js";
|
||||
export type { OpenClawPluginApi } from "../types.js";
|
||||
export type { PollInput } from "../../polls.js";
|
||||
export { normalizePollInput } from "../../polls.js";
|
||||
export {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
normalizeAccountId,
|
||||
normalizeOptionalAccountId,
|
||||
resolveAgentIdFromSessionKey,
|
||||
} from "../../routing/session-key.js";
|
||||
export type { RuntimeEnv } from "../../runtime.js";
|
||||
export { normalizeStringEntries } from "../../shared/string-normalization.js";
|
||||
export { formatDocsLink } from "../../terminal/links.js";
|
||||
export { redactSensitiveText } from "../../logging/redact.js";
|
||||
export type { WizardPrompter } from "../../wizard/prompts.js";
|
||||
export {
|
||||
evaluateGroupRouteAccessForPolicy,
|
||||
resolveSenderScopedGroupPolicy,
|
||||
} from "../../plugin-sdk/group-access.js";
|
||||
export { createChannelPairingController } from "../../plugin-sdk/channel-pairing.js";
|
||||
export { readJsonFileWithFallback, writeJsonFileAtomically } from "../../plugin-sdk/json-store.js";
|
||||
export { formatResolvedUnresolvedNote } from "../../plugin-sdk/resolution-notes.js";
|
||||
export { runPluginCommandWithTimeout } from "../../plugin-sdk/run-command.js";
|
||||
export { createLoggerBackedRuntime, resolveRuntimeEnv } from "../../plugin-sdk/runtime.js";
|
||||
export { dispatchReplyFromConfigWithSettledDispatcher } from "../../plugin-sdk/inbound-reply-dispatch.js";
|
||||
export {
|
||||
buildProbeChannelStatusSummary,
|
||||
collectStatusIssuesFromLastError,
|
||||
} from "../../plugin-sdk/status-helpers.js";
|
||||
export {
|
||||
resolveMatrixAccountStorageRoot,
|
||||
resolveMatrixCredentialsDir,
|
||||
resolveMatrixCredentialsPath,
|
||||
resolveMatrixLegacyFlatStoragePaths,
|
||||
} from "../../plugin-sdk/matrix-helper.js";
|
||||
export { getMatrixScopedEnvVarNames } from "../../plugin-sdk/matrix-helper.js";
|
||||
export {
|
||||
requiresExplicitMatrixDefaultAccount,
|
||||
resolveMatrixDefaultOrOnlyAccountId,
|
||||
} from "../../plugin-sdk/matrix-helper.js";
|
||||
|
||||
const matrixSetup = createOptionalChannelSetupSurface({
|
||||
channel: "matrix",
|
||||
label: "Matrix",
|
||||
npmSpec: "@openclaw/matrix",
|
||||
docsPath: "/channels/matrix",
|
||||
});
|
||||
|
||||
export const matrixSetupWizard = matrixSetup.setupWizard;
|
||||
export const matrixSetupAdapter = matrixSetup.setupAdapter;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
|
||||
|
||||
export type MatrixThreadBindingIdleTimeoutParams = {
|
||||
accountId: string;
|
||||
targetSessionKey: string;
|
||||
idleTimeoutMs: number;
|
||||
};
|
||||
|
||||
export type MatrixThreadBindingMaxAgeParams = {
|
||||
accountId: string;
|
||||
targetSessionKey: string;
|
||||
maxAgeMs: number;
|
||||
};
|
||||
|
||||
export type MatrixRuntimeBoundaryModule = {
|
||||
setMatrixThreadBindingIdleTimeoutBySessionKey: (
|
||||
params: MatrixThreadBindingIdleTimeoutParams,
|
||||
) => SessionBindingRecord[];
|
||||
setMatrixThreadBindingMaxAgeBySessionKey: (
|
||||
params: MatrixThreadBindingMaxAgeParams,
|
||||
) => SessionBindingRecord[];
|
||||
};
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import {
|
||||
setMatrixThreadBindingIdleTimeoutBySessionKey,
|
||||
setMatrixThreadBindingMaxAgeBySessionKey,
|
||||
} from "./runtime-matrix-boundary.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
export function createRuntimeMatrix(): PluginRuntimeChannel["matrix"] {
|
||||
return {
|
||||
threadBindings: {
|
||||
setIdleTimeoutBySessionKey: setMatrixThreadBindingIdleTimeoutBySessionKey,
|
||||
setMaxAgeBySessionKey: setMatrixThreadBindingMaxAgeBySessionKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -16,6 +16,13 @@ type PluginRuntimeRecord = {
|
|||
source: string;
|
||||
};
|
||||
|
||||
type CachedPluginBoundaryLoaderParams = {
|
||||
pluginId: string;
|
||||
entryBaseName: string;
|
||||
required?: boolean;
|
||||
missingLabel?: string;
|
||||
};
|
||||
|
||||
export function readPluginBoundaryConfigSafely() {
|
||||
try {
|
||||
return loadConfig();
|
||||
|
|
@ -104,3 +111,47 @@ export function loadPluginBoundaryModuleWithJiti<TModule>(
|
|||
): TModule {
|
||||
return getPluginBoundaryJiti(modulePath, loaders)(modulePath) as TModule;
|
||||
}
|
||||
|
||||
export function createCachedPluginBoundaryModuleLoader<TModule>(
|
||||
params: CachedPluginBoundaryLoaderParams,
|
||||
): () => TModule | null {
|
||||
let cachedModulePath: string | null = null;
|
||||
let cachedModule: TModule | null = null;
|
||||
const loaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
return () => {
|
||||
const missingLabel = params.missingLabel ?? `${params.pluginId} plugin runtime`;
|
||||
const record = resolvePluginRuntimeRecord(
|
||||
params.pluginId,
|
||||
params.required
|
||||
? () => {
|
||||
throw new Error(`${missingLabel} is unavailable: missing plugin '${params.pluginId}'`);
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
const modulePath = resolvePluginRuntimeModulePath(
|
||||
record,
|
||||
params.entryBaseName,
|
||||
params.required
|
||||
? () => {
|
||||
throw new Error(
|
||||
`${missingLabel} is unavailable: missing ${params.entryBaseName} for plugin '${params.pluginId}'`,
|
||||
);
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
if (!modulePath) {
|
||||
return null;
|
||||
}
|
||||
if (cachedModule && cachedModulePath === modulePath) {
|
||||
return cachedModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<TModule>(modulePath, loaders);
|
||||
cachedModulePath = modulePath;
|
||||
cachedModule = loaded;
|
||||
return loaded;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import {
|
||||
monitorSignalProvider,
|
||||
probeSignal,
|
||||
signalMessageActions,
|
||||
sendMessageSignal,
|
||||
} from "../../plugin-sdk/signal-runtime-surface.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
export function createRuntimeSignal(): PluginRuntimeChannel["signal"] {
|
||||
return {
|
||||
probeSignal,
|
||||
sendMessageSignal,
|
||||
monitorSignalProvider,
|
||||
messageActions: signalMessageActions,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
listSlackDirectoryGroupsLive as listSlackDirectoryGroupsLiveImpl,
|
||||
listSlackDirectoryPeersLive as listSlackDirectoryPeersLiveImpl,
|
||||
monitorSlackProvider as monitorSlackProviderImpl,
|
||||
probeSlack as probeSlackImpl,
|
||||
resolveSlackChannelAllowlist as resolveSlackChannelAllowlistImpl,
|
||||
resolveSlackUserAllowlist as resolveSlackUserAllowlistImpl,
|
||||
sendMessageSlack as sendMessageSlackImpl,
|
||||
handleSlackAction as handleSlackActionImpl,
|
||||
} from "../../plugin-sdk/slack-runtime-surface.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
type RuntimeSlackOps = Pick<
|
||||
PluginRuntimeChannel["slack"],
|
||||
| "listDirectoryGroupsLive"
|
||||
| "listDirectoryPeersLive"
|
||||
| "probeSlack"
|
||||
| "resolveChannelAllowlist"
|
||||
| "resolveUserAllowlist"
|
||||
| "sendMessageSlack"
|
||||
| "monitorSlackProvider"
|
||||
| "handleSlackAction"
|
||||
>;
|
||||
|
||||
export const runtimeSlackOps = {
|
||||
listDirectoryGroupsLive: listSlackDirectoryGroupsLiveImpl,
|
||||
listDirectoryPeersLive: listSlackDirectoryPeersLiveImpl,
|
||||
probeSlack: probeSlackImpl,
|
||||
resolveChannelAllowlist: resolveSlackChannelAllowlistImpl,
|
||||
resolveUserAllowlist: resolveSlackUserAllowlistImpl,
|
||||
sendMessageSlack: sendMessageSlackImpl,
|
||||
monitorSlackProvider: monitorSlackProviderImpl,
|
||||
handleSlackAction: handleSlackActionImpl,
|
||||
} satisfies RuntimeSlackOps;
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import {
|
||||
createLazyRuntimeMethodBinder,
|
||||
createLazyRuntimeSurface,
|
||||
} from "../../shared/lazy-runtime.js";
|
||||
import type { PluginRuntimeChannel } from "./types-channel.js";
|
||||
|
||||
const loadRuntimeSlackOps = createLazyRuntimeSurface(
|
||||
() => import("./runtime-slack-ops.runtime.js"),
|
||||
({ runtimeSlackOps }) => runtimeSlackOps,
|
||||
);
|
||||
|
||||
const bindSlackRuntimeMethod = createLazyRuntimeMethodBinder(loadRuntimeSlackOps);
|
||||
|
||||
const listDirectoryGroupsLiveLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.listDirectoryGroupsLive,
|
||||
);
|
||||
const listDirectoryPeersLiveLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.listDirectoryPeersLive,
|
||||
);
|
||||
const probeSlackLazy = bindSlackRuntimeMethod((runtimeSlackOps) => runtimeSlackOps.probeSlack);
|
||||
const resolveChannelAllowlistLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.resolveChannelAllowlist,
|
||||
);
|
||||
const resolveUserAllowlistLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.resolveUserAllowlist,
|
||||
);
|
||||
const sendMessageSlackLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.sendMessageSlack,
|
||||
);
|
||||
const monitorSlackProviderLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.monitorSlackProvider,
|
||||
);
|
||||
const handleSlackActionLazy = bindSlackRuntimeMethod(
|
||||
(runtimeSlackOps) => runtimeSlackOps.handleSlackAction,
|
||||
);
|
||||
|
||||
export function createRuntimeSlack(): PluginRuntimeChannel["slack"] {
|
||||
return {
|
||||
listDirectoryGroupsLive: listDirectoryGroupsLiveLazy,
|
||||
listDirectoryPeersLive: listDirectoryPeersLiveLazy,
|
||||
probeSlack: probeSlackLazy,
|
||||
resolveChannelAllowlist: resolveChannelAllowlistLazy,
|
||||
resolveUserAllowlist: resolveUserAllowlistLazy,
|
||||
sendMessageSlack: sendMessageSlackLazy,
|
||||
monitorSlackProvider: monitorSlackProviderLazy,
|
||||
handleSlackAction: handleSlackActionLazy,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,286 +0,0 @@
|
|||
import { createJiti } from "jiti";
|
||||
type WhatsAppHeavyRuntimeModule = typeof import("@openclaw/whatsapp/runtime-api.js");
|
||||
type WhatsAppLightRuntimeModule = typeof import("@openclaw/whatsapp/light-runtime-api.js");
|
||||
import {
|
||||
getDefaultLocalRoots as getDefaultLocalRootsImpl,
|
||||
loadWebMedia as loadWebMediaImpl,
|
||||
loadWebMediaRaw as loadWebMediaRawImpl,
|
||||
optimizeImageToJpeg as optimizeImageToJpegImpl,
|
||||
} from "../../media/web-media.js";
|
||||
import {
|
||||
loadPluginBoundaryModuleWithJiti,
|
||||
resolvePluginRuntimeModulePath,
|
||||
resolvePluginRuntimeRecord,
|
||||
} from "./runtime-plugin-boundary.js";
|
||||
|
||||
const WHATSAPP_PLUGIN_ID = "whatsapp";
|
||||
|
||||
type WhatsAppPluginRecord = {
|
||||
origin: string;
|
||||
rootDir?: string;
|
||||
source: string;
|
||||
};
|
||||
|
||||
let cachedHeavyModulePath: string | null = null;
|
||||
let cachedHeavyModule: WhatsAppHeavyRuntimeModule | null = null;
|
||||
let cachedLightModulePath: string | null = null;
|
||||
let cachedLightModule: WhatsAppLightRuntimeModule | null = null;
|
||||
|
||||
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
function resolveWhatsAppPluginRecord(): WhatsAppPluginRecord {
|
||||
return resolvePluginRuntimeRecord(WHATSAPP_PLUGIN_ID, () => {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
}) as WhatsAppPluginRecord;
|
||||
}
|
||||
|
||||
function resolveWhatsAppRuntimeModulePath(
|
||||
record: WhatsAppPluginRecord,
|
||||
entryBaseName: "light-runtime-api" | "runtime-api",
|
||||
): string {
|
||||
const modulePath = resolvePluginRuntimeModulePath(record, entryBaseName, () => {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
});
|
||||
if (!modulePath) {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
}
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
function loadCurrentHeavyModuleSync(): WhatsAppHeavyRuntimeModule {
|
||||
const modulePath = resolveWhatsAppRuntimeModulePath(resolveWhatsAppPluginRecord(), "runtime-api");
|
||||
return loadPluginBoundaryModuleWithJiti<WhatsAppHeavyRuntimeModule>(modulePath, jitiLoaders);
|
||||
}
|
||||
|
||||
function loadWhatsAppLightModule(): WhatsAppLightRuntimeModule {
|
||||
const modulePath = resolveWhatsAppRuntimeModulePath(
|
||||
resolveWhatsAppPluginRecord(),
|
||||
"light-runtime-api",
|
||||
);
|
||||
if (cachedLightModule && cachedLightModulePath === modulePath) {
|
||||
return cachedLightModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WhatsAppLightRuntimeModule>(
|
||||
modulePath,
|
||||
jitiLoaders,
|
||||
);
|
||||
cachedLightModulePath = modulePath;
|
||||
cachedLightModule = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
async function loadWhatsAppHeavyModule(): Promise<WhatsAppHeavyRuntimeModule> {
|
||||
const record = resolveWhatsAppPluginRecord();
|
||||
const modulePath = resolveWhatsAppRuntimeModulePath(record, "runtime-api");
|
||||
if (cachedHeavyModule && cachedHeavyModulePath === modulePath) {
|
||||
return cachedHeavyModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WhatsAppHeavyRuntimeModule>(
|
||||
modulePath,
|
||||
jitiLoaders,
|
||||
);
|
||||
cachedHeavyModulePath = modulePath;
|
||||
cachedHeavyModule = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
function getLightExport<K extends keyof WhatsAppLightRuntimeModule>(
|
||||
exportName: K,
|
||||
): NonNullable<WhatsAppLightRuntimeModule[K]> {
|
||||
const loaded = loadWhatsAppLightModule();
|
||||
const value = loaded[exportName];
|
||||
if (value == null) {
|
||||
throw new Error(`WhatsApp plugin runtime is missing export '${String(exportName)}'`);
|
||||
}
|
||||
return value as NonNullable<WhatsAppLightRuntimeModule[K]>;
|
||||
}
|
||||
|
||||
async function getHeavyExport<K extends keyof WhatsAppHeavyRuntimeModule>(
|
||||
exportName: K,
|
||||
): Promise<NonNullable<WhatsAppHeavyRuntimeModule[K]>> {
|
||||
const loaded = await loadWhatsAppHeavyModule();
|
||||
const value = loaded[exportName];
|
||||
if (value == null) {
|
||||
throw new Error(`WhatsApp plugin runtime is missing export '${String(exportName)}'`);
|
||||
}
|
||||
return value as NonNullable<WhatsAppHeavyRuntimeModule[K]>;
|
||||
}
|
||||
|
||||
export function getActiveWebListener(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["getActiveWebListener"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["getActiveWebListener"]> {
|
||||
return getLightExport("getActiveWebListener")(...args);
|
||||
}
|
||||
|
||||
export function getWebAuthAgeMs(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["getWebAuthAgeMs"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["getWebAuthAgeMs"]> {
|
||||
return getLightExport("getWebAuthAgeMs")(...args);
|
||||
}
|
||||
|
||||
export function logWebSelfId(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["logWebSelfId"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["logWebSelfId"]> {
|
||||
return getLightExport("logWebSelfId")(...args);
|
||||
}
|
||||
|
||||
export function loginWeb(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["loginWeb"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["loginWeb"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.loginWeb(...args));
|
||||
}
|
||||
|
||||
export function logoutWeb(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["logoutWeb"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["logoutWeb"]> {
|
||||
return getLightExport("logoutWeb")(...args);
|
||||
}
|
||||
|
||||
export function readWebSelfId(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["readWebSelfId"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["readWebSelfId"]> {
|
||||
return getLightExport("readWebSelfId")(...args);
|
||||
}
|
||||
|
||||
export function webAuthExists(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["webAuthExists"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["webAuthExists"]> {
|
||||
return getLightExport("webAuthExists")(...args);
|
||||
}
|
||||
|
||||
export function sendMessageWhatsApp(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["sendMessageWhatsApp"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["sendMessageWhatsApp"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.sendMessageWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function sendPollWhatsApp(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["sendPollWhatsApp"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["sendPollWhatsApp"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.sendPollWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function sendReactionWhatsApp(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["sendReactionWhatsApp"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["sendReactionWhatsApp"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.sendReactionWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function createRuntimeWhatsAppLoginTool(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["createWhatsAppLoginTool"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["createWhatsAppLoginTool"]> {
|
||||
return getLightExport("createWhatsAppLoginTool")(...args);
|
||||
}
|
||||
|
||||
export function createWaSocket(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["createWaSocket"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["createWaSocket"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.createWaSocket(...args));
|
||||
}
|
||||
|
||||
export function formatError(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["formatError"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["formatError"]> {
|
||||
return getLightExport("formatError")(...args);
|
||||
}
|
||||
|
||||
export function getStatusCode(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["getStatusCode"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["getStatusCode"]> {
|
||||
return getLightExport("getStatusCode")(...args);
|
||||
}
|
||||
|
||||
export function pickWebChannel(
|
||||
...args: Parameters<WhatsAppLightRuntimeModule["pickWebChannel"]>
|
||||
): ReturnType<WhatsAppLightRuntimeModule["pickWebChannel"]> {
|
||||
return getLightExport("pickWebChannel")(...args);
|
||||
}
|
||||
|
||||
export function resolveWaWebAuthDir(): WhatsAppLightRuntimeModule["WA_WEB_AUTH_DIR"] {
|
||||
return getLightExport("WA_WEB_AUTH_DIR");
|
||||
}
|
||||
|
||||
export async function handleWhatsAppAction(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["handleWhatsAppAction"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["handleWhatsAppAction"]> {
|
||||
return (await getHeavyExport("handleWhatsAppAction"))(...args);
|
||||
}
|
||||
|
||||
export async function loadWebMedia(
|
||||
...args: Parameters<typeof loadWebMediaImpl>
|
||||
): ReturnType<typeof loadWebMediaImpl> {
|
||||
return await loadWebMediaImpl(...args);
|
||||
}
|
||||
|
||||
export async function loadWebMediaRaw(
|
||||
...args: Parameters<typeof loadWebMediaRawImpl>
|
||||
): ReturnType<typeof loadWebMediaRawImpl> {
|
||||
return await loadWebMediaRawImpl(...args);
|
||||
}
|
||||
|
||||
export function monitorWebChannel(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["monitorWebChannel"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["monitorWebChannel"]> {
|
||||
return loadWhatsAppHeavyModule().then((loaded) => loaded.monitorWebChannel(...args));
|
||||
}
|
||||
|
||||
export async function monitorWebInbox(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["monitorWebInbox"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["monitorWebInbox"]> {
|
||||
return (await getHeavyExport("monitorWebInbox"))(...args);
|
||||
}
|
||||
|
||||
export async function optimizeImageToJpeg(
|
||||
...args: Parameters<typeof optimizeImageToJpegImpl>
|
||||
): ReturnType<typeof optimizeImageToJpegImpl> {
|
||||
return await optimizeImageToJpegImpl(...args);
|
||||
}
|
||||
|
||||
export async function runWebHeartbeatOnce(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["runWebHeartbeatOnce"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["runWebHeartbeatOnce"]> {
|
||||
return (await getHeavyExport("runWebHeartbeatOnce"))(...args);
|
||||
}
|
||||
|
||||
export async function startWebLoginWithQr(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["startWebLoginWithQr"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["startWebLoginWithQr"]> {
|
||||
return (await getHeavyExport("startWebLoginWithQr"))(...args);
|
||||
}
|
||||
|
||||
export async function waitForWaConnection(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["waitForWaConnection"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["waitForWaConnection"]> {
|
||||
return (await getHeavyExport("waitForWaConnection"))(...args);
|
||||
}
|
||||
|
||||
export async function waitForWebLogin(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["waitForWebLogin"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["waitForWebLogin"]> {
|
||||
return (await getHeavyExport("waitForWebLogin"))(...args);
|
||||
}
|
||||
|
||||
export const extractMediaPlaceholder = (
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["extractMediaPlaceholder"]>
|
||||
) => loadCurrentHeavyModuleSync().extractMediaPlaceholder(...args);
|
||||
|
||||
export const extractText = (...args: Parameters<WhatsAppHeavyRuntimeModule["extractText"]>) =>
|
||||
loadCurrentHeavyModuleSync().extractText(...args);
|
||||
|
||||
export function getDefaultLocalRoots(
|
||||
...args: Parameters<typeof getDefaultLocalRootsImpl>
|
||||
): ReturnType<typeof getDefaultLocalRootsImpl> {
|
||||
return getDefaultLocalRootsImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveHeartbeatRecipients(
|
||||
...args: Parameters<WhatsAppHeavyRuntimeModule["resolveHeartbeatRecipients"]>
|
||||
): ReturnType<WhatsAppHeavyRuntimeModule["resolveHeartbeatRecipients"]> {
|
||||
return loadCurrentHeavyModuleSync().resolveHeartbeatRecipients(...args);
|
||||
}
|
||||
289
src/plugins/runtime/runtime-web-channel-plugin.ts
Normal file
289
src/plugins/runtime/runtime-web-channel-plugin.ts
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
import { createJiti } from "jiti";
|
||||
type WebChannelHeavyRuntimeModule = typeof import("@openclaw/whatsapp/runtime-api.js");
|
||||
type WebChannelLightRuntimeModule = typeof import("@openclaw/whatsapp/light-runtime-api.js");
|
||||
import {
|
||||
getDefaultLocalRoots as getDefaultLocalRootsImpl,
|
||||
loadWebMedia as loadWebMediaImpl,
|
||||
loadWebMediaRaw as loadWebMediaRawImpl,
|
||||
optimizeImageToJpeg as optimizeImageToJpegImpl,
|
||||
} from "../../media/web-media.js";
|
||||
import {
|
||||
loadPluginBoundaryModuleWithJiti,
|
||||
resolvePluginRuntimeModulePath,
|
||||
resolvePluginRuntimeRecord,
|
||||
} from "./runtime-plugin-boundary.js";
|
||||
|
||||
const WEB_CHANNEL_PLUGIN_ID = "whatsapp";
|
||||
|
||||
type WebChannelPluginRecord = {
|
||||
origin: string;
|
||||
rootDir?: string;
|
||||
source: string;
|
||||
};
|
||||
|
||||
let cachedHeavyModulePath: string | null = null;
|
||||
let cachedHeavyModule: WebChannelHeavyRuntimeModule | null = null;
|
||||
let cachedLightModulePath: string | null = null;
|
||||
let cachedLightModule: WebChannelLightRuntimeModule | null = null;
|
||||
|
||||
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
function resolveWebChannelPluginRecord(): WebChannelPluginRecord {
|
||||
return resolvePluginRuntimeRecord(WEB_CHANNEL_PLUGIN_ID, () => {
|
||||
throw new Error(
|
||||
`web channel plugin runtime is unavailable: missing plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
|
||||
);
|
||||
}) as WebChannelPluginRecord;
|
||||
}
|
||||
|
||||
function resolveWebChannelRuntimeModulePath(
|
||||
record: WebChannelPluginRecord,
|
||||
entryBaseName: "light-runtime-api" | "runtime-api",
|
||||
): string {
|
||||
const modulePath = resolvePluginRuntimeModulePath(record, entryBaseName, () => {
|
||||
throw new Error(
|
||||
`web channel plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
|
||||
);
|
||||
});
|
||||
if (!modulePath) {
|
||||
throw new Error(
|
||||
`web channel plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WEB_CHANNEL_PLUGIN_ID}'`,
|
||||
);
|
||||
}
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
function loadCurrentHeavyModuleSync(): WebChannelHeavyRuntimeModule {
|
||||
const modulePath = resolveWebChannelRuntimeModulePath(
|
||||
resolveWebChannelPluginRecord(),
|
||||
"runtime-api",
|
||||
);
|
||||
return loadPluginBoundaryModuleWithJiti<WebChannelHeavyRuntimeModule>(modulePath, jitiLoaders);
|
||||
}
|
||||
|
||||
function loadWebChannelLightModule(): WebChannelLightRuntimeModule {
|
||||
const modulePath = resolveWebChannelRuntimeModulePath(
|
||||
resolveWebChannelPluginRecord(),
|
||||
"light-runtime-api",
|
||||
);
|
||||
if (cachedLightModule && cachedLightModulePath === modulePath) {
|
||||
return cachedLightModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WebChannelLightRuntimeModule>(
|
||||
modulePath,
|
||||
jitiLoaders,
|
||||
);
|
||||
cachedLightModulePath = modulePath;
|
||||
cachedLightModule = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
async function loadWebChannelHeavyModule(): Promise<WebChannelHeavyRuntimeModule> {
|
||||
const record = resolveWebChannelPluginRecord();
|
||||
const modulePath = resolveWebChannelRuntimeModulePath(record, "runtime-api");
|
||||
if (cachedHeavyModule && cachedHeavyModulePath === modulePath) {
|
||||
return cachedHeavyModule;
|
||||
}
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WebChannelHeavyRuntimeModule>(
|
||||
modulePath,
|
||||
jitiLoaders,
|
||||
);
|
||||
cachedHeavyModulePath = modulePath;
|
||||
cachedHeavyModule = loaded;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
function getLightExport<K extends keyof WebChannelLightRuntimeModule>(
|
||||
exportName: K,
|
||||
): NonNullable<WebChannelLightRuntimeModule[K]> {
|
||||
const loaded = loadWebChannelLightModule();
|
||||
const value = loaded[exportName];
|
||||
if (value == null) {
|
||||
throw new Error(`web channel plugin runtime is missing export '${String(exportName)}'`);
|
||||
}
|
||||
return value as NonNullable<WebChannelLightRuntimeModule[K]>;
|
||||
}
|
||||
|
||||
async function getHeavyExport<K extends keyof WebChannelHeavyRuntimeModule>(
|
||||
exportName: K,
|
||||
): Promise<NonNullable<WebChannelHeavyRuntimeModule[K]>> {
|
||||
const loaded = await loadWebChannelHeavyModule();
|
||||
const value = loaded[exportName];
|
||||
if (value == null) {
|
||||
throw new Error(`web channel plugin runtime is missing export '${String(exportName)}'`);
|
||||
}
|
||||
return value as NonNullable<WebChannelHeavyRuntimeModule[K]>;
|
||||
}
|
||||
|
||||
export function getActiveWebListener(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["getActiveWebListener"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["getActiveWebListener"]> {
|
||||
return getLightExport("getActiveWebListener")(...args);
|
||||
}
|
||||
|
||||
export function getWebAuthAgeMs(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["getWebAuthAgeMs"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["getWebAuthAgeMs"]> {
|
||||
return getLightExport("getWebAuthAgeMs")(...args);
|
||||
}
|
||||
|
||||
export function logWebSelfId(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["logWebSelfId"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["logWebSelfId"]> {
|
||||
return getLightExport("logWebSelfId")(...args);
|
||||
}
|
||||
|
||||
export function loginWeb(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["loginWeb"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["loginWeb"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.loginWeb(...args));
|
||||
}
|
||||
|
||||
export function logoutWeb(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["logoutWeb"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["logoutWeb"]> {
|
||||
return getLightExport("logoutWeb")(...args);
|
||||
}
|
||||
|
||||
export function readWebSelfId(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["readWebSelfId"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["readWebSelfId"]> {
|
||||
return getLightExport("readWebSelfId")(...args);
|
||||
}
|
||||
|
||||
export function webAuthExists(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["webAuthExists"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["webAuthExists"]> {
|
||||
return getLightExport("webAuthExists")(...args);
|
||||
}
|
||||
|
||||
export function sendWebChannelMessage(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["sendMessageWhatsApp"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["sendMessageWhatsApp"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.sendMessageWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function sendWebChannelPoll(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["sendPollWhatsApp"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["sendPollWhatsApp"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.sendPollWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function sendWebChannelReaction(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["sendReactionWhatsApp"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["sendReactionWhatsApp"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.sendReactionWhatsApp(...args));
|
||||
}
|
||||
|
||||
export function createRuntimeWebChannelLoginTool(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["createWhatsAppLoginTool"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["createWhatsAppLoginTool"]> {
|
||||
return getLightExport("createWhatsAppLoginTool")(...args);
|
||||
}
|
||||
|
||||
export function createWebChannelSocket(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["createWaSocket"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["createWaSocket"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.createWaSocket(...args));
|
||||
}
|
||||
|
||||
export function formatError(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["formatError"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["formatError"]> {
|
||||
return getLightExport("formatError")(...args);
|
||||
}
|
||||
|
||||
export function getStatusCode(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["getStatusCode"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["getStatusCode"]> {
|
||||
return getLightExport("getStatusCode")(...args);
|
||||
}
|
||||
|
||||
export function pickWebChannel(
|
||||
...args: Parameters<WebChannelLightRuntimeModule["pickWebChannel"]>
|
||||
): ReturnType<WebChannelLightRuntimeModule["pickWebChannel"]> {
|
||||
return getLightExport("pickWebChannel")(...args);
|
||||
}
|
||||
|
||||
export function resolveWebChannelAuthDir(): WebChannelLightRuntimeModule["WA_WEB_AUTH_DIR"] {
|
||||
return getLightExport("WA_WEB_AUTH_DIR");
|
||||
}
|
||||
|
||||
export async function handleWebChannelAction(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["handleWhatsAppAction"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["handleWhatsAppAction"]> {
|
||||
return (await getHeavyExport("handleWhatsAppAction"))(...args);
|
||||
}
|
||||
|
||||
export async function loadWebMedia(
|
||||
...args: Parameters<typeof loadWebMediaImpl>
|
||||
): ReturnType<typeof loadWebMediaImpl> {
|
||||
return await loadWebMediaImpl(...args);
|
||||
}
|
||||
|
||||
export async function loadWebMediaRaw(
|
||||
...args: Parameters<typeof loadWebMediaRawImpl>
|
||||
): ReturnType<typeof loadWebMediaRawImpl> {
|
||||
return await loadWebMediaRawImpl(...args);
|
||||
}
|
||||
|
||||
export function monitorWebChannel(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["monitorWebChannel"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["monitorWebChannel"]> {
|
||||
return loadWebChannelHeavyModule().then((loaded) => loaded.monitorWebChannel(...args));
|
||||
}
|
||||
|
||||
export async function monitorWebInbox(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["monitorWebInbox"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["monitorWebInbox"]> {
|
||||
return (await getHeavyExport("monitorWebInbox"))(...args);
|
||||
}
|
||||
|
||||
export async function optimizeImageToJpeg(
|
||||
...args: Parameters<typeof optimizeImageToJpegImpl>
|
||||
): ReturnType<typeof optimizeImageToJpegImpl> {
|
||||
return await optimizeImageToJpegImpl(...args);
|
||||
}
|
||||
|
||||
export async function runWebHeartbeatOnce(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["runWebHeartbeatOnce"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["runWebHeartbeatOnce"]> {
|
||||
return (await getHeavyExport("runWebHeartbeatOnce"))(...args);
|
||||
}
|
||||
|
||||
export async function startWebLoginWithQr(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["startWebLoginWithQr"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["startWebLoginWithQr"]> {
|
||||
return (await getHeavyExport("startWebLoginWithQr"))(...args);
|
||||
}
|
||||
|
||||
export async function waitForWebChannelConnection(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["waitForWaConnection"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["waitForWaConnection"]> {
|
||||
return (await getHeavyExport("waitForWaConnection"))(...args);
|
||||
}
|
||||
|
||||
export async function waitForWebLogin(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["waitForWebLogin"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["waitForWebLogin"]> {
|
||||
return (await getHeavyExport("waitForWebLogin"))(...args);
|
||||
}
|
||||
|
||||
export const extractMediaPlaceholder = (
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["extractMediaPlaceholder"]>
|
||||
) => loadCurrentHeavyModuleSync().extractMediaPlaceholder(...args);
|
||||
|
||||
export const extractText = (...args: Parameters<WebChannelHeavyRuntimeModule["extractText"]>) =>
|
||||
loadCurrentHeavyModuleSync().extractText(...args);
|
||||
|
||||
export function getDefaultLocalRoots(
|
||||
...args: Parameters<typeof getDefaultLocalRootsImpl>
|
||||
): ReturnType<typeof getDefaultLocalRootsImpl> {
|
||||
return getDefaultLocalRootsImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveHeartbeatRecipients(
|
||||
...args: Parameters<WebChannelHeavyRuntimeModule["resolveHeartbeatRecipients"]>
|
||||
): ReturnType<WebChannelHeavyRuntimeModule["resolveHeartbeatRecipients"]> {
|
||||
return loadCurrentHeavyModuleSync().resolveHeartbeatRecipients(...args);
|
||||
}
|
||||
|
|
@ -1,17 +1,14 @@
|
|||
/**
|
||||
* Runtime helpers for native channel plugins.
|
||||
*
|
||||
* This surface exposes core and channel-specific helpers used by bundled
|
||||
* plugins. Prefer hooks unless you need tight in-process coupling with the
|
||||
* OpenClaw messaging/runtime stack.
|
||||
* This surface exposes generic core helpers only. Plugin-owned behavior stays
|
||||
* inside the owning plugin package instead of hanging off core runtime slots
|
||||
* like `channel.discord` or `channel.slack`.
|
||||
*/
|
||||
type ReadChannelAllowFromStore =
|
||||
typeof import("../../pairing/pairing-store.js").readChannelAllowFromStore;
|
||||
type UpsertChannelPairingRequest =
|
||||
typeof import("../../pairing/pairing-store.js").upsertChannelPairingRequest;
|
||||
type DiscordRuntimeSurface = typeof import("../../../extensions/discord/runtime-api.js");
|
||||
type DiscordThreadBindings = typeof import("../../../extensions/discord/runtime-api.js");
|
||||
type MatrixThreadBindings = typeof import("../../../extensions/matrix/api.js");
|
||||
|
||||
type ReadChannelAllowFromStoreForAccount = (params: {
|
||||
channel: Parameters<ReadChannelAllowFromStore>[0];
|
||||
|
|
@ -110,98 +107,16 @@ export type PluginRuntimeChannel = {
|
|||
};
|
||||
threadBindings: {
|
||||
setIdleTimeoutBySessionKey: (params: {
|
||||
channelId: "discord" | "matrix" | "telegram";
|
||||
channelId: string;
|
||||
targetSessionKey: string;
|
||||
accountId?: string;
|
||||
idleTimeoutMs: number;
|
||||
}) => RuntimeThreadBindingLifecycleRecord[];
|
||||
setMaxAgeBySessionKey: (params: {
|
||||
channelId: "discord" | "matrix" | "telegram";
|
||||
channelId: string;
|
||||
targetSessionKey: string;
|
||||
accountId?: string;
|
||||
maxAgeMs: number;
|
||||
}) => RuntimeThreadBindingLifecycleRecord[];
|
||||
};
|
||||
discord: {
|
||||
messageActions: DiscordRuntimeSurface["discordMessageActions"];
|
||||
auditChannelPermissions: DiscordRuntimeSurface["auditDiscordChannelPermissions"];
|
||||
listDirectoryGroupsLive: DiscordRuntimeSurface["listDiscordDirectoryGroupsLive"];
|
||||
listDirectoryPeersLive: DiscordRuntimeSurface["listDiscordDirectoryPeersLive"];
|
||||
probeDiscord: DiscordRuntimeSurface["probeDiscord"];
|
||||
resolveChannelAllowlist: DiscordRuntimeSurface["resolveDiscordChannelAllowlist"];
|
||||
resolveUserAllowlist: DiscordRuntimeSurface["resolveDiscordUserAllowlist"];
|
||||
sendComponentMessage: DiscordRuntimeSurface["sendDiscordComponentMessage"];
|
||||
sendMessageDiscord: DiscordRuntimeSurface["sendMessageDiscord"];
|
||||
sendPollDiscord: DiscordRuntimeSurface["sendPollDiscord"];
|
||||
monitorDiscordProvider: DiscordRuntimeSurface["monitorDiscordProvider"];
|
||||
threadBindings: {
|
||||
getManager: DiscordThreadBindings["getThreadBindingManager"];
|
||||
resolveIdleTimeoutMs: DiscordThreadBindings["resolveThreadBindingIdleTimeoutMs"];
|
||||
resolveInactivityExpiresAt: DiscordThreadBindings["resolveThreadBindingInactivityExpiresAt"];
|
||||
resolveMaxAgeMs: DiscordThreadBindings["resolveThreadBindingMaxAgeMs"];
|
||||
resolveMaxAgeExpiresAt: DiscordThreadBindings["resolveThreadBindingMaxAgeExpiresAt"];
|
||||
setIdleTimeoutBySessionKey: DiscordThreadBindings["setThreadBindingIdleTimeoutBySessionKey"];
|
||||
setMaxAgeBySessionKey: DiscordThreadBindings["setThreadBindingMaxAgeBySessionKey"];
|
||||
unbindBySessionKey: DiscordThreadBindings["unbindThreadBindingsBySessionKey"];
|
||||
};
|
||||
typing: {
|
||||
pulse: DiscordRuntimeSurface["sendTypingDiscord"];
|
||||
start: (params: {
|
||||
channelId: string;
|
||||
accountId?: string;
|
||||
cfg?: ReturnType<typeof import("../../config/config.js").loadConfig>;
|
||||
intervalMs?: number;
|
||||
}) => Promise<{
|
||||
refresh: () => Promise<void>;
|
||||
stop: () => void;
|
||||
}>;
|
||||
};
|
||||
conversationActions: {
|
||||
editMessage: DiscordRuntimeSurface["editMessageDiscord"];
|
||||
deleteMessage: DiscordRuntimeSurface["deleteMessageDiscord"];
|
||||
pinMessage: DiscordRuntimeSurface["pinMessageDiscord"];
|
||||
unpinMessage: DiscordRuntimeSurface["unpinMessageDiscord"];
|
||||
createThread: DiscordRuntimeSurface["createThreadDiscord"];
|
||||
editChannel: DiscordRuntimeSurface["editChannelDiscord"];
|
||||
};
|
||||
};
|
||||
slack: {
|
||||
listDirectoryGroupsLive: typeof import("../../../extensions/slack/runtime-api.js").listSlackDirectoryGroupsLive;
|
||||
listDirectoryPeersLive: typeof import("../../../extensions/slack/runtime-api.js").listSlackDirectoryPeersLive;
|
||||
probeSlack: typeof import("../../../extensions/slack/runtime-api.js").probeSlack;
|
||||
resolveChannelAllowlist: typeof import("../../../extensions/slack/runtime-api.js").resolveSlackChannelAllowlist;
|
||||
resolveUserAllowlist: typeof import("../../../extensions/slack/runtime-api.js").resolveSlackUserAllowlist;
|
||||
sendMessageSlack: typeof import("../../../extensions/slack/runtime-api.js").sendMessageSlack;
|
||||
monitorSlackProvider: typeof import("../../../extensions/slack/runtime-api.js").monitorSlackProvider;
|
||||
handleSlackAction: typeof import("../../../extensions/slack/runtime-api.js").handleSlackAction;
|
||||
};
|
||||
matrix: {
|
||||
threadBindings: {
|
||||
setIdleTimeoutBySessionKey: MatrixThreadBindings["setMatrixThreadBindingIdleTimeoutBySessionKey"];
|
||||
setMaxAgeBySessionKey: MatrixThreadBindings["setMatrixThreadBindingMaxAgeBySessionKey"];
|
||||
};
|
||||
};
|
||||
signal: {
|
||||
probeSignal: typeof import("../../../extensions/signal/runtime-api.js").probeSignal;
|
||||
sendMessageSignal: typeof import("../../../extensions/signal/runtime-api.js").sendMessageSignal;
|
||||
monitorSignalProvider: typeof import("../../../extensions/signal/runtime-api.js").monitorSignalProvider;
|
||||
messageActions: typeof import("../../../extensions/signal/runtime-api.js").signalMessageActions;
|
||||
};
|
||||
line: {
|
||||
listLineAccountIds: typeof import("../../plugin-sdk/line.js").listLineAccountIds;
|
||||
resolveDefaultLineAccountId: typeof import("../../plugin-sdk/line.js").resolveDefaultLineAccountId;
|
||||
resolveLineAccount: typeof import("../../plugin-sdk/line.js").resolveLineAccount;
|
||||
normalizeAccountId: typeof import("../../plugin-sdk/line.js").normalizeAccountId;
|
||||
probeLineBot: typeof import("../../plugin-sdk/line-runtime.js").probeLineBot;
|
||||
sendMessageLine: typeof import("../../plugin-sdk/line-runtime.js").sendMessageLine;
|
||||
pushMessageLine: typeof import("../../plugin-sdk/line-runtime.js").pushMessageLine;
|
||||
pushMessagesLine: typeof import("../../plugin-sdk/line-runtime.js").pushMessagesLine;
|
||||
pushFlexMessage: typeof import("../../plugin-sdk/line-runtime.js").pushFlexMessage;
|
||||
pushTemplateMessage: typeof import("../../plugin-sdk/line-runtime.js").pushTemplateMessage;
|
||||
pushLocationMessage: typeof import("../../plugin-sdk/line-runtime.js").pushLocationMessage;
|
||||
pushTextMessageWithQuickReplies: typeof import("../../plugin-sdk/line-runtime.js").pushTextMessageWithQuickReplies;
|
||||
createQuickReplyItems: typeof import("../../plugin-sdk/line-runtime.js").createQuickReplyItems;
|
||||
buildTemplateMessageFromPayload: typeof import("../../plugin-sdk/line-runtime.js").buildTemplateMessageFromPayload;
|
||||
monitorLineProvider: typeof import("../../plugin-sdk/line-runtime.js").monitorLineProvider;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -333,12 +333,7 @@ export function createPluginRuntimeMock(overrides: DeepPartial<PluginRuntime> =
|
|||
setMaxAgeBySessionKey:
|
||||
vi.fn() as unknown as PluginRuntime["channel"]["threadBindings"]["setMaxAgeBySessionKey"],
|
||||
},
|
||||
discord: {} as PluginRuntime["channel"]["discord"],
|
||||
activity: {} as PluginRuntime["channel"]["activity"],
|
||||
line: {} as PluginRuntime["channel"]["line"],
|
||||
slack: {} as PluginRuntime["channel"]["slack"],
|
||||
matrix: {} as PluginRuntime["channel"]["matrix"],
|
||||
signal: {} as PluginRuntime["channel"]["signal"],
|
||||
},
|
||||
events: {
|
||||
onAgentEvent: vi.fn(() => () => {}) as unknown as PluginRuntime["events"]["onAgentEvent"],
|
||||
|
|
|
|||
|
|
@ -128,7 +128,6 @@ function buildCoreDistEntries(): Record<string, string> {
|
|||
"agents/pi-model-discovery-runtime": "src/agents/pi-model-discovery-runtime.ts",
|
||||
"commands/status.summary.runtime": "src/commands/status.summary.runtime.ts",
|
||||
"plugins/provider-runtime.runtime": "src/plugins/provider-runtime.runtime.ts",
|
||||
"plugins/runtime/runtime-line.contract": "src/plugins/runtime/runtime-line.contract.ts",
|
||||
extensionAPI: "src/extensionAPI.ts",
|
||||
"infra/warning-filter": "src/infra/warning-filter.ts",
|
||||
"telegram/audit": bundledPluginFile("telegram", "src/audit.ts"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue