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.
Files changed (2) hide show
  1. package/dist/bin.js +439 -96
  2. 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
- id: tc.id,
203
- name: tc.function.name,
204
- arguments: JSON.parse(tc.function.arguments)
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 status = error.status;
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(/^&gt;\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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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 rawSend = async (msg) => {
831
- await withRetry(() => this.bot.api.sendMessage(Number(msg.userId), msg.text, {
832
- parse_mode: "MarkdownV2",
833
- ...msg.replyTo ? { reply_parameters: { message_id: Number(msg.replyTo) } } : {}
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 cron jobs",
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) => `- ${j.id}: "${j.prompt}" @ ${j.cron} (${j.enabled ? "enabled" : "disabled"})`);
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 (!cron || !prompt) {
994
- return { success: false, output: "Missing required fields: cron and prompt" };
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 (!validate(job.cron)) {
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 telegramChannel;
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: config.sandbox.image ?? "augure-sandbox:latest",
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
- const telegram = new TelegramChannel({
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
- telegram.onMessage(async (msg) => {
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 telegram.send({
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 telegram.send({
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 telegram.send({
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 telegram.start();
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 (telegramChannel)
3356
- await telegramChannel.stop();
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 = createRequire(import.meta.url);
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.4.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/core": "0.1.0",
31
- "@augure/memory": "0.0.2",
32
- "@augure/sandbox": "0.0.2",
33
- "@augure/channels": "0.1.0",
34
- "@augure/scheduler": "0.0.2",
35
- "@augure/tools": "0.0.2",
36
- "@augure/types": "0.1.0",
37
- "@augure/skills": "0.1.0"
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",