@cmdop/bot 2026.2.26
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/CHANGELOG.md +36 -0
- package/README.md +284 -0
- package/dist/index.cjs +17385 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +873 -0
- package/dist/index.d.ts +873 -0
- package/dist/index.js +17344 -0
- package/dist/index.js.map +1 -0
- package/examples/README.md +142 -0
- package/examples/custom-channel.ts +206 -0
- package/examples/discord.ts +65 -0
- package/examples/multi-channel.ts +105 -0
- package/examples/slack.ts +66 -0
- package/examples/telegram.ts +53 -0
- package/package.json +77 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { CMDOPClient } from '@cmdop/node';
|
|
3
|
+
import { WebClient } from '@slack/web-api';
|
|
4
|
+
import { InlineKeyboardButton } from 'grammy/types';
|
|
5
|
+
import { RESTPostAPIChatInputApplicationCommandsJSONBody } from 'discord.js';
|
|
6
|
+
|
|
7
|
+
declare const AttachmentSchema: z.ZodObject<{
|
|
8
|
+
type: z.ZodEnum<{
|
|
9
|
+
photo: "photo";
|
|
10
|
+
document: "document";
|
|
11
|
+
audio: "audio";
|
|
12
|
+
video: "video";
|
|
13
|
+
}>;
|
|
14
|
+
fileId: z.ZodOptional<z.ZodString>;
|
|
15
|
+
url: z.ZodOptional<z.ZodString>;
|
|
16
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
17
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
type Attachment = z.infer<typeof AttachmentSchema>;
|
|
20
|
+
declare const IncomingMessageSchema: z.ZodObject<{
|
|
21
|
+
id: z.ZodString;
|
|
22
|
+
userId: z.ZodString;
|
|
23
|
+
channelId: z.ZodString;
|
|
24
|
+
text: z.ZodString;
|
|
25
|
+
timestamp: z.ZodCoercedDate<unknown>;
|
|
26
|
+
attachments: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
27
|
+
type: z.ZodEnum<{
|
|
28
|
+
photo: "photo";
|
|
29
|
+
document: "document";
|
|
30
|
+
audio: "audio";
|
|
31
|
+
video: "video";
|
|
32
|
+
}>;
|
|
33
|
+
fileId: z.ZodOptional<z.ZodString>;
|
|
34
|
+
url: z.ZodOptional<z.ZodString>;
|
|
35
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
36
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
}, z.core.$strip>>>;
|
|
38
|
+
replyToId: z.ZodOptional<z.ZodString>;
|
|
39
|
+
threadId: z.ZodOptional<z.ZodString>;
|
|
40
|
+
raw: z.ZodOptional<z.ZodUnknown>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
type IncomingMessage = z.infer<typeof IncomingMessageSchema>;
|
|
43
|
+
declare const TextMessageSchema: z.ZodObject<{
|
|
44
|
+
type: z.ZodLiteral<"text">;
|
|
45
|
+
text: z.ZodString;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
type TextMessage = z.infer<typeof TextMessageSchema>;
|
|
48
|
+
declare const CodeMessageSchema: z.ZodObject<{
|
|
49
|
+
type: z.ZodLiteral<"code">;
|
|
50
|
+
code: z.ZodString;
|
|
51
|
+
language: z.ZodOptional<z.ZodString>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
type CodeMessage = z.infer<typeof CodeMessageSchema>;
|
|
54
|
+
declare const ErrorMessageSchema: z.ZodObject<{
|
|
55
|
+
type: z.ZodLiteral<"error">;
|
|
56
|
+
message: z.ZodString;
|
|
57
|
+
hint: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
type ErrorMessage = z.infer<typeof ErrorMessageSchema>;
|
|
60
|
+
declare const OutgoingMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
61
|
+
type: z.ZodLiteral<"text">;
|
|
62
|
+
text: z.ZodString;
|
|
63
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
64
|
+
type: z.ZodLiteral<"code">;
|
|
65
|
+
code: z.ZodString;
|
|
66
|
+
language: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
68
|
+
type: z.ZodLiteral<"error">;
|
|
69
|
+
message: z.ZodString;
|
|
70
|
+
hint: z.ZodOptional<z.ZodString>;
|
|
71
|
+
}, z.core.$strip>], "type">;
|
|
72
|
+
type OutgoingMessage = z.infer<typeof OutgoingMessageSchema>;
|
|
73
|
+
|
|
74
|
+
declare const PermissionLevelSchema: z.ZodEnum<{
|
|
75
|
+
NONE: "NONE";
|
|
76
|
+
READ: "READ";
|
|
77
|
+
EXECUTE: "EXECUTE";
|
|
78
|
+
FILES: "FILES";
|
|
79
|
+
ADMIN: "ADMIN";
|
|
80
|
+
}>;
|
|
81
|
+
type PermissionLevel = z.infer<typeof PermissionLevelSchema>;
|
|
82
|
+
declare const PERMISSION_ORDER: Record<PermissionLevel, number>;
|
|
83
|
+
declare const UserIdentitySchema: z.ZodObject<{
|
|
84
|
+
platform: z.ZodString;
|
|
85
|
+
platformId: z.ZodString;
|
|
86
|
+
username: z.ZodOptional<z.ZodString>;
|
|
87
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
88
|
+
}, z.core.$strip>;
|
|
89
|
+
type UserIdentity = z.infer<typeof UserIdentitySchema>;
|
|
90
|
+
declare const BotUserSchema: z.ZodObject<{
|
|
91
|
+
id: z.ZodString;
|
|
92
|
+
identity: z.ZodObject<{
|
|
93
|
+
platform: z.ZodString;
|
|
94
|
+
platformId: z.ZodString;
|
|
95
|
+
username: z.ZodOptional<z.ZodString>;
|
|
96
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
permission: z.ZodEnum<{
|
|
99
|
+
NONE: "NONE";
|
|
100
|
+
READ: "READ";
|
|
101
|
+
EXECUTE: "EXECUTE";
|
|
102
|
+
FILES: "FILES";
|
|
103
|
+
ADMIN: "ADMIN";
|
|
104
|
+
}>;
|
|
105
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
106
|
+
lastSeenAt: z.ZodCoercedDate<unknown>;
|
|
107
|
+
}, z.core.$strip>;
|
|
108
|
+
type BotUser = z.infer<typeof BotUserSchema>;
|
|
109
|
+
|
|
110
|
+
declare const ParsedCommandSchema: z.ZodObject<{
|
|
111
|
+
name: z.ZodString;
|
|
112
|
+
args: z.ZodArray<z.ZodString>;
|
|
113
|
+
rawText: z.ZodString;
|
|
114
|
+
}, z.core.$strip>;
|
|
115
|
+
type ParsedCommand = z.infer<typeof ParsedCommandSchema>;
|
|
116
|
+
type CommandContext = {
|
|
117
|
+
readonly userId: string;
|
|
118
|
+
readonly command: string;
|
|
119
|
+
readonly args: readonly string[];
|
|
120
|
+
readonly channelId: string;
|
|
121
|
+
readonly message: IncomingMessage;
|
|
122
|
+
readonly machine?: string;
|
|
123
|
+
};
|
|
124
|
+
type CommandInfo = {
|
|
125
|
+
readonly name: string;
|
|
126
|
+
readonly description: string;
|
|
127
|
+
readonly usage: string;
|
|
128
|
+
readonly requiredPermission: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Parse a bot command from message text.
|
|
132
|
+
* Supports: /exec ls -la, !exec ls -la
|
|
133
|
+
* Requires a / or ! prefix to distinguish commands from plain chat messages.
|
|
134
|
+
*/
|
|
135
|
+
declare function parseCommand(text: string): ParsedCommand | null;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @cmdop/bot — Error hierarchy
|
|
139
|
+
*
|
|
140
|
+
* All errors thrown by @cmdop/bot extend BotError.
|
|
141
|
+
* Never expose raw @cmdop/node or platform errors to consumers — wrap them.
|
|
142
|
+
*/
|
|
143
|
+
declare class BotError extends Error {
|
|
144
|
+
readonly code: string;
|
|
145
|
+
readonly context: Record<string, unknown>;
|
|
146
|
+
constructor(message: string, options?: {
|
|
147
|
+
code?: string;
|
|
148
|
+
context?: Record<string, unknown>;
|
|
149
|
+
cause?: Error;
|
|
150
|
+
});
|
|
151
|
+
toLog(): Record<string, unknown>;
|
|
152
|
+
}
|
|
153
|
+
declare class PermissionDeniedError extends BotError {
|
|
154
|
+
constructor(userId: string, required: string);
|
|
155
|
+
}
|
|
156
|
+
declare class RateLimitError extends BotError {
|
|
157
|
+
readonly retryAfterMs?: number;
|
|
158
|
+
constructor(message: string, retryAfterMs?: number);
|
|
159
|
+
}
|
|
160
|
+
declare class HandlerError extends BotError {
|
|
161
|
+
constructor(message: string, options?: {
|
|
162
|
+
code?: string;
|
|
163
|
+
context?: Record<string, unknown>;
|
|
164
|
+
cause?: Error;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
declare class CommandNotFoundError extends HandlerError {
|
|
168
|
+
constructor(command: string);
|
|
169
|
+
}
|
|
170
|
+
declare class CommandArgsError extends HandlerError {
|
|
171
|
+
constructor(command: string, hint: string);
|
|
172
|
+
}
|
|
173
|
+
declare class ExecutionTimeoutError extends HandlerError {
|
|
174
|
+
constructor(command: string, timeoutMs: number);
|
|
175
|
+
}
|
|
176
|
+
declare class CMDOPError extends BotError {
|
|
177
|
+
constructor(message: string, cause?: Error);
|
|
178
|
+
}
|
|
179
|
+
declare class MachineNotFoundError extends BotError {
|
|
180
|
+
constructor(machine: string);
|
|
181
|
+
}
|
|
182
|
+
declare class MachineOfflineError extends BotError {
|
|
183
|
+
constructor(machine: string);
|
|
184
|
+
}
|
|
185
|
+
declare class CMDOPTimeoutError extends BotError {
|
|
186
|
+
constructor(operation: string, timeoutMs: number, cause?: Error);
|
|
187
|
+
}
|
|
188
|
+
declare class ChannelError extends BotError {
|
|
189
|
+
constructor(message: string, options?: {
|
|
190
|
+
code?: string;
|
|
191
|
+
context?: Record<string, unknown>;
|
|
192
|
+
cause?: Error;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
declare class TelegramError extends ChannelError {
|
|
196
|
+
constructor(message: string, cause?: Error);
|
|
197
|
+
}
|
|
198
|
+
declare class DiscordError extends ChannelError {
|
|
199
|
+
constructor(message: string, cause?: Error);
|
|
200
|
+
}
|
|
201
|
+
declare class SlackError extends ChannelError {
|
|
202
|
+
constructor(message: string, cause?: Error);
|
|
203
|
+
}
|
|
204
|
+
declare class TeamsError extends ChannelError {
|
|
205
|
+
constructor(message: string, cause?: Error);
|
|
206
|
+
}
|
|
207
|
+
declare class ConfigError extends BotError {
|
|
208
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
type HandlerResult = {
|
|
212
|
+
ok: true;
|
|
213
|
+
value: OutgoingMessage;
|
|
214
|
+
} | {
|
|
215
|
+
ok: false;
|
|
216
|
+
error: BotError;
|
|
217
|
+
};
|
|
218
|
+
declare function ok(value: OutgoingMessage): HandlerResult;
|
|
219
|
+
declare function err(error: BotError): HandlerResult;
|
|
220
|
+
interface ChannelProtocol {
|
|
221
|
+
readonly id: string;
|
|
222
|
+
readonly name: string;
|
|
223
|
+
start(): Promise<void>;
|
|
224
|
+
stop(): Promise<void>;
|
|
225
|
+
send(userId: string, message: OutgoingMessage): Promise<void>;
|
|
226
|
+
onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
227
|
+
}
|
|
228
|
+
interface HandlerProtocol {
|
|
229
|
+
readonly name: string;
|
|
230
|
+
readonly description: string;
|
|
231
|
+
readonly usage: string;
|
|
232
|
+
readonly requiredPermission: PermissionLevel;
|
|
233
|
+
handle(ctx: CommandContext): Promise<HandlerResult>;
|
|
234
|
+
}
|
|
235
|
+
interface FormatterProtocol {
|
|
236
|
+
formatText(text: string): string;
|
|
237
|
+
formatCode(code: string, language?: string): string;
|
|
238
|
+
formatError(error: BotError): string;
|
|
239
|
+
}
|
|
240
|
+
interface PermissionStoreProtocol {
|
|
241
|
+
getLevel(userId: string): Promise<PermissionLevel>;
|
|
242
|
+
setLevel(userId: string, level: PermissionLevel): Promise<void>;
|
|
243
|
+
deleteUser(userId: string): Promise<void>;
|
|
244
|
+
}
|
|
245
|
+
interface LoggerProtocol {
|
|
246
|
+
debug(msg: string, meta?: Record<string, unknown>): void;
|
|
247
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
248
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
249
|
+
error(msg: string, meta?: Record<string, unknown>): void;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
declare class InMemoryPermissionStore implements PermissionStoreProtocol {
|
|
253
|
+
private readonly store;
|
|
254
|
+
constructor(seed?: Record<string, PermissionLevel>);
|
|
255
|
+
getLevel(userId: string): Promise<PermissionLevel>;
|
|
256
|
+
setLevel(userId: string, level: PermissionLevel): Promise<void>;
|
|
257
|
+
deleteUser(userId: string): Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Maps platform-scoped user IDs to a single canonical user ID.
|
|
261
|
+
*
|
|
262
|
+
* Platform IDs are namespaced as `"<channel>:<userId>"` (e.g. `"telegram:12345"`).
|
|
263
|
+
* When a user is linked, all their platform IDs resolve to the same canonical ID,
|
|
264
|
+
* so a permission granted on Telegram automatically applies on Discord and Slack.
|
|
265
|
+
*
|
|
266
|
+
* Example:
|
|
267
|
+
* map.link('telegram:12345', 'discord:67890');
|
|
268
|
+
* map.resolve('discord:67890') // → 'telegram:12345' (canonical = first linked)
|
|
269
|
+
*/
|
|
270
|
+
declare class IdentityMap {
|
|
271
|
+
private readonly links;
|
|
272
|
+
/**
|
|
273
|
+
* Build a namespaced platform ID.
|
|
274
|
+
*/
|
|
275
|
+
static platformId(channel: string, userId: string): string;
|
|
276
|
+
/**
|
|
277
|
+
* Link two platform IDs together.
|
|
278
|
+
* The first ID becomes (or joins) the canonical identity for the group.
|
|
279
|
+
* Both IDs resolve to the same canonical ID after linking.
|
|
280
|
+
*/
|
|
281
|
+
link(idA: string, idB: string): void;
|
|
282
|
+
/**
|
|
283
|
+
* Resolve a platform ID to its canonical ID.
|
|
284
|
+
* Returns the original ID if no link exists (it is its own canonical).
|
|
285
|
+
*/
|
|
286
|
+
resolve(id: string): string;
|
|
287
|
+
/**
|
|
288
|
+
* Remove all links for a given platform ID.
|
|
289
|
+
* The canonical group still exists — only this ID is unlinked.
|
|
290
|
+
*/
|
|
291
|
+
unlink(id: string): void;
|
|
292
|
+
/**
|
|
293
|
+
* True if the two IDs resolve to the same canonical identity.
|
|
294
|
+
*/
|
|
295
|
+
areSameIdentity(idA: string, idB: string): boolean;
|
|
296
|
+
}
|
|
297
|
+
interface PermissionManagerOptions {
|
|
298
|
+
adminUsers?: string[];
|
|
299
|
+
defaultLevel?: PermissionLevel;
|
|
300
|
+
/** Cross-channel identity map. Shared across all channels in an IntegrationHub. */
|
|
301
|
+
identityMap?: IdentityMap;
|
|
302
|
+
}
|
|
303
|
+
declare class PermissionManager {
|
|
304
|
+
private readonly store;
|
|
305
|
+
private readonly adminSet;
|
|
306
|
+
private readonly defaultLevel;
|
|
307
|
+
private readonly identityMap;
|
|
308
|
+
constructor(store: PermissionStoreProtocol, options?: PermissionManagerOptions);
|
|
309
|
+
/**
|
|
310
|
+
* Get the effective permission level for a user.
|
|
311
|
+
* Resolves cross-channel identity before looking up the store.
|
|
312
|
+
*/
|
|
313
|
+
getLevel(userId: string): Promise<PermissionLevel>;
|
|
314
|
+
setLevel(userId: string, level: PermissionLevel): Promise<void>;
|
|
315
|
+
deleteUser(userId: string): Promise<void>;
|
|
316
|
+
/**
|
|
317
|
+
* Throws PermissionDeniedError if user doesn't have required level.
|
|
318
|
+
*/
|
|
319
|
+
checkPermission(userId: string, required: PermissionLevel): Promise<void>;
|
|
320
|
+
hasPermission(userId: string, required: PermissionLevel): Promise<boolean>;
|
|
321
|
+
/** Expose the identity map for cross-channel linking at the hub level. */
|
|
322
|
+
get identity(): IdentityMap;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
declare class MessageDispatcher {
|
|
326
|
+
private readonly permissions;
|
|
327
|
+
private readonly logger;
|
|
328
|
+
private readonly handlers;
|
|
329
|
+
constructor(permissions: PermissionManager, logger: LoggerProtocol);
|
|
330
|
+
register(handler: HandlerProtocol): void;
|
|
331
|
+
dispatch(ctx: CommandContext): Promise<HandlerResult>;
|
|
332
|
+
getCommandList(minPermission?: PermissionLevel): CommandInfo[];
|
|
333
|
+
hasCommand(name: string): boolean;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
declare abstract class BaseChannel implements ChannelProtocol {
|
|
337
|
+
readonly id: string;
|
|
338
|
+
readonly name: string;
|
|
339
|
+
protected readonly permissions: PermissionManager;
|
|
340
|
+
protected readonly dispatcher: MessageDispatcher;
|
|
341
|
+
protected readonly logger: LoggerProtocol;
|
|
342
|
+
protected constructor(id: string, name: string, permissions: PermissionManager, dispatcher: MessageDispatcher, logger: LoggerProtocol);
|
|
343
|
+
abstract start(): Promise<void>;
|
|
344
|
+
abstract stop(): Promise<void>;
|
|
345
|
+
abstract send(userId: string, message: OutgoingMessage): Promise<void>;
|
|
346
|
+
abstract onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
347
|
+
/**
|
|
348
|
+
* Process an incoming message: parse command → check permission → dispatch → send result.
|
|
349
|
+
* Channels call this from their platform event handler.
|
|
350
|
+
*/
|
|
351
|
+
protected processMessage(msg: IncomingMessage): Promise<void>;
|
|
352
|
+
private formatErrorMessage;
|
|
353
|
+
protected logEvent(event: string, meta?: Record<string, unknown>): void;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface SlackChannelOptions {
|
|
357
|
+
/** Bot OAuth token (xoxb-...) */
|
|
358
|
+
token: string;
|
|
359
|
+
/** App-level token for Socket Mode (xapp-...) */
|
|
360
|
+
appToken: string;
|
|
361
|
+
/** User IDs that get ADMIN permission automatically */
|
|
362
|
+
adminUsers?: string[];
|
|
363
|
+
/** Max characters before truncating outgoing messages. Default: 2800 */
|
|
364
|
+
maxMessageLength?: number;
|
|
365
|
+
}
|
|
366
|
+
declare class SlackChannel extends BaseChannel {
|
|
367
|
+
private app;
|
|
368
|
+
private readonly token;
|
|
369
|
+
private readonly appToken;
|
|
370
|
+
private readonly formatter;
|
|
371
|
+
private readonly maxLength;
|
|
372
|
+
private messageHandlers;
|
|
373
|
+
constructor(options: SlackChannelOptions, permissions: PermissionManager, dispatcher: MessageDispatcher, logger: LoggerProtocol);
|
|
374
|
+
start(): Promise<void>;
|
|
375
|
+
stop(): Promise<void>;
|
|
376
|
+
send(userId: string, message: OutgoingMessage): Promise<void>;
|
|
377
|
+
onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
378
|
+
private processMessageWithSay;
|
|
379
|
+
private buildText;
|
|
380
|
+
private truncate;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
interface DiscordChannelOptions {
|
|
384
|
+
/** Bot token from Discord Developer Portal */
|
|
385
|
+
token: string;
|
|
386
|
+
/** Your Discord application/client ID */
|
|
387
|
+
clientId: string;
|
|
388
|
+
/** Guild ID for guild-scoped command registration (instant). Omit for global commands (up to 1h). */
|
|
389
|
+
guildId?: string;
|
|
390
|
+
/** User IDs (string) that get ADMIN permission automatically */
|
|
391
|
+
adminUsers?: string[];
|
|
392
|
+
/** Max characters before truncating outgoing messages. Default: 1900 */
|
|
393
|
+
maxMessageLength?: number;
|
|
394
|
+
}
|
|
395
|
+
declare class DiscordChannel extends BaseChannel {
|
|
396
|
+
private client;
|
|
397
|
+
private readonly token;
|
|
398
|
+
private readonly clientId;
|
|
399
|
+
private readonly guildId;
|
|
400
|
+
private readonly formatter;
|
|
401
|
+
private readonly maxLength;
|
|
402
|
+
private messageHandlers;
|
|
403
|
+
constructor(options: DiscordChannelOptions, permissions: PermissionManager, dispatcher: MessageDispatcher, logger: LoggerProtocol);
|
|
404
|
+
start(): Promise<void>;
|
|
405
|
+
stop(): Promise<void>;
|
|
406
|
+
send(userId: string, message: OutgoingMessage): Promise<void>;
|
|
407
|
+
onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
408
|
+
private handleInteraction;
|
|
409
|
+
private buildCommandText;
|
|
410
|
+
private extractArgs;
|
|
411
|
+
private outgoingToString;
|
|
412
|
+
private truncate;
|
|
413
|
+
private registerSlashCommands;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
interface TelegramChannelOptions {
|
|
417
|
+
token: string;
|
|
418
|
+
/** User IDs (numeric as string) that get ADMIN permission automatically */
|
|
419
|
+
adminUsers?: string[];
|
|
420
|
+
/** Max length before truncating outgoing messages. Default: 4000 */
|
|
421
|
+
maxMessageLength?: number;
|
|
422
|
+
}
|
|
423
|
+
declare class TelegramChannel extends BaseChannel {
|
|
424
|
+
private bot;
|
|
425
|
+
private readonly token;
|
|
426
|
+
private readonly formatter;
|
|
427
|
+
private readonly maxLength;
|
|
428
|
+
private messageHandlers;
|
|
429
|
+
constructor(options: TelegramChannelOptions, permissions: PermissionManager, dispatcher: MessageDispatcher, logger: LoggerProtocol);
|
|
430
|
+
start(): Promise<void>;
|
|
431
|
+
stop(): Promise<void>;
|
|
432
|
+
send(userId: string, message: OutgoingMessage): Promise<void>;
|
|
433
|
+
onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
434
|
+
private normalizeContext;
|
|
435
|
+
private truncate;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
declare const BotSettingsSchema: z.ZodObject<{
|
|
439
|
+
logLevel: z.ZodDefault<z.ZodEnum<{
|
|
440
|
+
error: "error";
|
|
441
|
+
debug: "debug";
|
|
442
|
+
info: "info";
|
|
443
|
+
warn: "warn";
|
|
444
|
+
}>>;
|
|
445
|
+
allowedUsers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
446
|
+
maxOutputLength: z.ZodDefault<z.ZodNumber>;
|
|
447
|
+
debounceMs: z.ZodDefault<z.ZodNumber>;
|
|
448
|
+
defaultMachine: z.ZodOptional<z.ZodString>;
|
|
449
|
+
}, z.core.$strip>;
|
|
450
|
+
type BotSettings = z.infer<typeof BotSettingsSchema>;
|
|
451
|
+
declare function loadSettings(env?: NodeJS.ProcessEnv): BotSettings;
|
|
452
|
+
|
|
453
|
+
type TelegramOptions = TelegramChannelOptions;
|
|
454
|
+
type DiscordOptions = DiscordChannelOptions;
|
|
455
|
+
type SlackOptions = SlackChannelOptions;
|
|
456
|
+
interface HubOptions {
|
|
457
|
+
/** CMDOP cloud API key. If omitted, uses local IPC connection. */
|
|
458
|
+
apiKey?: string;
|
|
459
|
+
/** Connect to local IPC agent instead of cloud. Default: false unless apiKey is absent. */
|
|
460
|
+
local?: boolean;
|
|
461
|
+
/** Default machine hostname for all operations. */
|
|
462
|
+
defaultMachine?: string;
|
|
463
|
+
/** Permission store for user levels. Default: in-memory. */
|
|
464
|
+
permissionStore?: PermissionStoreProtocol;
|
|
465
|
+
/** Admin user IDs (always have ADMIN permission). */
|
|
466
|
+
adminUsers?: string[];
|
|
467
|
+
/** Logger instance. Default: stderr JSON logger at info level. */
|
|
468
|
+
logger?: LoggerProtocol;
|
|
469
|
+
/** Bot settings. Default: loaded from environment variables. */
|
|
470
|
+
settings?: Partial<BotSettings>;
|
|
471
|
+
/**
|
|
472
|
+
* Whether a channel start() failure should be treated as fatal (throws) or
|
|
473
|
+
* isolated (logs error and continues with remaining channels).
|
|
474
|
+
* Default: 'isolated'
|
|
475
|
+
*/
|
|
476
|
+
channelStartMode?: 'strict' | 'isolated';
|
|
477
|
+
}
|
|
478
|
+
/** Per-channel runtime status */
|
|
479
|
+
type ChannelStatus = 'pending' | 'running' | 'failed' | 'stopped';
|
|
480
|
+
declare class IntegrationHub {
|
|
481
|
+
private readonly _client;
|
|
482
|
+
private readonly _logger;
|
|
483
|
+
private readonly _settings;
|
|
484
|
+
private readonly channels;
|
|
485
|
+
private readonly channelStatus;
|
|
486
|
+
private readonly _dispatcher;
|
|
487
|
+
private readonly _permissions;
|
|
488
|
+
private readonly channelStartMode;
|
|
489
|
+
private started;
|
|
490
|
+
private constructor();
|
|
491
|
+
/**
|
|
492
|
+
* Create and configure an IntegrationHub.
|
|
493
|
+
* Registers default handlers: exec, agent, files, help.
|
|
494
|
+
*/
|
|
495
|
+
static create(options?: HubOptions): Promise<IntegrationHub>;
|
|
496
|
+
registerChannel(channel: ChannelProtocol): this;
|
|
497
|
+
/**
|
|
498
|
+
* Convenience: create and register a TelegramChannel without importing it manually.
|
|
499
|
+
* Requires `grammy` to be installed.
|
|
500
|
+
*/
|
|
501
|
+
addTelegram(options: TelegramOptions): Promise<this>;
|
|
502
|
+
/**
|
|
503
|
+
* Convenience: create and register a DiscordChannel without importing it manually.
|
|
504
|
+
* Requires `discord.js`, `@discordjs/rest`, and `@discordjs/builders` to be installed.
|
|
505
|
+
*/
|
|
506
|
+
addDiscord(options: DiscordOptions): Promise<this>;
|
|
507
|
+
/**
|
|
508
|
+
* Convenience: create and register a SlackChannel without importing it manually.
|
|
509
|
+
* Requires `@slack/bolt` and `@slack/web-api` to be installed.
|
|
510
|
+
*/
|
|
511
|
+
addSlack(options: SlackOptions): Promise<this>;
|
|
512
|
+
registerHandler(handler: HandlerProtocol): this;
|
|
513
|
+
start(): Promise<void>;
|
|
514
|
+
stop(): Promise<void>;
|
|
515
|
+
/**
|
|
516
|
+
* Link two platform-scoped user IDs to share the same permission identity.
|
|
517
|
+
*
|
|
518
|
+
* Example — a user's Telegram ID and Discord ID map to the same permissions:
|
|
519
|
+
* hub.linkIdentities('telegram', '12345', 'discord', '67890');
|
|
520
|
+
*
|
|
521
|
+
* After linking, granting EXECUTE to the Telegram user also grants it to the
|
|
522
|
+
* Discord user (and vice versa), because both resolve to the same canonical ID.
|
|
523
|
+
*/
|
|
524
|
+
linkIdentities(channelA: string, userIdA: string, channelB: string, userIdB: string): void;
|
|
525
|
+
/** Get a registered channel by its ID, or undefined if not found. */
|
|
526
|
+
getChannel(id: string): ChannelProtocol | undefined;
|
|
527
|
+
/** IDs of all registered channels. */
|
|
528
|
+
get channelIds(): string[];
|
|
529
|
+
/** Total number of registered channels. */
|
|
530
|
+
get channelCount(): number;
|
|
531
|
+
/** IDs of channels that started successfully. */
|
|
532
|
+
get runningChannelIds(): string[];
|
|
533
|
+
/** IDs of channels that failed to start. */
|
|
534
|
+
get failedChannelIds(): string[];
|
|
535
|
+
/** Runtime status of a specific channel. */
|
|
536
|
+
getChannelStatus(id: string): ChannelStatus | undefined;
|
|
537
|
+
/** Read-only access to the CMDOP client (for advanced usage in handlers). */
|
|
538
|
+
get cmdop(): CMDOPClient;
|
|
539
|
+
get settings(): BotSettings;
|
|
540
|
+
get isStarted(): boolean;
|
|
541
|
+
/** Read-only access to the permission manager (for programmatic grants). */
|
|
542
|
+
get permissions(): PermissionManager;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
declare abstract class BaseHandler implements HandlerProtocol {
|
|
546
|
+
protected readonly client: CMDOPClient;
|
|
547
|
+
protected readonly logger: LoggerProtocol;
|
|
548
|
+
abstract readonly name: string;
|
|
549
|
+
abstract readonly description: string;
|
|
550
|
+
abstract readonly usage: string;
|
|
551
|
+
abstract readonly requiredPermission: PermissionLevel;
|
|
552
|
+
protected constructor(client: CMDOPClient, logger: LoggerProtocol);
|
|
553
|
+
abstract handle(ctx: CommandContext): Promise<HandlerResult>;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
557
|
+
declare function createLogger(level?: LogLevel): LoggerProtocol;
|
|
558
|
+
|
|
559
|
+
declare const UserSessionSchema: z.ZodObject<{
|
|
560
|
+
userId: z.ZodString;
|
|
561
|
+
currentMachine: z.ZodOptional<z.ZodString>;
|
|
562
|
+
lastCommand: z.ZodOptional<z.ZodString>;
|
|
563
|
+
updatedAt: z.ZodCoercedDate<unknown>;
|
|
564
|
+
}, z.core.$strip>;
|
|
565
|
+
type UserSession = z.infer<typeof UserSessionSchema>;
|
|
566
|
+
declare const MachineSessionSchema: z.ZodObject<{
|
|
567
|
+
sessionId: z.ZodString;
|
|
568
|
+
hostname: z.ZodOptional<z.ZodString>;
|
|
569
|
+
status: z.ZodEnum<{
|
|
570
|
+
unknown: "unknown";
|
|
571
|
+
online: "online";
|
|
572
|
+
offline: "offline";
|
|
573
|
+
}>;
|
|
574
|
+
connectedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
575
|
+
}, z.core.$strip>;
|
|
576
|
+
type MachineSession = z.infer<typeof MachineSessionSchema>;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* TokenBuffer — debounced flush for streaming tokens to rate-limited platforms.
|
|
580
|
+
*
|
|
581
|
+
* Telegram/Discord enforce ~1 message edit per second per chat.
|
|
582
|
+
* Accumulate tokens and flush on a timer to avoid 429 errors.
|
|
583
|
+
*/
|
|
584
|
+
declare class TokenBuffer {
|
|
585
|
+
private readonly flush;
|
|
586
|
+
private readonly debounceMs;
|
|
587
|
+
private buffer;
|
|
588
|
+
private timer;
|
|
589
|
+
private stopped;
|
|
590
|
+
constructor(flush: (text: string) => Promise<void>, debounceMs?: number);
|
|
591
|
+
/**
|
|
592
|
+
* Append a token. Schedules a flush if not already scheduled.
|
|
593
|
+
* No-op after drain() is called.
|
|
594
|
+
*/
|
|
595
|
+
append(token: string): void;
|
|
596
|
+
private scheduleFlush;
|
|
597
|
+
/**
|
|
598
|
+
* Cancel pending timer, flush remaining content immediately, stop accepting tokens.
|
|
599
|
+
* Always call this after the stream ends.
|
|
600
|
+
*/
|
|
601
|
+
drain(): Promise<void>;
|
|
602
|
+
get bufferedLength(): number;
|
|
603
|
+
get isStopped(): boolean;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Slack native streaming via chat.startStream / appendStream / stopStream.
|
|
608
|
+
*
|
|
609
|
+
* Unlike Telegram/Discord (which need TokenBuffer debouncing), Slack provides
|
|
610
|
+
* first-class streaming primitives — no debounce required.
|
|
611
|
+
*
|
|
612
|
+
* The streaming protocol uses the message `ts` (timestamp) as the handle:
|
|
613
|
+
* 1. startStream() → returns { ts, channel }
|
|
614
|
+
* 2. appendStream({ channel, ts, markdown_text })
|
|
615
|
+
* 3. stopStream({ channel, ts })
|
|
616
|
+
*
|
|
617
|
+
* Usage:
|
|
618
|
+
* const stream = await SlackStream.start(client, channel, threadTs);
|
|
619
|
+
* await stream.append('token...');
|
|
620
|
+
* await stream.finish();
|
|
621
|
+
*/
|
|
622
|
+
declare class SlackStream {
|
|
623
|
+
private readonly client;
|
|
624
|
+
private readonly channel;
|
|
625
|
+
private readonly ts;
|
|
626
|
+
private stopped;
|
|
627
|
+
private constructor();
|
|
628
|
+
/**
|
|
629
|
+
* Start a new streaming message in the given channel/thread.
|
|
630
|
+
*/
|
|
631
|
+
static start(client: WebClient, channel: string, threadTs?: string): Promise<SlackStream>;
|
|
632
|
+
/**
|
|
633
|
+
* Append a token/chunk to the in-progress stream.
|
|
634
|
+
* Silently ignores appends after finish().
|
|
635
|
+
*/
|
|
636
|
+
append(text: string): Promise<void>;
|
|
637
|
+
/**
|
|
638
|
+
* Stop the stream and finalise the message.
|
|
639
|
+
* Idempotent — safe to call multiple times.
|
|
640
|
+
*/
|
|
641
|
+
finish(): Promise<void>;
|
|
642
|
+
get isFinished(): boolean;
|
|
643
|
+
/** The Slack message ts that identifies this stream. */
|
|
644
|
+
get messageTs(): string;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
interface TerminalHandlerConfig {
|
|
648
|
+
maxOutputLength?: number;
|
|
649
|
+
defaultTimeoutMs?: number;
|
|
650
|
+
}
|
|
651
|
+
declare class TerminalHandler extends BaseHandler {
|
|
652
|
+
readonly name = "exec";
|
|
653
|
+
readonly description = "Execute a shell command on the remote machine";
|
|
654
|
+
readonly usage = "/exec <command>";
|
|
655
|
+
readonly requiredPermission: "EXECUTE";
|
|
656
|
+
private readonly maxOutput;
|
|
657
|
+
private readonly timeoutMs;
|
|
658
|
+
constructor(client: CMDOPClient, logger: LoggerProtocol, config?: TerminalHandlerConfig);
|
|
659
|
+
handle(ctx: CommandContext): Promise<HandlerResult>;
|
|
660
|
+
private formatResult;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
interface AgentHandlerConfig {
|
|
664
|
+
timeoutSeconds?: number;
|
|
665
|
+
maxOutputLength?: number;
|
|
666
|
+
}
|
|
667
|
+
declare class AgentHandler extends BaseHandler {
|
|
668
|
+
readonly name = "agent";
|
|
669
|
+
readonly description = "Run an AI agent prompt on the remote machine";
|
|
670
|
+
readonly usage = "/agent <prompt>";
|
|
671
|
+
readonly requiredPermission: "EXECUTE";
|
|
672
|
+
private readonly timeout;
|
|
673
|
+
private readonly maxOutput;
|
|
674
|
+
constructor(client: CMDOPClient, logger: LoggerProtocol, config?: AgentHandlerConfig);
|
|
675
|
+
handle(ctx: CommandContext): Promise<HandlerResult>;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
interface FilesHandlerConfig {
|
|
679
|
+
maxEntries?: number;
|
|
680
|
+
maxFileSize?: number;
|
|
681
|
+
}
|
|
682
|
+
declare class FilesHandler extends BaseHandler {
|
|
683
|
+
readonly name = "files";
|
|
684
|
+
readonly description = "List files in a directory on the remote machine";
|
|
685
|
+
readonly usage = "/files <path>";
|
|
686
|
+
readonly requiredPermission: "READ";
|
|
687
|
+
private readonly maxEntries;
|
|
688
|
+
constructor(client: CMDOPClient, logger: LoggerProtocol, config?: FilesHandlerConfig);
|
|
689
|
+
handle(ctx: CommandContext): Promise<HandlerResult>;
|
|
690
|
+
private handleList;
|
|
691
|
+
private handleRead;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
interface HelpHandlerConfig {
|
|
695
|
+
getCommands: () => CommandInfo[];
|
|
696
|
+
}
|
|
697
|
+
declare class HelpHandler extends BaseHandler {
|
|
698
|
+
private readonly config;
|
|
699
|
+
readonly name = "help";
|
|
700
|
+
readonly description = "Show available commands and their usage";
|
|
701
|
+
readonly usage = "/help";
|
|
702
|
+
readonly requiredPermission: "NONE";
|
|
703
|
+
constructor(client: CMDOPClient, logger: LoggerProtocol, config: HelpHandlerConfig);
|
|
704
|
+
handle(_ctx: CommandContext): Promise<HandlerResult>;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
interface DemoChannelOptions {
|
|
708
|
+
/** Called for every outgoing message (default: JSON to stdout) */
|
|
709
|
+
onOutput?: (text: string, message: OutgoingMessage) => void;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* DemoChannel — stdio-based channel for local testing and CLI usage.
|
|
713
|
+
* No real platform; messages are injected programmatically via injectMessage().
|
|
714
|
+
*/
|
|
715
|
+
declare class DemoChannel extends BaseChannel {
|
|
716
|
+
private messageHandlers;
|
|
717
|
+
private readonly onOutput;
|
|
718
|
+
constructor(permissions: PermissionManager, dispatcher: MessageDispatcher, logger: LoggerProtocol, options?: DemoChannelOptions);
|
|
719
|
+
start(): Promise<void>;
|
|
720
|
+
stop(): Promise<void>;
|
|
721
|
+
send(_userId: string, message: OutgoingMessage): Promise<void>;
|
|
722
|
+
onMessage(handler: (msg: IncomingMessage) => Promise<void>): void;
|
|
723
|
+
/**
|
|
724
|
+
* Inject a message as if it came from a real platform user.
|
|
725
|
+
* Primarily used in tests and CLI demos.
|
|
726
|
+
*/
|
|
727
|
+
injectMessage(input: {
|
|
728
|
+
userId?: string;
|
|
729
|
+
text: string;
|
|
730
|
+
}): Promise<void>;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
declare class TelegramFormatter implements FormatterProtocol {
|
|
734
|
+
/**
|
|
735
|
+
* Escape plain text for safe inclusion in MarkdownV2 messages.
|
|
736
|
+
*/
|
|
737
|
+
formatText(text: string): string;
|
|
738
|
+
/**
|
|
739
|
+
* Wrap code in a triple-backtick code block.
|
|
740
|
+
* Truncates if over Telegram's limit.
|
|
741
|
+
*/
|
|
742
|
+
formatCode(code: string, language?: string): string;
|
|
743
|
+
/**
|
|
744
|
+
* Format a BotError into a user-friendly Telegram message.
|
|
745
|
+
*/
|
|
746
|
+
formatError(error: BotError): string;
|
|
747
|
+
/**
|
|
748
|
+
* Format a file listing entry line.
|
|
749
|
+
*/
|
|
750
|
+
formatFileEntry(name: string, isDir: boolean, size?: number): string;
|
|
751
|
+
private escapeInline;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
interface KeyboardAction {
|
|
755
|
+
label: string;
|
|
756
|
+
command: string;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Build a simple inline keyboard row from command shortcuts.
|
|
760
|
+
*/
|
|
761
|
+
declare function buildInlineKeyboard(actions: KeyboardAction[]): {
|
|
762
|
+
inline_keyboard: InlineKeyboardButton[][];
|
|
763
|
+
};
|
|
764
|
+
/**
|
|
765
|
+
* Standard machine selection keyboard.
|
|
766
|
+
*/
|
|
767
|
+
declare function buildMachineKeyboard(machines: string[]): {
|
|
768
|
+
inline_keyboard: InlineKeyboardButton[][];
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
declare class DiscordFormatter implements FormatterProtocol {
|
|
772
|
+
/**
|
|
773
|
+
* Escape Discord Markdown in user-supplied text.
|
|
774
|
+
* Escapes: _ * ~ ` | > \
|
|
775
|
+
*/
|
|
776
|
+
formatText(text: string): string;
|
|
777
|
+
/**
|
|
778
|
+
* Wrap code in a triple-backtick code block.
|
|
779
|
+
* Truncates if over Discord's limit.
|
|
780
|
+
*/
|
|
781
|
+
formatCode(code: string, language?: string): string;
|
|
782
|
+
/**
|
|
783
|
+
* Format a BotError into a user-friendly Discord message.
|
|
784
|
+
*/
|
|
785
|
+
formatError(error: BotError): string;
|
|
786
|
+
/**
|
|
787
|
+
* Format a file listing entry line.
|
|
788
|
+
*/
|
|
789
|
+
formatFileEntry(name: string, isDir: boolean, size?: number): string;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Discord slash command definitions for CMDOP bot.
|
|
794
|
+
*
|
|
795
|
+
* All commands use the Interactions API (slash commands).
|
|
796
|
+
* Message content intent is NOT required — preferred since 2022.
|
|
797
|
+
*/
|
|
798
|
+
declare const DISCORD_COMMANDS: RESTPostAPIChatInputApplicationCommandsJSONBody[];
|
|
799
|
+
/** All slash command names — used to build the prefix-based text fallback */
|
|
800
|
+
declare const DISCORD_COMMAND_NAMES: string[];
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Slack mrkdwn formatter.
|
|
804
|
+
*
|
|
805
|
+
* Slack uses its own "mrkdwn" dialect (not standard Markdown).
|
|
806
|
+
* Bold: *text* Italic: _text_ Code: `text` Code block: ```text```
|
|
807
|
+
* No escaping of . - ! etc. Special chars are: * _ ` & < >
|
|
808
|
+
*/
|
|
809
|
+
declare const SLACK_MAX_TEXT_LENGTH = 3000;
|
|
810
|
+
declare class SlackFormatter implements FormatterProtocol {
|
|
811
|
+
/**
|
|
812
|
+
* Escape mrkdwn special characters in plain text.
|
|
813
|
+
* Slack requires escaping: & < >
|
|
814
|
+
* Ampersand must be escaped first to avoid double-escaping.
|
|
815
|
+
*/
|
|
816
|
+
formatText(text: string): string;
|
|
817
|
+
/**
|
|
818
|
+
* Wrap code in a triple-backtick code block.
|
|
819
|
+
* Truncates if over Slack's limit.
|
|
820
|
+
*/
|
|
821
|
+
formatCode(code: string, language?: string): string;
|
|
822
|
+
/**
|
|
823
|
+
* Format a BotError into a user-friendly Slack mrkdwn message.
|
|
824
|
+
*/
|
|
825
|
+
formatError(error: BotError): string;
|
|
826
|
+
/**
|
|
827
|
+
* Format a file listing entry line.
|
|
828
|
+
*/
|
|
829
|
+
formatFileEntry(name: string, isDir: boolean, size?: number): string;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Slack Block Kit builders for structured CMDOP output.
|
|
834
|
+
*
|
|
835
|
+
* Blocks are the Slack equivalent of Discord embeds.
|
|
836
|
+
* Used for file listings, machine tables, and error cards.
|
|
837
|
+
*
|
|
838
|
+
* Reference: https://api.slack.com/block-kit
|
|
839
|
+
*/
|
|
840
|
+
interface SlackBlock {
|
|
841
|
+
type: string;
|
|
842
|
+
[key: string]: unknown;
|
|
843
|
+
}
|
|
844
|
+
declare function sectionBlock(text: string): SlackBlock;
|
|
845
|
+
declare function headerBlock(text: string): SlackBlock;
|
|
846
|
+
declare function dividerBlock(): SlackBlock;
|
|
847
|
+
declare function contextBlock(text: string): SlackBlock;
|
|
848
|
+
interface FileEntry {
|
|
849
|
+
name: string;
|
|
850
|
+
isDir: boolean;
|
|
851
|
+
size?: number;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Build a Block Kit representation of a file listing.
|
|
855
|
+
* Splits into chunks of up to `chunkSize` entries per section block
|
|
856
|
+
* to respect Slack's 3000-char section limit.
|
|
857
|
+
*/
|
|
858
|
+
declare function fileListBlocks(path: string, entries: FileEntry[], chunkSize?: number): SlackBlock[];
|
|
859
|
+
interface MachineStatus {
|
|
860
|
+
hostname: string;
|
|
861
|
+
online: boolean;
|
|
862
|
+
label?: string;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Build a Block Kit machine status table.
|
|
866
|
+
*/
|
|
867
|
+
declare function machineStatusBlocks(machines: MachineStatus[]): SlackBlock[];
|
|
868
|
+
/**
|
|
869
|
+
* Build a Block Kit error card with context.
|
|
870
|
+
*/
|
|
871
|
+
declare function errorBlocks(message: string, detail?: string): SlackBlock[];
|
|
872
|
+
|
|
873
|
+
export { AgentHandler, type AgentHandlerConfig, type Attachment, AttachmentSchema, BaseChannel, BaseHandler, BotError, type BotSettings, BotSettingsSchema, type BotUser, BotUserSchema, CMDOPError, CMDOPTimeoutError, ChannelError, type ChannelProtocol, type ChannelStatus, type CodeMessage, CodeMessageSchema, CommandArgsError, type CommandContext, type CommandInfo, CommandNotFoundError, ConfigError, DISCORD_COMMANDS, DISCORD_COMMAND_NAMES, DemoChannel, type DemoChannelOptions, DiscordChannel, type DiscordChannelOptions, DiscordError, DiscordFormatter, type ErrorMessage, ErrorMessageSchema, ExecutionTimeoutError, type FileEntry, FilesHandler, type FilesHandlerConfig, type FormatterProtocol, HandlerError, type HandlerProtocol, type HandlerResult, HelpHandler, type HelpHandlerConfig, type HubOptions, IdentityMap, InMemoryPermissionStore, type IncomingMessage, IncomingMessageSchema, IntegrationHub, type KeyboardAction, type LoggerProtocol, MachineNotFoundError, MachineOfflineError, type MachineSession, MachineSessionSchema, type MachineStatus, MessageDispatcher, type OutgoingMessage, OutgoingMessageSchema, PERMISSION_ORDER, type ParsedCommand, ParsedCommandSchema, PermissionDeniedError, type PermissionLevel, PermissionLevelSchema, PermissionManager, type PermissionStoreProtocol, RateLimitError, SLACK_MAX_TEXT_LENGTH, type SlackBlock, SlackChannel, type SlackChannelOptions, SlackError, SlackFormatter, SlackStream, TeamsError, TelegramChannel, type TelegramChannelOptions, TelegramError, TelegramFormatter, TerminalHandler, type TerminalHandlerConfig, type TextMessage, TextMessageSchema, TokenBuffer, type UserIdentity, UserIdentitySchema, type UserSession, UserSessionSchema, buildInlineKeyboard, buildMachineKeyboard, contextBlock, createLogger, dividerBlock, err, errorBlocks, fileListBlocks, headerBlock, loadSettings, machineStatusBlocks, ok, parseCommand, sectionBlock };
|