@cremini/skillpack 1.2.3 → 1.2.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -66
- package/dist/cli.js +836 -152
- package/package.json +2 -2
- package/web/js/chat.js +0 -15
package/dist/cli.js
CHANGED
|
@@ -267,6 +267,76 @@ var init_attachment_utils = __esm({
|
|
|
267
267
|
}
|
|
268
268
|
});
|
|
269
269
|
|
|
270
|
+
// src/runtime/commands/index.ts
|
|
271
|
+
function resolveCommand(input) {
|
|
272
|
+
const key = input.split(/\s/)[0].toLowerCase();
|
|
273
|
+
return COMMAND_ALIASES[key] ?? null;
|
|
274
|
+
}
|
|
275
|
+
function getVisibleCommands() {
|
|
276
|
+
return COMMAND_REGISTRY.filter((cmd) => cmd.visibleInHelp);
|
|
277
|
+
}
|
|
278
|
+
function getTelegramBotCommands() {
|
|
279
|
+
return COMMAND_REGISTRY.filter((cmd) => cmd.visibleInHelp).map((cmd) => ({
|
|
280
|
+
command: cmd.command,
|
|
281
|
+
description: cmd.description
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
var COMMAND_REGISTRY, COMMAND_ALIASES;
|
|
285
|
+
var init_commands = __esm({
|
|
286
|
+
"src/runtime/commands/index.ts"() {
|
|
287
|
+
"use strict";
|
|
288
|
+
COMMAND_REGISTRY = [
|
|
289
|
+
{
|
|
290
|
+
command: "help",
|
|
291
|
+
description: "Show available commands, skills, and usage tips",
|
|
292
|
+
visibleInHelp: true
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
command: "clear",
|
|
296
|
+
description: "Clear current session and start fresh",
|
|
297
|
+
visibleInHelp: true
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
command: "restart",
|
|
301
|
+
description: "Restart the server process",
|
|
302
|
+
visibleInHelp: true
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
command: "shutdown",
|
|
306
|
+
description: "Shut down the server process",
|
|
307
|
+
visibleInHelp: true
|
|
308
|
+
},
|
|
309
|
+
// "new" is a hidden alias for "clear" — not shown in /help
|
|
310
|
+
{
|
|
311
|
+
command: "new",
|
|
312
|
+
description: "Start a new session (alias for /clear)",
|
|
313
|
+
visibleInHelp: false
|
|
314
|
+
}
|
|
315
|
+
];
|
|
316
|
+
COMMAND_ALIASES = {
|
|
317
|
+
"/help": "help",
|
|
318
|
+
"/clear": "clear",
|
|
319
|
+
"/new": "clear",
|
|
320
|
+
"/restart": "restart",
|
|
321
|
+
"/shutdown": "shutdown"
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// src/runtime/adapters/types.ts
|
|
327
|
+
var types_exports = {};
|
|
328
|
+
__export(types_exports, {
|
|
329
|
+
isMessageSender: () => isMessageSender
|
|
330
|
+
});
|
|
331
|
+
function isMessageSender(adapter) {
|
|
332
|
+
return typeof adapter.sendMessage === "function";
|
|
333
|
+
}
|
|
334
|
+
var init_types = __esm({
|
|
335
|
+
"src/runtime/adapters/types.ts"() {
|
|
336
|
+
"use strict";
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
270
340
|
// src/runtime/adapters/markdown.ts
|
|
271
341
|
function unwrapMarkdownSourceBlocks(text) {
|
|
272
342
|
return text.replace(
|
|
@@ -307,6 +377,7 @@ function formatSlackInline(text) {
|
|
|
307
377
|
);
|
|
308
378
|
formatted = formatted.replace(/\*\*([^*\n]+)\*\*/g, "*$1*");
|
|
309
379
|
formatted = formatted.replace(/__([^_\n]+)__/g, "*$1*");
|
|
380
|
+
formatted = formatted.replace(/^(?:-|\*) /gm, "\u2022 ");
|
|
310
381
|
return formatted;
|
|
311
382
|
}
|
|
312
383
|
function formatTelegramInline(text) {
|
|
@@ -386,19 +457,15 @@ var telegram_exports = {};
|
|
|
386
457
|
__export(telegram_exports, {
|
|
387
458
|
TelegramAdapter: () => TelegramAdapter
|
|
388
459
|
});
|
|
389
|
-
import
|
|
460
|
+
import fs13 from "fs";
|
|
390
461
|
import TelegramBot from "node-telegram-bot-api";
|
|
391
|
-
var
|
|
462
|
+
var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
|
|
392
463
|
var init_telegram = __esm({
|
|
393
464
|
"src/runtime/adapters/telegram.ts"() {
|
|
394
465
|
"use strict";
|
|
395
466
|
init_markdown();
|
|
396
467
|
init_attachment_utils();
|
|
397
|
-
|
|
398
|
-
"/clear": "clear",
|
|
399
|
-
"/restart": "restart",
|
|
400
|
-
"/shutdown": "shutdown"
|
|
401
|
-
};
|
|
468
|
+
init_commands();
|
|
402
469
|
MAX_MESSAGE_LENGTH = 4096;
|
|
403
470
|
ACK_REACTION = {
|
|
404
471
|
type: "emoji",
|
|
@@ -410,23 +477,21 @@ var init_telegram = __esm({
|
|
|
410
477
|
agent = null;
|
|
411
478
|
options;
|
|
412
479
|
rootDir = "";
|
|
480
|
+
ipcBroadcaster = null;
|
|
413
481
|
constructor(options) {
|
|
414
482
|
this.options = options;
|
|
415
483
|
}
|
|
416
484
|
async start(ctx) {
|
|
417
485
|
this.agent = ctx.agent;
|
|
418
486
|
this.rootDir = ctx.rootDir;
|
|
487
|
+
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
419
488
|
this.bot = new TelegramBot(this.options.token, { polling: true });
|
|
420
489
|
this.bot.on("message", (msg) => {
|
|
421
490
|
this.handleTelegramMessage(msg).catch((err) => {
|
|
422
491
|
console.error("[Telegram] Error handling message:", err);
|
|
423
492
|
});
|
|
424
493
|
});
|
|
425
|
-
await this.bot.setMyCommands(
|
|
426
|
-
{ command: "clear", description: "Clear current session and start new" },
|
|
427
|
-
{ command: "restart", description: "Restart the server process" },
|
|
428
|
-
{ command: "shutdown", description: "Shut down the server process" }
|
|
429
|
-
]);
|
|
494
|
+
await this.bot.setMyCommands(getTelegramBotCommands());
|
|
430
495
|
const me = await this.bot.getMe();
|
|
431
496
|
console.log(`[TelegramAdapter] Started as @${me.username}`);
|
|
432
497
|
}
|
|
@@ -461,12 +526,21 @@ var init_telegram = __esm({
|
|
|
461
526
|
const messageId = msg.message_id;
|
|
462
527
|
const text = (msg.text || msg.caption || "").trim();
|
|
463
528
|
const channelId = `telegram-${chatId}`;
|
|
529
|
+
this.ipcBroadcaster?.broadcastInbound(
|
|
530
|
+
channelId,
|
|
531
|
+
"telegram",
|
|
532
|
+
{
|
|
533
|
+
id: String(msg.from?.id || ""),
|
|
534
|
+
username: msg.from?.username || msg.from?.first_name || ""
|
|
535
|
+
},
|
|
536
|
+
text
|
|
537
|
+
);
|
|
464
538
|
const attachments = await this.extractAttachments(msg, channelId);
|
|
465
539
|
if (!text && attachments.length === 0) return;
|
|
466
540
|
await this.tryAckReaction(chatId, messageId);
|
|
467
541
|
if (text) {
|
|
468
542
|
const commandKey = text.split(/\s/)[0].toLowerCase();
|
|
469
|
-
const command =
|
|
543
|
+
const command = this.resolveTelegramCommand(commandKey);
|
|
470
544
|
if (command) {
|
|
471
545
|
const result = await this.agent.handleCommand(command, channelId);
|
|
472
546
|
await this.sendSafe(chatId, result.message || `/${command} executed.`);
|
|
@@ -490,6 +564,7 @@ var init_telegram = __esm({
|
|
|
490
564
|
});
|
|
491
565
|
break;
|
|
492
566
|
}
|
|
567
|
+
this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
|
|
493
568
|
};
|
|
494
569
|
try {
|
|
495
570
|
const userText = text || "(User sent an attachment)";
|
|
@@ -521,6 +596,9 @@ var init_telegram = __esm({
|
|
|
521
596
|
await this.sendFileSafe(chatId, file.filePath, file.caption);
|
|
522
597
|
}
|
|
523
598
|
}
|
|
599
|
+
resolveTelegramCommand(commandKey) {
|
|
600
|
+
return resolveCommand(commandKey);
|
|
601
|
+
}
|
|
524
602
|
// -------------------------------------------------------------------------
|
|
525
603
|
// Send helpers
|
|
526
604
|
// -------------------------------------------------------------------------
|
|
@@ -697,7 +775,7 @@ var init_telegram = __esm({
|
|
|
697
775
|
async sendFileSafe(chatId, filePath, caption) {
|
|
698
776
|
if (!this.bot) return;
|
|
699
777
|
try {
|
|
700
|
-
if (!
|
|
778
|
+
if (!fs13.existsSync(filePath)) {
|
|
701
779
|
console.error(`[Telegram] File not found for sending: ${filePath}`);
|
|
702
780
|
return;
|
|
703
781
|
}
|
|
@@ -717,16 +795,18 @@ var slack_exports = {};
|
|
|
717
795
|
__export(slack_exports, {
|
|
718
796
|
SlackAdapter: () => SlackAdapter
|
|
719
797
|
});
|
|
720
|
-
import
|
|
721
|
-
import
|
|
798
|
+
import fs14 from "fs";
|
|
799
|
+
import path13 from "path";
|
|
722
800
|
import { App, LogLevel } from "@slack/bolt";
|
|
723
|
-
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, SlackAdapter;
|
|
801
|
+
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
|
|
724
802
|
var init_slack = __esm({
|
|
725
803
|
"src/runtime/adapters/slack.ts"() {
|
|
726
804
|
"use strict";
|
|
727
805
|
init_markdown();
|
|
728
806
|
init_attachment_utils();
|
|
807
|
+
init_commands();
|
|
729
808
|
INLINE_COMMANDS = {
|
|
809
|
+
"/help": "help",
|
|
730
810
|
"/clear": "clear",
|
|
731
811
|
"/restart": "restart",
|
|
732
812
|
"/shutdown": "shutdown"
|
|
@@ -738,6 +818,7 @@ var init_slack = __esm({
|
|
|
738
818
|
};
|
|
739
819
|
MAX_MESSAGE_LENGTH2 = 3500;
|
|
740
820
|
ACK_REACTION2 = "eyes";
|
|
821
|
+
PROCESSING_MESSAGE = "_Processing..._";
|
|
741
822
|
SlackAdapter = class {
|
|
742
823
|
name = "slack";
|
|
743
824
|
app = null;
|
|
@@ -746,12 +827,14 @@ var init_slack = __esm({
|
|
|
746
827
|
botUserId = null;
|
|
747
828
|
lastThreadByChannel = /* @__PURE__ */ new Map();
|
|
748
829
|
rootDir = "";
|
|
830
|
+
ipcBroadcaster = null;
|
|
749
831
|
constructor(options) {
|
|
750
832
|
this.options = options;
|
|
751
833
|
}
|
|
752
834
|
async start(ctx) {
|
|
753
835
|
this.agent = ctx.agent;
|
|
754
836
|
this.rootDir = ctx.rootDir;
|
|
837
|
+
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
755
838
|
this.app = new App({
|
|
756
839
|
token: this.options.botToken,
|
|
757
840
|
appToken: this.options.appToken,
|
|
@@ -807,7 +890,7 @@ var init_slack = __esm({
|
|
|
807
890
|
console.error("[Slack] Error handling mention:", err);
|
|
808
891
|
}
|
|
809
892
|
});
|
|
810
|
-
for (const commandName of Object.keys(SLASH_COMMANDS)) {
|
|
893
|
+
for (const commandName of [...Object.keys(SLASH_COMMANDS), "/new"]) {
|
|
811
894
|
app.command(commandName, async (args) => {
|
|
812
895
|
try {
|
|
813
896
|
await this.handleSlashCommand(args);
|
|
@@ -837,6 +920,15 @@ var init_slack = __esm({
|
|
|
837
920
|
const teamId = this.getTeamId(body, context);
|
|
838
921
|
const channelId = `slack-dm-${teamId}-${event.channel}`;
|
|
839
922
|
const route = { channel: event.channel };
|
|
923
|
+
this.ipcBroadcaster?.broadcastInbound(
|
|
924
|
+
channelId,
|
|
925
|
+
"slack",
|
|
926
|
+
{
|
|
927
|
+
id: String(event.user || ""),
|
|
928
|
+
username: String(event.user || "")
|
|
929
|
+
},
|
|
930
|
+
text
|
|
931
|
+
);
|
|
840
932
|
const attachments = await this.extractSlackFiles(event, channelId, client);
|
|
841
933
|
if (!text && attachments.length === 0) return;
|
|
842
934
|
await this.tryAckReaction(client, event);
|
|
@@ -867,12 +959,21 @@ var init_slack = __esm({
|
|
|
867
959
|
threadTs
|
|
868
960
|
);
|
|
869
961
|
const text = this.stripBotMention(event.text || "").trim();
|
|
962
|
+
this.ipcBroadcaster?.broadcastInbound(
|
|
963
|
+
channelId,
|
|
964
|
+
"slack",
|
|
965
|
+
{
|
|
966
|
+
id: String(event.user || ""),
|
|
967
|
+
username: String(event.user || "")
|
|
968
|
+
},
|
|
969
|
+
text
|
|
970
|
+
);
|
|
870
971
|
const attachments = await this.extractSlackFiles(event, channelId, client);
|
|
871
972
|
if (!text && attachments.length === 0) {
|
|
872
973
|
await this.sendSafe(
|
|
873
974
|
client,
|
|
874
975
|
route,
|
|
875
|
-
"Mention me with a message, or use `/clear` to reset this thread."
|
|
976
|
+
"Mention me with a message, or use `/clear` or `/new` to reset this thread."
|
|
876
977
|
);
|
|
877
978
|
return;
|
|
878
979
|
}
|
|
@@ -890,7 +991,7 @@ var init_slack = __esm({
|
|
|
890
991
|
ack
|
|
891
992
|
}) {
|
|
892
993
|
const commandName = command?.command;
|
|
893
|
-
const mapped = commandName ?
|
|
994
|
+
const mapped = commandName ? this.resolveSlashCommand(commandName) : void 0;
|
|
894
995
|
if (!this.agent || !mapped) {
|
|
895
996
|
await this.safeAck(ack, "Unsupported slash command.");
|
|
896
997
|
return;
|
|
@@ -916,6 +1017,7 @@ var init_slack = __esm({
|
|
|
916
1017
|
let hasError = false;
|
|
917
1018
|
let errorMessage = "";
|
|
918
1019
|
const pendingFiles = [];
|
|
1020
|
+
const placeholder = await this.sendPlaceholderMessage(client, route);
|
|
919
1021
|
const onEvent = (event) => {
|
|
920
1022
|
if (event.type === "text_delta") {
|
|
921
1023
|
finalText += event.delta;
|
|
@@ -925,6 +1027,7 @@ var init_slack = __esm({
|
|
|
925
1027
|
caption: event.caption
|
|
926
1028
|
});
|
|
927
1029
|
}
|
|
1030
|
+
this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
|
|
928
1031
|
};
|
|
929
1032
|
try {
|
|
930
1033
|
const result = await this.agent.handleMessage(
|
|
@@ -943,13 +1046,25 @@ var init_slack = __esm({
|
|
|
943
1046
|
errorMessage = this.getErrorMessage(err);
|
|
944
1047
|
}
|
|
945
1048
|
if (hasError) {
|
|
946
|
-
await this.
|
|
1049
|
+
await this.sendOrUpdateSafe(
|
|
1050
|
+
client,
|
|
1051
|
+
route,
|
|
1052
|
+
`\u274C Error: ${errorMessage}`,
|
|
1053
|
+
placeholder
|
|
1054
|
+
);
|
|
947
1055
|
return;
|
|
948
1056
|
}
|
|
949
1057
|
if (finalText.trim()) {
|
|
950
|
-
await this.sendLongMessage(client, route, finalText);
|
|
1058
|
+
await this.sendLongMessage(client, route, finalText, placeholder);
|
|
951
1059
|
} else if (pendingFiles.length === 0) {
|
|
952
|
-
await this.
|
|
1060
|
+
await this.sendOrUpdateSafe(
|
|
1061
|
+
client,
|
|
1062
|
+
route,
|
|
1063
|
+
"(No response generated)",
|
|
1064
|
+
placeholder
|
|
1065
|
+
);
|
|
1066
|
+
} else if (placeholder) {
|
|
1067
|
+
await this.deleteMessageSafe(client, route, placeholder.ts);
|
|
953
1068
|
}
|
|
954
1069
|
for (const file of pendingFiles) {
|
|
955
1070
|
await this.sendFileSafe(client, route, file.filePath, file.caption);
|
|
@@ -961,7 +1076,7 @@ var init_slack = __esm({
|
|
|
961
1076
|
async tryHandleInlineCommand(text, channelId, client, route) {
|
|
962
1077
|
if (!this.agent) return false;
|
|
963
1078
|
const commandKey = text.split(/\s/)[0].toLowerCase();
|
|
964
|
-
const command =
|
|
1079
|
+
const command = this.resolveInlineCommand(commandKey);
|
|
965
1080
|
if (!command) return false;
|
|
966
1081
|
const result = await this.agent.handleCommand(command, channelId);
|
|
967
1082
|
await this.sendSafe(
|
|
@@ -971,6 +1086,19 @@ var init_slack = __esm({
|
|
|
971
1086
|
);
|
|
972
1087
|
return true;
|
|
973
1088
|
}
|
|
1089
|
+
resolveInlineCommand(commandKey) {
|
|
1090
|
+
const resolved = resolveCommand(commandKey);
|
|
1091
|
+
if (resolved) {
|
|
1092
|
+
return resolved;
|
|
1093
|
+
}
|
|
1094
|
+
return INLINE_COMMANDS[commandKey];
|
|
1095
|
+
}
|
|
1096
|
+
resolveSlashCommand(commandName) {
|
|
1097
|
+
if (commandName === "/new") {
|
|
1098
|
+
return "clear";
|
|
1099
|
+
}
|
|
1100
|
+
return SLASH_COMMANDS[commandName];
|
|
1101
|
+
}
|
|
974
1102
|
resolveSlashCommandTarget(payload, context) {
|
|
975
1103
|
const teamId = this.getTeamId(payload, context);
|
|
976
1104
|
const channel = payload?.channel_id;
|
|
@@ -988,7 +1116,7 @@ var init_slack = __esm({
|
|
|
988
1116
|
);
|
|
989
1117
|
if (!threadTs) {
|
|
990
1118
|
return {
|
|
991
|
-
message: "No active Skillpack thread found in this channel. Mention the bot first, or run the command inside the thread as `@bot /clear`."
|
|
1119
|
+
message: "No active Skillpack thread found in this channel. Mention the bot first, or run the command inside the thread as `@bot /clear` or `@bot /new`."
|
|
992
1120
|
};
|
|
993
1121
|
}
|
|
994
1122
|
return {
|
|
@@ -1017,33 +1145,35 @@ var init_slack = __esm({
|
|
|
1017
1145
|
return text.replace(mention, "");
|
|
1018
1146
|
}
|
|
1019
1147
|
splitMessage(text) {
|
|
1020
|
-
if (text
|
|
1148
|
+
if (this.isSlackMessageWithinLimit(text)) {
|
|
1021
1149
|
return [text];
|
|
1022
1150
|
}
|
|
1023
1151
|
const chunks = [];
|
|
1024
1152
|
let remaining = text;
|
|
1025
1153
|
while (remaining.length > 0) {
|
|
1026
|
-
if (remaining
|
|
1154
|
+
if (this.isSlackMessageWithinLimit(remaining)) {
|
|
1027
1155
|
chunks.push(remaining);
|
|
1028
1156
|
break;
|
|
1029
1157
|
}
|
|
1030
|
-
let splitAt =
|
|
1031
|
-
if (splitAt < MAX_MESSAGE_LENGTH2 * 0.5) {
|
|
1032
|
-
splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH2);
|
|
1033
|
-
}
|
|
1034
|
-
if (splitAt < MAX_MESSAGE_LENGTH2 * 0.3) {
|
|
1035
|
-
splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH2);
|
|
1036
|
-
}
|
|
1037
|
-
if (splitAt < 1) {
|
|
1038
|
-
splitAt = MAX_MESSAGE_LENGTH2;
|
|
1039
|
-
}
|
|
1158
|
+
let splitAt = this.findSlackSafeSplitPoint(remaining);
|
|
1040
1159
|
chunks.push(remaining.slice(0, splitAt));
|
|
1041
1160
|
remaining = remaining.slice(splitAt).trimStart();
|
|
1042
1161
|
}
|
|
1043
1162
|
return chunks;
|
|
1044
1163
|
}
|
|
1045
|
-
async sendLongMessage(client, route, text) {
|
|
1046
|
-
|
|
1164
|
+
async sendLongMessage(client, route, text, placeholder) {
|
|
1165
|
+
const chunks = this.splitMessage(text);
|
|
1166
|
+
if (chunks.length === 0) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (placeholder) {
|
|
1170
|
+
await this.updateMessageSafe(client, route, placeholder.ts, chunks[0]);
|
|
1171
|
+
for (const chunk of chunks.slice(1)) {
|
|
1172
|
+
await this.sendSafe(client, route, chunk);
|
|
1173
|
+
}
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
for (const chunk of chunks) {
|
|
1047
1177
|
await this.sendWithRetry(client, route, chunk);
|
|
1048
1178
|
}
|
|
1049
1179
|
}
|
|
@@ -1054,17 +1184,35 @@ var init_slack = __esm({
|
|
|
1054
1184
|
console.error("[Slack] Failed to send message:", err);
|
|
1055
1185
|
}
|
|
1056
1186
|
}
|
|
1187
|
+
async sendOrUpdateSafe(client, route, text, placeholder) {
|
|
1188
|
+
if (placeholder) {
|
|
1189
|
+
await this.updateMessageSafe(client, route, placeholder.ts, text);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
await this.sendSafe(client, route, text);
|
|
1193
|
+
}
|
|
1194
|
+
async sendPlaceholderMessage(client, route) {
|
|
1195
|
+
try {
|
|
1196
|
+
return await this.sendWithRetry(client, route, PROCESSING_MESSAGE);
|
|
1197
|
+
} catch (err) {
|
|
1198
|
+
console.error("[Slack] Failed to send placeholder message:", err);
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1057
1202
|
async sendWithRetry(client, route, text, maxRetries = 3) {
|
|
1058
1203
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1059
1204
|
try {
|
|
1060
|
-
await client.chat.postMessage({
|
|
1205
|
+
const response = await client.chat.postMessage({
|
|
1061
1206
|
channel: route.channel,
|
|
1062
1207
|
text: formatSlackMessage(text),
|
|
1063
1208
|
mrkdwn: true,
|
|
1064
1209
|
thread_ts: route.threadTs,
|
|
1065
1210
|
reply_broadcast: false
|
|
1066
1211
|
});
|
|
1067
|
-
|
|
1212
|
+
if (typeof response.ts !== "string") {
|
|
1213
|
+
throw new Error("Slack postMessage response missing ts");
|
|
1214
|
+
}
|
|
1215
|
+
return { ts: response.ts };
|
|
1068
1216
|
} catch (err) {
|
|
1069
1217
|
const retryAfter = this.getRetryAfterSeconds(err);
|
|
1070
1218
|
if (retryAfter && attempt < maxRetries) {
|
|
@@ -1079,6 +1227,93 @@ var init_slack = __esm({
|
|
|
1079
1227
|
throw err;
|
|
1080
1228
|
}
|
|
1081
1229
|
}
|
|
1230
|
+
throw new Error("Slack postMessage failed after retries");
|
|
1231
|
+
}
|
|
1232
|
+
async updateMessageSafe(client, route, ts, text) {
|
|
1233
|
+
try {
|
|
1234
|
+
await this.updateWithRetry(client, route, ts, text);
|
|
1235
|
+
} catch (err) {
|
|
1236
|
+
console.error("[Slack] Failed to update message:", err);
|
|
1237
|
+
await this.deleteMessageSafe(client, route, ts);
|
|
1238
|
+
await this.sendSafe(client, route, text);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async updateWithRetry(client, route, ts, text, maxRetries = 3) {
|
|
1242
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1243
|
+
try {
|
|
1244
|
+
await client.chat.update({
|
|
1245
|
+
channel: route.channel,
|
|
1246
|
+
ts,
|
|
1247
|
+
text: formatSlackMessage(text),
|
|
1248
|
+
mrkdwn: true
|
|
1249
|
+
});
|
|
1250
|
+
return;
|
|
1251
|
+
} catch (err) {
|
|
1252
|
+
const retryAfter = this.getRetryAfterSeconds(err);
|
|
1253
|
+
if (retryAfter && attempt < maxRetries) {
|
|
1254
|
+
console.log(
|
|
1255
|
+
`[Slack] Rate limited while updating, retrying after ${retryAfter}s...`
|
|
1256
|
+
);
|
|
1257
|
+
await new Promise(
|
|
1258
|
+
(resolve) => setTimeout(resolve, retryAfter * 1e3)
|
|
1259
|
+
);
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
throw err;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
async deleteMessageSafe(client, route, ts) {
|
|
1267
|
+
try {
|
|
1268
|
+
await client.chat.delete({
|
|
1269
|
+
channel: route.channel,
|
|
1270
|
+
ts
|
|
1271
|
+
});
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
console.error("[Slack] Failed to delete placeholder message:", err);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
isSlackMessageWithinLimit(text) {
|
|
1277
|
+
return formatSlackMessage(text).length <= MAX_MESSAGE_LENGTH2;
|
|
1278
|
+
}
|
|
1279
|
+
findSlackSafeSplitPoint(text) {
|
|
1280
|
+
const preferredBreaks = ["\n\n", "\n", " "];
|
|
1281
|
+
const minSplit = Math.floor(MAX_MESSAGE_LENGTH2 * 0.3);
|
|
1282
|
+
for (const token of preferredBreaks) {
|
|
1283
|
+
const index = this.findBestSlackSplitBefore(text, token);
|
|
1284
|
+
if (index >= minSplit) {
|
|
1285
|
+
return index;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
let low = 1;
|
|
1289
|
+
let high = text.length;
|
|
1290
|
+
let best = Math.min(text.length, MAX_MESSAGE_LENGTH2);
|
|
1291
|
+
while (low <= high) {
|
|
1292
|
+
const mid = Math.floor((low + high) / 2);
|
|
1293
|
+
const candidate = text.slice(0, mid);
|
|
1294
|
+
if (this.isSlackMessageWithinLimit(candidate)) {
|
|
1295
|
+
best = mid;
|
|
1296
|
+
low = mid + 1;
|
|
1297
|
+
} else {
|
|
1298
|
+
high = mid - 1;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return Math.max(1, best);
|
|
1302
|
+
}
|
|
1303
|
+
findBestSlackSplitBefore(text, token) {
|
|
1304
|
+
let fromIndex = Math.min(text.length, MAX_MESSAGE_LENGTH2);
|
|
1305
|
+
while (fromIndex > 0) {
|
|
1306
|
+
const index = text.lastIndexOf(token, fromIndex);
|
|
1307
|
+
if (index < 0) {
|
|
1308
|
+
return -1;
|
|
1309
|
+
}
|
|
1310
|
+
const splitAt = index + token.length;
|
|
1311
|
+
if (this.isSlackMessageWithinLimit(text.slice(0, splitAt))) {
|
|
1312
|
+
return splitAt;
|
|
1313
|
+
}
|
|
1314
|
+
fromIndex = index - 1;
|
|
1315
|
+
}
|
|
1316
|
+
return -1;
|
|
1082
1317
|
}
|
|
1083
1318
|
async tryAckReaction(client, event) {
|
|
1084
1319
|
try {
|
|
@@ -1196,12 +1431,12 @@ var init_slack = __esm({
|
|
|
1196
1431
|
*/
|
|
1197
1432
|
async sendFileSafe(client, route, filePath, caption) {
|
|
1198
1433
|
try {
|
|
1199
|
-
if (!
|
|
1434
|
+
if (!fs14.existsSync(filePath)) {
|
|
1200
1435
|
console.error(`[Slack] File not found for sending: ${filePath}`);
|
|
1201
1436
|
return;
|
|
1202
1437
|
}
|
|
1203
|
-
const filename =
|
|
1204
|
-
const fileContent =
|
|
1438
|
+
const filename = path13.basename(filePath);
|
|
1439
|
+
const fileContent = fs14.readFileSync(filePath);
|
|
1205
1440
|
await client.files.uploadV2({
|
|
1206
1441
|
channel_id: route.channel,
|
|
1207
1442
|
thread_ts: route.threadTs,
|
|
@@ -1218,20 +1453,6 @@ var init_slack = __esm({
|
|
|
1218
1453
|
}
|
|
1219
1454
|
});
|
|
1220
1455
|
|
|
1221
|
-
// src/runtime/adapters/types.ts
|
|
1222
|
-
var types_exports = {};
|
|
1223
|
-
__export(types_exports, {
|
|
1224
|
-
isMessageSender: () => isMessageSender
|
|
1225
|
-
});
|
|
1226
|
-
function isMessageSender(adapter) {
|
|
1227
|
-
return typeof adapter.sendMessage === "function";
|
|
1228
|
-
}
|
|
1229
|
-
var init_types = __esm({
|
|
1230
|
-
"src/runtime/adapters/types.ts"() {
|
|
1231
|
-
"use strict";
|
|
1232
|
-
}
|
|
1233
|
-
});
|
|
1234
|
-
|
|
1235
1456
|
// src/runtime/adapters/scheduler.ts
|
|
1236
1457
|
var scheduler_exports = {};
|
|
1237
1458
|
__export(scheduler_exports, {
|
|
@@ -2242,15 +2463,15 @@ async function interactiveCreate(workDir) {
|
|
|
2242
2463
|
}
|
|
2243
2464
|
|
|
2244
2465
|
// src/commands/run.ts
|
|
2245
|
-
import
|
|
2246
|
-
import
|
|
2466
|
+
import path15 from "path";
|
|
2467
|
+
import fs16 from "fs";
|
|
2247
2468
|
import inquirer2 from "inquirer";
|
|
2248
2469
|
import chalk4 from "chalk";
|
|
2249
2470
|
|
|
2250
2471
|
// src/runtime/server.ts
|
|
2251
2472
|
import express from "express";
|
|
2252
|
-
import
|
|
2253
|
-
import
|
|
2473
|
+
import path14 from "path";
|
|
2474
|
+
import fs15 from "fs";
|
|
2254
2475
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2255
2476
|
import { createServer } from "http";
|
|
2256
2477
|
import { exec } from "child_process";
|
|
@@ -2258,8 +2479,8 @@ import { exec } from "child_process";
|
|
|
2258
2479
|
// src/runtime/agent.ts
|
|
2259
2480
|
init_config();
|
|
2260
2481
|
init_attachment_utils();
|
|
2261
|
-
import
|
|
2262
|
-
import
|
|
2482
|
+
import path9 from "path";
|
|
2483
|
+
import fs9 from "fs";
|
|
2263
2484
|
import { fileURLToPath } from "url";
|
|
2264
2485
|
import {
|
|
2265
2486
|
AuthStorage,
|
|
@@ -5115,6 +5336,67 @@ ${lines.join("\n")}`
|
|
|
5115
5336
|
};
|
|
5116
5337
|
}
|
|
5117
5338
|
|
|
5339
|
+
// src/runtime/commands/help-command.ts
|
|
5340
|
+
init_commands();
|
|
5341
|
+
import fs8 from "fs";
|
|
5342
|
+
import path8 from "path";
|
|
5343
|
+
function buildHelpMessage(rootDir) {
|
|
5344
|
+
const sections = [];
|
|
5345
|
+
const commands = getVisibleCommands();
|
|
5346
|
+
const commandLines = commands.map(
|
|
5347
|
+
(cmd) => `- \`/${cmd.command}\` \u2014 ${cmd.description}`
|
|
5348
|
+
);
|
|
5349
|
+
sections.push(`\u{1F4CB} **Available Commands**
|
|
5350
|
+
|
|
5351
|
+
${commandLines.join("\n")}`);
|
|
5352
|
+
const configPath = path8.resolve(rootDir, "skillpack.json");
|
|
5353
|
+
const skills = readInstalledSkills(configPath);
|
|
5354
|
+
if (skills.length > 0) {
|
|
5355
|
+
const skillLines = skills.map(
|
|
5356
|
+
(skill) => `- **${skill.name}**${skill.description ? ` \u2014 ${skill.description}` : ""}`
|
|
5357
|
+
);
|
|
5358
|
+
sections.push(
|
|
5359
|
+
`\u{1F9E9} **Installed Skills** (${skills.length})
|
|
5360
|
+
|
|
5361
|
+
${skillLines.join("\n")}`
|
|
5362
|
+
);
|
|
5363
|
+
} else {
|
|
5364
|
+
sections.push("\u{1F9E9} **Installed Skills**\nNo skills installed.");
|
|
5365
|
+
}
|
|
5366
|
+
sections.push(
|
|
5367
|
+
[
|
|
5368
|
+
"\u23F0 **Scheduled Tasks**",
|
|
5369
|
+
"",
|
|
5370
|
+
"You can set up recurring tasks using natural language. For example:",
|
|
5371
|
+
"",
|
|
5372
|
+
'- "Send me a daily market briefing every morning at 9 AM"',
|
|
5373
|
+
`- "Summarize this week's trading data every Friday at 6 PM"`,
|
|
5374
|
+
'- "Check for new announcements every 30 minutes"',
|
|
5375
|
+
"",
|
|
5376
|
+
"I will handle the cron scheduling automatically."
|
|
5377
|
+
].join("\n")
|
|
5378
|
+
);
|
|
5379
|
+
return sections.join("\n\n");
|
|
5380
|
+
}
|
|
5381
|
+
function handleHelpCommand(rootDir) {
|
|
5382
|
+
return {
|
|
5383
|
+
success: true,
|
|
5384
|
+
message: buildHelpMessage(rootDir)
|
|
5385
|
+
};
|
|
5386
|
+
}
|
|
5387
|
+
function readInstalledSkills(configPath) {
|
|
5388
|
+
if (!fs8.existsSync(configPath)) {
|
|
5389
|
+
return [];
|
|
5390
|
+
}
|
|
5391
|
+
try {
|
|
5392
|
+
const raw = fs8.readFileSync(configPath, "utf-8");
|
|
5393
|
+
const config = JSON.parse(raw);
|
|
5394
|
+
return Array.isArray(config.skills) ? config.skills : [];
|
|
5395
|
+
} catch {
|
|
5396
|
+
return [];
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
5399
|
+
|
|
5118
5400
|
// src/runtime/agent.ts
|
|
5119
5401
|
var DEBUG = true;
|
|
5120
5402
|
var log = (...args) => DEBUG && console.log(...args);
|
|
@@ -5127,24 +5409,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
|
5127
5409
|
var PACK_AGENTS_FILE = "AGENTS.md";
|
|
5128
5410
|
var PACK_SOUL_FILE = "SOUL.md";
|
|
5129
5411
|
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
5130
|
-
if (!
|
|
5412
|
+
if (!fs9.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
5131
5413
|
log(
|
|
5132
5414
|
`[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
|
|
5133
5415
|
);
|
|
5134
5416
|
return null;
|
|
5135
5417
|
}
|
|
5136
|
-
const packConfigPath =
|
|
5137
|
-
const skillDir =
|
|
5138
|
-
const skillPath =
|
|
5418
|
+
const packConfigPath = path9.resolve(rootDir, "skillpack.json");
|
|
5419
|
+
const skillDir = path9.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
|
|
5420
|
+
const skillPath = path9.join(skillDir, "SKILL.md");
|
|
5139
5421
|
const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
|
|
5140
5422
|
const copyDir = (srcDir, destDir) => {
|
|
5141
|
-
|
|
5142
|
-
for (const entry of
|
|
5423
|
+
fs9.mkdirSync(destDir, { recursive: true });
|
|
5424
|
+
for (const entry of fs9.readdirSync(srcDir, { withFileTypes: true })) {
|
|
5143
5425
|
if (entry.name === ".DS_Store") {
|
|
5144
5426
|
continue;
|
|
5145
5427
|
}
|
|
5146
|
-
const srcPath =
|
|
5147
|
-
const destPath =
|
|
5428
|
+
const srcPath = path9.join(srcDir, entry.name);
|
|
5429
|
+
const destPath = path9.join(destDir, entry.name);
|
|
5148
5430
|
if (entry.isDirectory()) {
|
|
5149
5431
|
copyDir(srcPath, destPath);
|
|
5150
5432
|
continue;
|
|
@@ -5153,17 +5435,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
|
5153
5435
|
continue;
|
|
5154
5436
|
}
|
|
5155
5437
|
if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
|
|
5156
|
-
const content =
|
|
5157
|
-
|
|
5438
|
+
const content = fs9.readFileSync(srcPath, "utf-8");
|
|
5439
|
+
fs9.writeFileSync(destPath, renderTemplate(content), "utf-8");
|
|
5158
5440
|
continue;
|
|
5159
5441
|
}
|
|
5160
|
-
|
|
5442
|
+
fs9.copyFileSync(srcPath, destPath);
|
|
5161
5443
|
}
|
|
5162
5444
|
};
|
|
5163
|
-
if (!
|
|
5445
|
+
if (!fs9.existsSync(skillDir)) {
|
|
5164
5446
|
copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
|
|
5165
5447
|
}
|
|
5166
|
-
if (!
|
|
5448
|
+
if (!fs9.existsSync(skillPath)) {
|
|
5167
5449
|
log(
|
|
5168
5450
|
`[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
|
|
5169
5451
|
);
|
|
@@ -5191,11 +5473,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
|
5191
5473
|
};
|
|
5192
5474
|
}
|
|
5193
5475
|
function readOptionalPackPromptFile(filePath) {
|
|
5194
|
-
if (!
|
|
5476
|
+
if (!fs9.existsSync(filePath)) {
|
|
5195
5477
|
return void 0;
|
|
5196
5478
|
}
|
|
5197
5479
|
try {
|
|
5198
|
-
const content =
|
|
5480
|
+
const content = fs9.readFileSync(filePath, "utf-8").trim();
|
|
5199
5481
|
return content.length > 0 ? content : void 0;
|
|
5200
5482
|
} catch (error) {
|
|
5201
5483
|
console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
|
|
@@ -5203,8 +5485,8 @@ function readOptionalPackPromptFile(filePath) {
|
|
|
5203
5485
|
}
|
|
5204
5486
|
}
|
|
5205
5487
|
function buildPackPromptBlock(rootDir) {
|
|
5206
|
-
const agentsPath =
|
|
5207
|
-
const soulPath =
|
|
5488
|
+
const agentsPath = path9.resolve(rootDir, PACK_AGENTS_FILE);
|
|
5489
|
+
const soulPath = path9.resolve(rootDir, PACK_SOUL_FILE);
|
|
5208
5490
|
const agentsContent = readOptionalPackPromptFile(agentsPath);
|
|
5209
5491
|
const soulContent = readOptionalPackPromptFile(soulPath);
|
|
5210
5492
|
if (!agentsContent && !soulContent) {
|
|
@@ -5270,7 +5552,7 @@ var PackAgent = class {
|
|
|
5270
5552
|
authStorage;
|
|
5271
5553
|
constructor(options) {
|
|
5272
5554
|
this.options = options;
|
|
5273
|
-
const configPath =
|
|
5555
|
+
const configPath = path9.resolve(options.rootDir, "data", "config.json");
|
|
5274
5556
|
const backend = new ConfigFileAuthBackend(configPath);
|
|
5275
5557
|
this.authStorage = AuthStorage.fromStorage(backend);
|
|
5276
5558
|
const providerMeta = SUPPORTED_PROVIDERS[options.provider];
|
|
@@ -5321,24 +5603,24 @@ var PackAgent = class {
|
|
|
5321
5603
|
if (resolvedModel && baseUrl) {
|
|
5322
5604
|
log(`[PackAgent] Overriding ${provider}/${modelId} baseUrl -> ${baseUrl}`);
|
|
5323
5605
|
}
|
|
5324
|
-
const sessionDir =
|
|
5606
|
+
const sessionDir = path9.resolve(
|
|
5325
5607
|
rootDir,
|
|
5326
5608
|
"data",
|
|
5327
5609
|
"sessions",
|
|
5328
5610
|
channelId
|
|
5329
5611
|
);
|
|
5330
|
-
|
|
5612
|
+
fs9.mkdirSync(sessionDir, { recursive: true });
|
|
5331
5613
|
const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
|
|
5332
5614
|
log(`[PackAgent] Session dir: ${sessionDir}`);
|
|
5333
|
-
const workspaceDir =
|
|
5615
|
+
const workspaceDir = path9.resolve(
|
|
5334
5616
|
rootDir,
|
|
5335
5617
|
"data",
|
|
5336
5618
|
"workspaces",
|
|
5337
5619
|
channelId
|
|
5338
5620
|
);
|
|
5339
|
-
|
|
5621
|
+
fs9.mkdirSync(workspaceDir, { recursive: true });
|
|
5340
5622
|
log(`[PackAgent] Workspace dir: ${workspaceDir}`);
|
|
5341
|
-
const skillsPath =
|
|
5623
|
+
const skillsPath = path9.resolve(rootDir, "skills");
|
|
5342
5624
|
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
5343
5625
|
const materializedSkillCreator = materializeBuiltinSkillCreator(
|
|
5344
5626
|
rootDir,
|
|
@@ -5529,6 +5811,8 @@ ${text}`;
|
|
|
5529
5811
|
}
|
|
5530
5812
|
async handleCommand(command, channelId) {
|
|
5531
5813
|
switch (command) {
|
|
5814
|
+
case "help":
|
|
5815
|
+
return handleHelpCommand(this.options.rootDir);
|
|
5532
5816
|
case "new":
|
|
5533
5817
|
case "clear": {
|
|
5534
5818
|
const cs = this.channels.get(channelId);
|
|
@@ -5537,9 +5821,9 @@ ${text}`;
|
|
|
5537
5821
|
this.channels.delete(channelId);
|
|
5538
5822
|
}
|
|
5539
5823
|
const { rootDir } = this.options;
|
|
5540
|
-
const sessionDir =
|
|
5541
|
-
if (
|
|
5542
|
-
|
|
5824
|
+
const sessionDir = path9.resolve(rootDir, "data", "sessions", channelId);
|
|
5825
|
+
if (fs9.existsSync(sessionDir)) {
|
|
5826
|
+
fs9.rmSync(sessionDir, { recursive: true, force: true });
|
|
5543
5827
|
log(`[PackAgent] Cleared session dir: ${sessionDir}`);
|
|
5544
5828
|
}
|
|
5545
5829
|
return {
|
|
@@ -5584,26 +5868,27 @@ ${text}`;
|
|
|
5584
5868
|
/** Reserved: restore a historical session */
|
|
5585
5869
|
async restoreSession(_sessionId) {
|
|
5586
5870
|
}
|
|
5871
|
+
getActiveChannelIds() {
|
|
5872
|
+
return Array.from(this.channels.keys());
|
|
5873
|
+
}
|
|
5587
5874
|
};
|
|
5588
5875
|
|
|
5589
5876
|
// src/runtime/adapters/web.ts
|
|
5590
5877
|
init_config();
|
|
5591
|
-
|
|
5592
|
-
import
|
|
5878
|
+
init_commands();
|
|
5879
|
+
import fs10 from "fs";
|
|
5880
|
+
import path10 from "path";
|
|
5593
5881
|
import { WebSocketServer } from "ws";
|
|
5594
5882
|
function getPackConfig(rootDir) {
|
|
5595
|
-
const raw =
|
|
5883
|
+
const raw = fs10.readFileSync(path10.join(rootDir, "skillpack.json"), "utf-8");
|
|
5596
5884
|
return JSON.parse(raw);
|
|
5597
5885
|
}
|
|
5598
|
-
var COMMANDS = {
|
|
5599
|
-
"/new": "new",
|
|
5600
|
-
"/clear": "clear",
|
|
5601
|
-
"/restart": "restart",
|
|
5602
|
-
"/shutdown": "shutdown"
|
|
5603
|
-
};
|
|
5604
5886
|
function parseCommand(text) {
|
|
5605
|
-
|
|
5606
|
-
|
|
5887
|
+
return resolveCommand(text.trim().toLowerCase());
|
|
5888
|
+
}
|
|
5889
|
+
function sendWsEvent(ws, event) {
|
|
5890
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
5891
|
+
ws.send(JSON.stringify(event));
|
|
5607
5892
|
}
|
|
5608
5893
|
function getRuntimeConfigSignature(config) {
|
|
5609
5894
|
return JSON.stringify({
|
|
@@ -5619,9 +5904,11 @@ var WebAdapter = class {
|
|
|
5619
5904
|
name = "web";
|
|
5620
5905
|
wss = null;
|
|
5621
5906
|
agent = null;
|
|
5907
|
+
ipcBroadcaster = null;
|
|
5622
5908
|
async start(ctx) {
|
|
5623
5909
|
const { agent, server, app, rootDir, lifecycle } = ctx;
|
|
5624
5910
|
this.agent = agent;
|
|
5911
|
+
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
5625
5912
|
const currentConf = configManager.getConfig();
|
|
5626
5913
|
let apiKey = currentConf.apiKey || "";
|
|
5627
5914
|
let currentProvider = currentConf.provider || "openai";
|
|
@@ -5744,23 +6031,23 @@ var WebAdapter = class {
|
|
|
5744
6031
|
res.status(400).json({ error: "Missing 'path' query parameter" });
|
|
5745
6032
|
return;
|
|
5746
6033
|
}
|
|
5747
|
-
const resolvedPath =
|
|
5748
|
-
const dataDir =
|
|
6034
|
+
const resolvedPath = path10.resolve(filePath);
|
|
6035
|
+
const dataDir = path10.resolve(rootDir, "data");
|
|
5749
6036
|
if (!resolvedPath.startsWith(dataDir)) {
|
|
5750
6037
|
res.status(403).json({ error: "Access denied" });
|
|
5751
6038
|
return;
|
|
5752
6039
|
}
|
|
5753
|
-
if (!
|
|
6040
|
+
if (!fs10.existsSync(resolvedPath)) {
|
|
5754
6041
|
res.status(404).json({ error: "File not found" });
|
|
5755
6042
|
return;
|
|
5756
6043
|
}
|
|
5757
|
-
const filename =
|
|
6044
|
+
const filename = path10.basename(resolvedPath);
|
|
5758
6045
|
res.setHeader("Content-Type", "application/octet-stream");
|
|
5759
6046
|
res.setHeader(
|
|
5760
6047
|
"Content-Disposition",
|
|
5761
6048
|
`attachment; filename="${filename}"`
|
|
5762
6049
|
);
|
|
5763
|
-
|
|
6050
|
+
fs10.createReadStream(resolvedPath).pipe(res);
|
|
5764
6051
|
});
|
|
5765
6052
|
const getScheduler = () => {
|
|
5766
6053
|
const schedulerAdapter = ctx.adapterMap?.get("scheduler");
|
|
@@ -5881,21 +6168,15 @@ var WebAdapter = class {
|
|
|
5881
6168
|
const command = parseCommand(text);
|
|
5882
6169
|
if (command) {
|
|
5883
6170
|
const result2 = await agent.handleCommand(command, channelId);
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
type: "command_result",
|
|
5887
|
-
command,
|
|
5888
|
-
...result2
|
|
5889
|
-
})
|
|
5890
|
-
);
|
|
5891
|
-
if (command === "clear" || command === "new") {
|
|
5892
|
-
ws.send(JSON.stringify({ done: true }));
|
|
6171
|
+
if (result2.message) {
|
|
6172
|
+
sendWsEvent(ws, { type: "text_delta", delta: result2.message });
|
|
5893
6173
|
}
|
|
6174
|
+
ws.send(JSON.stringify({ done: true }));
|
|
5894
6175
|
return;
|
|
5895
6176
|
}
|
|
5896
6177
|
const onEvent = (event) => {
|
|
5897
|
-
|
|
5898
|
-
|
|
6178
|
+
sendWsEvent(ws, event);
|
|
6179
|
+
this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
|
|
5899
6180
|
};
|
|
5900
6181
|
const result = await agent.handleMessage("web", channelId, text, onEvent);
|
|
5901
6182
|
if (result.errorMessage) {
|
|
@@ -5913,6 +6194,367 @@ var WebAdapter = class {
|
|
|
5913
6194
|
}
|
|
5914
6195
|
};
|
|
5915
6196
|
|
|
6197
|
+
// src/runtime/adapters/ipc.ts
|
|
6198
|
+
init_config();
|
|
6199
|
+
|
|
6200
|
+
// src/runtime/services/conversation.ts
|
|
6201
|
+
import fs11 from "fs";
|
|
6202
|
+
import path11 from "path";
|
|
6203
|
+
import {
|
|
6204
|
+
parseSessionEntries
|
|
6205
|
+
} from "@mariozechner/pi-coding-agent";
|
|
6206
|
+
var ConversationService = class {
|
|
6207
|
+
constructor(rootDir) {
|
|
6208
|
+
this.rootDir = rootDir;
|
|
6209
|
+
}
|
|
6210
|
+
/**
|
|
6211
|
+
* Scan data/sessions and return conversation summaries sorted by recency.
|
|
6212
|
+
*/
|
|
6213
|
+
listConversations(activeChannels) {
|
|
6214
|
+
const sessionsDir = path11.resolve(this.rootDir, "data", "sessions");
|
|
6215
|
+
const channelIds = new Set(activeChannels);
|
|
6216
|
+
if (fs11.existsSync(sessionsDir)) {
|
|
6217
|
+
for (const entry of fs11.readdirSync(sessionsDir)) {
|
|
6218
|
+
const channelDir = path11.join(sessionsDir, entry);
|
|
6219
|
+
try {
|
|
6220
|
+
if (fs11.statSync(channelDir).isDirectory()) {
|
|
6221
|
+
channelIds.add(entry);
|
|
6222
|
+
}
|
|
6223
|
+
} catch {
|
|
6224
|
+
}
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
const results = [];
|
|
6228
|
+
for (const channelId of channelIds) {
|
|
6229
|
+
const channelDir = path11.join(sessionsDir, channelId);
|
|
6230
|
+
const sessionFile = this.findLatestSessionFile(channelDir);
|
|
6231
|
+
let messageCount = 0;
|
|
6232
|
+
let lastMessageAt = "";
|
|
6233
|
+
let lastMessagePreview = "";
|
|
6234
|
+
if (sessionFile) {
|
|
6235
|
+
const entries = this.loadEntries(sessionFile);
|
|
6236
|
+
const messages = entries.filter(
|
|
6237
|
+
(entry) => entry.type === "message"
|
|
6238
|
+
);
|
|
6239
|
+
messageCount = messages.length;
|
|
6240
|
+
const lastMessage = messages[messages.length - 1];
|
|
6241
|
+
if (lastMessage) {
|
|
6242
|
+
lastMessageAt = lastMessage.timestamp;
|
|
6243
|
+
lastMessagePreview = this.extractTextPreview(lastMessage, 100);
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
results.push({
|
|
6247
|
+
channelId,
|
|
6248
|
+
platform: this.detectPlatform(channelId),
|
|
6249
|
+
sessionFile,
|
|
6250
|
+
messageCount,
|
|
6251
|
+
lastMessageAt,
|
|
6252
|
+
lastMessagePreview
|
|
6253
|
+
});
|
|
6254
|
+
}
|
|
6255
|
+
return results.sort((a, b) => {
|
|
6256
|
+
const recency = (b.lastMessageAt || "").localeCompare(a.lastMessageAt || "");
|
|
6257
|
+
if (recency !== 0) return recency;
|
|
6258
|
+
return a.channelId.localeCompare(b.channelId);
|
|
6259
|
+
});
|
|
6260
|
+
}
|
|
6261
|
+
/**
|
|
6262
|
+
* Load latest messages for a channel in a simplified format.
|
|
6263
|
+
*/
|
|
6264
|
+
getMessages(channelId, limit = 100) {
|
|
6265
|
+
const channelDir = path11.resolve(
|
|
6266
|
+
this.rootDir,
|
|
6267
|
+
"data",
|
|
6268
|
+
"sessions",
|
|
6269
|
+
channelId
|
|
6270
|
+
);
|
|
6271
|
+
const sessionFile = this.findLatestSessionFile(channelDir);
|
|
6272
|
+
if (!sessionFile) return [];
|
|
6273
|
+
const safeLimit = Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : 100;
|
|
6274
|
+
if (safeLimit === 0) return [];
|
|
6275
|
+
const entries = this.loadEntries(sessionFile);
|
|
6276
|
+
const messages = [];
|
|
6277
|
+
for (const entry of entries) {
|
|
6278
|
+
if (entry.type !== "message") continue;
|
|
6279
|
+
const role = entry.message?.role;
|
|
6280
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
6281
|
+
const text = this.extractText(entry.message);
|
|
6282
|
+
if (!text) continue;
|
|
6283
|
+
const toolCalls = role === "assistant" ? this.extractToolCallSummaries(entry.message) : void 0;
|
|
6284
|
+
messages.push({
|
|
6285
|
+
id: entry.id,
|
|
6286
|
+
role,
|
|
6287
|
+
text,
|
|
6288
|
+
timestamp: entry.timestamp,
|
|
6289
|
+
toolCalls
|
|
6290
|
+
});
|
|
6291
|
+
}
|
|
6292
|
+
return messages.slice(-safeLimit);
|
|
6293
|
+
}
|
|
6294
|
+
findLatestSessionFile(channelDir) {
|
|
6295
|
+
if (!fs11.existsSync(channelDir)) return null;
|
|
6296
|
+
let stats;
|
|
6297
|
+
try {
|
|
6298
|
+
stats = fs11.statSync(channelDir);
|
|
6299
|
+
} catch {
|
|
6300
|
+
return null;
|
|
6301
|
+
}
|
|
6302
|
+
if (!stats.isDirectory()) return null;
|
|
6303
|
+
const files = fs11.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
|
|
6304
|
+
return files[0] ? path11.join(channelDir, files[0]) : null;
|
|
6305
|
+
}
|
|
6306
|
+
loadEntries(filePath) {
|
|
6307
|
+
try {
|
|
6308
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
6309
|
+
const fileEntries = parseSessionEntries(content);
|
|
6310
|
+
return fileEntries.filter((entry) => entry.type !== "session");
|
|
6311
|
+
} catch (err) {
|
|
6312
|
+
console.warn(`[ConversationService] Failed to load ${filePath}:`, err);
|
|
6313
|
+
return [];
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
extractText(message) {
|
|
6317
|
+
if (!message?.content) return "";
|
|
6318
|
+
if (typeof message.content === "string") return message.content.trim();
|
|
6319
|
+
if (!Array.isArray(message.content)) return "";
|
|
6320
|
+
return message.content.filter((item) => item?.type === "text").map((item) => typeof item?.text === "string" ? item.text : "").join("").trim();
|
|
6321
|
+
}
|
|
6322
|
+
extractTextPreview(entry, maxLen) {
|
|
6323
|
+
const text = this.extractText(entry.message);
|
|
6324
|
+
return text.length > maxLen ? `${text.slice(0, maxLen)}\u2026` : text;
|
|
6325
|
+
}
|
|
6326
|
+
extractToolCallSummaries(message) {
|
|
6327
|
+
if (!Array.isArray(message?.content)) return void 0;
|
|
6328
|
+
const toolCalls = message.content.filter((item) => item?.type === "toolCall").map((item) => ({
|
|
6329
|
+
name: typeof item?.name === "string" && item.name ? item.name : "unknown",
|
|
6330
|
+
isError: false
|
|
6331
|
+
}));
|
|
6332
|
+
return toolCalls.length > 0 ? toolCalls : void 0;
|
|
6333
|
+
}
|
|
6334
|
+
detectPlatform(channelId) {
|
|
6335
|
+
if (channelId.startsWith("telegram-")) return "telegram";
|
|
6336
|
+
if (channelId.startsWith("slack-")) return "slack";
|
|
6337
|
+
if (channelId.startsWith("scheduler-")) return "scheduler";
|
|
6338
|
+
return "web";
|
|
6339
|
+
}
|
|
6340
|
+
};
|
|
6341
|
+
|
|
6342
|
+
// src/runtime/adapters/ipc.ts
|
|
6343
|
+
init_types();
|
|
6344
|
+
var IpcAdapter = class {
|
|
6345
|
+
name = "ipc";
|
|
6346
|
+
agent = null;
|
|
6347
|
+
rootDir = "";
|
|
6348
|
+
adapterMap = null;
|
|
6349
|
+
conversationService = null;
|
|
6350
|
+
messageListener;
|
|
6351
|
+
started = false;
|
|
6352
|
+
async start(ctx) {
|
|
6353
|
+
if (typeof process.send !== "function") {
|
|
6354
|
+
return;
|
|
6355
|
+
}
|
|
6356
|
+
this.agent = ctx.agent;
|
|
6357
|
+
this.rootDir = ctx.rootDir;
|
|
6358
|
+
this.adapterMap = ctx.adapterMap ?? null;
|
|
6359
|
+
this.conversationService = new ConversationService(ctx.rootDir);
|
|
6360
|
+
this.messageListener = (message) => {
|
|
6361
|
+
if (!this.isIpcRequest(message)) return;
|
|
6362
|
+
void this.handleRequest(message);
|
|
6363
|
+
};
|
|
6364
|
+
process.on("message", this.messageListener);
|
|
6365
|
+
this.started = true;
|
|
6366
|
+
console.log("[IpcAdapter] Started");
|
|
6367
|
+
}
|
|
6368
|
+
async stop() {
|
|
6369
|
+
if (this.messageListener) {
|
|
6370
|
+
process.off("message", this.messageListener);
|
|
6371
|
+
this.messageListener = void 0;
|
|
6372
|
+
}
|
|
6373
|
+
if (this.started) {
|
|
6374
|
+
console.log("[IpcAdapter] Stopped");
|
|
6375
|
+
}
|
|
6376
|
+
this.started = false;
|
|
6377
|
+
}
|
|
6378
|
+
notifyReady(port) {
|
|
6379
|
+
this.sendIpc({
|
|
6380
|
+
type: "ready",
|
|
6381
|
+
port
|
|
6382
|
+
});
|
|
6383
|
+
}
|
|
6384
|
+
broadcastInbound(channelId, platform, sender, text) {
|
|
6385
|
+
this.sendIpc({
|
|
6386
|
+
type: "inbound_message",
|
|
6387
|
+
channelId,
|
|
6388
|
+
platform,
|
|
6389
|
+
sender,
|
|
6390
|
+
text,
|
|
6391
|
+
timestamp: Date.now()
|
|
6392
|
+
});
|
|
6393
|
+
}
|
|
6394
|
+
broadcastAgentEvent(channelId, event) {
|
|
6395
|
+
this.sendIpc({
|
|
6396
|
+
type: "agent_event",
|
|
6397
|
+
channelId,
|
|
6398
|
+
event
|
|
6399
|
+
});
|
|
6400
|
+
}
|
|
6401
|
+
isIpcRequest(message) {
|
|
6402
|
+
if (!message || typeof message !== "object") return false;
|
|
6403
|
+
const maybe = message;
|
|
6404
|
+
return typeof maybe.id === "string" && typeof maybe.type === "string";
|
|
6405
|
+
}
|
|
6406
|
+
async handleRequest(request) {
|
|
6407
|
+
if (!this.agent || !this.conversationService) {
|
|
6408
|
+
this.replyError(request.id, "IPC adapter is not ready yet");
|
|
6409
|
+
return;
|
|
6410
|
+
}
|
|
6411
|
+
try {
|
|
6412
|
+
switch (request.type) {
|
|
6413
|
+
case "get_conversations": {
|
|
6414
|
+
const activeChannels = new Set(this.agent.getActiveChannelIds());
|
|
6415
|
+
const conversations = this.conversationService.listConversations(activeChannels);
|
|
6416
|
+
this.reply(request.id, conversations);
|
|
6417
|
+
return;
|
|
6418
|
+
}
|
|
6419
|
+
case "get_messages": {
|
|
6420
|
+
if (!request.channelId || typeof request.channelId !== "string") {
|
|
6421
|
+
this.replyError(request.id, "channelId is required");
|
|
6422
|
+
return;
|
|
6423
|
+
}
|
|
6424
|
+
const messages = this.conversationService.getMessages(
|
|
6425
|
+
request.channelId,
|
|
6426
|
+
request.limit ?? 100
|
|
6427
|
+
);
|
|
6428
|
+
this.reply(request.id, messages);
|
|
6429
|
+
return;
|
|
6430
|
+
}
|
|
6431
|
+
case "send_message": {
|
|
6432
|
+
if (!request.channelId || typeof request.channelId !== "string") {
|
|
6433
|
+
this.replyError(request.id, "channelId is required");
|
|
6434
|
+
return;
|
|
6435
|
+
}
|
|
6436
|
+
if (typeof request.text !== "string") {
|
|
6437
|
+
this.replyError(request.id, "text is required");
|
|
6438
|
+
return;
|
|
6439
|
+
}
|
|
6440
|
+
const platform = this.detectPlatform(request.channelId);
|
|
6441
|
+
let fullText = "";
|
|
6442
|
+
const result = await this.agent.handleMessage(
|
|
6443
|
+
platform,
|
|
6444
|
+
request.channelId,
|
|
6445
|
+
request.text,
|
|
6446
|
+
(event) => {
|
|
6447
|
+
if (event.type === "text_delta") {
|
|
6448
|
+
fullText += event.delta;
|
|
6449
|
+
}
|
|
6450
|
+
this.broadcastAgentEvent(request.channelId, event);
|
|
6451
|
+
}
|
|
6452
|
+
);
|
|
6453
|
+
if (fullText.trim() && platform !== "web" && platform !== "scheduler") {
|
|
6454
|
+
const adapter = this.adapterMap?.get(platform);
|
|
6455
|
+
if (adapter && isMessageSender(adapter)) {
|
|
6456
|
+
await adapter.sendMessage(request.channelId, fullText);
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
this.reply(request.id, {
|
|
6460
|
+
...result,
|
|
6461
|
+
text: fullText
|
|
6462
|
+
});
|
|
6463
|
+
return;
|
|
6464
|
+
}
|
|
6465
|
+
case "command": {
|
|
6466
|
+
if (!request.channelId || typeof request.channelId !== "string") {
|
|
6467
|
+
this.replyError(request.id, "channelId is required");
|
|
6468
|
+
return;
|
|
6469
|
+
}
|
|
6470
|
+
const result = await this.agent.handleCommand(request.command, request.channelId);
|
|
6471
|
+
this.reply(request.id, result);
|
|
6472
|
+
return;
|
|
6473
|
+
}
|
|
6474
|
+
case "get_config": {
|
|
6475
|
+
this.reply(request.id, configManager.getConfig());
|
|
6476
|
+
return;
|
|
6477
|
+
}
|
|
6478
|
+
case "update_config": {
|
|
6479
|
+
configManager.save(this.rootDir, request.updates || {});
|
|
6480
|
+
const updated = configManager.getConfig();
|
|
6481
|
+
const provider = updated.provider || "openai";
|
|
6482
|
+
this.agent.updateAuth(provider, updated.apiKey);
|
|
6483
|
+
this.reply(request.id, updated);
|
|
6484
|
+
return;
|
|
6485
|
+
}
|
|
6486
|
+
case "get_status": {
|
|
6487
|
+
this.reply(request.id, {
|
|
6488
|
+
status: "running",
|
|
6489
|
+
pid: process.pid
|
|
6490
|
+
});
|
|
6491
|
+
return;
|
|
6492
|
+
}
|
|
6493
|
+
case "get_scheduled_jobs": {
|
|
6494
|
+
const scheduler = this.getSchedulerAdapter();
|
|
6495
|
+
this.reply(request.id, scheduler ? scheduler.listJobs() : []);
|
|
6496
|
+
return;
|
|
6497
|
+
}
|
|
6498
|
+
case "add_scheduled_job": {
|
|
6499
|
+
const scheduler = this.getSchedulerAdapter();
|
|
6500
|
+
if (!scheduler) {
|
|
6501
|
+
this.replyError(request.id, "Scheduler adapter is not available");
|
|
6502
|
+
return;
|
|
6503
|
+
}
|
|
6504
|
+
const result = scheduler.addJob(request.job);
|
|
6505
|
+
if (!result.success) {
|
|
6506
|
+
this.replyError(request.id, result.message);
|
|
6507
|
+
return;
|
|
6508
|
+
}
|
|
6509
|
+
this.reply(request.id, result);
|
|
6510
|
+
return;
|
|
6511
|
+
}
|
|
6512
|
+
case "remove_scheduled_job": {
|
|
6513
|
+
const scheduler = this.getSchedulerAdapter();
|
|
6514
|
+
if (!scheduler) {
|
|
6515
|
+
this.replyError(request.id, "Scheduler adapter is not available");
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
const result = scheduler.removeJob(request.name);
|
|
6519
|
+
if (!result.success) {
|
|
6520
|
+
this.replyError(request.id, result.message);
|
|
6521
|
+
return;
|
|
6522
|
+
}
|
|
6523
|
+
this.reply(request.id, result);
|
|
6524
|
+
return;
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
} catch (err) {
|
|
6528
|
+
this.replyError(
|
|
6529
|
+
request.id,
|
|
6530
|
+
err instanceof Error ? err.message : String(err)
|
|
6531
|
+
);
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
getSchedulerAdapter() {
|
|
6535
|
+
const adapter = this.adapterMap?.get("scheduler");
|
|
6536
|
+
if (!adapter) return null;
|
|
6537
|
+
return adapter;
|
|
6538
|
+
}
|
|
6539
|
+
detectPlatform(channelId) {
|
|
6540
|
+
if (channelId.startsWith("telegram-")) return "telegram";
|
|
6541
|
+
if (channelId.startsWith("slack-")) return "slack";
|
|
6542
|
+
if (channelId.startsWith("scheduler-")) return "scheduler";
|
|
6543
|
+
return "web";
|
|
6544
|
+
}
|
|
6545
|
+
sendIpc(payload) {
|
|
6546
|
+
if (typeof process.send === "function") {
|
|
6547
|
+
process.send(payload);
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
reply(id, data) {
|
|
6551
|
+
this.sendIpc({ id, type: "result", data });
|
|
6552
|
+
}
|
|
6553
|
+
replyError(id, message) {
|
|
6554
|
+
this.sendIpc({ id, type: "error", message });
|
|
6555
|
+
}
|
|
6556
|
+
};
|
|
6557
|
+
|
|
5916
6558
|
// src/runtime/server.ts
|
|
5917
6559
|
init_config();
|
|
5918
6560
|
|
|
@@ -5989,28 +6631,28 @@ var Lifecycle = class {
|
|
|
5989
6631
|
|
|
5990
6632
|
// src/runtime/registry.ts
|
|
5991
6633
|
import crypto from "crypto";
|
|
5992
|
-
import
|
|
6634
|
+
import fs12 from "fs";
|
|
5993
6635
|
import os from "os";
|
|
5994
|
-
import
|
|
5995
|
-
var SKILLPACK_HOME =
|
|
5996
|
-
var LEGACY_REGISTRY_FILE =
|
|
5997
|
-
var REGISTRY_DIR =
|
|
6636
|
+
import path12 from "path";
|
|
6637
|
+
var SKILLPACK_HOME = path12.join(os.homedir(), ".skillpack");
|
|
6638
|
+
var LEGACY_REGISTRY_FILE = path12.join(SKILLPACK_HOME, "registry.json");
|
|
6639
|
+
var REGISTRY_DIR = path12.join(SKILLPACK_HOME, "registry.d");
|
|
5998
6640
|
var migrationChecked = false;
|
|
5999
6641
|
function ensureHomeDir() {
|
|
6000
|
-
if (!
|
|
6001
|
-
|
|
6642
|
+
if (!fs12.existsSync(SKILLPACK_HOME)) {
|
|
6643
|
+
fs12.mkdirSync(SKILLPACK_HOME, { recursive: true });
|
|
6002
6644
|
}
|
|
6003
6645
|
}
|
|
6004
6646
|
function ensureRegistryDir() {
|
|
6005
6647
|
ensureHomeDir();
|
|
6006
|
-
if (!
|
|
6007
|
-
|
|
6648
|
+
if (!fs12.existsSync(REGISTRY_DIR)) {
|
|
6649
|
+
fs12.mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
6008
6650
|
}
|
|
6009
6651
|
}
|
|
6010
6652
|
function canonicalizeDir(dir) {
|
|
6011
|
-
const resolved =
|
|
6653
|
+
const resolved = path12.resolve(dir);
|
|
6012
6654
|
try {
|
|
6013
|
-
return
|
|
6655
|
+
return fs12.realpathSync(resolved);
|
|
6014
6656
|
} catch {
|
|
6015
6657
|
return resolved;
|
|
6016
6658
|
}
|
|
@@ -6019,7 +6661,7 @@ function hashDir(dir) {
|
|
|
6019
6661
|
return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
|
|
6020
6662
|
}
|
|
6021
6663
|
function getEntryPathForCanonicalDir(dir) {
|
|
6022
|
-
return
|
|
6664
|
+
return path12.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
|
|
6023
6665
|
}
|
|
6024
6666
|
function getEntryPath(dir) {
|
|
6025
6667
|
ensureRegistryReady();
|
|
@@ -6027,11 +6669,11 @@ function getEntryPath(dir) {
|
|
|
6027
6669
|
}
|
|
6028
6670
|
function listEntryFiles() {
|
|
6029
6671
|
ensureRegistryReady();
|
|
6030
|
-
return
|
|
6672
|
+
return fs12.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path12.join(REGISTRY_DIR, file));
|
|
6031
6673
|
}
|
|
6032
6674
|
function readEntryFile(filePath) {
|
|
6033
6675
|
try {
|
|
6034
|
-
const raw =
|
|
6676
|
+
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
6035
6677
|
const data = JSON.parse(raw);
|
|
6036
6678
|
if (typeof data?.dir !== "string" || typeof data?.name !== "string" || typeof data?.version !== "string" || typeof data?.port !== "number" || typeof data?.pid !== "number" && data?.pid !== null || data?.status !== "running" && data?.status !== "stopped") {
|
|
6037
6679
|
return null;
|
|
@@ -6064,8 +6706,8 @@ function writeEntryFile(entry) {
|
|
|
6064
6706
|
};
|
|
6065
6707
|
const entryPath = getEntryPathForCanonicalDir(normalized.dir);
|
|
6066
6708
|
const tmpPath = createTmpPath(entryPath);
|
|
6067
|
-
|
|
6068
|
-
|
|
6709
|
+
fs12.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
6710
|
+
fs12.renameSync(tmpPath, entryPath);
|
|
6069
6711
|
}
|
|
6070
6712
|
function migrateLegacyRegistryIfNeeded() {
|
|
6071
6713
|
if (migrationChecked) {
|
|
@@ -6073,14 +6715,14 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
6073
6715
|
}
|
|
6074
6716
|
migrationChecked = true;
|
|
6075
6717
|
ensureRegistryDir();
|
|
6076
|
-
if (!
|
|
6718
|
+
if (!fs12.existsSync(LEGACY_REGISTRY_FILE)) {
|
|
6077
6719
|
return;
|
|
6078
6720
|
}
|
|
6079
6721
|
if (listEntryFiles().length > 0) {
|
|
6080
6722
|
return;
|
|
6081
6723
|
}
|
|
6082
6724
|
try {
|
|
6083
|
-
const raw =
|
|
6725
|
+
const raw = fs12.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
|
|
6084
6726
|
const data = JSON.parse(raw);
|
|
6085
6727
|
const packs = Array.isArray(data?.packs) ? data.packs : [];
|
|
6086
6728
|
for (const pack of packs) {
|
|
@@ -6093,7 +6735,7 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
6093
6735
|
} catch {
|
|
6094
6736
|
}
|
|
6095
6737
|
}
|
|
6096
|
-
|
|
6738
|
+
fs12.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
|
|
6097
6739
|
} catch (err) {
|
|
6098
6740
|
console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
|
|
6099
6741
|
}
|
|
@@ -6146,7 +6788,7 @@ function deregister(dir, pid) {
|
|
|
6146
6788
|
}
|
|
6147
6789
|
|
|
6148
6790
|
// src/runtime/server.ts
|
|
6149
|
-
var __dirname =
|
|
6791
|
+
var __dirname = path14.dirname(fileURLToPath2(import.meta.url));
|
|
6150
6792
|
async function startServer(options) {
|
|
6151
6793
|
const {
|
|
6152
6794
|
rootDir,
|
|
@@ -6161,8 +6803,8 @@ async function startServer(options) {
|
|
|
6161
6803
|
const packConfig = loadConfig(canonicalRootDir);
|
|
6162
6804
|
const baseUrl = dataConfig.baseUrl?.trim() || void 0;
|
|
6163
6805
|
const modelId = SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId;
|
|
6164
|
-
const packageRoot =
|
|
6165
|
-
const webDir =
|
|
6806
|
+
const packageRoot = path14.resolve(__dirname, "..");
|
|
6807
|
+
const webDir = fs15.existsSync(path14.join(rootDir, "web")) ? path14.join(rootDir, "web") : path14.join(packageRoot, "web");
|
|
6166
6808
|
const app = express();
|
|
6167
6809
|
app.use(express.json());
|
|
6168
6810
|
app.use(express.static(webDir));
|
|
@@ -6190,8 +6832,31 @@ async function startServer(options) {
|
|
|
6190
6832
|
});
|
|
6191
6833
|
const adapters = [];
|
|
6192
6834
|
const adapterMap = /* @__PURE__ */ new Map();
|
|
6835
|
+
const hasIpcChannel = typeof process.send === "function";
|
|
6836
|
+
const ipcAdapter = new IpcAdapter();
|
|
6837
|
+
if (hasIpcChannel) {
|
|
6838
|
+
await ipcAdapter.start({
|
|
6839
|
+
agent,
|
|
6840
|
+
server,
|
|
6841
|
+
app,
|
|
6842
|
+
rootDir,
|
|
6843
|
+
lifecycle,
|
|
6844
|
+
adapterMap
|
|
6845
|
+
});
|
|
6846
|
+
adapters.push(ipcAdapter);
|
|
6847
|
+
adapterMap.set(ipcAdapter.name, ipcAdapter);
|
|
6848
|
+
}
|
|
6849
|
+
const ipcBroadcaster = hasIpcChannel ? ipcAdapter : void 0;
|
|
6193
6850
|
const webAdapter = new WebAdapter();
|
|
6194
|
-
await webAdapter.start({
|
|
6851
|
+
await webAdapter.start({
|
|
6852
|
+
agent,
|
|
6853
|
+
server,
|
|
6854
|
+
app,
|
|
6855
|
+
rootDir,
|
|
6856
|
+
lifecycle,
|
|
6857
|
+
adapterMap,
|
|
6858
|
+
ipcBroadcaster
|
|
6859
|
+
});
|
|
6195
6860
|
adapters.push(webAdapter);
|
|
6196
6861
|
adapterMap.set(webAdapter.name, webAdapter);
|
|
6197
6862
|
if (dataConfig.adapters?.telegram?.token) {
|
|
@@ -6200,7 +6865,15 @@ async function startServer(options) {
|
|
|
6200
6865
|
const telegramAdapter = new TelegramAdapter2({
|
|
6201
6866
|
token: dataConfig.adapters.telegram.token
|
|
6202
6867
|
});
|
|
6203
|
-
await telegramAdapter.start({
|
|
6868
|
+
await telegramAdapter.start({
|
|
6869
|
+
agent,
|
|
6870
|
+
server,
|
|
6871
|
+
app,
|
|
6872
|
+
rootDir,
|
|
6873
|
+
lifecycle,
|
|
6874
|
+
adapterMap,
|
|
6875
|
+
ipcBroadcaster
|
|
6876
|
+
});
|
|
6204
6877
|
adapters.push(telegramAdapter);
|
|
6205
6878
|
adapterMap.set(telegramAdapter.name, telegramAdapter);
|
|
6206
6879
|
} catch (err) {
|
|
@@ -6220,7 +6893,15 @@ async function startServer(options) {
|
|
|
6220
6893
|
botToken: slackConfig.botToken,
|
|
6221
6894
|
appToken: slackConfig.appToken
|
|
6222
6895
|
});
|
|
6223
|
-
await slackAdapter.start({
|
|
6896
|
+
await slackAdapter.start({
|
|
6897
|
+
agent,
|
|
6898
|
+
server,
|
|
6899
|
+
app,
|
|
6900
|
+
rootDir,
|
|
6901
|
+
lifecycle,
|
|
6902
|
+
adapterMap,
|
|
6903
|
+
ipcBroadcaster
|
|
6904
|
+
});
|
|
6224
6905
|
adapters.push(slackAdapter);
|
|
6225
6906
|
adapterMap.set(slackAdapter.name, slackAdapter);
|
|
6226
6907
|
} catch (err) {
|
|
@@ -6283,6 +6964,9 @@ async function startServer(options) {
|
|
|
6283
6964
|
} catch (err) {
|
|
6284
6965
|
console.warn(" [Registry] Could not register pack:", err);
|
|
6285
6966
|
}
|
|
6967
|
+
if (hasIpcChannel) {
|
|
6968
|
+
ipcAdapter.notifyReady(typeof actualPort === "number" ? actualPort : port);
|
|
6969
|
+
}
|
|
6286
6970
|
if (!daemonRun) {
|
|
6287
6971
|
const cmd = process.platform === "darwin" ? `open ${url}` : process.platform === "win32" ? `start ${url}` : `xdg-open ${url}`;
|
|
6288
6972
|
exec(cmd, (err) => {
|
|
@@ -6330,23 +7014,23 @@ function findMissingSkills(workDir, config) {
|
|
|
6330
7014
|
});
|
|
6331
7015
|
}
|
|
6332
7016
|
function copyStartTemplates2(workDir) {
|
|
6333
|
-
const templateDir =
|
|
7017
|
+
const templateDir = path15.resolve(
|
|
6334
7018
|
new URL("../templates", import.meta.url).pathname
|
|
6335
7019
|
);
|
|
6336
7020
|
for (const file of ["start.sh", "start.bat"]) {
|
|
6337
|
-
const src =
|
|
6338
|
-
const dest =
|
|
6339
|
-
if (
|
|
6340
|
-
|
|
7021
|
+
const src = path15.join(templateDir, file);
|
|
7022
|
+
const dest = path15.join(workDir, file);
|
|
7023
|
+
if (fs16.existsSync(src)) {
|
|
7024
|
+
fs16.copyFileSync(src, dest);
|
|
6341
7025
|
if (file === "start.sh") {
|
|
6342
|
-
|
|
7026
|
+
fs16.chmodSync(dest, 493);
|
|
6343
7027
|
}
|
|
6344
7028
|
}
|
|
6345
7029
|
}
|
|
6346
7030
|
}
|
|
6347
7031
|
async function runCommand(directory) {
|
|
6348
|
-
const workDir = directory ?
|
|
6349
|
-
|
|
7032
|
+
const workDir = directory ? path15.resolve(directory) : process.cwd();
|
|
7033
|
+
fs16.mkdirSync(workDir, { recursive: true });
|
|
6350
7034
|
if (!configExists(workDir)) {
|
|
6351
7035
|
console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
|
|
6352
7036
|
const { name, description } = await inquirer2.prompt([
|
|
@@ -6391,9 +7075,9 @@ async function runCommand(directory) {
|
|
|
6391
7075
|
}
|
|
6392
7076
|
|
|
6393
7077
|
// src/cli.ts
|
|
6394
|
-
import
|
|
7078
|
+
import fs17 from "fs";
|
|
6395
7079
|
var packageJson = JSON.parse(
|
|
6396
|
-
|
|
7080
|
+
fs17.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
6397
7081
|
);
|
|
6398
7082
|
var program = new Command();
|
|
6399
7083
|
program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
|