squads-cli 0.6.1 → 0.7.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 (112) hide show
  1. package/README.md +196 -1152
  2. package/dist/auth-YW3UPFSB.js +23 -0
  3. package/dist/autonomy-BWTVDEAT.js +102 -0
  4. package/dist/autonomy-BWTVDEAT.js.map +1 -0
  5. package/dist/chunk-3KCWNZWW.js +401 -0
  6. package/dist/chunk-3KCWNZWW.js.map +1 -0
  7. package/dist/chunk-67RO2HKR.js +174 -0
  8. package/dist/chunk-67RO2HKR.js.map +1 -0
  9. package/dist/chunk-7JVD7RD4.js +275 -0
  10. package/dist/chunk-7JVD7RD4.js.map +1 -0
  11. package/dist/chunk-BODLDQY7.js +452 -0
  12. package/dist/chunk-BODLDQY7.js.map +1 -0
  13. package/dist/chunk-FFFCFZ6A.js +121 -0
  14. package/dist/chunk-FFFCFZ6A.js.map +1 -0
  15. package/dist/chunk-FIWT2NMM.js +165 -0
  16. package/dist/chunk-FIWT2NMM.js.map +1 -0
  17. package/dist/chunk-L6GQCHDF.js +222 -0
  18. package/dist/chunk-L6GQCHDF.js.map +1 -0
  19. package/dist/{chunk-O7UV3FWI.js → chunk-LDM62TIX.js} +2 -2
  20. package/dist/chunk-LDM62TIX.js.map +1 -0
  21. package/dist/chunk-LOA3KWYJ.js +294 -0
  22. package/dist/chunk-LOA3KWYJ.js.map +1 -0
  23. package/dist/chunk-NA45DFXY.js +616 -0
  24. package/dist/chunk-NA45DFXY.js.map +1 -0
  25. package/dist/{chunk-4CMAEQQY.js → chunk-NQN6JPI7.js} +4 -3
  26. package/dist/chunk-NQN6JPI7.js.map +1 -0
  27. package/dist/chunk-OQJHPULO.js +103 -0
  28. package/dist/chunk-OQJHPULO.js.map +1 -0
  29. package/dist/chunk-QHNUMM4V.js +87 -0
  30. package/dist/chunk-QHNUMM4V.js.map +1 -0
  31. package/dist/chunk-RM6BWILN.js +74 -0
  32. package/dist/chunk-RM6BWILN.js.map +1 -0
  33. package/dist/chunk-WBR5J7EX.js +90 -0
  34. package/dist/chunk-WBR5J7EX.js.map +1 -0
  35. package/dist/chunk-Z2UKDBNL.js +162 -0
  36. package/dist/chunk-Z2UKDBNL.js.map +1 -0
  37. package/dist/cli.js +2151 -12594
  38. package/dist/cli.js.map +1 -1
  39. package/dist/context-M2A2DOFV.js +291 -0
  40. package/dist/context-M2A2DOFV.js.map +1 -0
  41. package/dist/context-feed-JMNW4GAM.js +391 -0
  42. package/dist/context-feed-JMNW4GAM.js.map +1 -0
  43. package/dist/cost-N37I4UTA.js +274 -0
  44. package/dist/cost-N37I4UTA.js.map +1 -0
  45. package/dist/create-554W5HNU.js +286 -0
  46. package/dist/create-554W5HNU.js.map +1 -0
  47. package/dist/daemon-XWPQPPPN.js +546 -0
  48. package/dist/daemon-XWPQPPPN.js.map +1 -0
  49. package/dist/dashboard-L7YKVQEB.js +945 -0
  50. package/dist/dashboard-L7YKVQEB.js.map +1 -0
  51. package/dist/dashboard-MFNRLCEE.js +794 -0
  52. package/dist/dashboard-MFNRLCEE.js.map +1 -0
  53. package/dist/doctor-RG75M5RO.js +346 -0
  54. package/dist/doctor-RG75M5RO.js.map +1 -0
  55. package/dist/env-config-KCLDBKYX.js +21 -0
  56. package/dist/exec-JQKBF7BL.js +197 -0
  57. package/dist/exec-JQKBF7BL.js.map +1 -0
  58. package/dist/feedback-KA2UYBZG.js +229 -0
  59. package/dist/feedback-KA2UYBZG.js.map +1 -0
  60. package/dist/github-UQTM5KMS.js +23 -0
  61. package/dist/goal-EOPC5ZCD.js +168 -0
  62. package/dist/goal-EOPC5ZCD.js.map +1 -0
  63. package/dist/health-3FZDOSR5.js +209 -0
  64. package/dist/health-3FZDOSR5.js.map +1 -0
  65. package/dist/history-TFVXJEDH.js +229 -0
  66. package/dist/history-TFVXJEDH.js.map +1 -0
  67. package/dist/index.js +1 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-UOWTNMIE.js +747 -0
  70. package/dist/init-UOWTNMIE.js.map +1 -0
  71. package/dist/kpi-2SQ2WCVT.js +413 -0
  72. package/dist/kpi-2SQ2WCVT.js.map +1 -0
  73. package/dist/learn-6ERTERAO.js +269 -0
  74. package/dist/learn-6ERTERAO.js.map +1 -0
  75. package/dist/list-KSOMUBMB.js +92 -0
  76. package/dist/list-KSOMUBMB.js.map +1 -0
  77. package/dist/login-ST6PAXYE.js +155 -0
  78. package/dist/login-ST6PAXYE.js.map +1 -0
  79. package/dist/memory-3CSNKXIL.js +562 -0
  80. package/dist/memory-3CSNKXIL.js.map +1 -0
  81. package/dist/progress-FKG4V2VH.js +202 -0
  82. package/dist/progress-FKG4V2VH.js.map +1 -0
  83. package/dist/providers-66PDCORB.js +65 -0
  84. package/dist/providers-66PDCORB.js.map +1 -0
  85. package/dist/results-2MJFLWEO.js +224 -0
  86. package/dist/results-2MJFLWEO.js.map +1 -0
  87. package/dist/run-72OQLH5A.js +2685 -0
  88. package/dist/run-72OQLH5A.js.map +1 -0
  89. package/dist/session-6H67XPAQ.js +64 -0
  90. package/dist/session-6H67XPAQ.js.map +1 -0
  91. package/dist/{chunk-NHGLXN2F.js → sessions-GVQIMN4W.js} +23 -459
  92. package/dist/sessions-GVQIMN4W.js.map +1 -0
  93. package/dist/{squad-parser-4BI3G4RS.js → squad-parser-CM3HOIWM.js} +2 -2
  94. package/dist/squad-parser-CM3HOIWM.js.map +1 -0
  95. package/dist/stats-ONZI557Q.js +335 -0
  96. package/dist/stats-ONZI557Q.js.map +1 -0
  97. package/dist/status-FYH42FTB.js +346 -0
  98. package/dist/status-FYH42FTB.js.map +1 -0
  99. package/dist/sync-HJZJNXHW.js +800 -0
  100. package/dist/sync-HJZJNXHW.js.map +1 -0
  101. package/dist/update-B4WMUOPO.js +83 -0
  102. package/dist/update-B4WMUOPO.js.map +1 -0
  103. package/dist/{update-ALJKFFM7.js → update-L7FGHN6W.js} +2 -2
  104. package/dist/update-L7FGHN6W.js.map +1 -0
  105. package/package.json +18 -10
  106. package/dist/chunk-4CMAEQQY.js.map +0 -1
  107. package/dist/chunk-NHGLXN2F.js.map +0 -1
  108. package/dist/chunk-O7UV3FWI.js.map +0 -1
  109. package/dist/sessions-6PB7ALCE.js +0 -16
  110. /package/dist/{sessions-6PB7ALCE.js.map → auth-YW3UPFSB.js.map} +0 -0
  111. /package/dist/{squad-parser-4BI3G4RS.js.map → env-config-KCLDBKYX.js.map} +0 -0
  112. /package/dist/{update-ALJKFFM7.js.map → github-UQTM5KMS.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/plan.ts","../src/lib/costs.ts"],"sourcesContent":["/**\n * Anthropic plan detection\n * Determines whether the user is on a Max (flat-fee) or Usage (pay-per-token) plan.\n */\n\n/**\n * Anthropic plan types:\n * - 'max': Flat fee subscription ($200/mo), no overage - only rate limits matter\n * - 'usage': Pay-per-token, budget tracking matters\n * - 'unknown': Not configured yet\n */\nexport type PlanType = 'max' | 'usage' | 'unknown';\n\n/**\n * Plan detection result with confidence and reason\n */\nexport interface PlanDetection {\n plan: PlanType;\n confidence: 'explicit' | 'inferred';\n reason: string;\n}\n\n/**\n * Detect the Anthropic plan type using multiple signals:\n *\n * Priority order:\n * 1. Explicit SQUADS_PLAN_TYPE env var (highest confidence)\n * 2. ANTHROPIC_BUDGET_DAILY set → usage plan (user cares about budget)\n * 3. Tier 4 + no budget → likely Max plan (heavy user)\n * 4. Low tier (1-2) → usage plan (new user, pay-as-you-go)\n * 5. Default: max (assumes professional use)\n */\nexport function detectPlan(): PlanDetection {\n // 1. Explicit configuration (highest priority)\n const explicitPlan = process.env.SQUADS_PLAN_TYPE?.toLowerCase();\n if (explicitPlan === 'usage') {\n return { plan: 'usage', confidence: 'explicit', reason: 'SQUADS_PLAN_TYPE=usage' };\n }\n if (explicitPlan === 'max') {\n return { plan: 'max', confidence: 'explicit', reason: 'SQUADS_PLAN_TYPE=max' };\n }\n\n // 2. Budget explicitly set → user cares about costs → usage plan\n const budgetSet = process.env.ANTHROPIC_BUDGET_DAILY || process.env.SQUADS_DAILY_BUDGET;\n if (budgetSet) {\n return { plan: 'usage', confidence: 'inferred', reason: `Budget set ($${budgetSet}/day)` };\n }\n\n // 3. Check tier - Tier 4 usually indicates Max plan user\n const tier = parseInt(process.env.ANTHROPIC_TIER || '0', 10);\n if (tier >= 4) {\n return { plan: 'max', confidence: 'inferred', reason: `Tier ${tier} (high usage)` };\n }\n\n // 4. Low tier (1-2) → likely new user on usage plan\n if (tier >= 1 && tier <= 2) {\n return { plan: 'usage', confidence: 'inferred', reason: `Tier ${tier} (new user)` };\n }\n\n // 5. Default: unknown - prompt user to configure\n return { plan: 'unknown', confidence: 'inferred', reason: 'Not configured' };\n}\n\n/**\n * Get the current Anthropic plan type\n * Use detectPlan() for full details including confidence\n */\nexport function getPlanType(): PlanType {\n return detectPlan().plan;\n}\n\n/**\n * Check if we're on a flat-fee plan where budget doesn't matter\n */\nexport function isMaxPlan(): boolean {\n return getPlanType() === 'max';\n}\n\n/**\n * Get human-readable plan description for dashboard display\n */\nexport function getPlanDescription(): string {\n const detection = detectPlan();\n const planName = detection.plan === 'max'\n ? 'Max ($200 flat)'\n : detection.plan === 'usage'\n ? 'Usage (pay-per-token)'\n : 'Unknown';\n const confidence = detection.confidence === 'explicit' ? '' : ` [${detection.reason}]`;\n return `${planName}${confidence}`;\n}\n","/**\n * Cost tracking via Squads Bridge (postgres) or Langfuse\n * Primary: Squads Bridge API → PostgreSQL\n * Fallback: Langfuse API (if bridge unavailable)\n */\n\nimport {\n ProviderName,\n ProviderDetection,\n detectProviderFromModel,\n detectProvidersFromEnv,\n calcCost as calcProviderCost,\n getProviderDisplayName,\n} from './providers.js';\n\n// Re-export provider types for convenience\nexport { ProviderName, ProviderDetection, detectProviderFromModel, detectProvidersFromEnv, getProviderDisplayName };\n\n// Re-export plan detection from dedicated module\nexport { PlanType, PlanDetection, detectPlan, getPlanType, isMaxPlan, getPlanDescription } from './plan.js';\n\ninterface SquadCosts {\n squad: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n cachedTokens: number;\n cost: number;\n models: Record<string, number>;\n}\n\nexport interface ProviderCosts {\n provider: ProviderName;\n displayName: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n plan?: string;\n confidence?: 'explicit' | 'inferred';\n reason?: string;\n}\n\nexport interface CostSummary {\n totalCost: number;\n dailyBudget: number;\n usedPercent: number;\n idleBudget: number;\n totalCalls: number;\n dailyCallLimit: number;\n callsPercent: number;\n totalCachedTokens: number;\n totalInputTokens: number;\n cacheHitRate: number;\n bySquad: SquadCosts[];\n byProvider: ProviderCosts[];\n source: 'postgres' | 'langfuse' | 'none';\n}\n\n// Legacy MODEL_PRICING kept for backward compatibility\n// New code should use getModelPricing() from providers.ts\nconst _MODEL_PRICING: Record<string, { input: number; output: number }> = {\n 'claude-opus-4-5-20251101': { input: 15.0, output: 75.0 },\n 'claude-sonnet-4-20250514': { input: 3.0, output: 15.0 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.0 },\n 'claude-3-5-sonnet-20241022': { input: 3.0, output: 15.0 },\n 'claude-3-5-haiku-20241022': { input: 0.80, output: 4.0 },\n default: { input: 3.0, output: 15.0 },\n};\n\nconst DEFAULT_DAILY_BUDGET = 200.0;\nconst DEFAULT_DAILY_CALL_LIMIT = 1000; // Default API call limit per day\nconst BRIDGE_URL = process.env.SQUADS_BRIDGE_URL || 'http://localhost:8088';\nconst FETCH_TIMEOUT_MS = 2000; // 2 second timeout for all fetch calls\n\n/**\n * Fetch with timeout to prevent hanging when services are down\n */\nasync function fetchWithTimeout(url: string, options: RequestInit = {}, timeoutMs = FETCH_TIMEOUT_MS): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { ...options, signal: controller.signal });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n}\n\nfunction calcCost(model: string, inputTokens: number, outputTokens: number): number {\n // Use provider-aware pricing\n const provider = detectProviderFromModel(model);\n return calcProviderCost(provider, model, inputTokens, outputTokens);\n}\n\n/**\n * Fetch cost summary from Squads Bridge (postgres)\n */\nasync function fetchFromBridge(period: 'day' | 'week' | 'month' = 'day'): Promise<CostSummary | null> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=${period}`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json() as { totals?: { cost_usd?: number }; by_squad?: Record<string, unknown>[] };\n const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n const totalCost = data.totals?.cost_usd || 0;\n\n const bySquad: SquadCosts[] = (data.by_squad || []).map((s: Record<string, unknown>) => ({\n squad: s.squad as string,\n calls: (s.generations as number) || 0,\n inputTokens: (s.input_tokens as number) || 0,\n outputTokens: (s.output_tokens as number) || 0,\n cachedTokens: (s.cached_tokens as number) || 0,\n cost: (s.cost_usd as number) || 0,\n models: {},\n }));\n\n const totalCalls = bySquad.reduce((sum, s) => sum + s.calls, 0);\n const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || '') || DEFAULT_DAILY_CALL_LIMIT;\n const totalCachedTokens = bySquad.reduce((sum, s) => sum + s.cachedTokens, 0);\n const totalInputTokens = bySquad.reduce((sum, s) => sum + s.inputTokens, 0);\n const totalAllInput = totalInputTokens + totalCachedTokens;\n const cacheHitRate = totalAllInput > 0 ? (totalCachedTokens / totalAllInput) * 100 : 0;\n\n // Build provider summary from detected providers in env\n const detectedProviders = detectProvidersFromEnv();\n const byProvider: ProviderCosts[] = detectedProviders.map((p) => ({\n provider: p.provider,\n displayName: getProviderDisplayName(p.provider),\n calls: 0, // Bridge doesn't track by provider yet\n inputTokens: 0,\n outputTokens: 0,\n cost: p.provider === 'anthropic' ? totalCost : 0, // Assume all cost is Anthropic for now\n plan: p.plan,\n confidence: p.confidence,\n reason: p.reason,\n }));\n\n return {\n totalCost,\n dailyBudget,\n usedPercent: (totalCost / dailyBudget) * 100,\n idleBudget: dailyBudget - totalCost,\n totalCalls,\n dailyCallLimit,\n callsPercent: (totalCalls / dailyCallLimit) * 100,\n totalCachedTokens,\n totalInputTokens,\n cacheHitRate,\n bySquad,\n byProvider,\n source: 'postgres',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch cost summary from Langfuse API (fallback)\n */\nasync function fetchFromLangfuse(limit = 100): Promise<CostSummary | null> {\n const publicKey = process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = process.env.LANGFUSE_SECRET_KEY;\n const host = process.env.LANGFUSE_HOST || process.env.LANGFUSE_BASE_URL || 'https://us.cloud.langfuse.com';\n\n if (!publicKey || !secretKey) {\n return null;\n }\n\n try {\n const auth = Buffer.from(`${publicKey}:${secretKey}`).toString('base64');\n const url = `${host}/api/public/observations?limit=${limit}`;\n\n const response = await fetchWithTimeout(url, {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return null;\n }\n\n interface LangfuseObs {\n type?: string;\n model?: string;\n metadata?: { squad?: string };\n usage?: { input?: number; output?: number };\n }\n const data = await response.json() as { data?: LangfuseObs[] };\n const observations = data.data || [];\n\n // Group by squad\n const bySquad: Record<string, SquadCosts> = {};\n\n for (const obs of observations) {\n if (obs.type !== 'GENERATION') continue;\n\n const metadata = obs.metadata || {};\n const squad = metadata.squad || 'unknown';\n const model = obs.model || 'unknown';\n const usage = obs.usage || {};\n\n const inputTokens = usage.input || 0;\n const outputTokens = usage.output || 0;\n const cost = calcCost(model, inputTokens, outputTokens);\n\n if (!bySquad[squad]) {\n bySquad[squad] = {\n squad,\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cachedTokens: 0,\n cost: 0,\n models: {},\n };\n }\n\n bySquad[squad].calls += 1;\n bySquad[squad].inputTokens += inputTokens;\n bySquad[squad].outputTokens += outputTokens;\n bySquad[squad].cost += cost;\n bySquad[squad].models[model] = (bySquad[squad].models[model] || 0) + 1;\n }\n\n const squadList = Object.values(bySquad).sort((a, b) => b.cost - a.cost);\n const totalCost = squadList.reduce((sum, s) => sum + s.cost, 0);\n const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n\n const totalCalls = squadList.reduce((sum, s) => sum + s.calls, 0);\n const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || '') || DEFAULT_DAILY_CALL_LIMIT;\n const totalCachedTokens = squadList.reduce((sum, s) => sum + s.cachedTokens, 0);\n const totalInputTokens = squadList.reduce((sum, s) => sum + s.inputTokens, 0);\n const totalAllInput = totalInputTokens + totalCachedTokens;\n const cacheHitRate = totalAllInput > 0 ? (totalCachedTokens / totalAllInput) * 100 : 0;\n\n // Build provider summary - Langfuse can track by model, so group by provider\n const providerMap: Record<string, ProviderCosts> = {};\n for (const obs of observations) {\n if (obs.type !== 'GENERATION') continue;\n const model = obs.model || 'unknown';\n const provider = detectProviderFromModel(model);\n const usage = obs.usage || {};\n const inputTokens = usage.input || 0;\n const outputTokens = usage.output || 0;\n const cost = calcCost(model, inputTokens, outputTokens);\n\n if (!providerMap[provider]) {\n const detection = detectProvidersFromEnv().find((p) => p.provider === provider);\n providerMap[provider] = {\n provider,\n displayName: getProviderDisplayName(provider),\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n plan: detection?.plan,\n confidence: detection?.confidence,\n reason: detection?.reason,\n };\n }\n providerMap[provider].calls += 1;\n providerMap[provider].inputTokens += inputTokens;\n providerMap[provider].outputTokens += outputTokens;\n providerMap[provider].cost += cost;\n }\n const byProvider = Object.values(providerMap).sort((a, b) => b.cost - a.cost);\n\n return {\n totalCost,\n dailyBudget,\n usedPercent: (totalCost / dailyBudget) * 100,\n idleBudget: dailyBudget - totalCost,\n totalCalls,\n dailyCallLimit,\n callsPercent: (totalCalls / dailyCallLimit) * 100,\n totalCachedTokens,\n totalInputTokens,\n cacheHitRate,\n bySquad: squadList,\n byProvider,\n source: 'langfuse',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch cost summary - tries postgres first, falls back to Langfuse\n */\nexport async function fetchCostSummary(\n limit = 100,\n period: 'day' | 'week' | 'month' = 'day'\n): Promise<CostSummary | null> {\n // Try postgres (via bridge) first\n const bridgeResult = await fetchFromBridge(period);\n if (bridgeResult) {\n return bridgeResult;\n }\n\n // Fall back to Langfuse\n const langfuseResult = await fetchFromLangfuse(limit);\n if (langfuseResult) {\n return langfuseResult;\n }\n\n // No data source available - still detect providers from env\n const defaultBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n const detectedProviders = detectProvidersFromEnv();\n const byProvider: ProviderCosts[] = detectedProviders.map((p) => ({\n provider: p.provider,\n displayName: getProviderDisplayName(p.provider),\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n plan: p.plan,\n confidence: p.confidence,\n reason: p.reason,\n }));\n\n return {\n totalCost: 0,\n dailyBudget: defaultBudget,\n usedPercent: 0,\n idleBudget: defaultBudget,\n totalCalls: 0,\n dailyCallLimit: DEFAULT_DAILY_CALL_LIMIT,\n callsPercent: 0,\n totalCachedTokens: 0,\n totalInputTokens: 0,\n cacheHitRate: 0,\n bySquad: [],\n byProvider,\n source: 'none',\n };\n}\n\nexport function formatCostBar(usedPercent: number, width = 20): string {\n const filled = Math.min(Math.round((usedPercent / 100) * width), width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\n/**\n * Bridge stats from /stats endpoint (Redis real-time or Postgres fallback)\n */\nexport interface BridgeStats {\n status: string;\n source: 'redis' | 'postgres' | 'none';\n today: {\n generations: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n };\n week?: {\n generations: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n byModel?: Array<{\n model: string;\n generations: number;\n costUsd: number;\n }>;\n };\n budget: {\n daily: number;\n used: number;\n remaining: number;\n usedPct: number;\n };\n bySquad: Array<{\n squad: string;\n costUsd: number;\n generations: number;\n }>;\n byModel?: Array<{\n model: string;\n generations: number;\n costUsd: number;\n }>;\n health: {\n postgres: string;\n redis: string;\n langfuse: string;\n };\n}\n\n/**\n * Fetch real-time stats from Squads Bridge\n * All HTTP calls are made in parallel for optimal performance\n */\nexport async function fetchBridgeStats(): Promise<BridgeStats | null> {\n try {\n interface StatsData {\n status?: string;\n source?: string;\n today?: {\n generations?: number;\n input_tokens?: number;\n output_tokens?: number;\n cost_usd?: number;\n };\n budget?: {\n daily?: number;\n used?: number;\n remaining?: number;\n used_pct?: number;\n };\n by_squad?: Array<{\n squad?: string;\n cost_usd?: number;\n generations?: number;\n }>;\n }\n\n interface HealthData {\n postgres?: string;\n redis?: string;\n langfuse?: string;\n }\n\n interface CostData {\n totals?: {\n generations?: number;\n input_tokens?: number;\n output_tokens?: number;\n cost_usd?: number;\n };\n by_model?: Array<{\n model?: string;\n generations?: number;\n cost_usd?: number;\n }>;\n }\n\n // Fetch ALL endpoints in parallel (4 requests -> 1 round trip)\n const [statsResponse, healthResponse, costResponse, weekResponse] = await Promise.all([\n fetchWithTimeout(`${BRIDGE_URL}/stats`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/health`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=day`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=week`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n ]);\n\n if (!statsResponse.ok) {\n return null;\n }\n\n // Parse all responses in parallel\n const [stats, health, costData, weekData] = await Promise.all([\n statsResponse.json() as Promise<StatsData>,\n healthResponse.ok ? healthResponse.json() as Promise<HealthData> : Promise.resolve({} as HealthData),\n costResponse.ok ? costResponse.json() as Promise<CostData> : Promise.resolve({} as CostData),\n weekResponse.ok ? weekResponse.json() as Promise<CostData> : Promise.resolve({} as CostData),\n ]);\n\n return {\n status: stats.status || 'unknown',\n source: (stats.source as 'redis' | 'postgres' | 'none') || 'none',\n today: {\n generations: stats.today?.generations || 0,\n inputTokens: stats.today?.input_tokens || 0,\n outputTokens: stats.today?.output_tokens || 0,\n costUsd: stats.today?.cost_usd || 0,\n },\n week: weekData.totals ? {\n generations: weekData.totals.generations || 0,\n inputTokens: weekData.totals.input_tokens || 0,\n outputTokens: weekData.totals.output_tokens || 0,\n costUsd: weekData.totals.cost_usd || 0,\n byModel: (weekData.by_model || []).map(m => ({\n model: m.model || 'unknown',\n generations: m.generations || 0,\n costUsd: m.cost_usd || 0,\n })),\n } : undefined,\n budget: {\n daily: stats.budget?.daily || DEFAULT_DAILY_BUDGET,\n used: stats.budget?.used || 0,\n remaining: stats.budget?.remaining || DEFAULT_DAILY_BUDGET,\n usedPct: stats.budget?.used_pct || 0,\n },\n bySquad: (stats.by_squad || []).map(s => ({\n squad: s.squad || 'unknown',\n costUsd: s.cost_usd || 0,\n generations: s.generations || 0,\n })),\n byModel: (costData.by_model || []).map(m => ({\n model: m.model || 'unknown',\n generations: m.generations || 0,\n costUsd: m.cost_usd || 0,\n })),\n health: {\n postgres: health.postgres || 'unknown',\n redis: health.redis || 'unknown',\n langfuse: health.langfuse || 'unknown',\n },\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Monthly quota/autonomy data from bridge\n */\nexport interface QuotaInfo {\n monthlyUsed: number;\n monthlyQuota: number;\n quotaPct: number;\n autonomyScore: number;\n confidenceLevel: string;\n learningCount: number;\n}\n\nexport async function fetchQuotaInfo(): Promise<QuotaInfo | null> {\n const bridgeUrl = process.env.SQUADS_BRIDGE_URL || 'http://localhost:8088';\n\n try {\n const response = await fetch(`${bridgeUrl}/api/autonomy/score`);\n if (!response.ok) return null;\n\n const data = await response.json() as {\n overall_score: number;\n confidence_level: string;\n execution_stats: {\n monthly_used: number;\n monthly_quota: number;\n quota_pct: number;\n learning_count: number;\n };\n };\n\n return {\n monthlyUsed: data.execution_stats.monthly_used,\n monthlyQuota: data.execution_stats.monthly_quota,\n quotaPct: data.execution_stats.quota_pct,\n autonomyScore: data.overall_score,\n confidenceLevel: data.confidence_level,\n learningCount: data.execution_stats.learning_count,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Rate limit data from Anthropic API headers\n */\nexport interface RateLimitInfo {\n model: string;\n requestsLimit: number;\n requestsRemaining: number;\n requestsReset?: string;\n tokensLimit: number;\n tokensRemaining: number;\n tokensReset?: string;\n inputTokensLimit?: number;\n inputTokensRemaining?: number;\n outputTokensLimit?: number;\n outputTokensRemaining?: number;\n capturedAt: string;\n}\n\nexport interface RateLimits {\n limits: Record<string, RateLimitInfo>;\n source: 'proxy' | 'none';\n}\n\n/**\n * Fetch real rate limits from the Anthropic proxy (via bridge)\n */\nexport async function fetchRateLimits(): Promise<RateLimits> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/rate-limits`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return { limits: {}, source: 'none' };\n }\n\n interface RateLimitResponse {\n rate_limits?: Record<string, {\n model?: string;\n requests_limit?: number;\n requests_remaining?: number;\n requests_reset?: string;\n tokens_limit?: number;\n tokens_remaining?: number;\n tokens_reset?: string;\n input_tokens_limit?: number;\n input_tokens_remaining?: number;\n output_tokens_limit?: number;\n output_tokens_remaining?: number;\n captured_at?: string;\n }>;\n }\n\n const data = await response.json() as RateLimitResponse;\n const rateLimits = data.rate_limits || {};\n\n const limits: Record<string, RateLimitInfo> = {};\n for (const [key, value] of Object.entries(rateLimits)) {\n limits[key] = {\n model: value.model || key,\n requestsLimit: value.requests_limit || 0,\n requestsRemaining: value.requests_remaining || 0,\n requestsReset: value.requests_reset,\n tokensLimit: value.tokens_limit || 0,\n tokensRemaining: value.tokens_remaining || 0,\n tokensReset: value.tokens_reset,\n inputTokensLimit: value.input_tokens_limit,\n inputTokensRemaining: value.input_tokens_remaining,\n outputTokensLimit: value.output_tokens_limit,\n outputTokensRemaining: value.output_tokens_remaining,\n capturedAt: value.captured_at || new Date().toISOString(),\n };\n }\n\n return { limits, source: 'proxy' };\n } catch {\n return { limits: {}, source: 'none' };\n }\n}\n\n/**\n * Task and quality insights\n */\nexport interface TaskMetrics {\n squad: string;\n tasksTotal: number;\n tasksCompleted: number;\n tasksFailed: number;\n successRate: number;\n totalRetries: number;\n tasksWithRetries: number;\n avgRetries: number;\n avgDurationMs: number;\n avgTokens: number;\n avgCost: number;\n avgContextPct: number;\n maxContextTokens: number;\n}\n\nexport interface ToolMetrics {\n toolName: string;\n usageCount: number;\n successRate: number;\n avgDurationMs: number;\n}\n\nexport interface QualityMetrics {\n squad: string;\n feedbackCount: number;\n avgQuality: number;\n helpfulPct: number;\n fixRequiredPct: number;\n}\n\nexport interface Insights {\n period: string;\n days: number;\n taskMetrics: TaskMetrics[];\n qualityMetrics: QualityMetrics[];\n topTools: ToolMetrics[];\n toolFailureRate: number;\n source: 'bridge' | 'none';\n}\n\n/**\n * Fetch insights from the bridge\n */\nexport async function fetchInsights(period: 'day' | 'week' | 'month' = 'week'): Promise<Insights> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/insights?period=${period}`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return {\n period,\n days: period === 'day' ? 1 : period === 'week' ? 7 : 30,\n taskMetrics: [],\n qualityMetrics: [],\n topTools: [],\n toolFailureRate: 0,\n source: 'none',\n };\n }\n\n interface InsightsResponse {\n period: string;\n days: number;\n task_metrics?: Array<{\n squad: string;\n tasks_total: number;\n tasks_completed: number;\n tasks_failed: number;\n success_rate: number;\n total_retries: number;\n tasks_with_retries: number;\n avg_retries: number;\n avg_duration_ms: number;\n avg_tokens: number;\n avg_cost: number;\n avg_context_pct: number;\n max_context_tokens: number;\n }>;\n quality_metrics?: Array<{\n squad: string;\n feedback_count: number;\n avg_quality: number;\n helpful_pct: number;\n fix_required_pct: number;\n }>;\n top_tools?: Array<{\n tool_name: string;\n usage_count: number;\n success_rate: number;\n avg_duration_ms: number;\n }>;\n tool_failure_rate?: number;\n }\n\n const data = await response.json() as InsightsResponse;\n\n return {\n period: data.period || period,\n days: data.days || 7,\n taskMetrics: (data.task_metrics || []).map(t => ({\n squad: t.squad,\n tasksTotal: t.tasks_total || 0,\n tasksCompleted: t.tasks_completed || 0,\n tasksFailed: t.tasks_failed || 0,\n successRate: t.success_rate || 0,\n totalRetries: t.total_retries || 0,\n tasksWithRetries: t.tasks_with_retries || 0,\n avgRetries: t.avg_retries || 0,\n avgDurationMs: t.avg_duration_ms || 0,\n avgTokens: t.avg_tokens || 0,\n avgCost: t.avg_cost || 0,\n avgContextPct: t.avg_context_pct || 0,\n maxContextTokens: t.max_context_tokens || 0,\n })),\n qualityMetrics: (data.quality_metrics || []).map(q => ({\n squad: q.squad,\n feedbackCount: q.feedback_count || 0,\n avgQuality: q.avg_quality || 0,\n helpfulPct: q.helpful_pct || 0,\n fixRequiredPct: q.fix_required_pct || 0,\n })),\n topTools: (data.top_tools || []).map(t => ({\n toolName: t.tool_name,\n usageCount: t.usage_count || 0,\n successRate: t.success_rate || 0,\n avgDurationMs: t.avg_duration_ms || 0,\n })),\n toolFailureRate: data.tool_failure_rate || 0,\n source: 'bridge',\n };\n } catch {\n return {\n period,\n days: period === 'day' ? 1 : period === 'week' ? 7 : 30,\n taskMetrics: [],\n qualityMetrics: [],\n topTools: [],\n toolFailureRate: 0,\n source: 'none',\n };\n }\n}\n\n// === NPM Stats for Acquisition Tracking ===\n\nexport interface NpmStats {\n package: string;\n downloads: {\n lastDay: number;\n lastWeek: number;\n lastMonth: number;\n };\n weekOverWeek: number; // percentage change\n}\n\nexport async function fetchNpmStats(packageName: string = process.env.SQUADS_NPM_PACKAGE || 'squads-cli'): Promise<NpmStats | null> {\n try {\n const [dayRes, weekRes, monthRes] = await Promise.all([\n fetch(`https://api.npmjs.org/downloads/point/last-day/${packageName}`),\n fetch(`https://api.npmjs.org/downloads/point/last-week/${packageName}`),\n fetch(`https://api.npmjs.org/downloads/point/last-month/${packageName}`),\n ]);\n\n if (!dayRes.ok || !weekRes.ok || !monthRes.ok) return null;\n\n const [dayData, weekData, monthData] = await Promise.all([\n dayRes.json() as Promise<{ downloads: number }>,\n weekRes.json() as Promise<{ downloads: number }>,\n monthRes.json() as Promise<{ downloads: number }>,\n ]);\n\n // Calculate week-over-week growth (rough estimate: this week vs avg of month)\n const avgWeeklyFromMonth = monthData.downloads / 4;\n const weekOverWeek = avgWeeklyFromMonth > 0\n ? Math.round(((weekData.downloads - avgWeeklyFromMonth) / avgWeeklyFromMonth) * 100)\n : 0;\n\n return {\n package: packageName,\n downloads: {\n lastDay: dayData.downloads,\n lastWeek: weekData.downloads,\n lastMonth: monthData.downloads,\n },\n weekOverWeek,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Claude Code subscription capacity data\n * Read from ~/.claude/stats-cache.json\n */\nexport interface ClaudeCodeCapacity {\n // Weekly capacity (from stats-cache.json token data)\n weeklyTokensUsed: number;\n weeklyTokensLimit: number;\n weeklyCapacityPct: number;\n // Session capacity (estimated from current session)\n sessionTokensUsed: number;\n sessionTokensLimit: number;\n sessionCapacityPct: number;\n // By model breakdown\n opusTokensUsed: number;\n sonnetTokensUsed: number;\n haikuTokensUsed: number;\n // Reset info\n weeklyResetDate: string;\n sessionResetTime: string;\n}\n\n// Claude Code subscription capacity estimation\n// The actual limits use \"usage units\" not raw tokens\n// Opus costs ~5x more usage units than Sonnet/Haiku\n// Based on user reports: Max 20x gives ~24-40 hours Opus/week\n// Conservative estimate: ~4M \"weighted tokens\" per week for Max 20x\nconst OPUS_WEIGHT = 5; // Opus costs ~5x more against limit\nconst SONNET_WEIGHT = 1; // Sonnet is the baseline\nconst HAIKU_WEIGHT = 0.25; // Haiku is cheaper\n\n// Effective weekly limit in weighted tokens (conservative for Max 20x)\n// Adjust via SQUADS_WEEKLY_LIMIT env var if your plan differs\nconst DEFAULT_WEEKLY_WEIGHTED_LIMIT = 4_000_000;\nconst MAX5_SESSION_TOKEN_LIMIT = 2_000_000; // ~2M tokens/session (raw)\n\n/**\n * Fetch Claude Code capacity from stats-cache.json\n */\nexport async function fetchClaudeCodeCapacity(): Promise<ClaudeCodeCapacity | null> {\n const { readFile } = await import('fs/promises');\n const { homedir } = await import('os');\n const { join } = await import('path');\n\n try {\n const cacheFile = join(homedir(), '.claude', 'stats-cache.json');\n const content = await readFile(cacheFile, 'utf-8');\n const data = JSON.parse(content) as {\n dailyModelTokens?: Array<{\n date: string;\n tokensByModel: Record<string, number>;\n }>;\n modelUsage?: Record<string, {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadInputTokens?: number;\n cacheCreationInputTokens?: number;\n }>;\n };\n\n // Calculate weekly token usage (last 7 days)\n const now = new Date();\n const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n const weekStart = weekAgo.toISOString().split('T')[0];\n\n let weeklyOpus = 0;\n\n if (data.dailyModelTokens) {\n for (const day of data.dailyModelTokens) {\n if (day.date >= weekStart) {\n for (const [model, tokens] of Object.entries(day.tokensByModel)) {\n if (model.includes('opus')) {\n weeklyOpus += tokens;\n }\n }\n }\n }\n }\n\n // Calculate weighted usage (Opus costs 5x more against limit)\n // This better reflects actual subscription capacity consumption\n const weeklyLimit = parseInt(process.env.SQUADS_WEEKLY_LIMIT || '', 10) || DEFAULT_WEEKLY_WEIGHTED_LIMIT;\n\n // Calculate session usage (today's tokens as proxy)\n const today = now.toISOString().split('T')[0];\n let sessionTokens = 0;\n if (data.dailyModelTokens) {\n const todayData = data.dailyModelTokens.find(d => d.date === today);\n if (todayData) {\n sessionTokens = Object.values(todayData.tokensByModel).reduce((a, b) => a + b, 0);\n }\n }\n\n // Calculate weekly reset (next Sunday 9:59pm in local timezone)\n const daysUntilSunday = (7 - now.getDay()) % 7 || 7;\n const nextSunday = new Date(now);\n nextSunday.setDate(now.getDate() + daysUntilSunday);\n nextSunday.setHours(21, 59, 0, 0);\n\n // Session reset (end of current session - approximate as end of day)\n const sessionReset = new Date(now);\n sessionReset.setHours(18, 59, 0, 0);\n if (sessionReset < now) {\n sessionReset.setDate(sessionReset.getDate() + 1);\n }\n\n // Calculate Sonnet/Haiku usage\n let sonnetTokens = 0;\n let haikuTokens = 0;\n if (data.dailyModelTokens) {\n for (const day of data.dailyModelTokens) {\n if (day.date >= weekStart) {\n for (const [model, tokens] of Object.entries(day.tokensByModel)) {\n if (model.includes('sonnet')) {\n sonnetTokens += tokens;\n } else if (model.includes('haiku')) {\n haikuTokens += tokens;\n }\n }\n }\n }\n }\n\n // Calculate weighted weekly usage\n const weeklyWeighted = Math.round(\n (weeklyOpus * OPUS_WEIGHT) +\n (sonnetTokens * SONNET_WEIGHT) +\n (haikuTokens * HAIKU_WEIGHT)\n );\n\n return {\n weeklyTokensUsed: weeklyWeighted, // Now weighted, not raw\n weeklyTokensLimit: weeklyLimit,\n weeklyCapacityPct: Math.round((weeklyWeighted / weeklyLimit) * 100),\n sessionTokensUsed: sessionTokens,\n sessionTokensLimit: MAX5_SESSION_TOKEN_LIMIT,\n sessionCapacityPct: Math.round((sessionTokens / MAX5_SESSION_TOKEN_LIMIT) * 100),\n opusTokensUsed: weeklyOpus,\n sonnetTokensUsed: sonnetTokens,\n haikuTokensUsed: haikuTokens,\n weeklyResetDate: nextSunday.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n sessionResetTime: sessionReset.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }),\n };\n } catch {\n return null;\n }\n}\n\n// === ROI and Cost Projection Types ===\n\n/**\n * ROI metrics for measuring value delivered vs cost\n */\nexport interface ROIMetrics {\n totalCostUsd: number;\n costPerGoal: number;\n costPerCommit: number;\n costPerPR: number;\n estimatedValueUsd: number;\n roiMultiplier: number;\n dailyProjectedCost: number;\n weeklyProjectedCost: number;\n monthlyProjectedCost: number;\n hoursTracked: number;\n costPerHour: number;\n}\n\n/**\n * Before/after comparison for a period\n */\nexport interface BeforeAfterMetrics {\n periodStart: string;\n periodEnd: string;\n baselineCostUsd: number;\n baselineGoals: number;\n baselineCommits: number;\n baselinePRs: number;\n baselineTokens: number;\n currentCostUsd: number;\n currentGoals: number;\n currentCommits: number;\n currentPRs: number;\n currentTokens: number;\n costDelta: number;\n goalsDelta: number;\n commitsDelta: number;\n prsDelta: number;\n tokensDelta: number;\n costPerGoalBefore: number;\n costPerGoalAfter: number;\n efficiencyImprovement: number;\n}\n\n/**\n * Squad-level cost projection\n */\nexport interface SquadCostProjection {\n squad: string;\n currentDailyCost: number;\n projectedDailyCost: number;\n projectedWeeklyCost: number;\n projectedMonthlyCost: number;\n costTrend: 'increasing' | 'stable' | 'decreasing';\n trendPct: number;\n}\n\n/**\n * Calculate ROI metrics from cost and activity data\n */\nexport function calculateROIMetrics(\n costs: CostSummary | null,\n goalsCompleted: number,\n commits: number,\n prsMerged: number,\n hoursTracked: number = 0\n): ROIMetrics {\n const totalCost = costs?.totalCost || 0;\n const costPerGoal = goalsCompleted > 0 ? totalCost / goalsCompleted : 0;\n const costPerCommit = commits > 0 ? totalCost / commits : 0;\n const costPerPR = prsMerged > 0 ? totalCost / prsMerged : 0;\n\n const GOAL_VALUE = parseFloat(process.env.SQUADS_GOAL_VALUE || '100');\n const PR_VALUE = parseFloat(process.env.SQUADS_PR_VALUE || '200');\n const COMMIT_VALUE = parseFloat(process.env.SQUADS_COMMIT_VALUE || '25');\n\n const estimatedValue = (goalsCompleted * GOAL_VALUE) + (prsMerged * PR_VALUE) + (commits * COMMIT_VALUE);\n const roiMultiplier = totalCost > 0 ? estimatedValue / totalCost : 0;\n\n const now = new Date();\n const hoursElapsed = hoursTracked > 0 ? hoursTracked : Math.max(now.getHours() + now.getMinutes() / 60, 1);\n const costPerHour = totalCost / hoursElapsed;\n\n return {\n totalCostUsd: totalCost,\n costPerGoal,\n costPerCommit,\n costPerPR,\n estimatedValueUsd: estimatedValue,\n roiMultiplier,\n dailyProjectedCost: costPerHour * 24,\n weeklyProjectedCost: costPerHour * 24 * 7,\n monthlyProjectedCost: costPerHour * 24 * 30,\n hoursTracked: hoursElapsed,\n costPerHour,\n };\n}\n\n/**\n * Calculate cost projections per squad\n */\nexport function calculateSquadCostProjections(\n bridgeStats: BridgeStats | null,\n _history: Array<{ squad: string; costUsd: number }[]> | null\n): SquadCostProjection[] {\n if (!bridgeStats || bridgeStats.bySquad.length === 0) {\n return [];\n }\n\n const now = new Date();\n const hoursElapsed = Math.max(now.getHours() + now.getMinutes() / 60, 1);\n\n return bridgeStats.bySquad.map(squad => {\n const hourlyRate = squad.costUsd / hoursElapsed;\n return {\n squad: squad.squad,\n currentDailyCost: squad.costUsd,\n projectedDailyCost: hourlyRate * 24,\n projectedWeeklyCost: hourlyRate * 24 * 7,\n projectedMonthlyCost: hourlyRate * 24 * 30,\n costTrend: 'stable' as const,\n trendPct: 0,\n };\n });\n}\n"],"mappings":"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","names":["calcCost"]}
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  RESET,
4
- colors
4
+ colors,
5
+ writeLine
5
6
  } from "./chunk-N7KDWU4W.js";
6
7
  import {
7
8
  __require
@@ -199,7 +200,7 @@ async function autoUpdateOnStartup(silent = false) {
199
200
  await new Promise((resolve) => {
200
201
  child.on("close", (code) => {
201
202
  if (code === 0) {
202
- console.log(`
203
+ writeLine(`
203
204
  ${colors.green}\u2713${RESET} Update successful! v${info.latestVersion} will be used on your next run.
204
205
  `);
205
206
  writeAutoUpdateCache({ lastAttempt: now, lastSuccess: now });
@@ -226,4 +227,4 @@ export {
226
227
  refreshVersionCache,
227
228
  autoUpdateOnStartup
228
229
  };
229
- //# sourceMappingURL=chunk-4CMAEQQY.js.map
230
+ //# sourceMappingURL=chunk-NQN6JPI7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/update.ts"],"sourcesContent":["/**\n * Update checker for squads-cli\n * Checks npm registry for newer versions and caches result\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { colors as termColors, RESET as termReset, writeLine } from './terminal.js';\n\n// Get current version from package.json\nfunction getPackageVersion(): string {\n try {\n // Try to find package.json relative to this module\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n\n // Look up from dist/lib to find package.json\n const possiblePaths = [\n join(__dirname, '..', '..', 'package.json'), // From dist/lib/\n join(__dirname, '..', 'package.json'), // From dist/\n join(__dirname, 'package.json'), // Same dir\n ];\n\n for (const pkgPath of possiblePaths) {\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return pkg.version || '0.0.0';\n }\n }\n } catch {\n // Ignore errors\n }\n return '0.0.0';\n}\n\nconst CURRENT_VERSION = getPackageVersion();\n\n// Cache settings\nconst CACHE_DIR = join(homedir(), '.squads');\nconst CACHE_FILE = join(CACHE_DIR, 'update-check.json');\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\ninterface UpdateCache {\n latestVersion: string;\n checkedAt: number;\n}\n\nexport interface UpdateInfo {\n currentVersion: string;\n latestVersion: string;\n updateAvailable: boolean;\n}\n\n/**\n * Compare semantic versions\n * Returns true if v2 > v1\n */\nfunction isNewerVersion(v1: string, v2: string): boolean {\n const parts1 = v1.replace(/^v/, '').split('.').map(Number);\n const parts2 = v2.replace(/^v/, '').split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n const p1 = parts1[i] || 0;\n const p2 = parts2[i] || 0;\n if (p2 > p1) return true;\n if (p2 < p1) return false;\n }\n return false;\n}\n\n/**\n * Read cached update info\n */\nfunction readCache(): UpdateCache | null {\n try {\n if (!existsSync(CACHE_FILE)) return null;\n const data = JSON.parse(readFileSync(CACHE_FILE, 'utf-8'));\n return data as UpdateCache;\n } catch {\n return null;\n }\n}\n\n/**\n * Write update info to cache\n */\nfunction writeCache(latestVersion: string): void {\n try {\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true });\n }\n const cache: UpdateCache = {\n latestVersion,\n checkedAt: Date.now(),\n };\n writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));\n } catch {\n // Ignore cache write errors\n }\n}\n\n/**\n * Fetch latest version from npm registry\n */\nfunction fetchLatestVersion(): string | null {\n try {\n const result = execSync('npm view squads-cli version 2>/dev/null', {\n encoding: 'utf-8',\n timeout: 5000,\n }).trim();\n return result || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check for updates (uses cache to avoid frequent npm calls)\n * Non-blocking: returns cached data immediately, triggers background refresh if stale\n */\nexport function checkForUpdate(): UpdateInfo {\n const result: UpdateInfo = {\n currentVersion: CURRENT_VERSION,\n latestVersion: CURRENT_VERSION,\n updateAvailable: false,\n };\n\n // Check cache first\n const cache = readCache();\n const now = Date.now();\n\n if (cache) {\n // Always use cached value immediately for fast response\n result.latestVersion = cache.latestVersion;\n result.updateAvailable = isNewerVersion(CURRENT_VERSION, cache.latestVersion);\n\n // If cache is stale, trigger background refresh (non-blocking)\n if ((now - cache.checkedAt) >= CACHE_TTL_MS) {\n // Fire and forget - don't await\n triggerBackgroundRefresh();\n }\n return result;\n }\n\n // No cache at all - trigger background refresh and return defaults\n triggerBackgroundRefresh();\n return result;\n}\n\n/**\n * Trigger a background version check that doesn't block the CLI\n * Uses spawn to run npm in a detached process\n */\nfunction triggerBackgroundRefresh(): void {\n try {\n // Use spawn with detached: true to run in background\n // This won't block the main process\n const { spawn } = require('child_process') as typeof import('child_process');\n const child = spawn('npm', ['view', 'squads-cli', 'version'], {\n detached: true,\n stdio: ['ignore', 'pipe', 'ignore'],\n shell: true,\n });\n\n // Collect output\n let output = '';\n child.stdout?.on('data', (data: Buffer) => {\n output += data.toString();\n });\n\n child.on('close', () => {\n const version = output.trim();\n if (version && /^\\d+\\.\\d+\\.\\d+/.test(version)) {\n writeCache(version);\n }\n });\n\n // Unref to allow main process to exit\n child.unref();\n } catch {\n // Ignore errors - background refresh is best effort\n }\n}\n\n/**\n * Get current version\n */\nexport function getCurrentVersion(): string {\n return CURRENT_VERSION;\n}\n\n/**\n * Perform the actual update via npm\n * Returns true if successful\n */\nexport function performUpdate(): { success: boolean; error?: string } {\n try {\n execSync('npm update -g squads-cli', {\n encoding: 'utf-8',\n stdio: 'inherit',\n timeout: 120000, // 2 minutes\n });\n // Clear cache so next check fetches fresh\n try {\n unlinkSync(CACHE_FILE);\n } catch {\n // Ignore\n }\n return { success: true };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Unknown error',\n };\n }\n}\n\n/**\n * Force refresh the version cache (bypass TTL)\n */\nexport function refreshVersionCache(): UpdateInfo {\n const latestVersion = fetchLatestVersion();\n if (latestVersion) {\n writeCache(latestVersion);\n return {\n currentVersion: CURRENT_VERSION,\n latestVersion,\n updateAvailable: isNewerVersion(CURRENT_VERSION, latestVersion),\n };\n }\n return checkForUpdate();\n}\n\n// Auto-update settings\nconst AUTO_UPDATE_CACHE_FILE = join(CACHE_DIR, 'auto-update.json');\nconst AUTO_UPDATE_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour cooldown between auto-update attempts\n\ninterface AutoUpdateCache {\n lastAttempt: number;\n lastSuccess?: number;\n}\n\nfunction readAutoUpdateCache(): AutoUpdateCache | null {\n try {\n if (!existsSync(AUTO_UPDATE_CACHE_FILE)) return null;\n return JSON.parse(readFileSync(AUTO_UPDATE_CACHE_FILE, 'utf-8'));\n } catch {\n return null;\n }\n}\n\nfunction writeAutoUpdateCache(cache: AutoUpdateCache): void {\n try {\n if (!existsSync(CACHE_DIR)) {\n mkdirSync(CACHE_DIR, { recursive: true });\n }\n writeFileSync(AUTO_UPDATE_CACHE_FILE, JSON.stringify(cache, null, 2));\n } catch {\n // Ignore errors\n }\n}\n\n/**\n * Seamless auto-update on CLI startup (like Gemini CLI)\n * Checks for updates and auto-installs without prompting.\n * Shows a message on success: \"Update successful! The new version will be used on your next run.\"\n *\n * @param silent - If true, don't show any output (for background checks)\n * @returns Promise that resolves when check/update is complete\n */\nexport async function autoUpdateOnStartup(silent = false): Promise<void> {\n // Skip in CI or if auto-update disabled\n if (process.env.CI || process.env.SQUADS_NO_AUTO_UPDATE) return;\n\n // Check cooldown - don't spam npm\n const autoCache = readAutoUpdateCache();\n const now = Date.now();\n if (autoCache && (now - autoCache.lastAttempt) < AUTO_UPDATE_COOLDOWN_MS) {\n return; // Too soon since last attempt\n }\n\n // Check if update is available\n const info = checkForUpdate();\n if (!info.updateAvailable) return;\n\n // Write attempt timestamp before trying\n writeAutoUpdateCache({ lastAttempt: now, lastSuccess: autoCache?.lastSuccess });\n\n // Perform update silently in background using spawn\n try {\n const { spawn } = await import('child_process');\n\n // Run npm update in background\n const child = spawn('npm', ['update', '-g', 'squads-cli'], {\n detached: true,\n stdio: silent ? 'ignore' : ['ignore', 'pipe', 'pipe'],\n shell: true,\n });\n\n if (!silent && child.stdout) {\n // Wait for completion and check result\n await new Promise<void>((resolve) => {\n child.on('close', (code) => {\n if (code === 0) {\n // Success - show Gemini-style message\n writeLine(`\\n ${termColors.green}✓${termReset} Update successful! v${info.latestVersion} will be used on your next run.\\n`);\n writeAutoUpdateCache({ lastAttempt: now, lastSuccess: now });\n // Clear version cache so next startup detects new version\n try { unlinkSync(CACHE_FILE); } catch { /* ignore */ }\n }\n resolve();\n });\n\n // Timeout after 30 seconds\n setTimeout(() => resolve(), 30000);\n });\n } else {\n // Silent mode - just fire and forget\n child.unref();\n }\n } catch {\n // Ignore errors - auto-update is best effort\n }\n}\n"],"mappings":"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","names":["__filename","__dirname"]}
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/env-config.ts
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ var CONFIG_DIR = join(homedir(), ".squads");
8
+ var CONFIG_PATH = join(CONFIG_DIR, "config.json");
9
+ var DEFAULT_CONFIG = {
10
+ current: "local",
11
+ environments: {
12
+ local: {
13
+ api_url: "http://localhost:8090",
14
+ admin_api_url: "http://localhost:8091",
15
+ console_url: "http://localhost:4322",
16
+ bridge_url: "http://localhost:8088",
17
+ database_url: "postgresql://squads:squads@localhost:5432/squads",
18
+ redis_url: "redis://localhost:6379",
19
+ execution: "local"
20
+ },
21
+ staging: {
22
+ api_url: "https://api-staging.agents-squads.com",
23
+ admin_api_url: "https://api-staging.agents-squads.com",
24
+ console_url: "https://console-staging.agents-squads.com",
25
+ bridge_url: "",
26
+ database_url: "",
27
+ redis_url: "",
28
+ execution: "cloud"
29
+ },
30
+ prod: {
31
+ api_url: "https://api.agents-squads.com",
32
+ admin_api_url: "https://api.agents-squads.com",
33
+ console_url: "https://console.agents-squads.com",
34
+ bridge_url: "",
35
+ database_url: "",
36
+ redis_url: "",
37
+ execution: "cloud"
38
+ }
39
+ }
40
+ };
41
+ function loadConfig() {
42
+ if (!existsSync(CONFIG_PATH)) {
43
+ saveConfig(DEFAULT_CONFIG);
44
+ return DEFAULT_CONFIG;
45
+ }
46
+ try {
47
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
48
+ const parsed = JSON.parse(raw);
49
+ return {
50
+ current: parsed.current || "local",
51
+ environments: {
52
+ ...DEFAULT_CONFIG.environments,
53
+ ...parsed.environments
54
+ }
55
+ };
56
+ } catch {
57
+ return DEFAULT_CONFIG;
58
+ }
59
+ }
60
+ function saveConfig(config) {
61
+ if (!existsSync(CONFIG_DIR)) {
62
+ mkdirSync(CONFIG_DIR, { recursive: true });
63
+ }
64
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
65
+ }
66
+ function getEnv() {
67
+ const config = loadConfig();
68
+ const envName = process.env.SQUADS_ENV || config.current;
69
+ const env = config.environments[envName] || config.environments.local;
70
+ return {
71
+ api_url: process.env.SQUADS_API_URL || env.api_url,
72
+ admin_api_url: process.env.SQUADS_ADMIN_API_URL || env.admin_api_url,
73
+ console_url: process.env.SQUADS_CONSOLE_URL || env.console_url,
74
+ bridge_url: process.env.SQUADS_BRIDGE_URL || env.bridge_url,
75
+ database_url: process.env.SQUADS_DATABASE_URL || env.database_url,
76
+ redis_url: process.env.REDIS_URL || env.redis_url,
77
+ execution: env.execution
78
+ };
79
+ }
80
+ function getEnvName() {
81
+ const config = loadConfig();
82
+ return process.env.SQUADS_ENV || config.current;
83
+ }
84
+ function getApiUrl() {
85
+ return getEnv().api_url;
86
+ }
87
+ function getBridgeUrl() {
88
+ return getEnv().bridge_url;
89
+ }
90
+ function getConsoleUrl() {
91
+ return getEnv().console_url;
92
+ }
93
+
94
+ export {
95
+ loadConfig,
96
+ saveConfig,
97
+ getEnv,
98
+ getEnvName,
99
+ getApiUrl,
100
+ getBridgeUrl,
101
+ getConsoleUrl
102
+ };
103
+ //# sourceMappingURL=chunk-OQJHPULO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/env-config.ts"],"sourcesContent":["/**\n * Environment configuration — single source of truth for all service URLs.\n *\n * Usage:\n * squads config use local Switch to local environment\n * squads config use staging Switch to staging\n * squads config use prod Switch to production\n * squads config show Show current config\n *\n * Config stored at ~/.squads/config.json\n * Env vars override config values (for CI/CD and one-off overrides).\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface EnvironmentConfig {\n api_url: string;\n admin_api_url: string;\n console_url: string;\n bridge_url: string;\n database_url: string;\n redis_url: string;\n execution: 'local' | 'cloud';\n}\n\nexport interface SquadsConfig {\n current: string;\n environments: Record<string, EnvironmentConfig>;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst CONFIG_DIR = join(homedir(), '.squads');\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.json');\n\nconst DEFAULT_CONFIG: SquadsConfig = {\n current: 'local',\n environments: {\n local: {\n api_url: 'http://localhost:8090',\n admin_api_url: 'http://localhost:8091',\n console_url: 'http://localhost:4322',\n bridge_url: 'http://localhost:8088',\n database_url: 'postgresql://squads:squads@localhost:5432/squads',\n redis_url: 'redis://localhost:6379',\n execution: 'local',\n },\n staging: {\n api_url: 'https://api-staging.agents-squads.com',\n admin_api_url: 'https://api-staging.agents-squads.com',\n console_url: 'https://console-staging.agents-squads.com',\n bridge_url: '',\n database_url: '',\n redis_url: '',\n execution: 'cloud',\n },\n prod: {\n api_url: 'https://api.agents-squads.com',\n admin_api_url: 'https://api.agents-squads.com',\n console_url: 'https://console.agents-squads.com',\n bridge_url: '',\n database_url: '',\n redis_url: '',\n execution: 'cloud',\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Load / Save\n// ---------------------------------------------------------------------------\n\nexport function loadConfig(): SquadsConfig {\n if (!existsSync(CONFIG_PATH)) {\n saveConfig(DEFAULT_CONFIG);\n return DEFAULT_CONFIG;\n }\n\n try {\n const raw = readFileSync(CONFIG_PATH, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<SquadsConfig>;\n return {\n current: parsed.current || 'local',\n environments: {\n ...DEFAULT_CONFIG.environments,\n ...parsed.environments,\n },\n };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport function saveConfig(config: SquadsConfig): void {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true });\n }\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Resolve — env vars override config\n// ---------------------------------------------------------------------------\n\nexport function getEnv(): EnvironmentConfig {\n const config = loadConfig();\n const envName = process.env.SQUADS_ENV || config.current;\n const env = config.environments[envName] || config.environments.local;\n\n return {\n api_url: process.env.SQUADS_API_URL || env.api_url,\n admin_api_url: process.env.SQUADS_ADMIN_API_URL || env.admin_api_url,\n console_url: process.env.SQUADS_CONSOLE_URL || env.console_url,\n bridge_url: process.env.SQUADS_BRIDGE_URL || env.bridge_url,\n database_url: process.env.SQUADS_DATABASE_URL || env.database_url,\n redis_url: process.env.REDIS_URL || env.redis_url,\n execution: env.execution,\n };\n}\n\nexport function getEnvName(): string {\n const config = loadConfig();\n return process.env.SQUADS_ENV || config.current;\n}\n\nexport function getApiUrl(): string {\n return getEnv().api_url;\n}\n\nexport function getBridgeUrl(): string {\n return getEnv().bridge_url;\n}\n\nexport function getConsoleUrl(): string {\n return getEnv().console_url;\n}\n"],"mappings":";;;AAaA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AAyBxB,IAAM,aAAa,KAAK,QAAQ,GAAG,SAAS;AAC5C,IAAM,cAAc,KAAK,YAAY,aAAa;AAElD,IAAM,iBAA+B;AAAA,EACnC,SAAS;AAAA,EACT,cAAc;AAAA,IACZ,OAAO;AAAA,MACL,SAAS;AAAA,MACT,eAAe;AAAA,MACf,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,SAAS;AAAA,MACT,eAAe;AAAA,MACf,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,eAAe;AAAA,MACf,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AACF;AAMO,SAAS,aAA2B;AACzC,MAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,eAAW,cAAc;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,MAAM,aAAa,aAAa,OAAO;AAC7C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO;AAAA,MACL,SAAS,OAAO,WAAW;AAAA,MAC3B,cAAc;AAAA,QACZ,GAAG,eAAe;AAAA,QAClB,GAAG,OAAO;AAAA,MACZ;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WAAW,QAA4B;AACrD,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,cAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC3C;AACA,gBAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AACnE;AAMO,SAAS,SAA4B;AAC1C,QAAM,SAAS,WAAW;AAC1B,QAAM,UAAU,QAAQ,IAAI,cAAc,OAAO;AACjD,QAAM,MAAM,OAAO,aAAa,OAAO,KAAK,OAAO,aAAa;AAEhE,SAAO;AAAA,IACL,SAAS,QAAQ,IAAI,kBAAkB,IAAI;AAAA,IAC3C,eAAe,QAAQ,IAAI,wBAAwB,IAAI;AAAA,IACvD,aAAa,QAAQ,IAAI,sBAAsB,IAAI;AAAA,IACnD,YAAY,QAAQ,IAAI,qBAAqB,IAAI;AAAA,IACjD,cAAc,QAAQ,IAAI,uBAAuB,IAAI;AAAA,IACrD,WAAW,QAAQ,IAAI,aAAa,IAAI;AAAA,IACxC,WAAW,IAAI;AAAA,EACjB;AACF;AAEO,SAAS,aAAqB;AACnC,QAAM,SAAS,WAAW;AAC1B,SAAO,QAAQ,IAAI,cAAc,OAAO;AAC1C;AAEO,SAAS,YAAoB;AAClC,SAAO,OAAO,EAAE;AAClB;AAEO,SAAS,eAAuB;AACrC,SAAO,OAAO,EAAE;AAClB;AAEO,SAAS,gBAAwB;AACtC,SAAO,OAAO,EAAE;AAClB;","names":[]}
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/llm-clis.ts
4
+ import { execSync } from "child_process";
5
+ function commandExists(command) {
6
+ try {
7
+ execSync(`which ${command}`, { stdio: "pipe" });
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ var LLM_CLIS = {
14
+ anthropic: {
15
+ provider: "anthropic",
16
+ displayName: "Anthropic",
17
+ command: "claude",
18
+ install: "npm i -g @anthropic-ai/claude-code",
19
+ buildArgs: (prompt) => ["--print", prompt]
20
+ },
21
+ google: {
22
+ provider: "google",
23
+ displayName: "Google",
24
+ command: "gemini",
25
+ install: "npm i -g @google/gemini-cli",
26
+ buildArgs: (prompt) => ["--yolo", "--prompt", prompt]
27
+ },
28
+ openai: {
29
+ provider: "openai",
30
+ displayName: "OpenAI",
31
+ command: "codex",
32
+ install: "npm i -g @openai/codex",
33
+ buildArgs: (prompt) => ["exec", prompt]
34
+ },
35
+ mistral: {
36
+ provider: "mistral",
37
+ displayName: "Mistral",
38
+ command: "vibe",
39
+ install: "curl -LsSf https://mistral.ai/vibe/install.sh | bash",
40
+ buildArgs: (prompt) => ["--prompt", prompt, "--auto-approve"]
41
+ },
42
+ xai: {
43
+ provider: "xai",
44
+ displayName: "xAI",
45
+ command: "grok",
46
+ install: "bun add -g @vibe-kit/grok-cli",
47
+ buildArgs: (prompt) => ["--prompt", prompt]
48
+ },
49
+ aider: {
50
+ provider: "aider",
51
+ displayName: "Aider (Multi)",
52
+ command: "aider",
53
+ install: "pip install aider-install && aider-install",
54
+ buildArgs: (prompt) => ["--message", prompt, "--yes"]
55
+ },
56
+ ollama: {
57
+ provider: "ollama",
58
+ displayName: "Ollama (Local)",
59
+ command: "ollama",
60
+ install: "brew install ollama",
61
+ buildArgs: (prompt, opts) => ["run", opts?.model || "llama3.1", prompt]
62
+ }
63
+ };
64
+ function getAllCLIStatus() {
65
+ return Object.values(LLM_CLIS).map((cli) => ({
66
+ provider: cli.provider,
67
+ displayName: cli.displayName,
68
+ command: cli.command,
69
+ available: commandExists(cli.command),
70
+ install: cli.install
71
+ }));
72
+ }
73
+ function getCLIConfig(provider) {
74
+ return LLM_CLIS[provider];
75
+ }
76
+ function isProviderCLIAvailable(provider) {
77
+ const config = LLM_CLIS[provider];
78
+ if (!config) return false;
79
+ return commandExists(config.command);
80
+ }
81
+
82
+ export {
83
+ getAllCLIStatus,
84
+ getCLIConfig,
85
+ isProviderCLIAvailable
86
+ };
87
+ //# sourceMappingURL=chunk-QHNUMM4V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/llm-clis.ts"],"sourcesContent":["/**\n * Multi-LLM CLI Support\n *\n * Enables squads to use different LLM providers by delegating to their native CLIs.\n * Unix-style composition: each provider maintains their own CLI, we orchestrate.\n *\n * @see specs/multi-llm.md\n */\n\nimport { execSync } from 'child_process';\n\nexport interface CLIConfig {\n /** Provider identifier (matches provider field in SQUAD.md/agent.md) */\n provider: string;\n\n /** Display name for UI */\n displayName: string;\n\n /** CLI command name */\n command: string;\n\n /** Install instructions */\n install: string;\n\n /** Build non-interactive args for execution */\n buildArgs: (prompt: string, options?: RunOptions) => string[];\n}\n\nexport interface RunOptions {\n /** Model override (for providers that support it) */\n model?: string;\n\n /** Working directory */\n cwd?: string;\n\n /** Dry run - just show what would execute */\n dryRun?: boolean;\n}\n\n/**\n * Check if a command exists in PATH\n */\nexport function commandExists(command: string): boolean {\n try {\n execSync(`which ${command}`, { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * LLM CLI registry\n * Maps provider IDs to their CLI configurations\n */\nexport const LLM_CLIS: Record<string, CLIConfig> = {\n anthropic: {\n provider: 'anthropic',\n displayName: 'Anthropic',\n command: 'claude',\n install: 'npm i -g @anthropic-ai/claude-code',\n buildArgs: (prompt) => ['--print', prompt],\n },\n\n google: {\n provider: 'google',\n displayName: 'Google',\n command: 'gemini',\n install: 'npm i -g @google/gemini-cli',\n buildArgs: (prompt) => ['--yolo', '--prompt', prompt],\n },\n\n openai: {\n provider: 'openai',\n displayName: 'OpenAI',\n command: 'codex',\n install: 'npm i -g @openai/codex',\n buildArgs: (prompt) => ['exec', prompt],\n },\n\n mistral: {\n provider: 'mistral',\n displayName: 'Mistral',\n command: 'vibe',\n install: 'curl -LsSf https://mistral.ai/vibe/install.sh | bash',\n buildArgs: (prompt) => ['--prompt', prompt, '--auto-approve'],\n },\n\n xai: {\n provider: 'xai',\n displayName: 'xAI',\n command: 'grok',\n install: 'bun add -g @vibe-kit/grok-cli',\n buildArgs: (prompt) => ['--prompt', prompt],\n },\n\n aider: {\n provider: 'aider',\n displayName: 'Aider (Multi)',\n command: 'aider',\n install: 'pip install aider-install && aider-install',\n buildArgs: (prompt) => ['--message', prompt, '--yes'],\n },\n\n ollama: {\n provider: 'ollama',\n displayName: 'Ollama (Local)',\n command: 'ollama',\n install: 'brew install ollama',\n buildArgs: (prompt, opts) => ['run', opts?.model || 'llama3.1', prompt],\n },\n};\n\nexport interface CLIStatus {\n provider: string;\n displayName: string;\n command: string;\n available: boolean;\n install: string;\n}\n\n/**\n * Get status of all LLM CLIs\n */\nexport function getAllCLIStatus(): CLIStatus[] {\n return Object.values(LLM_CLIS).map((cli) => ({\n provider: cli.provider,\n displayName: cli.displayName,\n command: cli.command,\n available: commandExists(cli.command),\n install: cli.install,\n }));\n}\n\n/**\n * Get CLI config for a provider\n */\nexport function getCLIConfig(provider: string): CLIConfig | undefined {\n return LLM_CLIS[provider];\n}\n\n/**\n * Check if a provider's CLI is available\n */\nexport function isProviderCLIAvailable(provider: string): boolean {\n const config = LLM_CLIS[provider];\n if (!config) return false;\n return commandExists(config.command);\n}\n"],"mappings":";;;AASA,SAAS,gBAAgB;AAiClB,SAAS,cAAc,SAA0B;AACtD,MAAI;AACF,aAAS,SAAS,OAAO,IAAI,EAAE,OAAO,OAAO,CAAC;AAC9C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,IAAM,WAAsC;AAAA,EACjD,WAAW;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,WAAW,MAAM;AAAA,EAC3C;AAAA,EAEA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,UAAU,YAAY,MAAM;AAAA,EACtD;AAAA,EAEA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,QAAQ,MAAM;AAAA,EACxC;AAAA,EAEA,SAAS;AAAA,IACP,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,YAAY,QAAQ,gBAAgB;AAAA,EAC9D;AAAA,EAEA,KAAK;AAAA,IACH,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,YAAY,MAAM;AAAA,EAC5C;AAAA,EAEA,OAAO;AAAA,IACL,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,WAAW,CAAC,aAAa,QAAQ,OAAO;AAAA,EACtD;AAAA,EAEA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW,CAAC,QAAQ,SAAS,CAAC,OAAO,MAAM,SAAS,YAAY,MAAM;AAAA,EACxE;AACF;AAaO,SAAS,kBAA+B;AAC7C,SAAO,OAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,SAAS;AAAA,IAC3C,UAAU,IAAI;AAAA,IACd,aAAa,IAAI;AAAA,IACjB,SAAS,IAAI;AAAA,IACb,WAAW,cAAc,IAAI,OAAO;AAAA,IACpC,SAAS,IAAI;AAAA,EACf,EAAE;AACJ;AAKO,SAAS,aAAa,UAAyC;AACpE,SAAO,SAAS,QAAQ;AAC1B;AAKO,SAAS,uBAAuB,UAA2B;AAChE,QAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,cAAc,OAAO,OAAO;AACrC;","names":[]}
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/cron.ts
4
+ function cronMatches(cron, date) {
5
+ const parts = cron.trim().split(/\s+/);
6
+ if (parts.length < 5) return false;
7
+ const fields = [
8
+ { value: date.getMinutes(), field: parts[0], min: 0, max: 59 },
9
+ { value: date.getHours(), field: parts[1], min: 0, max: 23 },
10
+ { value: date.getDate(), field: parts[2], min: 1, max: 31 },
11
+ { value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },
12
+ { value: date.getDay(), field: parts[4], min: 0, max: 6 }
13
+ ];
14
+ return fields.every(
15
+ ({ value, field, min, max }) => fieldMatches(field, value, min, max)
16
+ );
17
+ }
18
+ function fieldMatches(field, value, min, max) {
19
+ if (field.includes(",")) {
20
+ return field.split(",").some((part) => fieldMatches(part.trim(), value, min, max));
21
+ }
22
+ if (field.includes("/")) {
23
+ const [range, stepStr] = field.split("/");
24
+ const step = parseInt(stepStr);
25
+ if (isNaN(step) || step <= 0) return false;
26
+ let start = min;
27
+ let end = max;
28
+ if (range !== "*") {
29
+ if (range.includes("-")) {
30
+ [start, end] = range.split("-").map(Number);
31
+ } else {
32
+ start = parseInt(range);
33
+ }
34
+ }
35
+ if (value < start || value > end) return false;
36
+ return (value - start) % step === 0;
37
+ }
38
+ if (field.includes("-")) {
39
+ const [start, end] = field.split("-").map(Number);
40
+ return value >= start && value <= end;
41
+ }
42
+ if (field === "*") return true;
43
+ return parseInt(field) === value;
44
+ }
45
+ function getNextCronRun(cron, after = /* @__PURE__ */ new Date()) {
46
+ const next = new Date(after);
47
+ next.setSeconds(0, 0);
48
+ next.setMinutes(next.getMinutes() + 1);
49
+ const maxIterations = 60 * 24 * 8;
50
+ for (let i = 0; i < maxIterations; i++) {
51
+ if (cronMatches(cron, next)) return next;
52
+ next.setMinutes(next.getMinutes() + 1);
53
+ }
54
+ const fallback = new Date(after);
55
+ fallback.setDate(fallback.getDate() + 1);
56
+ return fallback;
57
+ }
58
+ function parseCooldown(cooldown) {
59
+ const match = cooldown.match(/^(\d+)\s*(m|min|minutes?|h|hours?|d|days?)$/i);
60
+ if (!match) return 0;
61
+ const value = parseInt(match[1]);
62
+ const unit = match[2].toLowerCase();
63
+ if (unit.startsWith("m")) return value * 60 * 1e3;
64
+ if (unit.startsWith("h")) return value * 60 * 60 * 1e3;
65
+ if (unit.startsWith("d")) return value * 24 * 60 * 60 * 1e3;
66
+ return 0;
67
+ }
68
+
69
+ export {
70
+ cronMatches,
71
+ getNextCronRun,
72
+ parseCooldown
73
+ };
74
+ //# sourceMappingURL=chunk-RM6BWILN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/cron.ts"],"sourcesContent":["/**\n * Zero-dependency cron evaluator utilities\n * Extracted from autonomous.ts for reusability and testing\n */\n\n/**\n * Check if a cron expression matches a given date\n * @param cron - Cron expression (minute hour day month weekday)\n * @param date - Date to check against\n * @returns true if the cron matches the date\n */\nexport function cronMatches(cron: string, date: Date): boolean {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length < 5) return false;\n\n const fields = [\n { value: date.getMinutes(), field: parts[0], min: 0, max: 59 },\n { value: date.getHours(), field: parts[1], min: 0, max: 23 },\n { value: date.getDate(), field: parts[2], min: 1, max: 31 },\n { value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },\n { value: date.getDay(), field: parts[4], min: 0, max: 6 },\n ];\n\n return fields.every(({ value, field, min, max }) =>\n fieldMatches(field, value, min, max)\n );\n}\n\n/**\n * Check if a cron field matches a value\n * Handles wildcards, ranges, steps, and lists\n */\nexport function fieldMatches(\n field: string,\n value: number,\n min: number,\n max: number\n): boolean {\n // Handle lists: \"1,3,5\"\n if (field.includes(\",\")) {\n return field.split(\",\").some((part) => fieldMatches(part.trim(), value, min, max));\n }\n\n // Handle step: \"*/5\" or \"1-10/2\"\n if (field.includes(\"/\")) {\n const [range, stepStr] = field.split(\"/\");\n const step = parseInt(stepStr);\n if (isNaN(step) || step <= 0) return false;\n\n let start = min;\n let end = max;\n if (range !== \"*\") {\n if (range.includes(\"-\")) {\n [start, end] = range.split(\"-\").map(Number);\n } else {\n start = parseInt(range);\n }\n }\n if (value < start || value > end) return false;\n return (value - start) % step === 0;\n }\n\n // Handle range: \"1-5\"\n if (field.includes(\"-\")) {\n const [start, end] = field.split(\"-\").map(Number);\n return value >= start && value <= end;\n }\n\n // Wildcard\n if (field === \"*\") return true;\n\n // Exact match\n return parseInt(field) === value;\n}\n\n/**\n * Get the next occurrence of a cron expression after `after`.\n * Brute-forces minute by minute (max 48h lookahead).\n */\nexport function getNextCronRun(cron: string, after: Date = new Date()): Date {\n const next = new Date(after);\n next.setSeconds(0, 0);\n next.setMinutes(next.getMinutes() + 1); // Start from next minute\n\n const maxIterations = 60 * 24 * 8; // 8 days (covers weekly schedules)\n for (let i = 0; i < maxIterations; i++) {\n if (cronMatches(cron, next)) return next;\n next.setMinutes(next.getMinutes() + 1);\n }\n\n // Fallback: 24h from now\n const fallback = new Date(after);\n fallback.setDate(fallback.getDate() + 1);\n return fallback;\n}\n\n/**\n * Parse a cooldown string like \"30m\", \"6 hours\", \"7d\" into milliseconds\n * @param cooldown - String like \"30m\", \"6 hours\", \"7d\"\n * @returns milliseconds, or 0 if invalid\n */\nexport function parseCooldown(cooldown: string): number {\n const match = cooldown.match(/^(\\d+)\\s*(m|min|minutes?|h|hours?|d|days?)$/i);\n if (!match) return 0;\n\n const value = parseInt(match[1]);\n const unit = match[2].toLowerCase();\n\n if (unit.startsWith(\"m\")) return value * 60 * 1000;\n if (unit.startsWith(\"h\")) return value * 60 * 60 * 1000;\n if (unit.startsWith(\"d\")) return value * 24 * 60 * 60 * 1000;\n return 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadSession
4
+ } from "./chunk-Z2UKDBNL.js";
5
+ import {
6
+ getApiUrl
7
+ } from "./chunk-OQJHPULO.js";
8
+
9
+ // src/lib/api-client.ts
10
+ var API_TIMEOUT_MS = 5e3;
11
+ function getApiConfig() {
12
+ const session = loadSession();
13
+ if (!session?.accessToken || session.status !== "active") return null;
14
+ const apiUrl = getApiUrl();
15
+ return { apiUrl, token: session.accessToken };
16
+ }
17
+ async function apiRequest(path, method, body) {
18
+ const config = getApiConfig();
19
+ if (!config) return false;
20
+ try {
21
+ const response = await fetch(`${config.apiUrl}${path}`, {
22
+ method,
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ Authorization: `Bearer ${config.token}`
26
+ },
27
+ body: JSON.stringify(body),
28
+ signal: AbortSignal.timeout(API_TIMEOUT_MS)
29
+ });
30
+ return response.ok;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ async function reportExecutionStart(squad, agent, executionId, metadata) {
36
+ const config = getApiConfig();
37
+ if (!config) return null;
38
+ try {
39
+ const response = await fetch(`${config.apiUrl}/agent-executions`, {
40
+ method: "POST",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ Authorization: `Bearer ${config.token}`
44
+ },
45
+ body: JSON.stringify({
46
+ squad,
47
+ agent,
48
+ executor: "cli",
49
+ brief: metadata?.brief,
50
+ model: metadata?.model,
51
+ metadata: {
52
+ local_execution_id: executionId,
53
+ trigger: metadata?.trigger || "manual"
54
+ }
55
+ }),
56
+ signal: AbortSignal.timeout(API_TIMEOUT_MS)
57
+ });
58
+ if (!response.ok) return null;
59
+ const data = await response.json();
60
+ return data.execution_id;
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ async function reportConversationResult(executionId, result) {
66
+ return apiRequest(`/agent-executions/${executionId}`, "PATCH", {
67
+ status: result.converged ? "completed" : "stopped",
68
+ summary: `${result.converged ? "Converged" : "Stopped"}: ${result.reason}`,
69
+ cost_usd: result.totalCost,
70
+ extra_data: {
71
+ conversation: {
72
+ turn_count: result.turnCount,
73
+ total_cost: result.totalCost,
74
+ converged: result.converged,
75
+ reason: result.reason,
76
+ agents_involved: result.agentsInvolved
77
+ }
78
+ }
79
+ });
80
+ }
81
+ async function pushCognitionSignal(signal) {
82
+ return apiRequest("/cognition/signals", "POST", signal);
83
+ }
84
+
85
+ export {
86
+ reportExecutionStart,
87
+ reportConversationResult,
88
+ pushCognitionSignal
89
+ };
90
+ //# sourceMappingURL=chunk-WBR5J7EX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/api-client.ts"],"sourcesContent":["/**\n * Thin API client for reporting executions to the Squads API.\n * All calls are fire-and-forget with timeouts — preserves offline-first behavior.\n */\n\nimport { loadSession } from './auth.js';\nimport { getApiUrl } from './env-config.js';\n\nconst API_TIMEOUT_MS = 5000;\n\nfunction getApiConfig(): { apiUrl: string; token: string } | null {\n const session = loadSession();\n if (!session?.accessToken || session.status !== 'active') return null;\n\n const apiUrl = getApiUrl();\n return { apiUrl, token: session.accessToken };\n}\n\nasync function apiRequest(\n path: string,\n method: 'POST' | 'PATCH',\n body: Record<string, unknown>,\n): Promise<boolean> {\n const config = getApiConfig();\n if (!config) return false;\n\n try {\n const response = await fetch(`${config.apiUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(API_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false; // Silent failure — offline-first\n }\n}\n\n/**\n * Report execution start to the API.\n * Non-blocking: returns a promise that resolves to the API execution_id or null.\n */\nexport async function reportExecutionStart(\n squad: string,\n agent: string,\n executionId: string,\n metadata?: {\n trigger?: string;\n model?: string;\n brief?: string;\n },\n): Promise<string | null> {\n const config = getApiConfig();\n if (!config) return null;\n\n try {\n const response = await fetch(`${config.apiUrl}/agent-executions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n squad,\n agent,\n executor: 'cli',\n brief: metadata?.brief,\n model: metadata?.model,\n metadata: {\n local_execution_id: executionId,\n trigger: metadata?.trigger || 'manual',\n },\n }),\n signal: AbortSignal.timeout(API_TIMEOUT_MS),\n });\n\n if (!response.ok) return null;\n const data = await response.json() as { execution_id: string };\n return data.execution_id;\n } catch {\n return null;\n }\n}\n\n/**\n * Report execution completion to the API.\n * Non-blocking: fire-and-forget.\n */\nexport async function reportExecutionComplete(\n executionId: string,\n status: 'completed' | 'failed',\n details?: {\n summary?: string;\n error?: string;\n durationMs?: number;\n },\n): Promise<boolean> {\n return apiRequest(`/agent-executions/${executionId}`, 'PATCH', {\n status,\n ...(details?.summary ? { summary: details.summary } : {}),\n ...(details?.error ? { error: details.error } : {}),\n });\n}\n\n/**\n * Report conversation result to the API.\n * Extends execution with conversation-specific data in extra_data.\n * Non-blocking: fire-and-forget.\n */\nexport async function reportConversationResult(\n executionId: string,\n result: {\n turnCount: number;\n totalCost: number;\n converged: boolean;\n reason: string;\n agentsInvolved: string[];\n },\n): Promise<boolean> {\n return apiRequest(`/agent-executions/${executionId}`, 'PATCH', {\n status: result.converged ? 'completed' : 'stopped',\n summary: `${result.converged ? 'Converged' : 'Stopped'}: ${result.reason}`,\n cost_usd: result.totalCost,\n extra_data: {\n conversation: {\n turn_count: result.turnCount,\n total_cost: result.totalCost,\n converged: result.converged,\n reason: result.reason,\n agents_involved: result.agentsInvolved,\n },\n },\n });\n}\n\n/**\n * Push a cognition signal to the API.\n * Fire-and-forget — returns true on success, false on failure.\n */\nexport async function pushCognitionSignal(signal: {\n source: string;\n signal_type: string;\n value?: number;\n unit?: string;\n data?: Record<string, unknown>;\n entity_type?: string;\n entity_id?: string;\n confidence?: number;\n}): Promise<boolean> {\n return apiRequest('/cognition/signals', 'POST', signal);\n}\n"],"mappings":";;;;;;;;;AAQA,IAAM,iBAAiB;AAEvB,SAAS,eAAyD;AAChE,QAAM,UAAU,YAAY;AAC5B,MAAI,CAAC,SAAS,eAAe,QAAQ,WAAW,SAAU,QAAO;AAEjE,QAAM,SAAS,UAAU;AACzB,SAAO,EAAE,QAAQ,OAAO,QAAQ,YAAY;AAC9C;AAEA,eAAe,WACb,MACA,QACA,MACkB;AAClB,QAAM,SAAS,aAAa;AAC5B,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,OAAO,MAAM,GAAG,IAAI,IAAI;AAAA,MACtD;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,OAAO,KAAK;AAAA,MACvC;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,YAAY,QAAQ,cAAc;AAAA,IAC5C,CAAC;AACD,WAAO,SAAS;AAAA,EAClB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,qBACpB,OACA,OACA,aACA,UAKwB;AACxB,QAAM,SAAS,aAAa;AAC5B,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,OAAO,MAAM,qBAAqB;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,OAAO,KAAK;AAAA,MACvC;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,OAAO,UAAU;AAAA,QACjB,OAAO,UAAU;AAAA,QACjB,UAAU;AAAA,UACR,oBAAoB;AAAA,UACpB,SAAS,UAAU,WAAW;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,MACD,QAAQ,YAAY,QAAQ,cAAc;AAAA,IAC5C,CAAC;AAED,QAAI,CAAC,SAAS,GAAI,QAAO;AACzB,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,KAAK;AAAA,EACd,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA2BA,eAAsB,yBACpB,aACA,QAOkB;AAClB,SAAO,WAAW,qBAAqB,WAAW,IAAI,SAAS;AAAA,IAC7D,QAAQ,OAAO,YAAY,cAAc;AAAA,IACzC,SAAS,GAAG,OAAO,YAAY,cAAc,SAAS,KAAK,OAAO,MAAM;AAAA,IACxE,UAAU,OAAO;AAAA,IACjB,YAAY;AAAA,MACV,cAAc;AAAA,QACZ,YAAY,OAAO;AAAA,QACnB,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,QAAQ,OAAO;AAAA,QACf,iBAAiB,OAAO;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAMA,eAAsB,oBAAoB,QASrB;AACnB,SAAO,WAAW,sBAAsB,QAAQ,MAAM;AACxD;","names":[]}