@michael-joseph-miller/ant-bot 0.1.0 → 0.1.1
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/CHANGELOG.md +23 -5
- package/README.md +69 -24
- package/dist/{browser-OHRD7YI3.js → browser-3UAU6ARY.js} +3 -3
- package/dist/{browser-OHRD7YI3.js.map → browser-3UAU6ARY.js.map} +1 -1
- package/dist/{bundled-46DUK5PG.js → bundled-26SJR7EB.js} +5 -5
- package/dist/{bundled-46DUK5PG.js.map → bundled-26SJR7EB.js.map} +1 -1
- package/dist/{chunk-AHAON6J7.js → chunk-CNCHYZVD.js} +2 -2
- package/dist/{chunk-AHAON6J7.js.map → chunk-CNCHYZVD.js.map} +1 -1
- package/dist/{chunk-Z2YT2PZN.js → chunk-GYWJUW4E.js} +6 -6
- package/dist/{chunk-Z2YT2PZN.js.map → chunk-GYWJUW4E.js.map} +3 -3
- package/dist/{chunk-7BOHBPB2.js → chunk-JCZBDGBA.js} +3 -3
- package/dist/{chunk-7BOHBPB2.js.map → chunk-JCZBDGBA.js.map} +1 -1
- package/dist/{chunk-KSQOVWP5.js → chunk-MSSHR6SW.js} +6 -6
- package/dist/chunk-MSSHR6SW.js.map +7 -0
- package/dist/{chunk-DYIJTUMY.js → chunk-XMIPBKUY.js} +8 -7
- package/dist/{chunk-DYIJTUMY.js.map → chunk-XMIPBKUY.js.map} +3 -3
- package/dist/index.js +28 -28
- package/dist/index.js.map +2 -2
- package/dist/{install-IY2M3OUQ.js → install-KTEW2CW3.js} +3 -3
- package/dist/{plugin-POMHVGD4.js → plugin-WYUCG6F7.js} +2 -2
- package/dist/{plugin-POMHVGD4.js.map → plugin-WYUCG6F7.js.map} +1 -1
- package/dist/{scheduler-Q7OHNGP6.js → scheduler-VZVHQWGD.js} +3 -3
- package/dist/scheduler-VZVHQWGD.js.map +7 -0
- package/dist/server.js +41 -41
- package/dist/server.js.map +3 -3
- package/dist/{skills-66WRX64H.js → skills-JUHWFBD6.js} +2 -2
- package/dist/skills-spec.js +2 -2
- package/dist/skills-spec.js.map +1 -1
- package/dist/{tools-P5537ASX.js → tools-YNE7ZRPR.js} +2 -2
- package/dist/{tools-P5537ASX.js.map → tools-YNE7ZRPR.js.map} +1 -1
- package/package.json +1 -1
- package/skills/README.md +1 -1
- package/web/dist/assets/index-CrbtUidR.js +130 -0
- package/web/dist/index.html +1 -1
- package/dist/chunk-KSQOVWP5.js.map +0 -7
- package/dist/scheduler-Q7OHNGP6.js.map +0 -7
- package/web/dist/assets/index-BLQ8rPiN.js +0 -130
- /package/dist/{install-IY2M3OUQ.js.map → install-KTEW2CW3.js.map} +0 -0
- /package/dist/{skills-66WRX64H.js.map → skills-JUHWFBD6.js.map} +0 -0
package/web/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>ant-bot</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CrbtUidR.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-IEIkG_jd.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../packages/server/src/skills/skills.ts", "../../packages/server/src/util/ids.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Store } from '../db/store.js';\nimport { slugify } from '../util/ids.js';\nimport type { Skill } from '@antbot/shared';\n\n/**\n * A skill is a directory `<skillsDir>/<slug>/SKILL.md` with a YAML-ish frontmatter block\n * (name, description) followed by a markdown body in the 6-part shape from outline \u00A78:\n * when to use it, required inputs and access, sequence of work, how to validate the result,\n * what to return, what requires approval.\n */\n\nexport const SKILL_TEMPLATE = `## When to use it\n\nDescribe the trigger: what request or situation should make a bot reach for this skill instead\nof improvising. Be specific enough that a bot with this skill enabled recognizes the moment.\n\n## Required inputs and access\n\nList what the skill needs before it can run: source systems, connectors/MCP servers, files,\ncredentials, or information the user must supply. Say what happens if something required is\nmissing.\n\n## Sequence of work\n\nWrite the ordered steps the bot should follow. Prefer \"gather \u2192 draft/reconcile \u2192 recommend\"\nover \"act immediately\" \u2014 automate preparation before execution.\n\n## How to validate the result\n\nState how the bot should check its own output before returning it: what to cross-check, what\ncounts as \"current\" data, and what stale-data policy applies. Per outline \u00A78: **if the source\ndata is unavailable, report the failure instead of using old data.**\n\n## What to return\n\nDescribe the shape of the deliverable: a document, a spreadsheet, a draft message, a linked\nwatch list, or a recommendation with evidence. Separate facts found, assumptions, actions\nalready taken, actions waiting for approval, and open questions where relevant.\n\n## What requires approval\n\nName the actions this skill must never take without an explicit human \"go ahead\": sending,\npublishing, purchasing, deleting, overwriting, or any production change. This section should\nnever be empty \u2014 every skill has an approval boundary.\n`;\n\nexport interface WriteSkillInput {\n name: string;\n description?: string;\n bodyMd: string;\n source?: 'user' | 'taught' | 'imported';\n}\n\nexport interface InstalledSkill {\n skill: Skill;\n executables: string[];\n manifestText: string;\n replaced: boolean;\n}\n\nexport interface ParsedSkillFile {\n name: string;\n description: string;\n bodyMd: string;\n}\n\n/** Parse the `---\\nname: \"...\"\\ndescription: \"...\"\\n---\\n<body>` shape. Pure, no I/O. */\nexport function parseFrontmatter(md: string): ParsedSkillFile {\n const m = md.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!m) return { name: '', description: '', bodyMd: md };\n const [, fm, rest] = m;\n const data: Record<string, string> = {};\n const lines = (fm ?? '').split(/\\r?\\n/);\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const idx = line.indexOf(':');\n if (idx === -1 || /^\\s/.test(line)) continue; // continuation lines are consumed below\n const key = line.slice(0, idx).trim();\n let value = line.slice(idx + 1).trim();\n\n // YAML block scalars: `>` folds newlines into spaces, `|` keeps them. Published\n // skills use both for long descriptions, and the description is what the model\n // matches on \u2014 dropping it to a bare \">\" makes the skill invisible.\n const block = /^([>|])([-+]?)$/.exec(value);\n if (block) {\n const folded = block[1] === '>';\n const collected: string[] = [];\n while (i + 1 < lines.length) {\n const next = lines[i + 1]!;\n if (next.trim() !== '' && !/^\\s/.test(next)) break; // next top-level key\n collected.push(next.trim());\n i++;\n }\n while (collected.length && collected[collected.length - 1] === '') collected.pop();\n value = folded\n ? collected.filter((l) => l !== '').join(' ')\n : collected.join('\\n');\n // A folded block separates paragraphs (blank lines) with a newline.\n if (folded && collected.includes('')) {\n value = collected\n .reduce<string[]>((acc, l) => {\n if (l === '') acc.push('');\n else if (acc.length && acc[acc.length - 1] !== '') acc[acc.length - 1] += ` ${l}`;\n else acc[acc.length - 1] = l;\n return acc;\n }, [''])\n .filter((para) => para !== '')\n .join('\\n');\n }\n data[key] = value;\n continue;\n }\n\n if (value.startsWith('\"') && value.endsWith('\"') && value.length >= 2) {\n value = value.slice(1, -1).replace(/\\\\\"/g, '\"');\n }\n data[key] = value;\n }\n return {\n name: data.name ?? '',\n description: data.description ?? '',\n bodyMd: (rest ?? '').replace(/^\\r?\\n/, ''),\n };\n}\n\n/** Render a SKILL.md file from its parts. Pure, no I/O. */\nexport function renderSkillFile(input: { name: string; description: string; bodyMd: string }): string {\n const esc = (s: string): string => s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `---\\nname: \"${esc(input.name)}\"\\ndescription: \"${esc(input.description)}\"\\n---\\n\\n${input.bodyMd.trim()}\\n`;\n}\n\n/**\n * Raised when a source resolves to more than one skill and the caller did not opt in.\n * Carries the names so the caller can tell the human (or the model) what to narrow to.\n */\nexport class MultipleSkillsError extends Error {\n constructor(\n readonly source: string,\n readonly names: string[],\n ) {\n super(`\"${source}\" contains ${names.length} skills, not one.`);\n this.name = 'MultipleSkillsError';\n }\n}\n\nexport class SkillStore {\n constructor(\n private store: Store,\n private skillsDir: string,\n ) {}\n\n private slugDir(slug: string): string {\n return path.join(this.skillsDir, slug);\n }\n\n /** Resolve a stored skill path and assert it is inside the skills dir (no path traversal). */\n private assertInsideSkillsDir(candidate: string): string {\n const root = path.resolve(this.skillsDir) + path.sep;\n const resolved = path.resolve(candidate);\n if (resolved !== path.resolve(this.skillsDir) && !resolved.startsWith(root)) {\n throw new Error(`Refusing to touch a skill path outside the skills directory: ${candidate}`);\n }\n return resolved;\n }\n\n /** Create (or overwrite by slug) a skill directory + SKILL.md, and register it. */\n writeSkill(input: WriteSkillInput): Skill {\n const existing = new Set(this.store.listSkills().map((s) => s.slug));\n const slug = slugify(input.name, existing);\n const dir = this.assertInsideSkillsDir(this.slugDir(slug));\n fs.mkdirSync(dir, { recursive: true });\n const content = renderSkillFile({\n name: input.name,\n description: input.description ?? '',\n bodyMd: input.bodyMd,\n });\n fs.writeFileSync(path.join(dir, 'SKILL.md'), content, 'utf8');\n return this.store.createSkill({\n slug,\n name: input.name,\n description: input.description,\n path: dir,\n source: input.source,\n });\n }\n\n /**\n * Install every skill found at `source` and register it. Existing slugs are replaced in\n * place so re-installing upgrades rather than accumulating copies.\n *\n * Throws `MultipleSkillsError` if `source` holds more than one skill unless\n * `allowMultiple` is set, so a caller cannot install a hundred skills by accident.\n */\n async installFromSource(\n source: string,\n opts: { allowMultiple?: boolean } = {},\n ): Promise<InstalledSkill[]> {\n const { stage, copyTree, renderManifest } = await import('./install.js');\n const parsed = (await import('./install.js')).parseSkillSource(source);\n const { staged, cleanup } = await stage(parsed);\n try {\n // Installing a whole monorepo when one skill was wanted is easy to do by accident and\n // tedious to undo, so a multi-skill source has to be asked for explicitly.\n if (staged.length > 1 && !opts.allowMultiple) {\n throw new MultipleSkillsError(source, staged.map((sk) => sk.name));\n }\n const out: InstalledSkill[] = [];\n for (const skill of staged) {\n const existing = this.store.getSkillBySlug(slugify(skill.name, new Set()));\n const others = new Set(this.store.listSkills().map((x) => x.slug));\n if (existing) others.delete(existing.slug);\n const slug = existing?.slug ?? slugify(skill.name, others);\n\n const dir = this.assertInsideSkillsDir(this.slugDir(slug));\n fs.rmSync(dir, { recursive: true, force: true });\n copyTree(skill.dir, dir);\n\n const registered = existing\n ? (this.store.updateSkill?.(existing.id, { name: skill.name, description: skill.description }) ?? existing)\n : this.store.createSkill({\n slug, name: skill.name, description: skill.description, path: dir, source: 'imported',\n });\n\n // Installed skills are held to the same spec as the ones ant-bot ships. This never\n // blocks an install \u2014 a third-party skill with a sloppy frontmatter still works \u2014 but\n // a `name` that does not match its directory makes the skill impossible for a bot to\n // enable, and silence about that is how it stays broken.\n const { validateSkillDir, formatSpecViolations } = await import('./spec.js');\n const specNotes = formatSpecViolations(validateSkillDir(dir));\n\n out.push({\n skill: registered,\n executables: skill.manifest.executables,\n manifestText: renderManifest(skill, dir) + specNotes,\n replaced: Boolean(existing),\n });\n }\n return out;\n } finally {\n cleanup();\n }\n }\n\n /** Read a skill's SKILL.md and return it with the parsed body (frontmatter stripped). */\n readSkill(id: string): (Skill & { bodyMd: string }) | null {\n const skill = this.store.getSkill(id);\n if (!skill) return null;\n const file = path.join(skill.path, 'SKILL.md');\n if (!fs.existsSync(file)) return { ...skill, bodyMd: '' };\n const raw = fs.readFileSync(file, 'utf8');\n const { bodyMd } = parseFrontmatter(raw);\n return { ...skill, bodyMd };\n }\n\n /** Remove a skill's directory and its db row. Guards against a maliciously escaped path. */\n deleteSkill(id: string): void {\n const skill = this.store.getSkill(id);\n if (!skill) return;\n const resolved = this.assertInsideSkillsDir(skill.path);\n fs.rmSync(resolved, { recursive: true, force: true });\n this.store.deleteSkill(id);\n }\n\n /**\n * Bring the registry back in line with what is actually on disk.\n *\n * A row can outlive its directory \u2014 a skill removed by hand, or a layout migration that\n * moved files without rewriting paths \u2014 and the UI then lists skills that cannot load.\n * Rows whose directory moved are repaired in place so bot assignments survive; rows with\n * nothing left on disk are dropped. Returns what it did, for the boot log.\n */\n reconcile(): { repaired: string[]; removed: string[] } {\n const repaired: string[] = [];\n const removed: string[] = [];\n for (const skill of this.store.listSkills()) {\n if (fs.existsSync(path.join(skill.path, 'SKILL.md'))) continue;\n const expected = path.join(this.skillsDir, skill.slug);\n if (fs.existsSync(path.join(expected, 'SKILL.md'))) {\n this.store.updateSkill(skill.id, { path: expected });\n repaired.push(skill.slug);\n } else {\n this.store.deleteSkill(skill.id);\n removed.push(skill.slug);\n }\n }\n return { repaired, removed };\n }\n\n /**\n * Re-read `name`/`description` from disk for the given slugs and update their rows.\n *\n * Registered names are not cosmetic \u2014 `manager.ts` hands them to the SDK as `enabledSkills`,\n * so a row whose name has drifted from its SKILL.md silently stops matching and the skill\n * quietly disappears from every bot that had it enabled. Any writer of a skill file that a\n * row already points at has to call this; `syncFromDisk` cannot, since it only ever looks at\n * slugs the db has never seen. Returns the slugs whose stored metadata actually changed.\n */\n refreshFromDisk(slugs: string[]): string[] {\n const changed: string[] = [];\n for (const slug of slugs) {\n const registered = this.store.getSkillBySlug(slug);\n if (!registered) continue;\n const file = path.join(this.skillsDir, slug, 'SKILL.md');\n if (!fs.existsSync(file)) continue;\n const { name, description } = parseFrontmatter(fs.readFileSync(file, 'utf8'));\n const nextName = name || registered.name;\n if (nextName === registered.name && description === (registered.description ?? '')) continue;\n this.store.updateSkill(registered.id, { name: nextName, description });\n changed.push(slug);\n }\n return changed;\n }\n\n /**\n * Scan the skills dir at boot and register any SKILL.md not yet known to the db, so users\n * can drop skills in by hand. Idempotent: already-registered slugs are left untouched.\n */\n syncFromDisk(): Skill[] {\n if (!fs.existsSync(this.skillsDir)) return [];\n const added: Skill[] = [];\n for (const entry of fs.readdirSync(this.skillsDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const slug = entry.name;\n const dir = path.join(this.skillsDir, slug);\n const file = path.join(dir, 'SKILL.md');\n if (!fs.existsSync(file)) continue;\n if (this.store.getSkillBySlug(slug)) continue;\n const raw = fs.readFileSync(file, 'utf8');\n const { name, description } = parseFrontmatter(raw);\n const skill = this.store.createSkill({\n slug,\n name: name || slug,\n description,\n path: dir,\n source: 'imported',\n });\n added.push(skill);\n }\n return added;\n }\n}\n", "import { randomUUID } from 'node:crypto';\nexport const newId = (): string => randomUUID();\nexport const now = (): number => Date.now();\nexport function slugify(name: string, existing: Set<string> = new Set()): string {\n const base =\n name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'bot';\n let slug = base;\n let n = 2;\n while (existing.has(slug)) slug = `${base}-${n++}`;\n return slug;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../packages/server/src/scheduler/scheduler.ts"],
|
|
4
|
-
"sourcesContent": ["import cron, { type ScheduledTask } from 'node-cron';\nimport type { Store } from '../db/store.js';\nimport type { EventBus } from '../util/bus.js';\nimport { logger } from '../util/log.js';\nimport type { Routine, Settings, TurnOrigin } from '@antbot/shared';\n\nconst log = logger('scheduler');\n\n/**\n * The subset of BotManager.enqueue the scheduler needs. Kept minimal and structural (rather than\n * importing BotManager directly) so tests can inject a fake manager without spawning real turns.\n */\nexport interface SchedulerEnqueueArgs {\n botId: string;\n threadId: string;\n prompt: string;\n origin: TurnOrigin;\n hops: number;\n priority?: number;\n onDone?: (summary: string, ok: boolean) => void;\n}\nexport interface SchedulerManager {\n enqueue(job: SchedulerEnqueueArgs): { id: string };\n}\n\nexport interface SchedulerDeps {\n store: Store;\n bus: EventBus;\n manager: SchedulerManager;\n getSettings: () => Settings;\n}\n\n/* ------------------------------- cron parsing ------------------------------- */\n\ninterface CronField {\n values: Set<number>;\n wildcard: boolean;\n}\n\ninterface ParsedCron {\n minute: CronField;\n hour: CronField;\n dayOfMonth: CronField;\n month: CronField;\n dayOfWeek: CronField;\n}\n\nfunction parseCronFieldPart(field: string, min: number, max: number): CronField {\n const trimmed = field.trim();\n if (trimmed === '*') {\n const values = new Set<number>();\n for (let v = min; v <= max; v++) values.add(v);\n return { values, wildcard: true };\n }\n const values = new Set<number>();\n for (const part of trimmed.split(',')) {\n const m = part.match(/^(\\*|\\d+)(?:-(\\d+))?(?:\\/(\\d+))?$/);\n if (!m) throw new Error(`Invalid cron field: \"${part}\"`);\n const [, startStr, endStr, stepStr] = m;\n const step = stepStr ? parseInt(stepStr, 10) : 1;\n let start: number;\n let end: number;\n if (startStr === '*') {\n start = min;\n end = max;\n } else {\n start = parseInt(startStr!, 10);\n end = endStr !== undefined ? parseInt(endStr, 10) : stepStr ? max : start;\n }\n for (let v = start; v <= end; v += step) values.add(v);\n }\n return { values, wildcard: false };\n}\n\n/** Parse a standard 5-field cron expression (minute hour dom month dow). Pure, no I/O. */\nexport function parseCronExpr(expr: string): ParsedCron {\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) throw new Error(`Cron expression must have 5 fields: \"${expr}\"`);\n const [minute, hour, dayOfMonth, month, dayOfWeekRaw] = parts as [string, string, string, string, string];\n const dayOfWeek = parseCronFieldPart(dayOfWeekRaw, 0, 7);\n // normalize 7 (alt-Sunday) into 0\n if (dayOfWeek.values.has(7)) {\n dayOfWeek.values.delete(7);\n dayOfWeek.values.add(0);\n }\n return {\n minute: parseCronFieldPart(minute, 0, 59),\n hour: parseCronFieldPart(hour, 0, 23),\n dayOfMonth: parseCronFieldPart(dayOfMonth, 1, 31),\n month: parseCronFieldPart(month, 1, 12),\n dayOfWeek,\n };\n}\n\ninterface TzParts {\n month: number;\n day: number;\n hour: number;\n minute: number;\n weekday: number;\n}\n\nconst WEEKDAY_INDEX: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };\n\nfunction makeTzFormatter(timeZone: string): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour12: false,\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n weekday: 'short',\n });\n}\n\nfunction tzParts(dtf: Intl.DateTimeFormat, ms: number): TzParts {\n const parts = dtf.formatToParts(new Date(ms));\n const map: Record<string, string> = {};\n for (const p of parts) map[p.type] = p.value;\n let hour = parseInt(map.hour ?? '0', 10);\n if (hour === 24) hour = 0; // some ICU builds report midnight as \"24\" under hour12:false\n return {\n month: parseInt(map.month ?? '1', 10),\n day: parseInt(map.day ?? '1', 10),\n hour,\n minute: parseInt(map.minute ?? '0', 10),\n weekday: WEEKDAY_INDEX[map.weekday ?? 'Sun'] ?? 0,\n };\n}\n\nconst MAX_LOOKAHEAD_MINUTES = 366 * 24 * 60;\n\n/**\n * Compute the next fire time (ms, strictly after `fromMs`) for a 5-field cron expression in a\n * given IANA timezone. Implemented by iterating minute-by-minute over a bounded window \u2014 no\n * dependency on node-cron internals. Returns null if nothing matches within 366 days.\n */\nexport function nextRunAt(cronExpr: string, timezone: string, fromMs: number): number | null {\n const fields = parseCronExpr(cronExpr);\n const dtf = makeTzFormatter(timezone);\n const bothWildcard = fields.dayOfMonth.wildcard && fields.dayOfWeek.wildcard;\n\n let t = Math.floor(fromMs / 60000) * 60000 + 60000;\n for (let i = 0; i < MAX_LOOKAHEAD_MINUTES; i++, t += 60000) {\n const p = tzParts(dtf, t);\n if (!fields.minute.values.has(p.minute)) continue;\n if (!fields.hour.values.has(p.hour)) continue;\n if (!fields.month.values.has(p.month)) continue;\n const domMatch = fields.dayOfMonth.values.has(p.day);\n const dowMatch = fields.dayOfWeek.values.has(p.weekday);\n const dayOk = bothWildcard\n ? true\n : fields.dayOfMonth.wildcard\n ? dowMatch\n : fields.dayOfWeek.wildcard\n ? domMatch\n : domMatch || dowMatch;\n if (!dayOk) continue;\n return t;\n }\n return null;\n}\n\n/* ------------------------------- human summary ------------------------------- */\n\nconst DOW_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\n\n/** Render a short human string for common cron shapes; falls back to the raw expression. */\nexport function cronToHuman(expr: string): string {\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) return expr;\n const [min, hour, dom, month, dow] = parts as [string, string, string, string, string];\n const pad = (n: string): string => n.padStart(2, '0');\n\n if (min.startsWith('*/') && hour === '*' && dom === '*' && month === '*' && dow === '*') {\n return `Every ${min.slice(2)} minutes`;\n }\n if (hour.startsWith('*/') && min === '0' && dom === '*' && month === '*' && dow === '*') {\n return `Every ${hour.slice(2)} hours`;\n }\n if (/^\\d+$/.test(min) && /^\\d+$/.test(hour)) {\n const time = `${pad(hour)}:${pad(min)}`;\n if (dom === '*' && month === '*') {\n if (dow === '*') return `Every day at ${time}`;\n if (dow === '1-5') return `Every weekday at ${time}`;\n if (dow === '0,6' || dow === '6,0') return `Every weekend day at ${time}`;\n if (/^\\d$/.test(dow)) return `Every ${DOW_NAMES[parseInt(dow, 10)]} at ${time}`;\n }\n if (/^\\d+$/.test(dom) && month === '*' && dow === '*') {\n return `Monthly on day ${dom} at ${time}`;\n }\n }\n return expr;\n}\n\n/* --------------------------------- away guard --------------------------------- */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Outline \u00A78: after a long absence, ask whether to keep routines running, and pause if there's\n * no response. `ok` under 7 days away, `ask` between 7 and 14 days, `pause` beyond 14 days.\n */\nexport function checkAwayGuard(lastUserActivityMs: number, nowMs: number): 'ok' | 'ask' | 'pause' {\n const gap = nowMs - lastUserActivityMs;\n if (gap > 14 * DAY_MS) return 'pause';\n if (gap > 7 * DAY_MS) return 'ask';\n return 'ok';\n}\n\n/* --------------------------------- prompt shape --------------------------------- */\n\n/** Build the turn prompt for a routine fire, per outline \u00A78 (naming + stale-data doctrine). */\nexport function buildRoutinePrompt(routine: Routine, isTest: boolean): string {\n const header = isTest\n ? `This is a **test run** of your routine \"${routine.name}\". Test runs perform real work \u2014 treat it like a live run, and flag anything before doing something irreversible.`\n : `This is a **scheduled run** of your routine \"${routine.name}\" (${routine.cronExpr}, ${routine.timezone}).`;\n return [\n header,\n '',\n 'If the source data is unavailable, report the failure instead of using old data.',\n '',\n '---',\n '',\n routine.instructionMd,\n ].join('\\n');\n}\n\n/* ---------------------------------- scheduler ---------------------------------- */\n\nexport class Scheduler {\n private tasks = new Map<string, ScheduledTask>();\n private paused = false;\n\n constructor(private deps: SchedulerDeps) {}\n\n /** Load every enabled routine and schedule it with node-cron. */\n start(): void {\n for (const routine of this.deps.store.listRoutines()) {\n if (routine.enabled) this.scheduleRoutine(routine);\n }\n }\n\n /** Cancel all scheduled tasks and release their timers. */\n stop(): void {\n for (const task of this.tasks.values()) {\n task.stop();\n void task.destroy();\n }\n this.tasks.clear();\n }\n\n /** Re-sync a single routine (call after create/update/delete/enable-toggle). */\n reload(routineId: string): void {\n this.unschedule(routineId);\n const routine = this.deps.store.getRoutine(routineId);\n if (routine?.enabled && !this.paused) this.scheduleRoutine(routine);\n }\n\n /** Re-sync every routine from scratch. */\n syncAll(): void {\n this.stop();\n this.start();\n }\n\n /** Pause every scheduled routine without forgetting its schedule (outline \u00A78 away-guard). */\n pauseAll(): void {\n this.paused = true;\n for (const task of this.tasks.values()) task.stop();\n }\n\n /** Resume every scheduled routine after a pause. */\n resumeAll(): void {\n this.paused = false;\n for (const task of this.tasks.values()) task.start();\n }\n\n private unschedule(routineId: string): void {\n const task = this.tasks.get(routineId);\n if (!task) return;\n task.stop();\n void task.destroy();\n this.tasks.delete(routineId);\n }\n\n private scheduleRoutine(routine: Routine): void {\n if (!cron.validate(routine.cronExpr)) {\n log.warn(`routine ${routine.id} has an invalid cron expression: \"${routine.cronExpr}\"`);\n return;\n }\n const task = cron.schedule(\n routine.cronExpr,\n () => {\n this.fire(routine.id, false);\n },\n { timezone: routine.timezone, unref: true },\n );\n if (this.paused) task.stop();\n this.tasks.set(routine.id, task);\n this.updateNextRunAt(routine);\n }\n\n private updateNextRunAt(routine: Routine): void {\n const next = nextRunAt(routine.cronExpr, routine.timezone, Date.now());\n this.deps.store.updateRoutine(routine.id, { nextRunAt: next });\n }\n\n /** Fire a routine now: starts a run record, publishes events, and enqueues the turn. */\n private fire(routineId: string, isTest: boolean): string {\n const { store, bus, manager } = this.deps;\n const routine = store.getRoutine(routineId);\n if (!routine) throw new Error(`Unknown routine: ${routineId}`);\n\n const bot = store.getBot(routine.botId);\n const run = store.startRun(routineId, isTest, bot?.threadId ?? undefined);\n bus.publish({ type: 'routine.run', threadId: bot?.threadId ?? null, botId: routine.botId, run });\n\n const next = isTest ? routine.nextRunAt : nextRunAt(routine.cronExpr, routine.timezone, Date.now());\n store.updateRoutine(routineId, { lastRunAt: run.startedAt, nextRunAt: next });\n\n if (!bot || !bot.threadId) {\n const failed = store.finishRun(run.id, 'failed', 'Owning bot no longer exists.');\n if (failed) bus.publish({ type: 'routine.run', threadId: null, botId: routine.botId, run: failed });\n return run.id;\n }\n\n manager.enqueue({\n botId: bot.id,\n threadId: bot.threadId,\n prompt: buildRoutinePrompt(routine, isTest),\n origin: 'routine',\n hops: 0,\n priority: 10,\n onDone: (summary, ok) => {\n const finished = store.finishRun(run.id, ok ? 'ok' : 'failed', summary);\n if (finished) bus.publish({ type: 'routine.run', threadId: bot.threadId, botId: routine.botId, run: finished });\n },\n });\n\n return run.id;\n }\n\n /** Fire a routine immediately as a test run (real work, marked `isTest`). Returns the run id. */\n testRun(routineId: string): string {\n return this.fire(routineId, true);\n }\n\n /**\n * Apply the outline \u00A78 away-guard: pause all routines beyond 14 days of inactivity, or notify\n * asking whether to keep them running between 7 and 14 days.\n */\n checkAndApplyAwayGuard(lastUserActivityMs: number, nowMs = Date.now()): 'ok' | 'ask' | 'pause' {\n const result = checkAwayGuard(lastUserActivityMs, nowMs);\n if (result === 'pause') {\n this.pauseAll();\n } else if (result === 'ask') {\n this.deps.bus.publish({\n type: 'notify',\n threadId: null,\n botId: null,\n title: 'Still away?',\n body: \"It's been a while since you were last active. Keep routines running, or pause them?\",\n level: 'warn',\n });\n }\n return result;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["values"]
|
|
7
|
-
}
|