@nextclaw/channel-plugin-feishu 0.2.14 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +2 -2
- package/package.json +1 -2
- package/src/accounts.ts +2 -2
- package/src/bitable.ts +1 -1
- package/src/bot.test.ts +1 -1
- package/src/bot.ts +2 -2
- package/src/card-action.ts +1 -1
- package/src/channel.test.ts +1 -1
- package/src/channel.ts +3 -3
- package/src/chat.ts +1 -1
- package/src/config-schema.ts +1 -1
- package/src/dedup.ts +1 -1
- package/src/directory.test.ts +1 -1
- package/src/directory.ts +2 -2
- package/src/docx.account-selection.test.ts +1 -1
- package/src/docx.ts +1 -1
- package/src/drive.ts +1 -1
- package/src/dynamic-agent.ts +1 -1
- package/src/media.ts +1 -1
- package/src/monitor.account.ts +1 -1
- package/src/monitor.reaction.test.ts +1 -1
- package/src/monitor.startup.test.ts +1 -1
- package/src/monitor.startup.ts +1 -1
- package/src/monitor.state.ts +1 -1
- package/src/monitor.transport.ts +1 -1
- package/src/monitor.ts +1 -1
- package/src/monitor.webhook.test-helpers.ts +1 -1
- package/src/nextclaw-sdk/account-id.ts +31 -0
- package/src/nextclaw-sdk/compat.ts +8 -0
- package/src/nextclaw-sdk/core-channel.ts +296 -0
- package/src/nextclaw-sdk/core-pairing.ts +224 -0
- package/src/nextclaw-sdk/core.ts +26 -0
- package/src/nextclaw-sdk/dedupe.ts +246 -0
- package/src/nextclaw-sdk/feishu.ts +77 -0
- package/src/nextclaw-sdk/history.ts +127 -0
- package/src/nextclaw-sdk/network-body.ts +245 -0
- package/src/nextclaw-sdk/network-fetch.ts +129 -0
- package/src/nextclaw-sdk/network-webhook.ts +182 -0
- package/src/nextclaw-sdk/network.ts +13 -0
- package/src/nextclaw-sdk/runtime-store.ts +26 -0
- package/src/nextclaw-sdk/secrets-config.ts +109 -0
- package/src/nextclaw-sdk/secrets-core.ts +170 -0
- package/src/nextclaw-sdk/secrets-prompt.ts +305 -0
- package/src/nextclaw-sdk/secrets.ts +18 -0
- package/src/nextclaw-sdk/types.ts +300 -0
- package/src/onboarding.status.test.ts +1 -1
- package/src/onboarding.ts +2 -2
- package/src/outbound.ts +1 -1
- package/src/perm.ts +1 -1
- package/src/policy.ts +2 -2
- package/src/reactions.ts +1 -1
- package/src/reply-dispatcher.ts +1 -1
- package/src/runtime.ts +2 -2
- package/src/secret-input.ts +1 -1
- package/src/send-target.test.ts +1 -1
- package/src/send-target.ts +1 -1
- package/src/send.test.ts +1 -1
- package/src/send.ts +1 -1
- package/src/streaming-card.ts +1 -1
- package/src/tool-account-routing.test.ts +1 -1
- package/src/tool-account.ts +1 -1
- package/src/tool-factory-test-harness.ts +1 -1
- package/src/types.ts +1 -1
- package/src/typing.ts +1 -1
- package/src/wiki.ts +1 -1
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "
|
|
2
|
-
import { emptyPluginConfigSchema } from "
|
|
1
|
+
import type { OpenClawPluginApi } from "./src/nextclaw-sdk/feishu.js";
|
|
2
|
+
import { emptyPluginConfigSchema } from "./src/nextclaw-sdk/feishu.js";
|
|
3
3
|
import { registerFeishuBitableTools } from "./src/bitable.js";
|
|
4
4
|
import { feishuPlugin } from "./src/channel.js";
|
|
5
5
|
import { registerFeishuChatTools } from "./src/chat.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/channel-plugin-feishu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "NextClaw Feishu/Lark channel plugin with doc/wiki/drive tools.",
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
44
44
|
"@sinclair/typebox": "0.34.48",
|
|
45
45
|
"https-proxy-agent": "^8.0.0",
|
|
46
|
-
"openclaw": "2026.3.13",
|
|
47
46
|
"zod": "^4.3.6"
|
|
48
47
|
},
|
|
49
48
|
"scripts": {
|
package/src/accounts.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "
|
|
2
|
-
import type { ClawdbotConfig } from "
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "./nextclaw-sdk/account-id.js";
|
|
2
|
+
import type { ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
3
3
|
import { normalizeResolvedSecretInputString, normalizeSecretInputString } from "./secret-input.js";
|
|
4
4
|
import type {
|
|
5
5
|
FeishuConfig,
|
package/src/bitable.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
|
-
import type { OpenClawPluginApi } from "
|
|
3
|
+
import type { OpenClawPluginApi } from "./nextclaw-sdk/feishu.js";
|
|
4
4
|
import { listEnabledFeishuAccounts } from "./accounts.js";
|
|
5
5
|
import { createFeishuToolClient } from "./tool-account.js";
|
|
6
6
|
|
package/src/bot.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, PluginRuntime, RuntimeEnv } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { createPluginRuntimeMock } from "../../test-utils/plugin-runtime-mock.js";
|
|
4
4
|
import type { FeishuMessageEvent } from "./bot.js";
|
package/src/bot.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import {
|
|
3
3
|
buildAgentMediaPayload,
|
|
4
4
|
buildPendingHistoryContextFromMap,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
resolveOpenProviderRuntimeGroupPolicy,
|
|
13
13
|
resolveDefaultGroupPolicy,
|
|
14
14
|
warnMissingProviderGroupPolicyFallbackOnce,
|
|
15
|
-
} from "
|
|
15
|
+
} from "./nextclaw-sdk/feishu.js";
|
|
16
16
|
import { resolveFeishuAccount } from "./accounts.js";
|
|
17
17
|
import { createFeishuClient } from "./client.js";
|
|
18
18
|
import { finalizeFeishuMessageProcessing, tryRecordMessagePersistent } from "./dedup.js";
|
package/src/card-action.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { resolveFeishuAccount } from "./accounts.js";
|
|
3
3
|
import { handleFeishuMessage, type FeishuMessageEvent } from "./bot.js";
|
|
4
4
|
|
package/src/channel.test.ts
CHANGED
package/src/channel.ts
CHANGED
|
@@ -2,15 +2,15 @@ import {
|
|
|
2
2
|
collectAllowlistProviderRestrictSendersWarnings,
|
|
3
3
|
formatAllowFromLowercase,
|
|
4
4
|
mapAllowFromEntries,
|
|
5
|
-
} from "
|
|
6
|
-
import type { ChannelMeta, ChannelPlugin, ClawdbotConfig } from "
|
|
5
|
+
} from "./nextclaw-sdk/compat.js";
|
|
6
|
+
import type { ChannelMeta, ChannelPlugin, ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
7
7
|
import {
|
|
8
8
|
buildProbeChannelStatusSummary,
|
|
9
9
|
buildRuntimeAccountStatusSnapshot,
|
|
10
10
|
createDefaultChannelRuntimeState,
|
|
11
11
|
DEFAULT_ACCOUNT_ID,
|
|
12
12
|
PAIRING_APPROVED_MESSAGE,
|
|
13
|
-
} from "
|
|
13
|
+
} from "./nextclaw-sdk/feishu.js";
|
|
14
14
|
import {
|
|
15
15
|
resolveFeishuAccount,
|
|
16
16
|
resolveFeishuCredentials,
|
package/src/chat.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
2
|
-
import type { OpenClawPluginApi } from "
|
|
2
|
+
import type { OpenClawPluginApi } from "./nextclaw-sdk/feishu.js";
|
|
3
3
|
import { listEnabledFeishuAccounts } from "./accounts.js";
|
|
4
4
|
import { FeishuChatSchema, type FeishuChatParams } from "./chat-schema.js";
|
|
5
5
|
import { createFeishuClient } from "./client.js";
|
package/src/config-schema.ts
CHANGED
package/src/dedup.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
createDedupeCache,
|
|
5
5
|
createPersistentDedupe,
|
|
6
6
|
readJsonFileWithFallback,
|
|
7
|
-
} from "
|
|
7
|
+
} from "./nextclaw-sdk/feishu.js";
|
|
8
8
|
|
|
9
9
|
// Persistent TTL: 24 hours — survives restarts & WebSocket reconnects.
|
|
10
10
|
const DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
package/src/directory.test.ts
CHANGED
package/src/directory.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listDirectoryGroupEntriesFromMapKeysAndAllowFrom,
|
|
3
3
|
listDirectoryUserEntriesFromAllowFromAndMapKeys,
|
|
4
|
-
} from "
|
|
5
|
-
import type { ClawdbotConfig } from "
|
|
4
|
+
} from "./nextclaw-sdk/compat.js";
|
|
5
|
+
import type { ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
6
6
|
import { resolveFeishuAccount } from "./accounts.js";
|
|
7
7
|
import { createFeishuClient } from "./client.js";
|
|
8
8
|
import { normalizeFeishuTarget } from "./targets.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "
|
|
1
|
+
import type { OpenClawPluginApi } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { describe, expect, test, vi } from "vitest";
|
|
3
3
|
import { registerFeishuDocTools } from "./docx.js";
|
|
4
4
|
import { createToolFactoryHarness } from "./tool-factory-test-harness.js";
|
package/src/docx.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { isAbsolute } from "node:path";
|
|
|
4
4
|
import { basename } from "node:path";
|
|
5
5
|
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
|
-
import type { OpenClawPluginApi } from "
|
|
7
|
+
import type { OpenClawPluginApi } from "./nextclaw-sdk/feishu.js";
|
|
8
8
|
import { listEnabledFeishuAccounts } from "./accounts.js";
|
|
9
9
|
import { FeishuDocSchema, type FeishuDocParams } from "./doc-schema.js";
|
|
10
10
|
import { BATCH_SIZE, insertBlocksInBatches } from "./docx-batch-insert.js";
|
package/src/drive.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
2
|
-
import type { OpenClawPluginApi } from "
|
|
2
|
+
import type { OpenClawPluginApi } from "./nextclaw-sdk/feishu.js";
|
|
3
3
|
import { listEnabledFeishuAccounts } from "./accounts.js";
|
|
4
4
|
import { FeishuDriveSchema, type FeishuDriveParams } from "./drive-schema.js";
|
|
5
5
|
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
|
package/src/dynamic-agent.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import type { OpenClawConfig, PluginRuntime } from "
|
|
4
|
+
import type { OpenClawConfig, PluginRuntime } from "./nextclaw-sdk/feishu.js";
|
|
5
5
|
import type { DynamicAgentCreationConfig } from "./types.js";
|
|
6
6
|
|
|
7
7
|
export type MaybeCreateDynamicAgentResult = {
|
package/src/media.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { Readable } from "stream";
|
|
4
|
-
import { withTempDownloadPath, type ClawdbotConfig } from "
|
|
4
|
+
import { withTempDownloadPath, type ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
5
5
|
import { resolveFeishuAccount } from "./accounts.js";
|
|
6
6
|
import { createFeishuClient } from "./client.js";
|
|
7
7
|
import { normalizeFeishuExternalKey } from "./external-keys.js";
|
package/src/monitor.account.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as crypto from "crypto";
|
|
2
2
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
3
|
-
import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "
|
|
3
|
+
import type { ClawdbotConfig, RuntimeEnv, HistoryEntry } from "./nextclaw-sdk/feishu.js";
|
|
4
4
|
import { resolveFeishuAccount } from "./accounts.js";
|
|
5
5
|
import { raceWithTimeoutAndAbort } from "./async.js";
|
|
6
6
|
import {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { hasControlCommand } from "../../../src/auto-reply/command-detection.js";
|
|
4
4
|
import {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig } from "
|
|
1
|
+
import type { ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { monitorFeishuProvider, stopFeishuMonitor } from "./monitor.js";
|
|
4
4
|
|
package/src/monitor.startup.ts
CHANGED
package/src/monitor.state.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type RuntimeEnv,
|
|
7
7
|
WEBHOOK_ANOMALY_COUNTER_DEFAULTS as WEBHOOK_ANOMALY_COUNTER_DEFAULTS_FROM_SDK,
|
|
8
8
|
WEBHOOK_RATE_LIMIT_DEFAULTS as WEBHOOK_RATE_LIMIT_DEFAULTS_FROM_SDK,
|
|
9
|
-
} from "
|
|
9
|
+
} from "./nextclaw-sdk/feishu.js";
|
|
10
10
|
|
|
11
11
|
export const wsClients = new Map<string, Lark.WSClient>();
|
|
12
12
|
export const httpServers = new Map<string, http.Server>();
|
package/src/monitor.transport.ts
CHANGED
package/src/monitor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawdbotConfig, RuntimeEnv } from "
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "./nextclaw-sdk/feishu.js";
|
|
2
2
|
import { listEnabledFeishuAccounts, resolveFeishuAccount } from "./accounts.js";
|
|
3
3
|
import {
|
|
4
4
|
monitorSingleAccount,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import type { AddressInfo } from "node:net";
|
|
3
|
-
import type { ClawdbotConfig } from "
|
|
3
|
+
import type { ClawdbotConfig } from "./nextclaw-sdk/feishu.js";
|
|
4
4
|
import { vi } from "vitest";
|
|
5
5
|
import type { monitorFeishuProvider } from "./monitor.js";
|
|
6
6
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const DEFAULT_ACCOUNT_ID = "default";
|
|
2
|
+
|
|
3
|
+
export function normalizeAccountId(accountId?: string | null): string {
|
|
4
|
+
const trimmed = accountId?.trim();
|
|
5
|
+
return trimmed || DEFAULT_ACCOUNT_ID;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function normalizeOptionalAccountId(accountId?: string | null): string | undefined {
|
|
9
|
+
const trimmed = accountId?.trim();
|
|
10
|
+
return trimmed ? normalizeAccountId(trimmed) : undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const VALID_AGENT_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
14
|
+
const INVALID_AGENT_CHARS_RE = /[^a-z0-9_-]+/g;
|
|
15
|
+
|
|
16
|
+
export function normalizeAgentId(value?: string | null): string {
|
|
17
|
+
const trimmed = value?.trim();
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
return "main";
|
|
20
|
+
}
|
|
21
|
+
if (VALID_AGENT_ID_RE.test(trimmed)) {
|
|
22
|
+
return trimmed.toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
const normalized = trimmed
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(INVALID_AGENT_CHARS_RE, "-")
|
|
27
|
+
.replace(/^-+/, "")
|
|
28
|
+
.replace(/-+$/, "")
|
|
29
|
+
.slice(0, 64);
|
|
30
|
+
return normalized || "main";
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {
|
|
2
|
+
collectAllowlistProviderRestrictSendersWarnings,
|
|
3
|
+
formatAllowFromLowercase,
|
|
4
|
+
listDirectoryGroupEntriesFromMapKeysAndAllowFrom,
|
|
5
|
+
listDirectoryUserEntriesFromAllowFromAndMapKeys,
|
|
6
|
+
mapAllowFromEntries,
|
|
7
|
+
} from "./core.js";
|
|
8
|
+
export { createPluginRuntimeStore } from "./runtime-store.js";
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import type { GroupPolicy, OpenClawConfig } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function emptyPluginConfigSchema(): {
|
|
4
|
+
type: "object";
|
|
5
|
+
additionalProperties: false;
|
|
6
|
+
properties: Record<string, unknown>;
|
|
7
|
+
} {
|
|
8
|
+
return {
|
|
9
|
+
type: "object",
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
properties: {},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const warnedMissingProviderGroupPolicy = new Set<string>();
|
|
16
|
+
|
|
17
|
+
export function resolveDefaultGroupPolicy(cfg: {
|
|
18
|
+
channels?: {
|
|
19
|
+
defaults?: {
|
|
20
|
+
groupPolicy?: GroupPolicy;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}): GroupPolicy | undefined {
|
|
24
|
+
return cfg.channels?.defaults?.groupPolicy;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveOpenProviderRuntimeGroupPolicy(params: {
|
|
28
|
+
providerConfigPresent: boolean;
|
|
29
|
+
groupPolicy?: GroupPolicy;
|
|
30
|
+
defaultGroupPolicy?: GroupPolicy;
|
|
31
|
+
}): {
|
|
32
|
+
groupPolicy: GroupPolicy;
|
|
33
|
+
providerMissingFallbackApplied: boolean;
|
|
34
|
+
} {
|
|
35
|
+
const groupPolicy = params.providerConfigPresent
|
|
36
|
+
? (params.groupPolicy ?? params.defaultGroupPolicy ?? "open")
|
|
37
|
+
: (params.groupPolicy ?? "allowlist");
|
|
38
|
+
return {
|
|
39
|
+
groupPolicy,
|
|
40
|
+
providerMissingFallbackApplied: !params.providerConfigPresent && params.groupPolicy === undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function warnMissingProviderGroupPolicyFallbackOnce(params: {
|
|
45
|
+
providerMissingFallbackApplied: boolean;
|
|
46
|
+
providerKey: string;
|
|
47
|
+
accountId?: string;
|
|
48
|
+
blockedLabel?: string;
|
|
49
|
+
log: (message: string) => void;
|
|
50
|
+
}): boolean {
|
|
51
|
+
if (!params.providerMissingFallbackApplied) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const key = `${params.providerKey}:${params.accountId ?? "*"}`;
|
|
55
|
+
if (warnedMissingProviderGroupPolicy.has(key)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
warnedMissingProviderGroupPolicy.add(key);
|
|
59
|
+
const blockedLabel = params.blockedLabel?.trim() || "group messages";
|
|
60
|
+
params.log(
|
|
61
|
+
`${params.providerKey}: channels.${params.providerKey} is missing; defaulting groupPolicy to "allowlist" (${blockedLabel} blocked until explicitly configured).`,
|
|
62
|
+
);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function evaluateSenderGroupAccessForPolicy(params: {
|
|
67
|
+
groupPolicy: GroupPolicy;
|
|
68
|
+
providerMissingFallbackApplied?: boolean;
|
|
69
|
+
groupAllowFrom: string[];
|
|
70
|
+
senderId: string;
|
|
71
|
+
isSenderAllowed: (senderId: string, allowFrom: string[]) => boolean;
|
|
72
|
+
}): {
|
|
73
|
+
allowed: boolean;
|
|
74
|
+
groupPolicy: GroupPolicy;
|
|
75
|
+
providerMissingFallbackApplied: boolean;
|
|
76
|
+
reason: "allowed" | "disabled" | "empty_allowlist" | "sender_not_allowlisted";
|
|
77
|
+
} {
|
|
78
|
+
if (params.groupPolicy === "disabled") {
|
|
79
|
+
return {
|
|
80
|
+
allowed: false,
|
|
81
|
+
groupPolicy: params.groupPolicy,
|
|
82
|
+
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
|
83
|
+
reason: "disabled",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (params.groupPolicy === "allowlist") {
|
|
87
|
+
if (params.groupAllowFrom.length === 0) {
|
|
88
|
+
return {
|
|
89
|
+
allowed: false,
|
|
90
|
+
groupPolicy: params.groupPolicy,
|
|
91
|
+
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
|
92
|
+
reason: "empty_allowlist",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!params.isSenderAllowed(params.senderId, params.groupAllowFrom)) {
|
|
96
|
+
return {
|
|
97
|
+
allowed: false,
|
|
98
|
+
groupPolicy: params.groupPolicy,
|
|
99
|
+
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
|
100
|
+
reason: "sender_not_allowlisted",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
allowed: true,
|
|
106
|
+
groupPolicy: params.groupPolicy,
|
|
107
|
+
providerMissingFallbackApplied: Boolean(params.providerMissingFallbackApplied),
|
|
108
|
+
reason: "allowed",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createDefaultChannelRuntimeState<T extends Record<string, unknown>>(
|
|
113
|
+
accountId: string,
|
|
114
|
+
extra?: T,
|
|
115
|
+
): {
|
|
116
|
+
accountId: string;
|
|
117
|
+
running: false;
|
|
118
|
+
lastStartAt: null;
|
|
119
|
+
lastStopAt: null;
|
|
120
|
+
lastError: null;
|
|
121
|
+
} & T {
|
|
122
|
+
return {
|
|
123
|
+
accountId,
|
|
124
|
+
running: false,
|
|
125
|
+
lastStartAt: null,
|
|
126
|
+
lastStopAt: null,
|
|
127
|
+
lastError: null,
|
|
128
|
+
...(extra ?? ({} as T)),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildProbeChannelStatusSummary<TExtra extends Record<string, unknown>>(
|
|
133
|
+
snapshot: {
|
|
134
|
+
configured?: boolean | null;
|
|
135
|
+
running?: boolean | null;
|
|
136
|
+
lastStartAt?: number | null;
|
|
137
|
+
lastStopAt?: number | null;
|
|
138
|
+
lastError?: string | null;
|
|
139
|
+
probe?: unknown;
|
|
140
|
+
lastProbeAt?: number | null;
|
|
141
|
+
},
|
|
142
|
+
extra?: TExtra,
|
|
143
|
+
) {
|
|
144
|
+
return {
|
|
145
|
+
configured: snapshot.configured ?? false,
|
|
146
|
+
running: snapshot.running ?? false,
|
|
147
|
+
lastStartAt: snapshot.lastStartAt ?? null,
|
|
148
|
+
lastStopAt: snapshot.lastStopAt ?? null,
|
|
149
|
+
lastError: snapshot.lastError ?? null,
|
|
150
|
+
...(extra ?? ({} as TExtra)),
|
|
151
|
+
probe: snapshot.probe,
|
|
152
|
+
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function buildRuntimeAccountStatusSnapshot(params: {
|
|
157
|
+
runtime?: {
|
|
158
|
+
running?: boolean | null;
|
|
159
|
+
lastStartAt?: number | null;
|
|
160
|
+
lastStopAt?: number | null;
|
|
161
|
+
lastError?: string | null;
|
|
162
|
+
} | null;
|
|
163
|
+
probe?: unknown;
|
|
164
|
+
}) {
|
|
165
|
+
return {
|
|
166
|
+
running: params.runtime?.running ?? false,
|
|
167
|
+
lastStartAt: params.runtime?.lastStartAt ?? null,
|
|
168
|
+
lastStopAt: params.runtime?.lastStopAt ?? null,
|
|
169
|
+
lastError: params.runtime?.lastError ?? null,
|
|
170
|
+
probe: params.probe,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function mapAllowFromEntries(
|
|
175
|
+
allowFrom: Array<string | number> | null | undefined,
|
|
176
|
+
): string[] {
|
|
177
|
+
return (allowFrom ?? []).map((entry) => String(entry));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function formatAllowFromLowercase(params: {
|
|
181
|
+
allowFrom: Array<string | number>;
|
|
182
|
+
stripPrefixRe?: RegExp;
|
|
183
|
+
}): string[] {
|
|
184
|
+
return params.allowFrom
|
|
185
|
+
.map((entry) => String(entry).trim())
|
|
186
|
+
.filter(Boolean)
|
|
187
|
+
.map((entry) => (params.stripPrefixRe ? entry.replace(params.stripPrefixRe, "") : entry))
|
|
188
|
+
.map((entry) => entry.toLowerCase());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function applyDirectoryQueryAndLimit(
|
|
192
|
+
ids: string[],
|
|
193
|
+
params: { query?: string | null; limit?: number | null },
|
|
194
|
+
): string[] {
|
|
195
|
+
const query = params.query?.trim().toLowerCase() || "";
|
|
196
|
+
const limit = typeof params.limit === "number" && params.limit > 0 ? params.limit : undefined;
|
|
197
|
+
const filtered = ids.filter((id) => (query ? id.toLowerCase().includes(query) : true));
|
|
198
|
+
return typeof limit === "number" ? filtered.slice(0, limit) : filtered;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function dedupeIds(ids: string[]): string[] {
|
|
202
|
+
return Array.from(new Set(ids));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function collectEntryIds(params: {
|
|
206
|
+
entries?: readonly unknown[];
|
|
207
|
+
normalizeId?: (entry: string) => string | null | undefined;
|
|
208
|
+
}): string[] {
|
|
209
|
+
return (params.entries ?? [])
|
|
210
|
+
.map((entry) => String(entry).trim())
|
|
211
|
+
.filter((entry) => Boolean(entry) && entry !== "*")
|
|
212
|
+
.map((entry) => {
|
|
213
|
+
const normalized = params.normalizeId ? params.normalizeId(entry) : entry;
|
|
214
|
+
return typeof normalized === "string" ? normalized.trim() : "";
|
|
215
|
+
})
|
|
216
|
+
.filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function collectMapIds(params: {
|
|
220
|
+
map?: Record<string, unknown>;
|
|
221
|
+
normalizeId?: (entry: string) => string | null | undefined;
|
|
222
|
+
}): string[] {
|
|
223
|
+
return collectEntryIds({
|
|
224
|
+
entries: Object.keys(params.map ?? {}),
|
|
225
|
+
normalizeId: params.normalizeId,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function listDirectoryUserEntriesFromAllowFromAndMapKeys(params: {
|
|
230
|
+
allowFrom?: readonly unknown[];
|
|
231
|
+
map?: Record<string, unknown>;
|
|
232
|
+
query?: string | null;
|
|
233
|
+
limit?: number | null;
|
|
234
|
+
normalizeAllowFromId?: (entry: string) => string | null | undefined;
|
|
235
|
+
normalizeMapKeyId?: (entry: string) => string | null | undefined;
|
|
236
|
+
}): Array<{ kind: "user"; id: string }> {
|
|
237
|
+
const ids = dedupeIds([
|
|
238
|
+
...collectEntryIds({
|
|
239
|
+
entries: params.allowFrom,
|
|
240
|
+
normalizeId: params.normalizeAllowFromId,
|
|
241
|
+
}),
|
|
242
|
+
...collectMapIds({
|
|
243
|
+
map: params.map,
|
|
244
|
+
normalizeId: params.normalizeMapKeyId,
|
|
245
|
+
}),
|
|
246
|
+
]);
|
|
247
|
+
return applyDirectoryQueryAndLimit(ids, params).map((id) => ({ kind: "user", id }));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function listDirectoryGroupEntriesFromMapKeysAndAllowFrom(params: {
|
|
251
|
+
groups?: Record<string, unknown>;
|
|
252
|
+
allowFrom?: readonly unknown[];
|
|
253
|
+
query?: string | null;
|
|
254
|
+
limit?: number | null;
|
|
255
|
+
normalizeMapKeyId?: (entry: string) => string | null | undefined;
|
|
256
|
+
normalizeAllowFromId?: (entry: string) => string | null | undefined;
|
|
257
|
+
}): Array<{ kind: "group"; id: string }> {
|
|
258
|
+
const ids = dedupeIds([
|
|
259
|
+
...collectMapIds({
|
|
260
|
+
map: params.groups,
|
|
261
|
+
normalizeId: params.normalizeMapKeyId,
|
|
262
|
+
}),
|
|
263
|
+
...collectEntryIds({
|
|
264
|
+
entries: params.allowFrom,
|
|
265
|
+
normalizeId: params.normalizeAllowFromId,
|
|
266
|
+
}),
|
|
267
|
+
]);
|
|
268
|
+
return applyDirectoryQueryAndLimit(ids, params).map((id) => ({ kind: "group", id }));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function collectAllowlistProviderRestrictSendersWarnings(params: {
|
|
272
|
+
cfg: OpenClawConfig;
|
|
273
|
+
providerConfigPresent: boolean;
|
|
274
|
+
configuredGroupPolicy?: GroupPolicy | null;
|
|
275
|
+
surface: string;
|
|
276
|
+
openScope: string;
|
|
277
|
+
groupPolicyPath: string;
|
|
278
|
+
groupAllowFromPath: string;
|
|
279
|
+
mentionGated?: boolean;
|
|
280
|
+
}): string[] {
|
|
281
|
+
const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg as {
|
|
282
|
+
channels?: { defaults?: { groupPolicy?: GroupPolicy } };
|
|
283
|
+
});
|
|
284
|
+
const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
|
|
285
|
+
providerConfigPresent: params.providerConfigPresent,
|
|
286
|
+
groupPolicy: params.configuredGroupPolicy ?? undefined,
|
|
287
|
+
defaultGroupPolicy,
|
|
288
|
+
});
|
|
289
|
+
if (groupPolicy !== "open") {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
const mentionSuffix = params.mentionGated === false ? "" : " (mention-gated)";
|
|
293
|
+
return [
|
|
294
|
+
`- ${params.surface}: groupPolicy="open" allows ${params.openScope} to trigger${mentionSuffix}. Set ${params.groupPolicyPath}="allowlist" + ${params.groupAllowFromPath} to restrict senders.`,
|
|
295
|
+
];
|
|
296
|
+
}
|