feat(channels): add Feishu (Lark) support
This commit is contained in:
parent
9688454a30
commit
963c5b5a7a
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@ -9,6 +9,11 @@
|
|||||||
- "src/discord/**"
|
- "src/discord/**"
|
||||||
- "extensions/discord/**"
|
- "extensions/discord/**"
|
||||||
- "docs/channels/discord.md"
|
- "docs/channels/discord.md"
|
||||||
|
"channel: feishu":
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- "extensions/feishu/**"
|
||||||
|
- "docs/channels/feishu.md"
|
||||||
"channel: googlechat":
|
"channel: googlechat":
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file:
|
- any-glob-to-any-file:
|
||||||
|
|||||||
59
docs/channels/feishu.md
Normal file
59
docs/channels/feishu.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
summary: "Feishu (Lark) bot support status, capabilities, and configuration"
|
||||||
|
read_when:
|
||||||
|
- Working on Feishu channel features
|
||||||
|
---
|
||||||
|
# Feishu (Lark)
|
||||||
|
|
||||||
|
Status: beta; inbound via WebSocket using `@larksuiteoapi/node-sdk`.
|
||||||
|
|
||||||
|
## Quick setup
|
||||||
|
1) Create a Feishu/Lark app and enable the IM message receive event.
|
||||||
|
2) Subscribe to `im.message.receive_v1` in your app settings.
|
||||||
|
3) Configure `channels.feishu.appId` and `channels.feishu.appSecret`.
|
||||||
|
4) Start the gateway.
|
||||||
|
|
||||||
|
Minimal config:
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
channels: {
|
||||||
|
feishu: {
|
||||||
|
enabled: true,
|
||||||
|
appId: "YOUR_APP_ID",
|
||||||
|
appSecret: "YOUR_APP_SECRET"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Multi-account example:
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
channels: {
|
||||||
|
feishu: {
|
||||||
|
accounts: {
|
||||||
|
work: { appId: "APP_ID", appSecret: "APP_SECRET" },
|
||||||
|
personal: { appId: "APP_ID", appSecret: "APP_SECRET" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
- Messages are received over the Feishu WebSocket event stream.
|
||||||
|
- Replies are sent back to the same `chat_id`.
|
||||||
|
- DMs use pairing by default (`channels.feishu.dm.policy`).
|
||||||
|
- Group chats can be restricted with `channels.feishu.groupPolicy` and `channels.feishu.groups`.
|
||||||
|
|
||||||
|
## Target formats
|
||||||
|
- `chat:<chat_id>` for group chats.
|
||||||
|
- `user:<open_id>` for direct messages.
|
||||||
|
|
||||||
|
## Configuration reference (Feishu)
|
||||||
|
- `channels.feishu.appId`: Feishu App ID.
|
||||||
|
- `channels.feishu.appSecret`: Feishu App Secret.
|
||||||
|
- `channels.feishu.dm.policy`: DM policy (`pairing`, `allowlist`, `open`, `disabled`).
|
||||||
|
- `channels.feishu.dm.allowFrom`: allowlist for DMs when policy is `allowlist` or `open`.
|
||||||
|
- `channels.feishu.groupPolicy`: `open`, `allowlist`, or `disabled`.
|
||||||
|
- `channels.feishu.groups`: per-chat overrides keyed by `chat_id` (supports `requireMention`, `tools`, `users`).
|
||||||
@ -14,6 +14,7 @@ Text is supported everywhere; media and reactions vary by channel.
|
|||||||
- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.
|
- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.
|
||||||
- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
|
- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
|
||||||
- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.
|
- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.
|
||||||
|
- [Feishu](/channels/feishu) — Lark/Feishu app via WebSocket events.
|
||||||
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
|
- [Slack](/channels/slack) — Bolt SDK; workspace apps.
|
||||||
- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook.
|
- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook.
|
||||||
- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).
|
- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).
|
||||||
|
|||||||
18
extensions/feishu/index.ts
Normal file
18
extensions/feishu/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { MoltbotPluginApi } from "clawdbot/plugin-sdk";
|
||||||
|
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
import { feishuPlugin } from "./src/channel.js";
|
||||||
|
import { setFeishuRuntime } from "./src/runtime.js";
|
||||||
|
|
||||||
|
const plugin = {
|
||||||
|
id: "feishu",
|
||||||
|
name: "Feishu",
|
||||||
|
description: "Feishu channel plugin",
|
||||||
|
configSchema: emptyPluginConfigSchema(),
|
||||||
|
register(api: MoltbotPluginApi) {
|
||||||
|
setFeishuRuntime(api.runtime);
|
||||||
|
api.registerChannel({ plugin: feishuPlugin });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default plugin;
|
||||||
11
extensions/feishu/moltbot.plugin.json
Normal file
11
extensions/feishu/moltbot.plugin.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "feishu",
|
||||||
|
"name": "Feishu",
|
||||||
|
"description": "Feishu channel plugin",
|
||||||
|
"channels": ["feishu"],
|
||||||
|
"configSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
extensions/feishu/package.json
Normal file
17
extensions/feishu/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "@moltbot/feishu",
|
||||||
|
"version": "2026.1.27-beta.1",
|
||||||
|
"type": "module",
|
||||||
|
"description": "Moltbot Feishu channel plugin",
|
||||||
|
"moltbot": {
|
||||||
|
"extensions": [
|
||||||
|
"./index.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@larksuiteoapi/node-sdk": "1.56.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"moltbot": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
||||||
73
extensions/feishu/src/accounts.ts
Normal file
73
extensions/feishu/src/accounts.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import type { FeishuAccountConfig, MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||||
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
export type ResolvedFeishuAccount = {
|
||||||
|
accountId: string;
|
||||||
|
enabled: boolean;
|
||||||
|
name?: string;
|
||||||
|
appId?: string;
|
||||||
|
appSecret?: string;
|
||||||
|
config: FeishuAccountConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
function listConfiguredAccountIds(cfg: MoltbotConfig): string[] {
|
||||||
|
const accounts = cfg.channels?.feishu?.accounts;
|
||||||
|
if (!accounts || typeof accounts !== "object") return [];
|
||||||
|
return Object.keys(accounts).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listFeishuAccountIds(cfg: MoltbotConfig): string[] {
|
||||||
|
const ids = listConfiguredAccountIds(cfg);
|
||||||
|
if (ids.length === 0) return [DEFAULT_ACCOUNT_ID];
|
||||||
|
return ids.sort((a, b) => a.localeCompare(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDefaultFeishuAccountId(cfg: MoltbotConfig): string {
|
||||||
|
const ids = listFeishuAccountIds(cfg);
|
||||||
|
if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID;
|
||||||
|
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAccountConfig(
|
||||||
|
cfg: MoltbotConfig,
|
||||||
|
accountId: string,
|
||||||
|
): FeishuAccountConfig | undefined {
|
||||||
|
const accounts = cfg.channels?.feishu?.accounts;
|
||||||
|
if (!accounts || typeof accounts !== "object") return undefined;
|
||||||
|
return accounts[accountId] as FeishuAccountConfig | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeFeishuAccountConfig(cfg: MoltbotConfig, accountId: string): FeishuAccountConfig {
|
||||||
|
const { accounts: _ignored, ...base } = (cfg.channels?.feishu ?? {}) as FeishuAccountConfig & {
|
||||||
|
accounts?: unknown;
|
||||||
|
};
|
||||||
|
const account = resolveAccountConfig(cfg, accountId) ?? {};
|
||||||
|
return { ...base, ...account };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveFeishuAccount(params: {
|
||||||
|
cfg: MoltbotConfig;
|
||||||
|
accountId?: string | null;
|
||||||
|
}): ResolvedFeishuAccount {
|
||||||
|
const accountId = normalizeAccountId(params.accountId);
|
||||||
|
const baseEnabled = params.cfg.channels?.feishu?.enabled !== false;
|
||||||
|
const merged = mergeFeishuAccountConfig(params.cfg, accountId);
|
||||||
|
const accountEnabled = merged.enabled !== false;
|
||||||
|
const enabled = baseEnabled && accountEnabled;
|
||||||
|
const appId = merged.appId?.trim() || undefined;
|
||||||
|
const appSecret = merged.appSecret?.trim() || undefined;
|
||||||
|
return {
|
||||||
|
accountId,
|
||||||
|
enabled,
|
||||||
|
name: merged.name?.trim() || undefined,
|
||||||
|
appId,
|
||||||
|
appSecret,
|
||||||
|
config: merged,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listEnabledFeishuAccounts(cfg: MoltbotConfig): ResolvedFeishuAccount[] {
|
||||||
|
return listFeishuAccountIds(cfg)
|
||||||
|
.map((accountId) => resolveFeishuAccount({ cfg, accountId }))
|
||||||
|
.filter((account) => account.enabled);
|
||||||
|
}
|
||||||
318
extensions/feishu/src/channel.ts
Normal file
318
extensions/feishu/src/channel.ts
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
import type { ChannelPlugin, MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||||
|
import {
|
||||||
|
applyAccountNameToChannelSection,
|
||||||
|
buildChannelConfigSchema,
|
||||||
|
DEFAULT_ACCOUNT_ID,
|
||||||
|
deleteAccountFromConfigSection,
|
||||||
|
formatPairingApproveHint,
|
||||||
|
getChatChannelMeta,
|
||||||
|
migrateBaseNameToDefaultAccount,
|
||||||
|
missingTargetError,
|
||||||
|
normalizeAccountId,
|
||||||
|
PAIRING_APPROVED_MESSAGE,
|
||||||
|
setAccountEnabledInConfigSection,
|
||||||
|
} from "clawdbot/plugin-sdk";
|
||||||
|
import { FeishuConfigSchema } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
import {
|
||||||
|
listFeishuAccountIds,
|
||||||
|
resolveDefaultFeishuAccountId,
|
||||||
|
resolveFeishuAccount,
|
||||||
|
type ResolvedFeishuAccount,
|
||||||
|
} from "./accounts.js";
|
||||||
|
import { startFeishuMonitor } from "./monitor.js";
|
||||||
|
import { resolveFeishuGroupToolPolicy } from "./policy.js";
|
||||||
|
import { getFeishuRuntime } from "./runtime.js";
|
||||||
|
import { sendFeishuMessage } from "./send.js";
|
||||||
|
import {
|
||||||
|
looksLikeFeishuTargetId,
|
||||||
|
normalizeFeishuMessagingTarget,
|
||||||
|
} from "./targets.js";
|
||||||
|
|
||||||
|
const meta = getChatChannelMeta("feishu");
|
||||||
|
|
||||||
|
function formatAllowFromEntry(entry: string): string {
|
||||||
|
return entry
|
||||||
|
.trim()
|
||||||
|
.replace(/^(feishu|lark|user|open_id|user_id|union_id|email):/i, "")
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
||||||
|
id: "feishu",
|
||||||
|
meta: {
|
||||||
|
...meta,
|
||||||
|
},
|
||||||
|
pairing: {
|
||||||
|
idLabel: "feishuUserId",
|
||||||
|
normalizeAllowEntry: (entry) => formatAllowFromEntry(entry),
|
||||||
|
notifyApproval: async ({ cfg, id }) => {
|
||||||
|
const account = resolveFeishuAccount({
|
||||||
|
cfg: cfg as MoltbotConfig,
|
||||||
|
accountId: DEFAULT_ACCOUNT_ID,
|
||||||
|
});
|
||||||
|
if (!account.appId || !account.appSecret) return;
|
||||||
|
await sendFeishuMessage({
|
||||||
|
account,
|
||||||
|
to: `user:${id}`,
|
||||||
|
text: PAIRING_APPROVED_MESSAGE,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
capabilities: {
|
||||||
|
chatTypes: ["direct", "channel"],
|
||||||
|
reactions: false,
|
||||||
|
threads: false,
|
||||||
|
media: false,
|
||||||
|
nativeCommands: false,
|
||||||
|
},
|
||||||
|
reload: { configPrefixes: ["channels.feishu"] },
|
||||||
|
configSchema: buildChannelConfigSchema(FeishuConfigSchema),
|
||||||
|
config: {
|
||||||
|
listAccountIds: (cfg) => listFeishuAccountIds(cfg as MoltbotConfig),
|
||||||
|
resolveAccount: (cfg, accountId) =>
|
||||||
|
resolveFeishuAccount({ cfg: cfg as MoltbotConfig, accountId }),
|
||||||
|
defaultAccountId: (cfg) => resolveDefaultFeishuAccountId(cfg as MoltbotConfig),
|
||||||
|
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
||||||
|
setAccountEnabledInConfigSection({
|
||||||
|
cfg: cfg as MoltbotConfig,
|
||||||
|
sectionKey: "feishu",
|
||||||
|
accountId,
|
||||||
|
enabled,
|
||||||
|
allowTopLevel: true,
|
||||||
|
}),
|
||||||
|
deleteAccount: ({ cfg, accountId }) =>
|
||||||
|
deleteAccountFromConfigSection({
|
||||||
|
cfg: cfg as MoltbotConfig,
|
||||||
|
sectionKey: "feishu",
|
||||||
|
accountId,
|
||||||
|
clearBaseFields: ["appId", "appSecret", "name"],
|
||||||
|
}),
|
||||||
|
isConfigured: (account) => Boolean(account.appId?.trim() && account.appSecret?.trim()),
|
||||||
|
describeAccount: (account) => ({
|
||||||
|
accountId: account.accountId,
|
||||||
|
name: account.name,
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured: Boolean(account.appId?.trim() && account.appSecret?.trim()),
|
||||||
|
}),
|
||||||
|
resolveAllowFrom: ({ cfg, accountId }) =>
|
||||||
|
(resolveFeishuAccount({ cfg: cfg as MoltbotConfig, accountId }).config.dm?.allowFrom ?? []).map(
|
||||||
|
(entry) => String(entry),
|
||||||
|
),
|
||||||
|
formatAllowFrom: ({ allowFrom }) =>
|
||||||
|
allowFrom
|
||||||
|
.map((entry) => String(entry).trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(formatAllowFromEntry),
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
||||||
|
const resolvedAccountId = accountId ?? account.accountId ?? DEFAULT_ACCOUNT_ID;
|
||||||
|
const useAccountPath = Boolean(
|
||||||
|
(cfg as MoltbotConfig).channels?.feishu?.accounts?.[resolvedAccountId],
|
||||||
|
);
|
||||||
|
const allowFromPath = useAccountPath
|
||||||
|
? `channels.feishu.accounts.${resolvedAccountId}.dm.`
|
||||||
|
: "channels.feishu.dm.";
|
||||||
|
return {
|
||||||
|
policy: account.config.dm?.policy ?? "pairing",
|
||||||
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
||||||
|
allowFromPath,
|
||||||
|
approveHint: formatPairingApproveHint("feishu"),
|
||||||
|
normalizeEntry: (raw) => formatAllowFromEntry(raw),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
resolveToolPolicy: resolveFeishuGroupToolPolicy,
|
||||||
|
},
|
||||||
|
messaging: {
|
||||||
|
normalizeTarget: normalizeFeishuMessagingTarget,
|
||||||
|
targetResolver: {
|
||||||
|
looksLikeId: looksLikeFeishuTargetId,
|
||||||
|
hint: "<chatId|user:OPEN_ID|chat:ID>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
outbound: {
|
||||||
|
deliveryMode: "direct",
|
||||||
|
chunker: (text, limit) => {
|
||||||
|
const runtime = getFeishuRuntime();
|
||||||
|
return runtime.channel.text.chunkText(text, limit);
|
||||||
|
},
|
||||||
|
chunkerMode: "text",
|
||||||
|
textChunkLimit: 4000,
|
||||||
|
resolveTarget: ({ to, allowFrom, mode }) => {
|
||||||
|
const trimmed = to?.trim() ?? "";
|
||||||
|
const allowListRaw = (allowFrom ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
||||||
|
const allowList = allowListRaw
|
||||||
|
.filter((entry) => entry !== "*")
|
||||||
|
.map((entry) => normalizeFeishuMessagingTarget(entry))
|
||||||
|
.filter((entry): entry is string => Boolean(entry));
|
||||||
|
|
||||||
|
if (trimmed) {
|
||||||
|
const normalized = normalizeFeishuMessagingTarget(trimmed);
|
||||||
|
if (!normalized) {
|
||||||
|
if ((mode === "implicit" || mode === "heartbeat") && allowList.length > 0) {
|
||||||
|
return { ok: true, to: allowList[0] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: missingTargetError(
|
||||||
|
"Feishu",
|
||||||
|
"<chat:ID|user:OPEN_ID> or channels.feishu.dm.allowFrom[0]",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ok: true, to: normalized };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowList.length > 0) {
|
||||||
|
return { ok: true, to: allowList[0] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: missingTargetError(
|
||||||
|
"Feishu",
|
||||||
|
"<chat:ID|user:OPEN_ID> or channels.feishu.dm.allowFrom[0]",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sendText: async ({ cfg, to, text, accountId }) => {
|
||||||
|
const account = resolveFeishuAccount({ cfg: cfg as MoltbotConfig, accountId });
|
||||||
|
const result = await sendFeishuMessage({ account, to, text });
|
||||||
|
return { channel: "feishu", messageId: result.messageId };
|
||||||
|
},
|
||||||
|
sendMedia: async ({ mediaUrl }) => {
|
||||||
|
if (!mediaUrl) {
|
||||||
|
throw new Error("Feishu mediaUrl is required.");
|
||||||
|
}
|
||||||
|
throw new Error("Feishu media is not supported yet.");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
defaultRuntime: {
|
||||||
|
accountId: DEFAULT_ACCOUNT_ID,
|
||||||
|
running: false,
|
||||||
|
lastStartAt: null,
|
||||||
|
lastStopAt: null,
|
||||||
|
lastError: null,
|
||||||
|
},
|
||||||
|
buildChannelSummary: ({ snapshot }) => ({
|
||||||
|
configured: snapshot.configured ?? false,
|
||||||
|
running: snapshot.running ?? false,
|
||||||
|
lastStartAt: snapshot.lastStartAt ?? null,
|
||||||
|
lastStopAt: snapshot.lastStopAt ?? null,
|
||||||
|
lastError: snapshot.lastError ?? null,
|
||||||
|
lastInboundAt: snapshot.lastInboundAt ?? null,
|
||||||
|
lastOutboundAt: snapshot.lastOutboundAt ?? null,
|
||||||
|
}),
|
||||||
|
buildAccountSnapshot: ({ account, runtime }) => ({
|
||||||
|
accountId: account.accountId,
|
||||||
|
name: account.name,
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured: Boolean(account.appId?.trim() && account.appSecret?.trim()),
|
||||||
|
running: runtime?.running ?? false,
|
||||||
|
lastStartAt: runtime?.lastStartAt ?? null,
|
||||||
|
lastStopAt: runtime?.lastStopAt ?? null,
|
||||||
|
lastError: runtime?.lastError ?? null,
|
||||||
|
lastInboundAt: runtime?.lastInboundAt ?? null,
|
||||||
|
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
||||||
|
dmPolicy: account.config.dm?.policy ?? "pairing",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
gateway: {
|
||||||
|
startAccount: async (ctx) => {
|
||||||
|
const account = ctx.account;
|
||||||
|
if (!account.appId?.trim() || !account.appSecret?.trim()) {
|
||||||
|
throw new Error(
|
||||||
|
`Feishu appId/appSecret missing for account "${account.accountId}" (set channels.feishu.appId/appSecret or channels.feishu.accounts.${account.accountId}.appId/appSecret).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ctx.log?.info(`[${account.accountId}] starting Feishu websocket`);
|
||||||
|
ctx.setStatus({
|
||||||
|
accountId: account.accountId,
|
||||||
|
running: true,
|
||||||
|
lastStartAt: Date.now(),
|
||||||
|
});
|
||||||
|
const stop = await startFeishuMonitor({
|
||||||
|
account,
|
||||||
|
config: ctx.cfg as MoltbotConfig,
|
||||||
|
runtime: ctx.runtime,
|
||||||
|
abortSignal: ctx.abortSignal,
|
||||||
|
statusSink: (patch) => ctx.setStatus({ accountId: account.accountId, ...patch }),
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
stop();
|
||||||
|
ctx.setStatus({
|
||||||
|
accountId: account.accountId,
|
||||||
|
running: false,
|
||||||
|
lastStopAt: Date.now(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup: {
|
||||||
|
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
|
||||||
|
applyAccountName: ({ cfg, accountId, name }) =>
|
||||||
|
applyAccountNameToChannelSection({
|
||||||
|
cfg: cfg as MoltbotConfig,
|
||||||
|
channelKey: "feishu",
|
||||||
|
accountId,
|
||||||
|
name,
|
||||||
|
}),
|
||||||
|
validateInput: ({ input }) => {
|
||||||
|
if (!input.appId || !input.appSecret) {
|
||||||
|
return "Feishu requires appId and appSecret.";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
applyAccountConfig: ({ cfg, accountId, input }) => {
|
||||||
|
const namedConfig = applyAccountNameToChannelSection({
|
||||||
|
cfg: cfg as MoltbotConfig,
|
||||||
|
channelKey: "feishu",
|
||||||
|
accountId,
|
||||||
|
name: input.name,
|
||||||
|
});
|
||||||
|
const next =
|
||||||
|
accountId !== DEFAULT_ACCOUNT_ID
|
||||||
|
? migrateBaseNameToDefaultAccount({
|
||||||
|
cfg: namedConfig,
|
||||||
|
channelKey: "feishu",
|
||||||
|
})
|
||||||
|
: namedConfig;
|
||||||
|
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||||
|
return {
|
||||||
|
...next,
|
||||||
|
channels: {
|
||||||
|
...next.channels,
|
||||||
|
feishu: {
|
||||||
|
...next.channels?.feishu,
|
||||||
|
enabled: true,
|
||||||
|
appId: input.appId,
|
||||||
|
appSecret: input.appSecret,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as MoltbotConfig;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...next,
|
||||||
|
channels: {
|
||||||
|
...next.channels,
|
||||||
|
feishu: {
|
||||||
|
...next.channels?.feishu,
|
||||||
|
enabled: true,
|
||||||
|
accounts: {
|
||||||
|
...next.channels?.feishu?.accounts,
|
||||||
|
[accountId]: {
|
||||||
|
...next.channels?.feishu?.accounts?.[accountId],
|
||||||
|
enabled: true,
|
||||||
|
appId: input.appId,
|
||||||
|
appSecret: input.appSecret,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as MoltbotConfig;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
498
extensions/feishu/src/monitor.ts
Normal file
498
extensions/feishu/src/monitor.ts
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
||||||
|
|
||||||
|
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||||
|
import {
|
||||||
|
resolveMentionGatingWithBypass,
|
||||||
|
type ReplyPayload,
|
||||||
|
} from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
import { getFeishuRuntime } from "./runtime.js";
|
||||||
|
import type { ResolvedFeishuAccount } from "./accounts.js";
|
||||||
|
import { sendFeishuMessage } from "./send.js";
|
||||||
|
|
||||||
|
export type FeishuRuntimeEnv = {
|
||||||
|
log?: (message: string) => void;
|
||||||
|
error?: (message: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeishuMonitorOptions = {
|
||||||
|
account: ResolvedFeishuAccount;
|
||||||
|
config: MoltbotConfig;
|
||||||
|
runtime: FeishuRuntimeEnv;
|
||||||
|
abortSignal: AbortSignal;
|
||||||
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FeishuSender = {
|
||||||
|
sender_type?: string;
|
||||||
|
sender_id?: {
|
||||||
|
user_id?: string;
|
||||||
|
open_id?: string;
|
||||||
|
union_id?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type FeishuMessage = {
|
||||||
|
chat_id?: string;
|
||||||
|
chat_type?: string;
|
||||||
|
message_id?: string;
|
||||||
|
message_type?: string;
|
||||||
|
content?: string;
|
||||||
|
create_time?: string;
|
||||||
|
root_id?: string;
|
||||||
|
parent_id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FeishuMessageEvent = {
|
||||||
|
message?: FeishuMessage;
|
||||||
|
sender?: FeishuSender;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FeishuCoreRuntime = ReturnType<typeof getFeishuRuntime>;
|
||||||
|
|
||||||
|
type FeishuWsClient = Lark.WSClient & {
|
||||||
|
stop?: () => void | Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function logVerbose(core: FeishuCoreRuntime, runtime: FeishuRuntimeEnv, message: string): void {
|
||||||
|
if (core.logging.shouldLogVerbose()) {
|
||||||
|
runtime.log?.(`[feishu] ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireFeishuCredentials(account: ResolvedFeishuAccount): {
|
||||||
|
appId: string;
|
||||||
|
appSecret: string;
|
||||||
|
} {
|
||||||
|
const appId = account.appId?.trim();
|
||||||
|
const appSecret = account.appSecret?.trim();
|
||||||
|
if (!appId || !appSecret) {
|
||||||
|
throw new Error(
|
||||||
|
`Feishu credentials missing for account "${account.accountId}" (set channels.feishu.appId/appSecret or channels.feishu.accounts.${account.accountId}.appId/appSecret).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { appId, appSecret };
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSenderInfo(
|
||||||
|
sender?: FeishuSender,
|
||||||
|
): { id: string; idType: "open_id" | "user_id" | "union_id" } | null {
|
||||||
|
if (!sender) return null;
|
||||||
|
const ids = sender.sender_id;
|
||||||
|
if (ids?.open_id?.trim()) {
|
||||||
|
return { id: ids.open_id.trim(), idType: "open_id" };
|
||||||
|
}
|
||||||
|
if (ids?.user_id?.trim()) {
|
||||||
|
return { id: ids.user_id.trim(), idType: "user_id" };
|
||||||
|
}
|
||||||
|
if (ids?.union_id?.trim()) {
|
||||||
|
return { id: ids.union_id.trim(), idType: "union_id" };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMessageText(params: {
|
||||||
|
content: string;
|
||||||
|
runtime: FeishuRuntimeEnv;
|
||||||
|
accountId: string;
|
||||||
|
}): string | null {
|
||||||
|
const trimmed = params.content.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed) as unknown;
|
||||||
|
if (!parsed || typeof parsed !== "object") return null;
|
||||||
|
const text = (parsed as { text?: unknown }).text;
|
||||||
|
return typeof text === "string" ? text : null;
|
||||||
|
} catch (err) {
|
||||||
|
params.runtime.error?.(
|
||||||
|
`[${params.accountId}] Feishu message content parse failed: ${String(err)}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMentionState(text: string): { hasAnyMention: boolean; wasMentioned: boolean } {
|
||||||
|
const hasAnyMention = /<at\b/i.test(text);
|
||||||
|
return { hasAnyMention, wasMentioned: hasAnyMention };
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAllowEntry(raw: string): string {
|
||||||
|
return raw
|
||||||
|
.trim()
|
||||||
|
.replace(/^(feishu|lark|user|open_id|user_id|union_id|email):/i, "")
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSenderAllowed(senderId: string, allowFrom: string[]): boolean {
|
||||||
|
if (allowFrom.length === 0) return false;
|
||||||
|
const normalizedSender = normalizeAllowEntry(senderId);
|
||||||
|
for (const entry of allowFrom) {
|
||||||
|
const normalized = normalizeAllowEntry(entry);
|
||||||
|
if (!normalized) continue;
|
||||||
|
if (normalized === "*") return true;
|
||||||
|
if (normalized === normalizedSender) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveGroupEntry(account: ResolvedFeishuAccount, chatId: string) {
|
||||||
|
const groups = account.config.groups ?? {};
|
||||||
|
const entry = groups[chatId];
|
||||||
|
const wildcard = groups["*"];
|
||||||
|
const allowlistConfigured = Object.keys(groups).length > 0;
|
||||||
|
return { entry, wildcard, allowlistConfigured };
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveGroupPolicy(cfg: MoltbotConfig, account: ResolvedFeishuAccount): "open" | "allowlist" | "disabled" {
|
||||||
|
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
||||||
|
return account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveChatType(chatType?: string): "direct" | "channel" {
|
||||||
|
if (!chatType) return "channel";
|
||||||
|
return chatType === "p2p" ? "direct" : "channel";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopFeishuWsClient(wsClient: FeishuWsClient, runtime: FeishuRuntimeEnv): Promise<void> {
|
||||||
|
const stop = wsClient.stop;
|
||||||
|
if (typeof stop !== "function") return;
|
||||||
|
try {
|
||||||
|
await Promise.resolve(stop.call(wsClient));
|
||||||
|
} catch (err) {
|
||||||
|
runtime.error?.(`feishu websocket stop failed: ${String(err)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deliverFeishuReply(params: {
|
||||||
|
payload: ReplyPayload;
|
||||||
|
account: ResolvedFeishuAccount;
|
||||||
|
chatId: string;
|
||||||
|
runtime: FeishuRuntimeEnv;
|
||||||
|
core: FeishuCoreRuntime;
|
||||||
|
config: MoltbotConfig;
|
||||||
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { payload, account, chatId, runtime, core, config, statusSink } = params;
|
||||||
|
const text = payload.text ?? "";
|
||||||
|
if (!text.trim()) return;
|
||||||
|
const textLimit = core.channel.text.resolveTextChunkLimit(config, "feishu", account.accountId, {
|
||||||
|
fallbackLimit: 4000,
|
||||||
|
});
|
||||||
|
const chunkMode = core.channel.text.resolveChunkMode(config, "feishu", account.accountId);
|
||||||
|
const chunks = core.channel.text.chunkTextWithMode(text, textLimit, chunkMode);
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
await sendFeishuMessage({ account, to: `chat:${chatId}`, text: chunk });
|
||||||
|
statusSink?.({ lastOutboundAt: Date.now() });
|
||||||
|
}
|
||||||
|
logVerbose(core, runtime, `reply sent to chat=${chatId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleFeishuMessage(params: {
|
||||||
|
event: FeishuMessageEvent;
|
||||||
|
account: ResolvedFeishuAccount;
|
||||||
|
config: MoltbotConfig;
|
||||||
|
runtime: FeishuRuntimeEnv;
|
||||||
|
core: FeishuCoreRuntime;
|
||||||
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { event, account, config, runtime, core, statusSink } = params;
|
||||||
|
const message = event.message;
|
||||||
|
if (!message) return;
|
||||||
|
const chatId = message.chat_id?.trim();
|
||||||
|
if (!chatId) return;
|
||||||
|
const senderInfo = resolveSenderInfo(event.sender);
|
||||||
|
if (!senderInfo) return;
|
||||||
|
const senderId = senderInfo.id;
|
||||||
|
const senderType = event.sender?.sender_type;
|
||||||
|
if (senderType && senderType !== "user" && account.config.allowBots !== true) {
|
||||||
|
logVerbose(core, runtime, `drop bot message senderType=${senderType}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const messageType = message.message_type?.trim() || "";
|
||||||
|
if (messageType && messageType !== "text") {
|
||||||
|
logVerbose(core, runtime, `drop unsupported message type=${messageType}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof message.content !== "string") return;
|
||||||
|
const rawBody = parseMessageText({
|
||||||
|
content: message.content,
|
||||||
|
runtime,
|
||||||
|
accountId: account.accountId,
|
||||||
|
});
|
||||||
|
if (rawBody === null) return;
|
||||||
|
const chatType = resolveChatType(message.chat_type);
|
||||||
|
const isGroup = chatType !== "direct";
|
||||||
|
|
||||||
|
statusSink?.({ lastInboundAt: Date.now() });
|
||||||
|
|
||||||
|
const groupPolicy = resolveGroupPolicy(config, account);
|
||||||
|
const groupInfo = resolveGroupEntry(account, chatId);
|
||||||
|
const groupEntry = groupInfo.entry;
|
||||||
|
if (isGroup) {
|
||||||
|
if (groupPolicy === "disabled") return;
|
||||||
|
const groupAllowed = Boolean(groupEntry) || Boolean(groupInfo.wildcard);
|
||||||
|
if (groupPolicy === "allowlist") {
|
||||||
|
if (!groupInfo.allowlistConfigured) {
|
||||||
|
logVerbose(core, runtime, `drop group message (allowlist empty, chat=${chatId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!groupAllowed) {
|
||||||
|
logVerbose(core, runtime, `drop group message (not allowlisted, chat=${chatId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (groupEntry?.enabled === false || groupEntry?.allow === false) {
|
||||||
|
logVerbose(core, runtime, `drop group message (disabled, chat=${chatId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (groupEntry?.users && groupEntry.users.length > 0) {
|
||||||
|
const ok = isSenderAllowed(senderId, groupEntry.users.map((value) => String(value)));
|
||||||
|
if (!ok) {
|
||||||
|
logVerbose(core, runtime, `drop group message (sender not allowed, ${senderId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dmPolicy = account.config.dm?.policy ?? "pairing";
|
||||||
|
const configAllowFrom = (account.config.dm?.allowFrom ?? []).map((value) => String(value));
|
||||||
|
const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, config);
|
||||||
|
const storeAllowFrom = !isGroup && (dmPolicy !== "open" || shouldComputeAuth)
|
||||||
|
? await core.channel.pairing.readAllowFromStore("feishu").catch((err) => {
|
||||||
|
runtime.error?.(`feishu: failed reading allowFrom store: ${String(err)}`);
|
||||||
|
return [];
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
const effectiveAllowFrom = [...configAllowFrom, ...storeAllowFrom];
|
||||||
|
const commandAllowFrom = isGroup
|
||||||
|
? (groupEntry?.users ?? []).map((value) => String(value))
|
||||||
|
: effectiveAllowFrom;
|
||||||
|
const useAccessGroups = config.commands?.useAccessGroups !== false;
|
||||||
|
const senderAllowedForCommands = isSenderAllowed(senderId, commandAllowFrom);
|
||||||
|
const commandAuthorized = shouldComputeAuth
|
||||||
|
? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
|
||||||
|
useAccessGroups,
|
||||||
|
authorizers: [
|
||||||
|
{
|
||||||
|
configured: commandAllowFrom.length > 0,
|
||||||
|
allowed: senderAllowedForCommands,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
let effectiveWasMentioned: boolean | undefined;
|
||||||
|
if (isGroup) {
|
||||||
|
const requireMention = groupEntry?.requireMention ?? account.config.requireMention ?? true;
|
||||||
|
const mentionState = resolveMentionState(rawBody);
|
||||||
|
const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
|
||||||
|
cfg: config,
|
||||||
|
surface: "feishu",
|
||||||
|
});
|
||||||
|
const mentionGate = resolveMentionGatingWithBypass({
|
||||||
|
isGroup: true,
|
||||||
|
requireMention,
|
||||||
|
canDetectMention: true,
|
||||||
|
wasMentioned: mentionState.wasMentioned,
|
||||||
|
implicitMention: false,
|
||||||
|
hasAnyMention: mentionState.hasAnyMention,
|
||||||
|
allowTextCommands,
|
||||||
|
hasControlCommand: core.channel.text.hasControlCommand(rawBody, config),
|
||||||
|
commandAuthorized: commandAuthorized === true,
|
||||||
|
});
|
||||||
|
effectiveWasMentioned = mentionGate.effectiveWasMentioned;
|
||||||
|
if (mentionGate.shouldSkip) {
|
||||||
|
logVerbose(core, runtime, `drop group message (mention required, chat=${chatId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGroup) {
|
||||||
|
if (dmPolicy === "disabled" || account.config.dm?.enabled === false) {
|
||||||
|
logVerbose(core, runtime, `drop DM (dmPolicy=disabled, sender=${senderId})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dmPolicy !== "open") {
|
||||||
|
const allowed = senderAllowedForCommands;
|
||||||
|
if (!allowed) {
|
||||||
|
if (dmPolicy === "pairing") {
|
||||||
|
const { code, created } = await core.channel.pairing.upsertPairingRequest({
|
||||||
|
channel: "feishu",
|
||||||
|
id: senderId,
|
||||||
|
});
|
||||||
|
if (created) {
|
||||||
|
try {
|
||||||
|
await sendFeishuMessage({
|
||||||
|
account,
|
||||||
|
to: `${senderInfo.idType}:${senderId}`,
|
||||||
|
text: core.channel.pairing.buildPairingReply({
|
||||||
|
channel: "feishu",
|
||||||
|
idLine: `Your Feishu user id: ${senderId}`,
|
||||||
|
code,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
statusSink?.({ lastOutboundAt: Date.now() });
|
||||||
|
} catch (err) {
|
||||||
|
runtime.error?.(`feishu pairing reply failed: ${String(err)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logVerbose(core, runtime, `drop DM (unauthorized, sender=${senderId})`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isGroup &&
|
||||||
|
core.channel.commands.isControlCommandMessage(rawBody, config) &&
|
||||||
|
commandAuthorized !== true
|
||||||
|
) {
|
||||||
|
logVerbose(core, runtime, `feishu: drop control command from ${senderId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = core.channel.routing.resolveAgentRoute({
|
||||||
|
cfg: config,
|
||||||
|
channel: "feishu",
|
||||||
|
accountId: account.accountId,
|
||||||
|
peer: {
|
||||||
|
kind: isGroup ? "group" : "dm",
|
||||||
|
id: chatId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fromLabel = isGroup ? `chat:${chatId}` : `user:${senderId}`;
|
||||||
|
const storePath = core.channel.session.resolveStorePath(config.session?.store, {
|
||||||
|
agentId: route.agentId,
|
||||||
|
});
|
||||||
|
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(config);
|
||||||
|
const previousTimestamp = core.channel.session.readSessionUpdatedAt({
|
||||||
|
storePath,
|
||||||
|
sessionKey: route.sessionKey,
|
||||||
|
});
|
||||||
|
const timestampMs = Number(message.create_time);
|
||||||
|
const timestamp = Number.isFinite(timestampMs) ? timestampMs : undefined;
|
||||||
|
const body = core.channel.reply.formatAgentEnvelope({
|
||||||
|
channel: "Feishu",
|
||||||
|
from: fromLabel,
|
||||||
|
timestamp,
|
||||||
|
previousTimestamp,
|
||||||
|
envelope: envelopeOptions,
|
||||||
|
body: rawBody,
|
||||||
|
});
|
||||||
|
|
||||||
|
const groupSystemPrompt = groupEntry?.systemPrompt?.trim() || undefined;
|
||||||
|
|
||||||
|
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
||||||
|
Body: body,
|
||||||
|
RawBody: rawBody,
|
||||||
|
CommandBody: rawBody,
|
||||||
|
From: `feishu:${senderId}`,
|
||||||
|
To: `feishu:${chatId}`,
|
||||||
|
SessionKey: route.sessionKey,
|
||||||
|
AccountId: route.accountId,
|
||||||
|
ChatType: isGroup ? "channel" : "direct",
|
||||||
|
ConversationLabel: fromLabel,
|
||||||
|
SenderId: senderId,
|
||||||
|
WasMentioned: isGroup ? effectiveWasMentioned : undefined,
|
||||||
|
CommandAuthorized: commandAuthorized,
|
||||||
|
Provider: "feishu",
|
||||||
|
Surface: "feishu",
|
||||||
|
MessageSid: message.message_id,
|
||||||
|
MessageSidFull: message.message_id,
|
||||||
|
ReplyToId: message.parent_id ?? message.root_id,
|
||||||
|
ReplyToIdFull: message.parent_id ?? message.root_id,
|
||||||
|
GroupSpace: isGroup ? chatId : undefined,
|
||||||
|
GroupSystemPrompt: isGroup ? groupSystemPrompt : undefined,
|
||||||
|
OriginatingChannel: "feishu",
|
||||||
|
OriginatingTo: `feishu:${chatId}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
void core.channel.session
|
||||||
|
.recordSessionMetaFromInbound({
|
||||||
|
storePath,
|
||||||
|
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
||||||
|
ctx: ctxPayload,
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
runtime.error?.(`feishu: failed updating session meta: ${String(err)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
||||||
|
ctx: ctxPayload,
|
||||||
|
cfg: config,
|
||||||
|
dispatcherOptions: {
|
||||||
|
deliver: async (payload) => {
|
||||||
|
await deliverFeishuReply({
|
||||||
|
payload,
|
||||||
|
account,
|
||||||
|
chatId,
|
||||||
|
runtime,
|
||||||
|
core,
|
||||||
|
config,
|
||||||
|
statusSink,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (err, info) => {
|
||||||
|
runtime.error?.(`[${account.accountId}] Feishu ${info.kind} reply failed: ${String(err)}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startFeishuMonitor(opts: FeishuMonitorOptions): Promise<() => void> {
|
||||||
|
const core = getFeishuRuntime();
|
||||||
|
const { appId, appSecret } = requireFeishuCredentials(opts.account);
|
||||||
|
const loggerLevel = core.logging.shouldLogVerbose()
|
||||||
|
? Lark.LoggerLevel.debug
|
||||||
|
: Lark.LoggerLevel.error;
|
||||||
|
const wsClient: FeishuWsClient = new Lark.WSClient({
|
||||||
|
appId,
|
||||||
|
appSecret,
|
||||||
|
loggerLevel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dispatcher = new Lark.EventDispatcher({}).register({
|
||||||
|
"im.message.receive_v1": async (data: unknown) => {
|
||||||
|
const event = data as FeishuMessageEvent;
|
||||||
|
try {
|
||||||
|
await handleFeishuMessage({
|
||||||
|
event,
|
||||||
|
account: opts.account,
|
||||||
|
config: opts.config,
|
||||||
|
runtime: opts.runtime,
|
||||||
|
core,
|
||||||
|
statusSink: opts.statusSink,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
opts.runtime.error?.(`feishu inbound handler failed: ${String(err)}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.resolve(
|
||||||
|
wsClient.start({
|
||||||
|
eventDispatcher: dispatcher,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const abortHandler = () => {
|
||||||
|
void stopFeishuWsClient(wsClient, opts.runtime);
|
||||||
|
};
|
||||||
|
if (opts.abortSignal.aborted) {
|
||||||
|
abortHandler();
|
||||||
|
} else {
|
||||||
|
opts.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
logVerbose(core, opts.runtime, `Feishu WS connected for account=${opts.account.accountId}`);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
opts.abortSignal.removeEventListener("abort", abortHandler);
|
||||||
|
void stopFeishuWsClient(wsClient, opts.runtime);
|
||||||
|
logVerbose(core, opts.runtime, `Feishu WS stopped for account=${opts.account.accountId}`);
|
||||||
|
};
|
||||||
|
}
|
||||||
62
extensions/feishu/src/policy.ts
Normal file
62
extensions/feishu/src/policy.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import type { GroupToolPolicyConfig, MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||||
|
import { normalizeAccountId, resolveToolsBySender } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
type FeishuGroupConfig = {
|
||||||
|
tools?: GroupToolPolicyConfig;
|
||||||
|
toolsBySender?: Record<string, GroupToolPolicyConfig>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FeishuGroupsConfig = Record<string, FeishuGroupConfig | undefined> | undefined;
|
||||||
|
|
||||||
|
function resolveGroupsConfig(
|
||||||
|
cfg: MoltbotConfig,
|
||||||
|
accountId?: string | null,
|
||||||
|
): FeishuGroupsConfig {
|
||||||
|
const normalizedAccountId = normalizeAccountId(accountId);
|
||||||
|
const channelConfig = cfg.channels?.feishu as
|
||||||
|
| {
|
||||||
|
accounts?: Record<string, { groups?: FeishuGroupsConfig }>;
|
||||||
|
groups?: FeishuGroupsConfig;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
if (!channelConfig) return undefined;
|
||||||
|
const accountGroups =
|
||||||
|
channelConfig.accounts?.[normalizedAccountId]?.groups ??
|
||||||
|
channelConfig.accounts?.[
|
||||||
|
Object.keys(channelConfig.accounts ?? {}).find(
|
||||||
|
(key) => key.toLowerCase() === normalizedAccountId.toLowerCase(),
|
||||||
|
) ?? ""
|
||||||
|
]?.groups;
|
||||||
|
return accountGroups ?? channelConfig.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveFeishuGroupToolPolicy(params: {
|
||||||
|
cfg: MoltbotConfig;
|
||||||
|
accountId?: string | null;
|
||||||
|
groupId?: string | null;
|
||||||
|
senderId?: string | null;
|
||||||
|
senderName?: string | null;
|
||||||
|
senderUsername?: string | null;
|
||||||
|
}): GroupToolPolicyConfig | undefined {
|
||||||
|
const groups = resolveGroupsConfig(params.cfg, params.accountId);
|
||||||
|
if (!groups) return undefined;
|
||||||
|
const groupId = params.groupId?.trim();
|
||||||
|
const groupConfig = groupId ? groups[groupId] : undefined;
|
||||||
|
const defaultConfig = groups["*"];
|
||||||
|
const groupSenderPolicy = resolveToolsBySender({
|
||||||
|
toolsBySender: groupConfig?.toolsBySender,
|
||||||
|
senderId: params.senderId,
|
||||||
|
senderName: params.senderName,
|
||||||
|
senderUsername: params.senderUsername,
|
||||||
|
});
|
||||||
|
if (groupSenderPolicy) return groupSenderPolicy;
|
||||||
|
if (groupConfig?.tools) return groupConfig.tools;
|
||||||
|
const defaultSenderPolicy = resolveToolsBySender({
|
||||||
|
toolsBySender: defaultConfig?.toolsBySender,
|
||||||
|
senderId: params.senderId,
|
||||||
|
senderName: params.senderName,
|
||||||
|
senderUsername: params.senderUsername,
|
||||||
|
});
|
||||||
|
if (defaultSenderPolicy) return defaultSenderPolicy;
|
||||||
|
return defaultConfig?.tools;
|
||||||
|
}
|
||||||
14
extensions/feishu/src/runtime.ts
Normal file
14
extensions/feishu/src/runtime.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { PluginRuntime } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
let runtime: PluginRuntime | null = null;
|
||||||
|
|
||||||
|
export function setFeishuRuntime(next: PluginRuntime) {
|
||||||
|
runtime = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFeishuRuntime(): PluginRuntime {
|
||||||
|
if (!runtime) {
|
||||||
|
throw new Error("Feishu runtime not initialized");
|
||||||
|
}
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
83
extensions/feishu/src/send.ts
Normal file
83
extensions/feishu/src/send.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
||||||
|
|
||||||
|
import type { MoltbotConfig } from "clawdbot/plugin-sdk";
|
||||||
|
|
||||||
|
import { resolveFeishuAccount, type ResolvedFeishuAccount } from "./accounts.js";
|
||||||
|
import { parseFeishuTarget, type FeishuReceiveIdType } from "./targets.js";
|
||||||
|
|
||||||
|
export type SendFeishuMessageParams = {
|
||||||
|
account: ResolvedFeishuAccount;
|
||||||
|
to: string;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SendFeishuMessageResult = {
|
||||||
|
messageId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function requireFeishuCredentials(account: ResolvedFeishuAccount): {
|
||||||
|
appId: string;
|
||||||
|
appSecret: string;
|
||||||
|
} {
|
||||||
|
const appId = account.appId?.trim();
|
||||||
|
const appSecret = account.appSecret?.trim();
|
||||||
|
if (!appId || !appSecret) {
|
||||||
|
throw new Error(
|
||||||
|
`Feishu credentials missing for account "${account.accountId}" (set channels.feishu.appId/appSecret or channels.feishu.accounts.${account.accountId}.appId/appSecret).`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { appId, appSecret };
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTextContent(text: string): string {
|
||||||
|
return JSON.stringify({ text });
|
||||||
|
}
|
||||||
|
|
||||||
|
function readMessageId(value: unknown): string {
|
||||||
|
if (!value || typeof value !== "object") return "";
|
||||||
|
const data = (value as { data?: unknown }).data;
|
||||||
|
if (!data || typeof data !== "object") return "";
|
||||||
|
const record = data as { message_id?: unknown; messageId?: unknown };
|
||||||
|
if (typeof record.message_id === "string") return record.message_id;
|
||||||
|
if (typeof record.messageId === "string") return record.messageId;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTarget(input: string): { receiveIdType: FeishuReceiveIdType; receiveId: string } {
|
||||||
|
const parsed = parseFeishuTarget(input);
|
||||||
|
if (!parsed) {
|
||||||
|
throw new Error(
|
||||||
|
"Feishu target is required (use chat:<id>, user:<open_id>, or open_id/user_id/union_id/email prefixes).",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendFeishuMessage(
|
||||||
|
params: SendFeishuMessageParams,
|
||||||
|
): Promise<SendFeishuMessageResult> {
|
||||||
|
const { appId, appSecret } = requireFeishuCredentials(params.account);
|
||||||
|
const target = resolveTarget(params.to);
|
||||||
|
const client = new Lark.Client({ appId, appSecret });
|
||||||
|
const response = await client.im.v1.message.create({
|
||||||
|
params: {
|
||||||
|
receive_id_type: target.receiveIdType,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
receive_id: target.receiveId,
|
||||||
|
content: buildTextContent(params.text ?? ""),
|
||||||
|
msg_type: "text",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { messageId: readMessageId(response) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendFeishuMessageFromConfig(params: {
|
||||||
|
cfg: MoltbotConfig;
|
||||||
|
accountId?: string | null;
|
||||||
|
to: string;
|
||||||
|
text: string;
|
||||||
|
}): Promise<SendFeishuMessageResult> {
|
||||||
|
const account = resolveFeishuAccount({ cfg: params.cfg, accountId: params.accountId });
|
||||||
|
return await sendFeishuMessage({ account, to: params.to, text: params.text });
|
||||||
|
}
|
||||||
61
extensions/feishu/src/targets.ts
Normal file
61
extensions/feishu/src/targets.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
export type FeishuReceiveIdType = "chat_id" | "open_id" | "user_id" | "union_id" | "email";
|
||||||
|
|
||||||
|
export type FeishuTarget = {
|
||||||
|
receiveIdType: FeishuReceiveIdType;
|
||||||
|
receiveId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeInput(raw: string): string {
|
||||||
|
return raw.trim().replace(/^feishu:/i, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseFeishuTarget(raw: string): FeishuTarget | null {
|
||||||
|
const trimmed = normalizeInput(raw);
|
||||||
|
if (!trimmed) return null;
|
||||||
|
const lowered = trimmed.toLowerCase();
|
||||||
|
if (lowered.startsWith("chat:")) {
|
||||||
|
const id = trimmed.slice("chat:".length).trim();
|
||||||
|
return id ? { receiveIdType: "chat_id", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (lowered.startsWith("user:")) {
|
||||||
|
const id = trimmed.slice("user:".length).trim();
|
||||||
|
return id ? { receiveIdType: "open_id", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (lowered.startsWith("open_id:")) {
|
||||||
|
const id = trimmed.slice("open_id:".length).trim();
|
||||||
|
return id ? { receiveIdType: "open_id", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (lowered.startsWith("user_id:")) {
|
||||||
|
const id = trimmed.slice("user_id:".length).trim();
|
||||||
|
return id ? { receiveIdType: "user_id", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (lowered.startsWith("union_id:")) {
|
||||||
|
const id = trimmed.slice("union_id:".length).trim();
|
||||||
|
return id ? { receiveIdType: "union_id", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (lowered.startsWith("email:")) {
|
||||||
|
const id = trimmed.slice("email:".length).trim();
|
||||||
|
return id ? { receiveIdType: "email", receiveId: id } : null;
|
||||||
|
}
|
||||||
|
if (/^ou_/i.test(trimmed)) return { receiveIdType: "open_id", receiveId: trimmed };
|
||||||
|
if (/^oc_/i.test(trimmed)) return { receiveIdType: "chat_id", receiveId: trimmed };
|
||||||
|
return { receiveIdType: "chat_id", receiveId: trimmed };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function looksLikeFeishuTargetId(raw: string): boolean {
|
||||||
|
const trimmed = normalizeInput(raw);
|
||||||
|
if (!trimmed) return false;
|
||||||
|
if (/^(chat|user|open_id|user_id|union_id|email):/i.test(trimmed)) return true;
|
||||||
|
return /^oc_/.test(trimmed) || /^ou_/.test(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeFeishuMessagingTarget(raw: string): string | undefined {
|
||||||
|
const parsed = parseFeishuTarget(raw);
|
||||||
|
if (!parsed) return undefined;
|
||||||
|
if (parsed.receiveIdType === "chat_id") return `chat:${parsed.receiveId}`;
|
||||||
|
if (parsed.receiveIdType === "open_id") return `user:${parsed.receiveId}`;
|
||||||
|
if (parsed.receiveIdType === "user_id") return `user_id:${parsed.receiveId}`;
|
||||||
|
if (parsed.receiveIdType === "union_id") return `union_id:${parsed.receiveId}`;
|
||||||
|
if (parsed.receiveIdType === "email") return `email:${parsed.receiveId}`;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
224
pnpm-lock.yaml
generated
224
pnpm-lock.yaml
generated
@ -172,6 +172,13 @@ importers:
|
|||||||
zod:
|
zod:
|
||||||
specifier: ^4.3.6
|
specifier: ^4.3.6
|
||||||
version: 4.3.6
|
version: 4.3.6
|
||||||
|
optionalDependencies:
|
||||||
|
'@napi-rs/canvas':
|
||||||
|
specifier: ^0.1.88
|
||||||
|
version: 0.1.88
|
||||||
|
node-llama-cpp:
|
||||||
|
specifier: 3.15.0
|
||||||
|
version: 3.15.0(typescript@5.9.3)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@grammyjs/types':
|
'@grammyjs/types':
|
||||||
specifier: ^3.23.0
|
specifier: ^3.23.0
|
||||||
@ -254,13 +261,6 @@ importers:
|
|||||||
wireit:
|
wireit:
|
||||||
specifier: ^0.14.12
|
specifier: ^0.14.12
|
||||||
version: 0.14.12
|
version: 0.14.12
|
||||||
optionalDependencies:
|
|
||||||
'@napi-rs/canvas':
|
|
||||||
specifier: ^0.1.88
|
|
||||||
version: 0.1.88
|
|
||||||
node-llama-cpp:
|
|
||||||
specifier: 3.15.0
|
|
||||||
version: 3.15.0(typescript@5.9.3)
|
|
||||||
|
|
||||||
extensions/bluebubbles: {}
|
extensions/bluebubbles: {}
|
||||||
|
|
||||||
@ -304,6 +304,16 @@ importers:
|
|||||||
|
|
||||||
extensions/discord: {}
|
extensions/discord: {}
|
||||||
|
|
||||||
|
extensions/feishu:
|
||||||
|
dependencies:
|
||||||
|
'@larksuiteoapi/node-sdk':
|
||||||
|
specifier: 1.56.1
|
||||||
|
version: 1.56.1
|
||||||
|
devDependencies:
|
||||||
|
moltbot:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../..
|
||||||
|
|
||||||
extensions/google-antigravity-auth: {}
|
extensions/google-antigravity-auth: {}
|
||||||
|
|
||||||
extensions/google-gemini-cli-auth: {}
|
extensions/google-gemini-cli-auth: {}
|
||||||
@ -383,12 +393,12 @@ importers:
|
|||||||
'@microsoft/agents-hosting-extensions-teams':
|
'@microsoft/agents-hosting-extensions-teams':
|
||||||
specifier: ^1.2.2
|
specifier: ^1.2.2
|
||||||
version: 1.2.2
|
version: 1.2.2
|
||||||
moltbot:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../..
|
|
||||||
express:
|
express:
|
||||||
specifier: ^5.2.1
|
specifier: ^5.2.1
|
||||||
version: 5.2.1
|
version: 5.2.1
|
||||||
|
moltbot:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../..
|
||||||
proper-lockfile:
|
proper-lockfile:
|
||||||
specifier: ^4.1.2
|
specifier: ^4.1.2
|
||||||
version: 4.1.2
|
version: 4.1.2
|
||||||
@ -1126,89 +1136,105 @@ packages:
|
|||||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||||
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||||
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||||
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@img/sharp-linux-arm64@0.34.5':
|
'@img/sharp-linux-arm64@0.34.5':
|
||||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linux-arm@0.34.5':
|
'@img/sharp-linux-arm@0.34.5':
|
||||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linux-ppc64@0.34.5':
|
'@img/sharp-linux-ppc64@0.34.5':
|
||||||
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linux-riscv64@0.34.5':
|
'@img/sharp-linux-riscv64@0.34.5':
|
||||||
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linux-s390x@0.34.5':
|
'@img/sharp-linux-s390x@0.34.5':
|
||||||
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linux-x64@0.34.5':
|
'@img/sharp-linux-x64@0.34.5':
|
||||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@img/sharp-wasm32@0.34.5':
|
'@img/sharp-wasm32@0.34.5':
|
||||||
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||||
@ -1288,24 +1314,28 @@ packages:
|
|||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@lancedb/lancedb-linux-arm64-musl@0.23.0':
|
'@lancedb/lancedb-linux-arm64-musl@0.23.0':
|
||||||
resolution: {integrity: sha512-c2UCtGoYjA3oDdw5y3RLK7J2th3rSjYBng+1I03vU9g092y8KATAJO/lV2AtyxSC+esSuyY1dMEaj8ADcXjZAA==}
|
resolution: {integrity: sha512-c2UCtGoYjA3oDdw5y3RLK7J2th3rSjYBng+1I03vU9g092y8KATAJO/lV2AtyxSC+esSuyY1dMEaj8ADcXjZAA==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@lancedb/lancedb-linux-x64-gnu@0.23.0':
|
'@lancedb/lancedb-linux-x64-gnu@0.23.0':
|
||||||
resolution: {integrity: sha512-OPL7tK3JCTx43ZxvbVs+CljfCer0KrojANQbcJ2V4VAp6XBhKx1sBAlIVGuCrd93pA8UOUP3iHsM7aglPo6rCg==}
|
resolution: {integrity: sha512-OPL7tK3JCTx43ZxvbVs+CljfCer0KrojANQbcJ2V4VAp6XBhKx1sBAlIVGuCrd93pA8UOUP3iHsM7aglPo6rCg==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@lancedb/lancedb-linux-x64-musl@0.23.0':
|
'@lancedb/lancedb-linux-x64-musl@0.23.0':
|
||||||
resolution: {integrity: sha512-1ZEoQDwOrKvwPyAG+95/r1NYqX8Ca5bRek8Vr62CzWCEmHd/pFeEGWZ5STrkh+Bt3GLdi2JOivFtRbmuBAJypQ==}
|
resolution: {integrity: sha512-1ZEoQDwOrKvwPyAG+95/r1NYqX8Ca5bRek8Vr62CzWCEmHd/pFeEGWZ5STrkh+Bt3GLdi2JOivFtRbmuBAJypQ==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@lancedb/lancedb-win32-arm64-msvc@0.23.0':
|
'@lancedb/lancedb-win32-arm64-msvc@0.23.0':
|
||||||
resolution: {integrity: sha512-OuD1mkrgXvijRlXdbx3LvfuorO04FD5qHegnTOWGXh1sIwwrvvhcJAvXUGBNLY4n/lsWvA+xTjtMwRjUitvPKg==}
|
resolution: {integrity: sha512-OuD1mkrgXvijRlXdbx3LvfuorO04FD5qHegnTOWGXh1sIwwrvvhcJAvXUGBNLY4n/lsWvA+xTjtMwRjUitvPKg==}
|
||||||
@ -1322,11 +1352,13 @@ packages:
|
|||||||
'@lancedb/lancedb@0.23.0':
|
'@lancedb/lancedb@0.23.0':
|
||||||
resolution: {integrity: sha512-aYrIoEG24AC+wILCL57Ius/Y4yU+xFHDPKLvmjzzN4byAjzeIGF0TC86S5RBt4Ji+dxS7yIWV5Q/gE5/fybIFQ==}
|
resolution: {integrity: sha512-aYrIoEG24AC+wILCL57Ius/Y4yU+xFHDPKLvmjzzN4byAjzeIGF0TC86S5RBt4Ji+dxS7yIWV5Q/gE5/fybIFQ==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
cpu: [x64, arm64]
|
|
||||||
os: [darwin, linux, win32]
|
os: [darwin, linux, win32]
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
apache-arrow: '>=15.0.0 <=18.1.0'
|
apache-arrow: '>=15.0.0 <=18.1.0'
|
||||||
|
|
||||||
|
'@larksuiteoapi/node-sdk@1.56.1':
|
||||||
|
resolution: {integrity: sha512-/ixtyJnWOmcupKgDXz+6G6qTLMi3cNrR+LGOuq2PMwcJ6hhXTUJNyAF+ADY7ah9OoeDniGU/UJwMb2gqKdxwcA==}
|
||||||
|
|
||||||
'@line/bot-sdk@10.6.0':
|
'@line/bot-sdk@10.6.0':
|
||||||
resolution: {integrity: sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==}
|
resolution: {integrity: sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
@ -1398,24 +1430,28 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@mariozechner/clipboard-linux-riscv64-gnu@0.3.0':
|
'@mariozechner/clipboard-linux-riscv64-gnu@0.3.0':
|
||||||
resolution: {integrity: sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ==}
|
resolution: {integrity: sha512-4BC08CIaOXSSAGRZLEjqJmQfioED8ohAzwt0k2amZPEbH96YKoBNorq5EdwPf5VT+odS0DeyCwhwtxokRLZIvQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@mariozechner/clipboard-linux-x64-gnu@0.3.0':
|
'@mariozechner/clipboard-linux-x64-gnu@0.3.0':
|
||||||
resolution: {integrity: sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA==}
|
resolution: {integrity: sha512-GpNY5Y9nOzr0Vt0Qi5U88qwe6piiIHk44kSMexl8ns90LluN5UTNYmyfi7Xq3/lmPZCpnB2xvBTYbsXCxnopIA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@mariozechner/clipboard-linux-x64-musl@0.3.0':
|
'@mariozechner/clipboard-linux-x64-musl@0.3.0':
|
||||||
resolution: {integrity: sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA==}
|
resolution: {integrity: sha512-+PnR48/x9GMY5Kh8BLjzHMx6trOegMtxAuqTM9X/bhV3QuW6sLLd7nojDHSGj/ZueK6i0tcQxvOrgNLozVtNDA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@mariozechner/clipboard-win32-arm64-msvc@0.3.0':
|
'@mariozechner/clipboard-win32-arm64-msvc@0.3.0':
|
||||||
resolution: {integrity: sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg==}
|
resolution: {integrity: sha512-+dy2vZ1Ph4EYj0cotB+bVUVk/uKl2bh9LOp/zlnFqoCCYDN6sm+L0VyIOPPo3hjoEVdGpHe1MUxp3qG/OLwXgg==}
|
||||||
@ -1516,30 +1552,35 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@napi-rs/canvas-linux-arm64-musl@0.1.88':
|
'@napi-rs/canvas-linux-arm64-musl@0.1.88':
|
||||||
resolution: {integrity: sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==}
|
resolution: {integrity: sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@napi-rs/canvas-linux-riscv64-gnu@0.1.88':
|
'@napi-rs/canvas-linux-riscv64-gnu@0.1.88':
|
||||||
resolution: {integrity: sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==}
|
resolution: {integrity: sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@napi-rs/canvas-linux-x64-gnu@0.1.88':
|
'@napi-rs/canvas-linux-x64-gnu@0.1.88':
|
||||||
resolution: {integrity: sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==}
|
resolution: {integrity: sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@napi-rs/canvas-linux-x64-musl@0.1.88':
|
'@napi-rs/canvas-linux-x64-musl@0.1.88':
|
||||||
resolution: {integrity: sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==}
|
resolution: {integrity: sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@napi-rs/canvas-win32-arm64-msvc@0.1.88':
|
'@napi-rs/canvas-win32-arm64-msvc@0.1.88':
|
||||||
resolution: {integrity: sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==}
|
resolution: {integrity: sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==}
|
||||||
@ -1585,36 +1626,42 @@ packages:
|
|||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [arm64, x64]
|
cpu: [arm64, x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/linux-armv7l@3.15.0':
|
'@node-llama-cpp/linux-armv7l@3.15.0':
|
||||||
resolution: {integrity: sha512-ZuZ3q6mejQnEP4o22la7zBv7jNR+IZfgItDm3KjAl04HUXTKJ43HpNwjnf9GyYYd+dEgtoX0MESvWz4RnGH8Jw==}
|
resolution: {integrity: sha512-ZuZ3q6mejQnEP4o22la7zBv7jNR+IZfgItDm3KjAl04HUXTKJ43HpNwjnf9GyYYd+dEgtoX0MESvWz4RnGH8Jw==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [arm, x64]
|
cpu: [arm, x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/linux-x64-cuda-ext@3.15.0':
|
'@node-llama-cpp/linux-x64-cuda-ext@3.15.0':
|
||||||
resolution: {integrity: sha512-wQwgSl7Qm8vH56oBt7IuWWDNNsDECkVMS000C92wl3PkbzjwZFiWzehwa+kF8Lr2BBMiCJNkI5nEabhYH3RN2Q==}
|
resolution: {integrity: sha512-wQwgSl7Qm8vH56oBt7IuWWDNNsDECkVMS000C92wl3PkbzjwZFiWzehwa+kF8Lr2BBMiCJNkI5nEabhYH3RN2Q==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/linux-x64-cuda@3.15.0':
|
'@node-llama-cpp/linux-x64-cuda@3.15.0':
|
||||||
resolution: {integrity: sha512-mDjyVulCTRYilm9Emm3lDMx7dbI1vzGqk28Pj28shartjERTUu8aUNDYOmVKNMLpUKS1akw7vy0lMF8t4qswxQ==}
|
resolution: {integrity: sha512-mDjyVulCTRYilm9Emm3lDMx7dbI1vzGqk28Pj28shartjERTUu8aUNDYOmVKNMLpUKS1akw7vy0lMF8t4qswxQ==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/linux-x64-vulkan@3.15.0':
|
'@node-llama-cpp/linux-x64-vulkan@3.15.0':
|
||||||
resolution: {integrity: sha512-htVIthQKq/rr8v5e7NiVtcHsstqTBAAC50kUymmDMbrzAu6d/EHacCJpNbU57b1UUa1nKN5cBqr6Jr+QqEalMA==}
|
resolution: {integrity: sha512-htVIthQKq/rr8v5e7NiVtcHsstqTBAAC50kUymmDMbrzAu6d/EHacCJpNbU57b1UUa1nKN5cBqr6Jr+QqEalMA==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/linux-x64@3.15.0':
|
'@node-llama-cpp/linux-x64@3.15.0':
|
||||||
resolution: {integrity: sha512-etUuTqSyNefRObqc5+JZviNTkuef2XEtHcQLaamEIWwjI1dj7nTD2YMZPBP7H3M3E55HSIY82vqCQ1bp6ZILiA==}
|
resolution: {integrity: sha512-etUuTqSyNefRObqc5+JZviNTkuef2XEtHcQLaamEIWwjI1dj7nTD2YMZPBP7H3M3E55HSIY82vqCQ1bp6ZILiA==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@node-llama-cpp/mac-arm64-metal@3.15.0':
|
'@node-llama-cpp/mac-arm64-metal@3.15.0':
|
||||||
resolution: {integrity: sha512-3Vkq6bpyQZaIzoaLLP7H2Tt8ty5BS0zxUY2pX0ox2S9P4fp8Au0CCJuUJF4V+EKi+/PTn70A6R1QCkRMfMQJig==}
|
resolution: {integrity: sha512-3Vkq6bpyQZaIzoaLLP7H2Tt8ty5BS0zxUY2pX0ox2S9P4fp8Au0CCJuUJF4V+EKi+/PTn70A6R1QCkRMfMQJig==}
|
||||||
@ -1962,21 +2009,25 @@ packages:
|
|||||||
resolution: {integrity: sha512-GubkQeQT5d3B/Jx/IiR7NMkSmXrCZcVI0BPh1i7mpFi8HgD1hQ/LbhiBKAMsMqs5bbugdQOgBEl8bOhe8JhW1g==}
|
resolution: {integrity: sha512-GubkQeQT5d3B/Jx/IiR7NMkSmXrCZcVI0BPh1i7mpFi8HgD1hQ/LbhiBKAMsMqs5bbugdQOgBEl8bOhe8JhW1g==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@oxfmt/linux-arm64-musl@0.26.0':
|
'@oxfmt/linux-arm64-musl@0.26.0':
|
||||||
resolution: {integrity: sha512-OEypUwK69bFPj+aa3/LYCnlIUPgoOLu//WNcriwpnWNmt47808Ht7RJSg+MNK8a7pSZHpXJ5/E6CRK/OTwFdaQ==}
|
resolution: {integrity: sha512-OEypUwK69bFPj+aa3/LYCnlIUPgoOLu//WNcriwpnWNmt47808Ht7RJSg+MNK8a7pSZHpXJ5/E6CRK/OTwFdaQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@oxfmt/linux-x64-gnu@0.26.0':
|
'@oxfmt/linux-x64-gnu@0.26.0':
|
||||||
resolution: {integrity: sha512-xO6iEW2bC6ZHyOTPmPWrg/nM6xgzyRPaS84rATy6F8d79wz69LdRdJ3l/PXlkqhi7XoxhvX4ExysA0Nf10ZZEQ==}
|
resolution: {integrity: sha512-xO6iEW2bC6ZHyOTPmPWrg/nM6xgzyRPaS84rATy6F8d79wz69LdRdJ3l/PXlkqhi7XoxhvX4ExysA0Nf10ZZEQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@oxfmt/linux-x64-musl@0.26.0':
|
'@oxfmt/linux-x64-musl@0.26.0':
|
||||||
resolution: {integrity: sha512-Z3KuZFC+MIuAyFCXBHY71kCsdRq1ulbsbzTe71v+hrEv7zVBn6yzql+/AZcgfIaKzWO9OXNuz5WWLWDmVALwow==}
|
resolution: {integrity: sha512-Z3KuZFC+MIuAyFCXBHY71kCsdRq1ulbsbzTe71v+hrEv7zVBn6yzql+/AZcgfIaKzWO9OXNuz5WWLWDmVALwow==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@oxfmt/win32-arm64@0.26.0':
|
'@oxfmt/win32-arm64@0.26.0':
|
||||||
resolution: {integrity: sha512-3zRbqwVWK1mDhRhTknlQFpRFL9GhEB5GfU6U7wawnuEwpvi39q91kJ+SRJvJnhyPCARkjZBd1V8XnweN5IFd1g==}
|
resolution: {integrity: sha512-3zRbqwVWK1mDhRhTknlQFpRFL9GhEB5GfU6U7wawnuEwpvi39q91kJ+SRJvJnhyPCARkjZBd1V8XnweN5IFd1g==}
|
||||||
@ -2032,21 +2083,25 @@ packages:
|
|||||||
resolution: {integrity: sha512-Fow7H84Bs8XxuaK1yfSEWBC8HI7rfEQB9eR2A0J61un1WgCas7jNrt1HbT6+p6KmUH2bhR+r/RDu/6JFAvvj4g==}
|
resolution: {integrity: sha512-Fow7H84Bs8XxuaK1yfSEWBC8HI7rfEQB9eR2A0J61un1WgCas7jNrt1HbT6+p6KmUH2bhR+r/RDu/6JFAvvj4g==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@oxlint/linux-arm64-musl@1.41.0':
|
'@oxlint/linux-arm64-musl@1.41.0':
|
||||||
resolution: {integrity: sha512-WoRRDNwgP5W3rjRh42Zdx8ferYnqpKoYCv2QQLenmdrLjRGYwAd52uywfkcS45mKEWHeY1RPwPkYCSROXiGb2w==}
|
resolution: {integrity: sha512-WoRRDNwgP5W3rjRh42Zdx8ferYnqpKoYCv2QQLenmdrLjRGYwAd52uywfkcS45mKEWHeY1RPwPkYCSROXiGb2w==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@oxlint/linux-x64-gnu@1.41.0':
|
'@oxlint/linux-x64-gnu@1.41.0':
|
||||||
resolution: {integrity: sha512-75k3CKj3fOc/a/2aSgO81s3HsTZOFROthPJ+UI2Oatic1LhvH6eKjKfx3jDDyVpzeDS2qekPlc/y3N33iZz5Og==}
|
resolution: {integrity: sha512-75k3CKj3fOc/a/2aSgO81s3HsTZOFROthPJ+UI2Oatic1LhvH6eKjKfx3jDDyVpzeDS2qekPlc/y3N33iZz5Og==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@oxlint/linux-x64-musl@1.41.0':
|
'@oxlint/linux-x64-musl@1.41.0':
|
||||||
resolution: {integrity: sha512-8r82eBwGPoAPn67ZvdxTlX/Z3gVb+ZtN6nbkyFzwwHWAh8yGutX+VBcVkyrePSl6XgBP4QAaddPnHmkvJjqY0g==}
|
resolution: {integrity: sha512-8r82eBwGPoAPn67ZvdxTlX/Z3gVb+ZtN6nbkyFzwwHWAh8yGutX+VBcVkyrePSl6XgBP4QAaddPnHmkvJjqY0g==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@oxlint/win32-arm64@1.41.0':
|
'@oxlint/win32-arm64@1.41.0':
|
||||||
resolution: {integrity: sha512-aK+DAcckQsNCOXKruatyYuY/ROjNiRejQB1PeJtkZwM21+8rV9ODYbvKNvt0pW+YCws7svftBSFMCpl3ke2unw==}
|
resolution: {integrity: sha512-aK+DAcckQsNCOXKruatyYuY/ROjNiRejQB1PeJtkZwM21+8rV9ODYbvKNvt0pW+YCws7svftBSFMCpl3ke2unw==}
|
||||||
@ -2118,24 +2173,28 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@reflink/reflink-linux-arm64-musl@0.1.19':
|
'@reflink/reflink-linux-arm64-musl@0.1.19':
|
||||||
resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==}
|
resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@reflink/reflink-linux-x64-gnu@0.1.19':
|
'@reflink/reflink-linux-x64-gnu@0.1.19':
|
||||||
resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==}
|
resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@reflink/reflink-linux-x64-musl@0.1.19':
|
'@reflink/reflink-linux-x64-musl@0.1.19':
|
||||||
resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==}
|
resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@reflink/reflink-win32-arm64-msvc@0.1.19':
|
'@reflink/reflink-win32-arm64-msvc@0.1.19':
|
||||||
resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==}
|
resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==}
|
||||||
@ -2188,24 +2247,28 @@ packages:
|
|||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
|
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.1':
|
||||||
resolution: {integrity: sha512-UvApLEGholmxw/HIwmUnLq3CwdydbhaHHllvWiCTNbyGom7wTwOtz5OAQbAKZYyiEOeIXZNPkM7nA4Dtng7CLw==}
|
resolution: {integrity: sha512-UvApLEGholmxw/HIwmUnLq3CwdydbhaHHllvWiCTNbyGom7wTwOtz5OAQbAKZYyiEOeIXZNPkM7nA4Dtng7CLw==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
|
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.1':
|
||||||
resolution: {integrity: sha512-uVctNgZHiGnJx5Fij7wHLhgw4uyZBVi6mykeWKOqE7bVy9Hcxn0fM/IuqdMwk6hXlaf9fFShDTFz2+YejP+x0A==}
|
resolution: {integrity: sha512-uVctNgZHiGnJx5Fij7wHLhgw4uyZBVi6mykeWKOqE7bVy9Hcxn0fM/IuqdMwk6hXlaf9fFShDTFz2+YejP+x0A==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
|
'@rolldown/binding-linux-x64-musl@1.0.0-rc.1':
|
||||||
resolution: {integrity: sha512-T6Eg0xWwcxd/MzBcuv4Z37YVbUbJxy5cMNnbIt/Yr99wFwli30O4BPlY8hKeGyn6lWNtU0QioBS46lVzDN38bg==}
|
resolution: {integrity: sha512-T6Eg0xWwcxd/MzBcuv4Z37YVbUbJxy5cMNnbIt/Yr99wFwli30O4BPlY8hKeGyn6lWNtU0QioBS46lVzDN38bg==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
|
'@rolldown/binding-openharmony-arm64@1.0.0-rc.1':
|
||||||
resolution: {integrity: sha512-PuGZVS2xNJyLADeh2F04b+Cz4NwvpglbtWACgrDOa5YDTEHKwmiTDjoD5eZ9/ptXtcpeFrMqD2H4Zn33KAh1Eg==}
|
resolution: {integrity: sha512-PuGZVS2xNJyLADeh2F04b+Cz4NwvpglbtWACgrDOa5YDTEHKwmiTDjoD5eZ9/ptXtcpeFrMqD2H4Zn33KAh1Eg==}
|
||||||
@ -2267,66 +2330,79 @@ packages:
|
|||||||
resolution: {integrity: sha512-YeGUhkN1oA+iSPzzhEjVPS29YbViOr8s4lSsFaZKLHswgqP911xx25fPOyE9+khmN6W4VeM0aevbDp4kkEoHiA==}
|
resolution: {integrity: sha512-YeGUhkN1oA+iSPzzhEjVPS29YbViOr8s4lSsFaZKLHswgqP911xx25fPOyE9+khmN6W4VeM0aevbDp4kkEoHiA==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm-musleabihf@4.55.3':
|
'@rollup/rollup-linux-arm-musleabihf@4.55.3':
|
||||||
resolution: {integrity: sha512-eo0iOIOvcAlWB3Z3eh8pVM8hZ0oVkK3AjEM9nSrkSug2l15qHzF3TOwT0747omI6+CJJvl7drwZepT+re6Fy/w==}
|
resolution: {integrity: sha512-eo0iOIOvcAlWB3Z3eh8pVM8hZ0oVkK3AjEM9nSrkSug2l15qHzF3TOwT0747omI6+CJJvl7drwZepT+re6Fy/w==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-gnu@4.55.3':
|
'@rollup/rollup-linux-arm64-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-DJay3ep76bKUDImmn//W5SvpjRN5LmK/ntWyeJs/dcnwiiHESd3N4uteK9FDLf0S0W8E6Y0sVRXpOCoQclQqNg==}
|
resolution: {integrity: sha512-DJay3ep76bKUDImmn//W5SvpjRN5LmK/ntWyeJs/dcnwiiHESd3N4uteK9FDLf0S0W8E6Y0sVRXpOCoQclQqNg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-musl@4.55.3':
|
'@rollup/rollup-linux-arm64-musl@4.55.3':
|
||||||
resolution: {integrity: sha512-BKKWQkY2WgJ5MC/ayvIJTHjy0JUGb5efaHCUiG/39sSUvAYRBaO3+/EK0AZT1RF3pSj86O24GLLik9mAYu0IJg==}
|
resolution: {integrity: sha512-BKKWQkY2WgJ5MC/ayvIJTHjy0JUGb5efaHCUiG/39sSUvAYRBaO3+/EK0AZT1RF3pSj86O24GLLik9mAYu0IJg==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-gnu@4.55.3':
|
'@rollup/rollup-linux-loong64-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-Q9nVlWtKAG7ISW80OiZGxTr6rYtyDSkauHUtvkQI6TNOJjFvpj4gcH+KaJihqYInnAzEEUetPQubRwHef4exVg==}
|
resolution: {integrity: sha512-Q9nVlWtKAG7ISW80OiZGxTr6rYtyDSkauHUtvkQI6TNOJjFvpj4gcH+KaJihqYInnAzEEUetPQubRwHef4exVg==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-musl@4.55.3':
|
'@rollup/rollup-linux-loong64-musl@4.55.3':
|
||||||
resolution: {integrity: sha512-2H5LmhzrpC4fFRNwknzmmTvvyJPHwESoJgyReXeFoYYuIDfBhP29TEXOkCJE/KxHi27mj7wDUClNq78ue3QEBQ==}
|
resolution: {integrity: sha512-2H5LmhzrpC4fFRNwknzmmTvvyJPHwESoJgyReXeFoYYuIDfBhP29TEXOkCJE/KxHi27mj7wDUClNq78ue3QEBQ==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-gnu@4.55.3':
|
'@rollup/rollup-linux-ppc64-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-9S542V0ie9LCTznPYlvaeySwBeIEa7rDBgLHKZ5S9DBgcqdJYburabm8TqiqG6mrdTzfV5uttQRHcbKff9lWtA==}
|
resolution: {integrity: sha512-9S542V0ie9LCTznPYlvaeySwBeIEa7rDBgLHKZ5S9DBgcqdJYburabm8TqiqG6mrdTzfV5uttQRHcbKff9lWtA==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-musl@4.55.3':
|
'@rollup/rollup-linux-ppc64-musl@4.55.3':
|
||||||
resolution: {integrity: sha512-ukxw+YH3XXpcezLgbJeasgxyTbdpnNAkrIlFGDl7t+pgCxZ89/6n1a+MxlY7CegU+nDgrgdqDelPRNQ/47zs0g==}
|
resolution: {integrity: sha512-ukxw+YH3XXpcezLgbJeasgxyTbdpnNAkrIlFGDl7t+pgCxZ89/6n1a+MxlY7CegU+nDgrgdqDelPRNQ/47zs0g==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-gnu@4.55.3':
|
'@rollup/rollup-linux-riscv64-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-Iauw9UsTTvlF++FhghFJjqYxyXdggXsOqGpFBylaRopVpcbfyIIsNvkf9oGwfgIcf57z3m8+/oSYTo6HutBFNw==}
|
resolution: {integrity: sha512-Iauw9UsTTvlF++FhghFJjqYxyXdggXsOqGpFBylaRopVpcbfyIIsNvkf9oGwfgIcf57z3m8+/oSYTo6HutBFNw==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-musl@4.55.3':
|
'@rollup/rollup-linux-riscv64-musl@4.55.3':
|
||||||
resolution: {integrity: sha512-3OqKAHSEQXKdq9mQ4eajqUgNIK27VZPW3I26EP8miIzuKzCJ3aW3oEn2pzF+4/Hj/Moc0YDsOtBgT5bZ56/vcA==}
|
resolution: {integrity: sha512-3OqKAHSEQXKdq9mQ4eajqUgNIK27VZPW3I26EP8miIzuKzCJ3aW3oEn2pzF+4/Hj/Moc0YDsOtBgT5bZ56/vcA==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-s390x-gnu@4.55.3':
|
'@rollup/rollup-linux-s390x-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-0CM8dSVzVIaqMcXIFej8zZrSFLnGrAE8qlNbbHfTw1EEPnFTg1U1ekI0JdzjPyzSfUsHWtodilQQG/RA55berA==}
|
resolution: {integrity: sha512-0CM8dSVzVIaqMcXIFej8zZrSFLnGrAE8qlNbbHfTw1EEPnFTg1U1ekI0JdzjPyzSfUsHWtodilQQG/RA55berA==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-gnu@4.55.3':
|
'@rollup/rollup-linux-x64-gnu@4.55.3':
|
||||||
resolution: {integrity: sha512-+fgJE12FZMIgBaKIAGd45rxf+5ftcycANJRWk8Vz0NnMTM5rADPGuRFTYar+Mqs560xuART7XsX2lSACa1iOmQ==}
|
resolution: {integrity: sha512-+fgJE12FZMIgBaKIAGd45rxf+5ftcycANJRWk8Vz0NnMTM5rADPGuRFTYar+Mqs560xuART7XsX2lSACa1iOmQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-musl@4.55.3':
|
'@rollup/rollup-linux-x64-musl@4.55.3':
|
||||||
resolution: {integrity: sha512-tMD7NnbAolWPzQlJQJjVFh/fNH3K/KnA7K8gv2dJWCwwnaK6DFCYST1QXYWfu5V0cDwarWC8Sf/cfMHniNq21A==}
|
resolution: {integrity: sha512-tMD7NnbAolWPzQlJQJjVFh/fNH3K/KnA7K8gv2dJWCwwnaK6DFCYST1QXYWfu5V0cDwarWC8Sf/cfMHniNq21A==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-openbsd-x64@4.55.3':
|
'@rollup/rollup-openbsd-x64@4.55.3':
|
||||||
resolution: {integrity: sha512-u5KsqxOxjEeIbn7bUK1MPM34jrnPwjeqgyin4/N6e/KzXKfpE9Mi0nCxcQjaM9lLmPcHmn/xx1yOjgTMtu1jWQ==}
|
resolution: {integrity: sha512-u5KsqxOxjEeIbn7bUK1MPM34jrnPwjeqgyin4/N6e/KzXKfpE9Mi0nCxcQjaM9lLmPcHmn/xx1yOjgTMtu1jWQ==}
|
||||||
@ -3071,6 +3147,9 @@ packages:
|
|||||||
aws4@1.13.2:
|
aws4@1.13.2:
|
||||||
resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
|
resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
|
||||||
|
|
||||||
|
axios@0.27.2:
|
||||||
|
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
||||||
|
|
||||||
axios@1.13.2:
|
axios@1.13.2:
|
||||||
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
|
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
|
||||||
|
|
||||||
@ -3214,11 +3293,6 @@ packages:
|
|||||||
class-variance-authority@0.7.1:
|
class-variance-authority@0.7.1:
|
||||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||||
|
|
||||||
clawdbot@2026.1.24-3:
|
|
||||||
resolution: {integrity: sha512-zt9BzhWXduq8ZZR4rfzQDurQWAgmijTTyPZCQGrn5ew6wCEwhxxEr2/NHG7IlCwcfRsKymsY4se9KMhoNz0JtQ==}
|
|
||||||
engines: {node: '>=22.12.0'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
cli-cursor@5.0.0:
|
cli-cursor@5.0.0:
|
||||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -4097,24 +4171,28 @@ packages:
|
|||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
lightningcss-linux-arm64-musl@1.30.2:
|
lightningcss-linux-arm64-musl@1.30.2:
|
||||||
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
|
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
lightningcss-linux-x64-gnu@1.30.2:
|
lightningcss-linux-x64-gnu@1.30.2:
|
||||||
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
|
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
lightningcss-linux-x64-musl@1.30.2:
|
lightningcss-linux-x64-musl@1.30.2:
|
||||||
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
|
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
lightningcss-win32-arm64-msvc@1.30.2:
|
lightningcss-win32-arm64-msvc@1.30.2:
|
||||||
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
|
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
|
||||||
@ -4165,6 +4243,9 @@ packages:
|
|||||||
lodash.debounce@4.0.8:
|
lodash.debounce@4.0.8:
|
||||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
|
|
||||||
|
lodash.identity@3.0.0:
|
||||||
|
resolution: {integrity: sha1-rXvGpOZH15yXLhuA/u968VYmeHY=}
|
||||||
|
|
||||||
lodash.includes@4.3.0:
|
lodash.includes@4.3.0:
|
||||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||||
|
|
||||||
@ -4183,9 +4264,15 @@ packages:
|
|||||||
lodash.isstring@4.0.1:
|
lodash.isstring@4.0.1:
|
||||||
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
|
||||||
|
|
||||||
|
lodash.merge@4.6.2:
|
||||||
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
lodash.once@4.1.1:
|
lodash.once@4.1.1:
|
||||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||||
|
|
||||||
|
lodash.pickby@4.6.0:
|
||||||
|
resolution: {integrity: sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=}
|
||||||
|
|
||||||
lodash@4.17.23:
|
lodash@4.17.23:
|
||||||
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
|
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
|
||||||
|
|
||||||
@ -6882,6 +6969,20 @@ snapshots:
|
|||||||
'@lancedb/lancedb-win32-arm64-msvc': 0.23.0
|
'@lancedb/lancedb-win32-arm64-msvc': 0.23.0
|
||||||
'@lancedb/lancedb-win32-x64-msvc': 0.23.0
|
'@lancedb/lancedb-win32-x64-msvc': 0.23.0
|
||||||
|
|
||||||
|
'@larksuiteoapi/node-sdk@1.56.1':
|
||||||
|
dependencies:
|
||||||
|
axios: 0.27.2
|
||||||
|
lodash.identity: 3.0.0
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
lodash.pickby: 4.6.0
|
||||||
|
protobufjs: 7.5.4
|
||||||
|
qs: 6.14.1
|
||||||
|
ws: 8.19.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- debug
|
||||||
|
- utf-8-validate
|
||||||
|
|
||||||
'@line/bot-sdk@10.6.0':
|
'@line/bot-sdk@10.6.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.10.9
|
'@types/node': 24.10.9
|
||||||
@ -8929,6 +9030,13 @@ snapshots:
|
|||||||
|
|
||||||
aws4@1.13.2: {}
|
aws4@1.13.2: {}
|
||||||
|
|
||||||
|
axios@0.27.2:
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: 1.15.11(debug@4.4.3)
|
||||||
|
form-data: 4.0.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
|
||||||
axios@1.13.2(debug@4.4.3):
|
axios@1.13.2(debug@4.4.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.11(debug@4.4.3)
|
follow-redirects: 1.15.11(debug@4.4.3)
|
||||||
@ -9098,84 +9206,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
||||||
clawdbot@2026.1.24-3(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3):
|
|
||||||
dependencies:
|
|
||||||
'@agentclientprotocol/sdk': 0.13.1(zod@4.3.6)
|
|
||||||
'@aws-sdk/client-bedrock': 3.975.0
|
|
||||||
'@buape/carbon': 0.14.0(hono@4.11.4)
|
|
||||||
'@clack/prompts': 0.11.0
|
|
||||||
'@grammyjs/runner': 2.0.3(grammy@1.39.3)
|
|
||||||
'@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3)
|
|
||||||
'@homebridge/ciao': 1.3.4
|
|
||||||
'@line/bot-sdk': 10.6.0
|
|
||||||
'@lydell/node-pty': 1.2.0-beta.3
|
|
||||||
'@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
|
||||||
'@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
|
||||||
'@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
|
||||||
'@mariozechner/pi-tui': 0.49.3
|
|
||||||
'@mozilla/readability': 0.6.0
|
|
||||||
'@sinclair/typebox': 0.34.47
|
|
||||||
'@slack/bolt': 4.6.0(@types/express@5.0.6)
|
|
||||||
'@slack/web-api': 7.13.0
|
|
||||||
'@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5)
|
|
||||||
ajv: 8.17.1
|
|
||||||
body-parser: 2.2.2
|
|
||||||
chalk: 5.6.2
|
|
||||||
chokidar: 5.0.0
|
|
||||||
chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482)
|
|
||||||
cli-highlight: 2.1.11
|
|
||||||
commander: 14.0.2
|
|
||||||
croner: 9.1.0
|
|
||||||
detect-libc: 2.1.2
|
|
||||||
discord-api-types: 0.38.37
|
|
||||||
dotenv: 17.2.3
|
|
||||||
express: 5.2.1
|
|
||||||
file-type: 21.3.0
|
|
||||||
grammy: 1.39.3
|
|
||||||
hono: 4.11.4
|
|
||||||
jiti: 2.6.1
|
|
||||||
json5: 2.2.3
|
|
||||||
jszip: 3.10.1
|
|
||||||
linkedom: 0.18.12
|
|
||||||
long: 5.3.2
|
|
||||||
markdown-it: 14.1.0
|
|
||||||
node-edge-tts: 1.2.9
|
|
||||||
osc-progress: 0.3.0
|
|
||||||
pdfjs-dist: 5.4.530
|
|
||||||
playwright-core: 1.58.0
|
|
||||||
proper-lockfile: 4.1.2
|
|
||||||
qrcode-terminal: 0.12.0
|
|
||||||
sharp: 0.34.5
|
|
||||||
sqlite-vec: 0.1.7-alpha.2
|
|
||||||
tar: 7.5.4
|
|
||||||
tslog: 4.10.2
|
|
||||||
undici: 7.19.0
|
|
||||||
ws: 8.19.0
|
|
||||||
yaml: 2.8.2
|
|
||||||
zod: 4.3.6
|
|
||||||
optionalDependencies:
|
|
||||||
'@napi-rs/canvas': 0.1.88
|
|
||||||
node-llama-cpp: 3.15.0(typescript@5.9.3)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@discordjs/opus'
|
|
||||||
- '@modelcontextprotocol/sdk'
|
|
||||||
- '@types/express'
|
|
||||||
- audio-decode
|
|
||||||
- aws-crt
|
|
||||||
- bufferutil
|
|
||||||
- canvas
|
|
||||||
- debug
|
|
||||||
- devtools-protocol
|
|
||||||
- encoding
|
|
||||||
- ffmpeg-static
|
|
||||||
- jimp
|
|
||||||
- link-preview-js
|
|
||||||
- node-opus
|
|
||||||
- opusscript
|
|
||||||
- supports-color
|
|
||||||
- typescript
|
|
||||||
- utf-8-validate
|
|
||||||
|
|
||||||
cli-cursor@5.0.0:
|
cli-cursor@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
restore-cursor: 5.1.0
|
restore-cursor: 5.1.0
|
||||||
@ -10251,6 +10281,8 @@ snapshots:
|
|||||||
lodash.debounce@4.0.8:
|
lodash.debounce@4.0.8:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
lodash.identity@3.0.0: {}
|
||||||
|
|
||||||
lodash.includes@4.3.0: {}
|
lodash.includes@4.3.0: {}
|
||||||
|
|
||||||
lodash.isboolean@3.0.3: {}
|
lodash.isboolean@3.0.3: {}
|
||||||
@ -10263,8 +10295,12 @@ snapshots:
|
|||||||
|
|
||||||
lodash.isstring@4.0.1: {}
|
lodash.isstring@4.0.1: {}
|
||||||
|
|
||||||
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
lodash.once@4.1.1: {}
|
lodash.once@4.1.1: {}
|
||||||
|
|
||||||
|
lodash.pickby@4.6.0: {}
|
||||||
|
|
||||||
lodash@4.17.23: {}
|
lodash@4.17.23: {}
|
||||||
|
|
||||||
log-symbols@6.0.0:
|
log-symbols@6.0.0:
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import { requireActivePluginRegistry } from "../plugins/runtime.js";
|
|||||||
import {
|
import {
|
||||||
resolveDiscordGroupRequireMention,
|
resolveDiscordGroupRequireMention,
|
||||||
resolveDiscordGroupToolPolicy,
|
resolveDiscordGroupToolPolicy,
|
||||||
|
resolveFeishuGroupRequireMention,
|
||||||
|
resolveFeishuGroupToolPolicy,
|
||||||
resolveGoogleChatGroupRequireMention,
|
resolveGoogleChatGroupRequireMention,
|
||||||
resolveGoogleChatGroupToolPolicy,
|
resolveGoogleChatGroupToolPolicy,
|
||||||
resolveIMessageGroupRequireMention,
|
resolveIMessageGroupRequireMention,
|
||||||
@ -76,6 +78,12 @@ const formatLower = (allowFrom: Array<string | number>) =>
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((entry) => entry.toLowerCase());
|
.map((entry) => entry.toLowerCase());
|
||||||
|
|
||||||
|
const normalizeFeishuAllowEntry = (entry: string) =>
|
||||||
|
entry
|
||||||
|
.trim()
|
||||||
|
.replace(/^(feishu|lark|user|open_id|user_id|union_id|email):/i, "")
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
|
||||||
// Channel docks: lightweight channel metadata/behavior for shared code paths.
|
// Channel docks: lightweight channel metadata/behavior for shared code paths.
|
||||||
@ -213,6 +221,40 @@ const DOCKS: Record<ChatChannelId, ChannelDock> = {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
feishu: {
|
||||||
|
id: "feishu",
|
||||||
|
capabilities: {
|
||||||
|
chatTypes: ["direct", "channel"],
|
||||||
|
},
|
||||||
|
outbound: { textChunkLimit: 4000 },
|
||||||
|
config: {
|
||||||
|
resolveAllowFrom: ({ cfg, accountId }) => {
|
||||||
|
const channel = cfg.channels?.feishu as
|
||||||
|
| {
|
||||||
|
accounts?: Record<string, { dm?: { allowFrom?: Array<string | number> } }>;
|
||||||
|
dm?: { allowFrom?: Array<string | number> };
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
const normalized = normalizeAccountId(accountId);
|
||||||
|
const account =
|
||||||
|
channel?.accounts?.[normalized] ??
|
||||||
|
channel?.accounts?.[
|
||||||
|
Object.keys(channel?.accounts ?? {}).find(
|
||||||
|
(key) => key.toLowerCase() === normalized.toLowerCase(),
|
||||||
|
) ?? ""
|
||||||
|
];
|
||||||
|
return (account?.dm?.allowFrom ?? channel?.dm?.allowFrom ?? []).map((entry) =>
|
||||||
|
String(entry),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
formatAllowFrom: ({ allowFrom }) =>
|
||||||
|
allowFrom.map((entry) => normalizeFeishuAllowEntry(String(entry))).filter(Boolean),
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
resolveRequireMention: resolveFeishuGroupRequireMention,
|
||||||
|
resolveToolPolicy: resolveFeishuGroupToolPolicy,
|
||||||
|
},
|
||||||
|
},
|
||||||
googlechat: {
|
googlechat: {
|
||||||
id: "googlechat",
|
id: "googlechat",
|
||||||
capabilities: {
|
capabilities: {
|
||||||
|
|||||||
@ -172,6 +172,15 @@ export function resolveGoogleChatGroupRequireMention(params: GroupMentionParams)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveFeishuGroupRequireMention(params: GroupMentionParams): boolean {
|
||||||
|
return resolveChannelGroupRequireMention({
|
||||||
|
cfg: params.cfg,
|
||||||
|
channel: "feishu",
|
||||||
|
groupId: params.groupId,
|
||||||
|
accountId: params.accountId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveGoogleChatGroupToolPolicy(
|
export function resolveGoogleChatGroupToolPolicy(
|
||||||
params: GroupMentionParams,
|
params: GroupMentionParams,
|
||||||
): GroupToolPolicyConfig | undefined {
|
): GroupToolPolicyConfig | undefined {
|
||||||
@ -187,6 +196,21 @@ export function resolveGoogleChatGroupToolPolicy(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveFeishuGroupToolPolicy(
|
||||||
|
params: GroupMentionParams,
|
||||||
|
): GroupToolPolicyConfig | undefined {
|
||||||
|
return resolveChannelGroupToolsPolicy({
|
||||||
|
cfg: params.cfg,
|
||||||
|
channel: "feishu",
|
||||||
|
groupId: params.groupId,
|
||||||
|
accountId: params.accountId,
|
||||||
|
senderId: params.senderId,
|
||||||
|
senderName: params.senderName,
|
||||||
|
senderUsername: params.senderUsername,
|
||||||
|
senderE164: params.senderE164,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveSlackGroupRequireMention(params: GroupMentionParams): boolean {
|
export function resolveSlackGroupRequireMention(params: GroupMentionParams): boolean {
|
||||||
const account = resolveSlackAccount({
|
const account = resolveSlackAccount({
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
|
|||||||
@ -22,6 +22,8 @@ export type ChannelSetupInput = {
|
|||||||
tokenFile?: string;
|
tokenFile?: string;
|
||||||
botToken?: string;
|
botToken?: string;
|
||||||
appToken?: string;
|
appToken?: string;
|
||||||
|
appId?: string;
|
||||||
|
appSecret?: string;
|
||||||
signalNumber?: string;
|
signalNumber?: string;
|
||||||
cliPath?: string;
|
cliPath?: string;
|
||||||
dbPath?: string;
|
dbPath?: string;
|
||||||
|
|||||||
@ -11,6 +11,7 @@ describe("channel registry", () => {
|
|||||||
expect(normalizeChatChannelId("imsg")).toBe("imessage");
|
expect(normalizeChatChannelId("imsg")).toBe("imessage");
|
||||||
expect(normalizeChatChannelId("gchat")).toBe("googlechat");
|
expect(normalizeChatChannelId("gchat")).toBe("googlechat");
|
||||||
expect(normalizeChatChannelId("google-chat")).toBe("googlechat");
|
expect(normalizeChatChannelId("google-chat")).toBe("googlechat");
|
||||||
|
expect(normalizeChatChannelId("lark")).toBe("feishu");
|
||||||
expect(normalizeChatChannelId("web")).toBeNull();
|
expect(normalizeChatChannelId("web")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export const CHAT_CHANNEL_ORDER = [
|
|||||||
"telegram",
|
"telegram",
|
||||||
"whatsapp",
|
"whatsapp",
|
||||||
"discord",
|
"discord",
|
||||||
|
"feishu",
|
||||||
"googlechat",
|
"googlechat",
|
||||||
"slack",
|
"slack",
|
||||||
"signal",
|
"signal",
|
||||||
@ -58,6 +59,16 @@ const CHAT_CHANNEL_META: Record<ChatChannelId, ChannelMeta> = {
|
|||||||
blurb: "very well supported right now.",
|
blurb: "very well supported right now.",
|
||||||
systemImage: "bubble.left.and.bubble.right",
|
systemImage: "bubble.left.and.bubble.right",
|
||||||
},
|
},
|
||||||
|
feishu: {
|
||||||
|
id: "feishu",
|
||||||
|
label: "Feishu",
|
||||||
|
selectionLabel: "Feishu (Lark SDK)",
|
||||||
|
detailLabel: "Feishu Bot",
|
||||||
|
docsPath: "/channels/feishu",
|
||||||
|
docsLabel: "feishu",
|
||||||
|
blurb: "Lark/Feishu app via WebSocket.",
|
||||||
|
systemImage: "message.badge",
|
||||||
|
},
|
||||||
googlechat: {
|
googlechat: {
|
||||||
id: "googlechat",
|
id: "googlechat",
|
||||||
label: "Google Chat",
|
label: "Google Chat",
|
||||||
@ -104,6 +115,7 @@ export const CHAT_CHANNEL_ALIASES: Record<string, ChatChannelId> = {
|
|||||||
imsg: "imessage",
|
imsg: "imessage",
|
||||||
"google-chat": "googlechat",
|
"google-chat": "googlechat",
|
||||||
gchat: "googlechat",
|
gchat: "googlechat",
|
||||||
|
lark: "feishu",
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeChannelKey = (raw?: string | null): string | undefined => {
|
const normalizeChannelKey = (raw?: string | null): string | undefined => {
|
||||||
|
|||||||
@ -25,6 +25,8 @@ const optionNamesAdd = [
|
|||||||
"tokenFile",
|
"tokenFile",
|
||||||
"botToken",
|
"botToken",
|
||||||
"appToken",
|
"appToken",
|
||||||
|
"appId",
|
||||||
|
"appSecret",
|
||||||
"signalNumber",
|
"signalNumber",
|
||||||
"cliPath",
|
"cliPath",
|
||||||
"dbPath",
|
"dbPath",
|
||||||
@ -162,6 +164,8 @@ export function registerChannelsCli(program: Command) {
|
|||||||
.option("--token-file <path>", "Bot token file (Telegram)")
|
.option("--token-file <path>", "Bot token file (Telegram)")
|
||||||
.option("--bot-token <token>", "Slack bot token (xoxb-...)")
|
.option("--bot-token <token>", "Slack bot token (xoxb-...)")
|
||||||
.option("--app-token <token>", "Slack app token (xapp-...)")
|
.option("--app-token <token>", "Slack app token (xapp-...)")
|
||||||
|
.option("--app-id <id>", "Feishu app id")
|
||||||
|
.option("--app-secret <secret>", "Feishu app secret")
|
||||||
.option("--signal-number <e164>", "Signal account number (E.164)")
|
.option("--signal-number <e164>", "Signal account number (E.164)")
|
||||||
.option("--cli-path <path>", "CLI path (signal-cli or imsg)")
|
.option("--cli-path <path>", "CLI path (signal-cli or imsg)")
|
||||||
.option("--db-path <path>", "iMessage database path")
|
.option("--db-path <path>", "iMessage database path")
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export function applyChannelAccountConfig(params: {
|
|||||||
tokenFile?: string;
|
tokenFile?: string;
|
||||||
botToken?: string;
|
botToken?: string;
|
||||||
appToken?: string;
|
appToken?: string;
|
||||||
|
appId?: string;
|
||||||
|
appSecret?: string;
|
||||||
signalNumber?: string;
|
signalNumber?: string;
|
||||||
cliPath?: string;
|
cliPath?: string;
|
||||||
dbPath?: string;
|
dbPath?: string;
|
||||||
@ -63,6 +65,8 @@ export function applyChannelAccountConfig(params: {
|
|||||||
tokenFile: params.tokenFile,
|
tokenFile: params.tokenFile,
|
||||||
botToken: params.botToken,
|
botToken: params.botToken,
|
||||||
appToken: params.appToken,
|
appToken: params.appToken,
|
||||||
|
appId: params.appId,
|
||||||
|
appSecret: params.appSecret,
|
||||||
signalNumber: params.signalNumber,
|
signalNumber: params.signalNumber,
|
||||||
cliPath: params.cliPath,
|
cliPath: params.cliPath,
|
||||||
dbPath: params.dbPath,
|
dbPath: params.dbPath,
|
||||||
|
|||||||
@ -23,6 +23,8 @@ export type ChannelsAddOptions = {
|
|||||||
tokenFile?: string;
|
tokenFile?: string;
|
||||||
botToken?: string;
|
botToken?: string;
|
||||||
appToken?: string;
|
appToken?: string;
|
||||||
|
appId?: string;
|
||||||
|
appSecret?: string;
|
||||||
signalNumber?: string;
|
signalNumber?: string;
|
||||||
cliPath?: string;
|
cliPath?: string;
|
||||||
dbPath?: string;
|
dbPath?: string;
|
||||||
@ -191,6 +193,8 @@ export async function channelsAddCommand(
|
|||||||
tokenFile: opts.tokenFile,
|
tokenFile: opts.tokenFile,
|
||||||
botToken: opts.botToken,
|
botToken: opts.botToken,
|
||||||
appToken: opts.appToken,
|
appToken: opts.appToken,
|
||||||
|
appId: opts.appId,
|
||||||
|
appSecret: opts.appSecret,
|
||||||
signalNumber: opts.signalNumber,
|
signalNumber: opts.signalNumber,
|
||||||
cliPath: opts.cliPath,
|
cliPath: opts.cliPath,
|
||||||
dbPath: opts.dbPath,
|
dbPath: opts.dbPath,
|
||||||
@ -234,6 +238,8 @@ export async function channelsAddCommand(
|
|||||||
tokenFile: opts.tokenFile,
|
tokenFile: opts.tokenFile,
|
||||||
botToken: opts.botToken,
|
botToken: opts.botToken,
|
||||||
appToken: opts.appToken,
|
appToken: opts.appToken,
|
||||||
|
appId: opts.appId,
|
||||||
|
appSecret: opts.appSecret,
|
||||||
signalNumber: opts.signalNumber,
|
signalNumber: opts.signalNumber,
|
||||||
cliPath: opts.cliPath,
|
cliPath: opts.cliPath,
|
||||||
dbPath: opts.dbPath,
|
dbPath: opts.dbPath,
|
||||||
|
|||||||
63
src/config/config.feishu.test.ts
Normal file
63
src/config/config.feishu.test.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { withTempHome } from "./test-helpers.js";
|
||||||
|
|
||||||
|
describe("config feishu", () => {
|
||||||
|
let previousHome: string | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
previousHome = process.env.HOME;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.HOME = previousHome;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("loads feishu app credentials + dm and group settings", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
const configDir = path.join(home, ".clawdbot");
|
||||||
|
await fs.mkdir(configDir, { recursive: true });
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(configDir, "moltbot.json"),
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
channels: {
|
||||||
|
feishu: {
|
||||||
|
enabled: true,
|
||||||
|
appId: "app-id",
|
||||||
|
appSecret: "app-secret",
|
||||||
|
dm: {
|
||||||
|
enabled: true,
|
||||||
|
policy: "allowlist",
|
||||||
|
allowFrom: ["ou_123"],
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
oc_456: {
|
||||||
|
requireMention: false,
|
||||||
|
users: ["ou_123"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
const { loadConfig } = await import("./config.js");
|
||||||
|
const cfg = loadConfig();
|
||||||
|
|
||||||
|
expect(cfg.channels?.feishu?.enabled).toBe(true);
|
||||||
|
expect(cfg.channels?.feishu?.appId).toBe("app-id");
|
||||||
|
expect(cfg.channels?.feishu?.appSecret).toBe("app-secret");
|
||||||
|
expect(cfg.channels?.feishu?.dm?.policy).toBe("allowlist");
|
||||||
|
expect(cfg.channels?.feishu?.dm?.allowFrom).toEqual(["ou_123"]);
|
||||||
|
expect(cfg.channels?.feishu?.groups?.["oc_456"]?.requireMention).toBe(false);
|
||||||
|
expect(cfg.channels?.feishu?.groups?.["oc_456"]?.users).toEqual(["ou_123"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -294,6 +294,7 @@ const FIELD_LABELS: Record<string, string> = {
|
|||||||
"channels.telegram": "Telegram",
|
"channels.telegram": "Telegram",
|
||||||
"channels.telegram.customCommands": "Telegram Custom Commands",
|
"channels.telegram.customCommands": "Telegram Custom Commands",
|
||||||
"channels.discord": "Discord",
|
"channels.discord": "Discord",
|
||||||
|
"channels.feishu": "Feishu",
|
||||||
"channels.slack": "Slack",
|
"channels.slack": "Slack",
|
||||||
"channels.mattermost": "Mattermost",
|
"channels.mattermost": "Mattermost",
|
||||||
"channels.signal": "Signal",
|
"channels.signal": "Signal",
|
||||||
@ -320,6 +321,7 @@ const FIELD_LABELS: Record<string, string> = {
|
|||||||
"channels.imessage.dmPolicy": "iMessage DM Policy",
|
"channels.imessage.dmPolicy": "iMessage DM Policy",
|
||||||
"channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
|
"channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
|
||||||
"channels.discord.dm.policy": "Discord DM Policy",
|
"channels.discord.dm.policy": "Discord DM Policy",
|
||||||
|
"channels.feishu.dm.policy": "Feishu DM Policy",
|
||||||
"channels.discord.retry.attempts": "Discord Retry Attempts",
|
"channels.discord.retry.attempts": "Discord Retry Attempts",
|
||||||
"channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
|
"channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
|
||||||
"channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
|
"channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
|
||||||
@ -330,6 +332,8 @@ const FIELD_LABELS: Record<string, string> = {
|
|||||||
"channels.slack.dm.policy": "Slack DM Policy",
|
"channels.slack.dm.policy": "Slack DM Policy",
|
||||||
"channels.slack.allowBots": "Slack Allow Bot Messages",
|
"channels.slack.allowBots": "Slack Allow Bot Messages",
|
||||||
"channels.discord.token": "Discord Bot Token",
|
"channels.discord.token": "Discord Bot Token",
|
||||||
|
"channels.feishu.appId": "Feishu App ID",
|
||||||
|
"channels.feishu.appSecret": "Feishu App Secret",
|
||||||
"channels.slack.botToken": "Slack Bot Token",
|
"channels.slack.botToken": "Slack Bot Token",
|
||||||
"channels.slack.appToken": "Slack App Token",
|
"channels.slack.appToken": "Slack App Token",
|
||||||
"channels.slack.userToken": "Slack User Token",
|
"channels.slack.userToken": "Slack User Token",
|
||||||
@ -602,6 +606,8 @@ const FIELD_HELP: Record<string, string> = {
|
|||||||
"Allow Mattermost to write config in response to channel events/commands (default: true).",
|
"Allow Mattermost to write config in response to channel events/commands (default: true).",
|
||||||
"channels.discord.configWrites":
|
"channels.discord.configWrites":
|
||||||
"Allow Discord to write config in response to channel events/commands (default: true).",
|
"Allow Discord to write config in response to channel events/commands (default: true).",
|
||||||
|
"channels.feishu.configWrites":
|
||||||
|
"Allow Feishu to write config in response to channel events/commands (default: true).",
|
||||||
"channels.whatsapp.configWrites":
|
"channels.whatsapp.configWrites":
|
||||||
"Allow WhatsApp to write config in response to channel events/commands (default: true).",
|
"Allow WhatsApp to write config in response to channel events/commands (default: true).",
|
||||||
"channels.signal.configWrites":
|
"channels.signal.configWrites":
|
||||||
@ -661,6 +667,8 @@ const FIELD_HELP: Record<string, string> = {
|
|||||||
'Direct message access control ("pairing" recommended). "open" requires channels.bluebubbles.allowFrom=["*"].',
|
'Direct message access control ("pairing" recommended). "open" requires channels.bluebubbles.allowFrom=["*"].',
|
||||||
"channels.discord.dm.policy":
|
"channels.discord.dm.policy":
|
||||||
'Direct message access control ("pairing" recommended). "open" requires channels.discord.dm.allowFrom=["*"].',
|
'Direct message access control ("pairing" recommended). "open" requires channels.discord.dm.allowFrom=["*"].',
|
||||||
|
"channels.feishu.dm.policy":
|
||||||
|
'Direct message access control ("pairing" recommended). "open" requires channels.feishu.dm.allowFrom=["*"].',
|
||||||
"channels.discord.retry.attempts":
|
"channels.discord.retry.attempts":
|
||||||
"Max retry attempts for outbound Discord API calls (default: 3).",
|
"Max retry attempts for outbound Discord API calls (default: 3).",
|
||||||
"channels.discord.retry.minDelayMs": "Minimum retry delay in ms for Discord outbound calls.",
|
"channels.discord.retry.minDelayMs": "Minimum retry delay in ms for Discord outbound calls.",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { DiscordConfig } from "./types.discord.js";
|
import type { DiscordConfig } from "./types.discord.js";
|
||||||
|
import type { FeishuConfig } from "./types.feishu.js";
|
||||||
import type { GoogleChatConfig } from "./types.googlechat.js";
|
import type { GoogleChatConfig } from "./types.googlechat.js";
|
||||||
import type { IMessageConfig } from "./types.imessage.js";
|
import type { IMessageConfig } from "./types.imessage.js";
|
||||||
import type { MSTeamsConfig } from "./types.msteams.js";
|
import type { MSTeamsConfig } from "./types.msteams.js";
|
||||||
@ -28,6 +29,7 @@ export type ChannelsConfig = {
|
|||||||
whatsapp?: WhatsAppConfig;
|
whatsapp?: WhatsAppConfig;
|
||||||
telegram?: TelegramConfig;
|
telegram?: TelegramConfig;
|
||||||
discord?: DiscordConfig;
|
discord?: DiscordConfig;
|
||||||
|
feishu?: FeishuConfig;
|
||||||
googlechat?: GoogleChatConfig;
|
googlechat?: GoogleChatConfig;
|
||||||
slack?: SlackConfig;
|
slack?: SlackConfig;
|
||||||
signal?: SignalConfig;
|
signal?: SignalConfig;
|
||||||
|
|||||||
90
src/config/types.feishu.ts
Normal file
90
src/config/types.feishu.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import type {
|
||||||
|
BlockStreamingCoalesceConfig,
|
||||||
|
DmPolicy,
|
||||||
|
GroupPolicy,
|
||||||
|
MarkdownConfig,
|
||||||
|
ReplyToMode,
|
||||||
|
} from "./types.base.js";
|
||||||
|
import type { ChannelHeartbeatVisibilityConfig } from "./types.channels.js";
|
||||||
|
import type { DmConfig } from "./types.messages.js";
|
||||||
|
import type { GroupToolPolicyBySenderConfig, GroupToolPolicyConfig } from "./types.tools.js";
|
||||||
|
|
||||||
|
export type FeishuDmConfig = {
|
||||||
|
/** If false, ignore all incoming Feishu DMs. Default: true. */
|
||||||
|
enabled?: boolean;
|
||||||
|
/** Direct message access policy (default: pairing). */
|
||||||
|
policy?: DmPolicy;
|
||||||
|
/** Allowlist for DM senders (ids). */
|
||||||
|
allowFrom?: Array<string | number>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeishuGroupConfig = {
|
||||||
|
/** If false, disable the bot in this chat. (Alias for allow: false.) */
|
||||||
|
enabled?: boolean;
|
||||||
|
/** Legacy group allow toggle; prefer enabled. */
|
||||||
|
allow?: boolean;
|
||||||
|
/** Require mentioning the bot to trigger replies. */
|
||||||
|
requireMention?: boolean;
|
||||||
|
/** Optional tool policy overrides for this chat. */
|
||||||
|
tools?: GroupToolPolicyConfig;
|
||||||
|
toolsBySender?: GroupToolPolicyBySenderConfig;
|
||||||
|
/** Allowlist of users that can invoke the bot in this chat. */
|
||||||
|
users?: Array<string | number>;
|
||||||
|
/** Optional system prompt for this chat. */
|
||||||
|
systemPrompt?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeishuAccountConfig = {
|
||||||
|
/** Optional display name for this account (used in CLI/UI lists). */
|
||||||
|
name?: string;
|
||||||
|
/** Optional provider capability tags used for agent/runtime guidance. */
|
||||||
|
capabilities?: string[];
|
||||||
|
/** Markdown formatting overrides (tables). */
|
||||||
|
markdown?: MarkdownConfig;
|
||||||
|
/** Allow channel-initiated config writes (default: true). */
|
||||||
|
configWrites?: boolean;
|
||||||
|
/** If false, do not start this Feishu account. Default: true. */
|
||||||
|
enabled?: boolean;
|
||||||
|
/** Feishu app id. */
|
||||||
|
appId?: string;
|
||||||
|
/** Feishu app secret. */
|
||||||
|
appSecret?: string;
|
||||||
|
/** Allow bot-authored messages to trigger replies (default: false). */
|
||||||
|
allowBots?: boolean;
|
||||||
|
/** Default mention requirement for group chats (default: true). */
|
||||||
|
requireMention?: boolean;
|
||||||
|
/**
|
||||||
|
* Controls how group chats are handled:
|
||||||
|
* - "open": chats bypass allowlists; mention-gating applies
|
||||||
|
* - "disabled": block all group chats
|
||||||
|
* - "allowlist": only allow chats present in channels.feishu.groups
|
||||||
|
*/
|
||||||
|
groupPolicy?: GroupPolicy;
|
||||||
|
/** Max group messages to keep as history context (0 disables). */
|
||||||
|
historyLimit?: number;
|
||||||
|
/** Max DM turns to keep as history context. */
|
||||||
|
dmHistoryLimit?: number;
|
||||||
|
/** Per-DM config overrides keyed by user ID. */
|
||||||
|
dms?: Record<string, DmConfig>;
|
||||||
|
/** Outbound text chunk size (chars). Default: 4000. */
|
||||||
|
textChunkLimit?: number;
|
||||||
|
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */
|
||||||
|
chunkMode?: "length" | "newline";
|
||||||
|
/** Merge streamed block replies before sending. */
|
||||||
|
blockStreaming?: boolean;
|
||||||
|
/** Merge streamed block replies before sending. */
|
||||||
|
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
|
||||||
|
/** Max media size in MB. */
|
||||||
|
mediaMaxMb?: number;
|
||||||
|
/** Control reply threading when reply tags are present (off|first|all). */
|
||||||
|
replyToMode?: ReplyToMode;
|
||||||
|
dm?: FeishuDmConfig;
|
||||||
|
groups?: Record<string, FeishuGroupConfig>;
|
||||||
|
/** Heartbeat visibility settings for this channel. */
|
||||||
|
heartbeat?: ChannelHeartbeatVisibilityConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeishuConfig = {
|
||||||
|
/** Optional per-account Feishu configuration (multi-account). */
|
||||||
|
accounts?: Record<string, FeishuAccountConfig>;
|
||||||
|
} & FeishuAccountConfig;
|
||||||
@ -25,6 +25,7 @@ export type HookMappingConfig = {
|
|||||||
| "whatsapp"
|
| "whatsapp"
|
||||||
| "telegram"
|
| "telegram"
|
||||||
| "discord"
|
| "discord"
|
||||||
|
| "feishu"
|
||||||
| "googlechat"
|
| "googlechat"
|
||||||
| "slack"
|
| "slack"
|
||||||
| "signal"
|
| "signal"
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export type QueueModeByProvider = {
|
|||||||
whatsapp?: QueueMode;
|
whatsapp?: QueueMode;
|
||||||
telegram?: QueueMode;
|
telegram?: QueueMode;
|
||||||
discord?: QueueMode;
|
discord?: QueueMode;
|
||||||
|
feishu?: QueueMode;
|
||||||
googlechat?: QueueMode;
|
googlechat?: QueueMode;
|
||||||
slack?: QueueMode;
|
slack?: QueueMode;
|
||||||
signal?: QueueMode;
|
signal?: QueueMode;
|
||||||
|
|||||||
@ -10,6 +10,7 @@ export * from "./types.channels.js";
|
|||||||
export * from "./types.clawdbot.js";
|
export * from "./types.clawdbot.js";
|
||||||
export * from "./types.cron.js";
|
export * from "./types.cron.js";
|
||||||
export * from "./types.discord.js";
|
export * from "./types.discord.js";
|
||||||
|
export * from "./types.feishu.js";
|
||||||
export * from "./types.googlechat.js";
|
export * from "./types.googlechat.js";
|
||||||
export * from "./types.gateway.js";
|
export * from "./types.gateway.js";
|
||||||
export * from "./types.hooks.js";
|
export * from "./types.hooks.js";
|
||||||
|
|||||||
@ -306,6 +306,7 @@ export const QueueModeBySurfaceSchema = z
|
|||||||
whatsapp: QueueModeSchema.optional(),
|
whatsapp: QueueModeSchema.optional(),
|
||||||
telegram: QueueModeSchema.optional(),
|
telegram: QueueModeSchema.optional(),
|
||||||
discord: QueueModeSchema.optional(),
|
discord: QueueModeSchema.optional(),
|
||||||
|
feishu: QueueModeSchema.optional(),
|
||||||
slack: QueueModeSchema.optional(),
|
slack: QueueModeSchema.optional(),
|
||||||
mattermost: QueueModeSchema.optional(),
|
mattermost: QueueModeSchema.optional(),
|
||||||
signal: QueueModeSchema.optional(),
|
signal: QueueModeSchema.optional(),
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export const HookMappingSchema = z
|
|||||||
z.literal("whatsapp"),
|
z.literal("whatsapp"),
|
||||||
z.literal("telegram"),
|
z.literal("telegram"),
|
||||||
z.literal("discord"),
|
z.literal("discord"),
|
||||||
|
z.literal("feishu"),
|
||||||
z.literal("slack"),
|
z.literal("slack"),
|
||||||
z.literal("signal"),
|
z.literal("signal"),
|
||||||
z.literal("imessage"),
|
z.literal("imessage"),
|
||||||
|
|||||||
@ -282,6 +282,67 @@ export const DiscordConfigSchema = DiscordAccountSchema.extend({
|
|||||||
accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional(),
|
accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const FeishuDmSchema = z
|
||||||
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
|
policy: DmPolicySchema.optional().default("pairing"),
|
||||||
|
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
.superRefine((value, ctx) => {
|
||||||
|
requireOpenAllowFrom({
|
||||||
|
policy: value.policy,
|
||||||
|
allowFrom: value.allowFrom,
|
||||||
|
ctx,
|
||||||
|
path: ["allowFrom"],
|
||||||
|
message:
|
||||||
|
'channels.feishu.dm.policy="open" requires channels.feishu.dm.allowFrom to include "*"',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FeishuGroupSchema = z
|
||||||
|
.object({
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
|
allow: z.boolean().optional(),
|
||||||
|
requireMention: z.boolean().optional(),
|
||||||
|
tools: ToolPolicySchema,
|
||||||
|
toolsBySender: ToolPolicyBySenderSchema,
|
||||||
|
users: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
|
systemPrompt: z.string().optional(),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const FeishuAccountSchema = z
|
||||||
|
.object({
|
||||||
|
name: z.string().optional(),
|
||||||
|
capabilities: z.array(z.string()).optional(),
|
||||||
|
markdown: MarkdownConfigSchema.optional(),
|
||||||
|
configWrites: z.boolean().optional(),
|
||||||
|
enabled: z.boolean().optional(),
|
||||||
|
appId: z.string().optional(),
|
||||||
|
appSecret: z.string().optional(),
|
||||||
|
allowBots: z.boolean().optional(),
|
||||||
|
requireMention: z.boolean().optional(),
|
||||||
|
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
||||||
|
historyLimit: z.number().int().min(0).optional(),
|
||||||
|
dmHistoryLimit: z.number().int().min(0).optional(),
|
||||||
|
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
||||||
|
textChunkLimit: z.number().int().positive().optional(),
|
||||||
|
chunkMode: z.enum(["length", "newline"]).optional(),
|
||||||
|
blockStreaming: z.boolean().optional(),
|
||||||
|
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
||||||
|
mediaMaxMb: z.number().positive().optional(),
|
||||||
|
replyToMode: ReplyToModeSchema.optional(),
|
||||||
|
dm: FeishuDmSchema.optional(),
|
||||||
|
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
||||||
|
heartbeat: ChannelHeartbeatVisibilitySchema,
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
|
|
||||||
|
export const FeishuConfigSchema = FeishuAccountSchema.extend({
|
||||||
|
accounts: z.record(z.string(), FeishuAccountSchema.optional()).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
export const GoogleChatDmSchema = z
|
export const GoogleChatDmSchema = z
|
||||||
.object({
|
.object({
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { z } from "zod";
|
|||||||
import {
|
import {
|
||||||
BlueBubblesConfigSchema,
|
BlueBubblesConfigSchema,
|
||||||
DiscordConfigSchema,
|
DiscordConfigSchema,
|
||||||
|
FeishuConfigSchema,
|
||||||
GoogleChatConfigSchema,
|
GoogleChatConfigSchema,
|
||||||
IMessageConfigSchema,
|
IMessageConfigSchema,
|
||||||
MSTeamsConfigSchema,
|
MSTeamsConfigSchema,
|
||||||
@ -30,6 +31,7 @@ export const ChannelsSchema = z
|
|||||||
whatsapp: WhatsAppConfigSchema.optional(),
|
whatsapp: WhatsAppConfigSchema.optional(),
|
||||||
telegram: TelegramConfigSchema.optional(),
|
telegram: TelegramConfigSchema.optional(),
|
||||||
discord: DiscordConfigSchema.optional(),
|
discord: DiscordConfigSchema.optional(),
|
||||||
|
feishu: FeishuConfigSchema.optional(),
|
||||||
googlechat: GoogleChatConfigSchema.optional(),
|
googlechat: GoogleChatConfigSchema.optional(),
|
||||||
slack: SlackConfigSchema.optional(),
|
slack: SlackConfigSchema.optional(),
|
||||||
signal: SignalConfigSchema.optional(),
|
signal: SignalConfigSchema.optional(),
|
||||||
|
|||||||
@ -84,6 +84,7 @@ export type {
|
|||||||
GroupToolPolicyBySenderConfig,
|
GroupToolPolicyBySenderConfig,
|
||||||
MarkdownConfig,
|
MarkdownConfig,
|
||||||
MarkdownTableMode,
|
MarkdownTableMode,
|
||||||
|
FeishuConfig,
|
||||||
GoogleChatAccountConfig,
|
GoogleChatAccountConfig,
|
||||||
GoogleChatConfig,
|
GoogleChatConfig,
|
||||||
GoogleChatDmConfig,
|
GoogleChatDmConfig,
|
||||||
@ -96,6 +97,7 @@ export type {
|
|||||||
} from "../config/types.js";
|
} from "../config/types.js";
|
||||||
export {
|
export {
|
||||||
DiscordConfigSchema,
|
DiscordConfigSchema,
|
||||||
|
FeishuConfigSchema,
|
||||||
GoogleChatConfigSchema,
|
GoogleChatConfigSchema,
|
||||||
IMessageConfigSchema,
|
IMessageConfigSchema,
|
||||||
MSTeamsConfigSchema,
|
MSTeamsConfigSchema,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user