olvid-channel 0.0.0-a0

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/SKILL.md ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ name: olvid-channel
3
+ description: Add a native Olvid channel in OpenClaw.
4
+ ---
package/index.ts ADDED
@@ -0,0 +1,177 @@
1
+ import {
2
+ type OpenClawPluginApi,
3
+ buildChannelConfigSchema,
4
+ type ChannelPlugin,
5
+ setAccountEnabledInConfigSection, deleteAccountFromConfigSection, DEFAULT_ACCOUNT_ID, ChannelAccountSnapshot,
6
+ emptyPluginConfigSchema
7
+ } from "openclaw/plugin-sdk";
8
+ import {
9
+ listOlvidAccountIds,
10
+ resolveDefaultOlvidAccountId,
11
+ ResolvedOlvidAccount,
12
+ resolveOlvidAccount
13
+ } from "./src/accounts";
14
+ import {OlvidConfigSchema} from "./src/config-schema";
15
+ import {CoreConfig} from "./src/types";
16
+ import {getOlvidRuntime, setOlvidRuntime} from "./src/runtime";
17
+ import {sendMessageOlvid} from "./src/send";
18
+ import {monitorOlvidProvider} from "./src/monitor";
19
+ import {olvidOnboardingAdapter} from "./src/onboarding";
20
+
21
+ let olvidPlugin: ChannelPlugin<ResolvedOlvidAccount> = {
22
+ id: "olvid",
23
+ meta: {
24
+ id: "olvid",
25
+ label: "Olvid",
26
+ selectionLabel: "Olvid (Daemon)",
27
+ docsPath: "/channels/olvid",
28
+ docsLabel: "olvid",
29
+ blurb: "Securely exchange with your bot with authenticated e2e encryption",
30
+ order: 65,
31
+ quickstartAllowFrom: true,
32
+ },
33
+ capabilities: {
34
+ chatTypes: ["direct", "group"],
35
+ media: true,
36
+ reactions: true,
37
+ threads: false,
38
+ reply: true,
39
+ nativeCommands: false,
40
+ blockStreaming: true
41
+ },
42
+ reload: { configPrefixes: ["channels.olvid"] },
43
+ configSchema: buildChannelConfigSchema(OlvidConfigSchema),
44
+ onboarding: olvidOnboardingAdapter,
45
+ config: {
46
+ listAccountIds: (cfg) => listOlvidAccountIds(cfg as CoreConfig),
47
+ resolveAccount: (cfg, accountId) => resolveOlvidAccount({ cfg: cfg as CoreConfig, accountId }),
48
+ defaultAccountId: (cfg) => resolveDefaultOlvidAccountId(cfg as CoreConfig),
49
+ setAccountEnabled: ({ cfg, accountId, enabled }) =>
50
+ setAccountEnabledInConfigSection({
51
+ cfg,
52
+ sectionKey: "olvid",
53
+ accountId,
54
+ enabled,
55
+ allowTopLevel: true,
56
+ }),
57
+ deleteAccount: ({ cfg, accountId }) =>
58
+ deleteAccountFromConfigSection({
59
+ cfg,
60
+ sectionKey: "olvid",
61
+ accountId,
62
+ clearBaseFields: ["clientKey", "daemonUrl", "name"],
63
+ }),
64
+ isConfigured: (account) => Boolean(account.clientKey && account.daemonUrl),
65
+ describeAccount: (account) => ({
66
+ accountId: account.accountId,
67
+ name: account.name,
68
+ enabled: account.enabled,
69
+ configured: Boolean(account.clientKey && account.daemonUrl),
70
+ clientKeySource: account.clientKeySource,
71
+ daemonUrl: account.daemonUrl,
72
+ }),
73
+ },
74
+ outbound: {
75
+ deliveryMode: "direct",
76
+ chunker: (text: string, limit: number): string[] => {
77
+ return getOlvidRuntime().channel.text.chunkMarkdownText(text, limit);
78
+ },
79
+ chunkerMode: "markdown",
80
+ textChunkLimit: 4000,
81
+ sendText: async ({ to, text, accountId, replyToId }) => {
82
+ const result = await sendMessageOlvid(to, text, {
83
+ accountId: accountId ?? undefined,
84
+ replyTo: replyToId ?? undefined,
85
+ });
86
+ return { channel: "olvid", ...result };
87
+ },
88
+ sendMedia: async ({ to, text, mediaUrl, accountId, replyToId }) => {
89
+ const result = await sendMessageOlvid(to, text, {
90
+ accountId: accountId ?? undefined,
91
+ replyTo: replyToId ?? undefined,
92
+ mediaUrls: mediaUrl ? [mediaUrl] : undefined,
93
+ });
94
+ return { channel: "olvid", ...result };
95
+ },
96
+ },
97
+ status: {
98
+ defaultRuntime: {
99
+ accountId: DEFAULT_ACCOUNT_ID,
100
+ running: false,
101
+ connected: false,
102
+ lastConnectedAt: null,
103
+ lastDisconnect: null,
104
+ lastStartAt: null,
105
+ lastStopAt: null,
106
+ lastError: null,
107
+ },
108
+ buildChannelSummary: ({ snapshot }) => ({
109
+ configured: snapshot.configured ?? false,
110
+ running: snapshot.running ?? false,
111
+ connected: snapshot.connected ?? false,
112
+ lastStartAt: snapshot.lastStartAt ?? null,
113
+ lastStopAt: snapshot.lastStopAt ?? null,
114
+ lastError: snapshot.lastError ?? null,
115
+ baseUrl: snapshot.baseUrl ?? null,
116
+ probe: snapshot.probe,
117
+ lastProbeAt: snapshot.lastProbeAt ?? null,
118
+ }),
119
+ buildAccountSnapshot: ({ account, runtime }) => {
120
+ return {
121
+ accountId: account.accountId,
122
+ name: account.name,
123
+ enabled: account.enabled,
124
+ configured: Boolean(account.clientKey && account.daemonUrl),
125
+ clientKeySource: account.clientKeySource,
126
+ baseUrl: account.daemonUrl,
127
+ connected: runtime?.connected ?? false,
128
+ lastConnectedAt: runtime?.lastConnectedAt ?? null,
129
+ lastDisconnect: runtime?.lastDisconnect ?? null,
130
+ lastStartAt: runtime?.lastStartAt ?? null,
131
+ lastStopAt: runtime?.lastStopAt ?? null,
132
+ lastError: runtime?.lastError ?? null,
133
+ lastInboundAt: runtime?.lastInboundAt ?? null,
134
+ lastOutboundAt: runtime?.lastOutboundAt ?? null,
135
+ };
136
+ },
137
+ },
138
+ gateway: {
139
+ startAccount: async (ctx) => {
140
+ const account = ctx.account;
141
+ if (!account.daemonUrl || !account.clientKey) {
142
+ throw new Error(`Olvid not configuration is invalid`);
143
+ }
144
+ ctx.setStatus({
145
+ accountId: account.accountId,
146
+ baseUrl: account.daemonUrl,
147
+ botTokenSource: account.clientKeySource,
148
+ lastStartAt: Date.now(),
149
+ });
150
+ ctx.log?.info(`[${account.accountId}] starting channel`);
151
+ return monitorOlvidProvider({
152
+ accountId: account.accountId,
153
+ config: ctx.cfg as CoreConfig,
154
+ runtime: ctx.runtime,
155
+ abortSignal: ctx.abortSignal,
156
+ statusSink: (patch: Partial<ChannelAccountSnapshot>) =>
157
+ ctx.setStatus({ accountId: ctx.accountId, ...patch }),
158
+ });
159
+ },
160
+ stopAccount: async (ctx) => {
161
+ ctx.setStatus({ accountId: ctx.accountId, lastStopAt: Date.now() });
162
+ },
163
+ }
164
+ }
165
+
166
+ const plugin: {id: string, name: string, description: string, configSchema: unknown, register: (api: OpenClawPluginApi) => void} = {
167
+ id: "olvid",
168
+ name: "Olvid",
169
+ description: "Olvid channel plugin",
170
+ configSchema: emptyPluginConfigSchema(),
171
+ register(api: OpenClawPluginApi) {
172
+ setOlvidRuntime(api.runtime);
173
+ api.registerChannel(olvidPlugin);
174
+ }
175
+ }
176
+
177
+ export default plugin;
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "olvid-channel",
3
+ "channels": ["olvid"],
4
+ "name": "Olvid Channel",
5
+ "openclaw": {
6
+ "extensions": ["./index.ts"],
7
+ "channel": {
8
+ "id": "olvid",
9
+ "label": "olvid",
10
+ "selectionLabel": "Olvid (Daemon)",
11
+ "docsPath": "/channels/olvid",
12
+ "docsLabel": "olvid",
13
+ "blurb": "Trust your agent with authenticated e2e encryption.",
14
+ "order": 35
15
+ }
16
+ },
17
+ "configSchema": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "properties": {
21
+ "daemonUrl": { "type": "string", "default": "http://localhost:50051" },
22
+ "clientKey": { "type": "string" }
23
+ }
24
+ },
25
+ "uiHints": {
26
+ "daemonUrl": { "label": "Daemon Url", "placeholder": "http://localhost:50051" },
27
+ "clientKey": { "label": "Client Key", "sensitive": true }
28
+ }
29
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "olvid-channel",
3
+ "version": "0.0.0a0",
4
+ "description": "Olvid native channel for OpenClaw.",
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@olvid/bot-node": "^1.5.0",
8
+ "zod": "^4.3.6"
9
+ },
10
+ "peerDependencies": {
11
+ "openclaw": "^2026.2.1"
12
+ },
13
+ "keywords": [
14
+ "openclaw",
15
+ "channel",
16
+ "olvid"
17
+ ],
18
+ "author": "bot@olvid.io",
19
+ "openclaw": {
20
+ "extensions": ["./index.ts"],
21
+ "channel": {
22
+ "id": "olvid",
23
+ "label": "olvid",
24
+ "selectionLabel": "Olvid (Daemon)",
25
+ "docsPath": "/channels/olvid",
26
+ "docsLabel": "olvid",
27
+ "blurb": "Trust your agent with authenticated e2e encryption.",
28
+ "order": 35
29
+ },
30
+ "install": {
31
+ "npmSpec": "olvid-channel",
32
+ "localPath": "extensions/olvid",
33
+ "defaultChoice": "npm"
34
+ }
35
+ }
36
+ }
package/pub/SKILL.md ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ name: olvid-channel
3
+ description: Add a native Olvid channel in OpenClaw.
4
+ ---
package/pub/index.ts ADDED
@@ -0,0 +1,177 @@
1
+ import {
2
+ type OpenClawPluginApi,
3
+ buildChannelConfigSchema,
4
+ type ChannelPlugin,
5
+ setAccountEnabledInConfigSection, deleteAccountFromConfigSection, DEFAULT_ACCOUNT_ID, ChannelAccountSnapshot,
6
+ emptyPluginConfigSchema
7
+ } from "openclaw/plugin-sdk";
8
+ import {
9
+ listOlvidAccountIds,
10
+ resolveDefaultOlvidAccountId,
11
+ ResolvedOlvidAccount,
12
+ resolveOlvidAccount
13
+ } from "./src/accounts";
14
+ import {OlvidConfigSchema} from "./src/config-schema";
15
+ import {CoreConfig} from "./src/types";
16
+ import {getOlvidRuntime, setOlvidRuntime} from "./src/runtime";
17
+ import {sendMessageOlvid} from "./src/send";
18
+ import {monitorOlvidProvider} from "./src/monitor";
19
+ import {olvidOnboardingAdapter} from "./src/onboarding";
20
+
21
+ let olvidPlugin: ChannelPlugin<ResolvedOlvidAccount> = {
22
+ id: "olvid",
23
+ meta: {
24
+ id: "olvid",
25
+ label: "Olvid",
26
+ selectionLabel: "Olvid (Daemon)",
27
+ docsPath: "/channels/olvid",
28
+ docsLabel: "olvid",
29
+ blurb: "Securely exchange with your bot with authenticated e2e encryption",
30
+ order: 65,
31
+ quickstartAllowFrom: true,
32
+ },
33
+ capabilities: {
34
+ chatTypes: ["direct", "group"],
35
+ media: true,
36
+ reactions: true,
37
+ threads: false,
38
+ reply: true,
39
+ nativeCommands: false,
40
+ blockStreaming: true
41
+ },
42
+ reload: { configPrefixes: ["channels.olvid"] },
43
+ configSchema: buildChannelConfigSchema(OlvidConfigSchema),
44
+ onboarding: olvidOnboardingAdapter,
45
+ config: {
46
+ listAccountIds: (cfg) => listOlvidAccountIds(cfg as CoreConfig),
47
+ resolveAccount: (cfg, accountId) => resolveOlvidAccount({ cfg: cfg as CoreConfig, accountId }),
48
+ defaultAccountId: (cfg) => resolveDefaultOlvidAccountId(cfg as CoreConfig),
49
+ setAccountEnabled: ({ cfg, accountId, enabled }) =>
50
+ setAccountEnabledInConfigSection({
51
+ cfg,
52
+ sectionKey: "olvid",
53
+ accountId,
54
+ enabled,
55
+ allowTopLevel: true,
56
+ }),
57
+ deleteAccount: ({ cfg, accountId }) =>
58
+ deleteAccountFromConfigSection({
59
+ cfg,
60
+ sectionKey: "olvid",
61
+ accountId,
62
+ clearBaseFields: ["clientKey", "daemonUrl", "name"],
63
+ }),
64
+ isConfigured: (account) => Boolean(account.clientKey && account.daemonUrl),
65
+ describeAccount: (account) => ({
66
+ accountId: account.accountId,
67
+ name: account.name,
68
+ enabled: account.enabled,
69
+ configured: Boolean(account.clientKey && account.daemonUrl),
70
+ clientKeySource: account.clientKeySource,
71
+ daemonUrl: account.daemonUrl,
72
+ }),
73
+ },
74
+ outbound: {
75
+ deliveryMode: "direct",
76
+ chunker: (text: string, limit: number): string[] => {
77
+ return getOlvidRuntime().channel.text.chunkMarkdownText(text, limit);
78
+ },
79
+ chunkerMode: "markdown",
80
+ textChunkLimit: 4000,
81
+ sendText: async ({ to, text, accountId, replyToId }) => {
82
+ const result = await sendMessageOlvid(to, text, {
83
+ accountId: accountId ?? undefined,
84
+ replyTo: replyToId ?? undefined,
85
+ });
86
+ return { channel: "olvid", ...result };
87
+ },
88
+ sendMedia: async ({ to, text, mediaUrl, accountId, replyToId }) => {
89
+ const result = await sendMessageOlvid(to, text, {
90
+ accountId: accountId ?? undefined,
91
+ replyTo: replyToId ?? undefined,
92
+ mediaUrls: mediaUrl ? [mediaUrl] : undefined,
93
+ });
94
+ return { channel: "olvid", ...result };
95
+ },
96
+ },
97
+ status: {
98
+ defaultRuntime: {
99
+ accountId: DEFAULT_ACCOUNT_ID,
100
+ running: false,
101
+ connected: false,
102
+ lastConnectedAt: null,
103
+ lastDisconnect: null,
104
+ lastStartAt: null,
105
+ lastStopAt: null,
106
+ lastError: null,
107
+ },
108
+ buildChannelSummary: ({ snapshot }) => ({
109
+ configured: snapshot.configured ?? false,
110
+ running: snapshot.running ?? false,
111
+ connected: snapshot.connected ?? false,
112
+ lastStartAt: snapshot.lastStartAt ?? null,
113
+ lastStopAt: snapshot.lastStopAt ?? null,
114
+ lastError: snapshot.lastError ?? null,
115
+ baseUrl: snapshot.baseUrl ?? null,
116
+ probe: snapshot.probe,
117
+ lastProbeAt: snapshot.lastProbeAt ?? null,
118
+ }),
119
+ buildAccountSnapshot: ({ account, runtime }) => {
120
+ return {
121
+ accountId: account.accountId,
122
+ name: account.name,
123
+ enabled: account.enabled,
124
+ configured: Boolean(account.clientKey && account.daemonUrl),
125
+ clientKeySource: account.clientKeySource,
126
+ baseUrl: account.daemonUrl,
127
+ connected: runtime?.connected ?? false,
128
+ lastConnectedAt: runtime?.lastConnectedAt ?? null,
129
+ lastDisconnect: runtime?.lastDisconnect ?? null,
130
+ lastStartAt: runtime?.lastStartAt ?? null,
131
+ lastStopAt: runtime?.lastStopAt ?? null,
132
+ lastError: runtime?.lastError ?? null,
133
+ lastInboundAt: runtime?.lastInboundAt ?? null,
134
+ lastOutboundAt: runtime?.lastOutboundAt ?? null,
135
+ };
136
+ },
137
+ },
138
+ gateway: {
139
+ startAccount: async (ctx) => {
140
+ const account = ctx.account;
141
+ if (!account.daemonUrl || !account.clientKey) {
142
+ throw new Error(`Olvid not configuration is invalid`);
143
+ }
144
+ ctx.setStatus({
145
+ accountId: account.accountId,
146
+ baseUrl: account.daemonUrl,
147
+ botTokenSource: account.clientKeySource,
148
+ lastStartAt: Date.now(),
149
+ });
150
+ ctx.log?.info(`[${account.accountId}] starting channel`);
151
+ return monitorOlvidProvider({
152
+ accountId: account.accountId,
153
+ config: ctx.cfg as CoreConfig,
154
+ runtime: ctx.runtime,
155
+ abortSignal: ctx.abortSignal,
156
+ statusSink: (patch: Partial<ChannelAccountSnapshot>) =>
157
+ ctx.setStatus({ accountId: ctx.accountId, ...patch }),
158
+ });
159
+ },
160
+ stopAccount: async (ctx) => {
161
+ ctx.setStatus({ accountId: ctx.accountId, lastStopAt: Date.now() });
162
+ },
163
+ }
164
+ }
165
+
166
+ const plugin: {id: string, name: string, description: string, configSchema: unknown, register: (api: OpenClawPluginApi) => void} = {
167
+ id: "olvid",
168
+ name: "Olvid",
169
+ description: "Olvid channel plugin",
170
+ configSchema: emptyPluginConfigSchema(),
171
+ register(api: OpenClawPluginApi) {
172
+ setOlvidRuntime(api.runtime);
173
+ api.registerChannel(olvidPlugin);
174
+ }
175
+ }
176
+
177
+ export default plugin;
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "olvid",
3
+ "channels": ["olvid"],
4
+ "name": "Olvid Channel",
5
+ "openclaw": {
6
+ "extensions": ["./index.ts"],
7
+ "channel": {
8
+ "id": "olvid",
9
+ "label": "olvid",
10
+ "selectionLabel": "Olvid (Daemon)",
11
+ "docsPath": "/channels/olvid",
12
+ "docsLabel": "olvid",
13
+ "blurb": "Trust your agent with authenticated e2e encryption.",
14
+ "order": 35
15
+ }
16
+ },
17
+ "configSchema": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "properties": {
21
+ "daemonUrl": { "type": "string", "default": "http://localhost:50051" },
22
+ "clientKey": { "type": "string" }
23
+ }
24
+ },
25
+ "uiHints": {
26
+ "daemonUrl": { "label": "Daemon Url", "placeholder": "http://localhost:50051" },
27
+ "clientKey": { "label": "Client Key", "sensitive": true }
28
+ }
29
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "openclaw-channel-olvid",
3
+ "version": "0.0.6",
4
+ "description": "Olvid native channel for OpenClaw.",
5
+ "type": "module",
6
+ "dependencies": {
7
+ "@olvid/bot-node": "^1.5.0",
8
+ "zod": "^4.3.6"
9
+ },
10
+ "devDependencies": {
11
+ "openclaw": "==2026.2.1"
12
+ },
13
+ "keywords": [
14
+ "openclaw",
15
+ "channel",
16
+ "olvid"
17
+ ],
18
+ "author": "bot@olvid.io",
19
+ "openclaw": {
20
+ "extensions": ["./index.ts"],
21
+ "channel": {
22
+ "id": "olvid",
23
+ "label": "olvid",
24
+ "selectionLabel": "Olvid (Daemon)",
25
+ "docsPath": "/channels/olvid",
26
+ "docsLabel": "olvid",
27
+ "blurb": "Trust your agent with authenticated e2e encryption.",
28
+ "order": 35
29
+ },
30
+ "install": {
31
+ "npmSpec": "openclaw-channel-olvid",
32
+ "localPath": "extensions/olvid",
33
+ "defaultChoice": "npm"
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,111 @@
1
+ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
2
+ import type { CoreConfig, OlvidAccountConfig } from "./types.js";
3
+
4
+ export type OlvidClientKeySource = "env" | "config" | "none";
5
+ export type OlvidDaemonUrlSource = "env" | "config" | "none";
6
+
7
+ export type ResolvedOlvidAccount = {
8
+ accountId: string;
9
+ enabled: boolean;
10
+ name?: string;
11
+ clientKey?: string;
12
+ daemonUrl?: string;
13
+ clientKeySource: OlvidClientKeySource;
14
+ daemonUrlSource: OlvidDaemonUrlSource;
15
+ config: OlvidAccountConfig;
16
+ oncharPrefixes?: string[];
17
+ requireMention?: boolean;
18
+ textChunkLimit?: number;
19
+ blockStreaming?: boolean;
20
+ blockStreamingCoalesce?: OlvidAccountConfig["blockStreamingCoalesce"];
21
+ };
22
+
23
+ function listConfiguredAccountIds(cfg: CoreConfig): string[] {
24
+ const accounts = cfg.channels?.olvid?.accounts;
25
+ if (!accounts || typeof accounts !== "object") {
26
+ return [];
27
+ }
28
+ return Object.keys(accounts).filter(Boolean);
29
+ }
30
+
31
+ export function listOlvidAccountIds(cfg: CoreConfig): string[] {
32
+ const ids = listConfiguredAccountIds(cfg);
33
+ if (ids.length === 0) {
34
+ return [DEFAULT_ACCOUNT_ID];
35
+ }
36
+ return ids.sort((a, b) => a.localeCompare(b));
37
+ }
38
+
39
+ export function resolveDefaultOlvidAccountId(cfg: CoreConfig): string {
40
+ const ids = listOlvidAccountIds(cfg);
41
+ if (ids.includes(DEFAULT_ACCOUNT_ID)) {
42
+ return DEFAULT_ACCOUNT_ID;
43
+ }
44
+ return ids[0] ?? DEFAULT_ACCOUNT_ID;
45
+ }
46
+
47
+ function resolveAccountConfig(cfg: CoreConfig, accountId: string): OlvidAccountConfig | undefined {
48
+ const accounts = cfg.channels?.olvid?.accounts;
49
+ if (!accounts || typeof accounts !== "object") {
50
+ return undefined;
51
+ }
52
+ return accounts[accountId] as ResolvedOlvidAccount | undefined;
53
+ }
54
+
55
+ function mergeOlvidAccountConfig(cfg: CoreConfig, accountId: string): OlvidAccountConfig {
56
+ const { accounts: _ignored, ...base } = (cfg.channels?.olvid ?? {}) as OlvidAccountConfig & {
57
+ accounts?: unknown;
58
+ };
59
+ const account = resolveAccountConfig(cfg, accountId) ?? {};
60
+ return { ...base, ...account };
61
+ }
62
+
63
+ export function resolveOlvidAccount(params: {
64
+ cfg: CoreConfig;
65
+ accountId?: string | null;
66
+ }): ResolvedOlvidAccount {
67
+ const accountId = normalizeAccountId(params.accountId);
68
+ const baseEnabled = params.cfg.channels?.olvid?.enabled !== false;
69
+ const merged = mergeOlvidAccountConfig(params.cfg, accountId);
70
+ const accountEnabled = merged.enabled !== false;
71
+ const enabled = baseEnabled && accountEnabled;
72
+
73
+ const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
74
+ const envClientKey = allowEnv ? process.env.OLVID_CLIENT_KEY?.trim() : undefined;
75
+ const envDaemonUrl = allowEnv ? process.env.OLVID_DAEMON_TARGET?.trim() : undefined;
76
+ const configClientKey = merged.clientKey?.trim();
77
+ const configDaemonUrl = merged.daemonUrl?.trim();
78
+ const clientKey = configClientKey || envClientKey;
79
+ const daemonUrl = configDaemonUrl || envDaemonUrl;
80
+
81
+ const clientKeySource: OlvidClientKeySource = configClientKey
82
+ ? "config"
83
+ : envClientKey
84
+ ? "env"
85
+ : "none";
86
+ const daemonUrlSource: OlvidDaemonUrlSource = configDaemonUrl
87
+ ? "config"
88
+ : envDaemonUrl
89
+ ? "env"
90
+ : "none";
91
+
92
+ return {
93
+ accountId,
94
+ enabled,
95
+ name: merged.name?.trim() || undefined,
96
+ clientKey,
97
+ daemonUrl,
98
+ clientKeySource: clientKeySource,
99
+ daemonUrlSource: daemonUrlSource,
100
+ config: merged,
101
+ textChunkLimit: merged.textChunkLimit,
102
+ blockStreaming: merged.blockStreaming,
103
+ blockStreamingCoalesce: merged.blockStreamingCoalesce,
104
+ };
105
+ }
106
+
107
+ export function listEnabledOlvidAccounts(cfg: CoreConfig): ResolvedOlvidAccount[] {
108
+ return listOlvidAccountIds(cfg)
109
+ .map((accountId) => resolveOlvidAccount({ cfg, accountId }))
110
+ .filter((account) => account.enabled);
111
+ }
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+
3
+ export const OlvidAccountSchema = z
4
+ .object({
5
+ name: z.string().optional(),
6
+ enabled: z.boolean().optional(),
7
+ daemonUrl: z.string().optional(),
8
+ clientKey: z.string().optional(),
9
+ })
10
+ .strict();
11
+
12
+ export const OlvidConfigSchema = OlvidAccountSchema.extend({
13
+ accounts: z.record(z.string(), OlvidAccountSchema.optional()).optional(),
14
+ });