@cremini/skillpack 1.2.5-beta.1 → 1.2.5

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/dist/cli.js CHANGED
@@ -107,6 +107,12 @@ var init_config = __esm({
107
107
  if (updates.baseUrl !== void 0) {
108
108
  this.configData.baseUrl = updates.baseUrl?.trim() || void 0;
109
109
  }
110
+ if (updates.modelId !== void 0) {
111
+ this.configData.modelId = updates.modelId?.trim() || void 0;
112
+ }
113
+ if (updates.apiProtocol !== void 0) {
114
+ this.configData.apiProtocol = updates.apiProtocol || void 0;
115
+ }
110
116
  if (updates.adapters !== void 0) {
111
117
  const merged = { ...this.configData.adapters || {} };
112
118
  for (const [adapterKey, adapterVal] of Object.entries(updates.adapters)) {
@@ -323,20 +329,6 @@ var init_commands = __esm({
323
329
  }
324
330
  });
325
331
 
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
-
340
332
  // src/runtime/adapters/markdown.ts
341
333
  function unwrapMarkdownSourceBlocks(text) {
342
334
  return text.replace(
@@ -457,7 +449,7 @@ var telegram_exports = {};
457
449
  __export(telegram_exports, {
458
450
  TelegramAdapter: () => TelegramAdapter
459
451
  });
460
- import fs13 from "fs";
452
+ import fs12 from "fs";
461
453
  import TelegramBot from "node-telegram-bot-api";
462
454
  var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
463
455
  var init_telegram = __esm({
@@ -477,14 +469,12 @@ var init_telegram = __esm({
477
469
  agent = null;
478
470
  options;
479
471
  rootDir = "";
480
- ipcBroadcaster = null;
481
472
  constructor(options) {
482
473
  this.options = options;
483
474
  }
484
475
  async start(ctx) {
485
476
  this.agent = ctx.agent;
486
477
  this.rootDir = ctx.rootDir;
487
- this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
488
478
  this.bot = new TelegramBot(this.options.token, { polling: true });
489
479
  this.bot.on("message", (msg) => {
490
480
  this.handleTelegramMessage(msg).catch((err) => {
@@ -526,15 +516,6 @@ var init_telegram = __esm({
526
516
  const messageId = msg.message_id;
527
517
  const text = (msg.text || msg.caption || "").trim();
528
518
  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
- );
538
519
  const attachments = await this.extractAttachments(msg, channelId);
539
520
  if (!text && attachments.length === 0) return;
540
521
  await this.tryAckReaction(chatId, messageId);
@@ -564,7 +545,6 @@ var init_telegram = __esm({
564
545
  });
565
546
  break;
566
547
  }
567
- this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
568
548
  };
569
549
  try {
570
550
  const userText = text || "(User sent an attachment)";
@@ -775,7 +755,7 @@ var init_telegram = __esm({
775
755
  async sendFileSafe(chatId, filePath, caption) {
776
756
  if (!this.bot) return;
777
757
  try {
778
- if (!fs13.existsSync(filePath)) {
758
+ if (!fs12.existsSync(filePath)) {
779
759
  console.error(`[Telegram] File not found for sending: ${filePath}`);
780
760
  return;
781
761
  }
@@ -795,8 +775,8 @@ var slack_exports = {};
795
775
  __export(slack_exports, {
796
776
  SlackAdapter: () => SlackAdapter
797
777
  });
798
- import fs14 from "fs";
799
- import path13 from "path";
778
+ import fs13 from "fs";
779
+ import path12 from "path";
800
780
  import { App, LogLevel } from "@slack/bolt";
801
781
  var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
802
782
  var init_slack = __esm({
@@ -827,14 +807,12 @@ var init_slack = __esm({
827
807
  botUserId = null;
828
808
  lastThreadByChannel = /* @__PURE__ */ new Map();
829
809
  rootDir = "";
830
- ipcBroadcaster = null;
831
810
  constructor(options) {
832
811
  this.options = options;
833
812
  }
834
813
  async start(ctx) {
835
814
  this.agent = ctx.agent;
836
815
  this.rootDir = ctx.rootDir;
837
- this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
838
816
  this.app = new App({
839
817
  token: this.options.botToken,
840
818
  appToken: this.options.appToken,
@@ -920,15 +898,6 @@ var init_slack = __esm({
920
898
  const teamId = this.getTeamId(body, context);
921
899
  const channelId = `slack-dm-${teamId}-${event.channel}`;
922
900
  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
- );
932
901
  const attachments = await this.extractSlackFiles(event, channelId, client);
933
902
  if (!text && attachments.length === 0) return;
934
903
  await this.tryAckReaction(client, event);
@@ -959,15 +928,6 @@ var init_slack = __esm({
959
928
  threadTs
960
929
  );
961
930
  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
- );
971
931
  const attachments = await this.extractSlackFiles(event, channelId, client);
972
932
  if (!text && attachments.length === 0) {
973
933
  await this.sendSafe(
@@ -1027,7 +987,6 @@ var init_slack = __esm({
1027
987
  caption: event.caption
1028
988
  });
1029
989
  }
1030
- this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
1031
990
  };
1032
991
  try {
1033
992
  const result = await this.agent.handleMessage(
@@ -1431,12 +1390,12 @@ var init_slack = __esm({
1431
1390
  */
1432
1391
  async sendFileSafe(client, route, filePath, caption) {
1433
1392
  try {
1434
- if (!fs14.existsSync(filePath)) {
1393
+ if (!fs13.existsSync(filePath)) {
1435
1394
  console.error(`[Slack] File not found for sending: ${filePath}`);
1436
1395
  return;
1437
1396
  }
1438
- const filename = path13.basename(filePath);
1439
- const fileContent = fs14.readFileSync(filePath);
1397
+ const filename = path12.basename(filePath);
1398
+ const fileContent = fs13.readFileSync(filePath);
1440
1399
  await client.files.uploadV2({
1441
1400
  channel_id: route.channel,
1442
1401
  thread_ts: route.threadTs,
@@ -1453,6 +1412,20 @@ var init_slack = __esm({
1453
1412
  }
1454
1413
  });
1455
1414
 
1415
+ // src/runtime/adapters/types.ts
1416
+ var types_exports = {};
1417
+ __export(types_exports, {
1418
+ isMessageSender: () => isMessageSender
1419
+ });
1420
+ function isMessageSender(adapter) {
1421
+ return typeof adapter.sendMessage === "function";
1422
+ }
1423
+ var init_types = __esm({
1424
+ "src/runtime/adapters/types.ts"() {
1425
+ "use strict";
1426
+ }
1427
+ });
1428
+
1456
1429
  // src/runtime/adapters/scheduler.ts
1457
1430
  var scheduler_exports = {};
1458
1431
  __export(scheduler_exports, {
@@ -2463,15 +2436,15 @@ async function interactiveCreate(workDir) {
2463
2436
  }
2464
2437
 
2465
2438
  // src/commands/run.ts
2466
- import path15 from "path";
2467
- import fs16 from "fs";
2439
+ import path14 from "path";
2440
+ import fs15 from "fs";
2468
2441
  import inquirer2 from "inquirer";
2469
2442
  import chalk4 from "chalk";
2470
2443
 
2471
2444
  // src/runtime/server.ts
2472
2445
  import express from "express";
2473
- import path14 from "path";
2474
- import fs15 from "fs";
2446
+ import path13 from "path";
2447
+ import fs14 from "fs";
2475
2448
  import { fileURLToPath as fileURLToPath2 } from "url";
2476
2449
  import { createServer } from "http";
2477
2450
  import { exec } from "child_process";
@@ -5598,10 +5571,30 @@ var PackAgent = class {
5598
5571
  const { rootDir, provider, modelId, baseUrl } = this.options;
5599
5572
  const authStorage = this.authStorage;
5600
5573
  const modelRegistry = new ModelRegistry(authStorage);
5574
+ if (baseUrl && modelId) {
5575
+ const apiProtocol = this.options.apiProtocol ?? "openai-completions";
5576
+ log(`[PackAgent] Registering custom model ${provider}/${modelId} api=${apiProtocol} baseUrl=${baseUrl}`);
5577
+ modelRegistry.registerProvider(provider, {
5578
+ baseUrl,
5579
+ apiKey: this.options.apiKey,
5580
+ models: [
5581
+ {
5582
+ id: modelId,
5583
+ name: modelId,
5584
+ api: apiProtocol,
5585
+ reasoning: false,
5586
+ input: ["text"],
5587
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
5588
+ contextWindow: 128e3,
5589
+ maxTokens: 4096
5590
+ }
5591
+ ]
5592
+ });
5593
+ }
5601
5594
  const resolvedModel = modelRegistry.find(provider, modelId);
5602
- const model = resolvedModel && baseUrl ? { ...resolvedModel, baseUrl } : resolvedModel;
5595
+ const model = resolvedModel && baseUrl && !this.options.apiProtocol ? { ...resolvedModel, baseUrl } : resolvedModel;
5603
5596
  if (resolvedModel && baseUrl) {
5604
- log(`[PackAgent] Overriding ${provider}/${modelId} baseUrl -> ${baseUrl}`);
5597
+ log(`[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`);
5605
5598
  }
5606
5599
  const sessionDir = path9.resolve(
5607
5600
  rootDir,
@@ -5868,9 +5861,6 @@ ${text}`;
5868
5861
  /** Reserved: restore a historical session */
5869
5862
  async restoreSession(_sessionId) {
5870
5863
  }
5871
- getActiveChannelIds() {
5872
- return Array.from(this.channels.keys());
5873
- }
5874
5864
  };
5875
5865
 
5876
5866
  // src/runtime/adapters/web.ts
@@ -5895,6 +5885,8 @@ function getRuntimeConfigSignature(config) {
5895
5885
  apiKey: config.apiKey || "",
5896
5886
  provider: config.provider || "openai",
5897
5887
  baseUrl: config.baseUrl || "",
5888
+ modelId: config.modelId || "",
5889
+ apiProtocol: config.apiProtocol || "",
5898
5890
  telegramToken: config.adapters?.telegram?.token || "",
5899
5891
  slackBotToken: config.adapters?.slack?.botToken || "",
5900
5892
  slackAppToken: config.adapters?.slack?.appToken || ""
@@ -5904,11 +5896,9 @@ var WebAdapter = class {
5904
5896
  name = "web";
5905
5897
  wss = null;
5906
5898
  agent = null;
5907
- ipcBroadcaster = null;
5908
5899
  async start(ctx) {
5909
5900
  const { agent, server, app, rootDir, lifecycle } = ctx;
5910
5901
  this.agent = agent;
5911
- this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
5912
5902
  const currentConf = configManager.getConfig();
5913
5903
  let apiKey = currentConf.apiKey || "";
5914
5904
  let currentProvider = currentConf.provider || "openai";
@@ -5927,6 +5917,8 @@ var WebAdapter = class {
5927
5917
  apiKey: conf.apiKey || "",
5928
5918
  provider: currentProvider2,
5929
5919
  baseUrl: conf.baseUrl || "",
5920
+ modelId: conf.modelId || "",
5921
+ apiProtocol: conf.apiProtocol || "",
5930
5922
  adapters: conf.adapters || {},
5931
5923
  supportedProviders: SUPPORTED_PROVIDERS,
5932
5924
  oauthConnected
@@ -5937,7 +5929,7 @@ var WebAdapter = class {
5937
5929
  res.json(config.skills || []);
5938
5930
  });
5939
5931
  app.post("/api/config/update", (req, res) => {
5940
- const { key, provider, baseUrl, adapters } = req.body;
5932
+ const { key, provider, baseUrl, modelId, apiProtocol, adapters } = req.body;
5941
5933
  const updates = {};
5942
5934
  const beforeConfig = JSON.parse(JSON.stringify(configManager.getConfig()));
5943
5935
  if (key !== void 0) {
@@ -5951,6 +5943,12 @@ var WebAdapter = class {
5951
5943
  if (baseUrl !== void 0) {
5952
5944
  updates.baseUrl = baseUrl;
5953
5945
  }
5946
+ if (modelId !== void 0) {
5947
+ updates.modelId = modelId;
5948
+ }
5949
+ if (apiProtocol !== void 0) {
5950
+ updates.apiProtocol = apiProtocol;
5951
+ }
5954
5952
  if (adapters !== void 0) {
5955
5953
  updates.adapters = adapters;
5956
5954
  }
@@ -6176,7 +6174,6 @@ var WebAdapter = class {
6176
6174
  }
6177
6175
  const onEvent = (event) => {
6178
6176
  sendWsEvent(ws, event);
6179
- this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
6180
6177
  };
6181
6178
  const result = await agent.handleMessage("web", channelId, text, onEvent);
6182
6179
  if (result.errorMessage) {
@@ -6194,367 +6191,6 @@ var WebAdapter = class {
6194
6191
  }
6195
6192
  };
6196
6193
 
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
-
6558
6194
  // src/runtime/server.ts
6559
6195
  init_config();
6560
6196
 
@@ -6631,28 +6267,28 @@ var Lifecycle = class {
6631
6267
 
6632
6268
  // src/runtime/registry.ts
6633
6269
  import crypto from "crypto";
6634
- import fs12 from "fs";
6270
+ import fs11 from "fs";
6635
6271
  import os from "os";
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");
6272
+ import path11 from "path";
6273
+ var SKILLPACK_HOME = path11.join(os.homedir(), ".skillpack");
6274
+ var LEGACY_REGISTRY_FILE = path11.join(SKILLPACK_HOME, "registry.json");
6275
+ var REGISTRY_DIR = path11.join(SKILLPACK_HOME, "registry.d");
6640
6276
  var migrationChecked = false;
6641
6277
  function ensureHomeDir() {
6642
- if (!fs12.existsSync(SKILLPACK_HOME)) {
6643
- fs12.mkdirSync(SKILLPACK_HOME, { recursive: true });
6278
+ if (!fs11.existsSync(SKILLPACK_HOME)) {
6279
+ fs11.mkdirSync(SKILLPACK_HOME, { recursive: true });
6644
6280
  }
6645
6281
  }
6646
6282
  function ensureRegistryDir() {
6647
6283
  ensureHomeDir();
6648
- if (!fs12.existsSync(REGISTRY_DIR)) {
6649
- fs12.mkdirSync(REGISTRY_DIR, { recursive: true });
6284
+ if (!fs11.existsSync(REGISTRY_DIR)) {
6285
+ fs11.mkdirSync(REGISTRY_DIR, { recursive: true });
6650
6286
  }
6651
6287
  }
6652
6288
  function canonicalizeDir(dir) {
6653
- const resolved = path12.resolve(dir);
6289
+ const resolved = path11.resolve(dir);
6654
6290
  try {
6655
- return fs12.realpathSync(resolved);
6291
+ return fs11.realpathSync(resolved);
6656
6292
  } catch {
6657
6293
  return resolved;
6658
6294
  }
@@ -6661,7 +6297,7 @@ function hashDir(dir) {
6661
6297
  return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
6662
6298
  }
6663
6299
  function getEntryPathForCanonicalDir(dir) {
6664
- return path12.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6300
+ return path11.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
6665
6301
  }
6666
6302
  function getEntryPath(dir) {
6667
6303
  ensureRegistryReady();
@@ -6669,11 +6305,11 @@ function getEntryPath(dir) {
6669
6305
  }
6670
6306
  function listEntryFiles() {
6671
6307
  ensureRegistryReady();
6672
- return fs12.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path12.join(REGISTRY_DIR, file));
6308
+ return fs11.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path11.join(REGISTRY_DIR, file));
6673
6309
  }
6674
6310
  function readEntryFile(filePath) {
6675
6311
  try {
6676
- const raw = fs12.readFileSync(filePath, "utf-8");
6312
+ const raw = fs11.readFileSync(filePath, "utf-8");
6677
6313
  const data = JSON.parse(raw);
6678
6314
  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") {
6679
6315
  return null;
@@ -6706,8 +6342,8 @@ function writeEntryFile(entry) {
6706
6342
  };
6707
6343
  const entryPath = getEntryPathForCanonicalDir(normalized.dir);
6708
6344
  const tmpPath = createTmpPath(entryPath);
6709
- fs12.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6710
- fs12.renameSync(tmpPath, entryPath);
6345
+ fs11.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
6346
+ fs11.renameSync(tmpPath, entryPath);
6711
6347
  }
6712
6348
  function migrateLegacyRegistryIfNeeded() {
6713
6349
  if (migrationChecked) {
@@ -6715,14 +6351,14 @@ function migrateLegacyRegistryIfNeeded() {
6715
6351
  }
6716
6352
  migrationChecked = true;
6717
6353
  ensureRegistryDir();
6718
- if (!fs12.existsSync(LEGACY_REGISTRY_FILE)) {
6354
+ if (!fs11.existsSync(LEGACY_REGISTRY_FILE)) {
6719
6355
  return;
6720
6356
  }
6721
6357
  if (listEntryFiles().length > 0) {
6722
6358
  return;
6723
6359
  }
6724
6360
  try {
6725
- const raw = fs12.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6361
+ const raw = fs11.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
6726
6362
  const data = JSON.parse(raw);
6727
6363
  const packs = Array.isArray(data?.packs) ? data.packs : [];
6728
6364
  for (const pack of packs) {
@@ -6735,7 +6371,7 @@ function migrateLegacyRegistryIfNeeded() {
6735
6371
  } catch {
6736
6372
  }
6737
6373
  }
6738
- fs12.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6374
+ fs11.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
6739
6375
  } catch (err) {
6740
6376
  console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
6741
6377
  }
@@ -6788,7 +6424,7 @@ function deregister(dir, pid) {
6788
6424
  }
6789
6425
 
6790
6426
  // src/runtime/server.ts
6791
- var __dirname = path14.dirname(fileURLToPath2(import.meta.url));
6427
+ var __dirname = path13.dirname(fileURLToPath2(import.meta.url));
6792
6428
  async function startServer(options) {
6793
6429
  const {
6794
6430
  rootDir,
@@ -6802,9 +6438,10 @@ async function startServer(options) {
6802
6438
  const canonicalRootDir = canonicalizeDir(rootDir);
6803
6439
  const packConfig = loadConfig(canonicalRootDir);
6804
6440
  const baseUrl = dataConfig.baseUrl?.trim() || void 0;
6805
- const modelId = SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId;
6806
- const packageRoot = path14.resolve(__dirname, "..");
6807
- const webDir = fs15.existsSync(path14.join(rootDir, "web")) ? path14.join(rootDir, "web") : path14.join(packageRoot, "web");
6441
+ const modelId = dataConfig.modelId?.trim() || (SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId);
6442
+ const apiProtocol = dataConfig.apiProtocol;
6443
+ const packageRoot = path13.resolve(__dirname, "..");
6444
+ const webDir = fs14.existsSync(path13.join(rootDir, "web")) ? path13.join(rootDir, "web") : path13.join(packageRoot, "web");
6808
6445
  const app = express();
6809
6446
  app.use(express.json());
6810
6447
  app.use(express.static(webDir));
@@ -6828,35 +6465,13 @@ async function startServer(options) {
6828
6465
  provider,
6829
6466
  modelId,
6830
6467
  baseUrl,
6468
+ apiProtocol,
6831
6469
  lifecycleHandler: lifecycle
6832
6470
  });
6833
6471
  const adapters = [];
6834
6472
  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;
6850
6473
  const webAdapter = new WebAdapter();
6851
- await webAdapter.start({
6852
- agent,
6853
- server,
6854
- app,
6855
- rootDir,
6856
- lifecycle,
6857
- adapterMap,
6858
- ipcBroadcaster
6859
- });
6474
+ await webAdapter.start({ agent, server, app, rootDir, lifecycle, adapterMap });
6860
6475
  adapters.push(webAdapter);
6861
6476
  adapterMap.set(webAdapter.name, webAdapter);
6862
6477
  if (dataConfig.adapters?.telegram?.token) {
@@ -6865,15 +6480,7 @@ async function startServer(options) {
6865
6480
  const telegramAdapter = new TelegramAdapter2({
6866
6481
  token: dataConfig.adapters.telegram.token
6867
6482
  });
6868
- await telegramAdapter.start({
6869
- agent,
6870
- server,
6871
- app,
6872
- rootDir,
6873
- lifecycle,
6874
- adapterMap,
6875
- ipcBroadcaster
6876
- });
6483
+ await telegramAdapter.start({ agent, server, app, rootDir, lifecycle });
6877
6484
  adapters.push(telegramAdapter);
6878
6485
  adapterMap.set(telegramAdapter.name, telegramAdapter);
6879
6486
  } catch (err) {
@@ -6893,15 +6500,7 @@ async function startServer(options) {
6893
6500
  botToken: slackConfig.botToken,
6894
6501
  appToken: slackConfig.appToken
6895
6502
  });
6896
- await slackAdapter.start({
6897
- agent,
6898
- server,
6899
- app,
6900
- rootDir,
6901
- lifecycle,
6902
- adapterMap,
6903
- ipcBroadcaster
6904
- });
6503
+ await slackAdapter.start({ agent, server, app, rootDir, lifecycle });
6905
6504
  adapters.push(slackAdapter);
6906
6505
  adapterMap.set(slackAdapter.name, slackAdapter);
6907
6506
  } catch (err) {
@@ -6964,9 +6563,6 @@ async function startServer(options) {
6964
6563
  } catch (err) {
6965
6564
  console.warn(" [Registry] Could not register pack:", err);
6966
6565
  }
6967
- if (hasIpcChannel) {
6968
- ipcAdapter.notifyReady(typeof actualPort === "number" ? actualPort : port);
6969
- }
6970
6566
  if (!daemonRun) {
6971
6567
  const cmd = process.platform === "darwin" ? `open ${url}` : process.platform === "win32" ? `start ${url}` : `xdg-open ${url}`;
6972
6568
  exec(cmd, (err) => {
@@ -7014,23 +6610,23 @@ function findMissingSkills(workDir, config) {
7014
6610
  });
7015
6611
  }
7016
6612
  function copyStartTemplates2(workDir) {
7017
- const templateDir = path15.resolve(
6613
+ const templateDir = path14.resolve(
7018
6614
  new URL("../templates", import.meta.url).pathname
7019
6615
  );
7020
6616
  for (const file of ["start.sh", "start.bat"]) {
7021
- const src = path15.join(templateDir, file);
7022
- const dest = path15.join(workDir, file);
7023
- if (fs16.existsSync(src)) {
7024
- fs16.copyFileSync(src, dest);
6617
+ const src = path14.join(templateDir, file);
6618
+ const dest = path14.join(workDir, file);
6619
+ if (fs15.existsSync(src)) {
6620
+ fs15.copyFileSync(src, dest);
7025
6621
  if (file === "start.sh") {
7026
- fs16.chmodSync(dest, 493);
6622
+ fs15.chmodSync(dest, 493);
7027
6623
  }
7028
6624
  }
7029
6625
  }
7030
6626
  }
7031
6627
  async function runCommand(directory) {
7032
- const workDir = directory ? path15.resolve(directory) : process.cwd();
7033
- fs16.mkdirSync(workDir, { recursive: true });
6628
+ const workDir = directory ? path14.resolve(directory) : process.cwd();
6629
+ fs15.mkdirSync(workDir, { recursive: true });
7034
6630
  if (!configExists(workDir)) {
7035
6631
  console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
7036
6632
  const { name, description } = await inquirer2.prompt([
@@ -7075,9 +6671,9 @@ async function runCommand(directory) {
7075
6671
  }
7076
6672
 
7077
6673
  // src/cli.ts
7078
- import fs17 from "fs";
6674
+ import fs16 from "fs";
7079
6675
  var packageJson = JSON.parse(
7080
- fs17.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
6676
+ fs16.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
7081
6677
  );
7082
6678
  var program = new Command();
7083
6679
  program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cremini/skillpack",
3
- "version": "1.2.5-beta.1",
3
+ "version": "1.2.5",
4
4
  "description": "Pack AI Skills into Local Agents",
5
5
  "type": "module",
6
6
  "repository": {
@@ -65,4 +65,4 @@
65
65
  "tsup": "^8.5.1",
66
66
  "typescript": "^5.9.3"
67
67
  }
68
- }
68
+ }
@@ -7,7 +7,7 @@ set "FIRST_RUN=1"
7
7
  :loop
8
8
  set "SKILLPACK_FIRST_RUN=%FIRST_RUN%"
9
9
  set "PACK_ROOT=%~dp0"
10
- npx -y @cremini/skillpack run .
10
+ npx -y @cremini/skillpack@latest run .
11
11
  set "EXIT_CODE=%errorlevel%"
12
12
 
13
13
  set "FIRST_RUN=0"
@@ -7,7 +7,7 @@ FIRST_RUN=1
7
7
  while true; do
8
8
  SKILLPACK_FIRST_RUN="$FIRST_RUN" \
9
9
  PACK_ROOT="$(pwd)" \
10
- npx -y @cremini/skillpack run .
10
+ npx -y @cremini/skillpack@latest run .
11
11
  EXIT_CODE=$?
12
12
 
13
13
  FIRST_RUN=0
package/web/index.html CHANGED
@@ -96,6 +96,19 @@
96
96
  <label>Custom Base URL <span class="label-hint">(optional)</span></label>
97
97
  <input type="text" id="apikey-baseurl-input" placeholder="https://api.openai.com/v1" class="form-input" />
98
98
  </div>
99
+ <div class="form-group" id="apikey-modelid-group" style="display: none;">
100
+ <label>Custom Model Name <span class="label-hint">(optional)</span></label>
101
+ <input type="text" id="apikey-modelid-input" placeholder="e.g. gpt-4o, llama-3-70b" class="form-input" />
102
+ </div>
103
+ <div class="form-group" id="apikey-protocol-group" style="display: none;">
104
+ <label>API Protocol</label>
105
+ <div class="provider-select-wrapper">
106
+ <select id="apikey-protocol-select" class="form-input">
107
+ <option value="openai-completions">Chat Completions API (most proxies)</option>
108
+ <option value="openai-responses">Responses API (native OpenAI)</option>
109
+ </select>
110
+ </div>
111
+ </div>
99
112
  </div>
100
113
 
101
114
  <!-- OAuth Section -->
@@ -16,6 +16,10 @@ let providerSelect;
16
16
  let apiKeyInput;
17
17
  let baseUrlInput;
18
18
  let baseUrlGroup;
19
+ let modelIdInput;
20
+ let modelIdGroup;
21
+ let protocolSelect;
22
+ let protocolGroup;
19
23
  let apikeySection;
20
24
  let oauthSection;
21
25
  let oauthLoginBtn;
@@ -36,6 +40,10 @@ export function initApiKeyDialog() {
36
40
  apiKeyInput = document.getElementById("apikey-input");
37
41
  baseUrlInput = document.getElementById("apikey-baseurl-input");
38
42
  baseUrlGroup = document.getElementById("apikey-baseurl-group");
43
+ modelIdInput = document.getElementById("apikey-modelid-input");
44
+ modelIdGroup = document.getElementById("apikey-modelid-group");
45
+ protocolSelect = document.getElementById("apikey-protocol-select");
46
+ protocolGroup = document.getElementById("apikey-protocol-group");
39
47
  apikeySection = document.getElementById("apikey-apikey-section");
40
48
  oauthSection = document.getElementById("apikey-oauth-section");
41
49
  oauthLoginBtn = document.getElementById("oauth-login-btn");
@@ -67,6 +75,9 @@ export function initApiKeyDialog() {
67
75
  if (oauthLogoutBtn) {
68
76
  oauthLogoutBtn.addEventListener("click", handleOAuthLogout);
69
77
  }
78
+ if (baseUrlInput) {
79
+ baseUrlInput.addEventListener("input", updateModelIdVisibility);
80
+ }
70
81
  }
71
82
 
72
83
  /**
@@ -126,6 +137,12 @@ function populateForm() {
126
137
  if (baseUrlInput) {
127
138
  baseUrlInput.value = config.baseUrl || "";
128
139
  }
140
+ if (modelIdInput) {
141
+ modelIdInput.value = config.modelId || "";
142
+ }
143
+ if (protocolSelect) {
144
+ protocolSelect.value = config.apiProtocol || "openai-completions";
145
+ }
129
146
 
130
147
  // OAuth Status
131
148
  if (config.oauthConnected) {
@@ -163,6 +180,9 @@ function updateProviderUI() {
163
180
  baseUrlInput.placeholder =
164
181
  meta.baseUrlPlaceholder || "https://api.openai.com/v1";
165
182
  }
183
+
184
+ // Show model name field only when a custom base URL is filled in
185
+ updateModelIdVisibility();
166
186
 
167
187
  // Update placeholder
168
188
  if (apiKeyInput) {
@@ -171,10 +191,21 @@ function updateProviderUI() {
171
191
  }
172
192
  }
173
193
 
194
+ function updateModelIdVisibility() {
195
+ if (!modelIdGroup) return;
196
+ const hasCustomUrl = baseUrlInput && baseUrlInput.value.trim().length > 0;
197
+ modelIdGroup.style.display = hasCustomUrl ? "" : "none";
198
+ if (protocolGroup) {
199
+ protocolGroup.style.display = hasCustomUrl ? "" : "none";
200
+ }
201
+ }
202
+
174
203
  async function handleSave() {
175
204
  const key = apiKeyInput.value.trim();
176
205
  const provider = providerSelect.value;
177
206
  const baseUrl = baseUrlInput.value.trim();
207
+ const modelId = modelIdInput ? modelIdInput.value.trim() : "";
208
+ const apiProtocol = protocolSelect ? protocolSelect.value : "";
178
209
 
179
210
  // If OAuth is selected, we don't handle it here but it shouldn't happen as save button is hidden
180
211
  const meta = state.config?.supportedProviders?.[provider];
@@ -184,6 +215,15 @@ async function handleSave() {
184
215
  if (baseUrl !== state.config.baseUrl) {
185
216
  updates.baseUrl = baseUrl;
186
217
  }
218
+ if (modelId !== (state.config.modelId || "")) {
219
+ updates.modelId = modelId;
220
+ }
221
+ const hasCustomUrl = baseUrl.length > 0;
222
+ if (hasCustomUrl && apiProtocol !== (state.config.apiProtocol || "openai-completions")) {
223
+ updates.apiProtocol = apiProtocol;
224
+ } else if (!hasCustomUrl && state.config.apiProtocol) {
225
+ updates.apiProtocol = "";
226
+ }
187
227
 
188
228
  if (key && key !== "***************************************************" && key !== state.config.apiKey) {
189
229
  updates.key = key;
@@ -195,6 +235,8 @@ async function handleSave() {
195
235
 
196
236
  state.config.provider = res.provider;
197
237
  state.config.baseUrl = res.baseUrl || "";
238
+ state.config.modelId = res.modelId || "";
239
+ state.config.apiProtocol = res.apiProtocol || "";
198
240
  if (updates.key) {
199
241
  state.config.hasApiKey = true;
200
242
  state.config.apiKey = updates.key;