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