@cremini/skillpack 1.2.10 → 1.2.11
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 +490 -652
- package/package.json +1 -2
package/dist/cli.js
CHANGED
|
@@ -417,7 +417,7 @@ var telegram_exports = {};
|
|
|
417
417
|
__export(telegram_exports, {
|
|
418
418
|
TelegramAdapter: () => TelegramAdapter
|
|
419
419
|
});
|
|
420
|
-
import
|
|
420
|
+
import fs14 from "fs";
|
|
421
421
|
import TelegramBot from "node-telegram-bot-api";
|
|
422
422
|
var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
|
|
423
423
|
var init_telegram = __esm({
|
|
@@ -735,7 +735,7 @@ var init_telegram = __esm({
|
|
|
735
735
|
async sendFileSafe(chatId, filePath, caption) {
|
|
736
736
|
if (!this.bot) return;
|
|
737
737
|
try {
|
|
738
|
-
if (!
|
|
738
|
+
if (!fs14.existsSync(filePath)) {
|
|
739
739
|
console.error(`[Telegram] File not found for sending: ${filePath}`);
|
|
740
740
|
return;
|
|
741
741
|
}
|
|
@@ -755,8 +755,8 @@ var slack_exports = {};
|
|
|
755
755
|
__export(slack_exports, {
|
|
756
756
|
SlackAdapter: () => SlackAdapter
|
|
757
757
|
});
|
|
758
|
-
import
|
|
759
|
-
import
|
|
758
|
+
import fs15 from "fs";
|
|
759
|
+
import path15 from "path";
|
|
760
760
|
import { App, LogLevel } from "@slack/bolt";
|
|
761
761
|
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
|
|
762
762
|
var init_slack = __esm({
|
|
@@ -1391,12 +1391,12 @@ var init_slack = __esm({
|
|
|
1391
1391
|
*/
|
|
1392
1392
|
async sendFileSafe(client, route, filePath, caption) {
|
|
1393
1393
|
try {
|
|
1394
|
-
if (!
|
|
1394
|
+
if (!fs15.existsSync(filePath)) {
|
|
1395
1395
|
console.error(`[Slack] File not found for sending: ${filePath}`);
|
|
1396
1396
|
return;
|
|
1397
1397
|
}
|
|
1398
|
-
const filename =
|
|
1399
|
-
const fileContent =
|
|
1398
|
+
const filename = path15.basename(filePath);
|
|
1399
|
+
const fileContent = fs15.readFileSync(filePath);
|
|
1400
1400
|
await client.files.uploadV2({
|
|
1401
1401
|
channel_id: route.channel,
|
|
1402
1402
|
thread_ts: route.threadTs,
|
|
@@ -1444,14 +1444,19 @@ var init_scheduler = __esm({
|
|
|
1444
1444
|
name = "scheduler";
|
|
1445
1445
|
agent;
|
|
1446
1446
|
rootDir = "";
|
|
1447
|
+
ipcBroadcaster = null;
|
|
1447
1448
|
notifyFn = async () => {
|
|
1448
1449
|
};
|
|
1449
1450
|
jobs = /* @__PURE__ */ new Map();
|
|
1450
1451
|
async start(ctx) {
|
|
1451
1452
|
this.agent = ctx.agent;
|
|
1452
1453
|
this.rootDir = ctx.rootDir;
|
|
1454
|
+
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
1453
1455
|
this.notifyFn = ctx.notify || (async () => {
|
|
1454
1456
|
});
|
|
1457
|
+
console.log(
|
|
1458
|
+
`[Scheduler] IPC broadcaster ${this.ipcBroadcaster ? "attached" : "not available"} for agent events`
|
|
1459
|
+
);
|
|
1455
1460
|
const jobConfigs = loadJobFile(this.rootDir).jobs;
|
|
1456
1461
|
let scheduledCount = 0;
|
|
1457
1462
|
let disabledCount = 0;
|
|
@@ -1549,8 +1554,27 @@ var init_scheduler = __esm({
|
|
|
1549
1554
|
console.log(`[Scheduler] Running job "${jobConfig.name}"`);
|
|
1550
1555
|
let fullText = "";
|
|
1551
1556
|
let agentFailed = false;
|
|
1557
|
+
let loggedFirstTextDelta = false;
|
|
1558
|
+
let sawAgentStart = false;
|
|
1559
|
+
let sawAgentEnd = false;
|
|
1552
1560
|
const pendingFiles = [];
|
|
1553
1561
|
const onEvent = (event) => {
|
|
1562
|
+
if (event.type === "agent_start") {
|
|
1563
|
+
sawAgentStart = true;
|
|
1564
|
+
} else if (event.type === "agent_end") {
|
|
1565
|
+
sawAgentEnd = true;
|
|
1566
|
+
}
|
|
1567
|
+
if (event.type === "agent_start" || event.type === "agent_end") {
|
|
1568
|
+
console.log(
|
|
1569
|
+
`[Scheduler] Forwarding ${event.type} for ${channelId} (ipc=${this.ipcBroadcaster ? "yes" : "no"})`
|
|
1570
|
+
);
|
|
1571
|
+
} else if (event.type === "text_delta" && !loggedFirstTextDelta) {
|
|
1572
|
+
loggedFirstTextDelta = true;
|
|
1573
|
+
console.log(
|
|
1574
|
+
`[Scheduler] Forwarding first text_delta for ${channelId} (${event.delta.length} chars, ipc=${this.ipcBroadcaster ? "yes" : "no"})`
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
this.ipcBroadcaster?.broadcastAgentEvent(channelId, event);
|
|
1554
1578
|
if (event.type === "text_delta") fullText += event.delta;
|
|
1555
1579
|
if (event.type === "file_output") {
|
|
1556
1580
|
pendingFiles.push({
|
|
@@ -1568,6 +1592,7 @@ var init_scheduler = __esm({
|
|
|
1568
1592
|
onEvent,
|
|
1569
1593
|
void 0
|
|
1570
1594
|
);
|
|
1595
|
+
this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
|
|
1571
1596
|
if (result.errorMessage) {
|
|
1572
1597
|
fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u6267\u884C\u5931\u8D25\uFF1A${result.errorMessage}`;
|
|
1573
1598
|
agentFailed = true;
|
|
@@ -1576,6 +1601,7 @@ var init_scheduler = __esm({
|
|
|
1576
1601
|
if (job) job.lastError = void 0;
|
|
1577
1602
|
}
|
|
1578
1603
|
} catch (err) {
|
|
1604
|
+
this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
|
|
1579
1605
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1580
1606
|
fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u5F02\u5E38\uFF1A${errorMsg}`;
|
|
1581
1607
|
agentFailed = true;
|
|
@@ -1612,10 +1638,19 @@ var init_scheduler = __esm({
|
|
|
1612
1638
|
return { text: fullText, notifyFailed };
|
|
1613
1639
|
}
|
|
1614
1640
|
async clearJobContext(channelId) {
|
|
1641
|
+
console.log(`[Scheduler] Clearing context for ${channelId}`);
|
|
1615
1642
|
const result = await this.agent.handleCommand("clear", channelId);
|
|
1616
1643
|
if (!result.success) {
|
|
1617
1644
|
throw new Error(result.message || `Failed to clear context for ${channelId}`);
|
|
1618
1645
|
}
|
|
1646
|
+
console.log(`[Scheduler] Context cleared for ${channelId}`);
|
|
1647
|
+
}
|
|
1648
|
+
ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent) {
|
|
1649
|
+
if (!sawAgentStart || sawAgentEnd) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
console.log(`[Scheduler] Synthesizing agent_end for ${channelId}`);
|
|
1653
|
+
onEvent({ type: "agent_end" });
|
|
1619
1654
|
}
|
|
1620
1655
|
// -------------------------------------------------------------------------
|
|
1621
1656
|
// Dynamic management API
|
|
@@ -2480,22 +2515,22 @@ async function interactiveCreate(workDir) {
|
|
|
2480
2515
|
}
|
|
2481
2516
|
|
|
2482
2517
|
// src/commands/run.ts
|
|
2483
|
-
import
|
|
2484
|
-
import
|
|
2518
|
+
import path17 from "path";
|
|
2519
|
+
import fs17 from "fs";
|
|
2485
2520
|
import inquirer2 from "inquirer";
|
|
2486
2521
|
import chalk4 from "chalk";
|
|
2487
2522
|
|
|
2488
2523
|
// src/runtime/server.ts
|
|
2489
2524
|
import express from "express";
|
|
2490
|
-
import
|
|
2491
|
-
import
|
|
2525
|
+
import path16 from "path";
|
|
2526
|
+
import fs16 from "fs";
|
|
2492
2527
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2493
2528
|
import { createServer } from "http";
|
|
2494
2529
|
import { exec } from "child_process";
|
|
2495
2530
|
|
|
2496
2531
|
// src/runtime/agent.ts
|
|
2497
|
-
import
|
|
2498
|
-
import
|
|
2532
|
+
import path11 from "path";
|
|
2533
|
+
import fs10 from "fs";
|
|
2499
2534
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2500
2535
|
import { fileURLToPath } from "url";
|
|
2501
2536
|
import {
|
|
@@ -2726,40 +2761,238 @@ var ConfigFileAuthBackend = class {
|
|
|
2726
2761
|
// src/runtime/agent.ts
|
|
2727
2762
|
init_attachment_utils();
|
|
2728
2763
|
|
|
2729
|
-
// src/runtime/
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2764
|
+
// src/runtime/custom-tools/delegated-custom-tool-client.ts
|
|
2765
|
+
import { randomUUID } from "crypto";
|
|
2766
|
+
function isObject(value) {
|
|
2767
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2768
|
+
}
|
|
2769
|
+
function assertToolDefinitions(value) {
|
|
2770
|
+
if (!Array.isArray(value)) {
|
|
2771
|
+
throw new Error("Invalid delegated custom tool definitions response");
|
|
2772
|
+
}
|
|
2773
|
+
for (const definition of value) {
|
|
2774
|
+
if (!isObject(definition) || typeof definition.name !== "string" || typeof definition.label !== "string" || typeof definition.description !== "string" || !isObject(definition.parameters)) {
|
|
2775
|
+
throw new Error("Invalid delegated custom tool definition");
|
|
2776
|
+
}
|
|
2733
2777
|
}
|
|
2734
|
-
const normalized = Math.floor(limit);
|
|
2735
|
-
return Math.max(1, Math.min(normalized, max));
|
|
2736
2778
|
}
|
|
2737
|
-
function
|
|
2738
|
-
if (!
|
|
2739
|
-
|
|
2779
|
+
function assertToolResult(value) {
|
|
2780
|
+
if (!isObject(value) || !Array.isArray(value.content)) {
|
|
2781
|
+
throw new Error("Invalid delegated custom tool result");
|
|
2740
2782
|
}
|
|
2741
|
-
return Math.max(0, Math.floor(offset));
|
|
2742
2783
|
}
|
|
2743
|
-
var
|
|
2744
|
-
constructor(
|
|
2745
|
-
this.
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2784
|
+
var DelegatedCustomToolClient = class {
|
|
2785
|
+
constructor(transport = process, options = {}) {
|
|
2786
|
+
this.transport = transport;
|
|
2787
|
+
this.timeoutMs = options.timeoutMs ?? 30 * 60 * 1e3;
|
|
2788
|
+
this.transport.on("message", this.handleMessage);
|
|
2789
|
+
}
|
|
2790
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
2791
|
+
timeoutMs;
|
|
2792
|
+
disposed = false;
|
|
2793
|
+
isAvailable() {
|
|
2794
|
+
return typeof this.transport.send === "function" && this.transport.connected !== false;
|
|
2795
|
+
}
|
|
2796
|
+
async listDefinitions() {
|
|
2797
|
+
if (!this.isAvailable()) {
|
|
2798
|
+
return [];
|
|
2799
|
+
}
|
|
2800
|
+
const data = await this.sendRequest("get_custom_tool_definitions");
|
|
2801
|
+
assertToolDefinitions(data);
|
|
2802
|
+
return data;
|
|
2803
|
+
}
|
|
2804
|
+
async executeTool(input) {
|
|
2805
|
+
const data = await this.sendRequest("execute_custom_tool", input);
|
|
2806
|
+
assertToolResult(data);
|
|
2807
|
+
return data;
|
|
2808
|
+
}
|
|
2809
|
+
dispose() {
|
|
2810
|
+
if (this.disposed) {
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
this.disposed = true;
|
|
2814
|
+
this.transport.off("message", this.handleMessage);
|
|
2815
|
+
this.rejectAllPending(new Error("Delegated custom tool client disposed"));
|
|
2816
|
+
}
|
|
2817
|
+
sendRequest(type, payload) {
|
|
2818
|
+
if (!this.isAvailable()) {
|
|
2819
|
+
throw new Error("Delegated custom tool IPC channel is not available");
|
|
2820
|
+
}
|
|
2821
|
+
const id = randomUUID();
|
|
2822
|
+
const request = {
|
|
2823
|
+
id,
|
|
2824
|
+
type,
|
|
2825
|
+
...payload || {}
|
|
2826
|
+
};
|
|
2827
|
+
return new Promise((resolve, reject) => {
|
|
2828
|
+
const timer = setTimeout(() => {
|
|
2829
|
+
this.pendingRequests.delete(id);
|
|
2830
|
+
reject(new Error(`Delegated custom tool request timed out: ${type}`));
|
|
2831
|
+
}, this.timeoutMs);
|
|
2832
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
2833
|
+
try {
|
|
2834
|
+
this.transport.send(request);
|
|
2835
|
+
} catch (error) {
|
|
2836
|
+
clearTimeout(timer);
|
|
2837
|
+
this.pendingRequests.delete(id);
|
|
2838
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2839
|
+
}
|
|
2752
2840
|
});
|
|
2753
2841
|
}
|
|
2842
|
+
handleMessage = (message) => {
|
|
2843
|
+
if (!isObject(message)) {
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
const payload = message;
|
|
2847
|
+
if (typeof payload.id !== "string" || payload.type !== "result" && payload.type !== "error") {
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
const pending = this.pendingRequests.get(payload.id);
|
|
2851
|
+
if (!pending) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
clearTimeout(pending.timer);
|
|
2855
|
+
this.pendingRequests.delete(payload.id);
|
|
2856
|
+
if (payload.type === "error") {
|
|
2857
|
+
pending.reject(new Error(payload.message));
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
pending.resolve(payload.data);
|
|
2861
|
+
};
|
|
2862
|
+
rejectAllPending(error) {
|
|
2863
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
2864
|
+
clearTimeout(pending.timer);
|
|
2865
|
+
pending.reject(error);
|
|
2866
|
+
this.pendingRequests.delete(id);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2754
2869
|
};
|
|
2755
2870
|
|
|
2756
|
-
// src/runtime/
|
|
2757
|
-
|
|
2758
|
-
|
|
2871
|
+
// src/runtime/custom-tools/delegated-custom-tool-factory.ts
|
|
2872
|
+
function toAgentToolResult(result) {
|
|
2873
|
+
return {
|
|
2874
|
+
content: result.content,
|
|
2875
|
+
details: result.details
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
function createDelegatedCustomTools(definitions, client, runContextRef) {
|
|
2879
|
+
return definitions.map((definition) => ({
|
|
2880
|
+
name: definition.name,
|
|
2881
|
+
label: definition.label,
|
|
2882
|
+
description: definition.description,
|
|
2883
|
+
promptSnippet: definition.promptSnippet,
|
|
2884
|
+
promptGuidelines: definition.promptGuidelines,
|
|
2885
|
+
parameters: definition.parameters,
|
|
2886
|
+
async execute(toolCallId, params) {
|
|
2887
|
+
const runContext = runContextRef.current;
|
|
2888
|
+
if (!runContext) {
|
|
2889
|
+
throw new Error(
|
|
2890
|
+
`Delegated custom tool ${definition.name} is not available outside an active run.`
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
return toAgentToolResult(
|
|
2894
|
+
await client.executeTool({
|
|
2895
|
+
toolName: definition.name,
|
|
2896
|
+
toolCallId,
|
|
2897
|
+
runContext,
|
|
2898
|
+
params
|
|
2899
|
+
})
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
}));
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
// src/runtime/host-ipc/host-ipc-client.ts
|
|
2906
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2907
|
+
function isObject2(value) {
|
|
2908
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2909
|
+
}
|
|
2910
|
+
var HostIpcClient = class {
|
|
2911
|
+
constructor(transport = process, options = {}) {
|
|
2912
|
+
this.transport = transport;
|
|
2913
|
+
this.timeoutMs = options.timeoutMs ?? 30 * 60 * 1e3;
|
|
2914
|
+
this.transport.on("message", this.handleMessage);
|
|
2915
|
+
}
|
|
2916
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
2917
|
+
timeoutMs;
|
|
2918
|
+
disposed = false;
|
|
2919
|
+
isAvailable() {
|
|
2920
|
+
return typeof this.transport.send === "function" && this.transport.connected !== false;
|
|
2921
|
+
}
|
|
2922
|
+
async notifyChannelSessionCleared(input) {
|
|
2923
|
+
if (!this.isAvailable()) {
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
await this.sendRequest("channel_session_cleared", input);
|
|
2927
|
+
}
|
|
2928
|
+
dispose() {
|
|
2929
|
+
if (this.disposed) {
|
|
2930
|
+
return;
|
|
2931
|
+
}
|
|
2932
|
+
this.disposed = true;
|
|
2933
|
+
this.transport.off("message", this.handleMessage);
|
|
2934
|
+
this.rejectAllPending(new Error("Host IPC client disposed"));
|
|
2935
|
+
}
|
|
2936
|
+
sendRequest(type, payload) {
|
|
2937
|
+
if (!this.isAvailable()) {
|
|
2938
|
+
throw new Error("Host IPC channel is not available");
|
|
2939
|
+
}
|
|
2940
|
+
const id = randomUUID2();
|
|
2941
|
+
const request = {
|
|
2942
|
+
id,
|
|
2943
|
+
type,
|
|
2944
|
+
...payload || {}
|
|
2945
|
+
};
|
|
2946
|
+
return new Promise((resolve, reject) => {
|
|
2947
|
+
const timer = setTimeout(() => {
|
|
2948
|
+
this.pendingRequests.delete(id);
|
|
2949
|
+
reject(new Error(`Host IPC request timed out: ${type}`));
|
|
2950
|
+
}, this.timeoutMs);
|
|
2951
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
2952
|
+
try {
|
|
2953
|
+
this.transport.send(request);
|
|
2954
|
+
} catch (error) {
|
|
2955
|
+
clearTimeout(timer);
|
|
2956
|
+
this.pendingRequests.delete(id);
|
|
2957
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
handleMessage = (message) => {
|
|
2962
|
+
if (!isObject2(message)) {
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
const payload = message;
|
|
2966
|
+
if (typeof payload.id !== "string" || payload.type !== "result" && payload.type !== "error") {
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
const pending = this.pendingRequests.get(payload.id);
|
|
2970
|
+
if (!pending) {
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
clearTimeout(pending.timer);
|
|
2974
|
+
this.pendingRequests.delete(payload.id);
|
|
2975
|
+
if (payload.type === "error") {
|
|
2976
|
+
pending.reject(new Error(payload.message));
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
pending.resolve(payload.data);
|
|
2980
|
+
};
|
|
2981
|
+
rejectAllPending(error) {
|
|
2982
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
2983
|
+
clearTimeout(pending.timer);
|
|
2984
|
+
pending.reject(error);
|
|
2985
|
+
this.pendingRequests.delete(id);
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
};
|
|
2989
|
+
|
|
2990
|
+
// src/runtime/tools/send-file-tool.ts
|
|
2991
|
+
import fs8 from "fs";
|
|
2759
2992
|
import path9 from "path";
|
|
2993
|
+
import { Type } from "@sinclair/typebox";
|
|
2760
2994
|
|
|
2761
2995
|
// src/runtime/files/metadata.ts
|
|
2762
|
-
import fs8 from "fs";
|
|
2763
2996
|
import path8 from "path";
|
|
2764
2997
|
var MIME_BY_EXT = {
|
|
2765
2998
|
".png": "image/png",
|
|
@@ -2789,419 +3022,14 @@ function isWithinDirectory(parentDir, targetPath) {
|
|
|
2789
3022
|
const relativePath = path8.relative(path8.resolve(parentDir), path8.resolve(targetPath));
|
|
2790
3023
|
return relativePath !== ".." && !relativePath.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relativePath);
|
|
2791
3024
|
}
|
|
2792
|
-
function toPackRelativePath(rootDir, filePath) {
|
|
2793
|
-
const resolvedRoot = path8.resolve(rootDir);
|
|
2794
|
-
const resolvedFile = path8.resolve(filePath);
|
|
2795
|
-
if (!isWithinDirectory(resolvedRoot, resolvedFile)) {
|
|
2796
|
-
throw new Error(`Path is outside the pack root: ${resolvedFile}`);
|
|
2797
|
-
}
|
|
2798
|
-
return path8.relative(resolvedRoot, resolvedFile).split(path8.sep).join("/");
|
|
2799
|
-
}
|
|
2800
|
-
function resolvePackFile(rootDir, filePath) {
|
|
2801
|
-
if (!path8.isAbsolute(filePath)) {
|
|
2802
|
-
throw new Error(`filePath must be absolute: ${filePath}`);
|
|
2803
|
-
}
|
|
2804
|
-
const resolvedPath = path8.resolve(filePath);
|
|
2805
|
-
if (!isWithinDirectory(rootDir, resolvedPath)) {
|
|
2806
|
-
throw new Error(`File is outside the pack root: ${resolvedPath}`);
|
|
2807
|
-
}
|
|
2808
|
-
if (!fs8.existsSync(resolvedPath)) {
|
|
2809
|
-
throw new Error(`File not found: ${resolvedPath}`);
|
|
2810
|
-
}
|
|
2811
|
-
const stats = fs8.statSync(resolvedPath);
|
|
2812
|
-
if (!stats.isFile()) {
|
|
2813
|
-
throw new Error(`Path is not a file: ${resolvedPath}`);
|
|
2814
|
-
}
|
|
2815
|
-
fs8.accessSync(resolvedPath, fs8.constants.R_OK);
|
|
2816
|
-
return {
|
|
2817
|
-
resolvedPath,
|
|
2818
|
-
fileName: path8.basename(resolvedPath),
|
|
2819
|
-
mimeType: detectMimeType(resolvedPath),
|
|
2820
|
-
sizeBytes: stats.size
|
|
2821
|
-
};
|
|
2822
|
-
}
|
|
2823
|
-
|
|
2824
|
-
// src/runtime/artifacts/snapshot-service.ts
|
|
2825
|
-
function sanitizeFileName(fileName) {
|
|
2826
|
-
const sanitized = fileName.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
2827
|
-
return sanitized || "artifact";
|
|
2828
|
-
}
|
|
2829
|
-
function formatSnapshotStamp(isoDate) {
|
|
2830
|
-
const normalized = isoDate.replace(/\D+/g, "").slice(0, 14);
|
|
2831
|
-
return normalized || String(Date.now());
|
|
2832
|
-
}
|
|
2833
|
-
var ArtifactSnapshotService = class {
|
|
2834
|
-
constructor(rootDir) {
|
|
2835
|
-
this.rootDir = rootDir;
|
|
2836
|
-
}
|
|
2837
|
-
createSnapshots(runId, artifacts, declaredAt) {
|
|
2838
|
-
if (artifacts.length === 0) {
|
|
2839
|
-
return [];
|
|
2840
|
-
}
|
|
2841
|
-
const artifactsRoot = path9.resolve(this.rootDir, "data", "artifacts");
|
|
2842
|
-
const runDir = path9.join(artifactsRoot, runId);
|
|
2843
|
-
const tempDir = path9.join(
|
|
2844
|
-
artifactsRoot,
|
|
2845
|
-
`.tmp-${runId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
2846
|
-
);
|
|
2847
|
-
const snapshots = [];
|
|
2848
|
-
const movedPaths = [];
|
|
2849
|
-
const snapshotNames = artifacts.map((artifact) => [
|
|
2850
|
-
formatSnapshotStamp(declaredAt),
|
|
2851
|
-
randomUUID(),
|
|
2852
|
-
sanitizeFileName(artifact.fileName)
|
|
2853
|
-
].join("-"));
|
|
2854
|
-
fs9.mkdirSync(artifactsRoot, { recursive: true });
|
|
2855
|
-
fs9.mkdirSync(tempDir, { recursive: true });
|
|
2856
|
-
try {
|
|
2857
|
-
snapshotNames.forEach((snapshotName, index) => {
|
|
2858
|
-
fs9.copyFileSync(
|
|
2859
|
-
artifacts[index].filePath,
|
|
2860
|
-
path9.join(tempDir, snapshotName)
|
|
2861
|
-
);
|
|
2862
|
-
});
|
|
2863
|
-
fs9.mkdirSync(runDir, { recursive: true });
|
|
2864
|
-
snapshotNames.forEach((snapshotName, index) => {
|
|
2865
|
-
const artifact = artifacts[index];
|
|
2866
|
-
const tempSnapshotPath = path9.join(tempDir, snapshotName);
|
|
2867
|
-
const finalSnapshotPath = path9.join(runDir, snapshotName);
|
|
2868
|
-
fs9.renameSync(tempSnapshotPath, finalSnapshotPath);
|
|
2869
|
-
movedPaths.push(finalSnapshotPath);
|
|
2870
|
-
snapshots.push({
|
|
2871
|
-
declaredAt,
|
|
2872
|
-
originalPath: toPackRelativePath(this.rootDir, artifact.filePath),
|
|
2873
|
-
snapshotPath: path9.join("data", "artifacts", runId, snapshotName).split(path9.sep).join("/"),
|
|
2874
|
-
fileName: artifact.fileName,
|
|
2875
|
-
mimeType: artifact.mimeType,
|
|
2876
|
-
sizeBytes: artifact.sizeBytes,
|
|
2877
|
-
title: artifact.title,
|
|
2878
|
-
isPrimary: artifact.isPrimary
|
|
2879
|
-
});
|
|
2880
|
-
});
|
|
2881
|
-
fs9.rmSync(tempDir, { recursive: true, force: true });
|
|
2882
|
-
return snapshots;
|
|
2883
|
-
} catch (error) {
|
|
2884
|
-
fs9.rmSync(tempDir, { recursive: true, force: true });
|
|
2885
|
-
movedPaths.forEach((filePath) => fs9.rmSync(filePath, { force: true }));
|
|
2886
|
-
this.removeEmptyRunDirectory(runDir);
|
|
2887
|
-
throw error;
|
|
2888
|
-
}
|
|
2889
|
-
}
|
|
2890
|
-
removeSnapshots(snapshotPaths) {
|
|
2891
|
-
const visitedRunDirs = /* @__PURE__ */ new Set();
|
|
2892
|
-
for (const snapshotPath of snapshotPaths) {
|
|
2893
|
-
const resolvedPath = path9.resolve(this.rootDir, snapshotPath);
|
|
2894
|
-
fs9.rmSync(resolvedPath, { force: true });
|
|
2895
|
-
visitedRunDirs.add(path9.dirname(resolvedPath));
|
|
2896
|
-
}
|
|
2897
|
-
visitedRunDirs.forEach((runDir) => this.removeEmptyRunDirectory(runDir));
|
|
2898
|
-
}
|
|
2899
|
-
removeEmptyRunDirectory(runDir) {
|
|
2900
|
-
try {
|
|
2901
|
-
if (!fs9.existsSync(runDir)) {
|
|
2902
|
-
return;
|
|
2903
|
-
}
|
|
2904
|
-
if (fs9.readdirSync(runDir).length === 0) {
|
|
2905
|
-
fs9.rmdirSync(runDir);
|
|
2906
|
-
}
|
|
2907
|
-
} catch {
|
|
2908
|
-
}
|
|
2909
|
-
}
|
|
2910
|
-
};
|
|
2911
|
-
|
|
2912
|
-
// src/runtime/artifacts/persistence-service.ts
|
|
2913
|
-
var ArtifactPersistenceService = class {
|
|
2914
|
-
constructor(snapshotService, resultStore) {
|
|
2915
|
-
this.snapshotService = snapshotService;
|
|
2916
|
-
this.resultStore = resultStore;
|
|
2917
|
-
}
|
|
2918
|
-
async saveArtifacts(input) {
|
|
2919
|
-
const declaredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2920
|
-
const snapshots = this.snapshotService.createSnapshots(
|
|
2921
|
-
input.runId,
|
|
2922
|
-
input.artifacts,
|
|
2923
|
-
declaredAt
|
|
2924
|
-
);
|
|
2925
|
-
try {
|
|
2926
|
-
await this.resultStore.insertArtifacts({
|
|
2927
|
-
runId: input.runId,
|
|
2928
|
-
channelId: input.channelId,
|
|
2929
|
-
artifacts: snapshots
|
|
2930
|
-
});
|
|
2931
|
-
return snapshots.length;
|
|
2932
|
-
} catch (error) {
|
|
2933
|
-
this.snapshotService.removeSnapshots(
|
|
2934
|
-
snapshots.map((artifact) => artifact.snapshotPath)
|
|
2935
|
-
);
|
|
2936
|
-
throw error;
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
};
|
|
2940
|
-
|
|
2941
|
-
// src/runtime/artifacts/store.ts
|
|
2942
|
-
import fs10 from "fs";
|
|
2943
|
-
import path10 from "path";
|
|
2944
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
2945
|
-
import sqlite3 from "sqlite3";
|
|
2946
|
-
function mapArtifactRow(row) {
|
|
2947
|
-
return {
|
|
2948
|
-
artifactId: row.artifact_id,
|
|
2949
|
-
runId: row.run_id,
|
|
2950
|
-
channelId: row.channel_id,
|
|
2951
|
-
originalPath: row.original_path,
|
|
2952
|
-
snapshotPath: row.snapshot_path,
|
|
2953
|
-
fileName: row.file_name,
|
|
2954
|
-
mimeType: row.mime_type,
|
|
2955
|
-
sizeBytes: row.size_bytes,
|
|
2956
|
-
title: row.title,
|
|
2957
|
-
isPrimary: row.is_primary === 1,
|
|
2958
|
-
declaredAt: row.declared_at
|
|
2959
|
-
};
|
|
2960
|
-
}
|
|
2961
|
-
var ResultStore = class {
|
|
2962
|
-
db = null;
|
|
2963
|
-
ready;
|
|
2964
|
-
constructor(rootDir) {
|
|
2965
|
-
const dataDir = path10.resolve(rootDir, "data");
|
|
2966
|
-
fs10.mkdirSync(dataDir, { recursive: true });
|
|
2967
|
-
this.ready = this.initialize(path10.join(dataDir, "result-v2.db"));
|
|
2968
|
-
}
|
|
2969
|
-
async initialize(databasePath) {
|
|
2970
|
-
this.db = await openDatabase(databasePath);
|
|
2971
|
-
await this.exec("PRAGMA journal_mode = WAL");
|
|
2972
|
-
await this.exec(`
|
|
2973
|
-
CREATE TABLE IF NOT EXISTS artifacts (
|
|
2974
|
-
artifact_id TEXT PRIMARY KEY,
|
|
2975
|
-
run_id TEXT NOT NULL,
|
|
2976
|
-
channel_id TEXT NOT NULL,
|
|
2977
|
-
original_path TEXT NOT NULL,
|
|
2978
|
-
snapshot_path TEXT NOT NULL,
|
|
2979
|
-
file_name TEXT NOT NULL,
|
|
2980
|
-
mime_type TEXT,
|
|
2981
|
-
size_bytes INTEGER NOT NULL,
|
|
2982
|
-
title TEXT,
|
|
2983
|
-
is_primary INTEGER NOT NULL DEFAULT 0,
|
|
2984
|
-
declared_at TEXT NOT NULL
|
|
2985
|
-
);
|
|
2986
|
-
|
|
2987
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_channel_declared_at
|
|
2988
|
-
ON artifacts(channel_id, declared_at DESC);
|
|
2989
|
-
`);
|
|
2990
|
-
}
|
|
2991
|
-
async insertArtifacts(input) {
|
|
2992
|
-
await this.ready;
|
|
2993
|
-
if (input.artifacts.length === 0) {
|
|
2994
|
-
return;
|
|
2995
|
-
}
|
|
2996
|
-
const insertArtifact = `
|
|
2997
|
-
INSERT INTO artifacts (
|
|
2998
|
-
artifact_id,
|
|
2999
|
-
run_id,
|
|
3000
|
-
channel_id,
|
|
3001
|
-
original_path,
|
|
3002
|
-
snapshot_path,
|
|
3003
|
-
file_name,
|
|
3004
|
-
mime_type,
|
|
3005
|
-
size_bytes,
|
|
3006
|
-
title,
|
|
3007
|
-
is_primary,
|
|
3008
|
-
declared_at
|
|
3009
|
-
) VALUES (
|
|
3010
|
-
?,
|
|
3011
|
-
?,
|
|
3012
|
-
?,
|
|
3013
|
-
?,
|
|
3014
|
-
?,
|
|
3015
|
-
?,
|
|
3016
|
-
?,
|
|
3017
|
-
?,
|
|
3018
|
-
?,
|
|
3019
|
-
?,
|
|
3020
|
-
?
|
|
3021
|
-
)
|
|
3022
|
-
`;
|
|
3023
|
-
await this.exec("BEGIN");
|
|
3024
|
-
try {
|
|
3025
|
-
for (const artifact of input.artifacts) {
|
|
3026
|
-
await this.run(insertArtifact, [
|
|
3027
|
-
randomUUID2(),
|
|
3028
|
-
input.runId,
|
|
3029
|
-
input.channelId,
|
|
3030
|
-
artifact.originalPath,
|
|
3031
|
-
artifact.snapshotPath,
|
|
3032
|
-
artifact.fileName,
|
|
3033
|
-
artifact.mimeType ?? null,
|
|
3034
|
-
artifact.sizeBytes,
|
|
3035
|
-
artifact.title ?? null,
|
|
3036
|
-
artifact.isPrimary ? 1 : 0,
|
|
3037
|
-
artifact.declaredAt
|
|
3038
|
-
]);
|
|
3039
|
-
}
|
|
3040
|
-
await this.exec("COMMIT");
|
|
3041
|
-
} catch (error) {
|
|
3042
|
-
await this.rollback();
|
|
3043
|
-
throw error;
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
async listRecentArtifacts(options = {}) {
|
|
3047
|
-
await this.ready;
|
|
3048
|
-
const limit = options.limit ?? 100;
|
|
3049
|
-
const offset = options.offset ?? 0;
|
|
3050
|
-
const conditions = [];
|
|
3051
|
-
const params = [];
|
|
3052
|
-
if (options.channelId) {
|
|
3053
|
-
conditions.push("channel_id = ?");
|
|
3054
|
-
params.push(options.channelId);
|
|
3055
|
-
}
|
|
3056
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3057
|
-
const rows = await this.all(`
|
|
3058
|
-
SELECT *
|
|
3059
|
-
FROM artifacts
|
|
3060
|
-
${whereClause}
|
|
3061
|
-
ORDER BY declared_at DESC, rowid DESC
|
|
3062
|
-
LIMIT ? OFFSET ?
|
|
3063
|
-
`, [...params, limit, offset]);
|
|
3064
|
-
return rows.map(mapArtifactRow);
|
|
3065
|
-
}
|
|
3066
|
-
getDatabase() {
|
|
3067
|
-
if (!this.db) {
|
|
3068
|
-
throw new Error("Result store database is not ready");
|
|
3069
|
-
}
|
|
3070
|
-
return this.db;
|
|
3071
|
-
}
|
|
3072
|
-
exec(sql) {
|
|
3073
|
-
const db = this.getDatabase();
|
|
3074
|
-
return new Promise((resolve, reject) => {
|
|
3075
|
-
db.exec(sql, (error) => {
|
|
3076
|
-
if (error) {
|
|
3077
|
-
reject(error);
|
|
3078
|
-
return;
|
|
3079
|
-
}
|
|
3080
|
-
resolve();
|
|
3081
|
-
});
|
|
3082
|
-
});
|
|
3083
|
-
}
|
|
3084
|
-
run(sql, params = []) {
|
|
3085
|
-
const db = this.getDatabase();
|
|
3086
|
-
return new Promise((resolve, reject) => {
|
|
3087
|
-
db.run(sql, params, (error) => {
|
|
3088
|
-
if (error) {
|
|
3089
|
-
reject(error);
|
|
3090
|
-
return;
|
|
3091
|
-
}
|
|
3092
|
-
resolve();
|
|
3093
|
-
});
|
|
3094
|
-
});
|
|
3095
|
-
}
|
|
3096
|
-
all(sql, params = []) {
|
|
3097
|
-
const db = this.getDatabase();
|
|
3098
|
-
return new Promise((resolve, reject) => {
|
|
3099
|
-
db.all(sql, params, (error, rows) => {
|
|
3100
|
-
if (error) {
|
|
3101
|
-
reject(error);
|
|
3102
|
-
return;
|
|
3103
|
-
}
|
|
3104
|
-
resolve(rows);
|
|
3105
|
-
});
|
|
3106
|
-
});
|
|
3107
|
-
}
|
|
3108
|
-
async rollback() {
|
|
3109
|
-
try {
|
|
3110
|
-
await this.exec("ROLLBACK");
|
|
3111
|
-
} catch {
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
};
|
|
3115
|
-
function openDatabase(databasePath) {
|
|
3116
|
-
return new Promise((resolve, reject) => {
|
|
3117
|
-
const db = new sqlite3.Database(databasePath, (error) => {
|
|
3118
|
-
if (error) {
|
|
3119
|
-
reject(error);
|
|
3120
|
-
return;
|
|
3121
|
-
}
|
|
3122
|
-
resolve(db);
|
|
3123
|
-
});
|
|
3124
|
-
});
|
|
3125
|
-
}
|
|
3126
|
-
|
|
3127
|
-
// src/runtime/artifacts/save-artifacts-tool.ts
|
|
3128
|
-
import { Type } from "@sinclair/typebox";
|
|
3129
|
-
var ArtifactItem = Type.Object({
|
|
3130
|
-
filePath: Type.String({
|
|
3131
|
-
description: "Absolute path to the artifact file. The file must exist, be readable, and be inside the current pack root."
|
|
3132
|
-
}),
|
|
3133
|
-
title: Type.Optional(
|
|
3134
|
-
Type.String({
|
|
3135
|
-
description: "Optional short title shown in the dashboard."
|
|
3136
|
-
})
|
|
3137
|
-
),
|
|
3138
|
-
isPrimary: Type.Optional(
|
|
3139
|
-
Type.Boolean({
|
|
3140
|
-
description: "Mark this artifact as a primary output."
|
|
3141
|
-
})
|
|
3142
|
-
)
|
|
3143
|
-
});
|
|
3144
|
-
var SaveArtifactsParams = Type.Object({
|
|
3145
|
-
artifacts: Type.Array(ArtifactItem, {
|
|
3146
|
-
minItems: 1,
|
|
3147
|
-
description: "The artifact files to save for this run."
|
|
3148
|
-
})
|
|
3149
|
-
});
|
|
3150
|
-
function textResult(text) {
|
|
3151
|
-
return { content: [{ type: "text", text }], details: void 0 };
|
|
3152
|
-
}
|
|
3153
|
-
function normalizeOptionalText(value) {
|
|
3154
|
-
const trimmed = value?.trim();
|
|
3155
|
-
return trimmed ? trimmed : void 0;
|
|
3156
|
-
}
|
|
3157
|
-
function createSaveArtifactsTool(rootDir, saveCallbackRef) {
|
|
3158
|
-
return {
|
|
3159
|
-
name: "save_artifacts",
|
|
3160
|
-
label: "Save Artifacts",
|
|
3161
|
-
description: [
|
|
3162
|
-
"Save the final output files produced by this run.",
|
|
3163
|
-
"Always use this for user-facing deliverables that are part of the final result.",
|
|
3164
|
-
"Do not use this for intermediate, temporary, draft, or scratch files.",
|
|
3165
|
-
"Each filePath must be an absolute path inside the current pack root."
|
|
3166
|
-
].join("\n"),
|
|
3167
|
-
promptSnippet: "save_artifacts: Save final result files for this task. Always call this for user-facing final deliverables, and never for intermediate files. Use absolute paths inside the current pack root.",
|
|
3168
|
-
promptGuidelines: [
|
|
3169
|
-
"Whenever you create a final result file for the user, call `save_artifacts` before finishing the response.",
|
|
3170
|
-
"Do not call `save_artifacts` for intermediate, temporary, draft, or scratch files."
|
|
3171
|
-
],
|
|
3172
|
-
parameters: SaveArtifactsParams,
|
|
3173
|
-
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3174
|
-
const saveArtifacts = saveCallbackRef.current;
|
|
3175
|
-
if (!saveArtifacts) {
|
|
3176
|
-
throw new Error("Artifact saving is not available for this run.");
|
|
3177
|
-
}
|
|
3178
|
-
const artifacts = params.artifacts.map((artifact) => {
|
|
3179
|
-
const metadata = resolvePackFile(rootDir, artifact.filePath);
|
|
3180
|
-
return {
|
|
3181
|
-
filePath: metadata.resolvedPath,
|
|
3182
|
-
fileName: metadata.fileName,
|
|
3183
|
-
mimeType: metadata.mimeType,
|
|
3184
|
-
sizeBytes: metadata.sizeBytes,
|
|
3185
|
-
title: normalizeOptionalText(artifact.title),
|
|
3186
|
-
isPrimary: artifact.isPrimary === true
|
|
3187
|
-
};
|
|
3188
|
-
});
|
|
3189
|
-
const savedCount = await saveArtifacts(artifacts);
|
|
3190
|
-
return textResult(`Saved ${savedCount} artifact(s).`);
|
|
3191
|
-
}
|
|
3192
|
-
};
|
|
3193
|
-
}
|
|
3194
3025
|
|
|
3195
3026
|
// src/runtime/tools/send-file-tool.ts
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
import { Type as Type2 } from "@sinclair/typebox";
|
|
3199
|
-
var SendFileParams = Type2.Object({
|
|
3200
|
-
filePath: Type2.String({
|
|
3027
|
+
var SendFileParams = Type.Object({
|
|
3028
|
+
filePath: Type.String({
|
|
3201
3029
|
description: "Absolute path to the file to send to the user. The file must exist and be readable."
|
|
3202
3030
|
}),
|
|
3203
|
-
caption:
|
|
3204
|
-
|
|
3031
|
+
caption: Type.Optional(
|
|
3032
|
+
Type.String({
|
|
3205
3033
|
description: "Optional caption or description to accompany the file."
|
|
3206
3034
|
})
|
|
3207
3035
|
)
|
|
@@ -3215,13 +3043,13 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3215
3043
|
parameters: SendFileParams,
|
|
3216
3044
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3217
3045
|
const { filePath, caption } = params;
|
|
3218
|
-
if (!
|
|
3046
|
+
if (!fs8.existsSync(filePath)) {
|
|
3219
3047
|
return {
|
|
3220
3048
|
content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
|
|
3221
3049
|
details: void 0
|
|
3222
3050
|
};
|
|
3223
3051
|
}
|
|
3224
|
-
const stats =
|
|
3052
|
+
const stats = fs8.statSync(filePath);
|
|
3225
3053
|
if (!stats.isFile()) {
|
|
3226
3054
|
return {
|
|
3227
3055
|
content: [
|
|
@@ -3230,7 +3058,7 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3230
3058
|
details: void 0
|
|
3231
3059
|
};
|
|
3232
3060
|
}
|
|
3233
|
-
const filename =
|
|
3061
|
+
const filename = path9.basename(filePath);
|
|
3234
3062
|
const mimeType = detectMimeType(filePath);
|
|
3235
3063
|
const callback = fileOutputCallbackRef.current;
|
|
3236
3064
|
if (callback) {
|
|
@@ -3257,51 +3085,51 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3257
3085
|
}
|
|
3258
3086
|
|
|
3259
3087
|
// src/runtime/tools/manage-schedule-tool.ts
|
|
3260
|
-
import { Type as
|
|
3261
|
-
var ManageScheduleParams =
|
|
3262
|
-
action:
|
|
3088
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
3089
|
+
var ManageScheduleParams = Type2.Object({
|
|
3090
|
+
action: Type2.Union(
|
|
3263
3091
|
[
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3092
|
+
Type2.Literal("add"),
|
|
3093
|
+
Type2.Literal("list"),
|
|
3094
|
+
Type2.Literal("remove"),
|
|
3095
|
+
Type2.Literal("trigger"),
|
|
3096
|
+
Type2.Literal("enable"),
|
|
3097
|
+
Type2.Literal("disable")
|
|
3270
3098
|
],
|
|
3271
3099
|
{ description: "The action to perform." }
|
|
3272
3100
|
),
|
|
3273
|
-
name:
|
|
3274
|
-
|
|
3101
|
+
name: Type2.Optional(
|
|
3102
|
+
Type2.String({
|
|
3275
3103
|
description: "Unique name for the scheduled task. Required for add/remove/trigger/enable/disable."
|
|
3276
3104
|
})
|
|
3277
3105
|
),
|
|
3278
|
-
cron:
|
|
3279
|
-
|
|
3106
|
+
cron: Type2.Optional(
|
|
3107
|
+
Type2.String({
|
|
3280
3108
|
description: "Cron expression (5 fields: minute hour day month weekday). Required for add."
|
|
3281
3109
|
})
|
|
3282
3110
|
),
|
|
3283
|
-
prompt:
|
|
3284
|
-
|
|
3111
|
+
prompt: Type2.Optional(
|
|
3112
|
+
Type2.String({
|
|
3285
3113
|
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."
|
|
3286
3114
|
})
|
|
3287
3115
|
),
|
|
3288
|
-
timezone:
|
|
3289
|
-
|
|
3116
|
+
timezone: Type2.Optional(
|
|
3117
|
+
Type2.String({
|
|
3290
3118
|
description: "Optional timezone for the cron schedule, e.g. 'Asia/Shanghai', 'America/New_York'."
|
|
3291
3119
|
})
|
|
3292
3120
|
),
|
|
3293
|
-
notifyAdapter:
|
|
3294
|
-
|
|
3121
|
+
notifyAdapter: Type2.Optional(
|
|
3122
|
+
Type2.String({
|
|
3295
3123
|
description: "Optional target adapter for notifications. If omitted, the current chat is used when supported (Telegram, Slack, or Web)."
|
|
3296
3124
|
})
|
|
3297
3125
|
),
|
|
3298
|
-
notifyChannelId:
|
|
3299
|
-
|
|
3126
|
+
notifyChannelId: Type2.Optional(
|
|
3127
|
+
Type2.String({
|
|
3300
3128
|
description: "Optional target channelId for notifications. Must be provided together with notifyAdapter when overriding the default target."
|
|
3301
3129
|
})
|
|
3302
3130
|
)
|
|
3303
3131
|
});
|
|
3304
|
-
function
|
|
3132
|
+
function textResult(text) {
|
|
3305
3133
|
return { content: [{ type: "text", text }], details: void 0 };
|
|
3306
3134
|
}
|
|
3307
3135
|
function getDefaultNotifyTarget(adapter, channelId) {
|
|
@@ -3341,7 +3169,7 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
|
|
|
3341
3169
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3342
3170
|
const scheduler = schedulerRef.current;
|
|
3343
3171
|
if (!scheduler) {
|
|
3344
|
-
return
|
|
3172
|
+
return textResult(
|
|
3345
3173
|
"Error: Scheduler is not available. The scheduled task system may not be initialized."
|
|
3346
3174
|
);
|
|
3347
3175
|
}
|
|
@@ -3349,24 +3177,24 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
|
|
|
3349
3177
|
case "list": {
|
|
3350
3178
|
const jobs = scheduler.listJobs();
|
|
3351
3179
|
if (jobs.length === 0) {
|
|
3352
|
-
return
|
|
3180
|
+
return textResult("No scheduled tasks configured.");
|
|
3353
3181
|
}
|
|
3354
3182
|
const lines = jobs.map(
|
|
3355
3183
|
(j) => `- **${j.name}**: \`${j.cron}\` \u2192 ${j.notify.adapter}:${j.notify.channelId} [${j.enabled ? "enabled" : "disabled"}]${j.running ? " (running)" : ""}${j.lastRunAt ? ` (last: ${j.lastRunAt})` : ""}`
|
|
3356
3184
|
);
|
|
3357
|
-
return
|
|
3185
|
+
return textResult(
|
|
3358
3186
|
`Scheduled tasks (${jobs.length}):
|
|
3359
3187
|
${lines.join("\n")}`
|
|
3360
3188
|
);
|
|
3361
3189
|
}
|
|
3362
3190
|
case "add": {
|
|
3363
3191
|
if (!params.name || !params.cron || !params.prompt) {
|
|
3364
|
-
return
|
|
3192
|
+
return textResult(
|
|
3365
3193
|
"Error: 'name', 'cron', and 'prompt' are required for adding a task."
|
|
3366
3194
|
);
|
|
3367
3195
|
}
|
|
3368
3196
|
if (params.notifyAdapter && !params.notifyChannelId || !params.notifyAdapter && params.notifyChannelId) {
|
|
3369
|
-
return
|
|
3197
|
+
return textResult(
|
|
3370
3198
|
"Error: 'notifyAdapter' and 'notifyChannelId' must be provided together when overriding the notification target."
|
|
3371
3199
|
);
|
|
3372
3200
|
}
|
|
@@ -3375,7 +3203,7 @@ ${lines.join("\n")}`
|
|
|
3375
3203
|
channelId: params.notifyChannelId
|
|
3376
3204
|
} : getDefaultNotifyTarget(adapter, channelId);
|
|
3377
3205
|
if (!notify) {
|
|
3378
|
-
return
|
|
3206
|
+
return textResult(
|
|
3379
3207
|
"Error: No default notification target is available for this chat. Provide 'notifyAdapter' and 'notifyChannelId'."
|
|
3380
3208
|
);
|
|
3381
3209
|
}
|
|
@@ -3388,46 +3216,46 @@ ${lines.join("\n")}`
|
|
|
3388
3216
|
timezone: params.timezone
|
|
3389
3217
|
};
|
|
3390
3218
|
const result = scheduler.addJob(jobConfig);
|
|
3391
|
-
return
|
|
3219
|
+
return textResult(result.message);
|
|
3392
3220
|
}
|
|
3393
3221
|
case "remove": {
|
|
3394
3222
|
if (!params.name) {
|
|
3395
|
-
return
|
|
3223
|
+
return textResult(
|
|
3396
3224
|
"Error: 'name' is required for removing a task."
|
|
3397
3225
|
);
|
|
3398
3226
|
}
|
|
3399
3227
|
const result = scheduler.removeJob(params.name);
|
|
3400
|
-
return
|
|
3228
|
+
return textResult(result.message);
|
|
3401
3229
|
}
|
|
3402
3230
|
case "trigger": {
|
|
3403
3231
|
if (!params.name) {
|
|
3404
|
-
return
|
|
3232
|
+
return textResult(
|
|
3405
3233
|
"Error: 'name' is required for triggering a task."
|
|
3406
3234
|
);
|
|
3407
3235
|
}
|
|
3408
3236
|
const result = await scheduler.triggerJob(params.name);
|
|
3409
|
-
return
|
|
3237
|
+
return textResult(result.message);
|
|
3410
3238
|
}
|
|
3411
3239
|
case "enable": {
|
|
3412
3240
|
if (!params.name) {
|
|
3413
|
-
return
|
|
3241
|
+
return textResult(
|
|
3414
3242
|
"Error: 'name' is required for enabling a task."
|
|
3415
3243
|
);
|
|
3416
3244
|
}
|
|
3417
3245
|
const result = scheduler.setEnabled(params.name, true);
|
|
3418
|
-
return
|
|
3246
|
+
return textResult(result.message);
|
|
3419
3247
|
}
|
|
3420
3248
|
case "disable": {
|
|
3421
3249
|
if (!params.name) {
|
|
3422
|
-
return
|
|
3250
|
+
return textResult(
|
|
3423
3251
|
"Error: 'name' is required for disabling a task."
|
|
3424
3252
|
);
|
|
3425
3253
|
}
|
|
3426
3254
|
const result = scheduler.setEnabled(params.name, false);
|
|
3427
|
-
return
|
|
3255
|
+
return textResult(result.message);
|
|
3428
3256
|
}
|
|
3429
3257
|
default:
|
|
3430
|
-
return
|
|
3258
|
+
return textResult(
|
|
3431
3259
|
`Error: Unknown action '${params.action}'. Use: add, list, remove, trigger, enable, disable.`
|
|
3432
3260
|
);
|
|
3433
3261
|
}
|
|
@@ -3437,8 +3265,8 @@ ${lines.join("\n")}`
|
|
|
3437
3265
|
|
|
3438
3266
|
// src/runtime/commands/help-command.ts
|
|
3439
3267
|
init_commands();
|
|
3440
|
-
import
|
|
3441
|
-
import
|
|
3268
|
+
import fs9 from "fs";
|
|
3269
|
+
import path10 from "path";
|
|
3442
3270
|
function buildHelpMessage(rootDir) {
|
|
3443
3271
|
const sections = [];
|
|
3444
3272
|
const commands = getVisibleCommands();
|
|
@@ -3448,7 +3276,7 @@ function buildHelpMessage(rootDir) {
|
|
|
3448
3276
|
sections.push(`\u{1F4CB} **Available Commands**
|
|
3449
3277
|
|
|
3450
3278
|
${commandLines.join("\n")}`);
|
|
3451
|
-
const configPath =
|
|
3279
|
+
const configPath = path10.resolve(rootDir, "skillpack.json");
|
|
3452
3280
|
const skills = readInstalledSkills(configPath);
|
|
3453
3281
|
if (skills.length > 0) {
|
|
3454
3282
|
const skillLines = skills.map(
|
|
@@ -3484,11 +3312,11 @@ function handleHelpCommand(rootDir) {
|
|
|
3484
3312
|
};
|
|
3485
3313
|
}
|
|
3486
3314
|
function readInstalledSkills(configPath) {
|
|
3487
|
-
if (!
|
|
3315
|
+
if (!fs9.existsSync(configPath)) {
|
|
3488
3316
|
return [];
|
|
3489
3317
|
}
|
|
3490
3318
|
try {
|
|
3491
|
-
const raw =
|
|
3319
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
3492
3320
|
const config = JSON.parse(raw);
|
|
3493
3321
|
return Array.isArray(config.skills) ? config.skills : [];
|
|
3494
3322
|
} catch {
|
|
@@ -3508,24 +3336,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
|
3508
3336
|
var PACK_AGENTS_FILE = "AGENTS.md";
|
|
3509
3337
|
var PACK_SOUL_FILE = "SOUL.md";
|
|
3510
3338
|
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
3511
|
-
if (!
|
|
3339
|
+
if (!fs10.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
3512
3340
|
log(
|
|
3513
3341
|
`[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
|
|
3514
3342
|
);
|
|
3515
3343
|
return null;
|
|
3516
3344
|
}
|
|
3517
|
-
const packConfigPath =
|
|
3518
|
-
const skillDir =
|
|
3519
|
-
const skillPath =
|
|
3345
|
+
const packConfigPath = path11.resolve(rootDir, "skillpack.json");
|
|
3346
|
+
const skillDir = path11.resolve(skillsPath, BUILTIN_SKILL_CREATOR_NAME);
|
|
3347
|
+
const skillPath = path11.join(skillDir, "SKILL.md");
|
|
3520
3348
|
const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
|
|
3521
3349
|
const copyDir = (srcDir, destDir) => {
|
|
3522
|
-
|
|
3523
|
-
for (const entry of
|
|
3350
|
+
fs10.mkdirSync(destDir, { recursive: true });
|
|
3351
|
+
for (const entry of fs10.readdirSync(srcDir, { withFileTypes: true })) {
|
|
3524
3352
|
if (entry.name === ".DS_Store") {
|
|
3525
3353
|
continue;
|
|
3526
3354
|
}
|
|
3527
|
-
const srcPath =
|
|
3528
|
-
const destPath =
|
|
3355
|
+
const srcPath = path11.join(srcDir, entry.name);
|
|
3356
|
+
const destPath = path11.join(destDir, entry.name);
|
|
3529
3357
|
if (entry.isDirectory()) {
|
|
3530
3358
|
copyDir(srcPath, destPath);
|
|
3531
3359
|
continue;
|
|
@@ -3534,17 +3362,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
|
3534
3362
|
continue;
|
|
3535
3363
|
}
|
|
3536
3364
|
if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
|
|
3537
|
-
const content =
|
|
3538
|
-
|
|
3365
|
+
const content = fs10.readFileSync(srcPath, "utf-8");
|
|
3366
|
+
fs10.writeFileSync(destPath, renderTemplate(content), "utf-8");
|
|
3539
3367
|
continue;
|
|
3540
3368
|
}
|
|
3541
|
-
|
|
3369
|
+
fs10.copyFileSync(srcPath, destPath);
|
|
3542
3370
|
}
|
|
3543
3371
|
};
|
|
3544
|
-
if (!
|
|
3372
|
+
if (!fs10.existsSync(skillDir)) {
|
|
3545
3373
|
copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
|
|
3546
3374
|
}
|
|
3547
|
-
if (!
|
|
3375
|
+
if (!fs10.existsSync(skillPath)) {
|
|
3548
3376
|
log(
|
|
3549
3377
|
`[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
|
|
3550
3378
|
);
|
|
@@ -3572,11 +3400,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
|
3572
3400
|
};
|
|
3573
3401
|
}
|
|
3574
3402
|
function readOptionalPackPromptFile(filePath) {
|
|
3575
|
-
if (!
|
|
3403
|
+
if (!fs10.existsSync(filePath)) {
|
|
3576
3404
|
return void 0;
|
|
3577
3405
|
}
|
|
3578
3406
|
try {
|
|
3579
|
-
const content =
|
|
3407
|
+
const content = fs10.readFileSync(filePath, "utf-8").trim();
|
|
3580
3408
|
return content.length > 0 ? content : void 0;
|
|
3581
3409
|
} catch (error) {
|
|
3582
3410
|
console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
|
|
@@ -3584,8 +3412,8 @@ function readOptionalPackPromptFile(filePath) {
|
|
|
3584
3412
|
}
|
|
3585
3413
|
}
|
|
3586
3414
|
function buildPackPromptBlock(rootDir) {
|
|
3587
|
-
const agentsPath =
|
|
3588
|
-
const soulPath =
|
|
3415
|
+
const agentsPath = path11.resolve(rootDir, PACK_AGENTS_FILE);
|
|
3416
|
+
const soulPath = path11.resolve(rootDir, PACK_SOUL_FILE);
|
|
3589
3417
|
const agentsContent = readOptionalPackPromptFile(agentsPath);
|
|
3590
3418
|
const soulContent = readOptionalPackPromptFile(soulPath);
|
|
3591
3419
|
if (!agentsContent && !soulContent) {
|
|
@@ -3644,13 +3472,15 @@ var PackAgent = class {
|
|
|
3644
3472
|
options;
|
|
3645
3473
|
channels = /* @__PURE__ */ new Map();
|
|
3646
3474
|
pendingSessionCreations = /* @__PURE__ */ new Map();
|
|
3647
|
-
schedulerRef = {
|
|
3475
|
+
schedulerRef = {
|
|
3476
|
+
current: null
|
|
3477
|
+
};
|
|
3648
3478
|
authStorage;
|
|
3649
|
-
|
|
3479
|
+
delegatedCustomToolClient = new DelegatedCustomToolClient();
|
|
3480
|
+
hostIpcClient = new HostIpcClient();
|
|
3650
3481
|
constructor(options) {
|
|
3651
3482
|
this.options = options;
|
|
3652
|
-
|
|
3653
|
-
const configPath = path13.resolve(options.rootDir, "data", "config.json");
|
|
3483
|
+
const configPath = path11.resolve(options.rootDir, "data", "config.json");
|
|
3654
3484
|
const backend = new ConfigFileAuthBackend(configPath);
|
|
3655
3485
|
this.authStorage = AuthStorage.fromStorage(backend);
|
|
3656
3486
|
const providerMeta = SUPPORTED_PROVIDERS[options.provider];
|
|
@@ -3677,13 +3507,20 @@ var PackAgent = class {
|
|
|
3677
3507
|
setScheduler(scheduler) {
|
|
3678
3508
|
this.schedulerRef.current = scheduler;
|
|
3679
3509
|
}
|
|
3680
|
-
createCustomTools(adapter, channelId, fileOutputCallbackRef,
|
|
3510
|
+
async createCustomTools(adapter, channelId, fileOutputCallbackRef, delegatedToolRunContextRef) {
|
|
3511
|
+
const delegatedDefinitions = await this.delegatedCustomToolClient.listDefinitions();
|
|
3681
3512
|
const tools = [
|
|
3682
3513
|
createSendFileTool(fileOutputCallbackRef),
|
|
3683
|
-
|
|
3514
|
+
...createDelegatedCustomTools(
|
|
3515
|
+
delegatedDefinitions,
|
|
3516
|
+
this.delegatedCustomToolClient,
|
|
3517
|
+
delegatedToolRunContextRef
|
|
3518
|
+
)
|
|
3684
3519
|
];
|
|
3685
3520
|
if (adapter !== "scheduler") {
|
|
3686
|
-
tools.push(
|
|
3521
|
+
tools.push(
|
|
3522
|
+
createManageScheduleTool(this.schedulerRef, adapter, channelId)
|
|
3523
|
+
);
|
|
3687
3524
|
}
|
|
3688
3525
|
return tools;
|
|
3689
3526
|
}
|
|
@@ -3701,7 +3538,9 @@ var PackAgent = class {
|
|
|
3701
3538
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
3702
3539
|
if (baseUrl && modelId) {
|
|
3703
3540
|
const apiProtocol = this.options.apiProtocol ?? "openai-completions";
|
|
3704
|
-
log(
|
|
3541
|
+
log(
|
|
3542
|
+
`[PackAgent] Registering custom model ${provider}/${modelId} api=${apiProtocol} baseUrl=${baseUrl}`
|
|
3543
|
+
);
|
|
3705
3544
|
modelRegistry.registerProvider(provider, {
|
|
3706
3545
|
baseUrl,
|
|
3707
3546
|
apiKey: this.options.apiKey,
|
|
@@ -3722,26 +3561,23 @@ var PackAgent = class {
|
|
|
3722
3561
|
const resolvedModel = modelRegistry.find(provider, modelId);
|
|
3723
3562
|
const model = resolvedModel && baseUrl && !this.options.apiProtocol ? { ...resolvedModel, baseUrl } : resolvedModel;
|
|
3724
3563
|
if (resolvedModel && baseUrl) {
|
|
3725
|
-
log(
|
|
3564
|
+
log(
|
|
3565
|
+
`[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`
|
|
3566
|
+
);
|
|
3726
3567
|
}
|
|
3727
|
-
const sessionDir =
|
|
3728
|
-
|
|
3729
|
-
"data",
|
|
3730
|
-
"sessions",
|
|
3731
|
-
channelId
|
|
3732
|
-
);
|
|
3733
|
-
fs13.mkdirSync(sessionDir, { recursive: true });
|
|
3568
|
+
const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
|
|
3569
|
+
fs10.mkdirSync(sessionDir, { recursive: true });
|
|
3734
3570
|
const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
|
|
3735
3571
|
log(`[PackAgent] Session dir: ${sessionDir}`);
|
|
3736
|
-
const workspaceDir =
|
|
3572
|
+
const workspaceDir = path11.resolve(
|
|
3737
3573
|
rootDir,
|
|
3738
3574
|
"data",
|
|
3739
3575
|
"workspaces",
|
|
3740
3576
|
channelId
|
|
3741
3577
|
);
|
|
3742
|
-
|
|
3578
|
+
fs10.mkdirSync(workspaceDir, { recursive: true });
|
|
3743
3579
|
log(`[PackAgent] Workspace dir: ${workspaceDir}`);
|
|
3744
|
-
const skillsPath =
|
|
3580
|
+
const skillsPath = path11.resolve(rootDir, "skills");
|
|
3745
3581
|
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
3746
3582
|
const materializedSkillCreator = materializeBuiltinSkillCreator(
|
|
3747
3583
|
rootDir,
|
|
@@ -3754,14 +3590,22 @@ var PackAgent = class {
|
|
|
3754
3590
|
}
|
|
3755
3591
|
const packPromptFiles = buildPackPromptBlock(rootDir);
|
|
3756
3592
|
if (packPromptFiles.agentsContent) {
|
|
3757
|
-
log(
|
|
3593
|
+
log(
|
|
3594
|
+
`[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`
|
|
3595
|
+
);
|
|
3758
3596
|
} else {
|
|
3759
|
-
log(
|
|
3597
|
+
log(
|
|
3598
|
+
`[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`
|
|
3599
|
+
);
|
|
3760
3600
|
}
|
|
3761
3601
|
if (packPromptFiles.soulContent) {
|
|
3762
|
-
log(
|
|
3602
|
+
log(
|
|
3603
|
+
`[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`
|
|
3604
|
+
);
|
|
3763
3605
|
} else {
|
|
3764
|
-
log(
|
|
3606
|
+
log(
|
|
3607
|
+
`[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`
|
|
3608
|
+
);
|
|
3765
3609
|
}
|
|
3766
3610
|
log(
|
|
3767
3611
|
`[PackAgent] Pack prompt injection: ${packPromptFiles.promptBlock ? "enabled" : "disabled"}`
|
|
@@ -3779,14 +3623,14 @@ var PackAgent = class {
|
|
|
3779
3623
|
const fileOutputCallbackRef = {
|
|
3780
3624
|
current: null
|
|
3781
3625
|
};
|
|
3782
|
-
const
|
|
3626
|
+
const delegatedToolRunContextRef = {
|
|
3783
3627
|
current: null
|
|
3784
3628
|
};
|
|
3785
|
-
const customTools = this.createCustomTools(
|
|
3629
|
+
const customTools = await this.createCustomTools(
|
|
3786
3630
|
adapter,
|
|
3787
3631
|
channelId,
|
|
3788
3632
|
fileOutputCallbackRef,
|
|
3789
|
-
|
|
3633
|
+
delegatedToolRunContextRef
|
|
3790
3634
|
);
|
|
3791
3635
|
const { session } = await createAgentSession({
|
|
3792
3636
|
cwd: workspaceDir,
|
|
@@ -3803,7 +3647,7 @@ var PackAgent = class {
|
|
|
3803
3647
|
running: false,
|
|
3804
3648
|
pending: Promise.resolve(),
|
|
3805
3649
|
fileOutputCallbackRef,
|
|
3806
|
-
|
|
3650
|
+
delegatedToolRunContextRef
|
|
3807
3651
|
};
|
|
3808
3652
|
this.channels.set(channelId, channelSession);
|
|
3809
3653
|
return channelSession;
|
|
@@ -3826,12 +3670,10 @@ var PackAgent = class {
|
|
|
3826
3670
|
cs.fileOutputCallbackRef.current = (event) => {
|
|
3827
3671
|
onEvent(event);
|
|
3828
3672
|
};
|
|
3829
|
-
cs.
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
artifacts
|
|
3834
|
-
});
|
|
3673
|
+
cs.delegatedToolRunContextRef.current = {
|
|
3674
|
+
runId,
|
|
3675
|
+
channelId,
|
|
3676
|
+
adapter
|
|
3835
3677
|
};
|
|
3836
3678
|
unsubscribe = cs.session.subscribe((event) => {
|
|
3837
3679
|
switch (event.type) {
|
|
@@ -3847,7 +3689,10 @@ var PackAgent = class {
|
|
|
3847
3689
|
if (event.message?.role === "user") {
|
|
3848
3690
|
log(JSON.stringify(event.message.content, null, 2));
|
|
3849
3691
|
}
|
|
3850
|
-
onEvent({
|
|
3692
|
+
onEvent({
|
|
3693
|
+
type: "message_start",
|
|
3694
|
+
role: event.message?.role ?? ""
|
|
3695
|
+
});
|
|
3851
3696
|
break;
|
|
3852
3697
|
case "message_update":
|
|
3853
3698
|
if (event.assistantMessageEvent?.type === "text_delta") {
|
|
@@ -3914,18 +3759,26 @@ var PackAgent = class {
|
|
|
3914
3759
|
let promptText = text;
|
|
3915
3760
|
const promptOptions = {};
|
|
3916
3761
|
if (attachments && attachments.length > 0) {
|
|
3917
|
-
const imageAttachments = attachments.filter(
|
|
3918
|
-
|
|
3762
|
+
const imageAttachments = attachments.filter(
|
|
3763
|
+
(a) => isImageMime(a.mimeType)
|
|
3764
|
+
);
|
|
3765
|
+
const nonImageAttachments = attachments.filter(
|
|
3766
|
+
(a) => !isImageMime(a.mimeType)
|
|
3767
|
+
);
|
|
3919
3768
|
if (imageAttachments.length > 0) {
|
|
3920
3769
|
promptOptions.images = attachmentsToImageContent(imageAttachments);
|
|
3921
|
-
log(
|
|
3770
|
+
log(
|
|
3771
|
+
`[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`
|
|
3772
|
+
);
|
|
3922
3773
|
}
|
|
3923
3774
|
if (nonImageAttachments.length > 0) {
|
|
3924
3775
|
const attachmentPrompt = formatAttachmentsPrompt(nonImageAttachments);
|
|
3925
3776
|
promptText = `${attachmentPrompt}
|
|
3926
3777
|
|
|
3927
3778
|
${text}`;
|
|
3928
|
-
log(
|
|
3779
|
+
log(
|
|
3780
|
+
`[PackAgent] Injecting ${nonImageAttachments.length} non-image attachment(s) into prompt`
|
|
3781
|
+
);
|
|
3929
3782
|
}
|
|
3930
3783
|
}
|
|
3931
3784
|
await cs.session.prompt(promptText, promptOptions);
|
|
@@ -3948,12 +3801,15 @@ ${text}`;
|
|
|
3948
3801
|
} finally {
|
|
3949
3802
|
cs.running = false;
|
|
3950
3803
|
cs.fileOutputCallbackRef.current = null;
|
|
3951
|
-
cs.
|
|
3804
|
+
cs.delegatedToolRunContextRef.current = null;
|
|
3952
3805
|
unsubscribe();
|
|
3953
3806
|
}
|
|
3954
3807
|
};
|
|
3955
3808
|
const resultPromise = cs.pending.catch(() => void 0).then(run);
|
|
3956
|
-
cs.pending = resultPromise.then(
|
|
3809
|
+
cs.pending = resultPromise.then(
|
|
3810
|
+
() => void 0,
|
|
3811
|
+
() => void 0
|
|
3812
|
+
);
|
|
3957
3813
|
return resultPromise;
|
|
3958
3814
|
}
|
|
3959
3815
|
async handleCommand(command, channelId) {
|
|
@@ -3968,11 +3824,12 @@ ${text}`;
|
|
|
3968
3824
|
this.channels.delete(channelId);
|
|
3969
3825
|
}
|
|
3970
3826
|
const { rootDir } = this.options;
|
|
3971
|
-
const sessionDir =
|
|
3972
|
-
if (
|
|
3973
|
-
|
|
3827
|
+
const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
|
|
3828
|
+
if (fs10.existsSync(sessionDir)) {
|
|
3829
|
+
fs10.rmSync(sessionDir, { recursive: true, force: true });
|
|
3974
3830
|
log(`[PackAgent] Cleared session dir: ${sessionDir}`);
|
|
3975
3831
|
}
|
|
3832
|
+
await this.hostIpcClient.notifyChannelSessionCleared({ channelId });
|
|
3976
3833
|
return {
|
|
3977
3834
|
success: true,
|
|
3978
3835
|
message: command === "new" ? "New session started." : "Session cleared."
|
|
@@ -4021,14 +3878,14 @@ ${text}`;
|
|
|
4021
3878
|
};
|
|
4022
3879
|
|
|
4023
3880
|
// src/runtime/adapters/web.ts
|
|
4024
|
-
import
|
|
4025
|
-
import
|
|
3881
|
+
import fs12 from "fs";
|
|
3882
|
+
import path13 from "path";
|
|
4026
3883
|
import { WebSocketServer } from "ws";
|
|
4027
3884
|
init_commands();
|
|
4028
3885
|
|
|
4029
3886
|
// src/runtime/services/conversation.ts
|
|
4030
|
-
import
|
|
4031
|
-
import
|
|
3887
|
+
import fs11 from "fs";
|
|
3888
|
+
import path12 from "path";
|
|
4032
3889
|
import {
|
|
4033
3890
|
parseSessionEntries
|
|
4034
3891
|
} from "@mariozechner/pi-coding-agent";
|
|
@@ -4046,17 +3903,17 @@ var ConversationService = class {
|
|
|
4046
3903
|
includeLegacyWeb = true,
|
|
4047
3904
|
allowedPlatforms
|
|
4048
3905
|
} = options;
|
|
4049
|
-
const sessionsDir =
|
|
3906
|
+
const sessionsDir = path12.resolve(this.rootDir, "data", "sessions");
|
|
4050
3907
|
const channelIds = new Set(activeChannels);
|
|
4051
3908
|
const allowedPlatformSet = allowedPlatforms ? new Set(allowedPlatforms) : null;
|
|
4052
3909
|
if (includeDefaultWeb) {
|
|
4053
3910
|
channelIds.add(DEFAULT_WEB_CHANNEL_ID);
|
|
4054
3911
|
}
|
|
4055
|
-
if (
|
|
4056
|
-
for (const entry of
|
|
4057
|
-
const channelDir =
|
|
3912
|
+
if (fs11.existsSync(sessionsDir)) {
|
|
3913
|
+
for (const entry of fs11.readdirSync(sessionsDir)) {
|
|
3914
|
+
const channelDir = path12.join(sessionsDir, entry);
|
|
4058
3915
|
try {
|
|
4059
|
-
if (!
|
|
3916
|
+
if (!fs11.statSync(channelDir).isDirectory()) {
|
|
4060
3917
|
continue;
|
|
4061
3918
|
}
|
|
4062
3919
|
const platform = this.detectPlatform(entry);
|
|
@@ -4080,7 +3937,7 @@ var ConversationService = class {
|
|
|
4080
3937
|
if (!includeLegacyWeb && this.isLegacyWebConversation(channelId)) {
|
|
4081
3938
|
continue;
|
|
4082
3939
|
}
|
|
4083
|
-
const channelDir =
|
|
3940
|
+
const channelDir = path12.join(sessionsDir, channelId);
|
|
4084
3941
|
const sessionFile = this.findLatestSessionFile(channelDir);
|
|
4085
3942
|
let messageCount = 0;
|
|
4086
3943
|
let lastMessageAt = "";
|
|
@@ -4124,7 +3981,7 @@ var ConversationService = class {
|
|
|
4124
3981
|
* Load latest messages for a channel in a simplified format.
|
|
4125
3982
|
*/
|
|
4126
3983
|
getMessages(channelId, limit = 100) {
|
|
4127
|
-
const channelDir =
|
|
3984
|
+
const channelDir = path12.resolve(
|
|
4128
3985
|
this.rootDir,
|
|
4129
3986
|
"data",
|
|
4130
3987
|
"sessions",
|
|
@@ -4213,20 +4070,20 @@ var ConversationService = class {
|
|
|
4213
4070
|
return messages.slice(-safeLimit);
|
|
4214
4071
|
}
|
|
4215
4072
|
findLatestSessionFile(channelDir) {
|
|
4216
|
-
if (!
|
|
4073
|
+
if (!fs11.existsSync(channelDir)) return null;
|
|
4217
4074
|
let stats;
|
|
4218
4075
|
try {
|
|
4219
|
-
stats =
|
|
4076
|
+
stats = fs11.statSync(channelDir);
|
|
4220
4077
|
} catch {
|
|
4221
4078
|
return null;
|
|
4222
4079
|
}
|
|
4223
4080
|
if (!stats.isDirectory()) return null;
|
|
4224
|
-
const files =
|
|
4225
|
-
return files[0] ?
|
|
4081
|
+
const files = fs11.readdirSync(channelDir).filter((file) => file.endsWith(".jsonl")).sort((a, b) => b.localeCompare(a));
|
|
4082
|
+
return files[0] ? path12.join(channelDir, files[0]) : null;
|
|
4226
4083
|
}
|
|
4227
4084
|
loadEntries(filePath) {
|
|
4228
4085
|
try {
|
|
4229
|
-
const content =
|
|
4086
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
4230
4087
|
const fileEntries = parseSessionEntries(content);
|
|
4231
4088
|
return fileEntries.filter(
|
|
4232
4089
|
(entry) => entry.type !== "session"
|
|
@@ -4422,7 +4279,7 @@ var ConversationService = class {
|
|
|
4422
4279
|
|
|
4423
4280
|
// src/runtime/adapters/web.ts
|
|
4424
4281
|
function getPackConfig(rootDir) {
|
|
4425
|
-
const raw =
|
|
4282
|
+
const raw = fs12.readFileSync(path13.join(rootDir, "skillpack.json"), "utf-8");
|
|
4426
4283
|
return JSON.parse(raw);
|
|
4427
4284
|
}
|
|
4428
4285
|
function parseCommand(text) {
|
|
@@ -4452,7 +4309,7 @@ function parsePositiveInt(value, fallback) {
|
|
|
4452
4309
|
return Math.floor(parsed);
|
|
4453
4310
|
}
|
|
4454
4311
|
function resolveDownloadFilePath(rootDir, filePath) {
|
|
4455
|
-
const resolvedPath =
|
|
4312
|
+
const resolvedPath = path13.isAbsolute(filePath) ? path13.resolve(filePath) : path13.resolve(rootDir, filePath);
|
|
4456
4313
|
return isWithinDirectory(rootDir, resolvedPath) ? resolvedPath : null;
|
|
4457
4314
|
}
|
|
4458
4315
|
var WebAdapter = class {
|
|
@@ -4467,7 +4324,6 @@ var WebAdapter = class {
|
|
|
4467
4324
|
this.agent = agent;
|
|
4468
4325
|
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
4469
4326
|
this.conversationService = new ConversationService(rootDir);
|
|
4470
|
-
const resultsQueryService = ctx.resultsQueryService ?? null;
|
|
4471
4327
|
const currentConf = configManager.getConfig();
|
|
4472
4328
|
let apiKey = currentConf.apiKey || "";
|
|
4473
4329
|
let currentProvider = currentConf.provider || "openai";
|
|
@@ -4611,17 +4467,6 @@ var WebAdapter = class {
|
|
|
4611
4467
|
)
|
|
4612
4468
|
);
|
|
4613
4469
|
});
|
|
4614
|
-
app.get("/api/results/artifacts", async (req, res) => {
|
|
4615
|
-
if (!resultsQueryService) {
|
|
4616
|
-
res.status(503).json({ error: "Results query service is not available" });
|
|
4617
|
-
return;
|
|
4618
|
-
}
|
|
4619
|
-
res.json(await resultsQueryService.listRecentArtifacts({
|
|
4620
|
-
channelId: typeof req.query.channelId === "string" ? req.query.channelId : void 0,
|
|
4621
|
-
limit: parsePositiveInt(req.query.limit, 100),
|
|
4622
|
-
offset: parsePositiveInt(req.query.offset, 0)
|
|
4623
|
-
}));
|
|
4624
|
-
});
|
|
4625
4470
|
app.get("/api/files", (req, res) => {
|
|
4626
4471
|
const filePath = req.query.path;
|
|
4627
4472
|
if (!filePath) {
|
|
@@ -4633,17 +4478,17 @@ var WebAdapter = class {
|
|
|
4633
4478
|
res.status(403).json({ error: "Access denied" });
|
|
4634
4479
|
return;
|
|
4635
4480
|
}
|
|
4636
|
-
if (!
|
|
4481
|
+
if (!fs12.existsSync(resolvedPath)) {
|
|
4637
4482
|
res.status(404).json({ error: "File not found" });
|
|
4638
4483
|
return;
|
|
4639
4484
|
}
|
|
4640
|
-
const filename =
|
|
4485
|
+
const filename = path13.basename(resolvedPath);
|
|
4641
4486
|
res.setHeader("Content-Type", "application/octet-stream");
|
|
4642
4487
|
res.setHeader(
|
|
4643
4488
|
"Content-Disposition",
|
|
4644
4489
|
`attachment; filename="${filename}"`
|
|
4645
4490
|
);
|
|
4646
|
-
|
|
4491
|
+
fs12.createReadStream(resolvedPath).pipe(res);
|
|
4647
4492
|
});
|
|
4648
4493
|
const getScheduler = () => {
|
|
4649
4494
|
const schedulerAdapter = ctx.adapterMap?.get("scheduler");
|
|
@@ -4828,13 +4673,28 @@ var WebAdapter = class {
|
|
|
4828
4673
|
|
|
4829
4674
|
// src/runtime/adapters/ipc.ts
|
|
4830
4675
|
init_types();
|
|
4676
|
+
var IPC_REQUEST_TYPES = /* @__PURE__ */ new Set([
|
|
4677
|
+
"get_conversations",
|
|
4678
|
+
"create_conversation",
|
|
4679
|
+
"get_messages",
|
|
4680
|
+
"send_message",
|
|
4681
|
+
"command",
|
|
4682
|
+
"get_config",
|
|
4683
|
+
"update_config",
|
|
4684
|
+
"get_status",
|
|
4685
|
+
"get_scheduled_jobs",
|
|
4686
|
+
"add_scheduled_job",
|
|
4687
|
+
"update_scheduled_job",
|
|
4688
|
+
"set_scheduled_job_enabled",
|
|
4689
|
+
"trigger_scheduled_job",
|
|
4690
|
+
"remove_scheduled_job"
|
|
4691
|
+
]);
|
|
4831
4692
|
var IpcAdapter = class {
|
|
4832
4693
|
name = "ipc";
|
|
4833
4694
|
agent = null;
|
|
4834
4695
|
rootDir = "";
|
|
4835
4696
|
adapterMap = null;
|
|
4836
4697
|
conversationService = null;
|
|
4837
|
-
resultsQueryService = null;
|
|
4838
4698
|
createdChannels = /* @__PURE__ */ new Set();
|
|
4839
4699
|
messageListener;
|
|
4840
4700
|
started = false;
|
|
@@ -4846,7 +4706,6 @@ var IpcAdapter = class {
|
|
|
4846
4706
|
this.rootDir = ctx.rootDir;
|
|
4847
4707
|
this.adapterMap = ctx.adapterMap ?? null;
|
|
4848
4708
|
this.conversationService = new ConversationService(ctx.rootDir);
|
|
4849
|
-
this.resultsQueryService = ctx.resultsQueryService ?? null;
|
|
4850
4709
|
this.messageListener = (message) => {
|
|
4851
4710
|
if (!this.isIpcRequest(message)) return;
|
|
4852
4711
|
void this.handleRequest(message);
|
|
@@ -4891,7 +4750,7 @@ var IpcAdapter = class {
|
|
|
4891
4750
|
isIpcRequest(message) {
|
|
4892
4751
|
if (!message || typeof message !== "object") return false;
|
|
4893
4752
|
const maybe = message;
|
|
4894
|
-
return typeof maybe.id === "string" && typeof maybe.type === "string";
|
|
4753
|
+
return typeof maybe.id === "string" && typeof maybe.type === "string" && IPC_REQUEST_TYPES.has(maybe.type);
|
|
4895
4754
|
}
|
|
4896
4755
|
async handleRequest(request) {
|
|
4897
4756
|
if (!this.agent || !this.conversationService) {
|
|
@@ -4930,18 +4789,6 @@ var IpcAdapter = class {
|
|
|
4930
4789
|
this.reply(request.id, messages);
|
|
4931
4790
|
return;
|
|
4932
4791
|
}
|
|
4933
|
-
case "get_recent_artifacts": {
|
|
4934
|
-
if (!this.resultsQueryService) {
|
|
4935
|
-
this.replyError(request.id, "Results query service is not available");
|
|
4936
|
-
return;
|
|
4937
|
-
}
|
|
4938
|
-
this.reply(request.id, await this.resultsQueryService.listRecentArtifacts({
|
|
4939
|
-
channelId: request.channelId,
|
|
4940
|
-
limit: request.limit,
|
|
4941
|
-
offset: request.offset
|
|
4942
|
-
}));
|
|
4943
|
-
return;
|
|
4944
|
-
}
|
|
4945
4792
|
case "send_message": {
|
|
4946
4793
|
if (!request.channelId || typeof request.channelId !== "string") {
|
|
4947
4794
|
this.replyError(request.id, "channelId is required");
|
|
@@ -5185,28 +5032,28 @@ var Lifecycle = class {
|
|
|
5185
5032
|
|
|
5186
5033
|
// src/runtime/registry.ts
|
|
5187
5034
|
import crypto from "crypto";
|
|
5188
|
-
import
|
|
5035
|
+
import fs13 from "fs";
|
|
5189
5036
|
import os from "os";
|
|
5190
|
-
import
|
|
5191
|
-
var SKILLPACK_HOME =
|
|
5192
|
-
var LEGACY_REGISTRY_FILE =
|
|
5193
|
-
var REGISTRY_DIR =
|
|
5037
|
+
import path14 from "path";
|
|
5038
|
+
var SKILLPACK_HOME = path14.join(os.homedir(), ".skillpack");
|
|
5039
|
+
var LEGACY_REGISTRY_FILE = path14.join(SKILLPACK_HOME, "registry.json");
|
|
5040
|
+
var REGISTRY_DIR = path14.join(SKILLPACK_HOME, "registry.d");
|
|
5194
5041
|
var migrationChecked = false;
|
|
5195
5042
|
function ensureHomeDir() {
|
|
5196
|
-
if (!
|
|
5197
|
-
|
|
5043
|
+
if (!fs13.existsSync(SKILLPACK_HOME)) {
|
|
5044
|
+
fs13.mkdirSync(SKILLPACK_HOME, { recursive: true });
|
|
5198
5045
|
}
|
|
5199
5046
|
}
|
|
5200
5047
|
function ensureRegistryDir() {
|
|
5201
5048
|
ensureHomeDir();
|
|
5202
|
-
if (!
|
|
5203
|
-
|
|
5049
|
+
if (!fs13.existsSync(REGISTRY_DIR)) {
|
|
5050
|
+
fs13.mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
5204
5051
|
}
|
|
5205
5052
|
}
|
|
5206
5053
|
function canonicalizeDir(dir) {
|
|
5207
|
-
const resolved =
|
|
5054
|
+
const resolved = path14.resolve(dir);
|
|
5208
5055
|
try {
|
|
5209
|
-
return
|
|
5056
|
+
return fs13.realpathSync(resolved);
|
|
5210
5057
|
} catch {
|
|
5211
5058
|
return resolved;
|
|
5212
5059
|
}
|
|
@@ -5215,7 +5062,7 @@ function hashDir(dir) {
|
|
|
5215
5062
|
return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
|
|
5216
5063
|
}
|
|
5217
5064
|
function getEntryPathForCanonicalDir(dir) {
|
|
5218
|
-
return
|
|
5065
|
+
return path14.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
|
|
5219
5066
|
}
|
|
5220
5067
|
function getEntryPath(dir) {
|
|
5221
5068
|
ensureRegistryReady();
|
|
@@ -5223,11 +5070,11 @@ function getEntryPath(dir) {
|
|
|
5223
5070
|
}
|
|
5224
5071
|
function listEntryFiles() {
|
|
5225
5072
|
ensureRegistryReady();
|
|
5226
|
-
return
|
|
5073
|
+
return fs13.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path14.join(REGISTRY_DIR, file));
|
|
5227
5074
|
}
|
|
5228
5075
|
function readEntryFile(filePath) {
|
|
5229
5076
|
try {
|
|
5230
|
-
const raw =
|
|
5077
|
+
const raw = fs13.readFileSync(filePath, "utf-8");
|
|
5231
5078
|
const data = JSON.parse(raw);
|
|
5232
5079
|
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") {
|
|
5233
5080
|
return null;
|
|
@@ -5260,8 +5107,8 @@ function writeEntryFile(entry) {
|
|
|
5260
5107
|
};
|
|
5261
5108
|
const entryPath = getEntryPathForCanonicalDir(normalized.dir);
|
|
5262
5109
|
const tmpPath = createTmpPath(entryPath);
|
|
5263
|
-
|
|
5264
|
-
|
|
5110
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
5111
|
+
fs13.renameSync(tmpPath, entryPath);
|
|
5265
5112
|
}
|
|
5266
5113
|
function migrateLegacyRegistryIfNeeded() {
|
|
5267
5114
|
if (migrationChecked) {
|
|
@@ -5269,14 +5116,14 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
5269
5116
|
}
|
|
5270
5117
|
migrationChecked = true;
|
|
5271
5118
|
ensureRegistryDir();
|
|
5272
|
-
if (!
|
|
5119
|
+
if (!fs13.existsSync(LEGACY_REGISTRY_FILE)) {
|
|
5273
5120
|
return;
|
|
5274
5121
|
}
|
|
5275
5122
|
if (listEntryFiles().length > 0) {
|
|
5276
5123
|
return;
|
|
5277
5124
|
}
|
|
5278
5125
|
try {
|
|
5279
|
-
const raw =
|
|
5126
|
+
const raw = fs13.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
|
|
5280
5127
|
const data = JSON.parse(raw);
|
|
5281
5128
|
const packs = Array.isArray(data?.packs) ? data.packs : [];
|
|
5282
5129
|
for (const pack of packs) {
|
|
@@ -5289,7 +5136,7 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
5289
5136
|
} catch {
|
|
5290
5137
|
}
|
|
5291
5138
|
}
|
|
5292
|
-
|
|
5139
|
+
fs13.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
|
|
5293
5140
|
} catch (err) {
|
|
5294
5141
|
console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
|
|
5295
5142
|
}
|
|
@@ -5342,7 +5189,7 @@ function deregister(dir, pid) {
|
|
|
5342
5189
|
}
|
|
5343
5190
|
|
|
5344
5191
|
// src/runtime/server.ts
|
|
5345
|
-
var __dirname =
|
|
5192
|
+
var __dirname = path16.dirname(fileURLToPath2(import.meta.url));
|
|
5346
5193
|
async function startServer(options) {
|
|
5347
5194
|
const {
|
|
5348
5195
|
rootDir,
|
|
@@ -5358,8 +5205,8 @@ async function startServer(options) {
|
|
|
5358
5205
|
const baseUrl = dataConfig.baseUrl?.trim() || void 0;
|
|
5359
5206
|
const modelId = dataConfig.modelId?.trim() || (SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId);
|
|
5360
5207
|
const apiProtocol = dataConfig.apiProtocol;
|
|
5361
|
-
const packageRoot =
|
|
5362
|
-
const webDir =
|
|
5208
|
+
const packageRoot = path16.resolve(__dirname, "..");
|
|
5209
|
+
const webDir = fs16.existsSync(path16.join(rootDir, "web")) ? path16.join(rootDir, "web") : path16.join(packageRoot, "web");
|
|
5363
5210
|
const app = express();
|
|
5364
5211
|
app.use(express.json());
|
|
5365
5212
|
app.use(express.static(webDir));
|
|
@@ -5377,13 +5224,6 @@ async function startServer(options) {
|
|
|
5377
5224
|
});
|
|
5378
5225
|
});
|
|
5379
5226
|
const lifecycle = new Lifecycle(server);
|
|
5380
|
-
const resultStore = new ResultStore(rootDir);
|
|
5381
|
-
const artifactSnapshotService = new ArtifactSnapshotService(rootDir);
|
|
5382
|
-
const artifactPersistenceService = new ArtifactPersistenceService(
|
|
5383
|
-
artifactSnapshotService,
|
|
5384
|
-
resultStore
|
|
5385
|
-
);
|
|
5386
|
-
const resultsQueryService = new ResultsQueryService(resultStore);
|
|
5387
5227
|
const agent = new PackAgent({
|
|
5388
5228
|
apiKey,
|
|
5389
5229
|
rootDir,
|
|
@@ -5391,14 +5231,16 @@ async function startServer(options) {
|
|
|
5391
5231
|
modelId,
|
|
5392
5232
|
baseUrl,
|
|
5393
5233
|
apiProtocol,
|
|
5394
|
-
lifecycleHandler: lifecycle
|
|
5395
|
-
artifactPersistenceService
|
|
5234
|
+
lifecycleHandler: lifecycle
|
|
5396
5235
|
});
|
|
5397
5236
|
const adapters = [];
|
|
5398
5237
|
const adapterMap = /* @__PURE__ */ new Map();
|
|
5399
5238
|
const hasIpcChannel = typeof process.send === "function";
|
|
5400
5239
|
const ipcAdapter = new IpcAdapter();
|
|
5401
5240
|
const webEnabled = runtimeMode === "standalone";
|
|
5241
|
+
console.log(
|
|
5242
|
+
`[Runtime] IPC channel ${hasIpcChannel ? "available" : "not available"} (mode=${runtimeMode})`
|
|
5243
|
+
);
|
|
5402
5244
|
if (hasIpcChannel) {
|
|
5403
5245
|
await ipcAdapter.start({
|
|
5404
5246
|
agent,
|
|
@@ -5406,8 +5248,7 @@ async function startServer(options) {
|
|
|
5406
5248
|
app,
|
|
5407
5249
|
rootDir,
|
|
5408
5250
|
lifecycle,
|
|
5409
|
-
adapterMap
|
|
5410
|
-
resultsQueryService
|
|
5251
|
+
adapterMap
|
|
5411
5252
|
});
|
|
5412
5253
|
adapters.push(ipcAdapter);
|
|
5413
5254
|
adapterMap.set(ipcAdapter.name, ipcAdapter);
|
|
@@ -5422,8 +5263,7 @@ async function startServer(options) {
|
|
|
5422
5263
|
rootDir,
|
|
5423
5264
|
lifecycle,
|
|
5424
5265
|
adapterMap,
|
|
5425
|
-
ipcBroadcaster
|
|
5426
|
-
resultsQueryService
|
|
5266
|
+
ipcBroadcaster
|
|
5427
5267
|
});
|
|
5428
5268
|
adapters.push(webAdapter);
|
|
5429
5269
|
adapterMap.set(webAdapter.name, webAdapter);
|
|
@@ -5441,8 +5281,7 @@ async function startServer(options) {
|
|
|
5441
5281
|
rootDir,
|
|
5442
5282
|
lifecycle,
|
|
5443
5283
|
adapterMap,
|
|
5444
|
-
ipcBroadcaster
|
|
5445
|
-
resultsQueryService
|
|
5284
|
+
ipcBroadcaster
|
|
5446
5285
|
});
|
|
5447
5286
|
adapters.push(telegramAdapter);
|
|
5448
5287
|
adapterMap.set(telegramAdapter.name, telegramAdapter);
|
|
@@ -5470,8 +5309,7 @@ async function startServer(options) {
|
|
|
5470
5309
|
rootDir,
|
|
5471
5310
|
lifecycle,
|
|
5472
5311
|
adapterMap,
|
|
5473
|
-
ipcBroadcaster
|
|
5474
|
-
resultsQueryService
|
|
5312
|
+
ipcBroadcaster
|
|
5475
5313
|
});
|
|
5476
5314
|
adapters.push(slackAdapter);
|
|
5477
5315
|
adapterMap.set(slackAdapter.name, slackAdapter);
|
|
@@ -5503,7 +5341,7 @@ async function startServer(options) {
|
|
|
5503
5341
|
lifecycle,
|
|
5504
5342
|
notify: notifyFn,
|
|
5505
5343
|
adapterMap,
|
|
5506
|
-
|
|
5344
|
+
ipcBroadcaster
|
|
5507
5345
|
});
|
|
5508
5346
|
adapters.push(schedulerAdapter);
|
|
5509
5347
|
adapterMap.set(schedulerAdapter.name, schedulerAdapter);
|
|
@@ -5591,23 +5429,23 @@ function findMissingSkills(workDir, config) {
|
|
|
5591
5429
|
});
|
|
5592
5430
|
}
|
|
5593
5431
|
function copyStartTemplates2(workDir) {
|
|
5594
|
-
const templateDir =
|
|
5432
|
+
const templateDir = path17.resolve(
|
|
5595
5433
|
new URL("../templates", import.meta.url).pathname
|
|
5596
5434
|
);
|
|
5597
5435
|
for (const file of ["start.sh", "start.bat"]) {
|
|
5598
|
-
const src =
|
|
5599
|
-
const dest =
|
|
5600
|
-
if (
|
|
5601
|
-
|
|
5436
|
+
const src = path17.join(templateDir, file);
|
|
5437
|
+
const dest = path17.join(workDir, file);
|
|
5438
|
+
if (fs17.existsSync(src)) {
|
|
5439
|
+
fs17.copyFileSync(src, dest);
|
|
5602
5440
|
if (file === "start.sh") {
|
|
5603
|
-
|
|
5441
|
+
fs17.chmodSync(dest, 493);
|
|
5604
5442
|
}
|
|
5605
5443
|
}
|
|
5606
5444
|
}
|
|
5607
5445
|
}
|
|
5608
5446
|
async function runCommand(directory) {
|
|
5609
|
-
const workDir = directory ?
|
|
5610
|
-
|
|
5447
|
+
const workDir = directory ? path17.resolve(directory) : process.cwd();
|
|
5448
|
+
fs17.mkdirSync(workDir, { recursive: true });
|
|
5611
5449
|
if (!configExists(workDir)) {
|
|
5612
5450
|
console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
|
|
5613
5451
|
const { name, description } = await inquirer2.prompt([
|
|
@@ -5651,14 +5489,14 @@ async function runCommand(directory) {
|
|
|
5651
5489
|
}
|
|
5652
5490
|
|
|
5653
5491
|
// src/cli.ts
|
|
5654
|
-
import
|
|
5655
|
-
import
|
|
5492
|
+
import fs18 from "fs";
|
|
5493
|
+
import path18 from "path";
|
|
5656
5494
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5657
5495
|
var packageJson = JSON.parse(
|
|
5658
|
-
|
|
5496
|
+
fs18.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
5659
5497
|
);
|
|
5660
5498
|
var program = new Command();
|
|
5661
|
-
var cliFilePath =
|
|
5499
|
+
var cliFilePath = path18.resolve(fileURLToPath3(import.meta.url));
|
|
5662
5500
|
program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
|
|
5663
5501
|
program.command("create [directory]").description("Create a skills pack interactively").option("--config <path-or-url>", "Initialize from a local or remote skillpack.json").action(async (directory, options) => {
|
|
5664
5502
|
await createCommand(directory, options);
|
|
@@ -5679,7 +5517,7 @@ program.command("zip").description("Package the current pack as a zip file (skil
|
|
|
5679
5517
|
function normalizeUserArgs(argv) {
|
|
5680
5518
|
if (argv.length === 0) return argv;
|
|
5681
5519
|
const firstArg = argv[0];
|
|
5682
|
-
if (firstArg &&
|
|
5520
|
+
if (firstArg && path18.resolve(firstArg) === cliFilePath) {
|
|
5683
5521
|
return argv.slice(1);
|
|
5684
5522
|
}
|
|
5685
5523
|
return argv;
|