@cremini/skillpack 1.2.9 → 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 +598 -710
- package/package.json +1 -2
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,26 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/job-schedule.ts
|
|
13
|
+
function normalizeJobCron(cron2) {
|
|
14
|
+
if (typeof cron2 !== "string") {
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
const normalized = cron2.trim();
|
|
18
|
+
return normalized ? normalized : void 0;
|
|
19
|
+
}
|
|
20
|
+
function hasJobSchedule(jobOrCron) {
|
|
21
|
+
if (typeof jobOrCron === "string" || jobOrCron == null) {
|
|
22
|
+
return !!normalizeJobCron(jobOrCron);
|
|
23
|
+
}
|
|
24
|
+
return !!normalizeJobCron(jobOrCron.cron);
|
|
25
|
+
}
|
|
26
|
+
var init_job_schedule = __esm({
|
|
27
|
+
"src/job-schedule.ts"() {
|
|
28
|
+
"use strict";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
12
32
|
// src/job-config.ts
|
|
13
33
|
import fs2 from "fs";
|
|
14
34
|
import path2 from "path";
|
|
@@ -27,9 +47,9 @@ function validateScheduledJobConfig(value, sourceLabel, index) {
|
|
|
27
47
|
`Invalid job config from ${sourceLabel}: "jobs[${index}].name" is required`
|
|
28
48
|
);
|
|
29
49
|
}
|
|
30
|
-
if (typeof job.cron !== "string"
|
|
50
|
+
if (job.cron !== void 0 && typeof job.cron !== "string") {
|
|
31
51
|
throw new Error(
|
|
32
|
-
`Invalid job config from ${sourceLabel}: "jobs[${index}].cron"
|
|
52
|
+
`Invalid job config from ${sourceLabel}: "jobs[${index}].cron" must be a string`
|
|
33
53
|
);
|
|
34
54
|
}
|
|
35
55
|
if (typeof job.prompt !== "string" || !job.prompt.trim()) {
|
|
@@ -87,18 +107,25 @@ function validateJobFileShape(value, sourceLabel) {
|
|
|
87
107
|
function normalizeJobFile(jobFile) {
|
|
88
108
|
return {
|
|
89
109
|
jobs: jobFile.jobs.map((job) => ({
|
|
90
|
-
|
|
91
|
-
cron: job.cron.trim(),
|
|
92
|
-
prompt: job.prompt,
|
|
93
|
-
notify: {
|
|
94
|
-
adapter: job.notify.adapter.trim(),
|
|
95
|
-
channelId: job.notify.channelId.trim()
|
|
96
|
-
},
|
|
97
|
-
...job.enabled !== void 0 ? { enabled: job.enabled } : {},
|
|
98
|
-
...job.timezone !== void 0 ? { timezone: job.timezone.trim() } : {}
|
|
110
|
+
...normalizeScheduledJobConfig(job)
|
|
99
111
|
}))
|
|
100
112
|
};
|
|
101
113
|
}
|
|
114
|
+
function normalizeScheduledJobConfig(job) {
|
|
115
|
+
const normalizedCron = normalizeJobCron(job.cron);
|
|
116
|
+
const normalizedTimezone = typeof job.timezone === "string" && job.timezone.trim() ? job.timezone.trim() : void 0;
|
|
117
|
+
return {
|
|
118
|
+
name: job.name.trim(),
|
|
119
|
+
...normalizedCron ? { cron: normalizedCron } : {},
|
|
120
|
+
prompt: job.prompt,
|
|
121
|
+
notify: {
|
|
122
|
+
adapter: job.notify.adapter.trim(),
|
|
123
|
+
channelId: job.notify.channelId.trim()
|
|
124
|
+
},
|
|
125
|
+
...normalizedCron && job.enabled !== void 0 ? { enabled: job.enabled } : {},
|
|
126
|
+
...normalizedCron && normalizedTimezone ? { timezone: normalizedTimezone } : {}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
102
129
|
function loadJobFile(workDir) {
|
|
103
130
|
const filePath = getJobFilePath(workDir);
|
|
104
131
|
if (!fs2.existsSync(filePath)) {
|
|
@@ -119,6 +146,7 @@ var JOB_FILE;
|
|
|
119
146
|
var init_job_config = __esm({
|
|
120
147
|
"src/job-config.ts"() {
|
|
121
148
|
"use strict";
|
|
149
|
+
init_job_schedule();
|
|
122
150
|
JOB_FILE = "job.json";
|
|
123
151
|
}
|
|
124
152
|
});
|
|
@@ -389,7 +417,7 @@ var telegram_exports = {};
|
|
|
389
417
|
__export(telegram_exports, {
|
|
390
418
|
TelegramAdapter: () => TelegramAdapter
|
|
391
419
|
});
|
|
392
|
-
import
|
|
420
|
+
import fs14 from "fs";
|
|
393
421
|
import TelegramBot from "node-telegram-bot-api";
|
|
394
422
|
var MAX_MESSAGE_LENGTH, ACK_REACTION, TelegramAdapter;
|
|
395
423
|
var init_telegram = __esm({
|
|
@@ -707,7 +735,7 @@ var init_telegram = __esm({
|
|
|
707
735
|
async sendFileSafe(chatId, filePath, caption) {
|
|
708
736
|
if (!this.bot) return;
|
|
709
737
|
try {
|
|
710
|
-
if (!
|
|
738
|
+
if (!fs14.existsSync(filePath)) {
|
|
711
739
|
console.error(`[Telegram] File not found for sending: ${filePath}`);
|
|
712
740
|
return;
|
|
713
741
|
}
|
|
@@ -727,8 +755,8 @@ var slack_exports = {};
|
|
|
727
755
|
__export(slack_exports, {
|
|
728
756
|
SlackAdapter: () => SlackAdapter
|
|
729
757
|
});
|
|
730
|
-
import
|
|
731
|
-
import
|
|
758
|
+
import fs15 from "fs";
|
|
759
|
+
import path15 from "path";
|
|
732
760
|
import { App, LogLevel } from "@slack/bolt";
|
|
733
761
|
var INLINE_COMMANDS, SLASH_COMMANDS, MAX_MESSAGE_LENGTH2, ACK_REACTION2, PROCESSING_MESSAGE, SlackAdapter;
|
|
734
762
|
var init_slack = __esm({
|
|
@@ -1363,12 +1391,12 @@ var init_slack = __esm({
|
|
|
1363
1391
|
*/
|
|
1364
1392
|
async sendFileSafe(client, route, filePath, caption) {
|
|
1365
1393
|
try {
|
|
1366
|
-
if (!
|
|
1394
|
+
if (!fs15.existsSync(filePath)) {
|
|
1367
1395
|
console.error(`[Slack] File not found for sending: ${filePath}`);
|
|
1368
1396
|
return;
|
|
1369
1397
|
}
|
|
1370
|
-
const filename =
|
|
1371
|
-
const fileContent =
|
|
1398
|
+
const filename = path15.basename(filePath);
|
|
1399
|
+
const fileContent = fs15.readFileSync(filePath);
|
|
1372
1400
|
await client.files.uploadV2({
|
|
1373
1401
|
channel_id: route.channel,
|
|
1374
1402
|
thread_ts: route.threadTs,
|
|
@@ -1402,31 +1430,43 @@ function isValidTimezone(tz) {
|
|
|
1402
1430
|
function isValidJobName(name) {
|
|
1403
1431
|
return VALID_JOB_NAME.test(name) && name.length <= 64;
|
|
1404
1432
|
}
|
|
1433
|
+
function isRecurringJob(jobConfig) {
|
|
1434
|
+
return hasJobSchedule(jobConfig);
|
|
1435
|
+
}
|
|
1405
1436
|
var VALID_JOB_NAME, SchedulerAdapter;
|
|
1406
1437
|
var init_scheduler = __esm({
|
|
1407
1438
|
"src/runtime/adapters/scheduler.ts"() {
|
|
1408
1439
|
"use strict";
|
|
1409
1440
|
init_job_config();
|
|
1441
|
+
init_job_schedule();
|
|
1410
1442
|
VALID_JOB_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
|
|
1411
1443
|
SchedulerAdapter = class {
|
|
1412
1444
|
name = "scheduler";
|
|
1413
1445
|
agent;
|
|
1414
1446
|
rootDir = "";
|
|
1447
|
+
ipcBroadcaster = null;
|
|
1415
1448
|
notifyFn = async () => {
|
|
1416
1449
|
};
|
|
1417
1450
|
jobs = /* @__PURE__ */ new Map();
|
|
1418
1451
|
async start(ctx) {
|
|
1419
1452
|
this.agent = ctx.agent;
|
|
1420
1453
|
this.rootDir = ctx.rootDir;
|
|
1454
|
+
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
1421
1455
|
this.notifyFn = ctx.notify || (async () => {
|
|
1422
1456
|
});
|
|
1457
|
+
console.log(
|
|
1458
|
+
`[Scheduler] IPC broadcaster ${this.ipcBroadcaster ? "attached" : "not available"} for agent events`
|
|
1459
|
+
);
|
|
1423
1460
|
const jobConfigs = loadJobFile(this.rootDir).jobs;
|
|
1424
1461
|
let scheduledCount = 0;
|
|
1425
1462
|
let disabledCount = 0;
|
|
1463
|
+
let oneTimeCount = 0;
|
|
1426
1464
|
for (const jc of jobConfigs) {
|
|
1427
1465
|
const result = this.registerJob(jc);
|
|
1428
1466
|
if (result.registered) {
|
|
1429
|
-
if (jc
|
|
1467
|
+
if (!isRecurringJob(jc)) {
|
|
1468
|
+
oneTimeCount++;
|
|
1469
|
+
} else if (jc.enabled === false) {
|
|
1430
1470
|
disabledCount++;
|
|
1431
1471
|
} else {
|
|
1432
1472
|
scheduledCount++;
|
|
@@ -1436,6 +1476,7 @@ var init_scheduler = __esm({
|
|
|
1436
1476
|
const parts = [];
|
|
1437
1477
|
if (scheduledCount > 0) parts.push(`${scheduledCount} active`);
|
|
1438
1478
|
if (disabledCount > 0) parts.push(`${disabledCount} disabled`);
|
|
1479
|
+
if (oneTimeCount > 0) parts.push(`${oneTimeCount} one-time`);
|
|
1439
1480
|
if (parts.length > 0) {
|
|
1440
1481
|
console.log(`[SchedulerAdapter] Started with ${parts.join(", ")} job(s)`);
|
|
1441
1482
|
} else {
|
|
@@ -1450,43 +1491,43 @@ var init_scheduler = __esm({
|
|
|
1450
1491
|
* Does NOT persist – callers decide when to persist.
|
|
1451
1492
|
*/
|
|
1452
1493
|
registerJob(jobConfig) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
}
|
|
1458
|
-
if (!cron.validate(jobConfig.cron)) {
|
|
1459
|
-
const msg = `[Scheduler] Invalid cron expression for job "${jobConfig.name}": ${jobConfig.cron}`;
|
|
1494
|
+
const normalizedConfig = normalizeScheduledJobConfig(jobConfig);
|
|
1495
|
+
const normalizedCron = normalizeJobCron(normalizedConfig.cron);
|
|
1496
|
+
if (!isValidJobName(normalizedConfig.name)) {
|
|
1497
|
+
const msg = `[Scheduler] Invalid job name "${normalizedConfig.name}": must match ${VALID_JOB_NAME} and be \u226464 chars`;
|
|
1460
1498
|
console.error(msg);
|
|
1461
1499
|
return { registered: false, message: msg };
|
|
1462
1500
|
}
|
|
1463
|
-
if (
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1501
|
+
if (normalizedCron) {
|
|
1502
|
+
if (!cron.validate(normalizedCron)) {
|
|
1503
|
+
const msg = `[Scheduler] Invalid cron expression for job "${normalizedConfig.name}": ${normalizedCron}`;
|
|
1504
|
+
console.error(msg);
|
|
1505
|
+
return { registered: false, message: msg };
|
|
1506
|
+
}
|
|
1507
|
+
if (normalizedConfig.timezone && !isValidTimezone(normalizedConfig.timezone)) {
|
|
1508
|
+
const msg = `[Scheduler] Invalid timezone for job "${normalizedConfig.name}": ${normalizedConfig.timezone}`;
|
|
1509
|
+
console.error(msg);
|
|
1510
|
+
return { registered: false, message: msg };
|
|
1511
|
+
}
|
|
1467
1512
|
}
|
|
1468
|
-
this.removeFromMap(
|
|
1513
|
+
this.removeFromMap(normalizedConfig.name);
|
|
1469
1514
|
let task = null;
|
|
1470
|
-
if (
|
|
1471
|
-
task =
|
|
1472
|
-
|
|
1473
|
-
()
|
|
1474
|
-
void this.runJob(jobConfig);
|
|
1475
|
-
},
|
|
1476
|
-
{
|
|
1477
|
-
timezone: jobConfig.timezone
|
|
1478
|
-
}
|
|
1515
|
+
if (normalizedCron && normalizedConfig.enabled !== false) {
|
|
1516
|
+
task = this.createCronTask(normalizedConfig);
|
|
1517
|
+
console.log(
|
|
1518
|
+
`[Scheduler] Job "${normalizedConfig.name}" scheduled: ${normalizedCron}${normalizedConfig.timezone ? ` (${normalizedConfig.timezone})` : ""}`
|
|
1479
1519
|
);
|
|
1520
|
+
} else if (normalizedCron) {
|
|
1480
1521
|
console.log(
|
|
1481
|
-
`[Scheduler] Job "${
|
|
1522
|
+
`[Scheduler] Job "${normalizedConfig.name}" registered (disabled)`
|
|
1482
1523
|
);
|
|
1483
1524
|
} else {
|
|
1484
1525
|
console.log(
|
|
1485
|
-
`[Scheduler] Job "${
|
|
1526
|
+
`[Scheduler] Job "${normalizedConfig.name}" registered as one-time (manual trigger only)`
|
|
1486
1527
|
);
|
|
1487
1528
|
}
|
|
1488
|
-
this.jobs.set(
|
|
1489
|
-
config:
|
|
1529
|
+
this.jobs.set(normalizedConfig.name, {
|
|
1530
|
+
config: normalizedConfig,
|
|
1490
1531
|
task,
|
|
1491
1532
|
running: false,
|
|
1492
1533
|
notifyFailed: false
|
|
@@ -1513,8 +1554,27 @@ var init_scheduler = __esm({
|
|
|
1513
1554
|
console.log(`[Scheduler] Running job "${jobConfig.name}"`);
|
|
1514
1555
|
let fullText = "";
|
|
1515
1556
|
let agentFailed = false;
|
|
1557
|
+
let loggedFirstTextDelta = false;
|
|
1558
|
+
let sawAgentStart = false;
|
|
1559
|
+
let sawAgentEnd = false;
|
|
1516
1560
|
const pendingFiles = [];
|
|
1517
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);
|
|
1518
1578
|
if (event.type === "text_delta") fullText += event.delta;
|
|
1519
1579
|
if (event.type === "file_output") {
|
|
1520
1580
|
pendingFiles.push({
|
|
@@ -1532,6 +1592,7 @@ var init_scheduler = __esm({
|
|
|
1532
1592
|
onEvent,
|
|
1533
1593
|
void 0
|
|
1534
1594
|
);
|
|
1595
|
+
this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
|
|
1535
1596
|
if (result.errorMessage) {
|
|
1536
1597
|
fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u6267\u884C\u5931\u8D25\uFF1A${result.errorMessage}`;
|
|
1537
1598
|
agentFailed = true;
|
|
@@ -1540,6 +1601,7 @@ var init_scheduler = __esm({
|
|
|
1540
1601
|
if (job) job.lastError = void 0;
|
|
1541
1602
|
}
|
|
1542
1603
|
} catch (err) {
|
|
1604
|
+
this.ensureTerminalAgentEvent(channelId, sawAgentStart, sawAgentEnd, onEvent);
|
|
1543
1605
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1544
1606
|
fullText = `\u274C \u5B9A\u65F6\u4EFB\u52A1 "${jobConfig.name}" \u5F02\u5E38\uFF1A${errorMsg}`;
|
|
1545
1607
|
agentFailed = true;
|
|
@@ -1576,10 +1638,19 @@ var init_scheduler = __esm({
|
|
|
1576
1638
|
return { text: fullText, notifyFailed };
|
|
1577
1639
|
}
|
|
1578
1640
|
async clearJobContext(channelId) {
|
|
1641
|
+
console.log(`[Scheduler] Clearing context for ${channelId}`);
|
|
1579
1642
|
const result = await this.agent.handleCommand("clear", channelId);
|
|
1580
1643
|
if (!result.success) {
|
|
1581
1644
|
throw new Error(result.message || `Failed to clear context for ${channelId}`);
|
|
1582
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" });
|
|
1583
1654
|
}
|
|
1584
1655
|
// -------------------------------------------------------------------------
|
|
1585
1656
|
// Dynamic management API
|
|
@@ -1599,10 +1670,11 @@ var init_scheduler = __esm({
|
|
|
1599
1670
|
return { success: false, message: result.message };
|
|
1600
1671
|
}
|
|
1601
1672
|
this.persistJobs();
|
|
1602
|
-
const
|
|
1673
|
+
const recurring = isRecurringJob(jobConfig);
|
|
1674
|
+
const enabled = recurring ? jobConfig.enabled !== false : true;
|
|
1603
1675
|
return {
|
|
1604
1676
|
success: true,
|
|
1605
|
-
message: enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).`
|
|
1677
|
+
message: recurring ? enabled ? `Job "${jobConfig.name}" created and scheduled.` : `Job "${jobConfig.name}" created (disabled).` : `Job "${jobConfig.name}" created as a one-time task.`
|
|
1606
1678
|
};
|
|
1607
1679
|
}
|
|
1608
1680
|
/**
|
|
@@ -1647,17 +1719,15 @@ var init_scheduler = __esm({
|
|
|
1647
1719
|
if (!job) {
|
|
1648
1720
|
return { success: false, message: `Job "${name}" not found.` };
|
|
1649
1721
|
}
|
|
1722
|
+
if (!isRecurringJob(job.config)) {
|
|
1723
|
+
return {
|
|
1724
|
+
success: false,
|
|
1725
|
+
message: `Job "${name}" does not have a schedule and cannot be enabled or disabled.`
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1650
1728
|
job.config.enabled = enabled;
|
|
1651
1729
|
if (enabled && !job.task) {
|
|
1652
|
-
job.task =
|
|
1653
|
-
job.config.cron,
|
|
1654
|
-
() => {
|
|
1655
|
-
void this.runJob(job.config);
|
|
1656
|
-
},
|
|
1657
|
-
{
|
|
1658
|
-
timezone: job.config.timezone
|
|
1659
|
-
}
|
|
1660
|
-
);
|
|
1730
|
+
job.task = this.createCronTask(job.config);
|
|
1661
1731
|
} else if (enabled && job.task) {
|
|
1662
1732
|
job.task.start();
|
|
1663
1733
|
} else if (!enabled && job.task) {
|
|
@@ -1703,13 +1773,13 @@ var init_scheduler = __esm({
|
|
|
1703
1773
|
for (const [, job] of this.jobs) {
|
|
1704
1774
|
result.push({
|
|
1705
1775
|
name: job.config.name,
|
|
1706
|
-
cron: job.config.cron,
|
|
1776
|
+
...job.config.cron ? { cron: job.config.cron } : {},
|
|
1707
1777
|
prompt: job.config.prompt,
|
|
1708
1778
|
notify: job.config.notify,
|
|
1709
|
-
enabled: job.config.enabled !== false,
|
|
1710
|
-
timezone: job.config.timezone,
|
|
1711
|
-
lastRunAt: job.lastRunAt,
|
|
1712
|
-
lastError: job.lastError,
|
|
1779
|
+
enabled: isRecurringJob(job.config) ? job.config.enabled !== false : true,
|
|
1780
|
+
...job.config.timezone ? { timezone: job.config.timezone } : {},
|
|
1781
|
+
...job.lastRunAt ? { lastRunAt: job.lastRunAt } : {},
|
|
1782
|
+
...job.lastError ? { lastError: job.lastError } : {},
|
|
1713
1783
|
running: job.running,
|
|
1714
1784
|
notifyFailed: job.notifyFailed
|
|
1715
1785
|
});
|
|
@@ -1749,6 +1819,21 @@ var init_scheduler = __esm({
|
|
|
1749
1819
|
this.jobs.clear();
|
|
1750
1820
|
console.log("[SchedulerAdapter] All jobs stopped.");
|
|
1751
1821
|
}
|
|
1822
|
+
createCronTask(jobConfig) {
|
|
1823
|
+
const cronExpr = normalizeJobCron(jobConfig.cron);
|
|
1824
|
+
if (!cronExpr) {
|
|
1825
|
+
throw new Error(`Job "${jobConfig.name}" does not have a valid cron expression`);
|
|
1826
|
+
}
|
|
1827
|
+
return cron.schedule(
|
|
1828
|
+
cronExpr,
|
|
1829
|
+
() => {
|
|
1830
|
+
void this.runJob(jobConfig);
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
timezone: jobConfig.timezone
|
|
1834
|
+
}
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1752
1837
|
};
|
|
1753
1838
|
}
|
|
1754
1839
|
});
|
|
@@ -2430,22 +2515,22 @@ async function interactiveCreate(workDir) {
|
|
|
2430
2515
|
}
|
|
2431
2516
|
|
|
2432
2517
|
// src/commands/run.ts
|
|
2433
|
-
import
|
|
2434
|
-
import
|
|
2518
|
+
import path17 from "path";
|
|
2519
|
+
import fs17 from "fs";
|
|
2435
2520
|
import inquirer2 from "inquirer";
|
|
2436
2521
|
import chalk4 from "chalk";
|
|
2437
2522
|
|
|
2438
2523
|
// src/runtime/server.ts
|
|
2439
2524
|
import express from "express";
|
|
2440
|
-
import
|
|
2441
|
-
import
|
|
2525
|
+
import path16 from "path";
|
|
2526
|
+
import fs16 from "fs";
|
|
2442
2527
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2443
2528
|
import { createServer } from "http";
|
|
2444
2529
|
import { exec } from "child_process";
|
|
2445
2530
|
|
|
2446
2531
|
// src/runtime/agent.ts
|
|
2447
|
-
import
|
|
2448
|
-
import
|
|
2532
|
+
import path11 from "path";
|
|
2533
|
+
import fs10 from "fs";
|
|
2449
2534
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2450
2535
|
import { fileURLToPath } from "url";
|
|
2451
2536
|
import {
|
|
@@ -2676,40 +2761,238 @@ var ConfigFileAuthBackend = class {
|
|
|
2676
2761
|
// src/runtime/agent.ts
|
|
2677
2762
|
init_attachment_utils();
|
|
2678
2763
|
|
|
2679
|
-
// src/runtime/
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
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
|
+
}
|
|
2683
2777
|
}
|
|
2684
|
-
const normalized = Math.floor(limit);
|
|
2685
|
-
return Math.max(1, Math.min(normalized, max));
|
|
2686
2778
|
}
|
|
2687
|
-
function
|
|
2688
|
-
if (!
|
|
2689
|
-
|
|
2779
|
+
function assertToolResult(value) {
|
|
2780
|
+
if (!isObject(value) || !Array.isArray(value.content)) {
|
|
2781
|
+
throw new Error("Invalid delegated custom tool result");
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
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
|
+
}
|
|
2840
|
+
});
|
|
2690
2841
|
}
|
|
2691
|
-
|
|
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
|
+
}
|
|
2869
|
+
};
|
|
2870
|
+
|
|
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
|
+
};
|
|
2692
2877
|
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
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
|
+
}
|
|
2702
2959
|
});
|
|
2703
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
|
+
}
|
|
2704
2988
|
};
|
|
2705
2989
|
|
|
2706
|
-
// src/runtime/
|
|
2707
|
-
import
|
|
2708
|
-
import fs9 from "fs";
|
|
2990
|
+
// src/runtime/tools/send-file-tool.ts
|
|
2991
|
+
import fs8 from "fs";
|
|
2709
2992
|
import path9 from "path";
|
|
2993
|
+
import { Type } from "@sinclair/typebox";
|
|
2710
2994
|
|
|
2711
2995
|
// src/runtime/files/metadata.ts
|
|
2712
|
-
import fs8 from "fs";
|
|
2713
2996
|
import path8 from "path";
|
|
2714
2997
|
var MIME_BY_EXT = {
|
|
2715
2998
|
".png": "image/png",
|
|
@@ -2739,419 +3022,14 @@ function isWithinDirectory(parentDir, targetPath) {
|
|
|
2739
3022
|
const relativePath = path8.relative(path8.resolve(parentDir), path8.resolve(targetPath));
|
|
2740
3023
|
return relativePath !== ".." && !relativePath.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relativePath);
|
|
2741
3024
|
}
|
|
2742
|
-
function toPackRelativePath(rootDir, filePath) {
|
|
2743
|
-
const resolvedRoot = path8.resolve(rootDir);
|
|
2744
|
-
const resolvedFile = path8.resolve(filePath);
|
|
2745
|
-
if (!isWithinDirectory(resolvedRoot, resolvedFile)) {
|
|
2746
|
-
throw new Error(`Path is outside the pack root: ${resolvedFile}`);
|
|
2747
|
-
}
|
|
2748
|
-
return path8.relative(resolvedRoot, resolvedFile).split(path8.sep).join("/");
|
|
2749
|
-
}
|
|
2750
|
-
function resolvePackFile(rootDir, filePath) {
|
|
2751
|
-
if (!path8.isAbsolute(filePath)) {
|
|
2752
|
-
throw new Error(`filePath must be absolute: ${filePath}`);
|
|
2753
|
-
}
|
|
2754
|
-
const resolvedPath = path8.resolve(filePath);
|
|
2755
|
-
if (!isWithinDirectory(rootDir, resolvedPath)) {
|
|
2756
|
-
throw new Error(`File is outside the pack root: ${resolvedPath}`);
|
|
2757
|
-
}
|
|
2758
|
-
if (!fs8.existsSync(resolvedPath)) {
|
|
2759
|
-
throw new Error(`File not found: ${resolvedPath}`);
|
|
2760
|
-
}
|
|
2761
|
-
const stats = fs8.statSync(resolvedPath);
|
|
2762
|
-
if (!stats.isFile()) {
|
|
2763
|
-
throw new Error(`Path is not a file: ${resolvedPath}`);
|
|
2764
|
-
}
|
|
2765
|
-
fs8.accessSync(resolvedPath, fs8.constants.R_OK);
|
|
2766
|
-
return {
|
|
2767
|
-
resolvedPath,
|
|
2768
|
-
fileName: path8.basename(resolvedPath),
|
|
2769
|
-
mimeType: detectMimeType(resolvedPath),
|
|
2770
|
-
sizeBytes: stats.size
|
|
2771
|
-
};
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
|
-
// src/runtime/artifacts/snapshot-service.ts
|
|
2775
|
-
function sanitizeFileName(fileName) {
|
|
2776
|
-
const sanitized = fileName.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
2777
|
-
return sanitized || "artifact";
|
|
2778
|
-
}
|
|
2779
|
-
function formatSnapshotStamp(isoDate) {
|
|
2780
|
-
const normalized = isoDate.replace(/\D+/g, "").slice(0, 14);
|
|
2781
|
-
return normalized || String(Date.now());
|
|
2782
|
-
}
|
|
2783
|
-
var ArtifactSnapshotService = class {
|
|
2784
|
-
constructor(rootDir) {
|
|
2785
|
-
this.rootDir = rootDir;
|
|
2786
|
-
}
|
|
2787
|
-
createSnapshots(runId, artifacts, declaredAt) {
|
|
2788
|
-
if (artifacts.length === 0) {
|
|
2789
|
-
return [];
|
|
2790
|
-
}
|
|
2791
|
-
const artifactsRoot = path9.resolve(this.rootDir, "data", "artifacts");
|
|
2792
|
-
const runDir = path9.join(artifactsRoot, runId);
|
|
2793
|
-
const tempDir = path9.join(
|
|
2794
|
-
artifactsRoot,
|
|
2795
|
-
`.tmp-${runId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
2796
|
-
);
|
|
2797
|
-
const snapshots = [];
|
|
2798
|
-
const movedPaths = [];
|
|
2799
|
-
const snapshotNames = artifacts.map((artifact) => [
|
|
2800
|
-
formatSnapshotStamp(declaredAt),
|
|
2801
|
-
randomUUID(),
|
|
2802
|
-
sanitizeFileName(artifact.fileName)
|
|
2803
|
-
].join("-"));
|
|
2804
|
-
fs9.mkdirSync(artifactsRoot, { recursive: true });
|
|
2805
|
-
fs9.mkdirSync(tempDir, { recursive: true });
|
|
2806
|
-
try {
|
|
2807
|
-
snapshotNames.forEach((snapshotName, index) => {
|
|
2808
|
-
fs9.copyFileSync(
|
|
2809
|
-
artifacts[index].filePath,
|
|
2810
|
-
path9.join(tempDir, snapshotName)
|
|
2811
|
-
);
|
|
2812
|
-
});
|
|
2813
|
-
fs9.mkdirSync(runDir, { recursive: true });
|
|
2814
|
-
snapshotNames.forEach((snapshotName, index) => {
|
|
2815
|
-
const artifact = artifacts[index];
|
|
2816
|
-
const tempSnapshotPath = path9.join(tempDir, snapshotName);
|
|
2817
|
-
const finalSnapshotPath = path9.join(runDir, snapshotName);
|
|
2818
|
-
fs9.renameSync(tempSnapshotPath, finalSnapshotPath);
|
|
2819
|
-
movedPaths.push(finalSnapshotPath);
|
|
2820
|
-
snapshots.push({
|
|
2821
|
-
declaredAt,
|
|
2822
|
-
originalPath: toPackRelativePath(this.rootDir, artifact.filePath),
|
|
2823
|
-
snapshotPath: path9.join("data", "artifacts", runId, snapshotName).split(path9.sep).join("/"),
|
|
2824
|
-
fileName: artifact.fileName,
|
|
2825
|
-
mimeType: artifact.mimeType,
|
|
2826
|
-
sizeBytes: artifact.sizeBytes,
|
|
2827
|
-
title: artifact.title,
|
|
2828
|
-
isPrimary: artifact.isPrimary
|
|
2829
|
-
});
|
|
2830
|
-
});
|
|
2831
|
-
fs9.rmSync(tempDir, { recursive: true, force: true });
|
|
2832
|
-
return snapshots;
|
|
2833
|
-
} catch (error) {
|
|
2834
|
-
fs9.rmSync(tempDir, { recursive: true, force: true });
|
|
2835
|
-
movedPaths.forEach((filePath) => fs9.rmSync(filePath, { force: true }));
|
|
2836
|
-
this.removeEmptyRunDirectory(runDir);
|
|
2837
|
-
throw error;
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
removeSnapshots(snapshotPaths) {
|
|
2841
|
-
const visitedRunDirs = /* @__PURE__ */ new Set();
|
|
2842
|
-
for (const snapshotPath of snapshotPaths) {
|
|
2843
|
-
const resolvedPath = path9.resolve(this.rootDir, snapshotPath);
|
|
2844
|
-
fs9.rmSync(resolvedPath, { force: true });
|
|
2845
|
-
visitedRunDirs.add(path9.dirname(resolvedPath));
|
|
2846
|
-
}
|
|
2847
|
-
visitedRunDirs.forEach((runDir) => this.removeEmptyRunDirectory(runDir));
|
|
2848
|
-
}
|
|
2849
|
-
removeEmptyRunDirectory(runDir) {
|
|
2850
|
-
try {
|
|
2851
|
-
if (!fs9.existsSync(runDir)) {
|
|
2852
|
-
return;
|
|
2853
|
-
}
|
|
2854
|
-
if (fs9.readdirSync(runDir).length === 0) {
|
|
2855
|
-
fs9.rmdirSync(runDir);
|
|
2856
|
-
}
|
|
2857
|
-
} catch {
|
|
2858
|
-
}
|
|
2859
|
-
}
|
|
2860
|
-
};
|
|
2861
|
-
|
|
2862
|
-
// src/runtime/artifacts/persistence-service.ts
|
|
2863
|
-
var ArtifactPersistenceService = class {
|
|
2864
|
-
constructor(snapshotService, resultStore) {
|
|
2865
|
-
this.snapshotService = snapshotService;
|
|
2866
|
-
this.resultStore = resultStore;
|
|
2867
|
-
}
|
|
2868
|
-
async saveArtifacts(input) {
|
|
2869
|
-
const declaredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2870
|
-
const snapshots = this.snapshotService.createSnapshots(
|
|
2871
|
-
input.runId,
|
|
2872
|
-
input.artifacts,
|
|
2873
|
-
declaredAt
|
|
2874
|
-
);
|
|
2875
|
-
try {
|
|
2876
|
-
await this.resultStore.insertArtifacts({
|
|
2877
|
-
runId: input.runId,
|
|
2878
|
-
channelId: input.channelId,
|
|
2879
|
-
artifacts: snapshots
|
|
2880
|
-
});
|
|
2881
|
-
return snapshots.length;
|
|
2882
|
-
} catch (error) {
|
|
2883
|
-
this.snapshotService.removeSnapshots(
|
|
2884
|
-
snapshots.map((artifact) => artifact.snapshotPath)
|
|
2885
|
-
);
|
|
2886
|
-
throw error;
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
};
|
|
2890
|
-
|
|
2891
|
-
// src/runtime/artifacts/store.ts
|
|
2892
|
-
import fs10 from "fs";
|
|
2893
|
-
import path10 from "path";
|
|
2894
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
2895
|
-
import sqlite3 from "sqlite3";
|
|
2896
|
-
function mapArtifactRow(row) {
|
|
2897
|
-
return {
|
|
2898
|
-
artifactId: row.artifact_id,
|
|
2899
|
-
runId: row.run_id,
|
|
2900
|
-
channelId: row.channel_id,
|
|
2901
|
-
originalPath: row.original_path,
|
|
2902
|
-
snapshotPath: row.snapshot_path,
|
|
2903
|
-
fileName: row.file_name,
|
|
2904
|
-
mimeType: row.mime_type,
|
|
2905
|
-
sizeBytes: row.size_bytes,
|
|
2906
|
-
title: row.title,
|
|
2907
|
-
isPrimary: row.is_primary === 1,
|
|
2908
|
-
declaredAt: row.declared_at
|
|
2909
|
-
};
|
|
2910
|
-
}
|
|
2911
|
-
var ResultStore = class {
|
|
2912
|
-
db = null;
|
|
2913
|
-
ready;
|
|
2914
|
-
constructor(rootDir) {
|
|
2915
|
-
const dataDir = path10.resolve(rootDir, "data");
|
|
2916
|
-
fs10.mkdirSync(dataDir, { recursive: true });
|
|
2917
|
-
this.ready = this.initialize(path10.join(dataDir, "result-v2.db"));
|
|
2918
|
-
}
|
|
2919
|
-
async initialize(databasePath) {
|
|
2920
|
-
this.db = await openDatabase(databasePath);
|
|
2921
|
-
await this.exec("PRAGMA journal_mode = WAL");
|
|
2922
|
-
await this.exec(`
|
|
2923
|
-
CREATE TABLE IF NOT EXISTS artifacts (
|
|
2924
|
-
artifact_id TEXT PRIMARY KEY,
|
|
2925
|
-
run_id TEXT NOT NULL,
|
|
2926
|
-
channel_id TEXT NOT NULL,
|
|
2927
|
-
original_path TEXT NOT NULL,
|
|
2928
|
-
snapshot_path TEXT NOT NULL,
|
|
2929
|
-
file_name TEXT NOT NULL,
|
|
2930
|
-
mime_type TEXT,
|
|
2931
|
-
size_bytes INTEGER NOT NULL,
|
|
2932
|
-
title TEXT,
|
|
2933
|
-
is_primary INTEGER NOT NULL DEFAULT 0,
|
|
2934
|
-
declared_at TEXT NOT NULL
|
|
2935
|
-
);
|
|
2936
|
-
|
|
2937
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_channel_declared_at
|
|
2938
|
-
ON artifacts(channel_id, declared_at DESC);
|
|
2939
|
-
`);
|
|
2940
|
-
}
|
|
2941
|
-
async insertArtifacts(input) {
|
|
2942
|
-
await this.ready;
|
|
2943
|
-
if (input.artifacts.length === 0) {
|
|
2944
|
-
return;
|
|
2945
|
-
}
|
|
2946
|
-
const insertArtifact = `
|
|
2947
|
-
INSERT INTO artifacts (
|
|
2948
|
-
artifact_id,
|
|
2949
|
-
run_id,
|
|
2950
|
-
channel_id,
|
|
2951
|
-
original_path,
|
|
2952
|
-
snapshot_path,
|
|
2953
|
-
file_name,
|
|
2954
|
-
mime_type,
|
|
2955
|
-
size_bytes,
|
|
2956
|
-
title,
|
|
2957
|
-
is_primary,
|
|
2958
|
-
declared_at
|
|
2959
|
-
) VALUES (
|
|
2960
|
-
?,
|
|
2961
|
-
?,
|
|
2962
|
-
?,
|
|
2963
|
-
?,
|
|
2964
|
-
?,
|
|
2965
|
-
?,
|
|
2966
|
-
?,
|
|
2967
|
-
?,
|
|
2968
|
-
?,
|
|
2969
|
-
?,
|
|
2970
|
-
?
|
|
2971
|
-
)
|
|
2972
|
-
`;
|
|
2973
|
-
await this.exec("BEGIN");
|
|
2974
|
-
try {
|
|
2975
|
-
for (const artifact of input.artifacts) {
|
|
2976
|
-
await this.run(insertArtifact, [
|
|
2977
|
-
randomUUID2(),
|
|
2978
|
-
input.runId,
|
|
2979
|
-
input.channelId,
|
|
2980
|
-
artifact.originalPath,
|
|
2981
|
-
artifact.snapshotPath,
|
|
2982
|
-
artifact.fileName,
|
|
2983
|
-
artifact.mimeType ?? null,
|
|
2984
|
-
artifact.sizeBytes,
|
|
2985
|
-
artifact.title ?? null,
|
|
2986
|
-
artifact.isPrimary ? 1 : 0,
|
|
2987
|
-
artifact.declaredAt
|
|
2988
|
-
]);
|
|
2989
|
-
}
|
|
2990
|
-
await this.exec("COMMIT");
|
|
2991
|
-
} catch (error) {
|
|
2992
|
-
await this.rollback();
|
|
2993
|
-
throw error;
|
|
2994
|
-
}
|
|
2995
|
-
}
|
|
2996
|
-
async listRecentArtifacts(options = {}) {
|
|
2997
|
-
await this.ready;
|
|
2998
|
-
const limit = options.limit ?? 100;
|
|
2999
|
-
const offset = options.offset ?? 0;
|
|
3000
|
-
const conditions = [];
|
|
3001
|
-
const params = [];
|
|
3002
|
-
if (options.channelId) {
|
|
3003
|
-
conditions.push("channel_id = ?");
|
|
3004
|
-
params.push(options.channelId);
|
|
3005
|
-
}
|
|
3006
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3007
|
-
const rows = await this.all(`
|
|
3008
|
-
SELECT *
|
|
3009
|
-
FROM artifacts
|
|
3010
|
-
${whereClause}
|
|
3011
|
-
ORDER BY declared_at DESC, rowid DESC
|
|
3012
|
-
LIMIT ? OFFSET ?
|
|
3013
|
-
`, [...params, limit, offset]);
|
|
3014
|
-
return rows.map(mapArtifactRow);
|
|
3015
|
-
}
|
|
3016
|
-
getDatabase() {
|
|
3017
|
-
if (!this.db) {
|
|
3018
|
-
throw new Error("Result store database is not ready");
|
|
3019
|
-
}
|
|
3020
|
-
return this.db;
|
|
3021
|
-
}
|
|
3022
|
-
exec(sql) {
|
|
3023
|
-
const db = this.getDatabase();
|
|
3024
|
-
return new Promise((resolve, reject) => {
|
|
3025
|
-
db.exec(sql, (error) => {
|
|
3026
|
-
if (error) {
|
|
3027
|
-
reject(error);
|
|
3028
|
-
return;
|
|
3029
|
-
}
|
|
3030
|
-
resolve();
|
|
3031
|
-
});
|
|
3032
|
-
});
|
|
3033
|
-
}
|
|
3034
|
-
run(sql, params = []) {
|
|
3035
|
-
const db = this.getDatabase();
|
|
3036
|
-
return new Promise((resolve, reject) => {
|
|
3037
|
-
db.run(sql, params, (error) => {
|
|
3038
|
-
if (error) {
|
|
3039
|
-
reject(error);
|
|
3040
|
-
return;
|
|
3041
|
-
}
|
|
3042
|
-
resolve();
|
|
3043
|
-
});
|
|
3044
|
-
});
|
|
3045
|
-
}
|
|
3046
|
-
all(sql, params = []) {
|
|
3047
|
-
const db = this.getDatabase();
|
|
3048
|
-
return new Promise((resolve, reject) => {
|
|
3049
|
-
db.all(sql, params, (error, rows) => {
|
|
3050
|
-
if (error) {
|
|
3051
|
-
reject(error);
|
|
3052
|
-
return;
|
|
3053
|
-
}
|
|
3054
|
-
resolve(rows);
|
|
3055
|
-
});
|
|
3056
|
-
});
|
|
3057
|
-
}
|
|
3058
|
-
async rollback() {
|
|
3059
|
-
try {
|
|
3060
|
-
await this.exec("ROLLBACK");
|
|
3061
|
-
} catch {
|
|
3062
|
-
}
|
|
3063
|
-
}
|
|
3064
|
-
};
|
|
3065
|
-
function openDatabase(databasePath) {
|
|
3066
|
-
return new Promise((resolve, reject) => {
|
|
3067
|
-
const db = new sqlite3.Database(databasePath, (error) => {
|
|
3068
|
-
if (error) {
|
|
3069
|
-
reject(error);
|
|
3070
|
-
return;
|
|
3071
|
-
}
|
|
3072
|
-
resolve(db);
|
|
3073
|
-
});
|
|
3074
|
-
});
|
|
3075
|
-
}
|
|
3076
|
-
|
|
3077
|
-
// src/runtime/artifacts/save-artifacts-tool.ts
|
|
3078
|
-
import { Type } from "@sinclair/typebox";
|
|
3079
|
-
var ArtifactItem = Type.Object({
|
|
3080
|
-
filePath: Type.String({
|
|
3081
|
-
description: "Absolute path to the artifact file. The file must exist, be readable, and be inside the current pack root."
|
|
3082
|
-
}),
|
|
3083
|
-
title: Type.Optional(
|
|
3084
|
-
Type.String({
|
|
3085
|
-
description: "Optional short title shown in the dashboard."
|
|
3086
|
-
})
|
|
3087
|
-
),
|
|
3088
|
-
isPrimary: Type.Optional(
|
|
3089
|
-
Type.Boolean({
|
|
3090
|
-
description: "Mark this artifact as a primary output."
|
|
3091
|
-
})
|
|
3092
|
-
)
|
|
3093
|
-
});
|
|
3094
|
-
var SaveArtifactsParams = Type.Object({
|
|
3095
|
-
artifacts: Type.Array(ArtifactItem, {
|
|
3096
|
-
minItems: 1,
|
|
3097
|
-
description: "The artifact files to save for this run."
|
|
3098
|
-
})
|
|
3099
|
-
});
|
|
3100
|
-
function textResult(text) {
|
|
3101
|
-
return { content: [{ type: "text", text }], details: void 0 };
|
|
3102
|
-
}
|
|
3103
|
-
function normalizeOptionalText(value) {
|
|
3104
|
-
const trimmed = value?.trim();
|
|
3105
|
-
return trimmed ? trimmed : void 0;
|
|
3106
|
-
}
|
|
3107
|
-
function createSaveArtifactsTool(rootDir, saveCallbackRef) {
|
|
3108
|
-
return {
|
|
3109
|
-
name: "save_artifacts",
|
|
3110
|
-
label: "Save Artifacts",
|
|
3111
|
-
description: [
|
|
3112
|
-
"Save the final output files produced by this run.",
|
|
3113
|
-
"Always use this for user-facing deliverables that are part of the final result.",
|
|
3114
|
-
"Do not use this for intermediate, temporary, draft, or scratch files.",
|
|
3115
|
-
"Each filePath must be an absolute path inside the current pack root."
|
|
3116
|
-
].join("\n"),
|
|
3117
|
-
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.",
|
|
3118
|
-
promptGuidelines: [
|
|
3119
|
-
"Whenever you create a final result file for the user, call `save_artifacts` before finishing the response.",
|
|
3120
|
-
"Do not call `save_artifacts` for intermediate, temporary, draft, or scratch files."
|
|
3121
|
-
],
|
|
3122
|
-
parameters: SaveArtifactsParams,
|
|
3123
|
-
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3124
|
-
const saveArtifacts = saveCallbackRef.current;
|
|
3125
|
-
if (!saveArtifacts) {
|
|
3126
|
-
throw new Error("Artifact saving is not available for this run.");
|
|
3127
|
-
}
|
|
3128
|
-
const artifacts = params.artifacts.map((artifact) => {
|
|
3129
|
-
const metadata = resolvePackFile(rootDir, artifact.filePath);
|
|
3130
|
-
return {
|
|
3131
|
-
filePath: metadata.resolvedPath,
|
|
3132
|
-
fileName: metadata.fileName,
|
|
3133
|
-
mimeType: metadata.mimeType,
|
|
3134
|
-
sizeBytes: metadata.sizeBytes,
|
|
3135
|
-
title: normalizeOptionalText(artifact.title),
|
|
3136
|
-
isPrimary: artifact.isPrimary === true
|
|
3137
|
-
};
|
|
3138
|
-
});
|
|
3139
|
-
const savedCount = await saveArtifacts(artifacts);
|
|
3140
|
-
return textResult(`Saved ${savedCount} artifact(s).`);
|
|
3141
|
-
}
|
|
3142
|
-
};
|
|
3143
|
-
}
|
|
3144
3025
|
|
|
3145
3026
|
// src/runtime/tools/send-file-tool.ts
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
import { Type as Type2 } from "@sinclair/typebox";
|
|
3149
|
-
var SendFileParams = Type2.Object({
|
|
3150
|
-
filePath: Type2.String({
|
|
3027
|
+
var SendFileParams = Type.Object({
|
|
3028
|
+
filePath: Type.String({
|
|
3151
3029
|
description: "Absolute path to the file to send to the user. The file must exist and be readable."
|
|
3152
3030
|
}),
|
|
3153
|
-
caption:
|
|
3154
|
-
|
|
3031
|
+
caption: Type.Optional(
|
|
3032
|
+
Type.String({
|
|
3155
3033
|
description: "Optional caption or description to accompany the file."
|
|
3156
3034
|
})
|
|
3157
3035
|
)
|
|
@@ -3165,13 +3043,13 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3165
3043
|
parameters: SendFileParams,
|
|
3166
3044
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3167
3045
|
const { filePath, caption } = params;
|
|
3168
|
-
if (!
|
|
3046
|
+
if (!fs8.existsSync(filePath)) {
|
|
3169
3047
|
return {
|
|
3170
3048
|
content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
|
|
3171
3049
|
details: void 0
|
|
3172
3050
|
};
|
|
3173
3051
|
}
|
|
3174
|
-
const stats =
|
|
3052
|
+
const stats = fs8.statSync(filePath);
|
|
3175
3053
|
if (!stats.isFile()) {
|
|
3176
3054
|
return {
|
|
3177
3055
|
content: [
|
|
@@ -3180,7 +3058,7 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3180
3058
|
details: void 0
|
|
3181
3059
|
};
|
|
3182
3060
|
}
|
|
3183
|
-
const filename =
|
|
3061
|
+
const filename = path9.basename(filePath);
|
|
3184
3062
|
const mimeType = detectMimeType(filePath);
|
|
3185
3063
|
const callback = fileOutputCallbackRef.current;
|
|
3186
3064
|
if (callback) {
|
|
@@ -3207,51 +3085,51 @@ function createSendFileTool(fileOutputCallbackRef) {
|
|
|
3207
3085
|
}
|
|
3208
3086
|
|
|
3209
3087
|
// src/runtime/tools/manage-schedule-tool.ts
|
|
3210
|
-
import { Type as
|
|
3211
|
-
var ManageScheduleParams =
|
|
3212
|
-
action:
|
|
3088
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
3089
|
+
var ManageScheduleParams = Type2.Object({
|
|
3090
|
+
action: Type2.Union(
|
|
3213
3091
|
[
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3092
|
+
Type2.Literal("add"),
|
|
3093
|
+
Type2.Literal("list"),
|
|
3094
|
+
Type2.Literal("remove"),
|
|
3095
|
+
Type2.Literal("trigger"),
|
|
3096
|
+
Type2.Literal("enable"),
|
|
3097
|
+
Type2.Literal("disable")
|
|
3220
3098
|
],
|
|
3221
3099
|
{ description: "The action to perform." }
|
|
3222
3100
|
),
|
|
3223
|
-
name:
|
|
3224
|
-
|
|
3101
|
+
name: Type2.Optional(
|
|
3102
|
+
Type2.String({
|
|
3225
3103
|
description: "Unique name for the scheduled task. Required for add/remove/trigger/enable/disable."
|
|
3226
3104
|
})
|
|
3227
3105
|
),
|
|
3228
|
-
cron:
|
|
3229
|
-
|
|
3106
|
+
cron: Type2.Optional(
|
|
3107
|
+
Type2.String({
|
|
3230
3108
|
description: "Cron expression (5 fields: minute hour day month weekday). Required for add."
|
|
3231
3109
|
})
|
|
3232
3110
|
),
|
|
3233
|
-
prompt:
|
|
3234
|
-
|
|
3111
|
+
prompt: Type2.Optional(
|
|
3112
|
+
Type2.String({
|
|
3235
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."
|
|
3236
3114
|
})
|
|
3237
3115
|
),
|
|
3238
|
-
timezone:
|
|
3239
|
-
|
|
3116
|
+
timezone: Type2.Optional(
|
|
3117
|
+
Type2.String({
|
|
3240
3118
|
description: "Optional timezone for the cron schedule, e.g. 'Asia/Shanghai', 'America/New_York'."
|
|
3241
3119
|
})
|
|
3242
3120
|
),
|
|
3243
|
-
notifyAdapter:
|
|
3244
|
-
|
|
3121
|
+
notifyAdapter: Type2.Optional(
|
|
3122
|
+
Type2.String({
|
|
3245
3123
|
description: "Optional target adapter for notifications. If omitted, the current chat is used when supported (Telegram, Slack, or Web)."
|
|
3246
3124
|
})
|
|
3247
3125
|
),
|
|
3248
|
-
notifyChannelId:
|
|
3249
|
-
|
|
3126
|
+
notifyChannelId: Type2.Optional(
|
|
3127
|
+
Type2.String({
|
|
3250
3128
|
description: "Optional target channelId for notifications. Must be provided together with notifyAdapter when overriding the default target."
|
|
3251
3129
|
})
|
|
3252
3130
|
)
|
|
3253
3131
|
});
|
|
3254
|
-
function
|
|
3132
|
+
function textResult(text) {
|
|
3255
3133
|
return { content: [{ type: "text", text }], details: void 0 };
|
|
3256
3134
|
}
|
|
3257
3135
|
function getDefaultNotifyTarget(adapter, channelId) {
|
|
@@ -3291,7 +3169,7 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
|
|
|
3291
3169
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
3292
3170
|
const scheduler = schedulerRef.current;
|
|
3293
3171
|
if (!scheduler) {
|
|
3294
|
-
return
|
|
3172
|
+
return textResult(
|
|
3295
3173
|
"Error: Scheduler is not available. The scheduled task system may not be initialized."
|
|
3296
3174
|
);
|
|
3297
3175
|
}
|
|
@@ -3299,24 +3177,24 @@ function createManageScheduleTool(schedulerRef, adapter, channelId) {
|
|
|
3299
3177
|
case "list": {
|
|
3300
3178
|
const jobs = scheduler.listJobs();
|
|
3301
3179
|
if (jobs.length === 0) {
|
|
3302
|
-
return
|
|
3180
|
+
return textResult("No scheduled tasks configured.");
|
|
3303
3181
|
}
|
|
3304
3182
|
const lines = jobs.map(
|
|
3305
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})` : ""}`
|
|
3306
3184
|
);
|
|
3307
|
-
return
|
|
3185
|
+
return textResult(
|
|
3308
3186
|
`Scheduled tasks (${jobs.length}):
|
|
3309
3187
|
${lines.join("\n")}`
|
|
3310
3188
|
);
|
|
3311
3189
|
}
|
|
3312
3190
|
case "add": {
|
|
3313
3191
|
if (!params.name || !params.cron || !params.prompt) {
|
|
3314
|
-
return
|
|
3192
|
+
return textResult(
|
|
3315
3193
|
"Error: 'name', 'cron', and 'prompt' are required for adding a task."
|
|
3316
3194
|
);
|
|
3317
3195
|
}
|
|
3318
3196
|
if (params.notifyAdapter && !params.notifyChannelId || !params.notifyAdapter && params.notifyChannelId) {
|
|
3319
|
-
return
|
|
3197
|
+
return textResult(
|
|
3320
3198
|
"Error: 'notifyAdapter' and 'notifyChannelId' must be provided together when overriding the notification target."
|
|
3321
3199
|
);
|
|
3322
3200
|
}
|
|
@@ -3325,7 +3203,7 @@ ${lines.join("\n")}`
|
|
|
3325
3203
|
channelId: params.notifyChannelId
|
|
3326
3204
|
} : getDefaultNotifyTarget(adapter, channelId);
|
|
3327
3205
|
if (!notify) {
|
|
3328
|
-
return
|
|
3206
|
+
return textResult(
|
|
3329
3207
|
"Error: No default notification target is available for this chat. Provide 'notifyAdapter' and 'notifyChannelId'."
|
|
3330
3208
|
);
|
|
3331
3209
|
}
|
|
@@ -3338,46 +3216,46 @@ ${lines.join("\n")}`
|
|
|
3338
3216
|
timezone: params.timezone
|
|
3339
3217
|
};
|
|
3340
3218
|
const result = scheduler.addJob(jobConfig);
|
|
3341
|
-
return
|
|
3219
|
+
return textResult(result.message);
|
|
3342
3220
|
}
|
|
3343
3221
|
case "remove": {
|
|
3344
3222
|
if (!params.name) {
|
|
3345
|
-
return
|
|
3223
|
+
return textResult(
|
|
3346
3224
|
"Error: 'name' is required for removing a task."
|
|
3347
3225
|
);
|
|
3348
3226
|
}
|
|
3349
3227
|
const result = scheduler.removeJob(params.name);
|
|
3350
|
-
return
|
|
3228
|
+
return textResult(result.message);
|
|
3351
3229
|
}
|
|
3352
3230
|
case "trigger": {
|
|
3353
3231
|
if (!params.name) {
|
|
3354
|
-
return
|
|
3232
|
+
return textResult(
|
|
3355
3233
|
"Error: 'name' is required for triggering a task."
|
|
3356
3234
|
);
|
|
3357
3235
|
}
|
|
3358
3236
|
const result = await scheduler.triggerJob(params.name);
|
|
3359
|
-
return
|
|
3237
|
+
return textResult(result.message);
|
|
3360
3238
|
}
|
|
3361
3239
|
case "enable": {
|
|
3362
3240
|
if (!params.name) {
|
|
3363
|
-
return
|
|
3241
|
+
return textResult(
|
|
3364
3242
|
"Error: 'name' is required for enabling a task."
|
|
3365
3243
|
);
|
|
3366
3244
|
}
|
|
3367
3245
|
const result = scheduler.setEnabled(params.name, true);
|
|
3368
|
-
return
|
|
3246
|
+
return textResult(result.message);
|
|
3369
3247
|
}
|
|
3370
3248
|
case "disable": {
|
|
3371
3249
|
if (!params.name) {
|
|
3372
|
-
return
|
|
3250
|
+
return textResult(
|
|
3373
3251
|
"Error: 'name' is required for disabling a task."
|
|
3374
3252
|
);
|
|
3375
3253
|
}
|
|
3376
3254
|
const result = scheduler.setEnabled(params.name, false);
|
|
3377
|
-
return
|
|
3255
|
+
return textResult(result.message);
|
|
3378
3256
|
}
|
|
3379
3257
|
default:
|
|
3380
|
-
return
|
|
3258
|
+
return textResult(
|
|
3381
3259
|
`Error: Unknown action '${params.action}'. Use: add, list, remove, trigger, enable, disable.`
|
|
3382
3260
|
);
|
|
3383
3261
|
}
|
|
@@ -3387,8 +3265,8 @@ ${lines.join("\n")}`
|
|
|
3387
3265
|
|
|
3388
3266
|
// src/runtime/commands/help-command.ts
|
|
3389
3267
|
init_commands();
|
|
3390
|
-
import
|
|
3391
|
-
import
|
|
3268
|
+
import fs9 from "fs";
|
|
3269
|
+
import path10 from "path";
|
|
3392
3270
|
function buildHelpMessage(rootDir) {
|
|
3393
3271
|
const sections = [];
|
|
3394
3272
|
const commands = getVisibleCommands();
|
|
@@ -3398,7 +3276,7 @@ function buildHelpMessage(rootDir) {
|
|
|
3398
3276
|
sections.push(`\u{1F4CB} **Available Commands**
|
|
3399
3277
|
|
|
3400
3278
|
${commandLines.join("\n")}`);
|
|
3401
|
-
const configPath =
|
|
3279
|
+
const configPath = path10.resolve(rootDir, "skillpack.json");
|
|
3402
3280
|
const skills = readInstalledSkills(configPath);
|
|
3403
3281
|
if (skills.length > 0) {
|
|
3404
3282
|
const skillLines = skills.map(
|
|
@@ -3434,11 +3312,11 @@ function handleHelpCommand(rootDir) {
|
|
|
3434
3312
|
};
|
|
3435
3313
|
}
|
|
3436
3314
|
function readInstalledSkills(configPath) {
|
|
3437
|
-
if (!
|
|
3315
|
+
if (!fs9.existsSync(configPath)) {
|
|
3438
3316
|
return [];
|
|
3439
3317
|
}
|
|
3440
3318
|
try {
|
|
3441
|
-
const raw =
|
|
3319
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
3442
3320
|
const config = JSON.parse(raw);
|
|
3443
3321
|
return Array.isArray(config.skills) ? config.skills : [];
|
|
3444
3322
|
} catch {
|
|
@@ -3458,24 +3336,24 @@ var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
|
3458
3336
|
var PACK_AGENTS_FILE = "AGENTS.md";
|
|
3459
3337
|
var PACK_SOUL_FILE = "SOUL.md";
|
|
3460
3338
|
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
3461
|
-
if (!
|
|
3339
|
+
if (!fs10.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
3462
3340
|
log(
|
|
3463
3341
|
`[PackAgent] Built-in skill-creator template missing: ${BUILTIN_SKILL_CREATOR_TEMPLATE_DIR}`
|
|
3464
3342
|
);
|
|
3465
3343
|
return null;
|
|
3466
3344
|
}
|
|
3467
|
-
const packConfigPath =
|
|
3468
|
-
const skillDir =
|
|
3469
|
-
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");
|
|
3470
3348
|
const renderTemplate = (content) => content.replaceAll("{{SKILLS_PATH}}", skillsPath).replaceAll("{{PACK_CONFIG_PATH}}", packConfigPath);
|
|
3471
3349
|
const copyDir = (srcDir, destDir) => {
|
|
3472
|
-
|
|
3473
|
-
for (const entry of
|
|
3350
|
+
fs10.mkdirSync(destDir, { recursive: true });
|
|
3351
|
+
for (const entry of fs10.readdirSync(srcDir, { withFileTypes: true })) {
|
|
3474
3352
|
if (entry.name === ".DS_Store") {
|
|
3475
3353
|
continue;
|
|
3476
3354
|
}
|
|
3477
|
-
const srcPath =
|
|
3478
|
-
const destPath =
|
|
3355
|
+
const srcPath = path11.join(srcDir, entry.name);
|
|
3356
|
+
const destPath = path11.join(destDir, entry.name);
|
|
3479
3357
|
if (entry.isDirectory()) {
|
|
3480
3358
|
copyDir(srcPath, destPath);
|
|
3481
3359
|
continue;
|
|
@@ -3484,17 +3362,17 @@ function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
|
3484
3362
|
continue;
|
|
3485
3363
|
}
|
|
3486
3364
|
if (entry.name.endsWith(".md") || entry.name.endsWith(".py")) {
|
|
3487
|
-
const content =
|
|
3488
|
-
|
|
3365
|
+
const content = fs10.readFileSync(srcPath, "utf-8");
|
|
3366
|
+
fs10.writeFileSync(destPath, renderTemplate(content), "utf-8");
|
|
3489
3367
|
continue;
|
|
3490
3368
|
}
|
|
3491
|
-
|
|
3369
|
+
fs10.copyFileSync(srcPath, destPath);
|
|
3492
3370
|
}
|
|
3493
3371
|
};
|
|
3494
|
-
if (!
|
|
3372
|
+
if (!fs10.existsSync(skillDir)) {
|
|
3495
3373
|
copyDir(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR, skillDir);
|
|
3496
3374
|
}
|
|
3497
|
-
if (!
|
|
3375
|
+
if (!fs10.existsSync(skillPath)) {
|
|
3498
3376
|
log(
|
|
3499
3377
|
`[PackAgent] Materialized built-in skill-creator but SKILL.md is missing: ${skillPath}`
|
|
3500
3378
|
);
|
|
@@ -3522,11 +3400,11 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
|
3522
3400
|
};
|
|
3523
3401
|
}
|
|
3524
3402
|
function readOptionalPackPromptFile(filePath) {
|
|
3525
|
-
if (!
|
|
3403
|
+
if (!fs10.existsSync(filePath)) {
|
|
3526
3404
|
return void 0;
|
|
3527
3405
|
}
|
|
3528
3406
|
try {
|
|
3529
|
-
const content =
|
|
3407
|
+
const content = fs10.readFileSync(filePath, "utf-8").trim();
|
|
3530
3408
|
return content.length > 0 ? content : void 0;
|
|
3531
3409
|
} catch (error) {
|
|
3532
3410
|
console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
|
|
@@ -3534,8 +3412,8 @@ function readOptionalPackPromptFile(filePath) {
|
|
|
3534
3412
|
}
|
|
3535
3413
|
}
|
|
3536
3414
|
function buildPackPromptBlock(rootDir) {
|
|
3537
|
-
const agentsPath =
|
|
3538
|
-
const soulPath =
|
|
3415
|
+
const agentsPath = path11.resolve(rootDir, PACK_AGENTS_FILE);
|
|
3416
|
+
const soulPath = path11.resolve(rootDir, PACK_SOUL_FILE);
|
|
3539
3417
|
const agentsContent = readOptionalPackPromptFile(agentsPath);
|
|
3540
3418
|
const soulContent = readOptionalPackPromptFile(soulPath);
|
|
3541
3419
|
if (!agentsContent && !soulContent) {
|
|
@@ -3594,13 +3472,15 @@ var PackAgent = class {
|
|
|
3594
3472
|
options;
|
|
3595
3473
|
channels = /* @__PURE__ */ new Map();
|
|
3596
3474
|
pendingSessionCreations = /* @__PURE__ */ new Map();
|
|
3597
|
-
schedulerRef = {
|
|
3475
|
+
schedulerRef = {
|
|
3476
|
+
current: null
|
|
3477
|
+
};
|
|
3598
3478
|
authStorage;
|
|
3599
|
-
|
|
3479
|
+
delegatedCustomToolClient = new DelegatedCustomToolClient();
|
|
3480
|
+
hostIpcClient = new HostIpcClient();
|
|
3600
3481
|
constructor(options) {
|
|
3601
3482
|
this.options = options;
|
|
3602
|
-
|
|
3603
|
-
const configPath = path13.resolve(options.rootDir, "data", "config.json");
|
|
3483
|
+
const configPath = path11.resolve(options.rootDir, "data", "config.json");
|
|
3604
3484
|
const backend = new ConfigFileAuthBackend(configPath);
|
|
3605
3485
|
this.authStorage = AuthStorage.fromStorage(backend);
|
|
3606
3486
|
const providerMeta = SUPPORTED_PROVIDERS[options.provider];
|
|
@@ -3627,13 +3507,20 @@ var PackAgent = class {
|
|
|
3627
3507
|
setScheduler(scheduler) {
|
|
3628
3508
|
this.schedulerRef.current = scheduler;
|
|
3629
3509
|
}
|
|
3630
|
-
createCustomTools(adapter, channelId, fileOutputCallbackRef,
|
|
3510
|
+
async createCustomTools(adapter, channelId, fileOutputCallbackRef, delegatedToolRunContextRef) {
|
|
3511
|
+
const delegatedDefinitions = await this.delegatedCustomToolClient.listDefinitions();
|
|
3631
3512
|
const tools = [
|
|
3632
3513
|
createSendFileTool(fileOutputCallbackRef),
|
|
3633
|
-
|
|
3514
|
+
...createDelegatedCustomTools(
|
|
3515
|
+
delegatedDefinitions,
|
|
3516
|
+
this.delegatedCustomToolClient,
|
|
3517
|
+
delegatedToolRunContextRef
|
|
3518
|
+
)
|
|
3634
3519
|
];
|
|
3635
3520
|
if (adapter !== "scheduler") {
|
|
3636
|
-
tools.push(
|
|
3521
|
+
tools.push(
|
|
3522
|
+
createManageScheduleTool(this.schedulerRef, adapter, channelId)
|
|
3523
|
+
);
|
|
3637
3524
|
}
|
|
3638
3525
|
return tools;
|
|
3639
3526
|
}
|
|
@@ -3651,7 +3538,9 @@ var PackAgent = class {
|
|
|
3651
3538
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
3652
3539
|
if (baseUrl && modelId) {
|
|
3653
3540
|
const apiProtocol = this.options.apiProtocol ?? "openai-completions";
|
|
3654
|
-
log(
|
|
3541
|
+
log(
|
|
3542
|
+
`[PackAgent] Registering custom model ${provider}/${modelId} api=${apiProtocol} baseUrl=${baseUrl}`
|
|
3543
|
+
);
|
|
3655
3544
|
modelRegistry.registerProvider(provider, {
|
|
3656
3545
|
baseUrl,
|
|
3657
3546
|
apiKey: this.options.apiKey,
|
|
@@ -3672,26 +3561,23 @@ var PackAgent = class {
|
|
|
3672
3561
|
const resolvedModel = modelRegistry.find(provider, modelId);
|
|
3673
3562
|
const model = resolvedModel && baseUrl && !this.options.apiProtocol ? { ...resolvedModel, baseUrl } : resolvedModel;
|
|
3674
3563
|
if (resolvedModel && baseUrl) {
|
|
3675
|
-
log(
|
|
3564
|
+
log(
|
|
3565
|
+
`[PackAgent] Resolved ${provider}/${modelId} api=${resolvedModel.api} baseUrl=${baseUrl}`
|
|
3566
|
+
);
|
|
3676
3567
|
}
|
|
3677
|
-
const sessionDir =
|
|
3678
|
-
|
|
3679
|
-
"data",
|
|
3680
|
-
"sessions",
|
|
3681
|
-
channelId
|
|
3682
|
-
);
|
|
3683
|
-
fs13.mkdirSync(sessionDir, { recursive: true });
|
|
3568
|
+
const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
|
|
3569
|
+
fs10.mkdirSync(sessionDir, { recursive: true });
|
|
3684
3570
|
const sessionManager = SessionManager.continueRecent(rootDir, sessionDir);
|
|
3685
3571
|
log(`[PackAgent] Session dir: ${sessionDir}`);
|
|
3686
|
-
const workspaceDir =
|
|
3572
|
+
const workspaceDir = path11.resolve(
|
|
3687
3573
|
rootDir,
|
|
3688
3574
|
"data",
|
|
3689
3575
|
"workspaces",
|
|
3690
3576
|
channelId
|
|
3691
3577
|
);
|
|
3692
|
-
|
|
3578
|
+
fs10.mkdirSync(workspaceDir, { recursive: true });
|
|
3693
3579
|
log(`[PackAgent] Workspace dir: ${workspaceDir}`);
|
|
3694
|
-
const skillsPath =
|
|
3580
|
+
const skillsPath = path11.resolve(rootDir, "skills");
|
|
3695
3581
|
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
3696
3582
|
const materializedSkillCreator = materializeBuiltinSkillCreator(
|
|
3697
3583
|
rootDir,
|
|
@@ -3704,14 +3590,22 @@ var PackAgent = class {
|
|
|
3704
3590
|
}
|
|
3705
3591
|
const packPromptFiles = buildPackPromptBlock(rootDir);
|
|
3706
3592
|
if (packPromptFiles.agentsContent) {
|
|
3707
|
-
log(
|
|
3593
|
+
log(
|
|
3594
|
+
`[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`
|
|
3595
|
+
);
|
|
3708
3596
|
} else {
|
|
3709
|
-
log(
|
|
3597
|
+
log(
|
|
3598
|
+
`[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`
|
|
3599
|
+
);
|
|
3710
3600
|
}
|
|
3711
3601
|
if (packPromptFiles.soulContent) {
|
|
3712
|
-
log(
|
|
3602
|
+
log(
|
|
3603
|
+
`[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`
|
|
3604
|
+
);
|
|
3713
3605
|
} else {
|
|
3714
|
-
log(
|
|
3606
|
+
log(
|
|
3607
|
+
`[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`
|
|
3608
|
+
);
|
|
3715
3609
|
}
|
|
3716
3610
|
log(
|
|
3717
3611
|
`[PackAgent] Pack prompt injection: ${packPromptFiles.promptBlock ? "enabled" : "disabled"}`
|
|
@@ -3729,14 +3623,14 @@ var PackAgent = class {
|
|
|
3729
3623
|
const fileOutputCallbackRef = {
|
|
3730
3624
|
current: null
|
|
3731
3625
|
};
|
|
3732
|
-
const
|
|
3626
|
+
const delegatedToolRunContextRef = {
|
|
3733
3627
|
current: null
|
|
3734
3628
|
};
|
|
3735
|
-
const customTools = this.createCustomTools(
|
|
3629
|
+
const customTools = await this.createCustomTools(
|
|
3736
3630
|
adapter,
|
|
3737
3631
|
channelId,
|
|
3738
3632
|
fileOutputCallbackRef,
|
|
3739
|
-
|
|
3633
|
+
delegatedToolRunContextRef
|
|
3740
3634
|
);
|
|
3741
3635
|
const { session } = await createAgentSession({
|
|
3742
3636
|
cwd: workspaceDir,
|
|
@@ -3753,7 +3647,7 @@ var PackAgent = class {
|
|
|
3753
3647
|
running: false,
|
|
3754
3648
|
pending: Promise.resolve(),
|
|
3755
3649
|
fileOutputCallbackRef,
|
|
3756
|
-
|
|
3650
|
+
delegatedToolRunContextRef
|
|
3757
3651
|
};
|
|
3758
3652
|
this.channels.set(channelId, channelSession);
|
|
3759
3653
|
return channelSession;
|
|
@@ -3776,12 +3670,10 @@ var PackAgent = class {
|
|
|
3776
3670
|
cs.fileOutputCallbackRef.current = (event) => {
|
|
3777
3671
|
onEvent(event);
|
|
3778
3672
|
};
|
|
3779
|
-
cs.
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
artifacts
|
|
3784
|
-
});
|
|
3673
|
+
cs.delegatedToolRunContextRef.current = {
|
|
3674
|
+
runId,
|
|
3675
|
+
channelId,
|
|
3676
|
+
adapter
|
|
3785
3677
|
};
|
|
3786
3678
|
unsubscribe = cs.session.subscribe((event) => {
|
|
3787
3679
|
switch (event.type) {
|
|
@@ -3797,7 +3689,10 @@ var PackAgent = class {
|
|
|
3797
3689
|
if (event.message?.role === "user") {
|
|
3798
3690
|
log(JSON.stringify(event.message.content, null, 2));
|
|
3799
3691
|
}
|
|
3800
|
-
onEvent({
|
|
3692
|
+
onEvent({
|
|
3693
|
+
type: "message_start",
|
|
3694
|
+
role: event.message?.role ?? ""
|
|
3695
|
+
});
|
|
3801
3696
|
break;
|
|
3802
3697
|
case "message_update":
|
|
3803
3698
|
if (event.assistantMessageEvent?.type === "text_delta") {
|
|
@@ -3864,18 +3759,26 @@ var PackAgent = class {
|
|
|
3864
3759
|
let promptText = text;
|
|
3865
3760
|
const promptOptions = {};
|
|
3866
3761
|
if (attachments && attachments.length > 0) {
|
|
3867
|
-
const imageAttachments = attachments.filter(
|
|
3868
|
-
|
|
3762
|
+
const imageAttachments = attachments.filter(
|
|
3763
|
+
(a) => isImageMime(a.mimeType)
|
|
3764
|
+
);
|
|
3765
|
+
const nonImageAttachments = attachments.filter(
|
|
3766
|
+
(a) => !isImageMime(a.mimeType)
|
|
3767
|
+
);
|
|
3869
3768
|
if (imageAttachments.length > 0) {
|
|
3870
3769
|
promptOptions.images = attachmentsToImageContent(imageAttachments);
|
|
3871
|
-
log(
|
|
3770
|
+
log(
|
|
3771
|
+
`[PackAgent] Passing ${imageAttachments.length} image(s) to LLM`
|
|
3772
|
+
);
|
|
3872
3773
|
}
|
|
3873
3774
|
if (nonImageAttachments.length > 0) {
|
|
3874
3775
|
const attachmentPrompt = formatAttachmentsPrompt(nonImageAttachments);
|
|
3875
3776
|
promptText = `${attachmentPrompt}
|
|
3876
3777
|
|
|
3877
3778
|
${text}`;
|
|
3878
|
-
log(
|
|
3779
|
+
log(
|
|
3780
|
+
`[PackAgent] Injecting ${nonImageAttachments.length} non-image attachment(s) into prompt`
|
|
3781
|
+
);
|
|
3879
3782
|
}
|
|
3880
3783
|
}
|
|
3881
3784
|
await cs.session.prompt(promptText, promptOptions);
|
|
@@ -3898,12 +3801,15 @@ ${text}`;
|
|
|
3898
3801
|
} finally {
|
|
3899
3802
|
cs.running = false;
|
|
3900
3803
|
cs.fileOutputCallbackRef.current = null;
|
|
3901
|
-
cs.
|
|
3804
|
+
cs.delegatedToolRunContextRef.current = null;
|
|
3902
3805
|
unsubscribe();
|
|
3903
3806
|
}
|
|
3904
3807
|
};
|
|
3905
3808
|
const resultPromise = cs.pending.catch(() => void 0).then(run);
|
|
3906
|
-
cs.pending = resultPromise.then(
|
|
3809
|
+
cs.pending = resultPromise.then(
|
|
3810
|
+
() => void 0,
|
|
3811
|
+
() => void 0
|
|
3812
|
+
);
|
|
3907
3813
|
return resultPromise;
|
|
3908
3814
|
}
|
|
3909
3815
|
async handleCommand(command, channelId) {
|
|
@@ -3918,11 +3824,12 @@ ${text}`;
|
|
|
3918
3824
|
this.channels.delete(channelId);
|
|
3919
3825
|
}
|
|
3920
3826
|
const { rootDir } = this.options;
|
|
3921
|
-
const sessionDir =
|
|
3922
|
-
if (
|
|
3923
|
-
|
|
3827
|
+
const sessionDir = path11.resolve(rootDir, "data", "sessions", channelId);
|
|
3828
|
+
if (fs10.existsSync(sessionDir)) {
|
|
3829
|
+
fs10.rmSync(sessionDir, { recursive: true, force: true });
|
|
3924
3830
|
log(`[PackAgent] Cleared session dir: ${sessionDir}`);
|
|
3925
3831
|
}
|
|
3832
|
+
await this.hostIpcClient.notifyChannelSessionCleared({ channelId });
|
|
3926
3833
|
return {
|
|
3927
3834
|
success: true,
|
|
3928
3835
|
message: command === "new" ? "New session started." : "Session cleared."
|
|
@@ -3971,14 +3878,14 @@ ${text}`;
|
|
|
3971
3878
|
};
|
|
3972
3879
|
|
|
3973
3880
|
// src/runtime/adapters/web.ts
|
|
3974
|
-
import
|
|
3975
|
-
import
|
|
3881
|
+
import fs12 from "fs";
|
|
3882
|
+
import path13 from "path";
|
|
3976
3883
|
import { WebSocketServer } from "ws";
|
|
3977
3884
|
init_commands();
|
|
3978
3885
|
|
|
3979
3886
|
// src/runtime/services/conversation.ts
|
|
3980
|
-
import
|
|
3981
|
-
import
|
|
3887
|
+
import fs11 from "fs";
|
|
3888
|
+
import path12 from "path";
|
|
3982
3889
|
import {
|
|
3983
3890
|
parseSessionEntries
|
|
3984
3891
|
} from "@mariozechner/pi-coding-agent";
|
|
@@ -3996,17 +3903,17 @@ var ConversationService = class {
|
|
|
3996
3903
|
includeLegacyWeb = true,
|
|
3997
3904
|
allowedPlatforms
|
|
3998
3905
|
} = options;
|
|
3999
|
-
const sessionsDir =
|
|
3906
|
+
const sessionsDir = path12.resolve(this.rootDir, "data", "sessions");
|
|
4000
3907
|
const channelIds = new Set(activeChannels);
|
|
4001
3908
|
const allowedPlatformSet = allowedPlatforms ? new Set(allowedPlatforms) : null;
|
|
4002
3909
|
if (includeDefaultWeb) {
|
|
4003
3910
|
channelIds.add(DEFAULT_WEB_CHANNEL_ID);
|
|
4004
3911
|
}
|
|
4005
|
-
if (
|
|
4006
|
-
for (const entry of
|
|
4007
|
-
const channelDir =
|
|
3912
|
+
if (fs11.existsSync(sessionsDir)) {
|
|
3913
|
+
for (const entry of fs11.readdirSync(sessionsDir)) {
|
|
3914
|
+
const channelDir = path12.join(sessionsDir, entry);
|
|
4008
3915
|
try {
|
|
4009
|
-
if (!
|
|
3916
|
+
if (!fs11.statSync(channelDir).isDirectory()) {
|
|
4010
3917
|
continue;
|
|
4011
3918
|
}
|
|
4012
3919
|
const platform = this.detectPlatform(entry);
|
|
@@ -4030,7 +3937,7 @@ var ConversationService = class {
|
|
|
4030
3937
|
if (!includeLegacyWeb && this.isLegacyWebConversation(channelId)) {
|
|
4031
3938
|
continue;
|
|
4032
3939
|
}
|
|
4033
|
-
const channelDir =
|
|
3940
|
+
const channelDir = path12.join(sessionsDir, channelId);
|
|
4034
3941
|
const sessionFile = this.findLatestSessionFile(channelDir);
|
|
4035
3942
|
let messageCount = 0;
|
|
4036
3943
|
let lastMessageAt = "";
|
|
@@ -4074,7 +3981,7 @@ var ConversationService = class {
|
|
|
4074
3981
|
* Load latest messages for a channel in a simplified format.
|
|
4075
3982
|
*/
|
|
4076
3983
|
getMessages(channelId, limit = 100) {
|
|
4077
|
-
const channelDir =
|
|
3984
|
+
const channelDir = path12.resolve(
|
|
4078
3985
|
this.rootDir,
|
|
4079
3986
|
"data",
|
|
4080
3987
|
"sessions",
|
|
@@ -4163,20 +4070,20 @@ var ConversationService = class {
|
|
|
4163
4070
|
return messages.slice(-safeLimit);
|
|
4164
4071
|
}
|
|
4165
4072
|
findLatestSessionFile(channelDir) {
|
|
4166
|
-
if (!
|
|
4073
|
+
if (!fs11.existsSync(channelDir)) return null;
|
|
4167
4074
|
let stats;
|
|
4168
4075
|
try {
|
|
4169
|
-
stats =
|
|
4076
|
+
stats = fs11.statSync(channelDir);
|
|
4170
4077
|
} catch {
|
|
4171
4078
|
return null;
|
|
4172
4079
|
}
|
|
4173
4080
|
if (!stats.isDirectory()) return null;
|
|
4174
|
-
const files =
|
|
4175
|
-
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;
|
|
4176
4083
|
}
|
|
4177
4084
|
loadEntries(filePath) {
|
|
4178
4085
|
try {
|
|
4179
|
-
const content =
|
|
4086
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
4180
4087
|
const fileEntries = parseSessionEntries(content);
|
|
4181
4088
|
return fileEntries.filter(
|
|
4182
4089
|
(entry) => entry.type !== "session"
|
|
@@ -4372,7 +4279,7 @@ var ConversationService = class {
|
|
|
4372
4279
|
|
|
4373
4280
|
// src/runtime/adapters/web.ts
|
|
4374
4281
|
function getPackConfig(rootDir) {
|
|
4375
|
-
const raw =
|
|
4282
|
+
const raw = fs12.readFileSync(path13.join(rootDir, "skillpack.json"), "utf-8");
|
|
4376
4283
|
return JSON.parse(raw);
|
|
4377
4284
|
}
|
|
4378
4285
|
function parseCommand(text) {
|
|
@@ -4402,7 +4309,7 @@ function parsePositiveInt(value, fallback) {
|
|
|
4402
4309
|
return Math.floor(parsed);
|
|
4403
4310
|
}
|
|
4404
4311
|
function resolveDownloadFilePath(rootDir, filePath) {
|
|
4405
|
-
const resolvedPath =
|
|
4312
|
+
const resolvedPath = path13.isAbsolute(filePath) ? path13.resolve(filePath) : path13.resolve(rootDir, filePath);
|
|
4406
4313
|
return isWithinDirectory(rootDir, resolvedPath) ? resolvedPath : null;
|
|
4407
4314
|
}
|
|
4408
4315
|
var WebAdapter = class {
|
|
@@ -4417,7 +4324,6 @@ var WebAdapter = class {
|
|
|
4417
4324
|
this.agent = agent;
|
|
4418
4325
|
this.ipcBroadcaster = ctx.ipcBroadcaster ?? null;
|
|
4419
4326
|
this.conversationService = new ConversationService(rootDir);
|
|
4420
|
-
const resultsQueryService = ctx.resultsQueryService ?? null;
|
|
4421
4327
|
const currentConf = configManager.getConfig();
|
|
4422
4328
|
let apiKey = currentConf.apiKey || "";
|
|
4423
4329
|
let currentProvider = currentConf.provider || "openai";
|
|
@@ -4561,17 +4467,6 @@ var WebAdapter = class {
|
|
|
4561
4467
|
)
|
|
4562
4468
|
);
|
|
4563
4469
|
});
|
|
4564
|
-
app.get("/api/results/artifacts", async (req, res) => {
|
|
4565
|
-
if (!resultsQueryService) {
|
|
4566
|
-
res.status(503).json({ error: "Results query service is not available" });
|
|
4567
|
-
return;
|
|
4568
|
-
}
|
|
4569
|
-
res.json(await resultsQueryService.listRecentArtifacts({
|
|
4570
|
-
channelId: typeof req.query.channelId === "string" ? req.query.channelId : void 0,
|
|
4571
|
-
limit: parsePositiveInt(req.query.limit, 100),
|
|
4572
|
-
offset: parsePositiveInt(req.query.offset, 0)
|
|
4573
|
-
}));
|
|
4574
|
-
});
|
|
4575
4470
|
app.get("/api/files", (req, res) => {
|
|
4576
4471
|
const filePath = req.query.path;
|
|
4577
4472
|
if (!filePath) {
|
|
@@ -4583,17 +4478,17 @@ var WebAdapter = class {
|
|
|
4583
4478
|
res.status(403).json({ error: "Access denied" });
|
|
4584
4479
|
return;
|
|
4585
4480
|
}
|
|
4586
|
-
if (!
|
|
4481
|
+
if (!fs12.existsSync(resolvedPath)) {
|
|
4587
4482
|
res.status(404).json({ error: "File not found" });
|
|
4588
4483
|
return;
|
|
4589
4484
|
}
|
|
4590
|
-
const filename =
|
|
4485
|
+
const filename = path13.basename(resolvedPath);
|
|
4591
4486
|
res.setHeader("Content-Type", "application/octet-stream");
|
|
4592
4487
|
res.setHeader(
|
|
4593
4488
|
"Content-Disposition",
|
|
4594
4489
|
`attachment; filename="${filename}"`
|
|
4595
4490
|
);
|
|
4596
|
-
|
|
4491
|
+
fs12.createReadStream(resolvedPath).pipe(res);
|
|
4597
4492
|
});
|
|
4598
4493
|
const getScheduler = () => {
|
|
4599
4494
|
const schedulerAdapter = ctx.adapterMap?.get("scheduler");
|
|
@@ -4615,20 +4510,20 @@ var WebAdapter = class {
|
|
|
4615
4510
|
return;
|
|
4616
4511
|
}
|
|
4617
4512
|
const { name, cron: cronExpr, prompt, notify, enabled, timezone } = req.body;
|
|
4618
|
-
if (!name || !
|
|
4513
|
+
if (!name || !prompt || !notify?.adapter || !notify?.channelId) {
|
|
4619
4514
|
res.status(400).json({
|
|
4620
4515
|
success: false,
|
|
4621
|
-
message: "Required fields: name,
|
|
4516
|
+
message: "Required fields: name, prompt, notify.adapter, notify.channelId"
|
|
4622
4517
|
});
|
|
4623
4518
|
return;
|
|
4624
4519
|
}
|
|
4625
4520
|
const result = scheduler.addJob({
|
|
4626
4521
|
name,
|
|
4627
|
-
cron: cronExpr,
|
|
4522
|
+
...typeof cronExpr === "string" ? { cron: cronExpr } : {},
|
|
4628
4523
|
prompt,
|
|
4629
4524
|
notify,
|
|
4630
|
-
enabled
|
|
4631
|
-
timezone
|
|
4525
|
+
...typeof enabled === "boolean" ? { enabled } : {},
|
|
4526
|
+
...typeof timezone === "string" ? { timezone } : {}
|
|
4632
4527
|
});
|
|
4633
4528
|
res.json(result);
|
|
4634
4529
|
});
|
|
@@ -4778,13 +4673,28 @@ var WebAdapter = class {
|
|
|
4778
4673
|
|
|
4779
4674
|
// src/runtime/adapters/ipc.ts
|
|
4780
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
|
+
]);
|
|
4781
4692
|
var IpcAdapter = class {
|
|
4782
4693
|
name = "ipc";
|
|
4783
4694
|
agent = null;
|
|
4784
4695
|
rootDir = "";
|
|
4785
4696
|
adapterMap = null;
|
|
4786
4697
|
conversationService = null;
|
|
4787
|
-
resultsQueryService = null;
|
|
4788
4698
|
createdChannels = /* @__PURE__ */ new Set();
|
|
4789
4699
|
messageListener;
|
|
4790
4700
|
started = false;
|
|
@@ -4796,7 +4706,6 @@ var IpcAdapter = class {
|
|
|
4796
4706
|
this.rootDir = ctx.rootDir;
|
|
4797
4707
|
this.adapterMap = ctx.adapterMap ?? null;
|
|
4798
4708
|
this.conversationService = new ConversationService(ctx.rootDir);
|
|
4799
|
-
this.resultsQueryService = ctx.resultsQueryService ?? null;
|
|
4800
4709
|
this.messageListener = (message) => {
|
|
4801
4710
|
if (!this.isIpcRequest(message)) return;
|
|
4802
4711
|
void this.handleRequest(message);
|
|
@@ -4841,7 +4750,7 @@ var IpcAdapter = class {
|
|
|
4841
4750
|
isIpcRequest(message) {
|
|
4842
4751
|
if (!message || typeof message !== "object") return false;
|
|
4843
4752
|
const maybe = message;
|
|
4844
|
-
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);
|
|
4845
4754
|
}
|
|
4846
4755
|
async handleRequest(request) {
|
|
4847
4756
|
if (!this.agent || !this.conversationService) {
|
|
@@ -4880,18 +4789,6 @@ var IpcAdapter = class {
|
|
|
4880
4789
|
this.reply(request.id, messages);
|
|
4881
4790
|
return;
|
|
4882
4791
|
}
|
|
4883
|
-
case "get_recent_artifacts": {
|
|
4884
|
-
if (!this.resultsQueryService) {
|
|
4885
|
-
this.replyError(request.id, "Results query service is not available");
|
|
4886
|
-
return;
|
|
4887
|
-
}
|
|
4888
|
-
this.reply(request.id, await this.resultsQueryService.listRecentArtifacts({
|
|
4889
|
-
channelId: request.channelId,
|
|
4890
|
-
limit: request.limit,
|
|
4891
|
-
offset: request.offset
|
|
4892
|
-
}));
|
|
4893
|
-
return;
|
|
4894
|
-
}
|
|
4895
4792
|
case "send_message": {
|
|
4896
4793
|
if (!request.channelId || typeof request.channelId !== "string") {
|
|
4897
4794
|
this.replyError(request.id, "channelId is required");
|
|
@@ -5135,28 +5032,28 @@ var Lifecycle = class {
|
|
|
5135
5032
|
|
|
5136
5033
|
// src/runtime/registry.ts
|
|
5137
5034
|
import crypto from "crypto";
|
|
5138
|
-
import
|
|
5035
|
+
import fs13 from "fs";
|
|
5139
5036
|
import os from "os";
|
|
5140
|
-
import
|
|
5141
|
-
var SKILLPACK_HOME =
|
|
5142
|
-
var LEGACY_REGISTRY_FILE =
|
|
5143
|
-
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");
|
|
5144
5041
|
var migrationChecked = false;
|
|
5145
5042
|
function ensureHomeDir() {
|
|
5146
|
-
if (!
|
|
5147
|
-
|
|
5043
|
+
if (!fs13.existsSync(SKILLPACK_HOME)) {
|
|
5044
|
+
fs13.mkdirSync(SKILLPACK_HOME, { recursive: true });
|
|
5148
5045
|
}
|
|
5149
5046
|
}
|
|
5150
5047
|
function ensureRegistryDir() {
|
|
5151
5048
|
ensureHomeDir();
|
|
5152
|
-
if (!
|
|
5153
|
-
|
|
5049
|
+
if (!fs13.existsSync(REGISTRY_DIR)) {
|
|
5050
|
+
fs13.mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
5154
5051
|
}
|
|
5155
5052
|
}
|
|
5156
5053
|
function canonicalizeDir(dir) {
|
|
5157
|
-
const resolved =
|
|
5054
|
+
const resolved = path14.resolve(dir);
|
|
5158
5055
|
try {
|
|
5159
|
-
return
|
|
5056
|
+
return fs13.realpathSync(resolved);
|
|
5160
5057
|
} catch {
|
|
5161
5058
|
return resolved;
|
|
5162
5059
|
}
|
|
@@ -5165,7 +5062,7 @@ function hashDir(dir) {
|
|
|
5165
5062
|
return crypto.createHash("md5").update(canonicalizeDir(dir)).digest("hex");
|
|
5166
5063
|
}
|
|
5167
5064
|
function getEntryPathForCanonicalDir(dir) {
|
|
5168
|
-
return
|
|
5065
|
+
return path14.join(REGISTRY_DIR, `${hashDir(dir)}.json`);
|
|
5169
5066
|
}
|
|
5170
5067
|
function getEntryPath(dir) {
|
|
5171
5068
|
ensureRegistryReady();
|
|
@@ -5173,11 +5070,11 @@ function getEntryPath(dir) {
|
|
|
5173
5070
|
}
|
|
5174
5071
|
function listEntryFiles() {
|
|
5175
5072
|
ensureRegistryReady();
|
|
5176
|
-
return
|
|
5073
|
+
return fs13.readdirSync(REGISTRY_DIR).filter((file) => file.endsWith(".json")).sort().map((file) => path14.join(REGISTRY_DIR, file));
|
|
5177
5074
|
}
|
|
5178
5075
|
function readEntryFile(filePath) {
|
|
5179
5076
|
try {
|
|
5180
|
-
const raw =
|
|
5077
|
+
const raw = fs13.readFileSync(filePath, "utf-8");
|
|
5181
5078
|
const data = JSON.parse(raw);
|
|
5182
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") {
|
|
5183
5080
|
return null;
|
|
@@ -5210,8 +5107,8 @@ function writeEntryFile(entry) {
|
|
|
5210
5107
|
};
|
|
5211
5108
|
const entryPath = getEntryPathForCanonicalDir(normalized.dir);
|
|
5212
5109
|
const tmpPath = createTmpPath(entryPath);
|
|
5213
|
-
|
|
5214
|
-
|
|
5110
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
5111
|
+
fs13.renameSync(tmpPath, entryPath);
|
|
5215
5112
|
}
|
|
5216
5113
|
function migrateLegacyRegistryIfNeeded() {
|
|
5217
5114
|
if (migrationChecked) {
|
|
@@ -5219,14 +5116,14 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
5219
5116
|
}
|
|
5220
5117
|
migrationChecked = true;
|
|
5221
5118
|
ensureRegistryDir();
|
|
5222
|
-
if (!
|
|
5119
|
+
if (!fs13.existsSync(LEGACY_REGISTRY_FILE)) {
|
|
5223
5120
|
return;
|
|
5224
5121
|
}
|
|
5225
5122
|
if (listEntryFiles().length > 0) {
|
|
5226
5123
|
return;
|
|
5227
5124
|
}
|
|
5228
5125
|
try {
|
|
5229
|
-
const raw =
|
|
5126
|
+
const raw = fs13.readFileSync(LEGACY_REGISTRY_FILE, "utf-8");
|
|
5230
5127
|
const data = JSON.parse(raw);
|
|
5231
5128
|
const packs = Array.isArray(data?.packs) ? data.packs : [];
|
|
5232
5129
|
for (const pack of packs) {
|
|
@@ -5239,7 +5136,7 @@ function migrateLegacyRegistryIfNeeded() {
|
|
|
5239
5136
|
} catch {
|
|
5240
5137
|
}
|
|
5241
5138
|
}
|
|
5242
|
-
|
|
5139
|
+
fs13.renameSync(LEGACY_REGISTRY_FILE, `${LEGACY_REGISTRY_FILE}.legacy`);
|
|
5243
5140
|
} catch (err) {
|
|
5244
5141
|
console.warn(" [Registry] Failed to migrate legacy registry.json:", err);
|
|
5245
5142
|
}
|
|
@@ -5292,7 +5189,7 @@ function deregister(dir, pid) {
|
|
|
5292
5189
|
}
|
|
5293
5190
|
|
|
5294
5191
|
// src/runtime/server.ts
|
|
5295
|
-
var __dirname =
|
|
5192
|
+
var __dirname = path16.dirname(fileURLToPath2(import.meta.url));
|
|
5296
5193
|
async function startServer(options) {
|
|
5297
5194
|
const {
|
|
5298
5195
|
rootDir,
|
|
@@ -5308,8 +5205,8 @@ async function startServer(options) {
|
|
|
5308
5205
|
const baseUrl = dataConfig.baseUrl?.trim() || void 0;
|
|
5309
5206
|
const modelId = dataConfig.modelId?.trim() || (SUPPORTED_PROVIDERS[provider]?.defaultModelId ?? SUPPORTED_PROVIDERS.openai.defaultModelId);
|
|
5310
5207
|
const apiProtocol = dataConfig.apiProtocol;
|
|
5311
|
-
const packageRoot =
|
|
5312
|
-
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");
|
|
5313
5210
|
const app = express();
|
|
5314
5211
|
app.use(express.json());
|
|
5315
5212
|
app.use(express.static(webDir));
|
|
@@ -5327,13 +5224,6 @@ async function startServer(options) {
|
|
|
5327
5224
|
});
|
|
5328
5225
|
});
|
|
5329
5226
|
const lifecycle = new Lifecycle(server);
|
|
5330
|
-
const resultStore = new ResultStore(rootDir);
|
|
5331
|
-
const artifactSnapshotService = new ArtifactSnapshotService(rootDir);
|
|
5332
|
-
const artifactPersistenceService = new ArtifactPersistenceService(
|
|
5333
|
-
artifactSnapshotService,
|
|
5334
|
-
resultStore
|
|
5335
|
-
);
|
|
5336
|
-
const resultsQueryService = new ResultsQueryService(resultStore);
|
|
5337
5227
|
const agent = new PackAgent({
|
|
5338
5228
|
apiKey,
|
|
5339
5229
|
rootDir,
|
|
@@ -5341,14 +5231,16 @@ async function startServer(options) {
|
|
|
5341
5231
|
modelId,
|
|
5342
5232
|
baseUrl,
|
|
5343
5233
|
apiProtocol,
|
|
5344
|
-
lifecycleHandler: lifecycle
|
|
5345
|
-
artifactPersistenceService
|
|
5234
|
+
lifecycleHandler: lifecycle
|
|
5346
5235
|
});
|
|
5347
5236
|
const adapters = [];
|
|
5348
5237
|
const adapterMap = /* @__PURE__ */ new Map();
|
|
5349
5238
|
const hasIpcChannel = typeof process.send === "function";
|
|
5350
5239
|
const ipcAdapter = new IpcAdapter();
|
|
5351
5240
|
const webEnabled = runtimeMode === "standalone";
|
|
5241
|
+
console.log(
|
|
5242
|
+
`[Runtime] IPC channel ${hasIpcChannel ? "available" : "not available"} (mode=${runtimeMode})`
|
|
5243
|
+
);
|
|
5352
5244
|
if (hasIpcChannel) {
|
|
5353
5245
|
await ipcAdapter.start({
|
|
5354
5246
|
agent,
|
|
@@ -5356,8 +5248,7 @@ async function startServer(options) {
|
|
|
5356
5248
|
app,
|
|
5357
5249
|
rootDir,
|
|
5358
5250
|
lifecycle,
|
|
5359
|
-
adapterMap
|
|
5360
|
-
resultsQueryService
|
|
5251
|
+
adapterMap
|
|
5361
5252
|
});
|
|
5362
5253
|
adapters.push(ipcAdapter);
|
|
5363
5254
|
adapterMap.set(ipcAdapter.name, ipcAdapter);
|
|
@@ -5372,8 +5263,7 @@ async function startServer(options) {
|
|
|
5372
5263
|
rootDir,
|
|
5373
5264
|
lifecycle,
|
|
5374
5265
|
adapterMap,
|
|
5375
|
-
ipcBroadcaster
|
|
5376
|
-
resultsQueryService
|
|
5266
|
+
ipcBroadcaster
|
|
5377
5267
|
});
|
|
5378
5268
|
adapters.push(webAdapter);
|
|
5379
5269
|
adapterMap.set(webAdapter.name, webAdapter);
|
|
@@ -5391,8 +5281,7 @@ async function startServer(options) {
|
|
|
5391
5281
|
rootDir,
|
|
5392
5282
|
lifecycle,
|
|
5393
5283
|
adapterMap,
|
|
5394
|
-
ipcBroadcaster
|
|
5395
|
-
resultsQueryService
|
|
5284
|
+
ipcBroadcaster
|
|
5396
5285
|
});
|
|
5397
5286
|
adapters.push(telegramAdapter);
|
|
5398
5287
|
adapterMap.set(telegramAdapter.name, telegramAdapter);
|
|
@@ -5420,8 +5309,7 @@ async function startServer(options) {
|
|
|
5420
5309
|
rootDir,
|
|
5421
5310
|
lifecycle,
|
|
5422
5311
|
adapterMap,
|
|
5423
|
-
ipcBroadcaster
|
|
5424
|
-
resultsQueryService
|
|
5312
|
+
ipcBroadcaster
|
|
5425
5313
|
});
|
|
5426
5314
|
adapters.push(slackAdapter);
|
|
5427
5315
|
adapterMap.set(slackAdapter.name, slackAdapter);
|
|
@@ -5453,7 +5341,7 @@ async function startServer(options) {
|
|
|
5453
5341
|
lifecycle,
|
|
5454
5342
|
notify: notifyFn,
|
|
5455
5343
|
adapterMap,
|
|
5456
|
-
|
|
5344
|
+
ipcBroadcaster
|
|
5457
5345
|
});
|
|
5458
5346
|
adapters.push(schedulerAdapter);
|
|
5459
5347
|
adapterMap.set(schedulerAdapter.name, schedulerAdapter);
|
|
@@ -5541,23 +5429,23 @@ function findMissingSkills(workDir, config) {
|
|
|
5541
5429
|
});
|
|
5542
5430
|
}
|
|
5543
5431
|
function copyStartTemplates2(workDir) {
|
|
5544
|
-
const templateDir =
|
|
5432
|
+
const templateDir = path17.resolve(
|
|
5545
5433
|
new URL("../templates", import.meta.url).pathname
|
|
5546
5434
|
);
|
|
5547
5435
|
for (const file of ["start.sh", "start.bat"]) {
|
|
5548
|
-
const src =
|
|
5549
|
-
const dest =
|
|
5550
|
-
if (
|
|
5551
|
-
|
|
5436
|
+
const src = path17.join(templateDir, file);
|
|
5437
|
+
const dest = path17.join(workDir, file);
|
|
5438
|
+
if (fs17.existsSync(src)) {
|
|
5439
|
+
fs17.copyFileSync(src, dest);
|
|
5552
5440
|
if (file === "start.sh") {
|
|
5553
|
-
|
|
5441
|
+
fs17.chmodSync(dest, 493);
|
|
5554
5442
|
}
|
|
5555
5443
|
}
|
|
5556
5444
|
}
|
|
5557
5445
|
}
|
|
5558
5446
|
async function runCommand(directory) {
|
|
5559
|
-
const workDir = directory ?
|
|
5560
|
-
|
|
5447
|
+
const workDir = directory ? path17.resolve(directory) : process.cwd();
|
|
5448
|
+
fs17.mkdirSync(workDir, { recursive: true });
|
|
5561
5449
|
if (!configExists(workDir)) {
|
|
5562
5450
|
console.log(chalk4.blue("\n No skillpack.json found. Let's set one up.\n"));
|
|
5563
5451
|
const { name, description } = await inquirer2.prompt([
|
|
@@ -5601,14 +5489,14 @@ async function runCommand(directory) {
|
|
|
5601
5489
|
}
|
|
5602
5490
|
|
|
5603
5491
|
// src/cli.ts
|
|
5604
|
-
import
|
|
5605
|
-
import
|
|
5492
|
+
import fs18 from "fs";
|
|
5493
|
+
import path18 from "path";
|
|
5606
5494
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5607
5495
|
var packageJson = JSON.parse(
|
|
5608
|
-
|
|
5496
|
+
fs18.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
5609
5497
|
);
|
|
5610
5498
|
var program = new Command();
|
|
5611
|
-
var cliFilePath =
|
|
5499
|
+
var cliFilePath = path18.resolve(fileURLToPath3(import.meta.url));
|
|
5612
5500
|
program.name("skillpack").description("Assemble, package, and run Agent Skills packs").version(packageJson.version);
|
|
5613
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) => {
|
|
5614
5502
|
await createCommand(directory, options);
|
|
@@ -5629,7 +5517,7 @@ program.command("zip").description("Package the current pack as a zip file (skil
|
|
|
5629
5517
|
function normalizeUserArgs(argv) {
|
|
5630
5518
|
if (argv.length === 0) return argv;
|
|
5631
5519
|
const firstArg = argv[0];
|
|
5632
|
-
if (firstArg &&
|
|
5520
|
+
if (firstArg && path18.resolve(firstArg) === cliFilePath) {
|
|
5633
5521
|
return argv.slice(1);
|
|
5634
5522
|
}
|
|
5635
5523
|
return argv;
|