@cremini/skillpack 1.2.3 → 1.2.4
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 +377 -135
- package/package.json +2 -2
- package/web/js/chat.js +0 -15
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Skillpack is built for teams that want AI Agents to be deployable, trusted, and
|
|
|
23
23
|
1. Download the example
|
|
24
24
|
- [Garry Tan SkillPack](https://github.com/CreminiAI/skillpack-examples/releases/download/v.0.0.3/garry-tan.zip)
|
|
25
25
|
- [Company Deep Research SkillPack](https://github.com/FinpeakInc/downloads/releases/download/v.0.0.1/Company-Deep-Research.zip)
|
|
26
|
-
2. Unzip it and Run ./start.sh on Mac OS,
|
|
26
|
+
2. Unzip it and Run ./start.sh on Mac OS, Or double click start.bat on Windows (see below), the server starts and opens http://127.0.0.1:26313 in your browser
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# macOS / Linux
|
|
@@ -110,76 +110,14 @@ If present, `AGENTS.md` and `SOUL.md` are read by SkillPack itself when a new ch
|
|
|
110
110
|
|
|
111
111
|
## Slack/Telegram Integrations
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
**Telegram configuration**: requires `Bot Token`
|
|
113
|
+
Talk to your Agents on Slack and Telegram
|
|
115
114
|
|
|
116
|
-
###
|
|
115
|
+
### 5 mins to get Slack `App Token` and `Bot Token`
|
|
117
116
|
https://skillpack.gitbook.io/skillpack-docs/getting-started/slack-integration
|
|
118
117
|
|
|
119
|
-
1
|
|
120
|
-
2. Enable Socket Mode (Settings → Socket Mode → Enable)
|
|
121
|
-
3. Generate an App-Level Token with `connections:write` scope. This is **`App Token`**
|
|
122
|
-
4. Add Bot Token Scopes (OAuth & Permissions):
|
|
123
|
-
|
|
124
|
-
- `app_mentions:read`
|
|
125
|
-
- `channels:history`
|
|
126
|
-
- `channels:read`
|
|
127
|
-
- `chat:write`
|
|
128
|
-
- `files:read`
|
|
129
|
-
- `files:write`
|
|
130
|
-
- `groups:history`
|
|
131
|
-
- `groups:read`
|
|
132
|
-
- `im:history`
|
|
133
|
-
- `im:read`
|
|
134
|
-
- `im:write`
|
|
135
|
-
- `users:read`
|
|
136
|
-
|
|
137
|
-
5. Subscribe to Bot Events (Event Subscriptions):
|
|
138
|
-
|
|
139
|
-
- `app_mention`
|
|
140
|
-
- `message.channels`
|
|
141
|
-
- `message.groups`
|
|
142
|
-
- `message.im`
|
|
143
|
-
|
|
144
|
-
6. Enable Direct Messages (App Home):
|
|
145
|
-
Go to App Home in the left sidebar
|
|
146
|
-
Under Show Tabs, enable the Messages Tab
|
|
147
|
-
Check Allow users to send Slash commands and messages from the messages tab
|
|
148
|
-
|
|
149
|
-
7. Install the app to your workspace. Get the Bot User OAuth Token. This is **`Bot Token`**
|
|
150
|
-
8. Add the app to any channels where you want the agent to operate (it'll only see messages in channels it's added to)
|
|
151
|
-
9. On the SkillPack buit-in UI http://127.0.0.1:26313, Tap "Connect to Chat App" button and Enter the **`Bot Token`** and **`App Token`**, Save
|
|
152
|
-
|
|
153
|
-
### Telegram Setup and how to get `Bot Token`
|
|
118
|
+
### 1 min to get Telegram `Bot Token`
|
|
154
119
|
https://skillpack.gitbook.io/skillpack-docs/getting-started/telegram-integration
|
|
155
120
|
|
|
156
|
-
1. **Open Telegram** and search for the official account **`@BotFather`** (it will have a blue verified checkmark).
|
|
157
|
-
2. **Start a chat** by tapping "Start" or sending the `/start` command.
|
|
158
|
-
3. **Send the command** `/newbot` to the BotFather.
|
|
159
|
-
4. **Follow the prompts** to choose a display name and a unique username for your bot. The username must end with the word "bot" (e.g., `MyHelperBot` or `My_Helper_bot`).
|
|
160
|
-
5. **Receive the token**. Once the bot is successfully created, the BotFather will provide you with a message containing your unique API token.
|
|
161
|
-
The token will look like a long string of numbers and letters, formatted as `123456789:AABBCCddEeff.... `
|
|
162
|
-
6. On the SkillPack buit-in UI http://127.0.0.1:26313, Tap "Connect to Chat App" button and Enter the **`Bot Token`**, Save
|
|
163
|
-
|
|
164
|
-
### (Optional) Put tokens into data/config.json if you don't use Web UI
|
|
165
|
-
|
|
166
|
-
Or Once you have telegram or slack tokens, you can also configure them in `data/config.json` (created at runtime, not included in the zip):
|
|
167
|
-
The runtime supports **Slack** and **Telegram** in addition to the built-in web UI.
|
|
168
|
-
|
|
169
|
-
```json
|
|
170
|
-
{
|
|
171
|
-
"adapters": {
|
|
172
|
-
"telegram": {
|
|
173
|
-
"token": "123456:ABC-DEF..."
|
|
174
|
-
},
|
|
175
|
-
"slack": {
|
|
176
|
-
"botToken": "xoxb-...",
|
|
177
|
-
"appToken": "xapp-..."
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
121
|
---
|
|
184
122
|
|
|
185
123
|
## Example Use Cases
|
package/dist/cli.js
CHANGED
|
@@ -267,6 +267,62 @@ 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
|
+
|
|
270
326
|
// src/runtime/adapters/markdown.ts
|
|
271
327
|
function unwrapMarkdownSourceBlocks(text) {
|
|
272
328
|
return text.replace(
|
|
@@ -307,6 +363,7 @@ function formatSlackInline(text) {
|
|
|
307
363
|
);
|
|
308
364
|
formatted = formatted.replace(/\*\*([^*\n]+)\*\*/g, "*$1*");
|
|
309
365
|
formatted = formatted.replace(/__([^_\n]+)__/g, "*$1*");
|
|
366
|
+
formatted = formatted.replace(/^(?:-|\*) /gm, "\u2022 ");
|
|
310
367
|
return formatted;
|
|
311
368
|
}
|
|
312
369
|
function formatTelegramInline(text) {
|
|
@@ -386,19 +443,15 @@ var telegram_exports = {};
|
|
|
386
443
|
__export(telegram_exports, {
|
|
387
444
|
TelegramAdapter: () => TelegramAdapter
|
|
388
445
|
});
|
|
389
|
-
import
|
|
446
|
+
import fs12 from "fs";
|
|
390
447
|
import TelegramBot from "node-telegram-bot-api";
|
|
391
|
-
var
|
|
448
|
+
var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
|
|
392
449
|
var init_telegram = __esm({
|
|
393
450
|
"src/runtime/adapters/telegram.ts"() {
|
|
394
451
|
"use strict";
|
|
395
452
|
init_markdown();
|
|
396
453
|
init_attachment_utils();
|
|
397
|
-
|
|
398
|
-
"/clear": "clear",
|
|
399
|
-
"/restart": "restart",
|
|
400
|
-
"/shutdown": "shutdown"
|
|
401
|
-
};
|
|
454
|
+
init_commands();
|
|
402
455
|
MAX_MESSAGE_LENGTH = 4096;
|
|
403
456
|
ACK_REACTION = {
|
|
404
457
|
type: "emoji",
|
|
@@ -422,11 +475,7 @@ var init_telegram = __esm({
|
|
|
422
475
|
console.error("[Telegram] Error handling message:", err);
|
|
423
476
|
});
|
|
424
477
|
});
|
|
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
|
-
]);
|
|
478
|
+
await this.bot.setMyCommands(getTelegramBotCommands());
|
|
430
479
|
const me = await this.bot.getMe();
|
|
431
480
|
console.log(`[TelegramAdapter] Started as @${me.username}`);
|
|
432
481
|
}
|
|
@@ -466,7 +515,7 @@ var init_telegram = __esm({
|
|
|
466
515
|
await this.tryAckReaction(chatId, messageId);
|
|
467
516
|
if (text) {
|
|
468
517
|
const commandKey = text.split(/\s/)[0].toLowerCase();
|
|
469
|
-
const command =
|
|
518
|
+
const command = this.resolveTelegramCommand(commandKey);
|
|
470
519
|
if (command) {
|
|
471
520
|
const result = await this.agent.handleCommand(command, channelId);
|
|
472
521
|
await this.sendSafe(chatId, result.message || `/${command} executed.`);
|
|
@@ -521,6 +570,9 @@ var init_telegram = __esm({
|
|
|
521
570
|
await this.sendFileSafe(chatId, file.filePath, file.caption);
|
|
522
571
|
}
|
|
523
572
|
}
|
|
573
|
+
resolveTelegramCommand(commandKey) {
|
|
574
|
+
return resolveCommand(commandKey);
|
|
575
|
+
}
|
|
524
576
|
// -------------------------------------------------------------------------
|
|
525
577
|
// Send helpers
|
|
526
578
|
// -------------------------------------------------------------------------
|
|
@@ -697,7 +749,7 @@ var init_telegram = __esm({
|
|
|
697
749
|
async sendFileSafe(chatId, filePath, caption) {
|
|
698
750
|
if (!this.bot) return;
|
|
699
751
|
try {
|
|
700
|
-
if (!
|
|
752
|
+
if (!fs12.existsSync(filePath)) {
|
|
701
753
|
console.error(`[Telegram] File not found for sending: ${filePath}`);
|
|
702
754
|
return;
|
|
703
755
|
}
|
|
@@ -717,16 +769,18 @@ var slack_exports = {};
|
|
|
717
769
|
__export(slack_exports, {
|
|
718
770
|
SlackAdapter: () => SlackAdapter
|
|
719
771
|
});
|
|
720
|
-
import
|
|
721
|
-
import
|
|
772
|
+
import fs13 from "fs";
|
|
773
|
+
import path12 from "path";
|
|
722
774
|
import { App, LogLevel } from "@slack/bolt";
|
|
723
|
-
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, SlackAdapter;
|
|
775
|
+
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
|
|
724
776
|
var init_slack = __esm({
|
|
725
777
|
"src/runtime/adapters/slack.ts"() {
|
|
726
778
|
"use strict";
|
|
727
779
|
init_markdown();
|
|
728
780
|
init_attachment_utils();
|
|
781
|
+
init_commands();
|
|
729
782
|
INLINE_COMMANDS = {
|
|
783
|
+
"/help": "help",
|
|
730
784
|
"/clear": "clear",
|
|
731
785
|
"/restart": "restart",
|
|
732
786
|
"/shutdown": "shutdown"
|
|
@@ -738,6 +792,7 @@ var init_slack = __esm({
|
|
|
738
792
|
};
|
|
739
793
|
MAX_MESSAGE_LENGTH2 = 3500;
|
|
740
794
|
ACK_REACTION2 = "eyes";
|
|
795
|
+
PROCESSING_MESSAGE = "_Processing..._";
|
|
741
796
|
SlackAdapter = class {
|
|
742
797
|
name = "slack";
|
|
743
798
|
app = null;
|
|
@@ -807,7 +862,7 @@ var init_slack = __esm({
|
|
|
807
862
|
console.error("[Slack] Error handling mention:", err);
|
|
808
863
|
}
|
|
809
864
|
});
|
|
810
|
-
for (const commandName of Object.keys(SLASH_COMMANDS)) {
|
|
865
|
+
for (const commandName of [...Object.keys(SLASH_COMMANDS), "/new"]) {
|
|
811
866
|
app.command(commandName, async (args) => {
|
|
812
867
|
try {
|
|
813
868
|
await this.handleSlashCommand(args);
|
|
@@ -872,7 +927,7 @@ var init_slack = __esm({
|
|
|
872
927
|
await this.sendSafe(
|
|
873
928
|
client,
|
|
874
929
|
route,
|
|
875
|
-
"Mention me with a message, or use `/clear` to reset this thread."
|
|
930
|
+
"Mention me with a message, or use `/clear` or `/new` to reset this thread."
|
|
876
931
|
);
|
|
877
932
|
return;
|
|
878
933
|
}
|
|
@@ -890,7 +945,7 @@ var init_slack = __esm({
|
|
|
890
945
|
ack
|
|
891
946
|
}) {
|
|
892
947
|
const commandName = command?.command;
|
|
893
|
-
const mapped = commandName ?
|
|
948
|
+
const mapped = commandName ? this.resolveSlashCommand(commandName) : void 0;
|
|
894
949
|
if (!this.agent || !mapped) {
|
|
895
950
|
await this.safeAck(ack, "Unsupported slash command.");
|
|
896
951
|
return;
|
|
@@ -916,6 +971,7 @@ var init_slack = __esm({
|
|
|
916
971
|
let hasError = false;
|
|
917
972
|
let errorMessage = "";
|
|
918
973
|
const pendingFiles = [];
|
|
974
|
+
const placeholder = await this.sendPlaceholderMessage(client, route);
|
|
919
975
|
const onEvent = (event) => {
|
|
920
976
|
if (event.type === "text_delta") {
|
|
921
977
|
finalText += event.delta;
|
|
@@ -943,13 +999,25 @@ var init_slack = __esm({
|
|
|
943
999
|
errorMessage = this.getErrorMessage(err);
|
|
944
1000
|
}
|
|
945
1001
|
if (hasError) {
|
|
946
|
-
await this.
|
|
1002
|
+
await this.sendOrUpdateSafe(
|
|
1003
|
+
client,
|
|
1004
|
+
route,
|
|
1005
|
+
`\u274C Error: ${errorMessage}`,
|
|
1006
|
+
placeholder
|
|
1007
|
+
);
|
|
947
1008
|
return;
|
|
948
1009
|
}
|
|
949
1010
|
if (finalText.trim()) {
|
|
950
|
-
await this.sendLongMessage(client, route, finalText);
|
|
1011
|
+
await this.sendLongMessage(client, route, finalText, placeholder);
|
|
951
1012
|
} else if (pendingFiles.length === 0) {
|
|
952
|
-
await this.
|
|
1013
|
+
await this.sendOrUpdateSafe(
|
|
1014
|
+
client,
|
|
1015
|
+
route,
|
|
1016
|
+
"(No response generated)",
|
|
1017
|
+
placeholder
|
|
1018
|
+
);
|
|
1019
|
+
} else if (placeholder) {
|
|
1020
|
+
await this.deleteMessageSafe(client, route, placeholder.ts);
|
|
953
1021
|
}
|
|
954
1022
|
for (const file of pendingFiles) {
|
|
955
1023
|
await this.sendFileSafe(client, route, file.filePath, file.caption);
|
|
@@ -961,7 +1029,7 @@ var init_slack = __esm({
|
|
|
961
1029
|
async tryHandleInlineCommand(text, channelId, client, route) {
|
|
962
1030
|
if (!this.agent) return false;
|
|
963
1031
|
const commandKey = text.split(/\s/)[0].toLowerCase();
|
|
964
|
-
const command =
|
|
1032
|
+
const command = this.resolveInlineCommand(commandKey);
|
|
965
1033
|
if (!command) return false;
|
|
966
1034
|
const result = await this.agent.handleCommand(command, channelId);
|
|
967
1035
|
await this.sendSafe(
|
|
@@ -971,6 +1039,19 @@ var init_slack = __esm({
|
|
|
971
1039
|
);
|
|
972
1040
|
return true;
|
|
973
1041
|
}
|
|
1042
|
+
resolveInlineCommand(commandKey) {
|
|
1043
|
+
const resolved = resolveCommand(commandKey);
|
|
1044
|
+
if (resolved) {
|
|
1045
|
+
return resolved;
|
|
1046
|
+
}
|
|
1047
|
+
return INLINE_COMMANDS[commandKey];
|
|
1048
|
+
}
|
|
1049
|
+
resolveSlashCommand(commandName) {
|
|
1050
|
+
if (commandName === "/new") {
|
|
1051
|
+
return "clear";
|
|
1052
|
+
}
|
|
1053
|
+
return SLASH_COMMANDS[commandName];
|
|
1054
|
+
}
|
|
974
1055
|
resolveSlashCommandTarget(payload, context) {
|
|
975
1056
|
const teamId = this.getTeamId(payload, context);
|
|
976
1057
|
const channel = payload?.channel_id;
|
|
@@ -988,7 +1069,7 @@ var init_slack = __esm({
|
|
|
988
1069
|
);
|
|
989
1070
|
if (!threadTs) {
|
|
990
1071
|
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`."
|
|
1072
|
+
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
1073
|
};
|
|
993
1074
|
}
|
|
994
1075
|
return {
|
|
@@ -1017,33 +1098,35 @@ var init_slack = __esm({
|
|
|
1017
1098
|
return text.replace(mention, "");
|
|
1018
1099
|
}
|
|
1019
1100
|
splitMessage(text) {
|
|
1020
|
-
if (text
|
|
1101
|
+
if (this.isSlackMessageWithinLimit(text)) {
|
|
1021
1102
|
return [text];
|
|
1022
1103
|
}
|
|
1023
1104
|
const chunks = [];
|
|
1024
1105
|
let remaining = text;
|
|
1025
1106
|
while (remaining.length > 0) {
|
|
1026
|
-
if (remaining
|
|
1107
|
+
if (this.isSlackMessageWithinLimit(remaining)) {
|
|
1027
1108
|
chunks.push(remaining);
|
|
1028
1109
|
break;
|
|
1029
1110
|
}
|
|
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
|
-
}
|
|
1111
|
+
let splitAt = this.findSlackSafeSplitPoint(remaining);
|
|
1040
1112
|
chunks.push(remaining.slice(0, splitAt));
|
|
1041
1113
|
remaining = remaining.slice(splitAt).trimStart();
|
|
1042
1114
|
}
|
|
1043
1115
|
return chunks;
|
|
1044
1116
|
}
|
|
1045
|
-
async sendLongMessage(client, route, text) {
|
|
1046
|
-
|
|
1117
|
+
async sendLongMessage(client, route, text, placeholder) {
|
|
1118
|
+
const chunks = this.splitMessage(text);
|
|
1119
|
+
if (chunks.length === 0) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (placeholder) {
|
|
1123
|
+
await this.updateMessageSafe(client, route, placeholder.ts, chunks[0]);
|
|
1124
|
+
for (const chunk of chunks.slice(1)) {
|
|
1125
|
+
await this.sendSafe(client, route, chunk);
|
|
1126
|
+
}
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
for (const chunk of chunks) {
|
|
1047
1130
|
await this.sendWithRetry(client, route, chunk);
|
|
1048
1131
|
}
|
|
1049
1132
|
}
|
|
@@ -1054,17 +1137,35 @@ var init_slack = __esm({
|
|
|
1054
1137
|
console.error("[Slack] Failed to send message:", err);
|
|
1055
1138
|
}
|
|
1056
1139
|
}
|
|
1140
|
+
async sendOrUpdateSafe(client, route, text, placeholder) {
|
|
1141
|
+
if (placeholder) {
|
|
1142
|
+
await this.updateMessageSafe(client, route, placeholder.ts, text);
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
await this.sendSafe(client, route, text);
|
|
1146
|
+
}
|
|
1147
|
+
async sendPlaceholderMessage(client, route) {
|
|
1148
|
+
try {
|
|
1149
|
+
return await this.sendWithRetry(client, route, PROCESSING_MESSAGE);
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
console.error("[Slack] Failed to send placeholder message:", err);
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1057
1155
|
async sendWithRetry(client, route, text, maxRetries = 3) {
|
|
1058
1156
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1059
1157
|
try {
|
|
1060
|
-
await client.chat.postMessage({
|
|
1158
|
+
const response = await client.chat.postMessage({
|
|
1061
1159
|
channel: route.channel,
|
|
1062
1160
|
text: formatSlackMessage(text),
|
|
1063
1161
|
mrkdwn: true,
|
|
1064
1162
|
thread_ts: route.threadTs,
|
|
1065
1163
|
reply_broadcast: false
|
|
1066
1164
|
});
|
|
1067
|
-
|
|
1165
|
+
if (typeof response.ts !== "string") {
|
|
1166
|
+
throw new Error("Slack postMessage response missing ts");
|
|
1167
|
+
}
|
|
1168
|
+
return { ts: response.ts };
|
|
1068
1169
|
} catch (err) {
|
|
1069
1170
|
const retryAfter = this.getRetryAfterSeconds(err);
|
|
1070
1171
|
if (retryAfter && attempt < maxRetries) {
|
|
@@ -1079,6 +1180,93 @@ var init_slack = __esm({
|
|
|
1079
1180
|
throw err;
|
|
1080
1181
|
}
|
|
1081
1182
|
}
|
|
1183
|
+
throw new Error("Slack postMessage failed after retries");
|
|
1184
|
+
}
|
|
1185
|
+
async updateMessageSafe(client, route, ts, text) {
|
|
1186
|
+
try {
|
|
1187
|
+
await this.updateWithRetry(client, route, ts, text);
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
console.error("[Slack] Failed to update message:", err);
|
|
1190
|
+
await this.deleteMessageSafe(client, route, ts);
|
|
1191
|
+
await this.sendSafe(client, route, text);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
async updateWithRetry(client, route, ts, text, maxRetries = 3) {
|
|
1195
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1196
|
+
try {
|
|
1197
|
+
await client.chat.update({
|
|
1198
|
+
channel: route.channel,
|
|
1199
|
+
ts,
|
|
1200
|
+
text: formatSlackMessage(text),
|
|
1201
|
+
mrkdwn: true
|
|
1202
|
+
});
|
|
1203
|
+
return;
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
const retryAfter = this.getRetryAfterSeconds(err);
|
|
1206
|
+
if (retryAfter && attempt < maxRetries) {
|
|
1207
|
+
console.log(
|
|
1208
|
+
`[Slack] Rate limited while updating, retrying after ${retryAfter}s...`
|
|
1209
|
+
);
|
|
1210
|
+
await new Promise(
|
|
1211
|
+
(resolve) => setTimeout(resolve, retryAfter * 1e3)
|
|
1212
|
+
);
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
throw err;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
async deleteMessageSafe(client, route, ts) {
|
|
1220
|
+
try {
|
|
1221
|
+
await client.chat.delete({
|
|
1222
|
+
channel: route.channel,
|
|
1223
|
+
ts
|
|
1224
|
+
});
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
console.error("[Slack] Failed to delete placeholder message:", err);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
isSlackMessageWithinLimit(text) {
|
|
1230
|
+
return formatSlackMessage(text).length <= MAX_MESSAGE_LENGTH2;
|
|
1231
|
+
}
|
|
1232
|
+
findSlackSafeSplitPoint(text) {
|
|
1233
|
+
const preferredBreaks = ["\n\n", "\n", " "];
|
|
1234
|
+
const minSplit = Math.floor(MAX_MESSAGE_LENGTH2 * 0.3);
|
|
1235
|
+
for (const token of preferredBreaks) {
|
|
1236
|
+
const index = this.findBestSlackSplitBefore(text, token);
|
|
1237
|
+
if (index >= minSplit) {
|
|
1238
|
+
return index;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
let low = 1;
|
|
1242
|
+
let high = text.length;
|
|
1243
|
+
let best = Math.min(text.length, MAX_MESSAGE_LENGTH2);
|
|
1244
|
+
while (low <= high) {
|
|
1245
|
+
const mid = Math.floor((low + high) / 2);
|
|
1246
|
+
const candidate = text.slice(0, mid);
|
|
1247
|
+
if (this.isSlackMessageWithinLimit(candidate)) {
|
|
1248
|
+
best = mid;
|
|
1249
|
+
low = mid + 1;
|
|
1250
|
+
} else {
|
|
1251
|
+
high = mid - 1;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return Math.max(1, best);
|
|
1255
|
+
}
|
|
1256
|
+
findBestSlackSplitBefore(text, token) {
|
|
1257
|
+
let fromIndex = Math.min(text.length, MAX_MESSAGE_LENGTH2);
|
|
1258
|
+
while (fromIndex > 0) {
|
|
1259
|
+
const index = text.lastIndexOf(token, fromIndex);
|
|
1260
|
+
if (index < 0) {
|
|
1261
|
+
return -1;
|
|
1262
|
+
}
|
|
1263
|
+
const splitAt = index + token.length;
|
|
1264
|
+
if (this.isSlackMessageWithinLimit(text.slice(0, splitAt))) {
|
|
1265
|
+
return splitAt;
|
|
1266
|
+
}
|
|
1267
|
+
fromIndex = index - 1;
|
|
1268
|
+
}
|
|
1269
|
+
return -1;
|
|
1082
1270
|
}
|
|
1083
1271
|
async tryAckReaction(client, event) {
|
|
1084
1272
|
try {
|
|
@@ -1196,12 +1384,12 @@ var init_slack = __esm({
|
|
|
1196
1384
|
*/
|
|
1197
1385
|
async sendFileSafe(client, route, filePath, caption) {
|
|
1198
1386
|
try {
|
|
1199
|
-
if (!
|
|
1387
|
+
if (!fs13.existsSync(filePath)) {
|
|
1200
1388
|
console.error(`[Slack] File not found for sending: ${filePath}`);
|
|
1201
1389
|
return;
|
|
1202
1390
|
}
|
|
1203
|
-
const filename =
|
|
1204
|
-
const fileContent =
|
|
1391
|
+
const filename = path12.basename(filePath);
|
|
1392
|
+
const fileContent = fs13.readFileSync(filePath);
|
|
1205
1393
|
await client.files.uploadV2({
|
|
1206
1394
|
channel_id: route.channel,
|
|
1207
1395
|
thread_ts: route.threadTs,
|
|
@@ -2242,15 +2430,15 @@ async function interactiveCreate(workDir) {
|
|
|
2242
2430
|
}
|
|
2243
2431
|
|
|
2244
2432
|
// src/commands/run.ts
|
|
2245
|
-
import
|
|
2246
|
-
import
|
|
2433
|
+
import path14 from "path";
|
|
2434
|
+
import fs15 from "fs";
|
|
2247
2435
|
import inquirer2 from "inquirer";
|
|
2248
2436
|
import chalk4 from "chalk";
|
|
2249
2437
|
|
|
2250
2438
|
// src/runtime/server.ts
|
|
2251
2439
|
import express from "express";
|
|
2252
|
-
import
|
|
2253
|
-
import
|
|
2440
|
+
import path13 from "path";
|
|
2441
|
+
import fs14 from "fs";
|
|
2254
2442
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2255
2443
|
import { createServer } from "http";
|
|
2256
2444
|
import { exec } from "child_process";
|
|
@@ -2258,8 +2446,8 @@ import { exec } from "child_process";
|
|
|
2258
2446
|
// src/runtime/agent.ts
|
|
2259
2447
|
init_config();
|
|
2260
2448
|
init_attachment_utils();
|
|
2261
|
-
import
|
|
2262
|
-
import
|
|
2449
|
+
import path9 from "path";
|
|
2450
|
+
import fs9 from "fs";
|
|
2263
2451
|
import { fileURLToPath } from "url";
|
|
2264
2452
|
import {
|
|
2265
2453
|
AuthStorage,
|
|
@@ -5115,6 +5303,67 @@ ${lines.join("\n")}`
|
|
|
5115
5303
|
};
|
|
5116
5304
|
}
|
|
5117
5305
|
|
|
5306
|
+
// src/runtime/commands/help-command.ts
|
|
5307
|
+
init_commands();
|
|
5308
|
+
import fs8 from "fs";
|
|
5309
|
+
import path8 from "path";
|
|
5310
|
+
function buildHelpMessage(rootDir) {
|
|
5311
|
+
const sections = [];
|
|
5312
|
+
const commands = getVisibleCommands();
|
|
5313
|
+
const commandLines = commands.map(
|
|
5314
|
+
(cmd) => `- \`/${cmd.command}\` \u2014 ${cmd.description}`
|
|
5315
|
+
);
|
|
5316
|
+
sections.push(`\u{1F4CB} **Available Commands**
|
|
5317
|
+
|
|
5318
|
+
${commandLines.join("\n")}`);
|
|
5319
|
+
const configPath = path8.resolve(rootDir, "skillpack.json");
|
|
5320
|
+
const skills = readInstalledSkills(configPath);
|
|
5321
|
+
if (skills.length > 0) {
|
|
5322
|
+
const skillLines = skills.map(
|
|
5323
|
+
(skill) => `- **${skill.name}**${skill.description ? ` \u2014 ${skill.description}` : ""}`
|
|
5324
|
+
);
|
|
5325
|
+
sections.push(
|
|
5326
|
+
`\u{1F9E9} **Installed Skills** (${skills.length})
|
|
5327
|
+
|
|
5328
|
+
${skillLines.join("\n")}`
|
|
5329
|
+
);
|
|
5330
|
+
} else {
|
|
5331
|
+
sections.push("\u{1F9E9} **Installed Skills**\nNo skills installed.");
|
|
5332
|
+
}
|
|
5333
|
+
sections.push(
|
|
5334
|
+
[
|
|
5335
|
+
"\u23F0 **Scheduled Tasks**",
|
|
5336
|
+
"",
|
|
5337
|
+
"You can set up recurring tasks using natural language. For example:",
|
|
5338
|
+
"",
|
|
5339
|
+
'- "Send me a daily market briefing every morning at 9 AM"',
|
|
5340
|
+
`- "Summarize this week's trading data every Friday at 6 PM"`,
|
|
5341
|
+
'- "Check for new announcements every 30 minutes"',
|
|
5342
|
+
"",
|
|
5343
|
+
"I will handle the cron scheduling automatically."
|
|
5344
|
+
].join("\n")
|
|
5345
|
+
);
|
|
5346
|
+
return sections.join("\n\n");
|
|
5347
|
+
}
|
|
5348
|
+
function handleHelpCommand(rootDir) {
|
|
5349
|
+
return {
|
|
5350
|
+
success: true,
|
|
5351
|
+
message: buildHelpMessage(rootDir)
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
function readInstalledSkills(configPath) {
|
|
5355
|
+
if (!fs8.existsSync(configPath)) {
|
|
5356
|
+
return [];
|
|
5357
|
+
}
|
|
5358
|
+
try {
|
|
5359
|
+
const raw = fs8.readFileSync(configPath, "utf-8");
|
|
5360
|
+
const config = JSON.parse(raw);
|
|
5361
|
+
return Array.isArray(config.skills) ? config.skills : [];
|
|
5362
|
+
} catch {
|
|
5363
|
+
return [];
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5118
5367
|
// src/runtime/agent.ts
|
|
5119
5368
|
var DEBUG = true;
|
|
5120
5369
|
var log = (...args) => DEBUG && console.log(...args);
|
|
@@ -5127,24 +5376,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
|
5127
5376
|
var PACK_AGENTS_FILE = "AGENTS.md";
|
|
5128
5377
|
var PACK_SOUL_FILE = "SOUL.md";
|
|
5129
5378
|
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
5130
|
-
if (!
|
|
5379
|
+
if (!fs9.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
5131
5380
|
log(
|
|
5132
5381
|
`[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
|
|
5133
5382
|
);
|
|
5134
5383
|
return null;
|
|
5135
5384
|
}
|
|
5136
|
-
const packConfigPath =
|
|
5137
|
-
const skillDir =
|
|
5138
|
-
const skillPath =
|
|
5385
|
+
const packConfigPath = path9.resolve(rootDir, "skillpack.json");
|
|
5386
|
+
const skillDir = path9.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
|
|
5387
|
+
const skillPath = path9.join(skillDir, "SKILL.md");
|
|
5139
5388
|
const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
|
|
5140
5389
|
const copyDir = (srcDir, destDir) => {
|
|
5141
|
-
|
|
5142
|
-
for (const entry of
|
|
5390
|
+
fs9.mkdirSync(destDir, { recursive: true });
|
|
5391
|
+
for (const entry of fs9.readdirSync(srcDir, { withFileTypes: true })) {
|
|
5143
5392
|
if (entry.name === ".DS_Store") {
|
|
5144
5393
|
continue;
|
|
5145
5394
|
}
|
|
5146
|
-
const srcPath =
|
|
5147
|
-
const destPath =
|
|
5395
|
+
const srcPath = path9.join(srcDir, entry.name);
|
|
5396
|
+
const destPath = path9.join(destDir, entry.name);
|
|
5148
5397
|
if (entry.isDirectory()) {
|
|
5149
5398
|
copyDir(srcPath, destPath);
|
|
5150
5399
|
continue;
|
|
@@ -5153,17 +5402,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
|
5153
5402
|
continue;
|
|
5154
5403
|
}
|
|
5155
5404
|
if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
|
|
5156
|
-
const content =
|
|
5157
|
-
|
|
5405
|
+
const content = fs9.readFileSync(srcPath, "utf-8");
|
|
5406
|
+
fs9.writeFileSync(destPath, renderTemplate(content), "utf-8");
|
|
5158
5407
|
continue;
|
|
5159
5408
|
}
|
|
5160
|
-
|
|
5409
|
+
fs9.copyFileSync(srcPath, destPath);
|
|
5161
5410
|
}
|
|
5162
5411
|
};
|
|
5163
|
-
if (!
|
|
5412
|
+
if (!fs9.existsSync(skillDir)) {
|
|
5164
5413
|
copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
|
|
5165
5414
|
}
|
|
5166
|
-
if (!
|
|
5415
|
+
if (!fs9.existsSync(skillPath)) {
|
|
5167
5416
|
log(
|
|
5168
5417
|
`[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
|
|
5169
5418
|
);
|
|
@@ -5191,11 +5440,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
|
5191
5440
|
};
|
|
5192
5441
|
}
|
|
5193
5442
|
function readOptionalPackPromptFile(filePath) {
|
|
5194
|
-
if (!
|
|
5443
|
+
if (!fs9.existsSync(filePath)) {
|
|
5195
5444
|
return void 0;
|
|
5196
5445
|
}
|
|
5197
5446
|
try {
|
|
5198
|
-
const content =
|
|
5447
|
+
const content = fs9.readFileSync(filePath, "utf-8").trim();
|
|
5199
5448
|
return content.length > 0 ? content : void 0;
|
|
5200
5449
|
} catch (error) {
|
|
5201
5450
|
console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
|
|
@@ -5203,8 +5452,8 @@ function readOptionalPackPromptFile(filePath) {
|
|
|
5203
5452
|
}
|
|
5204
5453
|
}
|
|
5205
5454
|
function buildPackPromptBlock(rootDir) {
|
|
5206
|
-
const agentsPath =
|
|
5207
|
-
const soulPath =
|
|
5455
|
+
const agentsPath = path9.resolve(rootDir, PACK_AGENTS_FILE);
|
|
5456
|
+
const soulPath = path9.resolve(rootDir, PACK_SOUL_FILE);
|
|
5208
5457
|
const agentsContent = readOptionalPackPromptFile(agentsPath);
|
|
5209
5458
|
const soulContent = readOptionalPackPromptFile(soulPath);
|
|
5210
5459
|
if (!agentsContent && !soulContent) {
|
|
@@ -5270,7 +5519,7 @@ var PackAgent = class {
|
|
|
5270
5519
|
authStorage;
|
|
5271
5520
|
constructor(options) {
|
|
5272
5521
|
this.options = options;
|
|
5273
|
-
const configPath =
|
|
5522
|
+
const configPath = path9.resolve(options.rootDir, "data", "config.json");
|
|
5274
5523
|
const backend = new ConfigFileAuthBackend(configPath);
|
|
5275
5524
|
this.authStorage = AuthStorage.fromStorage(backend);
|
|
5276
5525
|
const providerMeta = SUPPORTED_PROVIDERS[options.provider];
|
|
@@ -5321,24 +5570,24 @@ var PackAgent = class {
|
|
|
5321
5570
|
if (resolvedModel && baseUrl) {
|
|
5322
5571
|
log(`[PackAgent] Overriding ${provider}/${modelId} baseUrl -> ${baseUrl}`);
|
|
5323
5572
|
}
|
|
5324
|
-
const sessionDir =
|
|
5573
|
+
const sessionDir = path9.resolve(
|
|
5325
5574
|
rootDir,
|
|
5326
5575
|
"data",
|
|
5327
5576
|
"sessions",
|
|
5328
5577
|
channelId
|
|
5329
5578
|
);
|
|
5330
|
-
|
|
5579
|
+
fs9.mkdirSync(sessionDir, { recursive: true });
|
|
5331
5580
|
const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
|
|
5332
5581
|
log(`[PackAgent] Session dir: ${sessionDir}`);
|
|
5333
|
-
const workspaceDir =
|
|
5582
|
+
const workspaceDir = path9.resolve(
|
|
5334
5583
|
rootDir,
|
|
5335
5584
|
"data",
|
|
5336
5585
|
"workspaces",
|
|
5337
5586
|
channelId
|
|
5338
5587
|
);
|
|
5339
|
-
|
|
5588
|
+
fs9.mkdirSync(workspaceDir, { recursive: true });
|
|
5340
5589
|
log(`[PackAgent] Workspace dir: ${workspaceDir}`);
|
|
5341
|
-
const skillsPath =
|
|
5590
|
+
const skillsPath = path9.resolve(rootDir, "skills");
|
|
5342
5591
|
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
5343
5592
|
const materializedSkillCreator = materializeBuiltinSkillCreator(
|
|
5344
5593
|
rootDir,
|
|
@@ -5529,6 +5778,8 @@ ${text}`;
|
|
|
5529
5778
|
}
|
|
5530
5779
|
async handleCommand(command, channelId) {
|
|
5531
5780
|
switch (command) {
|
|
5781
|
+
case "help":
|
|
5782
|
+
return handleHelpCommand(this.options.rootDir);
|
|
5532
5783
|
case "new":
|
|
5533
5784
|
case "clear": {
|
|
5534
5785
|
const cs = this.channels.get(channelId);
|
|
@@ -5537,9 +5788,9 @@ ${text}`;
|
|
|
5537
5788
|
this.channels.delete(channelId);
|
|
5538
5789
|
}
|
|
5539
5790
|
const { rootDir } = this.options;
|
|
5540
|
-
const sessionDir =
|
|
5541
|
-
if (
|
|
5542
|
-
|
|
5791
|
+
const sessionDir = path9.resolve(rootDir, "data", "sessions", channelId);
|
|
5792
|
+
if (fs9.existsSync(sessionDir)) {
|
|
5793
|
+
fs9.rmSync(sessionDir, { recursive: true, force: true });
|
|
5543
5794
|
log(`[PackAgent] Cleared session dir: ${sessionDir}`);
|
|
5544
5795
|
}
|
|
5545
5796
|
return {
|
|
@@ -5588,22 +5839,20 @@ ${text}`;
|
|
|
5588
5839
|
|
|
5589
5840
|
// src/runtime/adapters/web.ts
|
|
5590
5841
|
init_config();
|
|
5591
|
-
|
|
5592
|
-
import
|
|
5842
|
+
init_commands();
|
|
5843
|
+
import fs10 from "fs";
|
|
5844
|
+
import path10 from "path";
|
|
5593
5845
|
import { WebSocketServer } from "ws";
|
|
5594
5846
|
function getPackConfig(rootDir) {
|
|
5595
|
-
const raw =
|
|
5847
|
+
const raw = fs10.readFileSync(path10.join(rootDir, "skillpack.json"), "utf-8");
|
|
5596
5848
|
return JSON.parse(raw);
|
|
5597
5849
|
}
|
|
5598
|
-
var COMMANDS = {
|
|
5599
|
-
"/new": "new",
|
|
5600
|
-
"/clear": "clear",
|
|
5601
|
-
"/restart": "restart",
|
|
5602
|
-
"/shutdown": "shutdown"
|
|
5603
|
-
};
|
|
5604
5850
|
function parseCommand(text) {
|
|
5605
|
-
|
|
5606
|
-
|
|
5851
|
+
return resolveCommand(text.trim().toLowerCase());
|
|
5852
|
+
}
|
|
5853
|
+
function sendWsEvent(ws, event) {
|
|
5854
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
5855
|
+
ws.send(JSON.stringify(event));
|
|
5607
5856
|
}
|
|
5608
5857
|
function getRuntimeConfigSignature(config) {
|
|
5609
5858
|
return JSON.stringify({
|
|
@@ -5744,23 +5993,23 @@ var WebAdapter = class {
|
|
|
5744
5993
|
res.status(400).json({ error: "Missing 'path' query parameter" });
|
|
5745
5994
|
return;
|
|
5746
5995
|
}
|
|
5747
|
-
const resolvedPath =
|
|
5748
|
-
const dataDir =
|
|
5996
|
+
const resolvedPath = path10.resolve(filePath);
|
|
5997
|
+
const dataDir = path10.resolve(rootDir, "data");
|
|
5749
5998
|
if (!resolvedPath.startsWith(dataDir)) {
|
|
5750
5999
|
res.status(403).json({ error: "Access denied" });
|
|
5751
6000
|
return;
|
|
5752
6001
|
}
|
|
5753
|
-
if (!
|
|
6002
|
+
if (!fs10.existsSync(resolvedPath)) {
|
|
5754
6003
|
res.status(404).json({ error: "File not found" });
|
|
5755
6004
|
return;
|
|
5756
6005
|
}
|
|
5757
|
-
const filename =
|
|
6006
|
+
const filename = path10.basename(resolvedPath);
|
|
5758
6007
|
res.setHeader("Content-Type", "application/octet-stream");
|
|
5759
6008
|
res.setHeader(
|
|
5760
6009
|
"Content-Disposition",
|
|
5761
6010
|
`attachment; filename="${filename}"`
|
|
5762
6011
|
);
|
|
5763
|
-
|
|
6012
|
+
fs10.createReadStream(resolvedPath).pipe(res);
|
|
5764
6013
|
});
|
|
5765
6014
|
const getScheduler = () => {
|
|
5766
6015
|
const schedulerAdapter = ctx.adapterMap?.get("scheduler");
|
|
@@ -5881,21 +6130,14 @@ var WebAdapter = class {
|
|
|
5881
6130
|
const command = parseCommand(text);
|
|
5882
6131
|
if (command) {
|
|
5883
6132
|
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 }));
|
|
6133
|
+
if (result2.message) {
|
|
6134
|
+
sendWsEvent(ws, { type: "text_delta", delta: result2.message });
|
|
5893
6135
|
}
|
|
6136
|
+
ws.send(JSON.stringify({ done: true }));
|
|
5894
6137
|
return;
|
|
5895
6138
|
}
|
|
5896
6139
|
const onEvent = (event) => {
|
|
5897
|
-
|
|
5898
|
-
ws.send(JSON.stringify(event));
|
|
6140
|
+
sendWsEvent(ws, event);
|
|
5899
6141
|
};
|
|
5900
6142
|
const result = await agent.handleMessage("web", channelId, text, onEvent);
|
|
5901
6143
|
if (result.errorMessage) {
|
|
@@ -5989,28 +6231,28 @@ var Lifecycle = class {
|
|
|
5989
6231
|
|
|
5990
6232
|
// src/runtime/registry.ts
|
|
5991
6233
|
import crypto from "crypto";
|
|
5992
|
-
import
|
|
6234
|
+
import fs11 from "fs";
|
|
5993
6235
|
import os from "os";
|
|
5994
|
-
import
|
|
5995
|
-
var SKILLPACK_HOME =
|
|
5996
|
-
var LEGACY_REGISTRY_FILE =
|
|
5997
|
-
var REGISTRY_DIR =
|
|
6236
|
+
import path11 from "path";
|
|
6237
|
+
var SKILLPACK_HOME = path11.join(os.homedir(), ".skillpack");
|
|
6238
|
+
var LEGACY_REGISTRY_FILE = path11.join(SKILLPACK_HOME, "registry.json");
|
|
6239
|
+
var REGISTRY_DIR = path11.join(SKILLPACK_HOME, "registry.d");
|
|
5998
6240
|
var migrationChecked = false;
|
|
5999
6241
|
function ensureHomeDir() {
|
|
6000
|
-
if (!
|
|
6001
|
-
|
|
6242
|
+
if (!fs11.existsSync(SKILLPACK_HOME)) {
|
|
6243
|
+
fs11.mkdirSync(SKILLPACK_HOME, { recursive: true });
|
|
6002
6244
|
}
|
|
6003
6245
|
}
|
|
6004
6246
|
function ensureRegistryDir() {
|
|
6005
6247
|
ensureHomeDir();
|
|
6006
|
-
if (!
|
|
6007
|
-
|
|
6248
|
+
if (!fs11.existsSync(REGISTRY_DIR)) {
|
|
6249
|
+
fs11.mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
6008
6250
|
}
|
|
6009
6251
|
}
|
|
6010
6252
|
function canonicalizeDir(dir) {
|
|
6011
|
-
const resolved =
|
|
6253
|
+
const resolved = path11.resolve(dir);
|
|
6012
6254
|
try {
|
|
6013
|
-
return
|
|
6255
|
+
return fs11.realpathSync(resolved);
|
|
6014
6256
|
} catch {
|
|
6015
6257
|
return resolved;
|
|
6016
6258
|
}
|
|
@@ -6019,7 +6261,7 @@ function hashDir(dir) {
|
|
|
6019
6261
|
return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
|
|
6020
6262
|
}
|
|
6021
6263
|
function getEntryPathForCanonicalDir(dir) {
|
|
6022
|
-
return
|
|
6264
|
+
return path11.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
|
|
6023
6265
|
}
|
|
6024
6266
|
function getEntryPath(dir) {
|
|
6025
6267
|
ensureRegistryReady();
|
|
@@ -6027,11 +6269,11 @@ function getEntryPath(dir) {
|
|
|
6027
6269
|
}
|
|
6028
6270
|
function listEntryFiles() {
|
|
6029
6271
|
ensureRegistryReady();
|
|
6030
|
-
return
|
|
6272
|
+
return fs11.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path11.join(REGISTRY_DIR, file));
|
|
6031
6273
|
}
|
|
6032
6274
|
function readEntryFile(filePath) {
|
|
6033
6275
|
try {
|
|
6034
|
-
const raw =
|
|
6276
|
+
const raw = fs11.readFileSync(filePath, "utf-8");
|
|
6035
6277
|
const data = JSON.parse(raw);
|
|
6036
6278
|
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
6279
|
return null;
|
|
@@ -6064,8 +6306,8 @@ function writeEntryFile(entry) {
|
|
|
6064
6306
|
};
|
|
6065
6307
|
const entryPath = getEntryPathForCanonicalDir(normalized.dir);
|
|
6066
6308
|
const tmpPath = createTmpPath(entryPath);
|
|
6067
|
-
|
|
6068
|
-
|
|
6309
|
+
fs11.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
6310
|
+
fs11.renameSync(tmpPath, entryPath);
|
|
6069
6311
|
}
|
|
6070
6312
|
function migrateLegacyRegistryIfNeeded() {
|
|
6071
6313
|
if (migrationChecked) {
|
|
@@ -6073,14 +6315,14 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
6073
6315
|
}
|
|
6074
6316
|
migrationChecked = true;
|
|
6075
6317
|
ensureRegistryDir();
|
|
6076
|
-
if (!
|
|
6318
|
+
if (!fs11.existsSync(LEGACY_REGISTRY_FILE)) {
|
|
6077
6319
|
return;
|
|
6078
6320
|
}
|
|
6079
6321
|
if (listEntryFiles().length > 0) {
|
|
6080
6322
|
return;
|
|
6081
6323
|
}
|
|
6082
6324
|
try {
|
|
6083
|
-
const raw =
|
|
6325
|
+
const raw = fs11.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
|
|
6084
6326
|
const data = JSON.parse(raw);
|
|
6085
6327
|
const packs = Array.isArray(data?.packs) ? data.packs : [];
|
|
6086
6328
|
for (const pack of packs) {
|
|
@@ -6093,7 +6335,7 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
6093
6335
|
} catch {
|
|
6094
6336
|
}
|
|
6095
6337
|
}
|
|
6096
|
-
|
|
6338
|
+
fs11.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
|
|
6097
6339
|
} catch (err) {
|
|
6098
6340
|
console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
|
|
6099
6341
|
}
|
|
@@ -6146,7 +6388,7 @@ function deregister(dir, pid) {
|
|
|
6146
6388
|
}
|
|
6147
6389
|
|
|
6148
6390
|
// src/runtime/server.ts
|
|
6149
|
-
var __dirname =
|
|
6391
|
+
var __dirname = path13.dirname(fileURLToPath2(import.meta.url));
|
|
6150
6392
|
async function startServer(options) {
|
|
6151
6393
|
const {
|
|
6152
6394
|
rootDir,
|
|
@@ -6161,8 +6403,8 @@ async function startServer(options) {
|
|
|
6161
6403
|
const packConfig = loadConfig(canonicalRootDir);
|
|
6162
6404
|
const baseUrl = dataConfig.baseUrl?.trim() || void 0;
|
|
6163
6405
|
const modelId = SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId;
|
|
6164
|
-
const packageRoot =
|
|
6165
|
-
const webDir =
|
|
6406
|
+
const packageRoot = path13.resolve(__dirname, "..");
|
|
6407
|
+
const webDir = fs14.existsSync(path13.join(rootDir, "web")) ? path13.join(rootDir, "web") : path13.join(packageRoot, "web");
|
|
6166
6408
|
const app = express();
|
|
6167
6409
|
app.use(express.json());
|
|
6168
6410
|
app.use(express.static(webDir));
|
|
@@ -6330,23 +6572,23 @@ function findMissingSkills(workDir, config) {
|
|
|
6330
6572
|
});
|
|
6331
6573
|
}
|
|
6332
6574
|
function copyStartTemplates2(workDir) {
|
|
6333
|
-
const templateDir =
|
|
6575
|
+
const templateDir = path14.resolve(
|
|
6334
6576
|
new URL("../templates", import.meta.url).pathname
|
|
6335
6577
|
);
|
|
6336
6578
|
for (const file of ["start.sh", "start.bat"]) {
|
|
6337
|
-
const src =
|
|
6338
|
-
const dest =
|
|
6339
|
-
if (
|
|
6340
|
-
|
|
6579
|
+
const src = path14.join(templateDir, file);
|
|
6580
|
+
const dest = path14.join(workDir, file);
|
|
6581
|
+
if (fs15.existsSync(src)) {
|
|
6582
|
+
fs15.copyFileSync(src, dest);
|
|
6341
6583
|
if (file === "start.sh") {
|
|
6342
|
-
|
|
6584
|
+
fs15.chmodSync(dest, 493);
|
|
6343
6585
|
}
|
|
6344
6586
|
}
|
|
6345
6587
|
}
|
|
6346
6588
|
}
|
|
6347
6589
|
async function runCommand(directory) {
|
|
6348
|
-
const workDir = directory ?
|
|
6349
|
-
|
|
6590
|
+
const workDir = directory ? path14.resolve(directory) : process.cwd();
|
|
6591
|
+
fs15.mkdirSync(workDir, { recursive: true });
|
|
6350
6592
|
if (!configExists(workDir)) {
|
|
6351
6593
|
console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
|
|
6352
6594
|
const { name, description } = await inquirer2.prompt([
|
|
@@ -6391,9 +6633,9 @@ async function runCommand(directory) {
|
|
|
6391
6633
|
}
|
|
6392
6634
|
|
|
6393
6635
|
// src/cli.ts
|
|
6394
|
-
import
|
|
6636
|
+
import fs16 from "fs";
|
|
6395
6637
|
var packageJson = JSON.parse(
|
|
6396
|
-
|
|
6638
|
+
fs16.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
6397
6639
|
);
|
|
6398
6640
|
var program = new Command();
|
|
6399
6641
|
program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
|
package/package.json
CHANGED
package/web/js/chat.js
CHANGED
|
@@ -282,21 +282,6 @@ function handleAgentEvent(event) {
|
|
|
282
282
|
hideLoadingIndicator();
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
// Handle bot command results injected by the backend WebSocket response
|
|
286
|
-
if (event.type === "command_result") {
|
|
287
|
-
const textBlock = getOrCreateTextBlock();
|
|
288
|
-
let resText = `Command \`${event.command}\` succeeded.`;
|
|
289
|
-
if (event.errorMessage) {
|
|
290
|
-
resText = `Command \`${event.command}\` failed: ${event.errorMessage}`;
|
|
291
|
-
} else if (event.result) {
|
|
292
|
-
resText = `Command \`${event.command}\` result:\n\n${event.result}`;
|
|
293
|
-
}
|
|
294
|
-
textBlock.dataset.mdContent += resText;
|
|
295
|
-
textBlock.innerHTML = renderMarkdown(textBlock.dataset.mdContent);
|
|
296
|
-
scrollToBottom();
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
285
|
switch (event.type) {
|
|
301
286
|
case "agent_start":
|
|
302
287
|
case "message_start":
|