tomo-ai 0.1.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 (90) hide show
  1. package/README.md +198 -0
  2. package/defaults/AGENT.md +27 -0
  3. package/defaults/IDENTITY.md +27 -0
  4. package/defaults/SOUL.md +34 -0
  5. package/defaults/skills/cron/SKILL.md +68 -0
  6. package/dist/agent.d.ts +25 -0
  7. package/dist/agent.d.ts.map +1 -0
  8. package/dist/agent.js +334 -0
  9. package/dist/agent.js.map +1 -0
  10. package/dist/channels/index.d.ts +3 -0
  11. package/dist/channels/index.d.ts.map +1 -0
  12. package/dist/channels/index.js +2 -0
  13. package/dist/channels/index.js.map +1 -0
  14. package/dist/channels/telegram.d.ts +22 -0
  15. package/dist/channels/telegram.d.ts.map +1 -0
  16. package/dist/channels/telegram.js +189 -0
  17. package/dist/channels/telegram.js.map +1 -0
  18. package/dist/channels/types.d.ts +53 -0
  19. package/dist/channels/types.d.ts.map +1 -0
  20. package/dist/channels/types.js +2 -0
  21. package/dist/channels/types.js.map +1 -0
  22. package/dist/cli/cron.d.ts +3 -0
  23. package/dist/cli/cron.d.ts.map +1 -0
  24. package/dist/cli/cron.js +87 -0
  25. package/dist/cli/cron.js.map +1 -0
  26. package/dist/cli/daemon.d.ts +6 -0
  27. package/dist/cli/daemon.d.ts.map +1 -0
  28. package/dist/cli/daemon.js +100 -0
  29. package/dist/cli/daemon.js.map +1 -0
  30. package/dist/cli/init.d.ts +3 -0
  31. package/dist/cli/init.d.ts.map +1 -0
  32. package/dist/cli/init.js +246 -0
  33. package/dist/cli/init.js.map +1 -0
  34. package/dist/cli/sessions.d.ts +3 -0
  35. package/dist/cli/sessions.d.ts.map +1 -0
  36. package/dist/cli/sessions.js +81 -0
  37. package/dist/cli/sessions.js.map +1 -0
  38. package/dist/cli/start.d.ts +3 -0
  39. package/dist/cli/start.d.ts.map +1 -0
  40. package/dist/cli/start.js +86 -0
  41. package/dist/cli/start.js.map +1 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +21 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/config.d.ts +12 -0
  47. package/dist/config.d.ts.map +1 -0
  48. package/dist/config.js +37 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/cron/scheduler.d.ts +12 -0
  51. package/dist/cron/scheduler.d.ts.map +1 -0
  52. package/dist/cron/scheduler.js +48 -0
  53. package/dist/cron/scheduler.js.map +1 -0
  54. package/dist/cron/store.d.ts +24 -0
  55. package/dist/cron/store.d.ts.map +1 -0
  56. package/dist/cron/store.js +143 -0
  57. package/dist/cron/store.js.map +1 -0
  58. package/dist/cron/types.d.ts +35 -0
  59. package/dist/cron/types.d.ts.map +1 -0
  60. package/dist/cron/types.js +2 -0
  61. package/dist/cron/types.js.map +1 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +14 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/logger.d.ts +3 -0
  67. package/dist/logger.d.ts.map +1 -0
  68. package/dist/logger.js +26 -0
  69. package/dist/logger.js.map +1 -0
  70. package/dist/sessions/index.d.ts +3 -0
  71. package/dist/sessions/index.d.ts.map +1 -0
  72. package/dist/sessions/index.js +2 -0
  73. package/dist/sessions/index.js.map +1 -0
  74. package/dist/sessions/store.d.ts +36 -0
  75. package/dist/sessions/store.d.ts.map +1 -0
  76. package/dist/sessions/store.js +216 -0
  77. package/dist/sessions/store.js.map +1 -0
  78. package/dist/sessions/types.d.ts +32 -0
  79. package/dist/sessions/types.d.ts.map +1 -0
  80. package/dist/sessions/types.js +2 -0
  81. package/dist/sessions/types.js.map +1 -0
  82. package/dist/skills/sync.d.ts +12 -0
  83. package/dist/skills/sync.d.ts.map +1 -0
  84. package/dist/skills/sync.js +71 -0
  85. package/dist/skills/sync.js.map +1 -0
  86. package/dist/workspace/index.d.ts +2 -0
  87. package/dist/workspace/index.d.ts.map +1 -0
  88. package/dist/workspace/index.js +99 -0
  89. package/dist/workspace/index.js.map +1 -0
  90. package/package.json +49 -0
