{"version":3,"file":"plans-command.mjs","names":[],"sources":["../../../src/commands/plans-command.ts"],"sourcesContent":["/**\n * /plans command — list and manage scheduled plans via tappable Telegram commands.\n *\n * Sub-commands:\n *   /plans         — show all plans with status\n *   /plans_active   — show only active (scheduled/running) plans\n *   /plans_cancel   — show cancellable plans with IDs\n *   /plans_clear    — cancel all active plans\n */\n\nimport { getScheduler } from '../services/plan-scheduler.js';\nimport type { Plan } from '../services/plan-types.js';\n\nfunction statusIcon(status: string): string {\n  switch (status) {\n    case 'scheduled': return '[SCHED]';\n    case 'running': return '[RUN]';\n    case 'paused': return '[PAUSE]';\n    case 'completed': return '[DONE]';\n    case 'failed': return '[FAIL]';\n    case 'cancelled': return '[X]';\n    case 'validated': return '[READY]';\n    case 'draft': return '[DRAFT]';\n    default: return `[${status.toUpperCase()}]`;\n  }\n}\n\nfunction describeTrigger(plan: Plan): string {\n  const t = plan.trigger;\n  if (!t) return 'immediate';\n  switch (t.type) {\n    case 'immediate': return 'immediate';\n    case 'time': return `at ${new Date(t.at).toLocaleString()}`;\n    case 'interval': return `every ${formatMs(t.everyMs)}`;\n    case 'condition': return 'when condition met';\n    case 'cron': return `cron: ${t.expression}${t.timezone ? ` (${t.timezone})` : ''}`;\n    case 'price': return `${t.token} ${t.condition} $${t.threshold}`;\n    case 'onchain_event': return `on-chain: ${t.eventSignature} on ${t.contractAddress.slice(0, 10)}...`;\n    case 'balance': return `${t.token} balance ${t.condition} ${t.threshold}`;\n  }\n}\n\nfunction formatMs(ms: number): string {\n  if (ms >= 86_400_000) return `${Math.round(ms / 86_400_000)}d`;\n  if (ms >= 3_600_000) return `${Math.round(ms / 3_600_000)}h`;\n  if (ms >= 60_000) return `${Math.round(ms / 60_000)}m`;\n  return `${Math.round(ms / 1000)}s`;\n}\n\nfunction formatPlan(plan: Plan, showId = false): string {\n  const parts = [\n    `${statusIcon(plan.status)} **${plan.name}**`,\n    `  Trigger: ${describeTrigger(plan)}`,\n  ];\n  if (showId) {\n    parts.push(`  ID: \\`${plan.id}\\``);\n  }\n  const age = Date.now() - plan.createdAt;\n  parts.push(`  Created: ${formatMs(age)} ago`);\n  return parts.join('\\n');\n}\n\nexport const plansCommand = {\n  name: 'plans',\n  description: 'List scheduled plans',\n  acceptsArgs: false,\n  requireAuth: true,\n\n  handler: async () => {\n    const scheduler = getScheduler();\n    const plans = scheduler.listPlans();\n\n    if (plans.length === 0) {\n      return {\n        text: 'No plans found.\\n\\nAsk me to create one: \"when ETH hits $4000, sell half for USDC\"',\n      };\n    }\n\n    // Sort: active first, then by creation time descending\n    const active = ['scheduled', 'running', 'paused', 'validated', 'draft'];\n    plans.sort((a, b) => {\n      const aActive = active.includes(a.status) ? 0 : 1;\n      const bActive = active.includes(b.status) ? 0 : 1;\n      if (aActive !== bActive) return aActive - bActive;\n      return b.createdAt - a.createdAt;\n    });\n\n    const lines = [\n      `**Plans** (${plans.length} total)`,\n      '',\n      ...plans.slice(0, 15).map(p => formatPlan(p, true)),\n    ];\n\n    if (plans.length > 15) {\n      lines.push(`\\n...and ${plans.length - 15} more`);\n    }\n\n    const activeCount = plans.filter(p => active.includes(p.status)).length;\n    lines.push('');\n    lines.push(`Active: ${activeCount} | /plans_active | /plans_cancel`);\n\n    return { text: lines.join('\\n') };\n  },\n};\n\nexport const plansActiveCommand = {\n  name: 'plans_active',\n  description: 'Show active plans only',\n  acceptsArgs: false,\n  requireAuth: true,\n\n  handler: async () => {\n    const scheduler = getScheduler();\n    const plans = scheduler.listPlans()\n      .filter(p => ['scheduled', 'running', 'paused'].includes(p.status));\n\n    if (plans.length === 0) {\n      return { text: 'No active plans.\\n\\nAsk me to create one, or tap /plans to see all plans.' };\n    }\n\n    const lines = [\n      `**Active Plans** (${plans.length})`,\n      '',\n      ...plans.map(p => formatPlan(p, true)),\n    ];\n\n    return { text: lines.join('\\n') };\n  },\n};\n\nexport const plansCancelCommand = {\n  name: 'plans_cancel',\n  description: 'Show plans that can be cancelled (with IDs)',\n  acceptsArgs: true,\n  requireAuth: true,\n\n  handler: async (ctx: any) => {\n    const scheduler = getScheduler();\n    const planId = (ctx?.args ?? ctx?.commandBody ?? '').trim();\n\n    // If a plan ID is provided, cancel it directly\n    if (planId) {\n      const success = scheduler.cancelPlan(planId);\n      if (!success) {\n        return { text: `Plan \\`${planId}\\` not found or already completed.` };\n      }\n      return { text: `Cancelled plan \\`${planId}\\`.` };\n    }\n\n    // Otherwise show cancellable plans\n    const cancellable = scheduler.listPlans()\n      .filter(p => ['scheduled', 'running', 'paused', 'validated', 'draft'].includes(p.status));\n\n    if (cancellable.length === 0) {\n      return { text: 'No plans to cancel.' };\n    }\n\n    const lines = [\n      `**Cancellable Plans** (${cancellable.length})`,\n      '',\n      ...cancellable.map(p => formatPlan(p, true)),\n      '',\n      'To cancel a plan, tell me: \"cancel plan PLAN_ID\"',\n      'To cancel all: /plans_clear',\n    ];\n\n    return { text: lines.join('\\n') };\n  },\n};\n\nexport const plansClearCommand = {\n  name: 'plans_clear',\n  description: 'Cancel all active plans',\n  acceptsArgs: false,\n  requireAuth: true,\n\n  handler: async () => {\n    const scheduler = getScheduler();\n    const active = scheduler.listPlans()\n      .filter(p => ['scheduled', 'running', 'paused', 'validated', 'draft'].includes(p.status));\n\n    if (active.length === 0) {\n      return { text: 'No active plans to clear.' };\n    }\n\n    let cancelled = 0;\n    for (const plan of active) {\n      if (scheduler.cancelPlan(plan.id)) cancelled++;\n    }\n\n    return {\n      text: `Cancelled ${cancelled} plan${cancelled !== 1 ? 's' : ''}.\\n\\nAll scheduled operations have been stopped.`,\n    };\n  },\n};\n"],"mappings":"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"}