augure 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +439 -96
- package/package.json +9 -9
package/dist/bin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
|
-
import { createRequire } from "module";
|
|
4
|
+
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
import { defineCommand as defineCommand4, runMain } from "citty";
|
|
6
6
|
|
|
7
7
|
// src/commands/start.ts
|
|
@@ -50,9 +50,12 @@ var AppConfigSchema = z.object({
|
|
|
50
50
|
heartbeatInterval: z.string().min(1),
|
|
51
51
|
jobs: z.array(z.object({
|
|
52
52
|
id: z.string().min(1),
|
|
53
|
-
cron: z.string().min(1),
|
|
53
|
+
cron: z.string().min(1).optional(),
|
|
54
|
+
runAt: z.string().min(1).optional(),
|
|
54
55
|
prompt: z.string().min(1),
|
|
55
56
|
channel: z.string().min(1)
|
|
57
|
+
}).refine((j) => j.cron || j.runAt, {
|
|
58
|
+
message: "Job must have either cron or runAt"
|
|
56
59
|
}))
|
|
57
60
|
}),
|
|
58
61
|
sandbox: z.object({
|
|
@@ -198,11 +201,15 @@ var OpenRouterClient = class {
|
|
|
198
201
|
const choice = data.choices[0];
|
|
199
202
|
return {
|
|
200
203
|
content: choice.message.content ?? "",
|
|
201
|
-
toolCalls: (choice.message.tool_calls ?? []).map((tc) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
toolCalls: (choice.message.tool_calls ?? []).map((tc) => {
|
|
205
|
+
let args = {};
|
|
206
|
+
try {
|
|
207
|
+
args = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
|
|
208
|
+
} catch {
|
|
209
|
+
console.error(`[augure] Failed to parse tool call arguments for ${tc.function.name}:`, tc.function.arguments);
|
|
210
|
+
}
|
|
211
|
+
return { id: tc.id, name: tc.function.name, arguments: args };
|
|
212
|
+
}),
|
|
206
213
|
usage: {
|
|
207
214
|
inputTokens: data.usage.prompt_tokens,
|
|
208
215
|
outputTokens: data.usage.completion_tokens
|
|
@@ -578,62 +585,6 @@ function createOutgoingPipeline(middlewares, send) {
|
|
|
578
585
|
};
|
|
579
586
|
}
|
|
580
587
|
|
|
581
|
-
// ../channels/dist/middleware/escape-markdown.js
|
|
582
|
-
var SPECIAL_CHARS = /* @__PURE__ */ new Set([".", "!", ">", "#", "+", "-", "=", "|", "{", "}", "~"]);
|
|
583
|
-
function escapeMarkdownV2(text) {
|
|
584
|
-
if (!text)
|
|
585
|
-
return "";
|
|
586
|
-
const parts = [];
|
|
587
|
-
let i = 0;
|
|
588
|
-
while (i < text.length) {
|
|
589
|
-
if (text.startsWith("```", i)) {
|
|
590
|
-
const endIdx = text.indexOf("```", i + 3);
|
|
591
|
-
if (endIdx !== -1) {
|
|
592
|
-
parts.push(text.slice(i, endIdx + 3));
|
|
593
|
-
i = endIdx + 3;
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
if (text[i] === "`") {
|
|
598
|
-
const endIdx = text.indexOf("`", i + 1);
|
|
599
|
-
if (endIdx !== -1) {
|
|
600
|
-
parts.push(text.slice(i, endIdx + 1));
|
|
601
|
-
i = endIdx + 1;
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (text[i] === "*" || text[i] === "_") {
|
|
606
|
-
parts.push(text[i]);
|
|
607
|
-
i++;
|
|
608
|
-
continue;
|
|
609
|
-
}
|
|
610
|
-
if (text[i] === "[" || text[i] === "(") {
|
|
611
|
-
parts.push(text[i]);
|
|
612
|
-
i++;
|
|
613
|
-
continue;
|
|
614
|
-
}
|
|
615
|
-
if (text[i] === "]") {
|
|
616
|
-
parts.push(text[i]);
|
|
617
|
-
i++;
|
|
618
|
-
continue;
|
|
619
|
-
}
|
|
620
|
-
const char = text[i];
|
|
621
|
-
if (SPECIAL_CHARS.has(char)) {
|
|
622
|
-
parts.push(`\\${char}`);
|
|
623
|
-
} else {
|
|
624
|
-
parts.push(char);
|
|
625
|
-
}
|
|
626
|
-
i++;
|
|
627
|
-
}
|
|
628
|
-
return parts.join("");
|
|
629
|
-
}
|
|
630
|
-
function createEscapeMarkdownMiddleware() {
|
|
631
|
-
return async (message, next) => {
|
|
632
|
-
message.text = escapeMarkdownV2(message.text);
|
|
633
|
-
await next();
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
|
|
637
588
|
// ../channels/dist/middleware/split-message.js
|
|
638
589
|
var TELEGRAM_MAX = 4096;
|
|
639
590
|
function splitText(text, maxLength) {
|
|
@@ -712,7 +663,8 @@ function createSplitMessageMiddleware(sendFn, maxLength = TELEGRAM_MAX) {
|
|
|
712
663
|
// ../channels/dist/middleware/error-handler.js
|
|
713
664
|
function isRetryable(error) {
|
|
714
665
|
if (error instanceof Error) {
|
|
715
|
-
const
|
|
666
|
+
const err2 = error;
|
|
667
|
+
const status = err2.status ?? err2.error_code;
|
|
716
668
|
if (status === 429 || status !== void 0 && status >= 500)
|
|
717
669
|
return true;
|
|
718
670
|
if (status !== void 0 && status >= 400 && status < 500)
|
|
@@ -738,6 +690,38 @@ async function withRetry(fn, options) {
|
|
|
738
690
|
throw lastError;
|
|
739
691
|
}
|
|
740
692
|
|
|
693
|
+
// ../channels/dist/middleware/markdown-to-html.js
|
|
694
|
+
function markdownToTelegramHtml(text) {
|
|
695
|
+
if (!text)
|
|
696
|
+
return "";
|
|
697
|
+
const placeholders = [];
|
|
698
|
+
const PH_PREFIX = "\uFFFCPH";
|
|
699
|
+
const PH_SUFFIX = "\uFFFC";
|
|
700
|
+
function hold(html) {
|
|
701
|
+
const idx = placeholders.length;
|
|
702
|
+
placeholders.push(html);
|
|
703
|
+
return `${PH_PREFIX}${idx}${PH_SUFFIX}`;
|
|
704
|
+
}
|
|
705
|
+
let out = text;
|
|
706
|
+
out = out.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => hold(lang ? `<pre><code class="language-${escapeHtml(lang)}">${escapeHtml(code.trimEnd())}</code></pre>` : `<pre>${escapeHtml(code.trimEnd())}</pre>`));
|
|
707
|
+
out = out.replace(/`([^`\n]+)`/g, (_, code) => hold(`<code>${escapeHtml(code)}</code>`));
|
|
708
|
+
out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, linkText, url) => hold(`<a href="${escapeHtml(url)}">${escapeHtml(linkText)}</a>`));
|
|
709
|
+
out = escapeHtml(out);
|
|
710
|
+
out = out.replace(/\*\*\*(.+?)\*\*\*/g, "<b><i>$1</i></b>");
|
|
711
|
+
out = out.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
712
|
+
out = out.replace(/\*([^*\n]+?)\*/g, "<i>$1</i>");
|
|
713
|
+
out = out.replace(/~~(.+?)~~/g, "<s>$1</s>");
|
|
714
|
+
out = out.replace(/^#{1,6}\s+(.+)$/gm, "<b>$1</b>");
|
|
715
|
+
out = out.replace(/^>\s?(.*)$/gm, "<blockquote>$1</blockquote>");
|
|
716
|
+
out = out.replace(/<\/blockquote>\n<blockquote>/g, "\n");
|
|
717
|
+
const phPattern = new RegExp(`${PH_PREFIX}(\\d+)${PH_SUFFIX}`, "g");
|
|
718
|
+
out = out.replace(phPattern, (_, idx) => placeholders[Number(idx)]);
|
|
719
|
+
return out;
|
|
720
|
+
}
|
|
721
|
+
function escapeHtml(text) {
|
|
722
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
723
|
+
}
|
|
724
|
+
|
|
741
725
|
// ../channels/dist/telegram/media.js
|
|
742
726
|
function registerMediaHandlers(bot, isAllowed, handlers, onRejected) {
|
|
743
727
|
bot.on("message:photo", async (ctx) => {
|
|
@@ -827,23 +811,20 @@ var TelegramChannel = class {
|
|
|
827
811
|
}
|
|
828
812
|
});
|
|
829
813
|
registerMediaHandlers(this.bot, (id) => this.isUserAllowed(id), this.handlers, (userId, ts) => this.handleRejected(userId, Math.floor(ts.getTime() / 1e3), config.rejectMessage));
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
814
|
+
const convertAndSend = async (msg) => {
|
|
815
|
+
const htmlText = markdownToTelegramHtml(msg.text);
|
|
816
|
+
const replyOpts = msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {};
|
|
817
|
+
await withRetry(() => this.bot.api.sendMessage(Number(msg.userId), htmlText, {
|
|
818
|
+
parse_mode: "HTML",
|
|
819
|
+
...replyOpts
|
|
834
820
|
}), { maxRetries: 3, baseDelayMs: 500 }).catch(async () => {
|
|
835
|
-
await this.bot.api.sendMessage(Number(msg.userId), msg.text, {
|
|
836
|
-
...msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {}
|
|
837
|
-
}).catch((fallbackErr) => {
|
|
821
|
+
await this.bot.api.sendMessage(Number(msg.userId), msg.text, replyOpts).catch((fallbackErr) => {
|
|
838
822
|
console.error("[augure:telegram] Fallback send also failed:", fallbackErr);
|
|
839
823
|
throw fallbackErr;
|
|
840
824
|
});
|
|
841
825
|
});
|
|
842
826
|
};
|
|
843
|
-
this.sendPipeline = createOutgoingPipeline([
|
|
844
|
-
createEscapeMarkdownMiddleware(),
|
|
845
|
-
createSplitMessageMiddleware(rawSend)
|
|
846
|
-
], rawSend);
|
|
827
|
+
this.sendPipeline = createOutgoingPipeline([createSplitMessageMiddleware(convertAndSend)], convertAndSend);
|
|
847
828
|
}
|
|
848
829
|
isUserAllowed(userId) {
|
|
849
830
|
return this.allowedUsers.has(userId);
|
|
@@ -962,7 +943,7 @@ var memoryWriteTool = {
|
|
|
962
943
|
// ../tools/dist/schedule.js
|
|
963
944
|
var scheduleTool = {
|
|
964
945
|
name: "schedule",
|
|
965
|
-
description: "Manage scheduled tasks: create, delete, or list
|
|
946
|
+
description: "Manage scheduled tasks: create recurring (cron) or one-shot (runAt) jobs, delete, or list them",
|
|
966
947
|
parameters: {
|
|
967
948
|
type: "object",
|
|
968
949
|
properties: {
|
|
@@ -972,13 +953,14 @@ var scheduleTool = {
|
|
|
972
953
|
description: "The scheduling action to perform"
|
|
973
954
|
},
|
|
974
955
|
id: { type: "string", description: "The schedule ID (for create/delete)" },
|
|
975
|
-
cron: { type: "string", description: "Cron expression (for create)" },
|
|
956
|
+
cron: { type: "string", description: "Cron expression for recurring jobs (for create)" },
|
|
957
|
+
runAt: { type: "string", description: "ISO 8601 date for one-shot jobs, e.g. 2025-03-15T14:00:00Z (for create)" },
|
|
976
958
|
prompt: { type: "string", description: "The prompt to execute on schedule (for create)" }
|
|
977
959
|
},
|
|
978
960
|
required: ["action"]
|
|
979
961
|
},
|
|
980
962
|
execute: async (params, ctx) => {
|
|
981
|
-
const { action, id, cron, prompt } = params;
|
|
963
|
+
const { action, id, cron, runAt, prompt } = params;
|
|
982
964
|
try {
|
|
983
965
|
switch (action) {
|
|
984
966
|
case "list": {
|
|
@@ -986,22 +968,29 @@ var scheduleTool = {
|
|
|
986
968
|
if (jobs.length === 0) {
|
|
987
969
|
return { success: true, output: "No scheduled jobs." };
|
|
988
970
|
}
|
|
989
|
-
const lines = jobs.map((j) =>
|
|
971
|
+
const lines = jobs.map((j) => {
|
|
972
|
+
const schedule = j.cron ? `cron: ${j.cron}` : `runAt: ${j.runAt}`;
|
|
973
|
+
return `- ${j.id}: "${j.prompt}" @ ${schedule} (${j.enabled ? "enabled" : "disabled"})`;
|
|
974
|
+
});
|
|
990
975
|
return { success: true, output: lines.join("\n") };
|
|
991
976
|
}
|
|
992
977
|
case "create": {
|
|
993
|
-
if (!
|
|
994
|
-
return { success: false, output: "Missing required
|
|
978
|
+
if (!prompt) {
|
|
979
|
+
return { success: false, output: "Missing required field: prompt" };
|
|
980
|
+
}
|
|
981
|
+
if (!cron && !runAt) {
|
|
982
|
+
return { success: false, output: "Must provide either cron (recurring) or runAt (one-shot)" };
|
|
995
983
|
}
|
|
996
984
|
const jobId = id ?? `job-${Date.now()}`;
|
|
997
985
|
ctx.scheduler.addJob({
|
|
998
986
|
id: jobId,
|
|
999
987
|
cron,
|
|
988
|
+
runAt,
|
|
1000
989
|
prompt,
|
|
1001
990
|
channel: "default",
|
|
1002
991
|
enabled: true
|
|
1003
992
|
});
|
|
1004
|
-
return { success: true, output: `Created job ${jobId}` };
|
|
993
|
+
return { success: true, output: `Created job ${jobId} (${cron ? "recurring" : `one-shot at ${runAt}`})` };
|
|
1005
994
|
}
|
|
1006
995
|
case "delete": {
|
|
1007
996
|
if (!id) {
|
|
@@ -1660,6 +1649,63 @@ var DockerContainerPool = class {
|
|
|
1660
1649
|
}
|
|
1661
1650
|
};
|
|
1662
1651
|
|
|
1652
|
+
// ../sandbox/dist/ensure-image.js
|
|
1653
|
+
import { Readable } from "stream";
|
|
1654
|
+
var DOCKERFILE = `FROM node:22-slim
|
|
1655
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
1656
|
+
python3 curl jq git \\
|
|
1657
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
1658
|
+
WORKDIR /workspace
|
|
1659
|
+
`;
|
|
1660
|
+
function buildTar(content) {
|
|
1661
|
+
const data = Buffer.from(content, "utf-8");
|
|
1662
|
+
const name = "Dockerfile";
|
|
1663
|
+
const header = Buffer.alloc(512, 0);
|
|
1664
|
+
header.write(name, 0, 100, "utf-8");
|
|
1665
|
+
header.write("0000644\0", 100, 8, "utf-8");
|
|
1666
|
+
header.write("0000000\0", 108, 8, "utf-8");
|
|
1667
|
+
header.write("0000000\0", 116, 8, "utf-8");
|
|
1668
|
+
header.write(data.length.toString(8).padStart(11, "0") + "\0", 124, 12, "utf-8");
|
|
1669
|
+
header.write(Math.floor(Date.now() / 1e3).toString(8).padStart(11, "0") + "\0", 136, 12, "utf-8");
|
|
1670
|
+
header.write("0", 156, 1, "utf-8");
|
|
1671
|
+
header.fill(32, 148, 156);
|
|
1672
|
+
let checksum = 0;
|
|
1673
|
+
for (let i = 0; i < 512; i++)
|
|
1674
|
+
checksum += header[i];
|
|
1675
|
+
header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8, "utf-8");
|
|
1676
|
+
const padding = 512 - (data.length % 512 || 512);
|
|
1677
|
+
const dataPadded = padding > 0 && padding < 512 ? Buffer.concat([data, Buffer.alloc(padding, 0)]) : data;
|
|
1678
|
+
const end = Buffer.alloc(1024, 0);
|
|
1679
|
+
return Buffer.concat([header, dataPadded, end]);
|
|
1680
|
+
}
|
|
1681
|
+
async function ensureImage(docker, imageName) {
|
|
1682
|
+
try {
|
|
1683
|
+
await docker.getImage(imageName).inspect();
|
|
1684
|
+
return;
|
|
1685
|
+
} catch (err2) {
|
|
1686
|
+
const statusCode = err2.statusCode;
|
|
1687
|
+
if (statusCode !== void 0 && statusCode !== 404)
|
|
1688
|
+
throw err2;
|
|
1689
|
+
}
|
|
1690
|
+
console.log(`[augure] Image "${imageName}" not found, building...`);
|
|
1691
|
+
const tar = buildTar(DOCKERFILE);
|
|
1692
|
+
const stream = await docker.buildImage(Readable.from(tar), {
|
|
1693
|
+
t: imageName
|
|
1694
|
+
});
|
|
1695
|
+
await new Promise((resolve5, reject) => {
|
|
1696
|
+
docker.modem.followProgress(stream, (err2) => {
|
|
1697
|
+
if (err2)
|
|
1698
|
+
reject(err2);
|
|
1699
|
+
else
|
|
1700
|
+
resolve5();
|
|
1701
|
+
}, (event) => {
|
|
1702
|
+
if (event.stream)
|
|
1703
|
+
process.stdout.write(event.stream);
|
|
1704
|
+
});
|
|
1705
|
+
});
|
|
1706
|
+
console.log(`[augure] Image "${imageName}" built successfully`);
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1663
1709
|
// ../memory/dist/store.js
|
|
1664
1710
|
import { readFile as readFile3, writeFile, mkdir as mkdir2, readdir as readdir2, access } from "fs/promises";
|
|
1665
1711
|
import { join as join3, dirname, relative } from "path";
|
|
@@ -1823,6 +1869,7 @@ var CronScheduler = class {
|
|
|
1823
1869
|
store;
|
|
1824
1870
|
jobs = /* @__PURE__ */ new Map();
|
|
1825
1871
|
tasks = /* @__PURE__ */ new Map();
|
|
1872
|
+
timers = /* @__PURE__ */ new Map();
|
|
1826
1873
|
handlers = [];
|
|
1827
1874
|
persistChain = Promise.resolve();
|
|
1828
1875
|
constructor(store) {
|
|
@@ -1832,11 +1879,17 @@ var CronScheduler = class {
|
|
|
1832
1879
|
this.handlers.push(handler);
|
|
1833
1880
|
}
|
|
1834
1881
|
addJob(job) {
|
|
1835
|
-
if (!
|
|
1882
|
+
if (!job.cron && !job.runAt) {
|
|
1883
|
+
throw new Error(`Job ${job.id} must have either cron or runAt`);
|
|
1884
|
+
}
|
|
1885
|
+
if (job.cron && !validate(job.cron)) {
|
|
1836
1886
|
throw new Error(`Invalid cron expression: ${job.cron}`);
|
|
1837
1887
|
}
|
|
1888
|
+
if (job.runAt && isNaN(Date.parse(job.runAt))) {
|
|
1889
|
+
throw new Error(`Invalid runAt date: ${job.runAt}`);
|
|
1890
|
+
}
|
|
1838
1891
|
this.jobs.set(job.id, job);
|
|
1839
|
-
if (job.enabled) {
|
|
1892
|
+
if (job.enabled && job.cron) {
|
|
1840
1893
|
const task = createTask(job.cron, () => {
|
|
1841
1894
|
void this.executeHandlers(job);
|
|
1842
1895
|
});
|
|
@@ -1850,6 +1903,11 @@ var CronScheduler = class {
|
|
|
1850
1903
|
task.stop();
|
|
1851
1904
|
this.tasks.delete(id);
|
|
1852
1905
|
}
|
|
1906
|
+
const timer = this.timers.get(id);
|
|
1907
|
+
if (timer) {
|
|
1908
|
+
clearTimeout(timer);
|
|
1909
|
+
this.timers.delete(id);
|
|
1910
|
+
}
|
|
1853
1911
|
this.jobs.delete(id);
|
|
1854
1912
|
this.persist();
|
|
1855
1913
|
}
|
|
@@ -1868,6 +1926,9 @@ var CronScheduler = class {
|
|
|
1868
1926
|
return;
|
|
1869
1927
|
const jobs = await this.store.load();
|
|
1870
1928
|
for (const job of jobs) {
|
|
1929
|
+
if (job.runAt && Date.parse(job.runAt) <= Date.now()) {
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1871
1932
|
this.addJob(job);
|
|
1872
1933
|
}
|
|
1873
1934
|
}
|
|
@@ -1875,11 +1936,32 @@ var CronScheduler = class {
|
|
|
1875
1936
|
for (const task of this.tasks.values()) {
|
|
1876
1937
|
task.start();
|
|
1877
1938
|
}
|
|
1939
|
+
for (const job of this.jobs.values()) {
|
|
1940
|
+
if (job.enabled && job.runAt && !job.cron) {
|
|
1941
|
+
this.scheduleOneShot(job);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1878
1944
|
}
|
|
1879
1945
|
stop() {
|
|
1880
1946
|
for (const task of this.tasks.values()) {
|
|
1881
1947
|
task.stop();
|
|
1882
1948
|
}
|
|
1949
|
+
for (const timer of this.timers.values()) {
|
|
1950
|
+
clearTimeout(timer);
|
|
1951
|
+
}
|
|
1952
|
+
this.timers.clear();
|
|
1953
|
+
}
|
|
1954
|
+
scheduleOneShot(job) {
|
|
1955
|
+
const delayMs = Date.parse(job.runAt) - Date.now();
|
|
1956
|
+
if (delayMs <= 0)
|
|
1957
|
+
return;
|
|
1958
|
+
const timer = setTimeout(() => {
|
|
1959
|
+
this.timers.delete(job.id);
|
|
1960
|
+
void this.executeHandlers(job).then(() => {
|
|
1961
|
+
this.removeJob(job.id);
|
|
1962
|
+
});
|
|
1963
|
+
}, delayMs);
|
|
1964
|
+
this.timers.set(job.id, timer);
|
|
1883
1965
|
}
|
|
1884
1966
|
persist() {
|
|
1885
1967
|
if (!this.store)
|
|
@@ -3163,8 +3245,162 @@ async function installBuiltins(manager) {
|
|
|
3163
3245
|
}
|
|
3164
3246
|
}
|
|
3165
3247
|
|
|
3248
|
+
// ../skills/dist/updater.js
|
|
3249
|
+
var SkillUpdater = class {
|
|
3250
|
+
config;
|
|
3251
|
+
constructor(config) {
|
|
3252
|
+
this.config = config;
|
|
3253
|
+
}
|
|
3254
|
+
/** Compare local skill versions with hub manifest */
|
|
3255
|
+
async checkForUpdates() {
|
|
3256
|
+
const [local, remote] = await Promise.all([
|
|
3257
|
+
this.config.manager.list(),
|
|
3258
|
+
this.config.hub.list()
|
|
3259
|
+
]);
|
|
3260
|
+
const localMap = new Map(local.map((s) => [s.id, s.version]));
|
|
3261
|
+
const updates = [];
|
|
3262
|
+
for (const entry of remote) {
|
|
3263
|
+
const localVersion = localMap.get(entry.id);
|
|
3264
|
+
if (localVersion !== void 0 && entry.version > localVersion) {
|
|
3265
|
+
updates.push({
|
|
3266
|
+
id: entry.id,
|
|
3267
|
+
localVersion,
|
|
3268
|
+
hubVersion: entry.version
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
return updates;
|
|
3273
|
+
}
|
|
3274
|
+
/** Apply a single skill update with backup and rollback */
|
|
3275
|
+
async applyUpdate(skillId) {
|
|
3276
|
+
let backup;
|
|
3277
|
+
try {
|
|
3278
|
+
backup = await this.config.manager.get(skillId);
|
|
3279
|
+
} catch (err2) {
|
|
3280
|
+
return {
|
|
3281
|
+
skillId,
|
|
3282
|
+
success: false,
|
|
3283
|
+
fromVersion: 0,
|
|
3284
|
+
toVersion: 0,
|
|
3285
|
+
error: `Failed to backup: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
const fromVersion = backup.meta.version;
|
|
3289
|
+
let newSkill;
|
|
3290
|
+
try {
|
|
3291
|
+
newSkill = await this.config.hub.download(skillId);
|
|
3292
|
+
} catch (err2) {
|
|
3293
|
+
return {
|
|
3294
|
+
skillId,
|
|
3295
|
+
success: false,
|
|
3296
|
+
fromVersion,
|
|
3297
|
+
toVersion: 0,
|
|
3298
|
+
error: `Failed to download: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
const toVersion = newSkill.meta.version;
|
|
3302
|
+
if (!newSkill.meta.sandbox) {
|
|
3303
|
+
await this.config.manager.save(backup);
|
|
3304
|
+
return {
|
|
3305
|
+
skillId,
|
|
3306
|
+
success: false,
|
|
3307
|
+
rolledBack: true,
|
|
3308
|
+
fromVersion,
|
|
3309
|
+
toVersion,
|
|
3310
|
+
error: "Downloaded skill has sandbox disabled \u2014 rejected for security"
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
const testResult = await this.config.tester.test(newSkill);
|
|
3314
|
+
if (testResult.success) {
|
|
3315
|
+
await this.config.manager.save(newSkill);
|
|
3316
|
+
return { skillId, success: true, fromVersion, toVersion };
|
|
3317
|
+
}
|
|
3318
|
+
await this.config.manager.save(backup);
|
|
3319
|
+
return {
|
|
3320
|
+
skillId,
|
|
3321
|
+
success: false,
|
|
3322
|
+
rolledBack: true,
|
|
3323
|
+
fromVersion,
|
|
3324
|
+
toVersion,
|
|
3325
|
+
error: `Update failed tests: ${testResult.error ?? "unknown"}`
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
/** Check for updates and apply all available ones */
|
|
3329
|
+
async checkAndApply() {
|
|
3330
|
+
const updates = await this.checkForUpdates();
|
|
3331
|
+
const results = [];
|
|
3332
|
+
for (const update of updates) {
|
|
3333
|
+
try {
|
|
3334
|
+
const result = await this.applyUpdate(update.id);
|
|
3335
|
+
results.push(result);
|
|
3336
|
+
} catch (err2) {
|
|
3337
|
+
results.push({
|
|
3338
|
+
skillId: update.id,
|
|
3339
|
+
success: false,
|
|
3340
|
+
fromVersion: update.localVersion,
|
|
3341
|
+
toVersion: update.hubVersion,
|
|
3342
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
3343
|
+
});
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
return results;
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
|
|
3166
3350
|
// ../core/dist/main.js
|
|
3167
3351
|
import { resolve } from "path";
|
|
3352
|
+
import { createRequire } from "module";
|
|
3353
|
+
|
|
3354
|
+
// ../core/dist/version-checker.js
|
|
3355
|
+
var VersionChecker = class _VersionChecker {
|
|
3356
|
+
config;
|
|
3357
|
+
constructor(config) {
|
|
3358
|
+
this.config = config;
|
|
3359
|
+
}
|
|
3360
|
+
/** Check npm registry for latest version */
|
|
3361
|
+
async check() {
|
|
3362
|
+
try {
|
|
3363
|
+
const response = await fetch(`https://registry.npmjs.org/${this.config.packageName}/latest`);
|
|
3364
|
+
if (!response.ok) {
|
|
3365
|
+
return {
|
|
3366
|
+
updateAvailable: false,
|
|
3367
|
+
currentVersion: this.config.currentVersion,
|
|
3368
|
+
error: `npm registry returned ${response.status}`
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3371
|
+
const data = await response.json();
|
|
3372
|
+
const latest = data.version;
|
|
3373
|
+
return {
|
|
3374
|
+
updateAvailable: _VersionChecker.compareVersions(this.config.currentVersion, latest) < 0,
|
|
3375
|
+
currentVersion: this.config.currentVersion,
|
|
3376
|
+
latestVersion: latest
|
|
3377
|
+
};
|
|
3378
|
+
} catch (err2) {
|
|
3379
|
+
return {
|
|
3380
|
+
updateAvailable: false,
|
|
3381
|
+
currentVersion: this.config.currentVersion,
|
|
3382
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
/** Compare two semver strings (MAJOR.MINOR.PATCH only, pre-release suffixes stripped). Returns -1 if a < b, 0 if equal, 1 if a > b */
|
|
3387
|
+
static compareVersions(a, b) {
|
|
3388
|
+
const clean = (v) => v.replace(/^v/, "").split("-")[0];
|
|
3389
|
+
const pa = clean(a).split(".").map(Number);
|
|
3390
|
+
const pb = clean(b).split(".").map(Number);
|
|
3391
|
+
for (let i = 0; i < 3; i++) {
|
|
3392
|
+
const va = pa[i] ?? 0;
|
|
3393
|
+
const vb = pb[i] ?? 0;
|
|
3394
|
+
if (va < vb)
|
|
3395
|
+
return -1;
|
|
3396
|
+
if (va > vb)
|
|
3397
|
+
return 1;
|
|
3398
|
+
}
|
|
3399
|
+
return 0;
|
|
3400
|
+
}
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3403
|
+
// ../core/dist/main.js
|
|
3168
3404
|
var SYSTEM_PROMPT = `You are Augure, a personal AI assistant. You are proactive, helpful, and concise.
|
|
3169
3405
|
You speak the same language as the user. You have access to tools and persistent memory.
|
|
3170
3406
|
Always be direct and actionable.`;
|
|
@@ -3179,7 +3415,7 @@ function resolveLLMClient(config, usage) {
|
|
|
3179
3415
|
async function startAgent(configPath) {
|
|
3180
3416
|
const config = await loadConfig(configPath);
|
|
3181
3417
|
console.log(`[augure] Loaded config: ${config.identity.name}`);
|
|
3182
|
-
let
|
|
3418
|
+
let telegram;
|
|
3183
3419
|
const llm = resolveLLMClient(config.llm, "default");
|
|
3184
3420
|
const ingestionLLM = resolveLLMClient(config.llm, "ingestion");
|
|
3185
3421
|
const monitoringLLM = resolveLLMClient(config.llm, "monitoring");
|
|
@@ -3209,12 +3445,15 @@ async function startAgent(configPath) {
|
|
|
3209
3445
|
}
|
|
3210
3446
|
}
|
|
3211
3447
|
const docker = new Dockerode();
|
|
3448
|
+
const sandboxImage = config.sandbox.image ?? "augure-sandbox:latest";
|
|
3449
|
+
await ensureImage(docker, sandboxImage);
|
|
3212
3450
|
const pool = new DockerContainerPool(docker, {
|
|
3213
|
-
image:
|
|
3451
|
+
image: sandboxImage,
|
|
3214
3452
|
maxTotal: config.security.maxConcurrentSandboxes
|
|
3215
3453
|
});
|
|
3216
3454
|
console.log(`[augure] Container pool created (max: ${config.security.maxConcurrentSandboxes})`);
|
|
3217
3455
|
let skillManagerRef;
|
|
3456
|
+
let skillUpdater;
|
|
3218
3457
|
if (config.skills) {
|
|
3219
3458
|
const skillsPath = resolve(configPath, "..", config.skills.path);
|
|
3220
3459
|
const codingLLM = resolveLLMClient(config.llm, "coding");
|
|
@@ -3250,6 +3489,26 @@ async function startAgent(configPath) {
|
|
|
3250
3489
|
}
|
|
3251
3490
|
const skillBridge = new SkillSchedulerBridge(scheduler, skillManager);
|
|
3252
3491
|
await skillBridge.syncAll();
|
|
3492
|
+
if (hub && config.updates?.skills?.enabled !== false) {
|
|
3493
|
+
skillUpdater = new SkillUpdater({
|
|
3494
|
+
manager: skillManager,
|
|
3495
|
+
hub,
|
|
3496
|
+
tester: skillTester
|
|
3497
|
+
});
|
|
3498
|
+
try {
|
|
3499
|
+
const updateResults = await skillUpdater.checkAndApply();
|
|
3500
|
+
const updated = updateResults.filter((r) => r.success);
|
|
3501
|
+
const failed = updateResults.filter((r) => !r.success);
|
|
3502
|
+
if (updated.length > 0) {
|
|
3503
|
+
console.log(`[augure] Skills updated: ${updated.map((r) => `${r.skillId} (v${r.fromVersion}\u2192v${r.toVersion})`).join(", ")}`);
|
|
3504
|
+
}
|
|
3505
|
+
if (failed.length > 0) {
|
|
3506
|
+
console.log(`[augure] Skill updates failed: ${failed.map((r) => `${r.skillId}: ${r.error}`).join(", ")}`);
|
|
3507
|
+
}
|
|
3508
|
+
} catch (err2) {
|
|
3509
|
+
console.error("[augure] Skill update check failed:", err2);
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3253
3512
|
skillManagerRef = skillManager;
|
|
3254
3513
|
console.log(`[augure] Skills system initialized at ${skillsPath}`);
|
|
3255
3514
|
}
|
|
@@ -3265,6 +3524,23 @@ async function startAgent(configPath) {
|
|
|
3265
3524
|
await personaResolver.loadAll();
|
|
3266
3525
|
console.log(`[augure] Personas loaded from ${personaPath}`);
|
|
3267
3526
|
}
|
|
3527
|
+
let cliVersion;
|
|
3528
|
+
try {
|
|
3529
|
+
const require3 = createRequire(import.meta.url);
|
|
3530
|
+
const pkg = require3("augure/package.json");
|
|
3531
|
+
cliVersion = pkg.version;
|
|
3532
|
+
} catch {
|
|
3533
|
+
}
|
|
3534
|
+
if (cliVersion && config.updates?.cli?.enabled !== false) {
|
|
3535
|
+
const versionChecker = new VersionChecker({
|
|
3536
|
+
currentVersion: cliVersion,
|
|
3537
|
+
packageName: "augure"
|
|
3538
|
+
});
|
|
3539
|
+
const versionResult = await versionChecker.check();
|
|
3540
|
+
if (versionResult.updateAvailable) {
|
|
3541
|
+
console.log(`[augure] Update available: v${versionResult.latestVersion} (current: v${versionResult.currentVersion}). Run: npm update -g augure`);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3268
3544
|
const guard = new ContextGuard({
|
|
3269
3545
|
maxContextTokens: 2e5,
|
|
3270
3546
|
reservedForOutput: config.llm.default.maxTokens ?? 8192
|
|
@@ -3281,23 +3557,24 @@ async function startAgent(configPath) {
|
|
|
3281
3557
|
modelName: config.llm.default.model
|
|
3282
3558
|
});
|
|
3283
3559
|
if (config.channels.telegram?.enabled) {
|
|
3284
|
-
|
|
3560
|
+
telegram = new TelegramChannel({
|
|
3285
3561
|
botToken: config.channels.telegram.botToken,
|
|
3286
3562
|
allowedUsers: config.channels.telegram.allowedUsers,
|
|
3287
3563
|
rejectMessage: config.channels.telegram.rejectMessage
|
|
3288
3564
|
});
|
|
3565
|
+
const tg = telegram;
|
|
3289
3566
|
const commandCtx = {
|
|
3290
3567
|
scheduler,
|
|
3291
3568
|
pool,
|
|
3292
3569
|
agent,
|
|
3293
3570
|
skillManager: skillManagerRef
|
|
3294
3571
|
};
|
|
3295
|
-
|
|
3572
|
+
tg.onMessage(async (msg) => {
|
|
3296
3573
|
console.log(`[augure] Message from ${msg.userId}: ${msg.text}`);
|
|
3297
3574
|
try {
|
|
3298
3575
|
const cmdResult = await handleCommand(msg.text, commandCtx);
|
|
3299
3576
|
if (cmdResult.handled) {
|
|
3300
|
-
await
|
|
3577
|
+
await tg.send({
|
|
3301
3578
|
channelType: "telegram",
|
|
3302
3579
|
userId: msg.userId,
|
|
3303
3580
|
text: cmdResult.response ?? "OK",
|
|
@@ -3309,7 +3586,7 @@ async function startAgent(configPath) {
|
|
|
3309
3586
|
agent.setPersona(personaResolver.resolve(msg.text));
|
|
3310
3587
|
}
|
|
3311
3588
|
const response = await agent.handleMessage(msg);
|
|
3312
|
-
await
|
|
3589
|
+
await tg.send({
|
|
3313
3590
|
channelType: "telegram",
|
|
3314
3591
|
userId: msg.userId,
|
|
3315
3592
|
text: response,
|
|
@@ -3317,15 +3594,14 @@ async function startAgent(configPath) {
|
|
|
3317
3594
|
});
|
|
3318
3595
|
} catch (err2) {
|
|
3319
3596
|
console.error("[augure] Error handling message:", err2);
|
|
3320
|
-
await
|
|
3597
|
+
await tg.send({
|
|
3321
3598
|
channelType: "telegram",
|
|
3322
3599
|
userId: msg.userId,
|
|
3323
3600
|
text: "An error occurred while processing your message."
|
|
3324
3601
|
});
|
|
3325
3602
|
}
|
|
3326
3603
|
});
|
|
3327
|
-
await
|
|
3328
|
-
telegramChannel = telegram;
|
|
3604
|
+
await tg.start();
|
|
3329
3605
|
console.log("[augure] Telegram bot started. Waiting for messages...");
|
|
3330
3606
|
}
|
|
3331
3607
|
const heartbeatIntervalMs = parseInterval(config.scheduler.heartbeatInterval);
|
|
@@ -3345,15 +3621,82 @@ async function startAgent(configPath) {
|
|
|
3345
3621
|
console.log(`[augure] Heartbeat response: ${response}`);
|
|
3346
3622
|
}
|
|
3347
3623
|
});
|
|
3624
|
+
scheduler.onJobTrigger(async (job) => {
|
|
3625
|
+
console.log(`[augure] Job triggered: ${job.id}`);
|
|
3626
|
+
const response = await agent.handleMessage({
|
|
3627
|
+
id: `job-${job.id}-${Date.now()}`,
|
|
3628
|
+
channelType: "system",
|
|
3629
|
+
userId: "system",
|
|
3630
|
+
text: job.prompt,
|
|
3631
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3632
|
+
});
|
|
3633
|
+
if (telegram && config.channels.telegram?.enabled) {
|
|
3634
|
+
const userId = config.channels.telegram.allowedUsers[0];
|
|
3635
|
+
if (userId !== void 0) {
|
|
3636
|
+
await telegram.send({
|
|
3637
|
+
channelType: "telegram",
|
|
3638
|
+
userId: String(userId),
|
|
3639
|
+
text: response
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
console.log(`[augure] Job ${job.id} completed`);
|
|
3644
|
+
});
|
|
3348
3645
|
scheduler.start();
|
|
3349
3646
|
heartbeat.start();
|
|
3350
3647
|
console.log(`[augure] Scheduler started with ${scheduler.listJobs().length} jobs. Heartbeat every ${config.scheduler.heartbeatInterval}.`);
|
|
3648
|
+
const updateTimers = [];
|
|
3649
|
+
if (skillUpdater && config.updates?.skills?.checkInterval) {
|
|
3650
|
+
const su = skillUpdater;
|
|
3651
|
+
const skillCheckMs = parseInterval(config.updates.skills.checkInterval);
|
|
3652
|
+
updateTimers.push(setInterval(async () => {
|
|
3653
|
+
try {
|
|
3654
|
+
const results = await su.checkAndApply();
|
|
3655
|
+
for (const r of results) {
|
|
3656
|
+
if (r.success) {
|
|
3657
|
+
console.log(`[augure] Skill auto-updated: ${r.skillId} v${r.fromVersion}\u2192v${r.toVersion}`);
|
|
3658
|
+
} else if (r.rolledBack) {
|
|
3659
|
+
console.log(`[augure] Skill update rolled back: ${r.skillId} - ${r.error}`);
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
} catch (err2) {
|
|
3663
|
+
console.error("[augure] Periodic skill update check failed:", err2);
|
|
3664
|
+
}
|
|
3665
|
+
}, skillCheckMs));
|
|
3666
|
+
}
|
|
3667
|
+
if (cliVersion && config.updates?.cli?.enabled !== false && config.channels.telegram?.enabled) {
|
|
3668
|
+
const cliCheckMs = parseInterval(config.updates?.cli?.checkInterval ?? "24h");
|
|
3669
|
+
const versionChecker = new VersionChecker({
|
|
3670
|
+
currentVersion: cliVersion,
|
|
3671
|
+
packageName: "augure"
|
|
3672
|
+
});
|
|
3673
|
+
updateTimers.push(setInterval(async () => {
|
|
3674
|
+
try {
|
|
3675
|
+
const result = await versionChecker.check();
|
|
3676
|
+
if (result.updateAvailable && telegram) {
|
|
3677
|
+
const userId = config.channels.telegram?.allowedUsers[0];
|
|
3678
|
+
if (userId !== void 0) {
|
|
3679
|
+
await telegram.send({
|
|
3680
|
+
channelType: "telegram",
|
|
3681
|
+
userId: String(userId),
|
|
3682
|
+
text: `Update available: Augure v${result.latestVersion} (current: v${result.currentVersion}).
|
|
3683
|
+
Run: \`npm update -g augure\``
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
} catch (err2) {
|
|
3688
|
+
console.error("[augure] CLI version check failed:", err2);
|
|
3689
|
+
}
|
|
3690
|
+
}, cliCheckMs));
|
|
3691
|
+
}
|
|
3351
3692
|
const shutdown = async () => {
|
|
3352
3693
|
console.log("\n[augure] Shutting down...");
|
|
3694
|
+
for (const timer of updateTimers)
|
|
3695
|
+
clearInterval(timer);
|
|
3353
3696
|
heartbeat.stop();
|
|
3354
3697
|
scheduler.stop();
|
|
3355
|
-
if (
|
|
3356
|
-
await
|
|
3698
|
+
if (telegram)
|
|
3699
|
+
await telegram.stop();
|
|
3357
3700
|
await pool.destroyAll();
|
|
3358
3701
|
await audit.close();
|
|
3359
3702
|
console.log("[augure] All containers destroyed");
|
|
@@ -3751,7 +4094,7 @@ var skillsCommand = defineCommand3({
|
|
|
3751
4094
|
});
|
|
3752
4095
|
|
|
3753
4096
|
// src/bin.ts
|
|
3754
|
-
var require2 =
|
|
4097
|
+
var require2 = createRequire2(import.meta.url);
|
|
3755
4098
|
var { version } = require2("../package.json");
|
|
3756
4099
|
var main = defineCommand4({
|
|
3757
4100
|
meta: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "augure",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Augure — your proactive AI agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"@types/dockerode": "^4.0.1",
|
|
28
28
|
"@types/node-cron": "^3.0.11",
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
|
-
"@augure/
|
|
31
|
-
"@augure/
|
|
32
|
-
"@augure/
|
|
33
|
-
"@augure/
|
|
34
|
-
"@augure/scheduler": "0.0
|
|
35
|
-
"@augure/
|
|
36
|
-
"@augure/types": "0.1.
|
|
37
|
-
"@augure/
|
|
30
|
+
"@augure/channels": "0.1.1",
|
|
31
|
+
"@augure/core": "0.2.0",
|
|
32
|
+
"@augure/memory": "0.0.3",
|
|
33
|
+
"@augure/sandbox": "0.1.0",
|
|
34
|
+
"@augure/scheduler": "0.1.0",
|
|
35
|
+
"@augure/skills": "0.1.1",
|
|
36
|
+
"@augure/types": "0.1.1",
|
|
37
|
+
"@augure/tools": "0.0.3"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
40
40
|
"ai",
|