@nextsparkjs/theme-default 0.1.0-beta.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/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { ToolDefinition } from '@/plugins/langchain/lib/tools-builder'
|
|
3
|
+
import { TasksService } from '@/themes/default/entities/tasks/tasks.service'
|
|
4
|
+
import { TaskStatus, TaskPriority } from '@/themes/default/entities/tasks/tasks.types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tool context for task operations
|
|
8
|
+
*/
|
|
9
|
+
export interface TaskToolContext {
|
|
10
|
+
userId: string
|
|
11
|
+
teamId: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create task management tools for the AI agent
|
|
16
|
+
*
|
|
17
|
+
* @param context - User and team context for RLS
|
|
18
|
+
* @returns Array of task-related tool definitions
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const tools = createTaskTools({ userId: 'user-123', teamId: 'team-456' })
|
|
23
|
+
* const agent = await createAgent({
|
|
24
|
+
* systemPrompt: loadSystemPrompt('task-assistant'),
|
|
25
|
+
* tools,
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function createTaskTools(context: TaskToolContext): ToolDefinition<any>[] {
|
|
30
|
+
const { userId, teamId } = context
|
|
31
|
+
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
name: 'list_tasks',
|
|
35
|
+
description: 'List all tasks assigned to the user. Optionally filter by status and/or priority.',
|
|
36
|
+
schema: z.object({
|
|
37
|
+
status: z.enum(['todo', 'in-progress', 'review', 'done', 'blocked'])
|
|
38
|
+
.optional()
|
|
39
|
+
.describe('Filter tasks by status'),
|
|
40
|
+
priority: z.enum(['low', 'medium', 'high', 'urgent'])
|
|
41
|
+
.optional()
|
|
42
|
+
.describe('Filter tasks by priority level'),
|
|
43
|
+
}),
|
|
44
|
+
func: async ({ status, priority }) => {
|
|
45
|
+
try {
|
|
46
|
+
const result = await TasksService.list(userId, {
|
|
47
|
+
status: status as TaskStatus,
|
|
48
|
+
limit: 50,
|
|
49
|
+
teamId,
|
|
50
|
+
})
|
|
51
|
+
// Filter by priority if specified
|
|
52
|
+
let tasks = result.tasks
|
|
53
|
+
if (priority) {
|
|
54
|
+
tasks = tasks.filter(t => t.priority === priority)
|
|
55
|
+
}
|
|
56
|
+
return JSON.stringify(tasks.map(t => ({
|
|
57
|
+
id: t.id,
|
|
58
|
+
title: t.title,
|
|
59
|
+
status: t.status,
|
|
60
|
+
priority: t.priority,
|
|
61
|
+
dueDate: t.dueDate,
|
|
62
|
+
})), null, 2)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return `Error listing tasks: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'search_tasks',
|
|
70
|
+
description: 'Search tasks by keyword in title or description.',
|
|
71
|
+
schema: z.object({
|
|
72
|
+
query: z.string().describe('Search keyword'),
|
|
73
|
+
}),
|
|
74
|
+
func: async ({ query }) => {
|
|
75
|
+
try {
|
|
76
|
+
const result = await TasksService.list(userId, { limit: 100, teamId })
|
|
77
|
+
const filtered = result.tasks.filter(t =>
|
|
78
|
+
t.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
79
|
+
t.description?.toLowerCase().includes(query.toLowerCase())
|
|
80
|
+
)
|
|
81
|
+
return JSON.stringify(filtered.slice(0, 10).map(t => ({
|
|
82
|
+
id: t.id,
|
|
83
|
+
title: t.title,
|
|
84
|
+
status: t.status,
|
|
85
|
+
priority: t.priority,
|
|
86
|
+
})), null, 2)
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return `Error searching tasks: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'get_task_details',
|
|
94
|
+
description: 'Get full details of a specific task by ID.',
|
|
95
|
+
schema: z.object({
|
|
96
|
+
taskId: z.string().describe('The task ID'),
|
|
97
|
+
}),
|
|
98
|
+
func: async ({ taskId }) => {
|
|
99
|
+
try {
|
|
100
|
+
const task = await TasksService.getById(taskId, userId)
|
|
101
|
+
if (!task) {
|
|
102
|
+
return JSON.stringify({ error: 'Task not found' })
|
|
103
|
+
}
|
|
104
|
+
return JSON.stringify(task, null, 2)
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return `Error getting task: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'create_task',
|
|
112
|
+
description: 'Create a new task.',
|
|
113
|
+
schema: z.object({
|
|
114
|
+
title: z.string().describe('Task title'),
|
|
115
|
+
description: z.string().optional().describe('Task description'),
|
|
116
|
+
priority: z.enum(['low', 'medium', 'high'])
|
|
117
|
+
.optional()
|
|
118
|
+
.default('medium')
|
|
119
|
+
.describe('Task priority'),
|
|
120
|
+
dueDate: z.string().optional().describe('Due date in ISO format (YYYY-MM-DD)'),
|
|
121
|
+
}),
|
|
122
|
+
func: async (data) => {
|
|
123
|
+
try {
|
|
124
|
+
const task = await TasksService.create(userId, {
|
|
125
|
+
title: data.title,
|
|
126
|
+
description: data.description,
|
|
127
|
+
priority: data.priority as TaskPriority,
|
|
128
|
+
dueDate: data.dueDate,
|
|
129
|
+
status: 'todo' as TaskStatus,
|
|
130
|
+
teamId,
|
|
131
|
+
})
|
|
132
|
+
return JSON.stringify(task, null, 2)
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return `Error creating task: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'update_task',
|
|
140
|
+
description: 'Update an existing task.',
|
|
141
|
+
schema: z.object({
|
|
142
|
+
taskId: z.string().describe('The task ID to update'),
|
|
143
|
+
title: z.string().optional().describe('New title'),
|
|
144
|
+
description: z.string().optional().describe('New description'),
|
|
145
|
+
status: z.enum(['todo', 'in-progress', 'review', 'done', 'blocked'])
|
|
146
|
+
.optional()
|
|
147
|
+
.describe('New status'),
|
|
148
|
+
priority: z.enum(['low', 'medium', 'high'])
|
|
149
|
+
.optional()
|
|
150
|
+
.describe('New priority'),
|
|
151
|
+
dueDate: z.string().optional().describe('New due date in ISO format'),
|
|
152
|
+
}),
|
|
153
|
+
func: async ({ taskId, ...updates }) => {
|
|
154
|
+
try {
|
|
155
|
+
const task = await TasksService.update(userId, taskId, {
|
|
156
|
+
...updates,
|
|
157
|
+
status: updates.status as TaskStatus,
|
|
158
|
+
priority: updates.priority as TaskPriority,
|
|
159
|
+
})
|
|
160
|
+
return JSON.stringify(task, null, 2)
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return `Error updating task: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
]
|
|
167
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing Scheduled Action Handler
|
|
3
|
+
*
|
|
4
|
+
* This handler performs daily checks for subscription renewals and updates
|
|
5
|
+
* subscription statuses for expired subscriptions.
|
|
6
|
+
*
|
|
7
|
+
* Uses core billing services for actual subscription lifecycle management.
|
|
8
|
+
*
|
|
9
|
+
* @module contents/themes/default/lib/scheduled-actions/billing
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { registerScheduledAction, scheduleRecurringAction, scheduleAction } from '@nextsparkjs/core/lib/scheduled-actions'
|
|
13
|
+
import type { ScheduledAction } from '@nextsparkjs/core/lib/scheduled-actions'
|
|
14
|
+
import { queryWithRLS } from '@nextsparkjs/core/lib/db'
|
|
15
|
+
import { expireTrials, handlePastDueGracePeriod } from '@nextsparkjs/core/lib/billing/jobs'
|
|
16
|
+
import { SubscriptionService } from '@nextsparkjs/core/lib/services/subscription.service'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register the billing check action handler
|
|
20
|
+
*
|
|
21
|
+
* This function registers the 'billing:check-renewals' action handler that
|
|
22
|
+
* processes subscription renewals and updates expired subscriptions.
|
|
23
|
+
*
|
|
24
|
+
* Uses core billing services:
|
|
25
|
+
* - expireTrials(): Expire trials that have passed their end date
|
|
26
|
+
* - handlePastDueGracePeriod(): Expire past_due subscriptions after grace period
|
|
27
|
+
* - SubscriptionService.listExpiringSoon(): Get subscriptions expiring soon
|
|
28
|
+
*/
|
|
29
|
+
export function registerBillingAction() {
|
|
30
|
+
registerScheduledAction(
|
|
31
|
+
'billing:check-renewals',
|
|
32
|
+
async (_payload: unknown, _action: ScheduledAction) => {
|
|
33
|
+
console.log('[billing:check-renewals] Starting subscription lifecycle check...')
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// 1. Expire trials that have passed their end date
|
|
37
|
+
const trialsResult = await expireTrials()
|
|
38
|
+
console.log(`[billing:check-renewals] Trials expired: ${trialsResult.processed}`)
|
|
39
|
+
if (trialsResult.errors.length > 0) {
|
|
40
|
+
console.warn('[billing:check-renewals] Trial expiration errors:', trialsResult.errors)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2. Handle past_due subscriptions after grace period
|
|
44
|
+
const pastDueResult = await handlePastDueGracePeriod()
|
|
45
|
+
console.log(`[billing:check-renewals] Past due expired: ${pastDueResult.processed}`)
|
|
46
|
+
if (pastDueResult.errors.length > 0) {
|
|
47
|
+
console.warn('[billing:check-renewals] Past due handling errors:', pastDueResult.errors)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Send reminder webhooks for subscriptions expiring in 7 days
|
|
51
|
+
const expiringSoon = await SubscriptionService.listExpiringSoon(7)
|
|
52
|
+
console.log(`[billing:check-renewals] Subscriptions expiring in 7 days: ${expiringSoon.length}`)
|
|
53
|
+
|
|
54
|
+
// Schedule webhook for each expiring subscription
|
|
55
|
+
let webhooksScheduled = 0
|
|
56
|
+
for (const sub of expiringSoon) {
|
|
57
|
+
const daysUntilExpiry = Math.ceil(
|
|
58
|
+
(new Date(sub.currentPeriodEnd).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
await scheduleAction('webhook:send', {
|
|
62
|
+
eventType: 'expiring_soon',
|
|
63
|
+
entityType: 'subscription',
|
|
64
|
+
entityId: sub.id,
|
|
65
|
+
data: {
|
|
66
|
+
subscriptionId: sub.id,
|
|
67
|
+
teamId: sub.teamId,
|
|
68
|
+
planSlug: sub.plan.slug,
|
|
69
|
+
status: sub.status,
|
|
70
|
+
periodEnd: sub.currentPeriodEnd,
|
|
71
|
+
daysUntilExpiry,
|
|
72
|
+
},
|
|
73
|
+
}, { teamId: sub.teamId })
|
|
74
|
+
|
|
75
|
+
webhooksScheduled++
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (webhooksScheduled > 0) {
|
|
79
|
+
console.log(`[billing:check-renewals] Scheduled ${webhooksScheduled} expiring_soon webhooks`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Log summary
|
|
83
|
+
console.log('[billing:check-renewals] Check completed successfully:', {
|
|
84
|
+
trialsExpired: trialsResult.processed,
|
|
85
|
+
pastDueExpired: pastDueResult.processed,
|
|
86
|
+
expiringSoon: expiringSoon.length,
|
|
87
|
+
webhooksScheduled,
|
|
88
|
+
})
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('[billing:check-renewals] Error during check:', error)
|
|
91
|
+
throw error
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
description: 'Daily subscription lifecycle check',
|
|
96
|
+
timeout: 60000, // 60 seconds for billing checks
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Ensure billing action is scheduled as recurring
|
|
103
|
+
*
|
|
104
|
+
* This function should be called at app startup to ensure the billing check
|
|
105
|
+
* is scheduled to run daily. It checks if a recurring action already exists
|
|
106
|
+
* to avoid duplicates.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // Call at app startup
|
|
110
|
+
* await ensureBillingActionScheduled()
|
|
111
|
+
*/
|
|
112
|
+
export async function ensureBillingActionScheduled() {
|
|
113
|
+
try {
|
|
114
|
+
// Check if a pending recurring billing action already exists
|
|
115
|
+
// Only check for pending status - if no pending exists, create a new one
|
|
116
|
+
const existingQuery = `
|
|
117
|
+
SELECT id
|
|
118
|
+
FROM "scheduled_actions"
|
|
119
|
+
WHERE "actionType" = 'billing:check-renewals'
|
|
120
|
+
AND "recurringInterval" = 'daily'
|
|
121
|
+
AND status = 'pending'
|
|
122
|
+
LIMIT 1
|
|
123
|
+
`
|
|
124
|
+
|
|
125
|
+
const existing = await queryWithRLS<{ id: string }>(
|
|
126
|
+
existingQuery,
|
|
127
|
+
[],
|
|
128
|
+
null // System operation, no user context
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if (existing.length > 0) {
|
|
132
|
+
console.log('[billing:check-renewals] Daily recurring action already scheduled')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Schedule new recurring daily action
|
|
137
|
+
console.log('[billing:check-renewals] Scheduling new daily recurring action...')
|
|
138
|
+
const actionId = await scheduleRecurringAction(
|
|
139
|
+
'billing:check-renewals',
|
|
140
|
+
{}, // No payload needed
|
|
141
|
+
'daily'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
console.log(`[billing:check-renewals] Daily recurring action scheduled with ID: ${actionId}`)
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('[billing:check-renewals] Error ensuring action is scheduled:', error)
|
|
147
|
+
// Don't throw - this is called at startup and shouldn't crash the app
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Scheduled Actions - Handler Registration
|
|
3
|
+
*
|
|
4
|
+
* This module registers all scheduled action handlers for the default theme.
|
|
5
|
+
* The core initializer calls these functions at startup.
|
|
6
|
+
*
|
|
7
|
+
* @module contents/themes/default/lib/scheduled-actions
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Called by core/lib/scheduled-actions/initializer.ts
|
|
11
|
+
* // Do NOT call directly from app code
|
|
12
|
+
* registerAllHandlers()
|
|
13
|
+
* await registerRecurringActions()
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { registerWebhookAction } from './webhook'
|
|
17
|
+
import { registerBillingAction, ensureBillingActionScheduled } from './billing'
|
|
18
|
+
import { addAction } from '@nextsparkjs/core/lib/plugins/hook-system'
|
|
19
|
+
import { scheduleAction } from '@nextsparkjs/core/lib/scheduled-actions'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register all scheduled action handlers for this theme
|
|
23
|
+
*
|
|
24
|
+
* This function registers all action handlers (webhook, billing, etc.).
|
|
25
|
+
* Called by the core initializer - the guard is handled there.
|
|
26
|
+
*/
|
|
27
|
+
export function registerAllHandlers(): void {
|
|
28
|
+
console.log('[theme:scheduled-actions] Registering action handlers...')
|
|
29
|
+
|
|
30
|
+
registerWebhookAction()
|
|
31
|
+
registerBillingAction()
|
|
32
|
+
registerEntityWebhookHooks()
|
|
33
|
+
|
|
34
|
+
console.log('[theme:scheduled-actions] All action handlers registered')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Register entity hooks for webhook triggers
|
|
39
|
+
*
|
|
40
|
+
* Listens to entity.tasks.created and entity.tasks.updated hooks
|
|
41
|
+
* and schedules webhook:send actions.
|
|
42
|
+
*/
|
|
43
|
+
function registerEntityWebhookHooks() {
|
|
44
|
+
console.log('[theme:scheduled-actions] Registering entity webhook hooks...')
|
|
45
|
+
|
|
46
|
+
// AC-15: Task create triggers webhook:send
|
|
47
|
+
addAction('entity.tasks.created', async (hookData: Record<string, unknown>) => {
|
|
48
|
+
try {
|
|
49
|
+
console.log('[webhook:trigger] Task created, scheduling webhook...')
|
|
50
|
+
const entityData = hookData.data as Record<string, unknown> | undefined
|
|
51
|
+
await scheduleAction('webhook:send', {
|
|
52
|
+
eventType: 'created',
|
|
53
|
+
entityType: 'task',
|
|
54
|
+
entityId: (hookData.id as string) || (entityData?.id as string),
|
|
55
|
+
data: entityData,
|
|
56
|
+
teamId: entityData?.teamId as string | undefined,
|
|
57
|
+
})
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('[webhook:trigger] Error scheduling task create webhook:', error)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// AC-16: Task update triggers webhook:send
|
|
64
|
+
addAction('entity.tasks.updated', async (hookData: Record<string, unknown>) => {
|
|
65
|
+
try {
|
|
66
|
+
console.log('[webhook:trigger] Task updated, scheduling webhook...')
|
|
67
|
+
const entityData = hookData.data as Record<string, unknown> | undefined
|
|
68
|
+
await scheduleAction('webhook:send', {
|
|
69
|
+
eventType: 'updated',
|
|
70
|
+
entityType: 'task',
|
|
71
|
+
entityId: hookData.id as string,
|
|
72
|
+
data: entityData,
|
|
73
|
+
teamId: entityData?.teamId as string | undefined,
|
|
74
|
+
})
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error('[webhook:trigger] Error scheduling task update webhook:', error)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// =========================================================================
|
|
81
|
+
// SUBSCRIPTION WEBHOOK HOOKS
|
|
82
|
+
// =========================================================================
|
|
83
|
+
// These hooks listen to subscription lifecycle events from SubscriptionService
|
|
84
|
+
// and schedule webhook:send actions to notify external systems.
|
|
85
|
+
|
|
86
|
+
// Subscription created
|
|
87
|
+
addAction('subscription.created', async (hookData: Record<string, unknown>) => {
|
|
88
|
+
try {
|
|
89
|
+
console.log('[webhook:trigger] Subscription created, scheduling webhook...')
|
|
90
|
+
const entityData = hookData.data as Record<string, unknown> | undefined
|
|
91
|
+
await scheduleAction('webhook:send', {
|
|
92
|
+
eventType: 'created',
|
|
93
|
+
entityType: 'subscription',
|
|
94
|
+
entityId: hookData.id as string,
|
|
95
|
+
data: entityData,
|
|
96
|
+
teamId: (hookData.teamId as string) || (entityData?.teamId as string),
|
|
97
|
+
webhookKey: 'subscriptions',
|
|
98
|
+
})
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('[webhook:trigger] Error scheduling subscription created webhook:', error)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Subscription updated (status change, plan change)
|
|
105
|
+
addAction('subscription.updated', async (hookData: Record<string, unknown>) => {
|
|
106
|
+
try {
|
|
107
|
+
console.log('[webhook:trigger] Subscription updated, scheduling webhook...')
|
|
108
|
+
const entityData = hookData.data as Record<string, unknown> | undefined
|
|
109
|
+
await scheduleAction('webhook:send', {
|
|
110
|
+
eventType: 'updated',
|
|
111
|
+
entityType: 'subscription',
|
|
112
|
+
entityId: hookData.id as string,
|
|
113
|
+
data: {
|
|
114
|
+
...entityData,
|
|
115
|
+
previousStatus: hookData.previousStatus,
|
|
116
|
+
newStatus: hookData.newStatus,
|
|
117
|
+
previousPlan: hookData.previousPlan,
|
|
118
|
+
newPlan: hookData.newPlan,
|
|
119
|
+
isUpgrade: hookData.isUpgrade,
|
|
120
|
+
},
|
|
121
|
+
teamId: (hookData.teamId as string) || (entityData?.teamId as string),
|
|
122
|
+
webhookKey: 'subscriptions',
|
|
123
|
+
})
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('[webhook:trigger] Error scheduling subscription updated webhook:', error)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Subscription cancelled
|
|
130
|
+
addAction('subscription.cancelled', async (hookData: Record<string, unknown>) => {
|
|
131
|
+
try {
|
|
132
|
+
console.log('[webhook:trigger] Subscription cancelled, scheduling webhook...')
|
|
133
|
+
const entityData = hookData.data as Record<string, unknown> | undefined
|
|
134
|
+
await scheduleAction('webhook:send', {
|
|
135
|
+
eventType: 'cancelled',
|
|
136
|
+
entityType: 'subscription',
|
|
137
|
+
entityId: hookData.id as string,
|
|
138
|
+
data: {
|
|
139
|
+
...entityData,
|
|
140
|
+
immediate: hookData.immediate,
|
|
141
|
+
cancelAtPeriodEnd: hookData.cancelAtPeriodEnd,
|
|
142
|
+
},
|
|
143
|
+
teamId: entityData?.teamId as string | undefined,
|
|
144
|
+
webhookKey: 'subscriptions',
|
|
145
|
+
})
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('[webhook:trigger] Error scheduling subscription cancelled webhook:', error)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
console.log('[theme:scheduled-actions] Entity webhook hooks registered')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Register recurring scheduled actions for this theme
|
|
156
|
+
*
|
|
157
|
+
* This async function ensures all recurring actions are scheduled.
|
|
158
|
+
* Called by the core initializer after handlers are registered.
|
|
159
|
+
*/
|
|
160
|
+
export async function registerRecurringActions(): Promise<void> {
|
|
161
|
+
console.log('[theme:scheduled-actions] Registering recurring actions...')
|
|
162
|
+
|
|
163
|
+
await ensureBillingActionScheduled()
|
|
164
|
+
|
|
165
|
+
console.log('[theme:scheduled-actions] All recurring actions registered')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Re-export individual functions for direct use
|
|
169
|
+
export { registerWebhookAction } from './webhook'
|
|
170
|
+
export { registerBillingAction, ensureBillingActionScheduled } from './billing'
|