@@ -0,0 +1,86 @@
1
+ import { Command } from "commander";
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ const TOMO_HOME = join(homedir(), ".tomo");
7
+ const PID_FILE = join(TOMO_HOME, "tomo.pid");
8
+ function isRunning(pid) {
9
+ try {
10
+ process.kill(pid, 0);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ function getRunningPid() {
18
+ if (!existsSync(PID_FILE))
19
+ return null;
20
+ const pid = Number(readFileSync(PID_FILE, "utf-8").trim());
21
+ if (isNaN(pid) || !isRunning(pid)) {
22
+ unlinkSync(PID_FILE);
23
+ return null;
24
+ }
25
+ return pid;
26
+ }
27
+ export const startCommand = new Command("start")
28
+ .description("Start Tomo")
29
+ .option("-f, --foreground", "Run in foreground (default: background)")
30
+ .action(async (opts) => {
31
+ if (opts.foreground) {
32
+ return startForeground();
33
+ }
34
+ return startDaemon();
35
+ });
36
+ async function startForeground() {
37
+ const { Agent } = await import("../agent.js");
38
+ const { TelegramChannel } = await import("../channels/index.js");
39
+ const { config } = await import("../config.js");
40
+ const { CronScheduler } = await import("../cron/scheduler.js");
41
+ const agent = new Agent();
42
+ agent.addChannel(new TelegramChannel(config.telegramToken));
43
+ const scheduler = new CronScheduler(agent);
44
+ // Write PID so `tomo stop` can find us
45
+ writeFileSync(PID_FILE, String(process.pid));
46
+ const shutdown = async () => {
47
+ scheduler.stop();
48
+ await agent.stop();
49
+ try {
50
+ unlinkSync(PID_FILE);
51
+ }
52
+ catch { }
53
+ process.exit(0);
54
+ };
55
+ process.on("SIGINT", shutdown);
56
+ process.on("SIGTERM", shutdown);
57
+ await agent.start();
58
+ scheduler.start();
59
+ }
60
+ async function startDaemon() {
61
+ const existing = getRunningPid();
62
+ if (existing) {
63
+ console.log(`Tomo is already running (PID ${existing})`);
64
+ process.exit(1);
65
+ }
66
+ const logFile = join(TOMO_HOME, "logs", "tomo.log");
67
+ const errFile = join(TOMO_HOME, "logs", "tomo.err");
68
+ const { openSync } = await import("node:fs");
69
+ const { mkdirSync: mkdirSyncFs } = await import("node:fs");
70
+ mkdirSyncFs(join(TOMO_HOME, "logs"), { recursive: true });
71
+ const errFd = openSync(errFile, "a");
72
+ // Re-run ourselves in foreground mode as a detached child
73
+ const child = spawn(process.execPath, [process.argv[1], "start", "--foreground"], {
74
+ detached: true,
75
+ stdio: ["ignore", "ignore", errFd],
76
+ env: {
77
+ ...process.env,
78
+ TOMO_LOG_FILE: logFile,
79
+ },
80
+ });
81
+ child.unref();
82
+ console.log(`Tomo started in background (PID ${child.pid})`);
83
+ console.log(`Logs: ${logFile}`);
84
+ process.exit(0);
85
+ }
86
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/cli/start.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { cronCommand } from "./cli/cron.js";
4
+ import { startCommand } from "./cli/start.js";
5
+ import { initCommand } from "./cli/init.js";
6
+ import { stopCommand, restartCommand, statusCommand, logsCommand } from "./cli/daemon.js";
7
+ import { sessionsCommand } from "./cli/sessions.js";
8
+ const program = new Command()
9
+ .name("tomo")
10
+ .description("Tomo — personal assistant powered by Claude")
11
+ .version("0.1.0");
12
+ program.addCommand(initCommand);
13
+ program.addCommand(startCommand);
14
+ program.addCommand(stopCommand);
15
+ program.addCommand(restartCommand);
16
+ program.addCommand(statusCommand);
17
+ program.addCommand(logsCommand);
18
+ program.addCommand(cronCommand);
19
+ program.addCommand(sessionsCommand);
20
+ program.parse();
21
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CAAC,6CAA6C,CAAC;KAC1D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AAEpC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,12 @@
1
+ interface TomoConfig {
2
+ telegramToken: string;
3
+ model: string;
4
+ workspaceDir: string;
5
+ sessionsDir: string;
6
+ historyLimit: number;
7
+ logsDir: string;
8
+ tomoHome: string;
9
+ }
10
+ export declare const config: TomoConfig;
11
+ export {};
12
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,UAAU,UAAU;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqCD,eAAO,MAAM,MAAM,YAAgB,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const HOME = homedir();
5
+ const TOMO_HOME = join(HOME, ".tomo");
6
+ const CONFIG_PATH = join(TOMO_HOME, "config.json");
7
+ function loadConfigFile() {
8
+ if (!existsSync(CONFIG_PATH))
9
+ return {};
10
+ try {
11
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
12
+ }
13
+ catch {
14
+ return {};
15
+ }
16
+ }
17
+ function buildConfig() {
18
+ const file = loadConfigFile();
19
+ const channels = (file.channels ?? {});
20
+ const telegramToken = process.env.TELEGRAM_BOT_TOKEN ??
21
+ channels.telegram?.token ??
22
+ "";
23
+ if (!telegramToken) {
24
+ throw new Error("Telegram bot token not found. Run 'tomo init' or set TELEGRAM_BOT_TOKEN.");
25
+ }
26
+ return {
27
+ telegramToken,
28
+ model: (process.env.CLAUDE_MODEL ?? file.model ?? "claude-sonnet-4-6"),
29
+ workspaceDir: process.env.TOMO_WORKSPACE ?? join(TOMO_HOME, "workspace"),
30
+ sessionsDir: process.env.SESSIONS_DIR ?? join(TOMO_HOME, "data", "sessions"),
31
+ historyLimit: Number(process.env.HISTORY_LIMIT ?? "20"),
32
+ logsDir: join(TOMO_HOME, "logs"),
33
+ tomoHome: TOMO_HOME,
34
+ };
35
+ }
36
+ export const config = buildConfig();
37
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;AACvB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACtC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAYnD,SAAS,cAAc;IACrB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAA2C,CAAC;IAEjF,MAAM,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,QAAQ,CAAC,QAAQ,EAAE,KAAK;QACxB,EAAE,CAAC;IAEL,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa;QACb,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,KAAK,IAAI,mBAAmB,CAAW;QAChF,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC;QACxE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC;QAC5E,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC;QACvD,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC;QAChC,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Agent } from "../agent.js";
2
+ export declare class CronScheduler {
3
+ private store;
4
+ private agent;
5
+ private timer;
6
+ constructor(agent: Agent);
7
+ start(): void;
8
+ stop(): void;
9
+ private tick;
10
+ private execute;
11
+ }
12
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/cron/scheduler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,qBAAa,aAAa;IACxB,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,KAAK,CAA+C;gBAEhD,KAAK,EAAE,KAAK;IAKxB,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;YAQE,IAAI;YAOJ,OAAO;CAgBtB"}
@@ -0,0 +1,48 @@
1
+ import { CronStore } from "./store.js";
2
+ import { log } from "../logger.js";
3
+ const POLL_INTERVAL_MS = 30_000; // Check every 30s
4
+ export class CronScheduler {
5
+ store;
6
+ agent;
7
+ timer = null;
8
+ constructor(agent) {
9
+ this.store = new CronStore();
10
+ this.agent = agent;
11
+ }
12
+ start() {
13
+ log.info("Cron scheduler started");
14
+ // Check immediately on start
15
+ this.tick();
16
+ this.timer = setInterval(() => this.tick(), POLL_INTERVAL_MS);
17
+ }
18
+ stop() {
19
+ if (this.timer) {
20
+ clearInterval(this.timer);
21
+ this.timer = null;
22
+ }
23
+ log.info("Cron scheduler stopped");
24
+ }
25
+ async tick() {
26
+ const dueJobs = this.store.getDueJobs();
27
+ for (const job of dueJobs) {
28
+ await this.execute(job.id);
29
+ }
30
+ }
31
+ async execute(jobId) {
32
+ const job = this.store.get(jobId);
33
+ if (!job)
34
+ return;
35
+ log.info({ jobId: job.id, name: job.name }, "Cron triggered: %s", job.message);
36
+ try {
37
+ const cronMessage = `System: Scheduled task "${job.name}" triggered. ${job.message}`;
38
+ await this.agent.handleCronMessage(cronMessage, job.channel, job.chatId);
39
+ this.store.markRun(jobId, "ok");
40
+ log.info({ jobId: job.id }, "Cron completed successfully");
41
+ }
42
+ catch (err) {
43
+ this.store.markRun(jobId, "error");
44
+ log.error({ err, jobId: job.id }, "Cron execution failed");
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/cron/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGnC,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,kBAAkB;AAEnD,MAAM,OAAO,aAAa;IAChB,KAAK,CAAY;IACjB,KAAK,CAAQ;IACb,KAAK,GAA0C,IAAI,CAAC;IAE5D,YAAY,KAAY;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK;QACH,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACnC,6BAA6B;QAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAChE,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,KAAa;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,oBAAoB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAE/E,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,2BAA2B,GAAG,CAAC,IAAI,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC;YACrF,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,WAAW,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACzE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,6BAA6B,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACnC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,uBAAuB,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import type { CronJob, CronSchedule } from "./types.js";
2
+ export declare class CronStore {
3
+ private jobs;
4
+ private path;
5
+ constructor(path?: string);
6
+ list(): CronJob[];
7
+ get(id: string): CronJob | undefined;
8
+ add(opts: {
9
+ name: string;
10
+ schedule: CronSchedule;
11
+ message: string;
12
+ channel?: string;
13
+ chatId?: string;
14
+ deleteAfterRun?: boolean;
15
+ }): CronJob;
16
+ remove(id: string): boolean;
17
+ markRun(id: string, status: "ok" | "error"): void;
18
+ getDueJobs(): CronJob[];
19
+ private load;
20
+ private save;
21
+ }
22
+ export declare function computeNextRun(schedule: CronSchedule, fromMs: number): number | null;
23
+ export declare function parseScheduleString(input: string): CronSchedule;
24
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cron/store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAIxD,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,IAAI,CAAS;gBAET,IAAI,SAAqB;IAKrC,IAAI,IAAI,OAAO,EAAE;IAIjB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIpC,GAAG,CAAC,IAAI,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,YAAY,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,GAAG,OAAO;IAqBX,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAU3B,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,IAAI;IAwBjD,UAAU,IAAI,OAAO,EAAE;IASvB,OAAO,CAAC,IAAI;IAaZ,OAAO,CAAC,IAAI;CAKb;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcpF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAgC/D"}
@@ -0,0 +1,143 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { randomUUID } from "node:crypto";
5
+ import { Cron } from "croner";
6
+ const DEFAULT_STORE_PATH = join(homedir(), ".tomo", "data", "cron", "jobs.json");
7
+ export class CronStore {
8
+ jobs = [];
9
+ path;
10
+ constructor(path = DEFAULT_STORE_PATH) {
11
+ this.path = path;
12
+ this.load();
13
+ }
14
+ list() {
15
+ return [...this.jobs];
16
+ }
17
+ get(id) {
18
+ return this.jobs.find((j) => j.id === id);
19
+ }
20
+ add(opts) {
21
+ const now = Date.now();
22
+ const job = {
23
+ id: randomUUID().slice(0, 8),
24
+ name: opts.name,
25
+ enabled: true,
26
+ schedule: opts.schedule,
27
+ message: opts.message,
28
+ channel: opts.channel,
29
+ chatId: opts.chatId,
30
+ deleteAfterRun: opts.deleteAfterRun ?? (opts.schedule.kind === "at"),
31
+ createdAt: now,
32
+ nextRunAt: computeNextRun(opts.schedule, now),
33
+ lastRunAt: null,
34
+ lastStatus: null,
35
+ };
36
+ this.jobs.push(job);
37
+ this.save();
38
+ return job;
39
+ }
40
+ remove(id) {
41
+ const before = this.jobs.length;
42
+ this.jobs = this.jobs.filter((j) => j.id !== id);
43
+ if (this.jobs.length < before) {
44
+ this.save();
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+ markRun(id, status) {
50
+ const job = this.get(id);
51
+ if (!job)
52
+ return;
53
+ const now = Date.now();
54
+ job.lastRunAt = now;
55
+ job.lastStatus = status;
56
+ if (status === "ok" && job.deleteAfterRun) {
57
+ this.remove(id);
58
+ return;
59
+ }
60
+ // Compute next run for recurring jobs
61
+ if (job.schedule.kind !== "at") {
62
+ job.nextRunAt = computeNextRun(job.schedule, now);
63
+ }
64
+ else {
65
+ job.enabled = false;
66
+ job.nextRunAt = null;
67
+ }
68
+ this.save();
69
+ }
70
+ getDueJobs() {
71
+ // Re-read from disk to pick up jobs added by external CLI calls
72
+ this.load();
73
+ const now = Date.now();
74
+ return this.jobs.filter((j) => j.enabled && j.nextRunAt !== null && j.nextRunAt <= now);
75
+ }
76
+ load() {
77
+ if (!existsSync(this.path)) {
78
+ this.jobs = [];
79
+ return;
80
+ }
81
+ try {
82
+ const data = JSON.parse(readFileSync(this.path, "utf-8"));
83
+ this.jobs = data.jobs ?? [];
84
+ }
85
+ catch {
86
+ this.jobs = [];
87
+ }
88
+ }
89
+ save() {
90
+ const dir = dirname(this.path);
91
+ mkdirSync(dir, { recursive: true });
92
+ writeFileSync(this.path, JSON.stringify({ version: 1, jobs: this.jobs }, null, 2) + "\n");
93
+ }
94
+ }
95
+ export function computeNextRun(schedule, fromMs) {
96
+ switch (schedule.kind) {
97
+ case "at": {
98
+ const ts = parseAtSchedule(schedule.at);
99
+ return ts > fromMs ? ts : null;
100
+ }
101
+ case "every":
102
+ return fromMs + schedule.everyMs;
103
+ case "cron": {
104
+ const cron = new Cron(schedule.expr, { timezone: schedule.tz });
105
+ const next = cron.nextRun();
106
+ return next ? next.getTime() : null;
107
+ }
108
+ }
109
+ }
110
+ export function parseScheduleString(input) {
111
+ // "in 20m", "in 2h", "in 1d"
112
+ const relMatch = input.match(/^in\s+(\d+)\s*(m|min|h|hr|d|day|s|sec)s?$/i);
113
+ if (relMatch) {
114
+ const val = Number(relMatch[1]);
115
+ const unit = relMatch[2].toLowerCase();
116
+ const ms = unit.startsWith("s") ? val * 1000
117
+ : unit.startsWith("m") ? val * 60_000
118
+ : unit.startsWith("h") ? val * 3_600_000
119
+ : val * 86_400_000;
120
+ return { kind: "at", at: new Date(Date.now() + ms).toISOString() };
121
+ }
122
+ // "every 30m", "every 2h"
123
+ const everyMatch = input.match(/^every\s+(\d+)\s*(m|min|h|hr|d|day|s|sec)s?$/i);
124
+ if (everyMatch) {
125
+ const val = Number(everyMatch[1]);
126
+ const unit = everyMatch[2].toLowerCase();
127
+ const ms = unit.startsWith("s") ? val * 1000
128
+ : unit.startsWith("m") ? val * 60_000
129
+ : unit.startsWith("h") ? val * 3_600_000
130
+ : val * 86_400_000;
131
+ return { kind: "every", everyMs: ms };
132
+ }
133
+ // ISO-8601 date
134
+ if (/^\d{4}-\d{2}-\d{2}/.test(input)) {
135
+ return { kind: "at", at: input };
136
+ }
137
+ // Assume cron expression
138
+ return { kind: "cron", expr: input, tz: Intl.DateTimeFormat().resolvedOptions().timeZone };
139
+ }
140
+ function parseAtSchedule(at) {
141
+ return new Date(at).getTime();
142
+ }
143
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cron/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAEjF,MAAM,OAAO,SAAS;IACZ,IAAI,GAAc,EAAE,CAAC;IACrB,IAAI,CAAS;IAErB,YAAY,IAAI,GAAG,kBAAkB;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,GAAG,CAAC,IAOH;QACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,GAAG,GAAY;YACnB,EAAE,EAAE,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC;YACpE,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;YAC7C,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,IAAI;SACjB,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,CAAC,EAAU;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACjD,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,EAAU,EAAE,MAAsB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC;QACpB,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;QAExB,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;YAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YAC/B,GAAG,CAAC,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;YACpB,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,UAAU;QACR,gEAAgE;QAChE,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CACrB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG,CAC/D,CAAC;IACJ,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;YACf,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;YAC1D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACjB,CAAC;IACH,CAAC;IAEO,IAAI;QACV,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5F,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,QAAsB,EAAE,MAAc;IACnE,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,IAAI,CAAC,CAAC,CAAC;YACV,MAAM,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACxC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,CAAC;QACD,KAAK,OAAO;YACV,OAAO,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC;QACnC,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACtC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC3E,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI;YAC1C,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM;gBACrC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,SAAS;oBACxC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;IACrE,CAAC;IAED,0BAA0B;IAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAChF,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI;YAC1C,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM;gBACrC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,SAAS;oBACxC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACxC,CAAC;IAED,gBAAgB;IAChB,IAAI,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IACnC,CAAC;IAED,yBAAyB;IACzB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,CAAC;AAC7F,CAAC;AAED,SAAS,eAAe,CAAC,EAAU;IACjC,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,35 @@
1
+ export type CronSchedule = {
2
+ kind: "at";
3
+ at: string;
4
+ } | {
5
+ kind: "every";
6
+ everyMs: number;
7
+ } | {
8
+ kind: "cron";
9
+ expr: string;
10
+ tz?: string;
11
+ };
12
+ export interface CronJob {
13
+ id: string;
14
+ name: string;
15
+ enabled: boolean;
16
+ schedule: CronSchedule;
17
+ message: string;
18
+ /** Channel + chatId to deliver response to */
19
+ channel?: string;
20
+ chatId?: string;
21
+ /** Auto-delete after successful run (for one-shots) */
22
+ deleteAfterRun: boolean;
23
+ createdAt: number;
24
+ /** Runtime state */
25
+ nextRunAt: number | null;
26
+ lastRunAt: number | null;
27
+ lastStatus: "ok" | "error" | null;
28
+ }
29
+ export type CronJobCreate = Pick<CronJob, "name" | "message"> & {
30
+ schedule: string;
31
+ channel?: string;
32
+ chatId?: string;
33
+ deleteAfterRun?: boolean;
34
+ };
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cron/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cron/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { Agent } from "./agent.js";
2
+ import { TelegramChannel } from "./channels/index.js";
3
+ import { config } from "./config.js";
4
+ const agent = new Agent();
5
+ agent.addChannel(new TelegramChannel(config.telegramToken));
6
+ // Graceful shutdown
7
+ const shutdown = async () => {
8
+ await agent.stop();
9
+ process.exit(0);
10
+ };
11
+ process.on("SIGINT", shutdown);
12
+ process.on("SIGTERM", shutdown);
13
+ agent.start();
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;AAE1B,KAAK,CAAC,UAAU,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAE5D,oBAAoB;AACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;IAC1B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;AACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAEhC,KAAK,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import pino from "pino";
2
+ export declare const log: pino.Logger<never, boolean>;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAwBxB,eAAO,MAAM,GAAG,6BAGd,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,26 @@
1
+ import pino from "pino";
2
+ import { mkdirSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ const logFile = process.env.TOMO_LOG_FILE;
5
+ // When running as daemon, log to file; otherwise pretty-print to stdout
6
+ const transport = logFile
7
+ ? (() => {
8
+ mkdirSync(dirname(logFile), { recursive: true });
9
+ return {
10
+ target: "pino/file",
11
+ options: { destination: logFile, mkdir: true },
12
+ };
13
+ })()
14
+ : {
15
+ target: "pino-pretty",
16
+ options: {
17
+ colorize: true,
18
+ ignore: "pid,hostname",
19
+ translateTime: "HH:MM:ss",
20
+ },
21
+ };
22
+ export const log = pino({
23
+ level: process.env.LOG_LEVEL ?? "debug",
24
+ transport,
25
+ });
26
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;AAE1C,wEAAwE;AACxE,MAAM,SAAS,GAAG,OAAO;IACvB,CAAC,CAAC,CAAC,GAAG,EAAE;QACJ,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE;SAC/C,CAAC;IACJ,CAAC,CAAC,EAAE;IACN,CAAC,CAAC;QACE,MAAM,EAAE,aAAa;QACrB,OAAO,EAAE;YACP,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,cAAc;YACtB,aAAa,EAAE,UAAU;SAC1B;KACF,CAAC;AAEN,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,OAAO;IACvC,SAAS;CACV,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type { Session, SessionMessage } from "./types.js";
2
+ export { SessionStore } from "./store.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { SessionStore } from "./store.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { Session, SessionMessage, SessionEntry } from "./types.js";
2
+ export declare class SessionStore {
3
+ private sessions;
4
+ private registry;
5
+ private dir;
6
+ private historyLimit;
7
+ constructor(dir: string, historyLimit: number);
8
+ /** Get or create a session, loading from disk on first access */
9
+ get(key: string): Session;
10
+ /** Append a message to the session and persist to disk */
11
+ append(key: string, message: SessionMessage): void;
12
+ /** Get the last N turns of conversation for LLM context */
13
+ getHistory(key: string): SessionMessage[];
14
+ /** Get the active SDK session ID for a channel key */
15
+ getSdkSessionId(key: string): string | undefined;
16
+ /** Link a new SDK session to a channel key */
17
+ setSdkSessionId(key: string, sessionId: string): void;
18
+ /** Touch the active session (update lastActiveAt) */
19
+ touchSession(key: string): void;
20
+ /** List all SDK session entries */
21
+ listSdkSessionIds(): [string, string][];
22
+ /** List all sessions including unlinked */
23
+ listAllSessions(): SessionEntry[];
24
+ /** Unlink a session (marks for deletion after TTL) */
25
+ clearSdkSessionId(key: string): void;
26
+ /** Delete expired unlinked sessions and their SDK JSONL files */
27
+ private cleanupExpired;
28
+ private get registryPath();
29
+ private loadRegistry;
30
+ private saveRegistry;
31
+ /** Migrate from the old simple key→value format */
32
+ private migrateOldFormat;
33
+ private transcriptPath;
34
+ private loadTranscript;
35
+ }
36
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sessions/store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAC;AAazF,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAS;gBAEjB,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;IAQ7C,iEAAiE;IACjE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAezB,0DAA0D;IAC1D,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IASlD,2DAA2D;IAC3D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE;IAqBzC,sDAAsD;IACtD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOhD,8CAA8C;IAC9C,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAgBrD,qDAAqD;IACrD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ/B,mCAAmC;IACnC,iBAAiB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;IAMvC,2CAA2C;IAC3C,eAAe,IAAI,YAAY,EAAE;IAIjC,sDAAsD;IACtD,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAepC,iEAAiE;IACjE,OAAO,CAAC,cAAc;IA2BtB,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,YAAY;IAKpB,mDAAmD;IACnD,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,cAAc;CAkBvB"}