@cmdop/bot 2026.2.26 → 2026.3.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/README.md +10 -3
- package/dist/index.cjs +380 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -63
- package/dist/index.d.ts +100 -63
- package/dist/index.js +380 -171
- package/dist/index.js.map +1 -1
- package/examples/discord.ts +1 -1
- package/examples/multi-channel.ts +1 -1
- package/examples/slack.ts +1 -1
- package/examples/telegram.ts +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Bot } from 'grammy';
|
|
|
3
3
|
import { Client, GatewayIntentBits, Events, REST, Routes } from 'discord.js';
|
|
4
4
|
import { App, Assistant } from '@slack/bolt';
|
|
5
5
|
import { CMDOPClient } from '@cmdop/node';
|
|
6
|
+
import { createConsola } from 'consola';
|
|
6
7
|
|
|
7
8
|
var __create = Object.create;
|
|
8
9
|
var __defProp = Object.defineProperty;
|
|
@@ -159,6 +160,95 @@ var init_errors = __esm({
|
|
|
159
160
|
}
|
|
160
161
|
});
|
|
161
162
|
|
|
163
|
+
// src/models/command.ts
|
|
164
|
+
var command_exports = {};
|
|
165
|
+
__export(command_exports, {
|
|
166
|
+
ParsedCommandSchema: () => ParsedCommandSchema,
|
|
167
|
+
parseCommand: () => parseCommand
|
|
168
|
+
});
|
|
169
|
+
function parseCommand(text) {
|
|
170
|
+
const trimmed = text.trim();
|
|
171
|
+
const match = /^[/!](\w+)(?:\s+(.*))?$/s.exec(trimmed);
|
|
172
|
+
if (!match) return null;
|
|
173
|
+
const name = (match[1] ?? "").toLowerCase();
|
|
174
|
+
const rest = (match[2] ?? "").trim();
|
|
175
|
+
const args = rest ? rest.split(/\s+/) : [];
|
|
176
|
+
return ParsedCommandSchema.parse({ name, args, rawText: trimmed });
|
|
177
|
+
}
|
|
178
|
+
var ParsedCommandSchema;
|
|
179
|
+
var init_command = __esm({
|
|
180
|
+
"src/models/command.ts"() {
|
|
181
|
+
ParsedCommandSchema = z.object({
|
|
182
|
+
name: z.string().min(1),
|
|
183
|
+
args: z.array(z.string()),
|
|
184
|
+
rawText: z.string()
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// src/core/base-channel.ts
|
|
190
|
+
var BaseChannel;
|
|
191
|
+
var init_base_channel = __esm({
|
|
192
|
+
"src/core/base-channel.ts"() {
|
|
193
|
+
init_command();
|
|
194
|
+
init_errors();
|
|
195
|
+
BaseChannel = class {
|
|
196
|
+
constructor(id, name, permissions, dispatcher, logger) {
|
|
197
|
+
this.id = id;
|
|
198
|
+
this.name = name;
|
|
199
|
+
this.permissions = permissions;
|
|
200
|
+
this.dispatcher = dispatcher;
|
|
201
|
+
this.logger = logger;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Process an incoming message: parse command → check permission → dispatch → send result.
|
|
205
|
+
* Plain text (non-command) is routed to the agent handler automatically.
|
|
206
|
+
* Channels call this from their platform event handler.
|
|
207
|
+
*/
|
|
208
|
+
async processMessage(msg) {
|
|
209
|
+
const parsed = parseCommand(msg.text);
|
|
210
|
+
const ctx = parsed ? {
|
|
211
|
+
userId: msg.userId,
|
|
212
|
+
command: parsed.name,
|
|
213
|
+
args: parsed.args,
|
|
214
|
+
channelId: msg.channelId,
|
|
215
|
+
message: msg
|
|
216
|
+
} : {
|
|
217
|
+
userId: msg.userId,
|
|
218
|
+
command: "agent",
|
|
219
|
+
args: [msg.text],
|
|
220
|
+
channelId: msg.channelId,
|
|
221
|
+
message: msg
|
|
222
|
+
};
|
|
223
|
+
let result;
|
|
224
|
+
try {
|
|
225
|
+
result = await this.dispatcher.dispatch(ctx);
|
|
226
|
+
} catch (err2) {
|
|
227
|
+
const botErr = err2 instanceof BotError ? err2 : new BotError("Unexpected error", { cause: err2 instanceof Error ? err2 : void 0 });
|
|
228
|
+
result = { ok: false, error: botErr };
|
|
229
|
+
}
|
|
230
|
+
if (result.ok) {
|
|
231
|
+
await this.send(msg.userId, result.value);
|
|
232
|
+
} else {
|
|
233
|
+
await this.send(msg.userId, {
|
|
234
|
+
type: "error",
|
|
235
|
+
message: this.formatErrorMessage(result.error)
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
formatErrorMessage(error) {
|
|
240
|
+
if (error instanceof CommandNotFoundError) {
|
|
241
|
+
return `Unknown command. Type /help for available commands.`;
|
|
242
|
+
}
|
|
243
|
+
return error.message;
|
|
244
|
+
}
|
|
245
|
+
logEvent(event, meta = {}) {
|
|
246
|
+
this.logger.info(`[${this.name}] ${event}`, { channel: this.id, ...meta });
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
162
252
|
// ../../node_modules/.pnpm/bottleneck@2.19.5/node_modules/bottleneck/lib/parser.js
|
|
163
253
|
var require_parser = __commonJS({
|
|
164
254
|
"../../node_modules/.pnpm/bottleneck@2.19.5/node_modules/bottleneck/lib/parser.js"(exports2) {
|
|
@@ -3009,9 +3099,9 @@ var require_lib = __commonJS({
|
|
|
3009
3099
|
}
|
|
3010
3100
|
});
|
|
3011
3101
|
|
|
3012
|
-
// ../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.
|
|
3102
|
+
// ../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.1/node_modules/@grammyjs/transformer-throttler/dist/deps.node.js
|
|
3013
3103
|
var require_deps_node = __commonJS({
|
|
3014
|
-
"../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.
|
|
3104
|
+
"../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.1/node_modules/@grammyjs/transformer-throttler/dist/deps.node.js"(exports2) {
|
|
3015
3105
|
var __importDefault = exports2 && exports2.__importDefault || function(mod2) {
|
|
3016
3106
|
return mod2 && mod2.__esModule ? mod2 : { "default": mod2 };
|
|
3017
3107
|
};
|
|
@@ -3024,9 +3114,9 @@ var require_deps_node = __commonJS({
|
|
|
3024
3114
|
}
|
|
3025
3115
|
});
|
|
3026
3116
|
|
|
3027
|
-
// ../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.
|
|
3117
|
+
// ../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.1/node_modules/@grammyjs/transformer-throttler/dist/mod.js
|
|
3028
3118
|
var require_mod = __commonJS({
|
|
3029
|
-
"../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.
|
|
3119
|
+
"../../node_modules/.pnpm/@grammyjs+transformer-throttler@1.2.1_grammy@1.40.1/node_modules/@grammyjs/transformer-throttler/dist/mod.js"(exports2) {
|
|
3030
3120
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3031
3121
|
exports2.bypassThrottler = exports2.apiThrottler = exports2.BottleneckStrategy = void 0;
|
|
3032
3122
|
var deps_node_js_1 = require_deps_node();
|
|
@@ -3089,90 +3179,10 @@ var require_mod = __commonJS({
|
|
|
3089
3179
|
}
|
|
3090
3180
|
});
|
|
3091
3181
|
|
|
3092
|
-
// src/models/command.ts
|
|
3093
|
-
var command_exports = {};
|
|
3094
|
-
__export(command_exports, {
|
|
3095
|
-
ParsedCommandSchema: () => ParsedCommandSchema,
|
|
3096
|
-
parseCommand: () => parseCommand
|
|
3097
|
-
});
|
|
3098
|
-
function parseCommand(text) {
|
|
3099
|
-
const trimmed = text.trim();
|
|
3100
|
-
const match = /^[/!](\w+)(?:\s+(.*))?$/s.exec(trimmed);
|
|
3101
|
-
if (!match) return null;
|
|
3102
|
-
const name = (match[1] ?? "").toLowerCase();
|
|
3103
|
-
const rest = (match[2] ?? "").trim();
|
|
3104
|
-
const args = rest ? rest.split(/\s+/) : [];
|
|
3105
|
-
return ParsedCommandSchema.parse({ name, args, rawText: trimmed });
|
|
3106
|
-
}
|
|
3107
|
-
var ParsedCommandSchema;
|
|
3108
|
-
var init_command = __esm({
|
|
3109
|
-
"src/models/command.ts"() {
|
|
3110
|
-
ParsedCommandSchema = z.object({
|
|
3111
|
-
name: z.string().min(1),
|
|
3112
|
-
args: z.array(z.string()),
|
|
3113
|
-
rawText: z.string()
|
|
3114
|
-
});
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
|
|
3118
|
-
// src/core/base-channel.ts
|
|
3119
|
-
var BaseChannel;
|
|
3120
|
-
var init_base_channel = __esm({
|
|
3121
|
-
"src/core/base-channel.ts"() {
|
|
3122
|
-
init_command();
|
|
3123
|
-
init_errors();
|
|
3124
|
-
BaseChannel = class {
|
|
3125
|
-
constructor(id, name, permissions, dispatcher, logger) {
|
|
3126
|
-
this.id = id;
|
|
3127
|
-
this.name = name;
|
|
3128
|
-
this.permissions = permissions;
|
|
3129
|
-
this.dispatcher = dispatcher;
|
|
3130
|
-
this.logger = logger;
|
|
3131
|
-
}
|
|
3132
|
-
/**
|
|
3133
|
-
* Process an incoming message: parse command → check permission → dispatch → send result.
|
|
3134
|
-
* Channels call this from their platform event handler.
|
|
3135
|
-
*/
|
|
3136
|
-
async processMessage(msg) {
|
|
3137
|
-
const parsed = parseCommand(msg.text);
|
|
3138
|
-
if (!parsed) return;
|
|
3139
|
-
const ctx = {
|
|
3140
|
-
userId: msg.userId,
|
|
3141
|
-
command: parsed.name,
|
|
3142
|
-
args: parsed.args,
|
|
3143
|
-
channelId: msg.channelId,
|
|
3144
|
-
message: msg
|
|
3145
|
-
};
|
|
3146
|
-
let result;
|
|
3147
|
-
try {
|
|
3148
|
-
result = await this.dispatcher.dispatch(ctx);
|
|
3149
|
-
} catch (err2) {
|
|
3150
|
-
const botErr = err2 instanceof BotError ? err2 : new BotError("Unexpected error", { cause: err2 instanceof Error ? err2 : void 0 });
|
|
3151
|
-
result = { ok: false, error: botErr };
|
|
3152
|
-
}
|
|
3153
|
-
if (result.ok) {
|
|
3154
|
-
await this.send(msg.userId, result.value);
|
|
3155
|
-
} else {
|
|
3156
|
-
await this.send(msg.userId, {
|
|
3157
|
-
type: "error",
|
|
3158
|
-
message: this.formatErrorMessage(result.error)
|
|
3159
|
-
});
|
|
3160
|
-
}
|
|
3161
|
-
}
|
|
3162
|
-
formatErrorMessage(error) {
|
|
3163
|
-
if (error instanceof CommandNotFoundError) {
|
|
3164
|
-
return `Unknown command. Type /help for available commands.`;
|
|
3165
|
-
}
|
|
3166
|
-
return error.message;
|
|
3167
|
-
}
|
|
3168
|
-
logEvent(event, meta = {}) {
|
|
3169
|
-
this.logger.info(`[${this.name}] ${event}`, { channel: this.id, ...meta });
|
|
3170
|
-
}
|
|
3171
|
-
};
|
|
3172
|
-
}
|
|
3173
|
-
});
|
|
3174
|
-
|
|
3175
3182
|
// src/channels/telegram/formatter.ts
|
|
3183
|
+
function escapeHtml(text) {
|
|
3184
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3185
|
+
}
|
|
3176
3186
|
function formatBytes(bytes) {
|
|
3177
3187
|
if (bytes < 1024) return `${bytes}B`;
|
|
3178
3188
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}K`;
|
|
@@ -3242,6 +3252,51 @@ ${this.formatText(error.message)}`;
|
|
|
3242
3252
|
const sizeStr = size !== void 0 && !isDir ? ` \\(${this.formatText(formatBytes(size))}\\)` : "";
|
|
3243
3253
|
return `${icon} \`${this.escapeInline(name)}\`${sizeStr}`;
|
|
3244
3254
|
}
|
|
3255
|
+
// ─── HTML formatting (preferred — avoids MarkdownV2 escaping hell) ────────
|
|
3256
|
+
/**
|
|
3257
|
+
* Escape HTML special chars, then convert basic markdown to HTML tags.
|
|
3258
|
+
* Handles: **bold**, `code`, ```code blocks```, _italic_
|
|
3259
|
+
*/
|
|
3260
|
+
formatTextHtml(text) {
|
|
3261
|
+
let html = escapeHtml(text);
|
|
3262
|
+
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, lang, code) => {
|
|
3263
|
+
const cls = lang ? ` class="language-${lang}"` : "";
|
|
3264
|
+
return `<pre><code${cls}>${code}</code></pre>`;
|
|
3265
|
+
});
|
|
3266
|
+
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
3267
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
3268
|
+
html = html.replace(/__(.+?)__/g, "<b>$1</b>");
|
|
3269
|
+
html = html.replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, "<i>$1</i>");
|
|
3270
|
+
html = html.replace(/(?<!\w)_([^_]+?)_(?!\w)/g, "<i>$1</i>");
|
|
3271
|
+
return html;
|
|
3272
|
+
}
|
|
3273
|
+
/**
|
|
3274
|
+
* Format code block as HTML <pre>.
|
|
3275
|
+
*/
|
|
3276
|
+
formatCodeHtml(code, language) {
|
|
3277
|
+
const escaped = escapeHtml(code);
|
|
3278
|
+
const cls = language ? ` class="language-${language}"` : "";
|
|
3279
|
+
const wrapped = `<pre><code${cls}>${escaped}</code></pre>`;
|
|
3280
|
+
if (wrapped.length <= TELEGRAM_MAX_MESSAGE_LENGTH) return wrapped;
|
|
3281
|
+
const overhead = 40 + (language?.length ?? 0);
|
|
3282
|
+
const maxCode = TELEGRAM_MAX_MESSAGE_LENGTH - overhead;
|
|
3283
|
+
return `<pre><code${cls}>${escaped.slice(0, maxCode)}
|
|
3284
|
+
\u2026(truncated)</code></pre>`;
|
|
3285
|
+
}
|
|
3286
|
+
/**
|
|
3287
|
+
* Format error message as HTML.
|
|
3288
|
+
*/
|
|
3289
|
+
formatErrorHtml(message) {
|
|
3290
|
+
return `\u274C <b>Error:</b> ${escapeHtml(message)}`;
|
|
3291
|
+
}
|
|
3292
|
+
/**
|
|
3293
|
+
* Format file entry as HTML.
|
|
3294
|
+
*/
|
|
3295
|
+
formatFileEntryHtml(name, isDir, size) {
|
|
3296
|
+
const icon = isDir ? "\u{1F4C1}" : "\u{1F4C4}";
|
|
3297
|
+
const sizeStr = size !== void 0 && !isDir ? ` (${formatBytes(size)})` : "";
|
|
3298
|
+
return `${icon} <code>${escapeHtml(name)}</code>${sizeStr}`;
|
|
3299
|
+
}
|
|
3245
3300
|
// Escape text that appears inside inline code (backtick context)
|
|
3246
3301
|
escapeInline(text) {
|
|
3247
3302
|
return text.replace(/`/g, "'");
|
|
@@ -3280,8 +3335,18 @@ var init_channel = __esm({
|
|
|
3280
3335
|
bot.on("message:text", async (ctx) => {
|
|
3281
3336
|
const msg = this.normalizeContext(ctx);
|
|
3282
3337
|
if (!msg) return;
|
|
3283
|
-
|
|
3284
|
-
|
|
3338
|
+
const typingInterval = setInterval(() => {
|
|
3339
|
+
ctx.api.sendChatAction(ctx.chat.id, "typing").catch(() => {
|
|
3340
|
+
});
|
|
3341
|
+
}, 4e3);
|
|
3342
|
+
await ctx.api.sendChatAction(ctx.chat.id, "typing").catch(() => {
|
|
3343
|
+
});
|
|
3344
|
+
try {
|
|
3345
|
+
for (const h3 of this.messageHandlers) await h3(msg);
|
|
3346
|
+
await this.processMessage(msg);
|
|
3347
|
+
} finally {
|
|
3348
|
+
clearInterval(typingInterval);
|
|
3349
|
+
}
|
|
3285
3350
|
});
|
|
3286
3351
|
bot.on("callback_query:data", async (ctx) => {
|
|
3287
3352
|
await ctx.answerCallbackQuery();
|
|
@@ -3329,20 +3394,18 @@ var init_channel = __esm({
|
|
|
3329
3394
|
try {
|
|
3330
3395
|
switch (message.type) {
|
|
3331
3396
|
case "text": {
|
|
3332
|
-
const text = this.truncate(this.formatter.
|
|
3333
|
-
await this.bot.api.sendMessage(chatId, text, { parse_mode: "
|
|
3397
|
+
const text = this.truncate(this.formatter.formatTextHtml(message.text));
|
|
3398
|
+
await this.bot.api.sendMessage(chatId, text, { parse_mode: "HTML" });
|
|
3334
3399
|
break;
|
|
3335
3400
|
}
|
|
3336
3401
|
case "code": {
|
|
3337
|
-
const code = this.formatter.
|
|
3338
|
-
await this.bot.api.sendMessage(chatId, code, { parse_mode: "
|
|
3402
|
+
const code = this.formatter.formatCodeHtml(message.code, message.language);
|
|
3403
|
+
await this.bot.api.sendMessage(chatId, code, { parse_mode: "HTML" });
|
|
3339
3404
|
break;
|
|
3340
3405
|
}
|
|
3341
3406
|
case "error": {
|
|
3342
|
-
const errText = this.formatter.
|
|
3343
|
-
|
|
3344
|
-
);
|
|
3345
|
-
await this.bot.api.sendMessage(chatId, errText, { parse_mode: "MarkdownV2" });
|
|
3407
|
+
const errText = this.formatter.formatErrorHtml(message.message);
|
|
3408
|
+
await this.bot.api.sendMessage(chatId, errText, { parse_mode: "HTML" });
|
|
3346
3409
|
break;
|
|
3347
3410
|
}
|
|
3348
3411
|
}
|
|
@@ -3369,7 +3432,7 @@ var init_channel = __esm({
|
|
|
3369
3432
|
}
|
|
3370
3433
|
truncate(text) {
|
|
3371
3434
|
if (text.length <= this.maxLength) return text;
|
|
3372
|
-
return text.slice(0, this.maxLength - 20) + "\n
|
|
3435
|
+
return text.slice(0, this.maxLength - 20) + "\n... <i>(truncated)</i>";
|
|
3373
3436
|
}
|
|
3374
3437
|
};
|
|
3375
3438
|
}
|
|
@@ -16291,7 +16354,7 @@ var init_channel3 = __esm({
|
|
|
16291
16354
|
{ title: "Get help", message: "/help" }
|
|
16292
16355
|
]
|
|
16293
16356
|
});
|
|
16294
|
-
await say("Hello! Use `/exec`, `/agent`, `/files`, or `/help`.");
|
|
16357
|
+
await say("Hello! Use `/exec`, `/agent`, `/skills`, `/files`, or `/help`.");
|
|
16295
16358
|
},
|
|
16296
16359
|
threadContextChanged: async ({ saveThreadContext }) => {
|
|
16297
16360
|
await saveThreadContext();
|
|
@@ -16578,21 +16641,34 @@ var PermissionManager = class {
|
|
|
16578
16641
|
return this.identityMap;
|
|
16579
16642
|
}
|
|
16580
16643
|
};
|
|
16581
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16644
|
+
var CONSOLA_LEVELS = {
|
|
16645
|
+
debug: 4,
|
|
16646
|
+
info: 3,
|
|
16647
|
+
warn: 2,
|
|
16648
|
+
error: 1
|
|
16649
|
+
};
|
|
16584
16650
|
function createLogger(level = "info") {
|
|
16585
|
-
const
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16651
|
+
const consola = createConsola({
|
|
16652
|
+
level: CONSOLA_LEVELS[level],
|
|
16653
|
+
formatOptions: {
|
|
16654
|
+
date: true,
|
|
16655
|
+
colors: true
|
|
16656
|
+
}
|
|
16657
|
+
}).withTag("bot");
|
|
16591
16658
|
return {
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16659
|
+
consola,
|
|
16660
|
+
debug(msg, meta) {
|
|
16661
|
+
meta ? consola.debug(msg, meta) : consola.debug(msg);
|
|
16662
|
+
},
|
|
16663
|
+
info(msg, meta) {
|
|
16664
|
+
meta ? consola.info(msg, meta) : consola.info(msg);
|
|
16665
|
+
},
|
|
16666
|
+
warn(msg, meta) {
|
|
16667
|
+
meta ? consola.warn(msg, meta) : consola.warn(msg);
|
|
16668
|
+
},
|
|
16669
|
+
error(msg, meta) {
|
|
16670
|
+
meta ? consola.error(msg, meta) : consola.error(msg);
|
|
16671
|
+
}
|
|
16596
16672
|
};
|
|
16597
16673
|
}
|
|
16598
16674
|
|
|
@@ -16699,7 +16775,21 @@ var AgentHandler = class extends BaseHandler {
|
|
|
16699
16775
|
const text = result.text.length > this.maxOutput ? result.text.slice(0, this.maxOutput) + "\n...(truncated)" : result.text;
|
|
16700
16776
|
return ok({ type: "text", text });
|
|
16701
16777
|
} catch (e3) {
|
|
16702
|
-
|
|
16778
|
+
const errMsg = e3 instanceof Error ? e3.message : String(e3);
|
|
16779
|
+
this.logger.error("Agent execution failed", { error: errMsg });
|
|
16780
|
+
if (errMsg.includes("session_id") || errMsg.includes("No active session")) {
|
|
16781
|
+
return err(new CMDOPError("Machine is offline or CMDOP agent is not running.\nhttps://cmdop.com/downloads/"));
|
|
16782
|
+
}
|
|
16783
|
+
if (errMsg.includes("context canceled") || errMsg.includes("CANCELLED")) {
|
|
16784
|
+
return err(new CMDOPError("Request was interrupted. Please try again."));
|
|
16785
|
+
}
|
|
16786
|
+
if (errMsg.includes("DEADLINE_EXCEEDED") || errMsg.includes("timeout")) {
|
|
16787
|
+
return err(new CMDOPError("Request timed out. The task may be too complex \u2014 try a simpler prompt."));
|
|
16788
|
+
}
|
|
16789
|
+
if (errMsg.includes("UNAVAILABLE") || errMsg.includes("Connection refused")) {
|
|
16790
|
+
return err(new CMDOPError("Server unavailable. Check your connection and try again."));
|
|
16791
|
+
}
|
|
16792
|
+
return err(new CMDOPError(`Agent error: ${errMsg}`, e3 instanceof Error ? e3 : void 0));
|
|
16703
16793
|
}
|
|
16704
16794
|
}
|
|
16705
16795
|
};
|
|
@@ -16796,6 +16886,103 @@ ${lines.join("\n\n")}`;
|
|
|
16796
16886
|
}
|
|
16797
16887
|
};
|
|
16798
16888
|
|
|
16889
|
+
// src/handlers/skills.ts
|
|
16890
|
+
init_errors();
|
|
16891
|
+
var SkillsHandler = class extends BaseHandler {
|
|
16892
|
+
name = "skills";
|
|
16893
|
+
description = "List, show, or run skills on the remote machine";
|
|
16894
|
+
usage = "/skills list | /skills show <name> | /skills run <name> <prompt>";
|
|
16895
|
+
requiredPermission = "EXECUTE";
|
|
16896
|
+
maxOutput;
|
|
16897
|
+
constructor(client, logger, config = {}) {
|
|
16898
|
+
super(client, logger);
|
|
16899
|
+
this.maxOutput = config.maxOutputLength ?? 4e3;
|
|
16900
|
+
}
|
|
16901
|
+
async handle(ctx) {
|
|
16902
|
+
const subcommand = ctx.args[0]?.toLowerCase();
|
|
16903
|
+
if (!subcommand) {
|
|
16904
|
+
return err(new CommandArgsError("skills", "Subcommand required. Usage: /skills list | /skills show <name> | /skills run <name> <prompt>"));
|
|
16905
|
+
}
|
|
16906
|
+
try {
|
|
16907
|
+
if (ctx.machine) {
|
|
16908
|
+
await this.client.skills.setMachine(ctx.machine);
|
|
16909
|
+
}
|
|
16910
|
+
switch (subcommand) {
|
|
16911
|
+
case "list":
|
|
16912
|
+
return await this.handleList();
|
|
16913
|
+
case "show":
|
|
16914
|
+
return await this.handleShow(ctx);
|
|
16915
|
+
case "run":
|
|
16916
|
+
return await this.handleRun(ctx);
|
|
16917
|
+
default:
|
|
16918
|
+
return err(new CommandArgsError("skills", `Unknown subcommand "${subcommand}". Usage: /skills list | /skills show <name> | /skills run <name> <prompt>`));
|
|
16919
|
+
}
|
|
16920
|
+
} catch (e3) {
|
|
16921
|
+
const errMsg = e3 instanceof Error ? e3.message : String(e3);
|
|
16922
|
+
this.logger.error("Skills operation failed", { error: errMsg });
|
|
16923
|
+
if (errMsg.includes("session_id") || errMsg.includes("No active session")) {
|
|
16924
|
+
return err(new CMDOPError("Machine is offline or CMDOP agent is not running.\nhttps://cmdop.com/downloads/"));
|
|
16925
|
+
}
|
|
16926
|
+
if (errMsg.includes("DEADLINE_EXCEEDED") || errMsg.includes("timeout")) {
|
|
16927
|
+
return err(new CMDOPError("Request timed out. The skill may be too complex \u2014 try a simpler prompt."));
|
|
16928
|
+
}
|
|
16929
|
+
if (errMsg.includes("UNAVAILABLE") || errMsg.includes("Connection refused")) {
|
|
16930
|
+
return err(new CMDOPError("Server unavailable. Check your connection and try again."));
|
|
16931
|
+
}
|
|
16932
|
+
return err(new CMDOPError(`Skills error: ${errMsg}`, e3 instanceof Error ? e3 : void 0));
|
|
16933
|
+
}
|
|
16934
|
+
}
|
|
16935
|
+
async handleList() {
|
|
16936
|
+
const skills = await this.client.skills.list();
|
|
16937
|
+
if (skills.length === 0) {
|
|
16938
|
+
return ok({ type: "text", text: "No skills installed." });
|
|
16939
|
+
}
|
|
16940
|
+
const lines = skills.map(
|
|
16941
|
+
(s4) => s4.description ? `${s4.name} \u2014 ${s4.description}` : s4.name
|
|
16942
|
+
);
|
|
16943
|
+
return ok({ type: "text", text: lines.join("\n") });
|
|
16944
|
+
}
|
|
16945
|
+
async handleShow(ctx) {
|
|
16946
|
+
const name = ctx.args[1];
|
|
16947
|
+
if (!name) {
|
|
16948
|
+
return err(new CommandArgsError("skills", "Skill name required. Usage: /skills show <name>"));
|
|
16949
|
+
}
|
|
16950
|
+
const detail = await this.client.skills.show(name);
|
|
16951
|
+
if (!detail.found) {
|
|
16952
|
+
return err(new CMDOPError(detail.error ?? `Skill "${name}" not found`));
|
|
16953
|
+
}
|
|
16954
|
+
const parts = [];
|
|
16955
|
+
if (detail.info) {
|
|
16956
|
+
parts.push(`Skill: ${detail.info.name}`);
|
|
16957
|
+
if (detail.info.description) parts.push(`Description: ${detail.info.description}`);
|
|
16958
|
+
if (detail.info.author) parts.push(`Author: ${detail.info.author}`);
|
|
16959
|
+
if (detail.info.version) parts.push(`Version: ${detail.info.version}`);
|
|
16960
|
+
if (detail.info.origin) parts.push(`Origin: ${detail.info.origin}`);
|
|
16961
|
+
}
|
|
16962
|
+
if (detail.source) parts.push(`Source: ${detail.source}`);
|
|
16963
|
+
if (detail.content) {
|
|
16964
|
+
const preview = detail.content.length > this.maxOutput ? detail.content.slice(0, this.maxOutput) + "\n...(truncated)" : detail.content;
|
|
16965
|
+
parts.push(`
|
|
16966
|
+
System prompt:
|
|
16967
|
+
${preview}`);
|
|
16968
|
+
}
|
|
16969
|
+
return ok({ type: "code", code: parts.join("\n") });
|
|
16970
|
+
}
|
|
16971
|
+
async handleRun(ctx) {
|
|
16972
|
+
const name = ctx.args[1];
|
|
16973
|
+
if (!name) {
|
|
16974
|
+
return err(new CommandArgsError("skills", "Skill name and prompt required. Usage: /skills run <name> <prompt>"));
|
|
16975
|
+
}
|
|
16976
|
+
const prompt = ctx.args.slice(2).join(" ").trim();
|
|
16977
|
+
if (!prompt) {
|
|
16978
|
+
return err(new CommandArgsError("skills", "Prompt required. Usage: /skills run <name> <prompt>"));
|
|
16979
|
+
}
|
|
16980
|
+
const result = await this.client.skills.run(name, prompt);
|
|
16981
|
+
const code = result.text.length > this.maxOutput ? result.text.slice(0, this.maxOutput) + "\n...(truncated)" : result.text;
|
|
16982
|
+
return ok({ type: "code", code });
|
|
16983
|
+
}
|
|
16984
|
+
};
|
|
16985
|
+
|
|
16799
16986
|
// src/config.ts
|
|
16800
16987
|
init_errors();
|
|
16801
16988
|
var BotSettingsSchema = z.object({
|
|
@@ -16820,6 +17007,63 @@ function loadSettings(env = process.env) {
|
|
|
16820
17007
|
return result.data;
|
|
16821
17008
|
}
|
|
16822
17009
|
|
|
17010
|
+
// src/channels/demo/channel.ts
|
|
17011
|
+
init_base_channel();
|
|
17012
|
+
var DemoChannel = class extends BaseChannel {
|
|
17013
|
+
messageHandlers = [];
|
|
17014
|
+
onOutput;
|
|
17015
|
+
constructor(permissions, dispatcher, logger, options2 = {}) {
|
|
17016
|
+
super("demo", "Demo", permissions, dispatcher, logger);
|
|
17017
|
+
this.onOutput = options2.onOutput ?? ((text) => process.stdout.write(text + "\n"));
|
|
17018
|
+
}
|
|
17019
|
+
async start() {
|
|
17020
|
+
this.logEvent("started");
|
|
17021
|
+
}
|
|
17022
|
+
async stop() {
|
|
17023
|
+
this.messageHandlers = [];
|
|
17024
|
+
this.logEvent("stopped");
|
|
17025
|
+
}
|
|
17026
|
+
send(_userId, message) {
|
|
17027
|
+
const text = formatOutgoing(message);
|
|
17028
|
+
this.onOutput(text, message);
|
|
17029
|
+
return Promise.resolve();
|
|
17030
|
+
}
|
|
17031
|
+
onMessage(handler) {
|
|
17032
|
+
this.messageHandlers.push(handler);
|
|
17033
|
+
}
|
|
17034
|
+
/**
|
|
17035
|
+
* Inject a message as if it came from a real platform user.
|
|
17036
|
+
* Primarily used in tests and CLI demos.
|
|
17037
|
+
*/
|
|
17038
|
+
async injectMessage(input) {
|
|
17039
|
+
const msg = {
|
|
17040
|
+
id: `demo-${Date.now()}`,
|
|
17041
|
+
userId: input.userId ?? "demo-user",
|
|
17042
|
+
channelId: this.id,
|
|
17043
|
+
text: input.text,
|
|
17044
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
17045
|
+
attachments: []
|
|
17046
|
+
};
|
|
17047
|
+
for (const handler of this.messageHandlers) {
|
|
17048
|
+
await handler(msg);
|
|
17049
|
+
}
|
|
17050
|
+
await this.processMessage(msg);
|
|
17051
|
+
}
|
|
17052
|
+
};
|
|
17053
|
+
function formatOutgoing(message) {
|
|
17054
|
+
switch (message.type) {
|
|
17055
|
+
case "text":
|
|
17056
|
+
return message.text;
|
|
17057
|
+
case "code":
|
|
17058
|
+
return `\`\`\`${message.language ?? ""}
|
|
17059
|
+
${message.code}
|
|
17060
|
+
\`\`\``;
|
|
17061
|
+
case "error":
|
|
17062
|
+
return `\u274C ${message.message}${message.hint ? `
|
|
17063
|
+
\u{1F4A1} ${message.hint}` : ""}`;
|
|
17064
|
+
}
|
|
17065
|
+
}
|
|
17066
|
+
|
|
16823
17067
|
// src/hub.ts
|
|
16824
17068
|
var IntegrationHub = class _IntegrationHub {
|
|
16825
17069
|
constructor(_client, _logger, _settings, permissions, dispatcher, channelStartMode) {
|
|
@@ -16845,11 +17089,12 @@ var IntegrationHub = class _IntegrationHub {
|
|
|
16845
17089
|
Object.assign(settings2, options2.settings ?? {});
|
|
16846
17090
|
if (options2.defaultMachine) settings2.defaultMachine = options2.defaultMachine;
|
|
16847
17091
|
const logger = options2.logger ?? createLogger(settings2.logLevel);
|
|
16848
|
-
const client = options2.apiKey ?
|
|
17092
|
+
const client = options2.apiKey ? CMDOPClient.remote(options2.apiKey) : CMDOPClient.local();
|
|
16849
17093
|
const store = options2.permissionStore ?? new InMemoryPermissionStore();
|
|
16850
17094
|
const identityMap = new IdentityMap();
|
|
16851
17095
|
const permissions = new PermissionManager(store, {
|
|
16852
17096
|
adminUsers: [...options2.adminUsers ?? [], ...settings2.allowedUsers],
|
|
17097
|
+
defaultLevel: options2.defaultPermission,
|
|
16853
17098
|
identityMap
|
|
16854
17099
|
});
|
|
16855
17100
|
const dispatcher = new MessageDispatcher(permissions, logger);
|
|
@@ -16857,6 +17102,7 @@ var IntegrationHub = class _IntegrationHub {
|
|
|
16857
17102
|
const hub = new _IntegrationHub(client, logger, settings2, permissions, dispatcher, mode);
|
|
16858
17103
|
hub.registerHandler(new TerminalHandler(client, logger, { maxOutputLength: settings2.maxOutputLength }));
|
|
16859
17104
|
hub.registerHandler(new AgentHandler(client, logger, { maxOutputLength: settings2.maxOutputLength }));
|
|
17105
|
+
hub.registerHandler(new SkillsHandler(client, logger, { maxOutputLength: settings2.maxOutputLength }));
|
|
16860
17106
|
hub.registerHandler(new FilesHandler(client, logger));
|
|
16861
17107
|
hub.registerHandler(new HelpHandler(client, logger, {
|
|
16862
17108
|
getCommands: () => dispatcher.getCommandList()
|
|
@@ -16896,6 +17142,15 @@ var IntegrationHub = class _IntegrationHub {
|
|
|
16896
17142
|
const channel = new SlackChannel2(options2, this._permissions, this._dispatcher, this._logger);
|
|
16897
17143
|
return this.registerChannel(channel);
|
|
16898
17144
|
}
|
|
17145
|
+
/**
|
|
17146
|
+
* Create and register a DemoChannel for CLI testing.
|
|
17147
|
+
* No external dependencies — messages are injected via the returned channel's `injectMessage()`.
|
|
17148
|
+
*/
|
|
17149
|
+
addDemo(options2) {
|
|
17150
|
+
const channel = new DemoChannel(this._permissions, this._dispatcher, this._logger, options2);
|
|
17151
|
+
this.registerChannel(channel);
|
|
17152
|
+
return channel;
|
|
17153
|
+
}
|
|
16899
17154
|
registerHandler(handler) {
|
|
16900
17155
|
this._dispatcher.register(handler);
|
|
16901
17156
|
return this;
|
|
@@ -16904,6 +17159,17 @@ var IntegrationHub = class _IntegrationHub {
|
|
|
16904
17159
|
async start() {
|
|
16905
17160
|
if (this.started) return;
|
|
16906
17161
|
this.started = true;
|
|
17162
|
+
if (this._settings.defaultMachine) {
|
|
17163
|
+
try {
|
|
17164
|
+
await this._client.setMachine(this._settings.defaultMachine);
|
|
17165
|
+
this._logger.info("Machine routing initialized", { machine: this._settings.defaultMachine });
|
|
17166
|
+
} catch (err2) {
|
|
17167
|
+
this._logger.error("Failed to initialize machine routing", {
|
|
17168
|
+
machine: this._settings.defaultMachine,
|
|
17169
|
+
err: err2 instanceof Error ? err2.message : String(err2)
|
|
17170
|
+
});
|
|
17171
|
+
}
|
|
17172
|
+
}
|
|
16907
17173
|
if (this.channelStartMode === "strict") {
|
|
16908
17174
|
await Promise.all([...this.channels.values()].map(async (ch) => {
|
|
16909
17175
|
await ch.start();
|
|
@@ -17166,63 +17432,6 @@ var SlackStream = class _SlackStream {
|
|
|
17166
17432
|
}
|
|
17167
17433
|
};
|
|
17168
17434
|
|
|
17169
|
-
// src/channels/demo/channel.ts
|
|
17170
|
-
init_base_channel();
|
|
17171
|
-
var DemoChannel = class extends BaseChannel {
|
|
17172
|
-
messageHandlers = [];
|
|
17173
|
-
onOutput;
|
|
17174
|
-
constructor(permissions, dispatcher, logger, options2 = {}) {
|
|
17175
|
-
super("demo", "Demo", permissions, dispatcher, logger);
|
|
17176
|
-
this.onOutput = options2.onOutput ?? ((text) => process.stdout.write(text + "\n"));
|
|
17177
|
-
}
|
|
17178
|
-
async start() {
|
|
17179
|
-
this.logEvent("started");
|
|
17180
|
-
}
|
|
17181
|
-
async stop() {
|
|
17182
|
-
this.messageHandlers = [];
|
|
17183
|
-
this.logEvent("stopped");
|
|
17184
|
-
}
|
|
17185
|
-
send(_userId, message) {
|
|
17186
|
-
const text = formatOutgoing(message);
|
|
17187
|
-
this.onOutput(text, message);
|
|
17188
|
-
return Promise.resolve();
|
|
17189
|
-
}
|
|
17190
|
-
onMessage(handler) {
|
|
17191
|
-
this.messageHandlers.push(handler);
|
|
17192
|
-
}
|
|
17193
|
-
/**
|
|
17194
|
-
* Inject a message as if it came from a real platform user.
|
|
17195
|
-
* Primarily used in tests and CLI demos.
|
|
17196
|
-
*/
|
|
17197
|
-
async injectMessage(input) {
|
|
17198
|
-
const msg = {
|
|
17199
|
-
id: `demo-${Date.now()}`,
|
|
17200
|
-
userId: input.userId ?? "demo-user",
|
|
17201
|
-
channelId: this.id,
|
|
17202
|
-
text: input.text,
|
|
17203
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
17204
|
-
attachments: []
|
|
17205
|
-
};
|
|
17206
|
-
for (const handler of this.messageHandlers) {
|
|
17207
|
-
await handler(msg);
|
|
17208
|
-
}
|
|
17209
|
-
await this.processMessage(msg);
|
|
17210
|
-
}
|
|
17211
|
-
};
|
|
17212
|
-
function formatOutgoing(message) {
|
|
17213
|
-
switch (message.type) {
|
|
17214
|
-
case "text":
|
|
17215
|
-
return message.text;
|
|
17216
|
-
case "code":
|
|
17217
|
-
return `\`\`\`${message.language ?? ""}
|
|
17218
|
-
${message.code}
|
|
17219
|
-
\`\`\``;
|
|
17220
|
-
case "error":
|
|
17221
|
-
return `\u274C ${message.message}${message.hint ? `
|
|
17222
|
-
\u{1F4A1} ${message.hint}` : ""}`;
|
|
17223
|
-
}
|
|
17224
|
-
}
|
|
17225
|
-
|
|
17226
17435
|
// src/channels/telegram/index.ts
|
|
17227
17436
|
init_channel();
|
|
17228
17437
|
init_formatter();
|