@valuya/telegram-channel-access 0.2.0-beta.1
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/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/index.d.ts +325 -0
- package/dist/index.js +538 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Valuya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @valuya/telegram-channel-access
|
|
2
|
+
|
|
3
|
+
Reusable Telegram channel access checks for Valuya Guard.
|
|
4
|
+
|
|
5
|
+
This package mirrors the newer channel-access contract used by `@valuya/whatsapp-channel-access`:
|
|
6
|
+
|
|
7
|
+
- prefer `POST /api/v2/channel-access/resolve`
|
|
8
|
+
- use `GET /api/v2/entitlements` only as conservative fallback
|
|
9
|
+
- keep runtime decisions backend-owned
|
|
10
|
+
- keep package behavior channel-generic and transport-focused
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @valuya/telegram-channel-access
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { TelegramChannelAccessService } from "@valuya/telegram-channel-access"
|
|
22
|
+
|
|
23
|
+
const access = new TelegramChannelAccessService({
|
|
24
|
+
baseUrl: process.env.VALUYA_BASE_URL!,
|
|
25
|
+
tenantToken: process.env.VALUYA_TENANT_TOKEN!,
|
|
26
|
+
channelResource: "telegram:channel:guarddemobot:premium_alpha",
|
|
27
|
+
channelPlan: "standard",
|
|
28
|
+
channelInviteUrl: "https://t.me/+premiumInvite",
|
|
29
|
+
linking: myTelegramLinking,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const result = await access.resolveAccess({
|
|
33
|
+
telegramUserId: "123",
|
|
34
|
+
telegramUsername: "ada",
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- `X-Valuya-Subject-Id` remains the canonical subject input
|
|
41
|
+
- request body channel fields are metadata only
|
|
42
|
+
- fallback to entitlements is only for narrow endpoint-unavailable cases
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
export type LogFn = (event: string, fields: Record<string, unknown>) => void;
|
|
2
|
+
export type ChannelAccessState = "paid_active" | "trial_active" | "expired_payment_required" | "inactive";
|
|
3
|
+
export type ChannelRuntimeConfig = {
|
|
4
|
+
mode: "human" | "agent";
|
|
5
|
+
channel: string;
|
|
6
|
+
channel_kind: string;
|
|
7
|
+
provider: string | null;
|
|
8
|
+
channel_app_id?: string | null;
|
|
9
|
+
visit_url: string | null;
|
|
10
|
+
human_routing?: Record<string, unknown> | null;
|
|
11
|
+
agent_routing?: Record<string, unknown> | null;
|
|
12
|
+
fallback?: {
|
|
13
|
+
allowed: boolean;
|
|
14
|
+
mode: "human";
|
|
15
|
+
} | null;
|
|
16
|
+
soul: {
|
|
17
|
+
id: number | string;
|
|
18
|
+
slug: string;
|
|
19
|
+
name: string;
|
|
20
|
+
version: number | string;
|
|
21
|
+
} | null;
|
|
22
|
+
};
|
|
23
|
+
export type SoulDefinition = {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
systemPrompt: string;
|
|
27
|
+
locale?: string;
|
|
28
|
+
memoryPolicy?: {
|
|
29
|
+
keepRecentTurns: number;
|
|
30
|
+
summarizeAfterTurns: number;
|
|
31
|
+
};
|
|
32
|
+
tools?: string[];
|
|
33
|
+
};
|
|
34
|
+
export type SoulMemoryTurn = {
|
|
35
|
+
role: "user" | "assistant";
|
|
36
|
+
content: string;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
};
|
|
39
|
+
export type SoulMemory = {
|
|
40
|
+
recentTurns: SoulMemoryTurn[];
|
|
41
|
+
summaries: string[];
|
|
42
|
+
userProfile?: Record<string, unknown>;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
};
|
|
45
|
+
export type SoulResponse = {
|
|
46
|
+
reply: string;
|
|
47
|
+
memory?: SoulMemory;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
export type ChannelAccessResolveRequest = {
|
|
51
|
+
resource: string;
|
|
52
|
+
plan: string;
|
|
53
|
+
channel?: {
|
|
54
|
+
kind?: string | null;
|
|
55
|
+
provider?: string | null;
|
|
56
|
+
channel_identifier?: string | null;
|
|
57
|
+
phone_number?: string | null;
|
|
58
|
+
bot_name?: string | null;
|
|
59
|
+
chat_id?: string | null;
|
|
60
|
+
} | null;
|
|
61
|
+
};
|
|
62
|
+
export type ChannelAccessResolveResponse = {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
state: ChannelAccessState;
|
|
65
|
+
resource: string;
|
|
66
|
+
anchor_resource: string;
|
|
67
|
+
plan: string;
|
|
68
|
+
expires_at: string | null;
|
|
69
|
+
payment_url: string | null;
|
|
70
|
+
reason: string | null;
|
|
71
|
+
runtime_config: ChannelRuntimeConfig | null;
|
|
72
|
+
capabilities: {
|
|
73
|
+
channel_access_version: string;
|
|
74
|
+
[key: string]: unknown;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
export type LegacyEntitlementResponse = {
|
|
78
|
+
active?: boolean;
|
|
79
|
+
reason?: string;
|
|
80
|
+
evaluated_plan?: string;
|
|
81
|
+
expires_at?: string | null;
|
|
82
|
+
payment_url?: string | null;
|
|
83
|
+
state?: string;
|
|
84
|
+
required?: Record<string, unknown> | null;
|
|
85
|
+
};
|
|
86
|
+
export type TelegramLinkResolver = {
|
|
87
|
+
ensureLinkedForPaymentAction(args: {
|
|
88
|
+
telegramUserId: string;
|
|
89
|
+
telegramUsername?: string;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
allowed: true;
|
|
92
|
+
link: {
|
|
93
|
+
valuya_protocol_subject_header?: string;
|
|
94
|
+
};
|
|
95
|
+
} | {
|
|
96
|
+
allowed: false;
|
|
97
|
+
code: string;
|
|
98
|
+
reply: string;
|
|
99
|
+
}>;
|
|
100
|
+
};
|
|
101
|
+
export type TelegramChannelAccessConfig = {
|
|
102
|
+
baseUrl: string;
|
|
103
|
+
tenantToken: string;
|
|
104
|
+
linking: TelegramLinkResolver;
|
|
105
|
+
channelResource?: string;
|
|
106
|
+
channelBot?: string;
|
|
107
|
+
channelName?: string;
|
|
108
|
+
channelPlan?: string;
|
|
109
|
+
channelInviteUrl?: string;
|
|
110
|
+
logger?: LogFn;
|
|
111
|
+
allowEntitlementFallbackOnServerError?: boolean;
|
|
112
|
+
};
|
|
113
|
+
export type ChannelMode = {
|
|
114
|
+
kind: "human";
|
|
115
|
+
} | {
|
|
116
|
+
kind: "agent";
|
|
117
|
+
soulId: string;
|
|
118
|
+
};
|
|
119
|
+
export type TelegramChannelRuntimeConfig = TelegramChannelAccessConfig & {
|
|
120
|
+
mode?: ChannelMode;
|
|
121
|
+
souls?: SoulDefinition[];
|
|
122
|
+
};
|
|
123
|
+
export type TelegramChannelAccessResult = {
|
|
124
|
+
allowed: true;
|
|
125
|
+
state: "paid_active" | "trial_active";
|
|
126
|
+
protocolSubjectHeader: string;
|
|
127
|
+
resource: string;
|
|
128
|
+
anchorResource: string;
|
|
129
|
+
plan: string;
|
|
130
|
+
joinUrl: string | null;
|
|
131
|
+
expiresAt?: string;
|
|
132
|
+
paymentUrl?: string | null;
|
|
133
|
+
runtimeConfig: ChannelRuntimeConfig | null;
|
|
134
|
+
capabilities: {
|
|
135
|
+
channel_access_version: string;
|
|
136
|
+
[key: string]: unknown;
|
|
137
|
+
} | null;
|
|
138
|
+
source: "channel_access_resolve" | "entitlements_fallback";
|
|
139
|
+
} | {
|
|
140
|
+
allowed: false;
|
|
141
|
+
state: "not_linked" | "expired_payment_required" | "inactive" | "guard_unavailable";
|
|
142
|
+
protocolSubjectHeader: string | null;
|
|
143
|
+
resource: string;
|
|
144
|
+
anchorResource: string;
|
|
145
|
+
plan: string;
|
|
146
|
+
reply: string;
|
|
147
|
+
expiresAt?: string;
|
|
148
|
+
paymentUrl?: string | null;
|
|
149
|
+
runtimeConfig: ChannelRuntimeConfig | null;
|
|
150
|
+
capabilities: {
|
|
151
|
+
channel_access_version: string;
|
|
152
|
+
[key: string]: unknown;
|
|
153
|
+
} | null;
|
|
154
|
+
source: "channel_access_resolve" | "entitlements_fallback" | "linking";
|
|
155
|
+
};
|
|
156
|
+
export type TelegramChannelRuntimeResult = {
|
|
157
|
+
kind: "blocked";
|
|
158
|
+
access: TelegramChannelAccessResult & {
|
|
159
|
+
allowed: false;
|
|
160
|
+
};
|
|
161
|
+
reply: string;
|
|
162
|
+
} | {
|
|
163
|
+
kind: "human";
|
|
164
|
+
access: TelegramChannelAccessResult & {
|
|
165
|
+
allowed: true;
|
|
166
|
+
};
|
|
167
|
+
protocolSubjectHeader: string;
|
|
168
|
+
reply: string;
|
|
169
|
+
} | {
|
|
170
|
+
kind: "allowed";
|
|
171
|
+
access: TelegramChannelAccessResult & {
|
|
172
|
+
allowed: true;
|
|
173
|
+
};
|
|
174
|
+
protocolSubjectHeader: string;
|
|
175
|
+
reply: string;
|
|
176
|
+
} | {
|
|
177
|
+
kind: "agent";
|
|
178
|
+
access: TelegramChannelAccessResult & {
|
|
179
|
+
allowed: true;
|
|
180
|
+
};
|
|
181
|
+
protocolSubjectHeader: string;
|
|
182
|
+
soulId: string;
|
|
183
|
+
reply: string;
|
|
184
|
+
metadata?: Record<string, unknown>;
|
|
185
|
+
} | {
|
|
186
|
+
kind: "runtime_error";
|
|
187
|
+
access: TelegramChannelAccessResult & {
|
|
188
|
+
allowed: true;
|
|
189
|
+
};
|
|
190
|
+
protocolSubjectHeader: string;
|
|
191
|
+
error: "runtime_missing" | "agent_misconfigured";
|
|
192
|
+
reply: string;
|
|
193
|
+
};
|
|
194
|
+
export interface MemoryStore {
|
|
195
|
+
load(args: {
|
|
196
|
+
telegramUserId: string;
|
|
197
|
+
soulId: string;
|
|
198
|
+
}): Promise<SoulMemory>;
|
|
199
|
+
save(args: {
|
|
200
|
+
telegramUserId: string;
|
|
201
|
+
soulId: string;
|
|
202
|
+
memory: SoulMemory;
|
|
203
|
+
}): Promise<void>;
|
|
204
|
+
}
|
|
205
|
+
export interface SoulRuntime {
|
|
206
|
+
run(args: {
|
|
207
|
+
soul: SoulDefinition;
|
|
208
|
+
message: string;
|
|
209
|
+
memory: SoulMemory;
|
|
210
|
+
protocolSubjectHeader: string;
|
|
211
|
+
locale?: string;
|
|
212
|
+
}): Promise<SoulResponse>;
|
|
213
|
+
}
|
|
214
|
+
export interface GuardToolClient {
|
|
215
|
+
getChannelAccessState(args: {
|
|
216
|
+
protocolSubjectHeader: string;
|
|
217
|
+
resource: string;
|
|
218
|
+
plan: string;
|
|
219
|
+
}): Promise<Record<string, unknown>>;
|
|
220
|
+
getEntitlements(args: {
|
|
221
|
+
protocolSubjectHeader: string;
|
|
222
|
+
resource: string;
|
|
223
|
+
plan: string;
|
|
224
|
+
}): Promise<Record<string, unknown>>;
|
|
225
|
+
getRecentOrders?(args: {
|
|
226
|
+
protocolSubjectHeader: string;
|
|
227
|
+
}): Promise<Record<string, unknown>>;
|
|
228
|
+
getRecentPayments?(args: {
|
|
229
|
+
protocolSubjectHeader: string;
|
|
230
|
+
}): Promise<Record<string, unknown>>;
|
|
231
|
+
}
|
|
232
|
+
export declare class TelegramChannelAccessService {
|
|
233
|
+
private readonly baseUrl;
|
|
234
|
+
private readonly tenantToken;
|
|
235
|
+
private readonly linking;
|
|
236
|
+
private readonly resource;
|
|
237
|
+
private readonly plan;
|
|
238
|
+
private readonly inviteUrl;
|
|
239
|
+
private readonly log;
|
|
240
|
+
private readonly channelMetadata;
|
|
241
|
+
private readonly allowEntitlementFallbackOnServerError;
|
|
242
|
+
constructor(config: TelegramChannelAccessConfig);
|
|
243
|
+
resolveAccess(args: {
|
|
244
|
+
telegramUserId: string;
|
|
245
|
+
telegramUsername?: string;
|
|
246
|
+
}): Promise<TelegramChannelAccessResult>;
|
|
247
|
+
private resolveFromGuard;
|
|
248
|
+
private resolveFromEntitlements;
|
|
249
|
+
}
|
|
250
|
+
export declare class InMemoryMemoryStore implements MemoryStore {
|
|
251
|
+
private readonly store;
|
|
252
|
+
load(args: {
|
|
253
|
+
telegramUserId: string;
|
|
254
|
+
soulId: string;
|
|
255
|
+
}): Promise<SoulMemory>;
|
|
256
|
+
save(args: {
|
|
257
|
+
telegramUserId: string;
|
|
258
|
+
soulId: string;
|
|
259
|
+
memory: SoulMemory;
|
|
260
|
+
}): Promise<void>;
|
|
261
|
+
}
|
|
262
|
+
export declare class OpenAISoulRuntimeAdapter implements SoulRuntime {
|
|
263
|
+
private readonly args;
|
|
264
|
+
constructor(args: {
|
|
265
|
+
runCompletion: (args: {
|
|
266
|
+
system: string;
|
|
267
|
+
user: string;
|
|
268
|
+
locale?: string;
|
|
269
|
+
soul: SoulDefinition;
|
|
270
|
+
}) => Promise<SoulResponse>;
|
|
271
|
+
});
|
|
272
|
+
run(args: {
|
|
273
|
+
soul: SoulDefinition;
|
|
274
|
+
message: string;
|
|
275
|
+
memory: SoulMemory;
|
|
276
|
+
protocolSubjectHeader: string;
|
|
277
|
+
locale?: string;
|
|
278
|
+
}): Promise<SoulResponse>;
|
|
279
|
+
}
|
|
280
|
+
export declare class StaticSoulRuntime implements SoulRuntime {
|
|
281
|
+
private readonly reply;
|
|
282
|
+
constructor(reply: string);
|
|
283
|
+
run(): Promise<SoulResponse>;
|
|
284
|
+
}
|
|
285
|
+
export declare function createGuardReadTools(client: GuardToolClient): {
|
|
286
|
+
getChannelAccessState(args: {
|
|
287
|
+
protocolSubjectHeader: string;
|
|
288
|
+
resource: string;
|
|
289
|
+
plan: string;
|
|
290
|
+
}): Promise<Record<string, unknown>>;
|
|
291
|
+
getEntitlements(args: {
|
|
292
|
+
protocolSubjectHeader: string;
|
|
293
|
+
resource: string;
|
|
294
|
+
plan: string;
|
|
295
|
+
}): Promise<Record<string, unknown>>;
|
|
296
|
+
getRecentOrders(args: {
|
|
297
|
+
protocolSubjectHeader: string;
|
|
298
|
+
}): Promise<{}>;
|
|
299
|
+
getRecentPayments(args: {
|
|
300
|
+
protocolSubjectHeader: string;
|
|
301
|
+
}): Promise<{}>;
|
|
302
|
+
};
|
|
303
|
+
export declare class TelegramChannelRuntime {
|
|
304
|
+
private readonly deps;
|
|
305
|
+
constructor(deps: {
|
|
306
|
+
access: TelegramChannelAccessService;
|
|
307
|
+
mode?: ChannelMode;
|
|
308
|
+
memoryStore: MemoryStore;
|
|
309
|
+
soulRuntime?: SoulRuntime;
|
|
310
|
+
souls?: SoulDefinition[];
|
|
311
|
+
});
|
|
312
|
+
handleMessage(args: {
|
|
313
|
+
telegramUserId: string;
|
|
314
|
+
body: string;
|
|
315
|
+
username?: string;
|
|
316
|
+
locale?: string;
|
|
317
|
+
}): Promise<TelegramChannelRuntimeResult>;
|
|
318
|
+
private resolveMode;
|
|
319
|
+
private matchSoulDefinition;
|
|
320
|
+
}
|
|
321
|
+
export declare function buildTelegramChannelResource(args: {
|
|
322
|
+
resource?: string;
|
|
323
|
+
bot?: string;
|
|
324
|
+
channel?: string;
|
|
325
|
+
}): string;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
export class TelegramChannelAccessService {
|
|
2
|
+
baseUrl;
|
|
3
|
+
tenantToken;
|
|
4
|
+
linking;
|
|
5
|
+
resource;
|
|
6
|
+
plan;
|
|
7
|
+
inviteUrl;
|
|
8
|
+
log;
|
|
9
|
+
channelMetadata;
|
|
10
|
+
allowEntitlementFallbackOnServerError;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.baseUrl = String(config.baseUrl || "").trim().replace(/\/+$/, "");
|
|
13
|
+
this.tenantToken = String(config.tenantToken || "").trim();
|
|
14
|
+
this.linking = config.linking;
|
|
15
|
+
this.resource = buildTelegramChannelResource({
|
|
16
|
+
resource: config.channelResource,
|
|
17
|
+
bot: config.channelBot,
|
|
18
|
+
channel: config.channelName,
|
|
19
|
+
});
|
|
20
|
+
this.plan = String(config.channelPlan || "standard").trim() || "standard";
|
|
21
|
+
this.inviteUrl = cleanOptional(config.channelInviteUrl) || null;
|
|
22
|
+
this.channelMetadata = {
|
|
23
|
+
kind: "telegram",
|
|
24
|
+
provider: null,
|
|
25
|
+
channel_identifier: cleanOptional(config.channelName) || null,
|
|
26
|
+
phone_number: null,
|
|
27
|
+
bot_name: cleanOptional(config.channelBot) || null,
|
|
28
|
+
chat_id: null,
|
|
29
|
+
};
|
|
30
|
+
this.allowEntitlementFallbackOnServerError = config.allowEntitlementFallbackOnServerError === true;
|
|
31
|
+
this.log =
|
|
32
|
+
config.logger ||
|
|
33
|
+
((event, fields) => {
|
|
34
|
+
console.log(JSON.stringify({ level: "info", event, ...fields }));
|
|
35
|
+
});
|
|
36
|
+
if (!this.baseUrl)
|
|
37
|
+
throw new Error("telegram_channel_base_url_required");
|
|
38
|
+
if (!this.tenantToken)
|
|
39
|
+
throw new Error("telegram_channel_tenant_token_required");
|
|
40
|
+
}
|
|
41
|
+
async resolveAccess(args) {
|
|
42
|
+
const telegramUserId = String(args.telegramUserId || "").trim();
|
|
43
|
+
this.log("telegram_channel_access_request", {
|
|
44
|
+
tenant: tokenPreview(this.tenantToken),
|
|
45
|
+
telegram_user_id: telegramUserId,
|
|
46
|
+
resource: this.resource,
|
|
47
|
+
plan: this.plan,
|
|
48
|
+
});
|
|
49
|
+
const linked = (await this.linking.ensureLinkedForPaymentAction({
|
|
50
|
+
telegramUserId,
|
|
51
|
+
telegramUsername: args.telegramUsername,
|
|
52
|
+
}));
|
|
53
|
+
if (!linked.allowed) {
|
|
54
|
+
return {
|
|
55
|
+
allowed: false,
|
|
56
|
+
state: linked.code === "not_linked" ? "not_linked" : "guard_unavailable",
|
|
57
|
+
protocolSubjectHeader: null,
|
|
58
|
+
resource: this.resource,
|
|
59
|
+
anchorResource: this.resource,
|
|
60
|
+
plan: this.plan,
|
|
61
|
+
reply: linked.reply,
|
|
62
|
+
runtimeConfig: null,
|
|
63
|
+
capabilities: null,
|
|
64
|
+
source: "linking",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const protocolSubjectHeader = String(linked.link.valuya_protocol_subject_header || "").trim();
|
|
68
|
+
if (!protocolSubjectHeader) {
|
|
69
|
+
return {
|
|
70
|
+
allowed: false,
|
|
71
|
+
state: "guard_unavailable",
|
|
72
|
+
protocolSubjectHeader: null,
|
|
73
|
+
resource: this.resource,
|
|
74
|
+
anchorResource: this.resource,
|
|
75
|
+
plan: this.plan,
|
|
76
|
+
reply: "Linked subject is missing. Please run onboarding /start again.",
|
|
77
|
+
runtimeConfig: null,
|
|
78
|
+
capabilities: null,
|
|
79
|
+
source: "linking",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const resolution = await this.resolveFromGuard({
|
|
83
|
+
protocolSubjectHeader,
|
|
84
|
+
resource: this.resource,
|
|
85
|
+
plan: this.plan,
|
|
86
|
+
});
|
|
87
|
+
const base = {
|
|
88
|
+
protocolSubjectHeader,
|
|
89
|
+
resource: resolution.resource,
|
|
90
|
+
anchorResource: resolution.anchor_resource,
|
|
91
|
+
plan: resolution.plan,
|
|
92
|
+
runtimeConfig: resolution.runtime_config,
|
|
93
|
+
capabilities: resolution.capabilities,
|
|
94
|
+
source: resolution.source,
|
|
95
|
+
};
|
|
96
|
+
if (resolution.state === "paid_active" || resolution.state === "trial_active") {
|
|
97
|
+
return {
|
|
98
|
+
allowed: true,
|
|
99
|
+
state: resolution.state,
|
|
100
|
+
joinUrl: this.inviteUrl,
|
|
101
|
+
...(resolution.expires_at ? { expiresAt: resolution.expires_at } : {}),
|
|
102
|
+
...(resolution.payment_url ? { paymentUrl: resolution.payment_url } : {}),
|
|
103
|
+
...base,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
allowed: false,
|
|
108
|
+
state: resolution.state,
|
|
109
|
+
reply: buildBlockedReply(resolution),
|
|
110
|
+
...(resolution.expires_at ? { expiresAt: resolution.expires_at } : {}),
|
|
111
|
+
...(resolution.payment_url ? { paymentUrl: resolution.payment_url } : {}),
|
|
112
|
+
...base,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async resolveFromGuard(args) {
|
|
116
|
+
const response = await fetch(`${this.baseUrl}/api/v2/channel-access/resolve`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
Authorization: `Bearer ${this.tenantToken}`,
|
|
120
|
+
Accept: "application/json",
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"X-Valuya-Subject-Id": args.protocolSubjectHeader,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
resource: args.resource,
|
|
126
|
+
plan: args.plan,
|
|
127
|
+
channel: this.channelMetadata,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
const body = await safeParseJson(response);
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
if (shouldFallback(response.status, body, this.allowEntitlementFallbackOnServerError)) {
|
|
133
|
+
return this.resolveFromEntitlements(args);
|
|
134
|
+
}
|
|
135
|
+
throw new Error(`telegram_channel_access_resolve_failed:${response.status}:${JSON.stringify(body).slice(0, 300)}`);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
...normalizeResolveResponse(body, args.resource, args.plan),
|
|
139
|
+
source: "channel_access_resolve",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async resolveFromEntitlements(args) {
|
|
143
|
+
const url = new URL(`${this.baseUrl}/api/v2/entitlements`);
|
|
144
|
+
url.searchParams.set("resource", args.resource);
|
|
145
|
+
url.searchParams.set("plan", args.plan);
|
|
146
|
+
const response = await fetch(url.toString(), {
|
|
147
|
+
method: "GET",
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${this.tenantToken}`,
|
|
150
|
+
Accept: "application/json",
|
|
151
|
+
"X-Valuya-Subject-Id": args.protocolSubjectHeader,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const body = await safeParseJson(response);
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new Error(`telegram_channel_entitlement_failed:${response.status}:${JSON.stringify(body).slice(0, 300)}`);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...normalizeLegacyEntitlement(body, args.resource, args.plan),
|
|
160
|
+
source: "entitlements_fallback",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export class InMemoryMemoryStore {
|
|
165
|
+
store = new Map();
|
|
166
|
+
async load(args) {
|
|
167
|
+
return this.store.get(memoryKey(args.telegramUserId, args.soulId)) || emptyMemory();
|
|
168
|
+
}
|
|
169
|
+
async save(args) {
|
|
170
|
+
this.store.set(memoryKey(args.telegramUserId, args.soulId), args.memory);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export class OpenAISoulRuntimeAdapter {
|
|
174
|
+
args;
|
|
175
|
+
constructor(args) {
|
|
176
|
+
this.args = args;
|
|
177
|
+
}
|
|
178
|
+
async run(args) {
|
|
179
|
+
const memorySummary = args.memory.summaries.join("\n") || "(no summary)";
|
|
180
|
+
const recentTurns = args.memory.recentTurns.map((turn) => `${turn.role}: ${turn.content}`).join("\n") || "(no history)";
|
|
181
|
+
return this.args.runCompletion({
|
|
182
|
+
system: args.soul.systemPrompt,
|
|
183
|
+
user: [
|
|
184
|
+
`Protocol subject: ${args.protocolSubjectHeader}`,
|
|
185
|
+
`Memory summary:\n${memorySummary}`,
|
|
186
|
+
`Recent turns:\n${recentTurns}`,
|
|
187
|
+
`Current message:\n${args.message}`,
|
|
188
|
+
].join("\n\n"),
|
|
189
|
+
locale: args.locale,
|
|
190
|
+
soul: args.soul,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export class StaticSoulRuntime {
|
|
195
|
+
reply;
|
|
196
|
+
constructor(reply) {
|
|
197
|
+
this.reply = reply;
|
|
198
|
+
}
|
|
199
|
+
async run() {
|
|
200
|
+
return { reply: this.reply };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export function createGuardReadTools(client) {
|
|
204
|
+
return {
|
|
205
|
+
async getChannelAccessState(args) {
|
|
206
|
+
return client.getChannelAccessState(args);
|
|
207
|
+
},
|
|
208
|
+
async getEntitlements(args) {
|
|
209
|
+
return client.getEntitlements(args);
|
|
210
|
+
},
|
|
211
|
+
async getRecentOrders(args) {
|
|
212
|
+
return client.getRecentOrders?.(args) || {};
|
|
213
|
+
},
|
|
214
|
+
async getRecentPayments(args) {
|
|
215
|
+
return client.getRecentPayments?.(args) || {};
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
export class TelegramChannelRuntime {
|
|
220
|
+
deps;
|
|
221
|
+
constructor(deps) {
|
|
222
|
+
this.deps = deps;
|
|
223
|
+
}
|
|
224
|
+
async handleMessage(args) {
|
|
225
|
+
const access = await this.deps.access.resolveAccess({
|
|
226
|
+
telegramUserId: args.telegramUserId,
|
|
227
|
+
telegramUsername: args.username,
|
|
228
|
+
});
|
|
229
|
+
if (!access.allowed) {
|
|
230
|
+
return {
|
|
231
|
+
kind: "blocked",
|
|
232
|
+
access,
|
|
233
|
+
reply: buildBlockedAccessReply(access),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const resolvedMode = this.resolveMode(access);
|
|
237
|
+
if (resolvedMode.kind === "none") {
|
|
238
|
+
return {
|
|
239
|
+
kind: "allowed",
|
|
240
|
+
access,
|
|
241
|
+
protocolSubjectHeader: access.protocolSubjectHeader,
|
|
242
|
+
reply: buildAllowedAccessReply({
|
|
243
|
+
state: access.state,
|
|
244
|
+
expiresAt: access.expiresAt,
|
|
245
|
+
visitUrl: access.runtimeConfig?.visit_url || access.joinUrl || null,
|
|
246
|
+
}),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (resolvedMode.kind === "runtime_error") {
|
|
250
|
+
return {
|
|
251
|
+
kind: "runtime_error",
|
|
252
|
+
access,
|
|
253
|
+
protocolSubjectHeader: access.protocolSubjectHeader,
|
|
254
|
+
error: resolvedMode.error,
|
|
255
|
+
reply: buildRuntimeErrorReply(resolvedMode.error),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (resolvedMode.kind === "human") {
|
|
259
|
+
return {
|
|
260
|
+
kind: "human",
|
|
261
|
+
access,
|
|
262
|
+
protocolSubjectHeader: access.protocolSubjectHeader,
|
|
263
|
+
reply: [
|
|
264
|
+
"Your access is active. Your message has been forwarded to the human channel.",
|
|
265
|
+
access.runtimeConfig?.visit_url ? `Direct link: ${access.runtimeConfig.visit_url}` : null,
|
|
266
|
+
].filter(Boolean).join("\n"),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (!this.deps.soulRuntime)
|
|
270
|
+
throw new Error("telegram_channel_soul_runtime_missing");
|
|
271
|
+
const soul = resolvedMode.soul;
|
|
272
|
+
const memory = await this.deps.memoryStore.load({
|
|
273
|
+
telegramUserId: args.telegramUserId,
|
|
274
|
+
soulId: soul.id,
|
|
275
|
+
});
|
|
276
|
+
const result = await this.deps.soulRuntime.run({
|
|
277
|
+
soul,
|
|
278
|
+
message: args.body,
|
|
279
|
+
memory,
|
|
280
|
+
protocolSubjectHeader: access.protocolSubjectHeader,
|
|
281
|
+
locale: args.locale || soul.locale,
|
|
282
|
+
});
|
|
283
|
+
await this.deps.memoryStore.save({
|
|
284
|
+
telegramUserId: args.telegramUserId,
|
|
285
|
+
soulId: soul.id,
|
|
286
|
+
memory: result.memory || appendMemory(memory, args.body, result.reply),
|
|
287
|
+
});
|
|
288
|
+
return {
|
|
289
|
+
kind: "agent",
|
|
290
|
+
access,
|
|
291
|
+
protocolSubjectHeader: access.protocolSubjectHeader,
|
|
292
|
+
soulId: soul.id,
|
|
293
|
+
reply: result.reply,
|
|
294
|
+
metadata: result.metadata,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
resolveMode(access) {
|
|
298
|
+
const runtimeConfig = access.runtimeConfig;
|
|
299
|
+
if (!runtimeConfig) {
|
|
300
|
+
if (!this.deps.mode)
|
|
301
|
+
return { kind: "none" };
|
|
302
|
+
if (this.deps.mode.kind === "human")
|
|
303
|
+
return { kind: "human" };
|
|
304
|
+
const soulId = this.deps.mode.soulId;
|
|
305
|
+
const soul = this.deps.souls?.find((entry) => entry.id === soulId);
|
|
306
|
+
return soul ? { kind: "agent", soul } : { kind: "runtime_error", error: "agent_misconfigured" };
|
|
307
|
+
}
|
|
308
|
+
if (runtimeConfig.mode === "human")
|
|
309
|
+
return { kind: "human" };
|
|
310
|
+
if (!runtimeConfig.soul) {
|
|
311
|
+
if (runtimeConfig.fallback?.allowed && runtimeConfig.fallback.mode === "human")
|
|
312
|
+
return { kind: "human" };
|
|
313
|
+
return { kind: "runtime_error", error: "agent_misconfigured" };
|
|
314
|
+
}
|
|
315
|
+
const soul = this.matchSoulDefinition(runtimeConfig.soul);
|
|
316
|
+
if (!soul) {
|
|
317
|
+
if (runtimeConfig.fallback?.allowed && runtimeConfig.fallback.mode === "human")
|
|
318
|
+
return { kind: "human" };
|
|
319
|
+
return { kind: "runtime_error", error: "agent_misconfigured" };
|
|
320
|
+
}
|
|
321
|
+
return { kind: "agent", soul };
|
|
322
|
+
}
|
|
323
|
+
matchSoulDefinition(soulConfig) {
|
|
324
|
+
const bySlug = this.deps.souls?.find((entry) => entry.id === soulConfig.slug);
|
|
325
|
+
if (bySlug)
|
|
326
|
+
return bySlug;
|
|
327
|
+
const byId = this.deps.souls?.find((entry) => entry.id === String(soulConfig.id));
|
|
328
|
+
if (byId)
|
|
329
|
+
return byId;
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
export function buildTelegramChannelResource(args) {
|
|
334
|
+
const explicit = cleanOptional(args.resource);
|
|
335
|
+
if (explicit)
|
|
336
|
+
return explicit;
|
|
337
|
+
const bot = cleanOptional(args.bot);
|
|
338
|
+
const channel = cleanOptional(args.channel);
|
|
339
|
+
if (!bot || !channel) {
|
|
340
|
+
throw new Error("telegram_channel_resource_config_missing");
|
|
341
|
+
}
|
|
342
|
+
return `telegram:channel:${bot}:${channel}`;
|
|
343
|
+
}
|
|
344
|
+
function normalizeResolveResponse(body, resource, plan) {
|
|
345
|
+
const record = body && typeof body === "object" ? body : {};
|
|
346
|
+
return {
|
|
347
|
+
ok: record.ok !== false,
|
|
348
|
+
state: normalizeState(record.state),
|
|
349
|
+
resource: readString(record.resource) || resource,
|
|
350
|
+
anchor_resource: readString(record.anchor_resource) || readString(record.resource) || resource,
|
|
351
|
+
plan: readString(record.plan) || plan,
|
|
352
|
+
expires_at: readString(record.expires_at) || null,
|
|
353
|
+
payment_url: readString(record.payment_url) || null,
|
|
354
|
+
reason: readString(record.reason) || null,
|
|
355
|
+
runtime_config: readRuntimeConfig(record.runtime_config),
|
|
356
|
+
capabilities: readCapabilities(record.capabilities),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function normalizeLegacyEntitlement(body, resource, plan) {
|
|
360
|
+
const record = body && typeof body === "object" ? body : {};
|
|
361
|
+
return {
|
|
362
|
+
ok: true,
|
|
363
|
+
state: record.active === true ? "paid_active" : "inactive",
|
|
364
|
+
resource,
|
|
365
|
+
anchor_resource: resource,
|
|
366
|
+
plan,
|
|
367
|
+
expires_at: readString(record.expires_at) || null,
|
|
368
|
+
payment_url: readString(record.payment_url) || null,
|
|
369
|
+
reason: readString(record.reason) || null,
|
|
370
|
+
runtime_config: null,
|
|
371
|
+
capabilities: {
|
|
372
|
+
channel_access_version: "fallback-entitlements",
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function normalizeState(value) {
|
|
377
|
+
const v = readString(value);
|
|
378
|
+
if (v === "paid_active" || v === "trial_active" || v === "expired_payment_required" || v === "inactive") {
|
|
379
|
+
return v;
|
|
380
|
+
}
|
|
381
|
+
return "inactive";
|
|
382
|
+
}
|
|
383
|
+
function readRuntimeConfig(value) {
|
|
384
|
+
if (!value || typeof value !== "object")
|
|
385
|
+
return null;
|
|
386
|
+
const record = value;
|
|
387
|
+
const mode = readString(record.mode);
|
|
388
|
+
const channel = readString(record.channel);
|
|
389
|
+
const channelKind = readString(record.channel_kind);
|
|
390
|
+
if ((mode !== "human" && mode !== "agent") || !channel || !channelKind)
|
|
391
|
+
return null;
|
|
392
|
+
const soul = readSoul(record.soul);
|
|
393
|
+
return {
|
|
394
|
+
mode,
|
|
395
|
+
channel,
|
|
396
|
+
channel_kind: channelKind,
|
|
397
|
+
provider: readString(record.provider) || null,
|
|
398
|
+
channel_app_id: readString(record.channel_app_id) || null,
|
|
399
|
+
visit_url: readString(record.visit_url) || null,
|
|
400
|
+
human_routing: asRecord(record.human_routing),
|
|
401
|
+
agent_routing: asRecord(record.agent_routing),
|
|
402
|
+
fallback: readFallback(record.fallback),
|
|
403
|
+
soul,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function readSoul(value) {
|
|
407
|
+
if (!value || typeof value !== "object")
|
|
408
|
+
return null;
|
|
409
|
+
const record = value;
|
|
410
|
+
const id = record.id;
|
|
411
|
+
const slug = readString(record.slug);
|
|
412
|
+
const name = readString(record.name);
|
|
413
|
+
const version = record.version;
|
|
414
|
+
if ((typeof id !== "string" && typeof id !== "number") || !slug || !name || (typeof version !== "string" && typeof version !== "number")) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
return { id, slug, name, version };
|
|
418
|
+
}
|
|
419
|
+
function readFallback(value) {
|
|
420
|
+
if (!value || typeof value !== "object")
|
|
421
|
+
return null;
|
|
422
|
+
const record = value;
|
|
423
|
+
return record.allowed === true && readString(record.mode) === "human"
|
|
424
|
+
? { allowed: true, mode: "human" }
|
|
425
|
+
: null;
|
|
426
|
+
}
|
|
427
|
+
function readCapabilities(value) {
|
|
428
|
+
const record = value && typeof value === "object" ? value : {};
|
|
429
|
+
return {
|
|
430
|
+
channel_access_version: readString(record.channel_access_version) || "1",
|
|
431
|
+
...record,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function buildBlockedReply(response) {
|
|
435
|
+
if (response.state === "expired_payment_required") {
|
|
436
|
+
return [
|
|
437
|
+
"Access to this Telegram channel requires payment.",
|
|
438
|
+
response.expires_at ? `Trial expired at: ${response.expires_at}` : null,
|
|
439
|
+
response.payment_url ? `Payment link: ${response.payment_url}` : null,
|
|
440
|
+
].filter(Boolean).join("\n");
|
|
441
|
+
}
|
|
442
|
+
return [
|
|
443
|
+
"Access to this Telegram channel is currently unavailable.",
|
|
444
|
+
response.payment_url ? `Payment link: ${response.payment_url}` : null,
|
|
445
|
+
].filter(Boolean).join("\n");
|
|
446
|
+
}
|
|
447
|
+
function shouldFallback(status, body, allowServerErrorFallback) {
|
|
448
|
+
if (status === 404 || status === 501 || status === 503)
|
|
449
|
+
return true;
|
|
450
|
+
const errorCode = body && typeof body === "object" ? readString(body.error) : undefined;
|
|
451
|
+
if (errorCode === "channel_access_not_available")
|
|
452
|
+
return true;
|
|
453
|
+
if (allowServerErrorFallback && status >= 500)
|
|
454
|
+
return true;
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
async function safeParseJson(response) {
|
|
458
|
+
const text = await response.text();
|
|
459
|
+
if (!text)
|
|
460
|
+
return {};
|
|
461
|
+
try {
|
|
462
|
+
return JSON.parse(text);
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
return { raw: text };
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function asRecord(value) {
|
|
469
|
+
return value && typeof value === "object" ? value : null;
|
|
470
|
+
}
|
|
471
|
+
function cleanOptional(value) {
|
|
472
|
+
const v = String(value || "").trim();
|
|
473
|
+
return v || undefined;
|
|
474
|
+
}
|
|
475
|
+
function readString(value) {
|
|
476
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
477
|
+
}
|
|
478
|
+
function tokenPreview(token) {
|
|
479
|
+
const t = String(token || "").trim();
|
|
480
|
+
return t ? t.slice(0, 12) : "unknown";
|
|
481
|
+
}
|
|
482
|
+
function memoryKey(telegramUserId, soulId) {
|
|
483
|
+
return `${telegramUserId}::${soulId}`;
|
|
484
|
+
}
|
|
485
|
+
function emptyMemory() {
|
|
486
|
+
return {
|
|
487
|
+
recentTurns: [],
|
|
488
|
+
summaries: [],
|
|
489
|
+
updatedAt: new Date(0).toISOString(),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function appendMemory(memory, userMessage, assistantReply) {
|
|
493
|
+
const nextTurns = [
|
|
494
|
+
...memory.recentTurns,
|
|
495
|
+
{ role: "user", content: userMessage, createdAt: new Date().toISOString() },
|
|
496
|
+
{ role: "assistant", content: assistantReply, createdAt: new Date().toISOString() },
|
|
497
|
+
].slice(-12);
|
|
498
|
+
return {
|
|
499
|
+
...memory,
|
|
500
|
+
recentTurns: nextTurns,
|
|
501
|
+
updatedAt: new Date().toISOString(),
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function buildBlockedAccessReply(decision) {
|
|
505
|
+
switch (decision.state) {
|
|
506
|
+
case "not_linked":
|
|
507
|
+
return decision.reply;
|
|
508
|
+
case "expired_payment_required":
|
|
509
|
+
return [
|
|
510
|
+
decision.reply,
|
|
511
|
+
decision.expiresAt ? `Free access ended at ${decision.expiresAt}.` : null,
|
|
512
|
+
decision.paymentUrl ? `Payment link: ${decision.paymentUrl}` : null,
|
|
513
|
+
].filter(Boolean).join("\n");
|
|
514
|
+
case "inactive":
|
|
515
|
+
return [
|
|
516
|
+
decision.reply,
|
|
517
|
+
decision.paymentUrl ? `Payment link: ${decision.paymentUrl}` : null,
|
|
518
|
+
].filter(Boolean).join("\n");
|
|
519
|
+
case "guard_unavailable":
|
|
520
|
+
default:
|
|
521
|
+
return decision.reply;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function buildAllowedAccessReply(args) {
|
|
525
|
+
return [
|
|
526
|
+
args.state === "trial_active"
|
|
527
|
+
? "Your access is active. Your free access is currently running."
|
|
528
|
+
: "Your access is active.",
|
|
529
|
+
args.expiresAt ? `Valid until: ${args.expiresAt}.` : null,
|
|
530
|
+
args.visitUrl ? `Direct link: ${args.visitUrl}` : null,
|
|
531
|
+
].filter(Boolean).join("\n");
|
|
532
|
+
}
|
|
533
|
+
function buildRuntimeErrorReply(error) {
|
|
534
|
+
if (error === "agent_misconfigured") {
|
|
535
|
+
return "Your access is active, but the agent is currently misconfigured. Please try again later.";
|
|
536
|
+
}
|
|
537
|
+
return "Your access is active, but no runtime is configured for this channel yet.";
|
|
538
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@valuya/telegram-channel-access",
|
|
3
|
+
"version": "0.2.0-beta.1",
|
|
4
|
+
"description": "Reusable Telegram paid-channel access checks for Valuya Guard bots",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@valuya/channel-access-core": "^0.2.0-beta.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.0.10"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"tag": "next"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
31
|
+
"test": "pnpm run build && node --test dist/*.test.js"
|
|
32
|
+
}
|
|
33
|
+
}
|