mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-24 00:11:31 +00:00
Merge branch 'main' into feat/deepseek-provider
This commit is contained in:
@@ -81,22 +81,23 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
return self.html("Not Found", title: "Canvas: 404")
|
||||
}
|
||||
|
||||
// Directory traversal guard: served files must live under the session root.
|
||||
let standardizedRoot = sessionRoot.standardizedFileURL
|
||||
let standardizedFile = fileURL.standardizedFileURL
|
||||
guard standardizedFile.path.hasPrefix(standardizedRoot.path) else {
|
||||
// Resolve symlinks before enforcing the session-root boundary so links inside
|
||||
// the canvas tree cannot escape to arbitrary host files.
|
||||
let resolvedRoot = sessionRoot.resolvingSymlinksInPath().standardizedFileURL
|
||||
let resolvedFile = fileURL.resolvingSymlinksInPath().standardizedFileURL
|
||||
guard self.isFileURL(resolvedFile, withinDirectory: resolvedRoot) else {
|
||||
return self.html("Forbidden", title: "Canvas: 403")
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: standardizedFile)
|
||||
let mime = CanvasScheme.mimeType(forExtension: standardizedFile.pathExtension)
|
||||
let servedPath = standardizedFile.path
|
||||
let data = try Data(contentsOf: resolvedFile)
|
||||
let mime = CanvasScheme.mimeType(forExtension: resolvedFile.pathExtension)
|
||||
let servedPath = resolvedFile.path
|
||||
canvasLogger.debug(
|
||||
"served \(session, privacy: .public)/\(path, privacy: .public) -> \(servedPath, privacy: .public)")
|
||||
return CanvasResponse(mime: mime, data: data)
|
||||
} catch {
|
||||
let failedPath = standardizedFile.path
|
||||
let failedPath = resolvedFile.path
|
||||
let errorText = error.localizedDescription
|
||||
canvasLogger
|
||||
.error(
|
||||
@@ -145,6 +146,11 @@ final class CanvasSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func isFileURL(_ fileURL: URL, withinDirectory rootURL: URL) -> Bool {
|
||||
let rootPath = rootURL.path.hasSuffix("/") ? rootURL.path : rootURL.path + "/"
|
||||
return fileURL.path == rootURL.path || fileURL.path.hasPrefix(rootPath)
|
||||
}
|
||||
|
||||
private func html(_ body: String, title: String = "Canvas") -> CanvasResponse {
|
||||
let html = """
|
||||
<!doctype html>
|
||||
|
||||
@@ -216,6 +216,32 @@ struct LowCoverageHelperTests {
|
||||
#expect(handler._testTextEncodingName(for: "application/octet-stream") == nil)
|
||||
}
|
||||
|
||||
@Test @MainActor func `canvas scheme handler blocks symlink escapes`() throws {
|
||||
let root = FileManager().temporaryDirectory
|
||||
.appendingPathComponent("canvas-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager().removeItem(at: root) }
|
||||
try FileManager().createDirectory(at: root, withIntermediateDirectories: true)
|
||||
|
||||
let session = root.appendingPathComponent("main", isDirectory: true)
|
||||
try FileManager().createDirectory(at: session, withIntermediateDirectories: true)
|
||||
|
||||
let outside = root.deletingLastPathComponent().appendingPathComponent("canvas-secret-\(UUID().uuidString).txt")
|
||||
defer { try? FileManager().removeItem(at: outside) }
|
||||
try "top-secret".write(to: outside, atomically: true, encoding: .utf8)
|
||||
|
||||
let symlink = session.appendingPathComponent("index.html")
|
||||
try FileManager().createSymbolicLink(at: symlink, withDestinationURL: outside)
|
||||
|
||||
let handler = CanvasSchemeHandler(root: root)
|
||||
let url = try #require(CanvasScheme.makeURL(session: "main", path: "index.html"))
|
||||
let response = handler._testResponse(for: url)
|
||||
let body = String(data: response.data, encoding: .utf8) ?? ""
|
||||
|
||||
#expect(response.mime == "text/html")
|
||||
#expect(body.contains("Forbidden"))
|
||||
#expect(!body.contains("top-secret"))
|
||||
}
|
||||
|
||||
@Test @MainActor func `menu context card injector inserts and finds index`() {
|
||||
let injector = MenuContextCardInjector()
|
||||
let menu = NSMenu()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js";
|
||||
import { registerSingleProviderPlugin } from "../test-utils/plugin-registration.js";
|
||||
import amazonBedrockPlugin from "./index.js";
|
||||
|
||||
describe("amazon-bedrock provider plugin", () => {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/bluebubbles";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/bluebubbles";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { bluebubblesPlugin } from "./src/channel.js";
|
||||
import { setBlueBubblesRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "bluebubbles",
|
||||
name: "BlueBubbles",
|
||||
description: "BlueBubbles channel plugin (macOS app)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setBlueBubblesRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: bluebubblesPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: bluebubblesPlugin,
|
||||
setRuntime: setBlueBubblesRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { bluebubblesPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: bluebubblesPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(bluebubblesPlugin);
|
||||
|
||||
@@ -1,13 +1,101 @@
|
||||
export { sendBlueBubblesAttachment } from "./attachments.js";
|
||||
export {
|
||||
addBlueBubblesParticipant,
|
||||
editBlueBubblesMessage,
|
||||
leaveBlueBubblesChat,
|
||||
removeBlueBubblesParticipant,
|
||||
renameBlueBubblesChat,
|
||||
setGroupIconBlueBubbles,
|
||||
unsendBlueBubblesMessage,
|
||||
import { sendBlueBubblesAttachment as sendBlueBubblesAttachmentImpl } from "./attachments.js";
|
||||
import {
|
||||
addBlueBubblesParticipant as addBlueBubblesParticipantImpl,
|
||||
editBlueBubblesMessage as editBlueBubblesMessageImpl,
|
||||
leaveBlueBubblesChat as leaveBlueBubblesChatImpl,
|
||||
removeBlueBubblesParticipant as removeBlueBubblesParticipantImpl,
|
||||
renameBlueBubblesChat as renameBlueBubblesChatImpl,
|
||||
setGroupIconBlueBubbles as setGroupIconBlueBubblesImpl,
|
||||
unsendBlueBubblesMessage as unsendBlueBubblesMessageImpl,
|
||||
} from "./chat.js";
|
||||
export { resolveBlueBubblesMessageId } from "./monitor.js";
|
||||
export { sendBlueBubblesReaction } from "./reactions.js";
|
||||
export { resolveChatGuidForTarget, sendMessageBlueBubbles } from "./send.js";
|
||||
import { resolveBlueBubblesMessageId as resolveBlueBubblesMessageIdImpl } from "./monitor.js";
|
||||
import { sendBlueBubblesReaction as sendBlueBubblesReactionImpl } from "./reactions.js";
|
||||
import {
|
||||
resolveChatGuidForTarget as resolveChatGuidForTargetImpl,
|
||||
sendMessageBlueBubbles as sendMessageBlueBubblesImpl,
|
||||
} from "./send.js";
|
||||
|
||||
type SendBlueBubblesAttachment = typeof import("./attachments.js").sendBlueBubblesAttachment;
|
||||
type AddBlueBubblesParticipant = typeof import("./chat.js").addBlueBubblesParticipant;
|
||||
type EditBlueBubblesMessage = typeof import("./chat.js").editBlueBubblesMessage;
|
||||
type LeaveBlueBubblesChat = typeof import("./chat.js").leaveBlueBubblesChat;
|
||||
type RemoveBlueBubblesParticipant = typeof import("./chat.js").removeBlueBubblesParticipant;
|
||||
type RenameBlueBubblesChat = typeof import("./chat.js").renameBlueBubblesChat;
|
||||
type SetGroupIconBlueBubbles = typeof import("./chat.js").setGroupIconBlueBubbles;
|
||||
type UnsendBlueBubblesMessage = typeof import("./chat.js").unsendBlueBubblesMessage;
|
||||
type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId;
|
||||
type SendBlueBubblesReaction = typeof import("./reactions.js").sendBlueBubblesReaction;
|
||||
type ResolveChatGuidForTarget = typeof import("./send.js").resolveChatGuidForTarget;
|
||||
type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles;
|
||||
|
||||
export function sendBlueBubblesAttachment(
|
||||
...args: Parameters<SendBlueBubblesAttachment>
|
||||
): ReturnType<SendBlueBubblesAttachment> {
|
||||
return sendBlueBubblesAttachmentImpl(...args);
|
||||
}
|
||||
|
||||
export function addBlueBubblesParticipant(
|
||||
...args: Parameters<AddBlueBubblesParticipant>
|
||||
): ReturnType<AddBlueBubblesParticipant> {
|
||||
return addBlueBubblesParticipantImpl(...args);
|
||||
}
|
||||
|
||||
export function editBlueBubblesMessage(
|
||||
...args: Parameters<EditBlueBubblesMessage>
|
||||
): ReturnType<EditBlueBubblesMessage> {
|
||||
return editBlueBubblesMessageImpl(...args);
|
||||
}
|
||||
|
||||
export function leaveBlueBubblesChat(
|
||||
...args: Parameters<LeaveBlueBubblesChat>
|
||||
): ReturnType<LeaveBlueBubblesChat> {
|
||||
return leaveBlueBubblesChatImpl(...args);
|
||||
}
|
||||
|
||||
export function removeBlueBubblesParticipant(
|
||||
...args: Parameters<RemoveBlueBubblesParticipant>
|
||||
): ReturnType<RemoveBlueBubblesParticipant> {
|
||||
return removeBlueBubblesParticipantImpl(...args);
|
||||
}
|
||||
|
||||
export function renameBlueBubblesChat(
|
||||
...args: Parameters<RenameBlueBubblesChat>
|
||||
): ReturnType<RenameBlueBubblesChat> {
|
||||
return renameBlueBubblesChatImpl(...args);
|
||||
}
|
||||
|
||||
export function setGroupIconBlueBubbles(
|
||||
...args: Parameters<SetGroupIconBlueBubbles>
|
||||
): ReturnType<SetGroupIconBlueBubbles> {
|
||||
return setGroupIconBlueBubblesImpl(...args);
|
||||
}
|
||||
|
||||
export function unsendBlueBubblesMessage(
|
||||
...args: Parameters<UnsendBlueBubblesMessage>
|
||||
): ReturnType<UnsendBlueBubblesMessage> {
|
||||
return unsendBlueBubblesMessageImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveBlueBubblesMessageId(
|
||||
...args: Parameters<ResolveBlueBubblesMessageId>
|
||||
): ReturnType<ResolveBlueBubblesMessageId> {
|
||||
return resolveBlueBubblesMessageIdImpl(...args);
|
||||
}
|
||||
|
||||
export function sendBlueBubblesReaction(
|
||||
...args: Parameters<SendBlueBubblesReaction>
|
||||
): ReturnType<SendBlueBubblesReaction> {
|
||||
return sendBlueBubblesReactionImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveChatGuidForTarget(
|
||||
...args: Parameters<ResolveChatGuidForTarget>
|
||||
): ReturnType<ResolveChatGuidForTarget> {
|
||||
return resolveChatGuidForTargetImpl(...args);
|
||||
}
|
||||
|
||||
export function sendMessageBlueBubbles(
|
||||
...args: Parameters<SendMessageBlueBubbles>
|
||||
): ReturnType<SendMessageBlueBubbles> {
|
||||
return sendMessageBlueBubblesImpl(...args);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,57 @@
|
||||
export { sendBlueBubblesMedia } from "./media-send.js";
|
||||
export { resolveBlueBubblesMessageId } from "./monitor.js";
|
||||
export { monitorBlueBubblesProvider, resolveWebhookPathFromConfig } from "./monitor.js";
|
||||
export { type BlueBubblesProbe, probeBlueBubbles } from "./probe.js";
|
||||
export { sendMessageBlueBubbles } from "./send.js";
|
||||
export { blueBubblesSetupWizard } from "./setup-surface.js";
|
||||
import { sendBlueBubblesMedia as sendBlueBubblesMediaImpl } from "./media-send.js";
|
||||
import {
|
||||
monitorBlueBubblesProvider as monitorBlueBubblesProviderImpl,
|
||||
resolveBlueBubblesMessageId as resolveBlueBubblesMessageIdImpl,
|
||||
resolveWebhookPathFromConfig as resolveWebhookPathFromConfigImpl,
|
||||
} from "./monitor.js";
|
||||
import { probeBlueBubbles as probeBlueBubblesImpl } from "./probe.js";
|
||||
import { sendMessageBlueBubbles as sendMessageBlueBubblesImpl } from "./send.js";
|
||||
import { blueBubblesSetupWizard as blueBubblesSetupWizardImpl } from "./setup-surface.js";
|
||||
|
||||
export type { BlueBubblesProbe } from "./probe.js";
|
||||
|
||||
type SendBlueBubblesMedia = typeof import("./media-send.js").sendBlueBubblesMedia;
|
||||
type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId;
|
||||
type MonitorBlueBubblesProvider = typeof import("./monitor.js").monitorBlueBubblesProvider;
|
||||
type ResolveWebhookPathFromConfig = typeof import("./monitor.js").resolveWebhookPathFromConfig;
|
||||
type ProbeBlueBubbles = typeof import("./probe.js").probeBlueBubbles;
|
||||
type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles;
|
||||
type BlueBubblesSetupWizard = typeof import("./setup-surface.js").blueBubblesSetupWizard;
|
||||
|
||||
export function sendBlueBubblesMedia(
|
||||
...args: Parameters<SendBlueBubblesMedia>
|
||||
): ReturnType<SendBlueBubblesMedia> {
|
||||
return sendBlueBubblesMediaImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveBlueBubblesMessageId(
|
||||
...args: Parameters<ResolveBlueBubblesMessageId>
|
||||
): ReturnType<ResolveBlueBubblesMessageId> {
|
||||
return resolveBlueBubblesMessageIdImpl(...args);
|
||||
}
|
||||
|
||||
export function monitorBlueBubblesProvider(
|
||||
...args: Parameters<MonitorBlueBubblesProvider>
|
||||
): ReturnType<MonitorBlueBubblesProvider> {
|
||||
return monitorBlueBubblesProviderImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveWebhookPathFromConfig(
|
||||
...args: Parameters<ResolveWebhookPathFromConfig>
|
||||
): ReturnType<ResolveWebhookPathFromConfig> {
|
||||
return resolveWebhookPathFromConfigImpl(...args);
|
||||
}
|
||||
|
||||
export function probeBlueBubbles(
|
||||
...args: Parameters<ProbeBlueBubbles>
|
||||
): ReturnType<ProbeBlueBubbles> {
|
||||
return probeBlueBubblesImpl(...args);
|
||||
}
|
||||
|
||||
export function sendMessageBlueBubbles(
|
||||
...args: Parameters<SendMessageBlueBubbles>
|
||||
): ReturnType<SendMessageBlueBubbles> {
|
||||
return sendMessageBlueBubblesImpl(...args);
|
||||
}
|
||||
|
||||
export const blueBubblesSetupWizard: BlueBubblesSetupWizard = { ...blueBubblesSetupWizardImpl };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { IncomingMessage } from "node:http";
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/diffs";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createMockServerResponse } from "../../src/test-utils/mock-http-response.js";
|
||||
import { createMockServerResponse } from "../test-utils/mock-http-response.js";
|
||||
import { createTestPluginApi } from "../test-utils/plugin-api.js";
|
||||
import plugin from "./index.js";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IncomingMessage } from "node:http";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { createMockServerResponse } from "../../../src/test-utils/mock-http-response.js";
|
||||
import { createMockServerResponse } from "../../test-utils/mock-http-response.js";
|
||||
import { createDiffsHttpHandler } from "./http.js";
|
||||
import { DiffArtifactStore } from "./store.js";
|
||||
import { createDiffStoreHarness } from "./test-helpers.js";
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { discordPlugin } from "./src/channel.js";
|
||||
import { setDiscordRuntime } from "./src/runtime.js";
|
||||
import { registerDiscordSubagentHooks } from "./src/subagent-hooks.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "discord",
|
||||
name: "Discord",
|
||||
description: "Discord channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setDiscordRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: discordPlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
registerDiscordSubagentHooks(api);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: discordPlugin,
|
||||
setRuntime: setDiscordRuntime,
|
||||
registerFull: registerDiscordSubagentHooks,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { discordSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: discordSetupPlugin };
|
||||
export default defineSetupPluginEntry(discordSetupPlugin);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { fetchDiscord } from "./api.js";
|
||||
import { jsonResponse } from "./test-http-helpers.js";
|
||||
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
export { discordSetupWizard } from "./setup-surface.js";
|
||||
import { discordSetupWizard as discordSetupWizardImpl } from "./setup-surface.js";
|
||||
|
||||
type DiscordSetupWizard = typeof import("./setup-surface.js").discordSetupWizard;
|
||||
|
||||
export const discordSetupWizard: DiscordSetupWizard = { ...discordSetupWizardImpl };
|
||||
|
||||
@@ -10,11 +10,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { normalizeMessageChannel } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import {
|
||||
buildOutboundBaseSessionKey,
|
||||
normalizeOutboundThreadId,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
buildTokenChannelStatusSummary,
|
||||
@@ -31,6 +27,7 @@ import {
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/discord";
|
||||
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
listDiscordAccountIds,
|
||||
resolveDiscordAccount,
|
||||
@@ -51,7 +48,7 @@ import { resolveDiscordUserAllowlist } from "./resolve-users.js";
|
||||
import { getDiscordRuntime } from "./runtime.js";
|
||||
import { fetchChannelPermissionsDiscord } from "./send.js";
|
||||
import { discordSetupAdapter } from "./setup-core.js";
|
||||
import { createDiscordPluginBase } from "./shared.js";
|
||||
import { createDiscordPluginBase, discordConfigAccessors } from "./shared.js";
|
||||
import { collectDiscordStatusIssues } from "./status-issues.js";
|
||||
import { parseDiscordTarget } from "./targets.js";
|
||||
import { DiscordUiContainer } from "./ui.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { countLines, hasBalancedFences } from "../../../src/test-utils/chunk-test-helpers.js";
|
||||
import { countLines, hasBalancedFences } from "../../test-utils/chunk-test-helpers.js";
|
||||
import { chunkDiscordText, chunkDiscordTextWithMode } from "./chunk.js";
|
||||
|
||||
describe("chunkDiscordText", () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ChannelType, type Guild } from "@buape/carbon";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { typedCases } from "../../../src/test-utils/typed-cases.js";
|
||||
import { typedCases } from "../../test-utils/typed-cases.js";
|
||||
import {
|
||||
allowListMatches,
|
||||
buildDiscordMediaPayload,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { resolveDiscordChannelAllowlist } from "./resolve-channels.js";
|
||||
import { jsonResponse, urlToString } from "./test-http-helpers.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { resolveDiscordUserAllowlist } from "./resolve-users.js";
|
||||
import { jsonResponse, urlToString } from "./test-http-helpers.js";
|
||||
|
||||
|
||||
@@ -100,28 +100,20 @@ async function resolveDiscordGroupAllowlist(params: {
|
||||
});
|
||||
}
|
||||
|
||||
export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase(async () => ({
|
||||
discordSetupWizard: {
|
||||
dmPolicy: {
|
||||
promptAllowFrom: promptDiscordAllowFrom,
|
||||
},
|
||||
groupAccess: {
|
||||
resolveAllowlist: async ({ cfg, accountId, credentialValues, entries }) =>
|
||||
await resolveDiscordGroupAllowlist({
|
||||
cfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
}),
|
||||
},
|
||||
allowFrom: {
|
||||
resolveEntries: async ({ cfg, accountId, credentialValues, entries }) =>
|
||||
await resolveDiscordAllowFromEntries({
|
||||
token:
|
||||
resolveDiscordAccount({ cfg, accountId }).token ||
|
||||
(typeof credentialValues.token === "string" ? credentialValues.token : ""),
|
||||
entries,
|
||||
}),
|
||||
},
|
||||
} as ChannelSetupWizard,
|
||||
}));
|
||||
export const discordSetupWizard: ChannelSetupWizard = createDiscordSetupWizardBase({
|
||||
promptAllowFrom: promptDiscordAllowFrom,
|
||||
resolveAllowFromEntries: async ({ cfg, accountId, credentialValues, entries }) =>
|
||||
await resolveDiscordAllowFromEntries({
|
||||
token:
|
||||
resolveDiscordAccount({ cfg, accountId }).token ||
|
||||
(typeof credentialValues.token === "string" ? credentialValues.token : ""),
|
||||
entries,
|
||||
}),
|
||||
resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries }) =>
|
||||
await resolveDiscordGroupAllowlist({
|
||||
cfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
export { DiscordVoiceManager, DiscordVoiceReadyListener } from "./manager.js";
|
||||
import {
|
||||
DiscordVoiceManager as DiscordVoiceManagerImpl,
|
||||
DiscordVoiceReadyListener as DiscordVoiceReadyListenerImpl,
|
||||
} from "./manager.js";
|
||||
|
||||
export class DiscordVoiceManager extends DiscordVoiceManagerImpl {}
|
||||
|
||||
export class DiscordVoiceReadyListener extends DiscordVoiceReadyListenerImpl {}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/feishu";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { registerFeishuBitableTools } from "./src/bitable.js";
|
||||
import { feishuPlugin } from "./src/channel.js";
|
||||
import { registerFeishuChatTools } from "./src/chat.js";
|
||||
@@ -46,17 +45,13 @@ export {
|
||||
} from "./src/mention.js";
|
||||
export { feishuPlugin } from "./src/channel.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "feishu",
|
||||
name: "Feishu",
|
||||
description: "Feishu/Lark channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setFeishuRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: feishuPlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
plugin: feishuPlugin,
|
||||
setRuntime: setFeishuRuntime,
|
||||
registerFull(api) {
|
||||
registerFeishuSubagentHooks(api);
|
||||
registerFeishuDocTools(api);
|
||||
registerFeishuChatTools(api);
|
||||
@@ -65,6 +60,4 @@ const plugin = {
|
||||
registerFeishuPermTools(api);
|
||||
registerFeishuBitableTools(api);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { feishuPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: feishuPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(feishuPlugin);
|
||||
|
||||
@@ -1,7 +1,129 @@
|
||||
export { listFeishuDirectoryGroupsLive, listFeishuDirectoryPeersLive } from "./directory.js";
|
||||
export { feishuOutbound } from "./outbound.js";
|
||||
export { createPinFeishu, listPinsFeishu, removePinFeishu } from "./pins.js";
|
||||
export { probeFeishu } from "./probe.js";
|
||||
export { addReactionFeishu, listReactionsFeishu, removeReactionFeishu } from "./reactions.js";
|
||||
export { getChatInfo, getChatMembers, getFeishuMemberInfo } from "./chat.js";
|
||||
export { editMessageFeishu, getMessageFeishu, sendCardFeishu, sendMessageFeishu } from "./send.js";
|
||||
import {
|
||||
getChatInfo as getChatInfoImpl,
|
||||
getChatMembers as getChatMembersImpl,
|
||||
getFeishuMemberInfo as getFeishuMemberInfoImpl,
|
||||
} from "./chat.js";
|
||||
import {
|
||||
listFeishuDirectoryGroupsLive as listFeishuDirectoryGroupsLiveImpl,
|
||||
listFeishuDirectoryPeersLive as listFeishuDirectoryPeersLiveImpl,
|
||||
} from "./directory.js";
|
||||
import { feishuOutbound as feishuOutboundImpl } from "./outbound.js";
|
||||
import {
|
||||
createPinFeishu as createPinFeishuImpl,
|
||||
listPinsFeishu as listPinsFeishuImpl,
|
||||
removePinFeishu as removePinFeishuImpl,
|
||||
} from "./pins.js";
|
||||
import { probeFeishu as probeFeishuImpl } from "./probe.js";
|
||||
import {
|
||||
addReactionFeishu as addReactionFeishuImpl,
|
||||
listReactionsFeishu as listReactionsFeishuImpl,
|
||||
removeReactionFeishu as removeReactionFeishuImpl,
|
||||
} from "./reactions.js";
|
||||
import {
|
||||
editMessageFeishu as editMessageFeishuImpl,
|
||||
getMessageFeishu as getMessageFeishuImpl,
|
||||
sendCardFeishu as sendCardFeishuImpl,
|
||||
sendMessageFeishu as sendMessageFeishuImpl,
|
||||
} from "./send.js";
|
||||
|
||||
type ListFeishuDirectoryGroupsLive = typeof import("./directory.js").listFeishuDirectoryGroupsLive;
|
||||
type ListFeishuDirectoryPeersLive = typeof import("./directory.js").listFeishuDirectoryPeersLive;
|
||||
type FeishuOutbound = typeof import("./outbound.js").feishuOutbound;
|
||||
type CreatePinFeishu = typeof import("./pins.js").createPinFeishu;
|
||||
type ListPinsFeishu = typeof import("./pins.js").listPinsFeishu;
|
||||
type RemovePinFeishu = typeof import("./pins.js").removePinFeishu;
|
||||
type ProbeFeishu = typeof import("./probe.js").probeFeishu;
|
||||
type AddReactionFeishu = typeof import("./reactions.js").addReactionFeishu;
|
||||
type ListReactionsFeishu = typeof import("./reactions.js").listReactionsFeishu;
|
||||
type RemoveReactionFeishu = typeof import("./reactions.js").removeReactionFeishu;
|
||||
type GetChatInfo = typeof import("./chat.js").getChatInfo;
|
||||
type GetChatMembers = typeof import("./chat.js").getChatMembers;
|
||||
type GetFeishuMemberInfo = typeof import("./chat.js").getFeishuMemberInfo;
|
||||
type EditMessageFeishu = typeof import("./send.js").editMessageFeishu;
|
||||
type GetMessageFeishu = typeof import("./send.js").getMessageFeishu;
|
||||
type SendCardFeishu = typeof import("./send.js").sendCardFeishu;
|
||||
type SendMessageFeishu = typeof import("./send.js").sendMessageFeishu;
|
||||
|
||||
export function listFeishuDirectoryGroupsLive(
|
||||
...args: Parameters<ListFeishuDirectoryGroupsLive>
|
||||
): ReturnType<ListFeishuDirectoryGroupsLive> {
|
||||
return listFeishuDirectoryGroupsLiveImpl(...args);
|
||||
}
|
||||
|
||||
export function listFeishuDirectoryPeersLive(
|
||||
...args: Parameters<ListFeishuDirectoryPeersLive>
|
||||
): ReturnType<ListFeishuDirectoryPeersLive> {
|
||||
return listFeishuDirectoryPeersLiveImpl(...args);
|
||||
}
|
||||
|
||||
export const feishuOutbound: FeishuOutbound = { ...feishuOutboundImpl };
|
||||
|
||||
export function createPinFeishu(...args: Parameters<CreatePinFeishu>): ReturnType<CreatePinFeishu> {
|
||||
return createPinFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function listPinsFeishu(...args: Parameters<ListPinsFeishu>): ReturnType<ListPinsFeishu> {
|
||||
return listPinsFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function removePinFeishu(...args: Parameters<RemovePinFeishu>): ReturnType<RemovePinFeishu> {
|
||||
return removePinFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function probeFeishu(...args: Parameters<ProbeFeishu>): ReturnType<ProbeFeishu> {
|
||||
return probeFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function addReactionFeishu(
|
||||
...args: Parameters<AddReactionFeishu>
|
||||
): ReturnType<AddReactionFeishu> {
|
||||
return addReactionFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function listReactionsFeishu(
|
||||
...args: Parameters<ListReactionsFeishu>
|
||||
): ReturnType<ListReactionsFeishu> {
|
||||
return listReactionsFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function removeReactionFeishu(
|
||||
...args: Parameters<RemoveReactionFeishu>
|
||||
): ReturnType<RemoveReactionFeishu> {
|
||||
return removeReactionFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function getChatInfo(...args: Parameters<GetChatInfo>): ReturnType<GetChatInfo> {
|
||||
return getChatInfoImpl(...args);
|
||||
}
|
||||
|
||||
export function getChatMembers(...args: Parameters<GetChatMembers>): ReturnType<GetChatMembers> {
|
||||
return getChatMembersImpl(...args);
|
||||
}
|
||||
|
||||
export function getFeishuMemberInfo(
|
||||
...args: Parameters<GetFeishuMemberInfo>
|
||||
): ReturnType<GetFeishuMemberInfo> {
|
||||
return getFeishuMemberInfoImpl(...args);
|
||||
}
|
||||
|
||||
export function editMessageFeishu(
|
||||
...args: Parameters<EditMessageFeishu>
|
||||
): ReturnType<EditMessageFeishu> {
|
||||
return editMessageFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function getMessageFeishu(
|
||||
...args: Parameters<GetMessageFeishu>
|
||||
): ReturnType<GetMessageFeishu> {
|
||||
return getMessageFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function sendCardFeishu(...args: Parameters<SendCardFeishu>): ReturnType<SendCardFeishu> {
|
||||
return sendCardFeishuImpl(...args);
|
||||
}
|
||||
|
||||
export function sendMessageFeishu(
|
||||
...args: Parameters<SendMessageFeishu>
|
||||
): ReturnType<SendMessageFeishu> {
|
||||
return sendMessageFeishuImpl(...args);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createProviderUsageFetch,
|
||||
makeResponse,
|
||||
} from "../../src/test-utils/provider-usage-fetch.js";
|
||||
import { createProviderUsageFetch, makeResponse } from "../test-utils/provider-usage-fetch.js";
|
||||
import { fetchCopilotUsage } from "./usage.js";
|
||||
|
||||
describe("fetchCopilotUsage", () => {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/googlechat";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/googlechat";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { googlechatPlugin } from "./src/channel.js";
|
||||
import { setGoogleChatRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "googlechat",
|
||||
name: "Google Chat",
|
||||
description: "OpenClaw Google Chat channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setGoogleChatRuntime(api.runtime);
|
||||
api.registerChannel(googlechatPlugin);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: googlechatPlugin,
|
||||
setRuntime: setGoogleChatRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { googlechatPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: googlechatPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(googlechatPlugin);
|
||||
|
||||
@@ -1,2 +1,43 @@
|
||||
export { probeGoogleChat, sendGoogleChatMessage, uploadGoogleChatAttachment } from "./api.js";
|
||||
export { resolveGoogleChatWebhookPath, startGoogleChatMonitor } from "./monitor.js";
|
||||
import {
|
||||
probeGoogleChat as probeGoogleChatImpl,
|
||||
sendGoogleChatMessage as sendGoogleChatMessageImpl,
|
||||
uploadGoogleChatAttachment as uploadGoogleChatAttachmentImpl,
|
||||
} from "./api.js";
|
||||
import {
|
||||
resolveGoogleChatWebhookPath as resolveGoogleChatWebhookPathImpl,
|
||||
startGoogleChatMonitor as startGoogleChatMonitorImpl,
|
||||
} from "./monitor.js";
|
||||
|
||||
type ProbeGoogleChat = typeof import("./api.js").probeGoogleChat;
|
||||
type SendGoogleChatMessage = typeof import("./api.js").sendGoogleChatMessage;
|
||||
type UploadGoogleChatAttachment = typeof import("./api.js").uploadGoogleChatAttachment;
|
||||
type ResolveGoogleChatWebhookPath = typeof import("./monitor.js").resolveGoogleChatWebhookPath;
|
||||
type StartGoogleChatMonitor = typeof import("./monitor.js").startGoogleChatMonitor;
|
||||
|
||||
export function probeGoogleChat(...args: Parameters<ProbeGoogleChat>): ReturnType<ProbeGoogleChat> {
|
||||
return probeGoogleChatImpl(...args);
|
||||
}
|
||||
|
||||
export function sendGoogleChatMessage(
|
||||
...args: Parameters<SendGoogleChatMessage>
|
||||
): ReturnType<SendGoogleChatMessage> {
|
||||
return sendGoogleChatMessageImpl(...args);
|
||||
}
|
||||
|
||||
export function uploadGoogleChatAttachment(
|
||||
...args: Parameters<UploadGoogleChatAttachment>
|
||||
): ReturnType<UploadGoogleChatAttachment> {
|
||||
return uploadGoogleChatAttachmentImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveGoogleChatWebhookPath(
|
||||
...args: Parameters<ResolveGoogleChatWebhookPath>
|
||||
): ReturnType<ResolveGoogleChatWebhookPath> {
|
||||
return resolveGoogleChatWebhookPathImpl(...args);
|
||||
}
|
||||
|
||||
export function startGoogleChatMonitor(
|
||||
...args: Parameters<StartGoogleChatMonitor>
|
||||
): ReturnType<StartGoogleChatMonitor> {
|
||||
return startGoogleChatMonitorImpl(...args);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/googlech
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
|
||||
import { createMockServerResponse } from "../../../src/test-utils/mock-http-response.js";
|
||||
import { createMockServerResponse } from "../../test-utils/mock-http-response.js";
|
||||
import type { ResolvedGoogleChatAccount } from "./accounts.js";
|
||||
import { verifyGoogleChatRequest } from "./auth.js";
|
||||
import { handleGoogleChatWebhookRequest, registerGoogleChatWebhookTarget } from "./monitor.js";
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { imessagePlugin } from "./src/channel.js";
|
||||
import { setIMessageRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "imessage",
|
||||
name: "iMessage",
|
||||
description: "iMessage channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setIMessageRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: imessagePlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: imessagePlugin,
|
||||
setRuntime: setIMessageRuntime,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { imessageSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: imessageSetupPlugin };
|
||||
export default defineSetupPluginEntry(imessageSetupPlugin);
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk/irc";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/irc";
|
||||
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { ircPlugin } from "./src/channel.js";
|
||||
import { setIrcRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "irc",
|
||||
name: "IRC",
|
||||
description: "IRC channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setIrcRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: ircPlugin as ChannelPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: ircPlugin as ChannelPlugin,
|
||||
setRuntime: setIrcRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { ircPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: ircPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(ircPlugin);
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/line";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/line";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { registerLineCardCommand } from "./src/card-command.js";
|
||||
import { linePlugin } from "./src/channel.js";
|
||||
import { setLineRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "line",
|
||||
name: "LINE",
|
||||
description: "LINE Messaging API channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setLineRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: linePlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
registerLineCardCommand(api);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: linePlugin,
|
||||
setRuntime: setLineRuntime,
|
||||
registerFull: registerLineCardCommand,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { lineSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default {
|
||||
plugin: lineSetupPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(lineSetupPlugin);
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/matrix";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/matrix";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { matrixPlugin } from "./src/channel.js";
|
||||
import { setMatrixRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "matrix",
|
||||
name: "Matrix",
|
||||
description: "Matrix channel plugin (matrix-js-sdk)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setMatrixRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: matrixPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
description: "Matrix channel plugin",
|
||||
plugin: matrixPlugin,
|
||||
setRuntime: setMatrixRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { matrixPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: matrixPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(matrixPlugin);
|
||||
|
||||
@@ -1,6 +1,55 @@
|
||||
export { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js";
|
||||
export { resolveMatrixAuth } from "./matrix/client.js";
|
||||
export { probeMatrix } from "./matrix/probe.js";
|
||||
export { sendMessageMatrix } from "./matrix/send.js";
|
||||
export { resolveMatrixTargets } from "./resolve-targets.js";
|
||||
export { matrixOutbound } from "./outbound.js";
|
||||
import {
|
||||
listMatrixDirectoryGroupsLive as listMatrixDirectoryGroupsLiveImpl,
|
||||
listMatrixDirectoryPeersLive as listMatrixDirectoryPeersLiveImpl,
|
||||
} from "./directory-live.js";
|
||||
import { resolveMatrixAuth as resolveMatrixAuthImpl } from "./matrix/client.js";
|
||||
import { probeMatrix as probeMatrixImpl } from "./matrix/probe.js";
|
||||
import { sendMessageMatrix as sendMessageMatrixImpl } from "./matrix/send.js";
|
||||
import { matrixOutbound as matrixOutboundImpl } from "./outbound.js";
|
||||
import { resolveMatrixTargets as resolveMatrixTargetsImpl } from "./resolve-targets.js";
|
||||
|
||||
type ListMatrixDirectoryGroupsLive =
|
||||
typeof import("./directory-live.js").listMatrixDirectoryGroupsLive;
|
||||
type ListMatrixDirectoryPeersLive =
|
||||
typeof import("./directory-live.js").listMatrixDirectoryPeersLive;
|
||||
type ResolveMatrixAuth = typeof import("./matrix/client.js").resolveMatrixAuth;
|
||||
type ProbeMatrix = typeof import("./matrix/probe.js").probeMatrix;
|
||||
type SendMessageMatrix = typeof import("./matrix/send.js").sendMessageMatrix;
|
||||
type ResolveMatrixTargets = typeof import("./resolve-targets.js").resolveMatrixTargets;
|
||||
type MatrixOutbound = typeof import("./outbound.js").matrixOutbound;
|
||||
|
||||
export function listMatrixDirectoryGroupsLive(
|
||||
...args: Parameters<ListMatrixDirectoryGroupsLive>
|
||||
): ReturnType<ListMatrixDirectoryGroupsLive> {
|
||||
return listMatrixDirectoryGroupsLiveImpl(...args);
|
||||
}
|
||||
|
||||
export function listMatrixDirectoryPeersLive(
|
||||
...args: Parameters<ListMatrixDirectoryPeersLive>
|
||||
): ReturnType<ListMatrixDirectoryPeersLive> {
|
||||
return listMatrixDirectoryPeersLiveImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveMatrixAuth(
|
||||
...args: Parameters<ResolveMatrixAuth>
|
||||
): ReturnType<ResolveMatrixAuth> {
|
||||
return resolveMatrixAuthImpl(...args);
|
||||
}
|
||||
|
||||
export function probeMatrix(...args: Parameters<ProbeMatrix>): ReturnType<ProbeMatrix> {
|
||||
return probeMatrixImpl(...args);
|
||||
}
|
||||
|
||||
export function sendMessageMatrix(
|
||||
...args: Parameters<SendMessageMatrix>
|
||||
): ReturnType<SendMessageMatrix> {
|
||||
return sendMessageMatrixImpl(...args);
|
||||
}
|
||||
|
||||
export function resolveMatrixTargets(
|
||||
...args: Parameters<ResolveMatrixTargets>
|
||||
): ReturnType<ResolveMatrixTargets> {
|
||||
return resolveMatrixTargetsImpl(...args);
|
||||
}
|
||||
|
||||
export const matrixOutbound: MatrixOutbound = { ...matrixOutboundImpl };
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/mattermost";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/mattermost";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { mattermostPlugin } from "./src/channel.js";
|
||||
import { getSlashCommandState, registerSlashCommandRoute } from "./src/mattermost/slash-state.js";
|
||||
import { registerSlashCommandRoute } from "./src/mattermost/slash-state.js";
|
||||
import { setMattermostRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "mattermost",
|
||||
name: "Mattermost",
|
||||
description: "Mattermost channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setMattermostRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: mattermostPlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the HTTP route for slash command callbacks.
|
||||
// The actual command registration with MM happens in the monitor
|
||||
// after the bot connects and we know the team ID.
|
||||
plugin: mattermostPlugin,
|
||||
setRuntime: setMattermostRuntime,
|
||||
registerFull(api) {
|
||||
// Actual slash-command registration happens after the monitor connects and
|
||||
// knows the team id; the route itself can be wired here.
|
||||
registerSlashCommandRoute(api);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { mattermostPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: mattermostPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(mattermostPlugin);
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/msteams";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/msteams";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { msteamsPlugin } from "./src/channel.js";
|
||||
import { setMSTeamsRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "msteams",
|
||||
name: "Microsoft Teams",
|
||||
description: "Microsoft Teams channel plugin (Bot Framework)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setMSTeamsRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: msteamsPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: msteamsPlugin,
|
||||
setRuntime: setMSTeamsRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { msteamsPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: msteamsPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(msteamsPlugin);
|
||||
|
||||
@@ -1,4 +1,49 @@
|
||||
export { listMSTeamsDirectoryGroupsLive, listMSTeamsDirectoryPeersLive } from "./directory-live.js";
|
||||
export { msteamsOutbound } from "./outbound.js";
|
||||
export { probeMSTeams } from "./probe.js";
|
||||
export { sendAdaptiveCardMSTeams, sendMessageMSTeams } from "./send.js";
|
||||
import {
|
||||
listMSTeamsDirectoryGroupsLive as listMSTeamsDirectoryGroupsLiveImpl,
|
||||
listMSTeamsDirectoryPeersLive as listMSTeamsDirectoryPeersLiveImpl,
|
||||
} from "./directory-live.js";
|
||||
import { msteamsOutbound as msteamsOutboundImpl } from "./outbound.js";
|
||||
import { probeMSTeams as probeMSTeamsImpl } from "./probe.js";
|
||||
import {
|
||||
sendAdaptiveCardMSTeams as sendAdaptiveCardMSTeamsImpl,
|
||||
sendMessageMSTeams as sendMessageMSTeamsImpl,
|
||||
} from "./send.js";
|
||||
|
||||
type ListMSTeamsDirectoryGroupsLive =
|
||||
typeof import("./directory-live.js").listMSTeamsDirectoryGroupsLive;
|
||||
type ListMSTeamsDirectoryPeersLive =
|
||||
typeof import("./directory-live.js").listMSTeamsDirectoryPeersLive;
|
||||
type MSTeamsOutbound = typeof import("./outbound.js").msteamsOutbound;
|
||||
type ProbeMSTeams = typeof import("./probe.js").probeMSTeams;
|
||||
type SendAdaptiveCardMSTeams = typeof import("./send.js").sendAdaptiveCardMSTeams;
|
||||
type SendMessageMSTeams = typeof import("./send.js").sendMessageMSTeams;
|
||||
|
||||
export function listMSTeamsDirectoryGroupsLive(
|
||||
...args: Parameters<ListMSTeamsDirectoryGroupsLive>
|
||||
): ReturnType<ListMSTeamsDirectoryGroupsLive> {
|
||||
return listMSTeamsDirectoryGroupsLiveImpl(...args);
|
||||
}
|
||||
|
||||
export function listMSTeamsDirectoryPeersLive(
|
||||
...args: Parameters<ListMSTeamsDirectoryPeersLive>
|
||||
): ReturnType<ListMSTeamsDirectoryPeersLive> {
|
||||
return listMSTeamsDirectoryPeersLiveImpl(...args);
|
||||
}
|
||||
|
||||
export const msteamsOutbound: MSTeamsOutbound = { ...msteamsOutboundImpl };
|
||||
|
||||
export function probeMSTeams(...args: Parameters<ProbeMSTeams>): ReturnType<ProbeMSTeams> {
|
||||
return probeMSTeamsImpl(...args);
|
||||
}
|
||||
|
||||
export function sendAdaptiveCardMSTeams(
|
||||
...args: Parameters<SendAdaptiveCardMSTeams>
|
||||
): ReturnType<SendAdaptiveCardMSTeams> {
|
||||
return sendAdaptiveCardMSTeamsImpl(...args);
|
||||
}
|
||||
|
||||
export function sendMessageMSTeams(
|
||||
...args: Parameters<SendMessageMSTeams>
|
||||
): ReturnType<SendMessageMSTeams> {
|
||||
return sendMessageMSTeamsImpl(...args);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { uploadToOneDrive, uploadToSharePoint } from "./graph-upload.js";
|
||||
|
||||
describe("graph upload helpers", () => {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/nextcloud-talk";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/nextcloud-talk";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { nextcloudTalkPlugin } from "./src/channel.js";
|
||||
import { setNextcloudTalkRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "nextcloud-talk",
|
||||
name: "Nextcloud Talk",
|
||||
description: "Nextcloud Talk channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setNextcloudTalkRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: nextcloudTalkPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: nextcloudTalkPlugin,
|
||||
setRuntime: setNextcloudTalkRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { nextcloudTalkPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: nextcloudTalkPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(nextcloudTalkPlugin);
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/nostr";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/nostr";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { nostrPlugin } from "./src/channel.js";
|
||||
import type { NostrProfile } from "./src/config-schema.js";
|
||||
import { createNostrProfileHttpHandler } from "./src/nostr-profile-http.js";
|
||||
import { setNostrRuntime, getNostrRuntime } from "./src/runtime.js";
|
||||
import { getNostrRuntime, setNostrRuntime } from "./src/runtime.js";
|
||||
import { resolveNostrAccount } from "./src/types.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "nostr",
|
||||
name: "Nostr",
|
||||
description: "Nostr DM channel plugin via NIP-04",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setNostrRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: nostrPlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register HTTP handler for profile management
|
||||
plugin: nostrPlugin,
|
||||
setRuntime: setNostrRuntime,
|
||||
registerFull(api) {
|
||||
const httpHandler = createNostrProfileHttpHandler({
|
||||
getConfigProfile: (accountId: string) => {
|
||||
const runtime = getNostrRuntime();
|
||||
@@ -30,23 +23,18 @@ const plugin = {
|
||||
const runtime = getNostrRuntime();
|
||||
const cfg = runtime.config.loadConfig();
|
||||
|
||||
// Build the config patch for channels.nostr.profile
|
||||
const channels = (cfg.channels ?? {}) as Record<string, unknown>;
|
||||
const nostrConfig = (channels.nostr ?? {}) as Record<string, unknown>;
|
||||
|
||||
const updatedNostrConfig = {
|
||||
...nostrConfig,
|
||||
profile,
|
||||
};
|
||||
|
||||
const updatedChannels = {
|
||||
...channels,
|
||||
nostr: updatedNostrConfig,
|
||||
};
|
||||
|
||||
await runtime.config.writeConfigFile({
|
||||
...cfg,
|
||||
channels: updatedChannels,
|
||||
channels: {
|
||||
...channels,
|
||||
nostr: {
|
||||
...nostrConfig,
|
||||
profile,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
getAccountInfo: (accountId: string) => {
|
||||
@@ -71,6 +59,4 @@ const plugin = {
|
||||
handler: httpHandler,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { nostrPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: nostrPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(nostrPlugin);
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { signalPlugin } from "./src/channel.js";
|
||||
import { setSignalRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "signal",
|
||||
name: "Signal",
|
||||
description: "Signal channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setSignalRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: signalPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: signalPlugin,
|
||||
setRuntime: setSignalRuntime,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { signalSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: signalSetupPlugin };
|
||||
export default defineSetupPluginEntry(signalSetupPlugin);
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
export { signalSetupWizard } from "./setup-surface.js";
|
||||
import { signalSetupWizard as signalSetupWizardImpl } from "./setup-surface.js";
|
||||
|
||||
type SignalSetupWizard = typeof import("./setup-surface.js").signalSetupWizard;
|
||||
|
||||
export const signalSetupWizard: SignalSetupWizard = { ...signalSetupWizardImpl };
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { slackPlugin } from "./src/channel.js";
|
||||
import { setSlackRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "slack",
|
||||
name: "Slack",
|
||||
description: "Slack channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setSlackRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: slackPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: slackPlugin,
|
||||
setRuntime: setSlackRuntime,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { slackSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: slackSetupPlugin };
|
||||
export default defineSetupPluginEntry(slackSetupPlugin);
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
export { slackSetupWizard } from "./setup-surface.js";
|
||||
import { slackSetupWizard as slackSetupWizardImpl } from "./setup-surface.js";
|
||||
|
||||
type SlackSetupWizard = typeof import("./setup-surface.js").slackSetupWizard;
|
||||
|
||||
export const slackSetupWizard: SlackSetupWizard = { ...slackSetupWizardImpl };
|
||||
|
||||
@@ -8,10 +8,7 @@ import {
|
||||
collectOpenProviderGroupPolicyWarnings,
|
||||
} from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import {
|
||||
buildOutboundBaseSessionKey,
|
||||
normalizeOutboundThreadId,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core";
|
||||
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
@@ -28,6 +25,7 @@ import {
|
||||
type ChannelPlugin,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/slack";
|
||||
import type { SlackActionContext } from "../../../src/agents/tools/slack-actions.js";
|
||||
import { createSlackActions } from "../../../src/channels/plugins/slack.actions.js";
|
||||
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
||||
import {
|
||||
@@ -489,7 +487,11 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount> = {
|
||||
},
|
||||
actions: createSlackActions(SLACK_CHANNEL, {
|
||||
invoke: async (action, cfg, toolContext) =>
|
||||
await getSlackRuntime().channel.slack.handleSlackAction(action, cfg, toolContext),
|
||||
await getSlackRuntime().channel.slack.handleSlackAction(
|
||||
action,
|
||||
cfg as OpenClawConfig,
|
||||
toolContext as SlackActionContext | undefined,
|
||||
),
|
||||
}),
|
||||
setup: slackSetupAdapter,
|
||||
outbound: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as mediaFetch from "../../../../src/media/fetch.js";
|
||||
import type { SavedMedia } from "../../../../src/media/store.js";
|
||||
import * as mediaStore from "../../../../src/media/store.js";
|
||||
import { mockPinnedHostnameResolution } from "../../../../src/test-helpers/ssrf.js";
|
||||
import { type FetchMock, withFetchPreconnect } from "../../../../src/test-utils/fetch-mock.js";
|
||||
import { type FetchMock, withFetchPreconnect } from "../../../test-utils/fetch-mock.js";
|
||||
import {
|
||||
fetchWithSlackAuth,
|
||||
resolveSlackAttachmentContent,
|
||||
|
||||
@@ -130,27 +130,19 @@ async function resolveSlackGroupAllowlist(params: {
|
||||
return keys;
|
||||
}
|
||||
|
||||
export const slackSetupWizard: ChannelSetupWizard = createSlackSetupWizardBase(async () => ({
|
||||
slackSetupWizard: {
|
||||
dmPolicy: {
|
||||
promptAllowFrom: promptSlackAllowFrom,
|
||||
},
|
||||
allowFrom: {
|
||||
resolveEntries: async ({ credentialValues, entries }) =>
|
||||
await resolveSlackAllowFromEntries({
|
||||
token: credentialValues.botToken,
|
||||
entries,
|
||||
}),
|
||||
},
|
||||
groupAccess: {
|
||||
resolveAllowlist: async ({ cfg, accountId, credentialValues, entries, prompter }) =>
|
||||
await resolveSlackGroupAllowlist({
|
||||
cfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
prompter,
|
||||
}),
|
||||
},
|
||||
} as ChannelSetupWizard,
|
||||
}));
|
||||
export const slackSetupWizard: ChannelSetupWizard = createSlackSetupWizardBase({
|
||||
promptAllowFrom: promptSlackAllowFrom,
|
||||
resolveAllowFromEntries: async ({ credentialValues, entries }) =>
|
||||
await resolveSlackAllowFromEntries({
|
||||
token: credentialValues.botToken,
|
||||
entries,
|
||||
}),
|
||||
resolveGroupAllowlist: async ({ cfg, accountId, credentialValues, entries, prompter }) =>
|
||||
await resolveSlackGroupAllowlist({
|
||||
cfg,
|
||||
accountId,
|
||||
credentialValues,
|
||||
entries,
|
||||
prompter,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/synology-chat";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/synology-chat";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { synologyChatPlugin } from "./src/channel.js";
|
||||
import { setSynologyRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "synology-chat",
|
||||
name: "Synology Chat",
|
||||
description: "Native Synology Chat channel plugin for OpenClaw",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setSynologyRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: synologyChatPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: synologyChatPlugin,
|
||||
setRuntime: setSynologyRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { synologyChatPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: synologyChatPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(synologyChatPlugin);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawPluginCommandDefinition } from "../../src/plugins/types.js";
|
||||
import type { OpenClawPluginCommandDefinition } from "../test-utils/plugin-command.js";
|
||||
import { createPluginRuntimeMock } from "../test-utils/plugin-runtime-mock.js";
|
||||
import register from "./index.js";
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import type { ChannelPlugin, OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { telegramPlugin } from "./src/channel.js";
|
||||
import { setTelegramRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "telegram",
|
||||
name: "Telegram",
|
||||
description: "Telegram channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setTelegramRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: telegramPlugin as ChannelPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: telegramPlugin as ChannelPlugin,
|
||||
setRuntime: setTelegramRuntime,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { telegramSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: telegramSetupPlugin };
|
||||
export default defineSetupPluginEntry(telegramSetupPlugin);
|
||||
|
||||
@@ -3,7 +3,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { withEnv } from "../../../src/test-utils/env.js";
|
||||
import { withEnv } from "../../test-utils/env.js";
|
||||
import { inspectTelegramAccount } from "./account-inspect.js";
|
||||
|
||||
describe("inspectTelegramAccount SecretRef resolution", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import * as subsystemModule from "../../../src/logging/subsystem.js";
|
||||
import { withEnv } from "../../../src/test-utils/env.js";
|
||||
import { withEnv } from "../../test-utils/env.js";
|
||||
import {
|
||||
listTelegramAccountIds,
|
||||
resetMissingDefaultWarnFlag,
|
||||
|
||||
@@ -2,9 +2,9 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { withEnvAsync } from "../../../src/test-utils/env.js";
|
||||
import { useFrozenTime, useRealTime } from "../../../src/test-utils/frozen-time.js";
|
||||
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
|
||||
import { withEnvAsync } from "../../test-utils/env.js";
|
||||
import { useFrozenTime, useRealTime } from "../../test-utils/frozen-time.js";
|
||||
import {
|
||||
answerCallbackQuerySpy,
|
||||
botCtorSpy,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { rm } from "node:fs/promises";
|
||||
import type { PluginInteractiveTelegramHandlerContext } from "openclaw/plugin-sdk/core";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { expectChannelInboundContextContract as expectInboundContextContract } from "../../../src/channels/plugins/contracts/suites.js";
|
||||
import {
|
||||
clearPluginInteractiveHandlers,
|
||||
registerPluginInteractiveHandler,
|
||||
} from "../../../src/plugins/interactive.js";
|
||||
import type { PluginInteractiveTelegramHandlerContext } from "../../../src/plugins/types.js";
|
||||
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
|
||||
import {
|
||||
answerCallbackQuerySpy,
|
||||
|
||||
@@ -6,12 +6,9 @@ import {
|
||||
} from "openclaw/plugin-sdk/channel-config-helpers";
|
||||
import { type OutboundSendDeps, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { normalizeMessageChannel } from "openclaw/plugin-sdk/channel-runtime";
|
||||
import { buildOutboundBaseSessionKey, normalizeOutboundThreadId } from "openclaw/plugin-sdk/core";
|
||||
import { resolveExecApprovalCommandDisplay } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import { buildExecApprovalPendingReplyPayload } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import {
|
||||
buildOutboundBaseSessionKey,
|
||||
normalizeOutboundThreadId,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { resolveThreadSessionKeys, type RoutePeer } from "openclaw/plugin-sdk/routing";
|
||||
import { parseTelegramTopicConversation } from "openclaw/plugin-sdk/telegram";
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { afterEach, type Mock, describe, expect, it, vi } from "vitest";
|
||||
import { withFetchPreconnect } from "../../../src/test-utils/fetch-mock.js";
|
||||
import { withFetchPreconnect } from "../../test-utils/fetch-mock.js";
|
||||
import { probeTelegram, resetTelegramProbeFetcherCacheForTests } from "./probe.js";
|
||||
|
||||
const resolveTelegramFetch = vi.hoisted(() => vi.fn());
|
||||
|
||||
1
extensions/test-utils/chunk-test-helpers.ts
Normal file
1
extensions/test-utils/chunk-test-helpers.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { countLines, hasBalancedFences } from "../../src/test-utils/chunk-test-helpers.js";
|
||||
1
extensions/test-utils/env.ts
Normal file
1
extensions/test-utils/env.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { captureEnv, withEnv, withEnvAsync } from "../../src/test-utils/env.js";
|
||||
1
extensions/test-utils/fetch-mock.ts
Normal file
1
extensions/test-utils/fetch-mock.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { withFetchPreconnect, type FetchMock } from "../../src/test-utils/fetch-mock.js";
|
||||
1
extensions/test-utils/frozen-time.ts
Normal file
1
extensions/test-utils/frozen-time.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { useFrozenTime, useRealTime } from "../../src/test-utils/frozen-time.js";
|
||||
1
extensions/test-utils/mock-http-response.ts
Normal file
1
extensions/test-utils/mock-http-response.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { createMockServerResponse } from "../../src/test-utils/mock-http-response.js";
|
||||
1
extensions/test-utils/plugin-command.ts
Normal file
1
extensions/test-utils/plugin-command.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type { OpenClawPluginCommandDefinition } from "openclaw/plugin-sdk/core";
|
||||
1
extensions/test-utils/plugin-registration.ts
Normal file
1
extensions/test-utils/plugin-registration.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { registerSingleProviderPlugin } from "../../src/test-utils/plugin-registration.js";
|
||||
4
extensions/test-utils/provider-usage-fetch.ts
Normal file
4
extensions/test-utils/provider-usage-fetch.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
createProviderUsageFetch,
|
||||
makeResponse,
|
||||
} from "../../src/test-utils/provider-usage-fetch.js";
|
||||
1
extensions/test-utils/temp-dir.ts
Normal file
1
extensions/test-utils/temp-dir.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { withTempDir } from "../../src/test-utils/temp-dir.js";
|
||||
1
extensions/test-utils/typed-cases.ts
Normal file
1
extensions/test-utils/typed-cases.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { typedCases } from "../../src/test-utils/typed-cases.js";
|
||||
@@ -2,14 +2,12 @@ import { spawn } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/tlon";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/tlon";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { tlonPlugin } from "./src/channel.js";
|
||||
import { setTlonRuntime } from "./src/runtime.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Whitelist of allowed tlon subcommands
|
||||
const ALLOWED_TLON_COMMANDS = new Set([
|
||||
"activity",
|
||||
"channels",
|
||||
@@ -24,40 +22,29 @@ const ALLOWED_TLON_COMMANDS = new Set([
|
||||
"version",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Find the tlon binary from the skill package
|
||||
*/
|
||||
let cachedTlonBinary: string | undefined;
|
||||
|
||||
function findTlonBinary(): string {
|
||||
if (cachedTlonBinary) {
|
||||
return cachedTlonBinary;
|
||||
}
|
||||
// Check in node_modules/.bin
|
||||
const skillBin = join(__dirname, "node_modules", ".bin", "tlon");
|
||||
if (existsSync(skillBin)) {
|
||||
cachedTlonBinary = skillBin;
|
||||
return skillBin;
|
||||
}
|
||||
|
||||
// Check for platform-specific binary directly
|
||||
const platform = process.platform;
|
||||
const arch = process.arch;
|
||||
const platformPkg = `@tloncorp/tlon-skill-${platform}-${arch}`;
|
||||
const platformPkg = `@tloncorp/tlon-skill-${process.platform}-${process.arch}`;
|
||||
const platformBin = join(__dirname, "node_modules", platformPkg, "tlon");
|
||||
if (existsSync(platformBin)) {
|
||||
cachedTlonBinary = platformBin;
|
||||
return platformBin;
|
||||
}
|
||||
|
||||
// Fallback to PATH
|
||||
cachedTlonBinary = "tlon";
|
||||
return cachedTlonBinary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shell-like argument splitter that respects quotes
|
||||
*/
|
||||
function shellSplit(str: string): string[] {
|
||||
const args: string[] = [];
|
||||
let cur = "";
|
||||
@@ -92,18 +79,15 @@ function shellSplit(str: string): string[] {
|
||||
}
|
||||
cur += ch;
|
||||
}
|
||||
if (cur) args.push(cur);
|
||||
if (cur) {
|
||||
args.push(cur);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the tlon command and return the result
|
||||
*/
|
||||
function runTlonCommand(binary: string, args: string[]): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(binary, args, {
|
||||
env: process.env,
|
||||
});
|
||||
const child = spawn(binary, args, { env: process.env });
|
||||
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
@@ -123,25 +107,20 @@ function runTlonCommand(binary: string, args: string[]): Promise<string> {
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(stderr || `tlon exited with code ${code}`));
|
||||
} else {
|
||||
resolve(stdout);
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "tlon",
|
||||
name: "Tlon",
|
||||
description: "Tlon/Urbit channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setTlonRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: tlonPlugin });
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin: tlonPlugin,
|
||||
setRuntime: setTlonRuntime,
|
||||
registerFull(api) {
|
||||
api.logger.debug?.("[tlon] Registering tlon tool");
|
||||
api.registerTool({
|
||||
name: "tlon",
|
||||
@@ -164,9 +143,6 @@ const plugin = {
|
||||
async execute(_id: string, params: { command: string }) {
|
||||
try {
|
||||
const args = shellSplit(params.command);
|
||||
const tlonBinary = findTlonBinary();
|
||||
|
||||
// Validate first argument is a whitelisted tlon subcommand
|
||||
const subcommand = args[0];
|
||||
if (!ALLOWED_TLON_COMMANDS.has(subcommand)) {
|
||||
return {
|
||||
@@ -180,7 +156,7 @@ const plugin = {
|
||||
};
|
||||
}
|
||||
|
||||
const output = await runTlonCommand(tlonBinary, args);
|
||||
const output = await runTlonCommand(findTlonBinary(), args);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: output }],
|
||||
details: undefined,
|
||||
@@ -194,6 +170,4 @@ const plugin = {
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { tlonPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: tlonPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(tlonPlugin);
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/twitch";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/twitch";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { twitchPlugin } from "./src/plugin.js";
|
||||
import { setTwitchRuntime } from "./src/runtime.js";
|
||||
|
||||
export { monitorTwitchProvider } from "./src/monitor.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "twitch",
|
||||
name: "Twitch",
|
||||
description: "Twitch channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setTwitchRuntime(api.runtime);
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
api.registerChannel({ plugin: twitchPlugin as any });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
description: "Twitch chat channel plugin",
|
||||
plugin: twitchPlugin,
|
||||
setRuntime: setTwitchRuntime,
|
||||
});
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { whatsappPlugin } from "./src/channel.js";
|
||||
import { setWhatsAppRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "whatsapp",
|
||||
name: "WhatsApp",
|
||||
description: "WhatsApp channel plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setWhatsAppRuntime(api.runtime);
|
||||
api.registerChannel({ plugin: whatsappPlugin });
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
plugin: whatsappPlugin,
|
||||
setRuntime: setWhatsAppRuntime,
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { whatsappSetupPlugin } from "./src/channel.setup.js";
|
||||
|
||||
export default { plugin: whatsappSetupPlugin };
|
||||
export default defineSetupPluginEntry(whatsappSetupPlugin);
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { captureEnv } from "../../../src/test-utils/env.js";
|
||||
import { captureEnv } from "../../test-utils/env.js";
|
||||
import { hasAnyWhatsAppAuth, listWhatsAppAuthDirs } from "./accounts.js";
|
||||
|
||||
describe("hasAnyWhatsAppAuth", () => {
|
||||
|
||||
@@ -4,8 +4,8 @@ import fs from "node:fs/promises";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import { setLoggerOverride } from "../../../src/logging.js";
|
||||
import { withEnvAsync } from "../../../src/test-utils/env.js";
|
||||
import { escapeRegExp, formatEnvelopeTimestamp } from "../../../test/helpers/envelope-timestamp.js";
|
||||
import { withEnvAsync } from "../../test-utils/env.js";
|
||||
import {
|
||||
createMockWebListener,
|
||||
createWebListenerFactoryCapture,
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { saveSessionStore } from "../../../../src/config/sessions.js";
|
||||
import { withTempDir } from "../../../../src/test-utils/temp-dir.js";
|
||||
import { withTempDir } from "../../../test-utils/temp-dir.js";
|
||||
import {
|
||||
debugMention,
|
||||
isBotMentionedFromTargets,
|
||||
|
||||
@@ -1,18 +1,73 @@
|
||||
export { getActiveWebListener } from "./active-listener.js";
|
||||
export {
|
||||
getWebAuthAgeMs,
|
||||
logWebSelfId,
|
||||
logoutWeb,
|
||||
readWebSelfId,
|
||||
webAuthExists,
|
||||
} from "./auth-store.js";
|
||||
export { loginWeb } from "./login.js";
|
||||
export { startWebLoginWithQr, waitForWebLogin } from "./login-qr.js";
|
||||
export { whatsappSetupWizard } from "./setup-surface.js";
|
||||
import { monitorWebChannel as monitorWebChannelImpl } from "openclaw/plugin-sdk/whatsapp";
|
||||
import { getActiveWebListener as getActiveWebListenerImpl } from "./active-listener.js";
|
||||
import {
|
||||
getWebAuthAgeMs as getWebAuthAgeMsImpl,
|
||||
logWebSelfId as logWebSelfIdImpl,
|
||||
logoutWeb as logoutWebImpl,
|
||||
readWebSelfId as readWebSelfIdImpl,
|
||||
webAuthExists as webAuthExistsImpl,
|
||||
} from "./auth-store.js";
|
||||
import {
|
||||
startWebLoginWithQr as startWebLoginWithQrImpl,
|
||||
waitForWebLogin as waitForWebLoginImpl,
|
||||
} from "./login-qr.js";
|
||||
import { loginWeb as loginWebImpl } from "./login.js";
|
||||
import { whatsappSetupWizard as whatsappSetupWizardImpl } from "./setup-surface.js";
|
||||
|
||||
type GetActiveWebListener = typeof import("./active-listener.js").getActiveWebListener;
|
||||
type GetWebAuthAgeMs = typeof import("./auth-store.js").getWebAuthAgeMs;
|
||||
type LogWebSelfId = typeof import("./auth-store.js").logWebSelfId;
|
||||
type LogoutWeb = typeof import("./auth-store.js").logoutWeb;
|
||||
type ReadWebSelfId = typeof import("./auth-store.js").readWebSelfId;
|
||||
type WebAuthExists = typeof import("./auth-store.js").webAuthExists;
|
||||
type LoginWeb = typeof import("./login.js").loginWeb;
|
||||
type StartWebLoginWithQr = typeof import("./login-qr.js").startWebLoginWithQr;
|
||||
type WaitForWebLogin = typeof import("./login-qr.js").waitForWebLogin;
|
||||
type WhatsAppSetupWizard = typeof import("./setup-surface.js").whatsappSetupWizard;
|
||||
type MonitorWebChannel = typeof import("openclaw/plugin-sdk/whatsapp").monitorWebChannel;
|
||||
|
||||
export function getActiveWebListener(
|
||||
...args: Parameters<GetActiveWebListener>
|
||||
): ReturnType<GetActiveWebListener> {
|
||||
return getActiveWebListenerImpl(...args);
|
||||
}
|
||||
|
||||
export function getWebAuthAgeMs(...args: Parameters<GetWebAuthAgeMs>): ReturnType<GetWebAuthAgeMs> {
|
||||
return getWebAuthAgeMsImpl(...args);
|
||||
}
|
||||
|
||||
export function logWebSelfId(...args: Parameters<LogWebSelfId>): ReturnType<LogWebSelfId> {
|
||||
return logWebSelfIdImpl(...args);
|
||||
}
|
||||
|
||||
export function logoutWeb(...args: Parameters<LogoutWeb>): ReturnType<LogoutWeb> {
|
||||
return logoutWebImpl(...args);
|
||||
}
|
||||
|
||||
export function readWebSelfId(...args: Parameters<ReadWebSelfId>): ReturnType<ReadWebSelfId> {
|
||||
return readWebSelfIdImpl(...args);
|
||||
}
|
||||
|
||||
export function webAuthExists(...args: Parameters<WebAuthExists>): ReturnType<WebAuthExists> {
|
||||
return webAuthExistsImpl(...args);
|
||||
}
|
||||
|
||||
export function loginWeb(...args: Parameters<LoginWeb>): ReturnType<LoginWeb> {
|
||||
return loginWebImpl(...args);
|
||||
}
|
||||
|
||||
export function startWebLoginWithQr(
|
||||
...args: Parameters<StartWebLoginWithQr>
|
||||
): ReturnType<StartWebLoginWithQr> {
|
||||
return startWebLoginWithQrImpl(...args);
|
||||
}
|
||||
|
||||
export function waitForWebLogin(...args: Parameters<WaitForWebLogin>): ReturnType<WaitForWebLogin> {
|
||||
return waitForWebLoginImpl(...args);
|
||||
}
|
||||
|
||||
export const whatsappSetupWizard: WhatsAppSetupWizard = { ...whatsappSetupWizardImpl };
|
||||
|
||||
export async function monitorWebChannel(
|
||||
...args: Parameters<MonitorWebChannel>
|
||||
): ReturnType<MonitorWebChannel> {
|
||||
|
||||
@@ -7,8 +7,8 @@ import { resolveStateDir } from "../../../src/config/paths.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../../../src/infra/tmp-openclaw-dir.js";
|
||||
import { optimizeImageToPng } from "../../../src/media/image-ops.js";
|
||||
import { mockPinnedHostnameResolution } from "../../../src/test-helpers/ssrf.js";
|
||||
import { captureEnv } from "../../../src/test-utils/env.js";
|
||||
import { sendVoiceMessageDiscord } from "../../discord/src/send.js";
|
||||
import { captureEnv } from "../../test-utils/env.js";
|
||||
import {
|
||||
LocalMediaAccessError,
|
||||
loadWebMedia,
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/zalo";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/zalo";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { zaloPlugin } from "./src/channel.js";
|
||||
import { setZaloRuntime } from "./src/runtime.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "zalo",
|
||||
name: "Zalo",
|
||||
description: "Zalo channel plugin (Bot API)",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setZaloRuntime(api.runtime);
|
||||
api.registerChannel(zaloPlugin);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
description: "Zalo channel plugin",
|
||||
plugin: zaloPlugin,
|
||||
setRuntime: setZaloRuntime,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { zaloPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: zaloPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(zaloPlugin);
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
export { sendMessageZalo } from "./send.js";
|
||||
import { sendMessageZalo as sendMessageZaloImpl } from "./send.js";
|
||||
|
||||
type SendMessageZalo = typeof import("./send.js").sendMessageZalo;
|
||||
|
||||
export function sendMessageZalo(...args: Parameters<SendMessageZalo>): ReturnType<SendMessageZalo> {
|
||||
return sendMessageZaloImpl(...args);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import type { AnyAgentTool, OpenClawPluginApi } from "openclaw/plugin-sdk/zalouser";
|
||||
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/zalouser";
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import type { AnyAgentTool } from "openclaw/plugin-sdk/zalouser";
|
||||
import { zalouserPlugin } from "./src/channel.js";
|
||||
import { setZalouserRuntime } from "./src/runtime.js";
|
||||
import { ZalouserToolSchema, executeZalouserTool } from "./src/tool.js";
|
||||
|
||||
const plugin = {
|
||||
export default defineChannelPluginEntry({
|
||||
id: "zalouser",
|
||||
name: "Zalo Personal",
|
||||
description: "Zalo personal account messaging via native zca-js integration",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
setZalouserRuntime(api.runtime);
|
||||
api.registerChannel(zalouserPlugin);
|
||||
if (api.registrationMode !== "full") {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin: zalouserPlugin,
|
||||
setRuntime: setZalouserRuntime,
|
||||
registerFull(api) {
|
||||
api.registerTool({
|
||||
name: "zalouser",
|
||||
label: "Zalo Personal",
|
||||
@@ -27,6 +22,4 @@ const plugin = {
|
||||
execute: executeZalouserTool,
|
||||
} as AnyAgentTool);
|
||||
},
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
import { zalouserPlugin } from "./src/channel.js";
|
||||
|
||||
export default {
|
||||
plugin: zalouserPlugin,
|
||||
};
|
||||
export default defineSetupPluginEntry(zalouserPlugin);
|
||||
|
||||
@@ -443,7 +443,7 @@
|
||||
"build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json || true",
|
||||
"build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && pnpm build:plugin-sdk:dts",
|
||||
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
|
||||
"check": "pnpm check:host-env-policy:swift && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",
|
||||
"check": "pnpm check:host-env-policy:swift && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope",
|
||||
"check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-i18n-glossary && pnpm docs:check-links",
|
||||
"check:host-env-policy:swift": "node scripts/generate-host-env-security-policy-swift.mjs --check",
|
||||
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500",
|
||||
@@ -495,6 +495,7 @@
|
||||
"lint:docs": "pnpm dlx markdownlint-cli2",
|
||||
"lint:docs:fix": "pnpm dlx markdownlint-cli2 --fix",
|
||||
"lint:fix": "oxlint --type-aware --fix && pnpm format",
|
||||
"lint:plugins:no-extension-test-core-imports": "node --import tsx scripts/check-no-extension-test-core-imports.ts",
|
||||
"lint:plugins:no-monolithic-plugin-sdk-entry-imports": "node --import tsx scripts/check-no-monolithic-plugin-sdk-entry-imports.ts",
|
||||
"lint:plugins:no-register-http-handler": "node scripts/check-no-register-http-handler.mjs",
|
||||
"lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)",
|
||||
|
||||
90
scripts/check-no-extension-test-core-imports.ts
Normal file
90
scripts/check-no-extension-test-core-imports.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
|
||||
{
|
||||
pattern: /["']openclaw\/plugin-sdk["']/,
|
||||
hint: "Use openclaw/plugin-sdk/<subpath> instead of the monolithic root entry.",
|
||||
},
|
||||
{
|
||||
pattern: /["']openclaw\/plugin-sdk\/compat["']/,
|
||||
hint: "Use a focused public plugin-sdk subpath instead of compat.",
|
||||
},
|
||||
{
|
||||
pattern: /["'](?:\.\.\/)+(?:src\/test-utils\/)[^"']+["']/,
|
||||
hint: "Use extensions/test-utils/* bridges for shared extension test helpers.",
|
||||
},
|
||||
{
|
||||
pattern: /["'](?:\.\.\/)+(?:src\/plugins\/types\.js)["']/,
|
||||
hint: "Use public plugin-sdk/core types or extensions/test-utils bridges instead.",
|
||||
},
|
||||
];
|
||||
|
||||
function isExtensionTestFile(filePath: string): boolean {
|
||||
return /\.test\.[cm]?[jt]sx?$/u.test(filePath) || /\.e2e\.test\.[cm]?[jt]sx?$/u.test(filePath);
|
||||
}
|
||||
|
||||
function collectExtensionTestFiles(rootDir: string): string[] {
|
||||
const files: string[] = [];
|
||||
const stack = [rootDir];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
if (!current) {
|
||||
continue;
|
||||
}
|
||||
let entries: fs.Dirent[] = [];
|
||||
try {
|
||||
entries = fs.readdirSync(current, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(current, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "coverage") {
|
||||
continue;
|
||||
}
|
||||
stack.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && isExtensionTestFile(fullPath)) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const extensionsDir = path.join(process.cwd(), "extensions");
|
||||
const files = collectExtensionTestFiles(extensionsDir);
|
||||
const offenders: Array<{ file: string; hint: string }> = [];
|
||||
|
||||
for (const file of files) {
|
||||
const content = fs.readFileSync(file, "utf8");
|
||||
for (const rule of FORBIDDEN_PATTERNS) {
|
||||
if (!rule.pattern.test(content)) {
|
||||
continue;
|
||||
}
|
||||
offenders.push({ file, hint: rule.hint });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offenders.length > 0) {
|
||||
console.error(
|
||||
"Extension test files must stay on extension test bridges or public plugin-sdk seams.",
|
||||
);
|
||||
for (const offender of offenders.toSorted((a, b) => a.file.localeCompare(b.file))) {
|
||||
const relative = path.relative(process.cwd(), offender.file) || offender.file;
|
||||
console.error(`- ${relative}: ${offender.hint}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`OK: extension test files avoid direct core test/internal imports (${files.length} checked).`,
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -175,6 +175,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => {
|
||||
|
||||
expect(
|
||||
adapter.validateInput?.({
|
||||
cfg: asConfig({}),
|
||||
accountId: "work",
|
||||
input: { useEnv: true },
|
||||
}),
|
||||
@@ -182,6 +183,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => {
|
||||
|
||||
expect(
|
||||
adapter.validateInput?.({
|
||||
cfg: asConfig({}),
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
input: {},
|
||||
}),
|
||||
@@ -189,6 +191,7 @@ describe("createEnvPatchedAccountSetupAdapter", () => {
|
||||
|
||||
expect(
|
||||
adapter.validateInput?.({
|
||||
cfg: asConfig({}),
|
||||
accountId: DEFAULT_ACCOUNT_ID,
|
||||
input: { token: "tok" },
|
||||
}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import { handleSlackAction, type SlackActionContext } from "../../agents/tools/slack-actions.js";
|
||||
import {
|
||||
extractSlackToolSend,
|
||||
@@ -12,7 +13,7 @@ type SlackActionInvoke = (
|
||||
action: Record<string, unknown>,
|
||||
cfg: unknown,
|
||||
toolContext: unknown,
|
||||
) => Promise<unknown>;
|
||||
) => Promise<AgentToolResult<unknown>>;
|
||||
|
||||
export function createSlackActions(
|
||||
providerId: string,
|
||||
|
||||
@@ -21,32 +21,32 @@ const sendFns = vi.hoisted(() => ({
|
||||
|
||||
vi.mock("./send-runtime/whatsapp.js", () => {
|
||||
moduleLoads.whatsapp();
|
||||
return { sendMessageWhatsApp: sendFns.whatsapp };
|
||||
return { runtimeSend: { sendMessage: sendFns.whatsapp } };
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/telegram.js", () => {
|
||||
moduleLoads.telegram();
|
||||
return { sendMessageTelegram: sendFns.telegram };
|
||||
return { runtimeSend: { sendMessage: sendFns.telegram } };
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/discord.js", () => {
|
||||
moduleLoads.discord();
|
||||
return { sendMessageDiscord: sendFns.discord };
|
||||
return { runtimeSend: { sendMessage: sendFns.discord } };
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/slack.js", () => {
|
||||
moduleLoads.slack();
|
||||
return { sendMessageSlack: sendFns.slack };
|
||||
return { runtimeSend: { sendMessage: sendFns.slack } };
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/signal.js", () => {
|
||||
moduleLoads.signal();
|
||||
return { sendMessageSignal: sendFns.signal };
|
||||
return { runtimeSend: { sendMessage: sendFns.signal } };
|
||||
});
|
||||
|
||||
vi.mock("./send-runtime/imessage.js", () => {
|
||||
moduleLoads.imessage();
|
||||
return { sendMessageIMessage: sendFns.imessage };
|
||||
return { runtimeSend: { sendMessage: sendFns.imessage } };
|
||||
});
|
||||
|
||||
describe("createDefaultDeps", () => {
|
||||
|
||||
@@ -6,9 +6,15 @@ import { createOutboundSendDepsFromCliSource } from "./outbound-send-mapping.js"
|
||||
* Values are proxy functions that dynamically import the real module on first use.
|
||||
*/
|
||||
export type CliDeps = { [channelId: string]: unknown };
|
||||
type RuntimeSend = {
|
||||
sendMessage: (...args: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
type RuntimeSendModule = {
|
||||
runtimeSend: RuntimeSend;
|
||||
};
|
||||
|
||||
// Per-channel module caches for lazy loading.
|
||||
const senderCache = new Map<string, Promise<Record<string, unknown>>>();
|
||||
const senderCache = new Map<string, Promise<RuntimeSend>>();
|
||||
|
||||
/**
|
||||
* Create a lazy-loading send function proxy for a channel.
|
||||
@@ -16,18 +22,16 @@ const senderCache = new Map<string, Promise<Record<string, unknown>>>();
|
||||
*/
|
||||
function createLazySender(
|
||||
channelId: string,
|
||||
loader: () => Promise<Record<string, unknown>>,
|
||||
exportName: string,
|
||||
loader: () => Promise<RuntimeSendModule>,
|
||||
): (...args: unknown[]) => Promise<unknown> {
|
||||
return async (...args: unknown[]) => {
|
||||
let cached = senderCache.get(channelId);
|
||||
if (!cached) {
|
||||
cached = loader();
|
||||
cached = loader().then(({ runtimeSend }) => runtimeSend);
|
||||
senderCache.set(channelId, cached);
|
||||
}
|
||||
const mod = await cached;
|
||||
const fn = mod[exportName] as (...a: unknown[]) => Promise<unknown>;
|
||||
return await fn(...args);
|
||||
const runtimeSend = await cached;
|
||||
return await runtimeSend.sendMessage(...args);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,33 +39,27 @@ export function createDefaultDeps(): CliDeps {
|
||||
return {
|
||||
whatsapp: createLazySender(
|
||||
"whatsapp",
|
||||
() => import("./send-runtime/whatsapp.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageWhatsApp",
|
||||
() => import("./send-runtime/whatsapp.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
telegram: createLazySender(
|
||||
"telegram",
|
||||
() => import("./send-runtime/telegram.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageTelegram",
|
||||
() => import("./send-runtime/telegram.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
discord: createLazySender(
|
||||
"discord",
|
||||
() => import("./send-runtime/discord.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageDiscord",
|
||||
() => import("./send-runtime/discord.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
slack: createLazySender(
|
||||
"slack",
|
||||
() => import("./send-runtime/slack.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageSlack",
|
||||
() => import("./send-runtime/slack.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
signal: createLazySender(
|
||||
"signal",
|
||||
() => import("./send-runtime/signal.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageSignal",
|
||||
() => import("./send-runtime/signal.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
imessage: createLazySender(
|
||||
"imessage",
|
||||
() => import("./send-runtime/imessage.js") as Promise<Record<string, unknown>>,
|
||||
"sendMessageIMessage",
|
||||
() => import("./send-runtime/imessage.js") as Promise<RuntimeSendModule>,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { sendMessageDiscord as sendMessageDiscordImpl } from "../../plugin-sdk/discord.js";
|
||||
|
||||
type SendMessageDiscord = typeof import("../../plugin-sdk/discord.js").sendMessageDiscord;
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../plugin-sdk/discord.js").sendMessageDiscord;
|
||||
};
|
||||
|
||||
export async function sendMessageDiscord(
|
||||
...args: Parameters<SendMessageDiscord>
|
||||
): ReturnType<SendMessageDiscord> {
|
||||
return await sendMessageDiscordImpl(...args);
|
||||
}
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageDiscordImpl,
|
||||
} satisfies RuntimeSend;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { sendMessageIMessage as sendMessageIMessageImpl } from "../../plugin-sdk/imessage.js";
|
||||
|
||||
type SendMessageIMessage = typeof import("../../plugin-sdk/imessage.js").sendMessageIMessage;
|
||||
type RuntimeSend = {
|
||||
sendMessage: typeof import("../../plugin-sdk/imessage.js").sendMessageIMessage;
|
||||
};
|
||||
|
||||
export async function sendMessageIMessage(
|
||||
...args: Parameters<SendMessageIMessage>
|
||||
): ReturnType<SendMessageIMessage> {
|
||||
return await sendMessageIMessageImpl(...args);
|
||||
}
|
||||
export const runtimeSend = {
|
||||
sendMessage: sendMessageIMessageImpl,
|
||||
} satisfies RuntimeSend;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user