@cremini/skillpack 1.1.8 → 1.2.0
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 +5 -1
- package/dist/cli.js +342 -91
- package/dist/runtime/registry.js +244 -0
- package/package.json +2 -2
- package/templates/builtin-skills/skill-creator/LICENSE.txt +202 -0
- package/templates/builtin-skills/skill-creator/SKILL.md +171 -0
- package/templates/builtin-skills/skill-creator/agents/analyzer.md +274 -0
- package/templates/builtin-skills/skill-creator/agents/comparator.md +202 -0
- package/templates/builtin-skills/skill-creator/agents/grader.md +223 -0
- package/templates/builtin-skills/skill-creator/assets/eval_review.html +146 -0
- package/templates/builtin-skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/templates/builtin-skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/templates/builtin-skills/skill-creator/references/schemas.md +430 -0
- package/templates/builtin-skills/skill-creator/scripts/__init__.py +0 -0
- package/templates/builtin-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/templates/builtin-skills/skill-creator/scripts/generate_report.py +326 -0
- package/templates/builtin-skills/skill-creator/scripts/improve_description.py +247 -0
- package/templates/builtin-skills/skill-creator/scripts/package_skill.py +136 -0
- package/templates/builtin-skills/skill-creator/scripts/quick_validate.py +103 -0
- package/templates/builtin-skills/skill-creator/scripts/run_eval.py +310 -0
- package/templates/builtin-skills/skill-creator/scripts/run_loop.py +328 -0
- package/templates/builtin-skills/skill-creator/scripts/utils.py +47 -0
- package/web/js/api-key-dialog.js +3 -5
- package/web/js/chat-apps-dialog.js +4 -10
- package/web/js/chat.js +8 -8
- package/web/js/settings.js +3 -8
package/dist/cli.js
CHANGED
|
@@ -114,7 +114,7 @@ var init_config = __esm({
|
|
|
114
114
|
console.warn(" Warning: Failed to parse data/config.json:", err);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
let { apiKey = "", provider = "openai" } = this.configData;
|
|
117
|
+
let { apiKey = "", provider = "openai", baseUrl = "" } = this.configData;
|
|
118
118
|
if (!apiKey) {
|
|
119
119
|
if (process.env.OPENAI_API_KEY) {
|
|
120
120
|
apiKey = process.env.OPENAI_API_KEY;
|
|
@@ -126,6 +126,7 @@ var init_config = __esm({
|
|
|
126
126
|
}
|
|
127
127
|
this.configData.apiKey = apiKey;
|
|
128
128
|
this.configData.provider = provider;
|
|
129
|
+
this.configData.baseUrl = baseUrl?.trim() || void 0;
|
|
129
130
|
return this.configData;
|
|
130
131
|
}
|
|
131
132
|
getConfig() {
|
|
@@ -141,6 +142,9 @@ var init_config = __esm({
|
|
|
141
142
|
}
|
|
142
143
|
if (updates.apiKey !== void 0) this.configData.apiKey = updates.apiKey;
|
|
143
144
|
if (updates.provider !== void 0) this.configData.provider = updates.provider;
|
|
145
|
+
if (updates.baseUrl !== void 0) {
|
|
146
|
+
this.configData.baseUrl = updates.baseUrl?.trim() || void 0;
|
|
147
|
+
}
|
|
144
148
|
if (updates.adapters !== void 0) {
|
|
145
149
|
const merged = { ...this.configData.adapters || {} };
|
|
146
150
|
for (const [adapterKey, adapterVal] of Object.entries(updates.adapters)) {
|
|
@@ -289,7 +293,7 @@ var telegram_exports = {};
|
|
|
289
293
|
__export(telegram_exports, {
|
|
290
294
|
TelegramAdapter: () => TelegramAdapter
|
|
291
295
|
});
|
|
292
|
-
import
|
|
296
|
+
import fs11 from "fs";
|
|
293
297
|
import TelegramBot from "node-telegram-bot-api";
|
|
294
298
|
var COMMANDS2, MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
|
|
295
299
|
var init_telegram = __esm({
|
|
@@ -397,6 +401,7 @@ var init_telegram = __esm({
|
|
|
397
401
|
try {
|
|
398
402
|
const userText = text || "(User sent an attachment)";
|
|
399
403
|
const result = await this.agent.handleMessage(
|
|
404
|
+
"telegram",
|
|
400
405
|
channelId,
|
|
401
406
|
userText,
|
|
402
407
|
onEvent,
|
|
@@ -599,7 +604,7 @@ var init_telegram = __esm({
|
|
|
599
604
|
async sendFileSafe(chatId, filePath, caption) {
|
|
600
605
|
if (!this.bot) return;
|
|
601
606
|
try {
|
|
602
|
-
if (!
|
|
607
|
+
if (!fs11.existsSync(filePath)) {
|
|
603
608
|
console.error(`[Telegram] File not found for sending: ${filePath}`);
|
|
604
609
|
return;
|
|
605
610
|
}
|
|
@@ -619,8 +624,8 @@ var slack_exports = {};
|
|
|
619
624
|
__export(slack_exports, {
|
|
620
625
|
SlackAdapter: () => SlackAdapter
|
|
621
626
|
});
|
|
622
|
-
import
|
|
623
|
-
import
|
|
627
|
+
import fs12 from "fs";
|
|
628
|
+
import path11 from "path";
|
|
624
629
|
import { App, LogLevel } from "@slack/bolt";
|
|
625
630
|
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, SlackAdapter;
|
|
626
631
|
var init_slack = __esm({
|
|
@@ -830,6 +835,7 @@ var init_slack = __esm({
|
|
|
830
835
|
};
|
|
831
836
|
try {
|
|
832
837
|
const result = await this.agent.handleMessage(
|
|
838
|
+
"slack",
|
|
833
839
|
channelId,
|
|
834
840
|
text,
|
|
835
841
|
onEvent,
|
|
@@ -1097,12 +1103,12 @@ var init_slack = __esm({
|
|
|
1097
1103
|
*/
|
|
1098
1104
|
async sendFileSafe(client, route, filePath, caption) {
|
|
1099
1105
|
try {
|
|
1100
|
-
if (!
|
|
1106
|
+
if (!fs12.existsSync(filePath)) {
|
|
1101
1107
|
console.error(`[Slack] File not found for sending: ${filePath}`);
|
|
1102
1108
|
return;
|
|
1103
1109
|
}
|
|
1104
|
-
const filename =
|
|
1105
|
-
const fileContent =
|
|
1110
|
+
const filename = path11.basename(filePath);
|
|
1111
|
+
const fileContent = fs12.readFileSync(filePath);
|
|
1106
1112
|
await client.files.uploadV2({
|
|
1107
1113
|
channel_id: route.channel,
|
|
1108
1114
|
thread_ts: route.threadTs,
|
|
@@ -1274,6 +1280,7 @@ var init_scheduler = __esm({
|
|
|
1274
1280
|
};
|
|
1275
1281
|
try {
|
|
1276
1282
|
const result = await this.agent.handleMessage(
|
|
1283
|
+
"scheduler",
|
|
1277
1284
|
channelId,
|
|
1278
1285
|
jobConfig.prompt,
|
|
1279
1286
|
onEvent
|
|
@@ -2036,16 +2043,16 @@ async function interactiveCreate(workDir) {
|
|
|
2036
2043
|
}
|
|
2037
2044
|
|
|
2038
2045
|
// src/commands/run.ts
|
|
2039
|
-
import
|
|
2040
|
-
import
|
|
2046
|
+
import path13 from "path";
|
|
2047
|
+
import fs14 from "fs";
|
|
2041
2048
|
import inquirer2 from "inquirer";
|
|
2042
2049
|
import chalk4 from "chalk";
|
|
2043
2050
|
|
|
2044
2051
|
// src/runtime/server.ts
|
|
2045
2052
|
import express from "express";
|
|
2046
|
-
import
|
|
2047
|
-
import
|
|
2048
|
-
import { fileURLToPath } from "url";
|
|
2053
|
+
import path12 from "path";
|
|
2054
|
+
import fs13 from "fs";
|
|
2055
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2049
2056
|
import { createServer } from "http";
|
|
2050
2057
|
import { exec } from "child_process";
|
|
2051
2058
|
|
|
@@ -2053,6 +2060,7 @@ import { exec } from "child_process";
|
|
|
2053
2060
|
init_attachment_utils();
|
|
2054
2061
|
import path7 from "path";
|
|
2055
2062
|
import fs7 from "fs";
|
|
2063
|
+
import { fileURLToPath } from "url";
|
|
2056
2064
|
import {
|
|
2057
2065
|
AuthStorage,
|
|
2058
2066
|
createAgentSession,
|
|
@@ -4785,16 +4793,6 @@ var ManageScheduleParams = Type.Object({
|
|
|
4785
4793
|
description: "The work prompt to execute when the task triggers. Required for add. Describe only what to do each run; do not repeat timing, cron, or 'every N minutes' instructions here."
|
|
4786
4794
|
})
|
|
4787
4795
|
),
|
|
4788
|
-
notifyAdapter: Type.Optional(
|
|
4789
|
-
Type.String({
|
|
4790
|
-
description: "Target adapter name for result notification: 'telegram' or 'slack'. Required for add."
|
|
4791
|
-
})
|
|
4792
|
-
),
|
|
4793
|
-
notifyChannelId: Type.Optional(
|
|
4794
|
-
Type.String({
|
|
4795
|
-
description: "Target channelId for result notification (e.g. 'telegram-123456'). Required for add."
|
|
4796
|
-
})
|
|
4797
|
-
),
|
|
4798
4796
|
timezone: Type.Optional(
|
|
4799
4797
|
Type.String({
|
|
4800
4798
|
description: "Optional timezone for the cron schedule, e.g. 'Asia/Shanghai', 'America/New_York'."
|
|
@@ -4804,7 +4802,7 @@ var ManageScheduleParams = Type.Object({
|
|
|
4804
4802
|
function textResult(text) {
|
|
4805
4803
|
return { content: [{ type: "text", text }], details: void 0 };
|
|
4806
4804
|
}
|
|
4807
|
-
function createManageScheduleTool(schedulerRef,
|
|
4805
|
+
function createManageScheduleTool(schedulerRef, adapter, channelId) {
|
|
4808
4806
|
return {
|
|
4809
4807
|
name: "manage_scheduled_task",
|
|
4810
4808
|
label: "Manage Scheduled Task",
|
|
@@ -4812,7 +4810,7 @@ function createManageScheduleTool(schedulerRef, _rootDirRef) {
|
|
|
4812
4810
|
"Manage scheduled tasks (cron jobs) that automatically execute prompts and push results to IM channels.",
|
|
4813
4811
|
"",
|
|
4814
4812
|
"Actions:",
|
|
4815
|
-
"- add: Create a new scheduled task. Requires: name, cron, prompt
|
|
4813
|
+
"- add: Create a new scheduled task. Requires: name, cron, prompt. The notification target always uses the current Telegram or Slack chat. The prompt must describe only the work for each run, not the schedule itself.",
|
|
4816
4814
|
"- list: List all scheduled tasks with their status.",
|
|
4817
4815
|
"- remove: Remove a scheduled task by name.",
|
|
4818
4816
|
"- trigger: Manually trigger a scheduled task by name (runs immediately).",
|
|
@@ -4823,10 +4821,7 @@ function createManageScheduleTool(schedulerRef, _rootDirRef) {
|
|
|
4823
4821
|
"Examples:",
|
|
4824
4822
|
" '0 9 * * 1-5' = every weekday at 9:00 AM",
|
|
4825
4823
|
" '0 18 * * 5' = every Friday at 6:00 PM",
|
|
4826
|
-
" '*/30 * * * *' = every 30 minutes"
|
|
4827
|
-
"",
|
|
4828
|
-
"notifyAdapter: 'telegram' or 'slack'",
|
|
4829
|
-
"notifyChannelId: the channel ID where result will be sent (e.g. 'telegram-123456')"
|
|
4824
|
+
" '*/30 * * * *' = every 30 minutes"
|
|
4830
4825
|
].join("\n"),
|
|
4831
4826
|
parameters: ManageScheduleParams,
|
|
4832
4827
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
@@ -4856,9 +4851,9 @@ ${lines.join("\n")}`
|
|
|
4856
4851
|
"Error: 'name', 'cron', and 'prompt' are required for adding a task."
|
|
4857
4852
|
);
|
|
4858
4853
|
}
|
|
4859
|
-
if (
|
|
4854
|
+
if (adapter !== "telegram" && adapter !== "slack") {
|
|
4860
4855
|
return textResult(
|
|
4861
|
-
"Error:
|
|
4856
|
+
"Error: Scheduled tasks can only be created from a Telegram or Slack."
|
|
4862
4857
|
);
|
|
4863
4858
|
}
|
|
4864
4859
|
const jobConfig = {
|
|
@@ -4866,8 +4861,8 @@ ${lines.join("\n")}`
|
|
|
4866
4861
|
cron: params.cron,
|
|
4867
4862
|
prompt: params.prompt,
|
|
4868
4863
|
notify: {
|
|
4869
|
-
adapter
|
|
4870
|
-
channelId
|
|
4864
|
+
adapter,
|
|
4865
|
+
channelId
|
|
4871
4866
|
},
|
|
4872
4867
|
enabled: true,
|
|
4873
4868
|
timezone: params.timezone
|
|
@@ -4924,6 +4919,75 @@ ${lines.join("\n")}`
|
|
|
4924
4919
|
var DEBUG = true;
|
|
4925
4920
|
var log = (...args) => DEBUG && console.log(...args);
|
|
4926
4921
|
var write = (data) => DEBUG && process.stdout.write(data);
|
|
4922
|
+
var BUILTIN_SKILL_CREATOR_NAME = "skill-creator";
|
|
4923
|
+
var BUILTIN_SKILL_CREATOR_DESCRIPTION = "Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.";
|
|
4924
|
+
var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
4925
|
+
new URL("../templates/builtin-skills/skill-creator", import.meta.url)
|
|
4926
|
+
);
|
|
4927
|
+
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
4928
|
+
if (!fs7.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
4929
|
+
log(
|
|
4930
|
+
`[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
|
|
4931
|
+
);
|
|
4932
|
+
return null;
|
|
4933
|
+
}
|
|
4934
|
+
const packConfigPath = path7.resolve(rootDir, "skillpack.json");
|
|
4935
|
+
const skillDir = path7.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
|
|
4936
|
+
const skillPath = path7.join(skillDir, "SKILL.md");
|
|
4937
|
+
const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
|
|
4938
|
+
const copyDir = (srcDir, destDir) => {
|
|
4939
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
4940
|
+
for (const entry of fs7.readdirSync(srcDir, { withFileTypes: true })) {
|
|
4941
|
+
if (entry.name === ".DS_Store") {
|
|
4942
|
+
continue;
|
|
4943
|
+
}
|
|
4944
|
+
const srcPath = path7.join(srcDir, entry.name);
|
|
4945
|
+
const destPath = path7.join(destDir, entry.name);
|
|
4946
|
+
if (entry.isDirectory()) {
|
|
4947
|
+
copyDir(srcPath, destPath);
|
|
4948
|
+
continue;
|
|
4949
|
+
}
|
|
4950
|
+
if (!entry.isFile()) {
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4953
|
+
if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
|
|
4954
|
+
const content = fs7.readFileSync(srcPath, "utf-8");
|
|
4955
|
+
fs7.writeFileSync(destPath, renderTemplate(content), "utf-8");
|
|
4956
|
+
continue;
|
|
4957
|
+
}
|
|
4958
|
+
fs7.copyFileSync(srcPath, destPath);
|
|
4959
|
+
}
|
|
4960
|
+
};
|
|
4961
|
+
if (!fs7.existsSync(skillDir)) {
|
|
4962
|
+
copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
|
|
4963
|
+
}
|
|
4964
|
+
if (!fs7.existsSync(skillPath)) {
|
|
4965
|
+
log(
|
|
4966
|
+
`[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
|
|
4967
|
+
);
|
|
4968
|
+
return null;
|
|
4969
|
+
}
|
|
4970
|
+
return {
|
|
4971
|
+
name: BUILTIN_SKILL_CREATOR_NAME,
|
|
4972
|
+
description: BUILTIN_SKILL_CREATOR_DESCRIPTION,
|
|
4973
|
+
filePath: skillPath,
|
|
4974
|
+
baseDir: skillDir,
|
|
4975
|
+
source: "path",
|
|
4976
|
+
disableModelInvocation: false
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4979
|
+
function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
4980
|
+
if (!materializedSkill) {
|
|
4981
|
+
return base;
|
|
4982
|
+
}
|
|
4983
|
+
const filtered = base.skills.filter(
|
|
4984
|
+
(skill) => skill.name !== BUILTIN_SKILL_CREATOR_NAME
|
|
4985
|
+
);
|
|
4986
|
+
return {
|
|
4987
|
+
skills: [materializedSkill, ...filtered],
|
|
4988
|
+
diagnostics: base.diagnostics
|
|
4989
|
+
};
|
|
4990
|
+
}
|
|
4927
4991
|
function getAssistantDiagnostics(message) {
|
|
4928
4992
|
if (!message || message.role !== "assistant") {
|
|
4929
4993
|
return null;
|
|
@@ -4949,17 +5013,9 @@ var PackAgent = class {
|
|
|
4949
5013
|
fileOutputCallbackRef = {
|
|
4950
5014
|
current: null
|
|
4951
5015
|
};
|
|
4952
|
-
sendFileToolDef = createSendFileTool(this.fileOutputCallbackRef);
|
|
4953
5016
|
schedulerRef = { current: null };
|
|
4954
|
-
rootDirRef;
|
|
4955
|
-
scheduleToolDef;
|
|
4956
5017
|
constructor(options) {
|
|
4957
5018
|
this.options = options;
|
|
4958
|
-
this.rootDirRef = { current: options.rootDir };
|
|
4959
|
-
this.scheduleToolDef = createManageScheduleTool(
|
|
4960
|
-
this.schedulerRef,
|
|
4961
|
-
this.rootDirRef
|
|
4962
|
-
);
|
|
4963
5019
|
}
|
|
4964
5020
|
/**
|
|
4965
5021
|
* Inject scheduler reference (called by server.ts after adapter init).
|
|
@@ -4967,22 +5023,33 @@ var PackAgent = class {
|
|
|
4967
5023
|
setScheduler(scheduler) {
|
|
4968
5024
|
this.schedulerRef.current = scheduler;
|
|
4969
5025
|
}
|
|
5026
|
+
createCustomTools(adapter, channelId) {
|
|
5027
|
+
const tools = [createSendFileTool(this.fileOutputCallbackRef)];
|
|
5028
|
+
if (adapter === "telegram" || adapter === "slack") {
|
|
5029
|
+
tools.push(createManageScheduleTool(this.schedulerRef, adapter, channelId));
|
|
5030
|
+
}
|
|
5031
|
+
return tools;
|
|
5032
|
+
}
|
|
4970
5033
|
/**
|
|
4971
5034
|
* Lazily create (or return existing) session for a channel.
|
|
4972
5035
|
*/
|
|
4973
|
-
async getOrCreateSession(channelId) {
|
|
5036
|
+
async getOrCreateSession(adapter, channelId) {
|
|
4974
5037
|
const existing = this.channels.get(channelId);
|
|
4975
5038
|
if (existing) return existing;
|
|
4976
5039
|
const pendingCreation = this.pendingSessionCreations.get(channelId);
|
|
4977
5040
|
if (pendingCreation) return pendingCreation;
|
|
4978
5041
|
const createSessionPromise = (async () => {
|
|
4979
|
-
const { apiKey, rootDir, provider, modelId } = this.options;
|
|
5042
|
+
const { apiKey, rootDir, provider, modelId, baseUrl } = this.options;
|
|
4980
5043
|
const authStorage = AuthStorage.inMemory({
|
|
4981
5044
|
[provider]: { type: "api_key", key: apiKey }
|
|
4982
5045
|
});
|
|
4983
5046
|
authStorage.setRuntimeApiKey(provider, apiKey);
|
|
4984
5047
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
4985
|
-
const
|
|
5048
|
+
const resolvedModel = modelRegistry.find(provider, modelId);
|
|
5049
|
+
const model = resolvedModel && baseUrl ? { ...resolvedModel, baseUrl } : resolvedModel;
|
|
5050
|
+
if (resolvedModel && baseUrl) {
|
|
5051
|
+
log(`[PackAgent] Overriding ${provider}/${modelId} baseUrl -> ${baseUrl}`);
|
|
5052
|
+
}
|
|
4986
5053
|
const sessionDir = path7.resolve(
|
|
4987
5054
|
rootDir,
|
|
4988
5055
|
"data",
|
|
@@ -5002,12 +5069,23 @@ var PackAgent = class {
|
|
|
5002
5069
|
log(`[PackAgent] Workspace dir: ${workspaceDir}`);
|
|
5003
5070
|
const skillsPath = path7.resolve(rootDir, "skills");
|
|
5004
5071
|
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
5072
|
+
const materializedSkillCreator = materializeBuiltinSkillCreator(
|
|
5073
|
+
rootDir,
|
|
5074
|
+
skillsPath
|
|
5075
|
+
);
|
|
5076
|
+
if (materializedSkillCreator) {
|
|
5077
|
+
log(
|
|
5078
|
+
`[PackAgent] Materialized built-in skill-creator to: ${materializedSkillCreator.filePath}`
|
|
5079
|
+
);
|
|
5080
|
+
}
|
|
5005
5081
|
const resourceLoader = new DefaultResourceLoader({
|
|
5006
5082
|
cwd: rootDir,
|
|
5007
|
-
additionalSkillPaths: [skillsPath]
|
|
5083
|
+
additionalSkillPaths: [skillsPath],
|
|
5084
|
+
skillsOverride: (base) => overrideBuiltinSkillCreator(base, materializedSkillCreator)
|
|
5008
5085
|
});
|
|
5009
5086
|
await resourceLoader.reload();
|
|
5010
5087
|
const tools = createCodingTools(workspaceDir);
|
|
5088
|
+
const customTools = this.createCustomTools(adapter, channelId);
|
|
5011
5089
|
const { session } = await createAgentSession({
|
|
5012
5090
|
cwd: workspaceDir,
|
|
5013
5091
|
authStorage,
|
|
@@ -5016,7 +5094,7 @@ var PackAgent = class {
|
|
|
5016
5094
|
resourceLoader,
|
|
5017
5095
|
model,
|
|
5018
5096
|
tools,
|
|
5019
|
-
customTools
|
|
5097
|
+
customTools
|
|
5020
5098
|
});
|
|
5021
5099
|
const channelSession = {
|
|
5022
5100
|
session,
|
|
@@ -5033,8 +5111,8 @@ var PackAgent = class {
|
|
|
5033
5111
|
this.pendingSessionCreations.delete(channelId);
|
|
5034
5112
|
}
|
|
5035
5113
|
}
|
|
5036
|
-
async handleMessage(channelId, text, onEvent, attachments) {
|
|
5037
|
-
const cs = await this.getOrCreateSession(channelId);
|
|
5114
|
+
async handleMessage(adapter, channelId, text, onEvent, attachments) {
|
|
5115
|
+
const cs = await this.getOrCreateSession(adapter, channelId);
|
|
5038
5116
|
const run = async () => {
|
|
5039
5117
|
cs.running = true;
|
|
5040
5118
|
let turnHadVisibleOutput = false;
|
|
@@ -5243,6 +5321,7 @@ function getRuntimeConfigSignature(config) {
|
|
|
5243
5321
|
return JSON.stringify({
|
|
5244
5322
|
apiKey: config.apiKey || "",
|
|
5245
5323
|
provider: config.provider || "openai",
|
|
5324
|
+
baseUrl: config.baseUrl || "",
|
|
5246
5325
|
telegramToken: config.adapters?.telegram?.token || "",
|
|
5247
5326
|
slackBotToken: config.adapters?.slack?.botToken || "",
|
|
5248
5327
|
slackAppToken: config.adapters?.slack?.appToken || ""
|
|
@@ -5269,8 +5348,8 @@ var WebAdapter = class {
|
|
|
5269
5348
|
hasApiKey: !!conf.apiKey,
|
|
5270
5349
|
apiKey: conf.apiKey || "",
|
|
5271
5350
|
provider: conf.provider || "openai",
|
|
5272
|
-
|
|
5273
|
-
|
|
5351
|
+
baseUrl: conf.baseUrl || "",
|
|
5352
|
+
adapters: conf.adapters || {}
|
|
5274
5353
|
});
|
|
5275
5354
|
});
|
|
5276
5355
|
app.get("/api/skills", (_req, res) => {
|
|
@@ -5278,7 +5357,7 @@ var WebAdapter = class {
|
|
|
5278
5357
|
res.json(config.skills || []);
|
|
5279
5358
|
});
|
|
5280
5359
|
app.post("/api/config/update", (req, res) => {
|
|
5281
|
-
const { key, provider, adapters } = req.body;
|
|
5360
|
+
const { key, provider, baseUrl, adapters } = req.body;
|
|
5282
5361
|
const updates = {};
|
|
5283
5362
|
const beforeConfig = JSON.parse(JSON.stringify(configManager.getConfig()));
|
|
5284
5363
|
if (key !== void 0) {
|
|
@@ -5289,6 +5368,9 @@ var WebAdapter = class {
|
|
|
5289
5368
|
updates.provider = provider;
|
|
5290
5369
|
currentProvider = provider;
|
|
5291
5370
|
}
|
|
5371
|
+
if (baseUrl !== void 0) {
|
|
5372
|
+
updates.baseUrl = baseUrl;
|
|
5373
|
+
}
|
|
5292
5374
|
if (adapters !== void 0) {
|
|
5293
5375
|
updates.adapters = adapters;
|
|
5294
5376
|
}
|
|
@@ -5298,23 +5380,14 @@ var WebAdapter = class {
|
|
|
5298
5380
|
res.json({
|
|
5299
5381
|
success: true,
|
|
5300
5382
|
provider: newConf.provider,
|
|
5383
|
+
baseUrl: newConf.baseUrl || "",
|
|
5301
5384
|
adapters: newConf.adapters,
|
|
5302
|
-
requiresRestart
|
|
5303
|
-
runtimeControl: lifecycle.getRuntimeControl()
|
|
5385
|
+
requiresRestart
|
|
5304
5386
|
});
|
|
5305
5387
|
});
|
|
5306
5388
|
app.post("/api/runtime/restart", async (_req, res) => {
|
|
5307
|
-
const runtimeControl = lifecycle.getRuntimeControl();
|
|
5308
|
-
if (!runtimeControl.canManagedRestart) {
|
|
5309
|
-
res.status(409).json({
|
|
5310
|
-
success: false,
|
|
5311
|
-
message: "Managed restart is unavailable for this process.",
|
|
5312
|
-
runtimeControl
|
|
5313
|
-
});
|
|
5314
|
-
return;
|
|
5315
|
-
}
|
|
5316
5389
|
const result = await lifecycle.requestRestart("web");
|
|
5317
|
-
res.status(202).json(
|
|
5390
|
+
res.status(202).json(result);
|
|
5318
5391
|
});
|
|
5319
5392
|
app.delete("/api/chat", (_req, res) => {
|
|
5320
5393
|
res.json({ success: true });
|
|
@@ -5483,7 +5556,7 @@ var WebAdapter = class {
|
|
|
5483
5556
|
if (ws.readyState !== ws.OPEN) return;
|
|
5484
5557
|
ws.send(JSON.stringify(event));
|
|
5485
5558
|
};
|
|
5486
|
-
const result = await agent.handleMessage(channelId, text, onEvent);
|
|
5559
|
+
const result = await agent.handleMessage("web", channelId, text, onEvent);
|
|
5487
5560
|
if (result.errorMessage) {
|
|
5488
5561
|
ws.send(JSON.stringify({ error: result.errorMessage }));
|
|
5489
5562
|
return;
|
|
@@ -5506,29 +5579,18 @@ init_config();
|
|
|
5506
5579
|
var SHUTDOWN_EXIT_CODE = 64;
|
|
5507
5580
|
var RESTART_EXIT_CODE = 75;
|
|
5508
5581
|
var STOP_TIMEOUT_MS = 3e3;
|
|
5509
|
-
function detectProcessManager() {
|
|
5510
|
-
return process.env.PACK_ROOT ? "wrapper" : "none";
|
|
5511
|
-
}
|
|
5512
5582
|
var Lifecycle = class {
|
|
5513
5583
|
server;
|
|
5514
5584
|
exitFn;
|
|
5515
|
-
processManager;
|
|
5516
5585
|
adapters = [];
|
|
5517
5586
|
stopReason = null;
|
|
5518
5587
|
constructor(server, exitFn = (code) => process.exit(code)) {
|
|
5519
5588
|
this.server = server;
|
|
5520
5589
|
this.exitFn = exitFn;
|
|
5521
|
-
this.processManager = detectProcessManager();
|
|
5522
5590
|
}
|
|
5523
5591
|
registerAdapters(adapters) {
|
|
5524
5592
|
this.adapters = adapters;
|
|
5525
5593
|
}
|
|
5526
|
-
getRuntimeControl() {
|
|
5527
|
-
return {
|
|
5528
|
-
canManagedRestart: this.processManager === "wrapper",
|
|
5529
|
-
processManager: this.processManager
|
|
5530
|
-
};
|
|
5531
|
-
}
|
|
5532
5594
|
async requestRestart(trigger) {
|
|
5533
5595
|
return this.requestStop("restart", trigger);
|
|
5534
5596
|
}
|
|
@@ -5584,31 +5646,205 @@ var Lifecycle = class {
|
|
|
5584
5646
|
}
|
|
5585
5647
|
};
|
|
5586
5648
|
|
|
5649
|
+
// src/runtime/registry.ts
|
|
5650
|
+
import crypto from "crypto";
|
|
5651
|
+
import fs10 from "fs";
|
|
5652
|
+
import os from "os";
|
|
5653
|
+
import path10 from "path";
|
|
5654
|
+
var SKILLPACK_HOME = path10.join(os.homedir(), ".skillpack");
|
|
5655
|
+
var LEGACY_REGISTRY_FILE = path10.join(SKILLPACK_HOME, "registry.json");
|
|
5656
|
+
var REGISTRY_DIR = path10.join(SKILLPACK_HOME, "registry.d");
|
|
5657
|
+
var migrationChecked = false;
|
|
5658
|
+
function ensureHomeDir() {
|
|
5659
|
+
if (!fs10.existsSync(SKILLPACK_HOME)) {
|
|
5660
|
+
fs10.mkdirSync(SKILLPACK_HOME, { recursive: true });
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
function ensureRegistryDir() {
|
|
5664
|
+
ensureHomeDir();
|
|
5665
|
+
if (!fs10.existsSync(REGISTRY_DIR)) {
|
|
5666
|
+
fs10.mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
function canonicalizeDir(dir) {
|
|
5670
|
+
const resolved = path10.resolve(dir);
|
|
5671
|
+
try {
|
|
5672
|
+
return fs10.realpathSync(resolved);
|
|
5673
|
+
} catch {
|
|
5674
|
+
return resolved;
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
function hashDir(dir) {
|
|
5678
|
+
return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
|
|
5679
|
+
}
|
|
5680
|
+
function getEntryPathForCanonicalDir(dir) {
|
|
5681
|
+
return path10.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
|
|
5682
|
+
}
|
|
5683
|
+
function getEntryPath(dir) {
|
|
5684
|
+
ensureRegistryReady();
|
|
5685
|
+
return getEntryPathForCanonicalDir(canonicalizeDir(dir));
|
|
5686
|
+
}
|
|
5687
|
+
function listEntryFiles() {
|
|
5688
|
+
ensureRegistryReady();
|
|
5689
|
+
return fs10.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path10.join(REGISTRY_DIR, file));
|
|
5690
|
+
}
|
|
5691
|
+
function readEntryFile(filePath) {
|
|
5692
|
+
try {
|
|
5693
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
5694
|
+
const data = JSON.parse(raw);
|
|
5695
|
+
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") {
|
|
5696
|
+
return null;
|
|
5697
|
+
}
|
|
5698
|
+
return {
|
|
5699
|
+
dir: canonicalizeDir(data.dir),
|
|
5700
|
+
name: data.name,
|
|
5701
|
+
version: data.version,
|
|
5702
|
+
port: data.port,
|
|
5703
|
+
pid: data.pid,
|
|
5704
|
+
status: data.status,
|
|
5705
|
+
startedAt: data.startedAt,
|
|
5706
|
+
stoppedAt: data.stoppedAt,
|
|
5707
|
+
updatedAt: data.updatedAt
|
|
5708
|
+
};
|
|
5709
|
+
} catch {
|
|
5710
|
+
return null;
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
function createTmpPath(entryPath) {
|
|
5714
|
+
const suffix = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
|
|
5715
|
+
return `${entryPath}.tmp.${suffix}`;
|
|
5716
|
+
}
|
|
5717
|
+
function writeEntryFile(entry) {
|
|
5718
|
+
ensureRegistryReady();
|
|
5719
|
+
const normalized = {
|
|
5720
|
+
...entry,
|
|
5721
|
+
dir: canonicalizeDir(entry.dir),
|
|
5722
|
+
updatedAt: entry.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
5723
|
+
};
|
|
5724
|
+
const entryPath = getEntryPathForCanonicalDir(normalized.dir);
|
|
5725
|
+
const tmpPath = createTmpPath(entryPath);
|
|
5726
|
+
fs10.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
5727
|
+
fs10.renameSync(tmpPath, entryPath);
|
|
5728
|
+
}
|
|
5729
|
+
function migrateLegacyRegistryIfNeeded() {
|
|
5730
|
+
if (migrationChecked) {
|
|
5731
|
+
return;
|
|
5732
|
+
}
|
|
5733
|
+
migrationChecked = true;
|
|
5734
|
+
ensureRegistryDir();
|
|
5735
|
+
if (!fs10.existsSync(LEGACY_REGISTRY_FILE)) {
|
|
5736
|
+
return;
|
|
5737
|
+
}
|
|
5738
|
+
if (listEntryFiles().length > 0) {
|
|
5739
|
+
return;
|
|
5740
|
+
}
|
|
5741
|
+
try {
|
|
5742
|
+
const raw = fs10.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
|
|
5743
|
+
const data = JSON.parse(raw);
|
|
5744
|
+
const packs = Array.isArray(data?.packs) ? data.packs : [];
|
|
5745
|
+
for (const pack of packs) {
|
|
5746
|
+
try {
|
|
5747
|
+
writeEntryFile({
|
|
5748
|
+
...pack,
|
|
5749
|
+
dir: canonicalizeDir(pack.dir),
|
|
5750
|
+
updatedAt: pack.updatedAt ?? pack.stoppedAt ?? pack.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
5751
|
+
});
|
|
5752
|
+
} catch {
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
fs10.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
|
|
5756
|
+
} catch (err) {
|
|
5757
|
+
console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
function ensureRegistryReady() {
|
|
5761
|
+
ensureRegistryDir();
|
|
5762
|
+
migrateLegacyRegistryIfNeeded();
|
|
5763
|
+
}
|
|
5764
|
+
function readEntry(dir) {
|
|
5765
|
+
ensureRegistryReady();
|
|
5766
|
+
return readEntryFile(getEntryPath(dir));
|
|
5767
|
+
}
|
|
5768
|
+
function register(opts) {
|
|
5769
|
+
try {
|
|
5770
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5771
|
+
const entry = {
|
|
5772
|
+
dir: canonicalizeDir(opts.dir),
|
|
5773
|
+
name: opts.name,
|
|
5774
|
+
version: opts.version,
|
|
5775
|
+
port: opts.port,
|
|
5776
|
+
pid: process.pid,
|
|
5777
|
+
status: "running",
|
|
5778
|
+
startedAt: now,
|
|
5779
|
+
updatedAt: now
|
|
5780
|
+
};
|
|
5781
|
+
writeEntryFile(entry);
|
|
5782
|
+
console.log(` [Registry] Registered "${opts.name}" (pid ${process.pid})`);
|
|
5783
|
+
} catch (err) {
|
|
5784
|
+
console.warn(" [Registry] Failed to register:", err);
|
|
5785
|
+
}
|
|
5786
|
+
}
|
|
5787
|
+
function deregister(dir, pid) {
|
|
5788
|
+
try {
|
|
5789
|
+
const entry = readEntry(dir);
|
|
5790
|
+
if (!entry || entry.pid !== pid) {
|
|
5791
|
+
return;
|
|
5792
|
+
}
|
|
5793
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5794
|
+
writeEntryFile({
|
|
5795
|
+
...entry,
|
|
5796
|
+
pid: null,
|
|
5797
|
+
status: "stopped",
|
|
5798
|
+
stoppedAt: now,
|
|
5799
|
+
updatedAt: now
|
|
5800
|
+
});
|
|
5801
|
+
console.log(` [Registry] Deregistered "${entry.name}"`);
|
|
5802
|
+
} catch (err) {
|
|
5803
|
+
console.warn(" [Registry] Failed to deregister:", err);
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
|
|
5587
5807
|
// src/runtime/server.ts
|
|
5588
|
-
var __dirname =
|
|
5808
|
+
var __dirname = path12.dirname(fileURLToPath2(import.meta.url));
|
|
5589
5809
|
async function startServer(options) {
|
|
5590
5810
|
const {
|
|
5591
5811
|
rootDir,
|
|
5592
5812
|
host = process.env.HOST || "127.0.0.1",
|
|
5593
5813
|
port = Number(process.env.PORT) || 26313,
|
|
5594
|
-
|
|
5814
|
+
daemonRun = false
|
|
5595
5815
|
} = options;
|
|
5596
5816
|
const dataConfig = configManager.load(rootDir);
|
|
5597
5817
|
const apiKey = dataConfig.apiKey || "";
|
|
5598
5818
|
const provider = dataConfig.provider || "openai";
|
|
5819
|
+
const canonicalRootDir = canonicalizeDir(rootDir);
|
|
5820
|
+
const packConfig = loadConfig(canonicalRootDir);
|
|
5821
|
+
const baseUrl = dataConfig.baseUrl?.trim() || void 0;
|
|
5599
5822
|
const modelId = provider === "anthropic" ? "claude-opus-4-6" : "gpt-5.4";
|
|
5600
|
-
const packageRoot =
|
|
5601
|
-
const webDir =
|
|
5823
|
+
const packageRoot = path12.resolve(__dirname, "..");
|
|
5824
|
+
const webDir = fs13.existsSync(path12.join(rootDir, "web")) ? path12.join(rootDir, "web") : path12.join(packageRoot, "web");
|
|
5602
5825
|
const app = express();
|
|
5603
5826
|
app.use(express.json());
|
|
5604
5827
|
app.use(express.static(webDir));
|
|
5605
5828
|
const server = createServer(app);
|
|
5829
|
+
app.get("/api/health", (_req, res) => {
|
|
5830
|
+
const address = server.address();
|
|
5831
|
+
const actualPort = typeof address === "string" ? port : address?.port ?? port;
|
|
5832
|
+
res.json({
|
|
5833
|
+
status: "ok",
|
|
5834
|
+
dir: canonicalRootDir,
|
|
5835
|
+
name: packConfig.name,
|
|
5836
|
+
version: packConfig.version,
|
|
5837
|
+
port: actualPort,
|
|
5838
|
+
pid: process.pid
|
|
5839
|
+
});
|
|
5840
|
+
});
|
|
5606
5841
|
const lifecycle = new Lifecycle(server);
|
|
5607
5842
|
const agent = new PackAgent({
|
|
5608
5843
|
apiKey,
|
|
5609
5844
|
rootDir,
|
|
5610
5845
|
provider,
|
|
5611
5846
|
modelId,
|
|
5847
|
+
baseUrl,
|
|
5612
5848
|
lifecycleHandler: lifecycle
|
|
5613
5849
|
});
|
|
5614
5850
|
const adapters = [];
|
|
@@ -5696,7 +5932,17 @@ async function startServer(options) {
|
|
|
5696
5932
|
Skills Pack Server`);
|
|
5697
5933
|
console.log(` Running at ${url}
|
|
5698
5934
|
`);
|
|
5699
|
-
|
|
5935
|
+
try {
|
|
5936
|
+
register({
|
|
5937
|
+
dir: canonicalRootDir,
|
|
5938
|
+
name: packConfig.name,
|
|
5939
|
+
version: packConfig.version,
|
|
5940
|
+
port: typeof actualPort === "number" ? actualPort : port
|
|
5941
|
+
});
|
|
5942
|
+
} catch (err) {
|
|
5943
|
+
console.warn(" [Registry] Could not register pack:", err);
|
|
5944
|
+
}
|
|
5945
|
+
if (!daemonRun) {
|
|
5700
5946
|
const cmd = process.platform === "darwin" ? `open ${url}` : process.platform === "win32" ? `start ${url}` : `xdg-open ${url}`;
|
|
5701
5947
|
exec(cmd, (err) => {
|
|
5702
5948
|
if (err) console.warn(` Could not open browser: ${err.message}`);
|
|
@@ -5704,9 +5950,11 @@ async function startServer(options) {
|
|
|
5704
5950
|
}
|
|
5705
5951
|
});
|
|
5706
5952
|
process.on("SIGINT", () => {
|
|
5953
|
+
deregister(canonicalRootDir, process.pid);
|
|
5707
5954
|
void lifecycle.requestShutdown("signal");
|
|
5708
5955
|
});
|
|
5709
5956
|
process.on("SIGTERM", () => {
|
|
5957
|
+
deregister(canonicalRootDir, process.pid);
|
|
5710
5958
|
void lifecycle.requestShutdown("signal");
|
|
5711
5959
|
});
|
|
5712
5960
|
await new Promise((resolve, reject) => {
|
|
@@ -5741,23 +5989,23 @@ function findMissingSkills(workDir, config) {
|
|
|
5741
5989
|
});
|
|
5742
5990
|
}
|
|
5743
5991
|
function copyStartTemplates2(workDir) {
|
|
5744
|
-
const templateDir =
|
|
5992
|
+
const templateDir = path13.resolve(
|
|
5745
5993
|
new URL("../templates", import.meta.url).pathname
|
|
5746
5994
|
);
|
|
5747
5995
|
for (const file of ["start.sh", "start.bat"]) {
|
|
5748
|
-
const src =
|
|
5749
|
-
const dest =
|
|
5750
|
-
if (
|
|
5751
|
-
|
|
5996
|
+
const src = path13.join(templateDir, file);
|
|
5997
|
+
const dest = path13.join(workDir, file);
|
|
5998
|
+
if (fs14.existsSync(src)) {
|
|
5999
|
+
fs14.copyFileSync(src, dest);
|
|
5752
6000
|
if (file === "start.sh") {
|
|
5753
|
-
|
|
6001
|
+
fs14.chmodSync(dest, 493);
|
|
5754
6002
|
}
|
|
5755
6003
|
}
|
|
5756
6004
|
}
|
|
5757
6005
|
}
|
|
5758
6006
|
async function runCommand(directory) {
|
|
5759
|
-
const workDir = directory ?
|
|
5760
|
-
|
|
6007
|
+
const workDir = directory ? path13.resolve(directory) : process.cwd();
|
|
6008
|
+
fs14.mkdirSync(workDir, { recursive: true });
|
|
5761
6009
|
if (!configExists(workDir)) {
|
|
5762
6010
|
console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
|
|
5763
6011
|
const { name, description } = await inquirer2.prompt([
|
|
@@ -5793,13 +6041,16 @@ async function runCommand(directory) {
|
|
|
5793
6041
|
console.warn(chalk4.yellow(` Warning: Some skills could not be installed: ${err}`));
|
|
5794
6042
|
}
|
|
5795
6043
|
}
|
|
5796
|
-
await startServer({
|
|
6044
|
+
await startServer({
|
|
6045
|
+
rootDir: workDir,
|
|
6046
|
+
daemonRun: process.env.DAEMON_RUN === "1"
|
|
6047
|
+
});
|
|
5797
6048
|
}
|
|
5798
6049
|
|
|
5799
6050
|
// src/cli.ts
|
|
5800
|
-
import
|
|
6051
|
+
import fs15 from "fs";
|
|
5801
6052
|
var packageJson = JSON.parse(
|
|
5802
|
-
|
|
6053
|
+
fs15.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
5803
6054
|
);
|
|
5804
6055
|
var program = new Command();
|
|
5805
6056
|
program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
|