refactor: plugin catalog + nextcloud policy
This commit is contained in:
@@ -150,6 +150,7 @@ describe("getDmHistoryLimitFromSessionKey", () => {
|
||||
"signal",
|
||||
"imessage",
|
||||
"msteams",
|
||||
"nextcloud-talk",
|
||||
] as const;
|
||||
|
||||
for (const provider of providers) {
|
||||
@@ -168,6 +169,7 @@ describe("getDmHistoryLimitFromSessionKey", () => {
|
||||
"signal",
|
||||
"imessage",
|
||||
"msteams",
|
||||
"nextcloud-talk",
|
||||
] as const;
|
||||
|
||||
for (const provider of providers) {
|
||||
|
||||
@@ -62,22 +62,16 @@ export function getDmHistoryLimitFromSessionKey(
|
||||
return providerConfig.dmHistoryLimit;
|
||||
};
|
||||
|
||||
switch (provider) {
|
||||
case "telegram":
|
||||
return getLimit(config.channels?.telegram);
|
||||
case "whatsapp":
|
||||
return getLimit(config.channels?.whatsapp);
|
||||
case "discord":
|
||||
return getLimit(config.channels?.discord);
|
||||
case "slack":
|
||||
return getLimit(config.channels?.slack);
|
||||
case "signal":
|
||||
return getLimit(config.channels?.signal);
|
||||
case "imessage":
|
||||
return getLimit(config.channels?.imessage);
|
||||
case "msteams":
|
||||
return getLimit(config.channels?.msteams);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
const resolveProviderConfig = (
|
||||
cfg: ClawdbotConfig | undefined,
|
||||
providerId: string,
|
||||
): { dmHistoryLimit?: number; dms?: Record<string, { historyLimit?: number }> } | undefined => {
|
||||
const channels = cfg?.channels;
|
||||
if (!channels || typeof channels !== "object") return undefined;
|
||||
const entry = (channels as Record<string, unknown>)[providerId];
|
||||
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return undefined;
|
||||
return entry as { dmHistoryLimit?: number; dms?: Record<string, { historyLimit?: number }> };
|
||||
};
|
||||
|
||||
return getLimit(resolveProviderConfig(config, provider));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import path from "node:path";
|
||||
|
||||
import { discoverClawdbotPlugins } from "../../plugins/discovery.js";
|
||||
import type { PluginOrigin } from "../../plugins/types.js";
|
||||
import type { ClawdbotManifest } from "../../plugins/manifest.js";
|
||||
import type { ChannelMeta } from "./types.js";
|
||||
|
||||
export type ChannelPluginCatalogEntry = {
|
||||
@@ -10,86 +15,133 @@ export type ChannelPluginCatalogEntry = {
|
||||
};
|
||||
};
|
||||
|
||||
const CATALOG: ChannelPluginCatalogEntry[] = [
|
||||
{
|
||||
id: "msteams",
|
||||
meta: {
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
selectionLabel: "Microsoft Teams (Bot Framework)",
|
||||
docsPath: "/channels/msteams",
|
||||
docsLabel: "msteams",
|
||||
blurb: "Bot Framework; enterprise support.",
|
||||
aliases: ["teams"],
|
||||
order: 60,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@clawdbot/msteams",
|
||||
localPath: "extensions/msteams",
|
||||
defaultChoice: "npm",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "matrix",
|
||||
meta: {
|
||||
id: "matrix",
|
||||
label: "Matrix",
|
||||
selectionLabel: "Matrix (plugin)",
|
||||
docsPath: "/channels/matrix",
|
||||
docsLabel: "matrix",
|
||||
blurb: "open protocol; install the plugin to enable.",
|
||||
order: 70,
|
||||
quickstartAllowFrom: true,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@clawdbot/matrix",
|
||||
localPath: "extensions/matrix",
|
||||
defaultChoice: "npm",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "bluebubbles",
|
||||
meta: {
|
||||
id: "bluebubbles",
|
||||
label: "BlueBubbles",
|
||||
selectionLabel: "BlueBubbles (macOS app)",
|
||||
docsPath: "/channels/bluebubbles",
|
||||
docsLabel: "bluebubbles",
|
||||
blurb: "iMessage via the BlueBubbles mac app + REST API.",
|
||||
order: 75,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@clawdbot/bluebubbles",
|
||||
localPath: "extensions/bluebubbles",
|
||||
defaultChoice: "npm",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "zalo",
|
||||
meta: {
|
||||
id: "zalo",
|
||||
label: "Zalo",
|
||||
selectionLabel: "Zalo (Bot API)",
|
||||
docsPath: "/channels/zalo",
|
||||
docsLabel: "zalo",
|
||||
blurb: "Vietnam-focused messaging platform with Bot API.",
|
||||
aliases: ["zl"],
|
||||
order: 80,
|
||||
quickstartAllowFrom: true,
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@clawdbot/zalo",
|
||||
localPath: "extensions/zalo",
|
||||
},
|
||||
},
|
||||
];
|
||||
type CatalogOptions = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
export function listChannelPluginCatalogEntries(): ChannelPluginCatalogEntry[] {
|
||||
return [...CATALOG];
|
||||
const ORIGIN_PRIORITY: Record<PluginOrigin, number> = {
|
||||
config: 0,
|
||||
workspace: 1,
|
||||
global: 2,
|
||||
bundled: 3,
|
||||
};
|
||||
|
||||
function toChannelMeta(params: {
|
||||
channel: NonNullable<ClawdbotManifest["channel"]>;
|
||||
id: string;
|
||||
}): ChannelMeta | null {
|
||||
const label = params.channel.label?.trim();
|
||||
if (!label) return null;
|
||||
const selectionLabel = params.channel.selectionLabel?.trim() || label;
|
||||
const docsPath = params.channel.docsPath?.trim() || `/channels/${params.id}`;
|
||||
const blurb = params.channel.blurb?.trim() || "";
|
||||
|
||||
return {
|
||||
id: params.id,
|
||||
label,
|
||||
selectionLabel,
|
||||
docsPath,
|
||||
docsLabel: params.channel.docsLabel?.trim() || undefined,
|
||||
blurb,
|
||||
...(params.channel.aliases ? { aliases: params.channel.aliases } : {}),
|
||||
...(params.channel.order !== undefined ? { order: params.channel.order } : {}),
|
||||
...(params.channel.selectionDocsPrefix
|
||||
? { selectionDocsPrefix: params.channel.selectionDocsPrefix }
|
||||
: {}),
|
||||
...(params.channel.selectionDocsOmitLabel !== undefined
|
||||
? { selectionDocsOmitLabel: params.channel.selectionDocsOmitLabel }
|
||||
: {}),
|
||||
...(params.channel.selectionExtras ? { selectionExtras: params.channel.selectionExtras } : {}),
|
||||
...(params.channel.showConfigured !== undefined
|
||||
? { showConfigured: params.channel.showConfigured }
|
||||
: {}),
|
||||
...(params.channel.quickstartAllowFrom !== undefined
|
||||
? { quickstartAllowFrom: params.channel.quickstartAllowFrom }
|
||||
: {}),
|
||||
...(params.channel.forceAccountBinding !== undefined
|
||||
? { forceAccountBinding: params.channel.forceAccountBinding }
|
||||
: {}),
|
||||
...(params.channel.preferSessionLookupForAnnounceTarget !== undefined
|
||||
? {
|
||||
preferSessionLookupForAnnounceTarget: params.channel.preferSessionLookupForAnnounceTarget,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function getChannelPluginCatalogEntry(id: string): ChannelPluginCatalogEntry | undefined {
|
||||
function resolveInstallInfo(params: {
|
||||
manifest: ClawdbotManifest;
|
||||
packageName?: string;
|
||||
packageDir?: string;
|
||||
workspaceDir?: string;
|
||||
}): ChannelPluginCatalogEntry["install"] | null {
|
||||
const npmSpec = params.manifest.install?.npmSpec?.trim() ?? params.packageName?.trim();
|
||||
if (!npmSpec) return null;
|
||||
let localPath = params.manifest.install?.localPath?.trim() || undefined;
|
||||
if (!localPath && params.workspaceDir && params.packageDir) {
|
||||
localPath = path.relative(params.workspaceDir, params.packageDir) || undefined;
|
||||
}
|
||||
const defaultChoice = params.manifest.install?.defaultChoice ?? (localPath ? "local" : "npm");
|
||||
return {
|
||||
npmSpec,
|
||||
...(localPath ? { localPath } : {}),
|
||||
...(defaultChoice ? { defaultChoice } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function buildCatalogEntry(candidate: {
|
||||
packageName?: string;
|
||||
packageDir?: string;
|
||||
workspaceDir?: string;
|
||||
packageClawdbot?: ClawdbotManifest;
|
||||
}): ChannelPluginCatalogEntry | null {
|
||||
const manifest = candidate.packageClawdbot;
|
||||
if (!manifest?.channel) return null;
|
||||
const id = manifest.channel.id?.trim();
|
||||
if (!id) return null;
|
||||
const meta = toChannelMeta({ channel: manifest.channel, id });
|
||||
if (!meta) return null;
|
||||
const install = resolveInstallInfo({
|
||||
manifest,
|
||||
packageName: candidate.packageName,
|
||||
packageDir: candidate.packageDir,
|
||||
workspaceDir: candidate.workspaceDir,
|
||||
});
|
||||
if (!install) return null;
|
||||
return { id, meta, install };
|
||||
}
|
||||
|
||||
export function listChannelPluginCatalogEntries(
|
||||
options: CatalogOptions = {},
|
||||
): ChannelPluginCatalogEntry[] {
|
||||
const discovery = discoverClawdbotPlugins({ workspaceDir: options.workspaceDir });
|
||||
const resolved = new Map<string, { entry: ChannelPluginCatalogEntry; priority: number }>();
|
||||
|
||||
for (const candidate of discovery.candidates) {
|
||||
const entry = buildCatalogEntry(candidate);
|
||||
if (!entry) continue;
|
||||
const priority = ORIGIN_PRIORITY[candidate.origin] ?? 99;
|
||||
const existing = resolved.get(entry.id);
|
||||
if (!existing || priority < existing.priority) {
|
||||
resolved.set(entry.id, { entry, priority });
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(resolved.values())
|
||||
.map(({ entry }) => entry)
|
||||
.sort((a, b) => {
|
||||
const orderA = a.meta.order ?? 999;
|
||||
const orderB = b.meta.order ?? 999;
|
||||
if (orderA !== orderB) return orderA - orderB;
|
||||
return a.meta.label.localeCompare(b.meta.label);
|
||||
});
|
||||
}
|
||||
|
||||
export function getChannelPluginCatalogEntry(
|
||||
id: string,
|
||||
options: CatalogOptions = {},
|
||||
): ChannelPluginCatalogEntry | undefined {
|
||||
const trimmed = id.trim();
|
||||
if (!trimmed) return undefined;
|
||||
return CATALOG.find((entry) => entry.id === trimmed);
|
||||
return listChannelPluginCatalogEntries(options).find((entry) => entry.id === trimmed);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ClawdbotConfig } from "./config.js";
|
||||
import { hasAnyWhatsAppAuth } from "../web/accounts.js";
|
||||
import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import { listChatChannels } from "../channels/registry.js";
|
||||
import { hasAnyWhatsAppAuth } from "../web/accounts.js";
|
||||
|
||||
type PluginEnableChange = {
|
||||
pluginId: string;
|
||||
@@ -12,20 +13,6 @@ export type PluginAutoEnableResult = {
|
||||
changes: string[];
|
||||
};
|
||||
|
||||
const CHANNEL_PLUGIN_IDS = [
|
||||
"whatsapp",
|
||||
"telegram",
|
||||
"discord",
|
||||
"slack",
|
||||
"signal",
|
||||
"imessage",
|
||||
"msteams",
|
||||
"matrix",
|
||||
"zalo",
|
||||
"zalouser",
|
||||
"bluebubbles",
|
||||
] as const;
|
||||
|
||||
const PROVIDER_PLUGIN_IDS: Array<{ pluginId: string; providerId: string }> = [
|
||||
{ pluginId: "google-antigravity-auth", providerId: "google-antigravity" },
|
||||
{ pluginId: "google-gemini-cli-auth", providerId: "google-gemini-cli" },
|
||||
@@ -239,7 +226,19 @@ function resolveConfiguredPlugins(
|
||||
env: NodeJS.ProcessEnv,
|
||||
): PluginEnableChange[] {
|
||||
const changes: PluginEnableChange[] = [];
|
||||
for (const channelId of CHANNEL_PLUGIN_IDS) {
|
||||
const channelIds = new Set<string>();
|
||||
for (const meta of listChatChannels()) {
|
||||
channelIds.add(meta.id);
|
||||
}
|
||||
const configuredChannels = cfg.channels as Record<string, unknown> | undefined;
|
||||
if (configuredChannels && typeof configuredChannels === "object") {
|
||||
for (const key of Object.keys(configuredChannels)) {
|
||||
if (key === "defaults") continue;
|
||||
channelIds.add(key);
|
||||
}
|
||||
}
|
||||
for (const channelId of channelIds) {
|
||||
if (!channelId) continue;
|
||||
if (isChannelConfigured(cfg, channelId, env)) {
|
||||
changes.push({
|
||||
pluginId: channelId,
|
||||
|
||||
@@ -60,7 +60,9 @@ export type { ClawdbotConfig } from "../config/config.js";
|
||||
export type { ChannelDock } from "../channels/dock.js";
|
||||
export { getChatChannelMeta } from "../channels/registry.js";
|
||||
export type {
|
||||
BlockStreamingCoalesceConfig,
|
||||
DmPolicy,
|
||||
DmConfig,
|
||||
GroupPolicy,
|
||||
MSTeamsChannelConfig,
|
||||
MSTeamsConfig,
|
||||
@@ -76,6 +78,14 @@ export {
|
||||
TelegramConfigSchema,
|
||||
} from "../config/zod-schema.providers-core.js";
|
||||
export { WhatsAppConfigSchema } from "../config/zod-schema.providers-whatsapp.js";
|
||||
export {
|
||||
BlockStreamingCoalesceSchema,
|
||||
DmConfigSchema,
|
||||
DmPolicySchema,
|
||||
GroupPolicySchema,
|
||||
normalizeAllowFrom,
|
||||
requireOpenAllowFrom,
|
||||
} from "../config/zod-schema.core.js";
|
||||
export type { RuntimeEnv } from "../runtime.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
||||
@@ -89,7 +99,10 @@ export {
|
||||
} from "../auto-reply/reply/history.js";
|
||||
export type { HistoryEntry } from "../auto-reply/reply/history.js";
|
||||
export { mergeAllowlist, summarizeMapping } from "../channels/allowlists/resolve-utils.js";
|
||||
export { resolveMentionGating } from "../channels/mention-gating.js";
|
||||
export {
|
||||
resolveMentionGating,
|
||||
resolveMentionGatingWithBypass,
|
||||
} from "../channels/mention-gating.js";
|
||||
export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js";
|
||||
export {
|
||||
resolveDiscordGroupRequireMention,
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from "node:path";
|
||||
|
||||
import { resolveConfigDir, resolveUserPath } from "../utils.js";
|
||||
import { resolveBundledPluginsDir } from "./bundled-dir.js";
|
||||
import type { ClawdbotManifest, PackageManifest } from "./manifest.js";
|
||||
import type { PluginDiagnostic, PluginOrigin } from "./types.js";
|
||||
|
||||
const EXTENSION_EXTS = new Set([".ts", ".js", ".mts", ".cts", ".mjs", ".cjs"]);
|
||||
@@ -16,6 +17,8 @@ export type PluginCandidate = {
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
packageDescription?: string;
|
||||
packageDir?: string;
|
||||
packageClawdbot?: ClawdbotManifest;
|
||||
};
|
||||
|
||||
export type PluginDiscoveryResult = {
|
||||
@@ -23,15 +26,6 @@ export type PluginDiscoveryResult = {
|
||||
diagnostics: PluginDiagnostic[];
|
||||
};
|
||||
|
||||
type PackageManifest = {
|
||||
name?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
clawdbot?: {
|
||||
extensions?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
function isExtensionFile(filePath: string): boolean {
|
||||
const ext = path.extname(filePath);
|
||||
if (!EXTENSION_EXTS.has(ext)) return false;
|
||||
@@ -83,6 +77,7 @@ function addCandidate(params: {
|
||||
origin: PluginOrigin;
|
||||
workspaceDir?: string;
|
||||
manifest?: PackageManifest | null;
|
||||
packageDir?: string;
|
||||
}) {
|
||||
const resolved = path.resolve(params.source);
|
||||
if (params.seen.has(resolved)) return;
|
||||
@@ -97,6 +92,8 @@ function addCandidate(params: {
|
||||
packageName: manifest?.name?.trim() || undefined,
|
||||
packageVersion: manifest?.version?.trim() || undefined,
|
||||
packageDescription: manifest?.description?.trim() || undefined,
|
||||
packageDir: params.packageDir,
|
||||
packageClawdbot: manifest?.clawdbot,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,6 +153,7 @@ function discoverInDirectory(params: {
|
||||
origin: params.origin,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
packageDir: fullPath,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
@@ -174,6 +172,8 @@ function discoverInDirectory(params: {
|
||||
rootDir: fullPath,
|
||||
origin: params.origin,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
packageDir: fullPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -239,6 +239,7 @@ function discoverFromPath(params: {
|
||||
origin: params.origin,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
packageDir: resolved,
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -258,6 +259,8 @@ function discoverFromPath(params: {
|
||||
rootDir: resolved,
|
||||
origin: params.origin,
|
||||
workspaceDir: params.workspaceDir,
|
||||
manifest,
|
||||
packageDir: resolved,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,91 +1,36 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import type { PluginConfigUiHint, PluginKind } from "./types.js";
|
||||
|
||||
export const PLUGIN_MANIFEST_FILENAME = "clawdbot.plugin.json";
|
||||
|
||||
export type PluginManifest = {
|
||||
id: string;
|
||||
configSchema: Record<string, unknown>;
|
||||
kind?: PluginKind;
|
||||
channels?: string[];
|
||||
providers?: string[];
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
uiHints?: Record<string, PluginConfigUiHint>;
|
||||
export type PluginManifestChannel = {
|
||||
id?: string;
|
||||
label?: string;
|
||||
selectionLabel?: string;
|
||||
docsPath?: string;
|
||||
docsLabel?: string;
|
||||
blurb?: string;
|
||||
order?: number;
|
||||
aliases?: string[];
|
||||
selectionDocsPrefix?: string;
|
||||
selectionDocsOmitLabel?: boolean;
|
||||
selectionExtras?: string[];
|
||||
showConfigured?: boolean;
|
||||
quickstartAllowFrom?: boolean;
|
||||
forceAccountBinding?: boolean;
|
||||
preferSessionLookupForAnnounceTarget?: boolean;
|
||||
};
|
||||
|
||||
export type PluginManifestLoadResult =
|
||||
| { ok: true; manifest: PluginManifest; manifestPath: string }
|
||||
| { ok: false; error: string; manifestPath: string };
|
||||
export type PluginManifestInstall = {
|
||||
npmSpec?: string;
|
||||
localPath?: string;
|
||||
defaultChoice?: "npm" | "local";
|
||||
};
|
||||
|
||||
function normalizeStringList(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
|
||||
}
|
||||
export type ClawdbotManifest = {
|
||||
extensions?: string[];
|
||||
channel?: PluginManifestChannel;
|
||||
install?: PluginManifestInstall;
|
||||
};
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
||||
}
|
||||
|
||||
export function resolvePluginManifestPath(rootDir: string): string {
|
||||
return path.join(rootDir, PLUGIN_MANIFEST_FILENAME);
|
||||
}
|
||||
|
||||
export function loadPluginManifest(rootDir: string): PluginManifestLoadResult {
|
||||
const manifestPath = resolvePluginManifestPath(rootDir);
|
||||
if (!fs.existsSync(manifestPath)) {
|
||||
return { ok: false, error: `plugin manifest not found: ${manifestPath}`, manifestPath };
|
||||
}
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as unknown;
|
||||
} catch (err) {
|
||||
return {
|
||||
ok: false,
|
||||
error: `failed to parse plugin manifest: ${String(err)}`,
|
||||
manifestPath,
|
||||
};
|
||||
}
|
||||
if (!isRecord(raw)) {
|
||||
return { ok: false, error: "plugin manifest must be an object", manifestPath };
|
||||
}
|
||||
const id = typeof raw.id === "string" ? raw.id.trim() : "";
|
||||
if (!id) {
|
||||
return { ok: false, error: "plugin manifest requires id", manifestPath };
|
||||
}
|
||||
const configSchema = isRecord(raw.configSchema) ? raw.configSchema : null;
|
||||
if (!configSchema) {
|
||||
return { ok: false, error: "plugin manifest requires configSchema", manifestPath };
|
||||
}
|
||||
|
||||
const kind = typeof raw.kind === "string" ? (raw.kind as PluginKind) : undefined;
|
||||
const name = typeof raw.name === "string" ? raw.name.trim() : undefined;
|
||||
const description = typeof raw.description === "string" ? raw.description.trim() : undefined;
|
||||
const version = typeof raw.version === "string" ? raw.version.trim() : undefined;
|
||||
const channels = normalizeStringList(raw.channels);
|
||||
const providers = normalizeStringList(raw.providers);
|
||||
|
||||
let uiHints: Record<string, PluginConfigUiHint> | undefined;
|
||||
if (isRecord(raw.uiHints)) {
|
||||
uiHints = raw.uiHints as Record<string, PluginConfigUiHint>;
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
manifest: {
|
||||
id,
|
||||
configSchema,
|
||||
kind,
|
||||
channels,
|
||||
providers,
|
||||
name,
|
||||
description,
|
||||
version,
|
||||
uiHints,
|
||||
},
|
||||
manifestPath,
|
||||
};
|
||||
}
|
||||
export type PackageManifest = {
|
||||
name?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
clawdbot?: ClawdbotManifest;
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ import { probeDiscord } from "../../discord/probe.js";
|
||||
import { resolveDiscordChannelAllowlist } from "../../discord/resolve-channels.js";
|
||||
import { resolveDiscordUserAllowlist } from "../../discord/resolve-users.js";
|
||||
import { sendMessageDiscord, sendPollDiscord } from "../../discord/send.js";
|
||||
import { getChannelActivity, recordChannelActivity } from "../../infra/channel-activity.js";
|
||||
import { enqueueSystemEvent } from "../../infra/system-events.js";
|
||||
import { monitorIMessageProvider } from "../../imessage/monitor.js";
|
||||
import { probeIMessage } from "../../imessage/probe.js";
|
||||
@@ -177,6 +178,10 @@ export function createPluginRuntime(): PluginRuntime {
|
||||
fetchRemoteMedia,
|
||||
saveMediaBuffer,
|
||||
},
|
||||
activity: {
|
||||
record: recordChannelActivity,
|
||||
get: getChannelActivity,
|
||||
},
|
||||
session: {
|
||||
resolveStorePath,
|
||||
readSessionUpdatedAt,
|
||||
|
||||
@@ -55,6 +55,8 @@ type ReadSessionUpdatedAt = typeof import("../../config/sessions.js").readSessio
|
||||
type UpdateLastRoute = typeof import("../../config/sessions.js").updateLastRoute;
|
||||
type LoadConfig = typeof import("../../config/config.js").loadConfig;
|
||||
type WriteConfigFile = typeof import("../../config/config.js").writeConfigFile;
|
||||
type RecordChannelActivity = typeof import("../../infra/channel-activity.js").recordChannelActivity;
|
||||
type GetChannelActivity = typeof import("../../infra/channel-activity.js").getChannelActivity;
|
||||
type EnqueueSystemEvent = typeof import("../../infra/system-events.js").enqueueSystemEvent;
|
||||
type RunCommandWithTimeout = typeof import("../../process/exec.js").runCommandWithTimeout;
|
||||
type LoadWebMedia = typeof import("../../web/media.js").loadWebMedia;
|
||||
@@ -188,6 +190,10 @@ export type PluginRuntime = {
|
||||
fetchRemoteMedia: FetchRemoteMedia;
|
||||
saveMediaBuffer: SaveMediaBuffer;
|
||||
};
|
||||
activity: {
|
||||
record: RecordChannelActivity;
|
||||
get: GetChannelActivity;
|
||||
};
|
||||
session: {
|
||||
resolveStorePath: ResolveStorePath;
|
||||
readSessionUpdatedAt: ReadSessionUpdatedAt;
|
||||
|
||||
Reference in New Issue
Block a user