spine-framework-cortex 0.1.1 → 0.1.5
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/README.md +27 -0
- package/functions/webhook-handlers.ts +28 -0
- package/lib/resolveTypeId.ts +16 -0
- package/manifest.json +2 -1
- package/package.json +2 -1
- package/pages/courses/CoursesPage.tsx +1 -1
- package/pages/support/RedactionReview.tsx +1 -1
- package/seed/link-types.json +14 -6
- package/seed/pipelines.json +30 -0
- package/seed/roles.json +12 -0
- package/seed/triggers.json +7 -2
- package/seed/types.json +552 -123
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# spine-framework-cortex
|
|
2
|
+
|
|
3
|
+
AI-powered support, CRM, and knowledge base app for Spine Framework.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install spine-framework-cortex
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx spine-framework install-app cortex
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Knowledge Base (KB) management with AI-powered article generation
|
|
20
|
+
- Support ticket routing with AI triage
|
|
21
|
+
- Anonymous visitor tracking and funnel signal processing
|
|
22
|
+
- Opportunity queue for high-engagement prospects
|
|
23
|
+
- Case analysis and resolution tracking
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Handler Self-Registration
|
|
3
|
+
*
|
|
4
|
+
* Registers custom webhook handlers in the dynamic `webhook_handlers` table.
|
|
5
|
+
* Core resolves handlers at runtime via `webhook-registry.ts` — no static imports needed.
|
|
6
|
+
*
|
|
7
|
+
* To add a new handler:
|
|
8
|
+
* 1. Create custom/functions/custom_{name}.ts with a default export function
|
|
9
|
+
* 2. Add a registerWebhookHandler() call below
|
|
10
|
+
* 3. Set integration config handler.path = the handler name
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { registerWebhookHandler } from './_shared/webhook-registration'
|
|
14
|
+
|
|
15
|
+
// Self-register on module load (idempotent)
|
|
16
|
+
registerWebhookHandler({
|
|
17
|
+
name: 'cortex-handler',
|
|
18
|
+
functionName: 'custom_cortex-handler',
|
|
19
|
+
description: 'Cortex integration webhook handler',
|
|
20
|
+
events: ['integration.webhook'],
|
|
21
|
+
}).catch(console.error)
|
|
22
|
+
|
|
23
|
+
registerWebhookHandler({
|
|
24
|
+
name: 'funnel-signal',
|
|
25
|
+
functionName: 'custom_funnel-signal',
|
|
26
|
+
description: 'Funnel signal processing webhook handler',
|
|
27
|
+
events: ['integration.webhook'],
|
|
28
|
+
}).catch(console.error)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Frontend type ID resolution — never hardcode UUIDs in custom app code.
|
|
2
|
+
// IDs differ on every fresh install; always resolve by slug at call time.
|
|
3
|
+
|
|
4
|
+
import { apiFetch } from '@core/lib/api'
|
|
5
|
+
|
|
6
|
+
const cache: Record<string, string> = {}
|
|
7
|
+
|
|
8
|
+
export async function resolveTypeId(slug: string): Promise<string> {
|
|
9
|
+
if (cache[slug]) return cache[slug]
|
|
10
|
+
|
|
11
|
+
const data = await apiFetch(`/.netlify/functions/types?action=getBySlug&slug=${encodeURIComponent(slug)}`)
|
|
12
|
+
if (!data?.id) throw new Error(`Type not found: ${slug}`)
|
|
13
|
+
|
|
14
|
+
cache[slug] = data.id
|
|
15
|
+
return data.id
|
|
16
|
+
}
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"slug": "cortex",
|
|
4
4
|
"description": "Unified workspace for CRM, Support, Community, and Knowledge Base",
|
|
5
5
|
"version": "1.0.0",
|
|
6
|
-
"required_roles": ["
|
|
6
|
+
"required_roles": ["support"],
|
|
7
7
|
"routes": [
|
|
8
8
|
"/cortex",
|
|
9
9
|
"/cortex/dashboard",
|
|
@@ -78,5 +78,6 @@
|
|
|
78
78
|
"features": ["crm", "support", "community", "kb", "courses", "intelligence"],
|
|
79
79
|
"dependencies": ["items", "accounts", "pipelines", "integrations"],
|
|
80
80
|
"entry_point": "./index.tsx",
|
|
81
|
+
"directories": ["pages", "components", "hooks", "config", "seed", "functions", "migrations", "tests"],
|
|
81
82
|
"sidebar_component": "./components/CortexSidebar.tsx"
|
|
82
83
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-framework-cortex",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Cortex — AI-powered support, CRM, and knowledge base app for Spine Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"pages/",
|
|
20
20
|
"components/",
|
|
21
21
|
"functions/",
|
|
22
|
+
"lib/",
|
|
22
23
|
"README.md"
|
|
23
24
|
],
|
|
24
25
|
"spine": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react'
|
|
2
|
-
import { resolveTypeId } from '
|
|
2
|
+
import { resolveTypeId } from '../../lib/resolveTypeId'
|
|
3
3
|
import { useNavigate } from 'react-router-dom'
|
|
4
4
|
import { apiFetch } from '@core/lib/api'
|
|
5
5
|
import { Button } from '@core/components/ui/button'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react'
|
|
2
|
-
import { resolveTypeId } from '
|
|
2
|
+
import { resolveTypeId } from '../../lib/resolveTypeId'
|
|
3
3
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
4
4
|
import { apiFetch } from '@core/lib/api'
|
|
5
5
|
import { Button } from '@core/components/ui/button'
|
package/seed/link-types.json
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
"slug": "account_opportunities",
|
|
4
4
|
"name": "Account Opportunities",
|
|
5
5
|
"description": "Links accounts to their opportunity queue entries",
|
|
6
|
+
"source_type_slug": "account",
|
|
7
|
+
"target_type_slug": "item",
|
|
8
|
+
"relationship_type": "many-to-many",
|
|
6
9
|
"config": {
|
|
7
|
-
"cardinality": "many-to-many"
|
|
8
|
-
"source_type": "account",
|
|
9
|
-
"target_type": "item"
|
|
10
|
+
"cardinality": "many-to-many"
|
|
10
11
|
},
|
|
11
12
|
"is_active": true
|
|
12
13
|
},
|
|
@@ -14,10 +15,11 @@
|
|
|
14
15
|
"slug": "account_signals",
|
|
15
16
|
"name": "Account Signals",
|
|
16
17
|
"description": "Links accounts to their funnel signals",
|
|
18
|
+
"source_type_slug": "account",
|
|
19
|
+
"target_type_slug": "item",
|
|
20
|
+
"relationship_type": "many-to-many",
|
|
17
21
|
"config": {
|
|
18
|
-
"cardinality": "many-to-many"
|
|
19
|
-
"source_type": "account",
|
|
20
|
-
"target_type": "item"
|
|
22
|
+
"cardinality": "many-to-many"
|
|
21
23
|
},
|
|
22
24
|
"is_active": true
|
|
23
25
|
},
|
|
@@ -25,6 +27,9 @@
|
|
|
25
27
|
"slug": "analyzed_by",
|
|
26
28
|
"name": "Analyzed By",
|
|
27
29
|
"description": "Ticket analyzed by case analysis",
|
|
30
|
+
"source_type_slug": "item",
|
|
31
|
+
"target_type_slug": "item",
|
|
32
|
+
"relationship_type": "many-to-many",
|
|
28
33
|
"config": {
|
|
29
34
|
"forward_label": "analyzed by",
|
|
30
35
|
"reverse_label": "analysis of"
|
|
@@ -35,6 +40,9 @@
|
|
|
35
40
|
"slug": "tagged_with",
|
|
36
41
|
"name": "Tagged With",
|
|
37
42
|
"description": "Entity tagged with a specific tag",
|
|
43
|
+
"source_type_slug": "item",
|
|
44
|
+
"target_type_slug": "item",
|
|
45
|
+
"relationship_type": "many-to-many",
|
|
38
46
|
"config": {
|
|
39
47
|
"forward_label": "tagged with",
|
|
40
48
|
"reverse_label": "applied to"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Case Analysis Pipeline",
|
|
4
|
+
"description": "Automatically analyzes resolved support tickets to extract insights and create tags",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"name": "Analyze Ticket",
|
|
8
|
+
"type": "function",
|
|
9
|
+
"config": {
|
|
10
|
+
"function": "case_analysis.analyze"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Generate Tags",
|
|
15
|
+
"type": "function",
|
|
16
|
+
"config": {
|
|
17
|
+
"function": "tag_management.generate"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"config": {
|
|
22
|
+
"debounce_ms": 5000,
|
|
23
|
+
"retry_count": 3,
|
|
24
|
+
"retry_delay_ms": 1000
|
|
25
|
+
},
|
|
26
|
+
"ownership": "tenant",
|
|
27
|
+
"is_system": false,
|
|
28
|
+
"is_active": true
|
|
29
|
+
}
|
|
30
|
+
]
|
package/seed/roles.json
ADDED
package/seed/triggers.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
{
|
|
3
3
|
"name": "Case Resolution Analysis",
|
|
4
4
|
"description": "Automatically triggers case analysis when support tickets are resolved",
|
|
5
|
-
"trigger_type": "
|
|
5
|
+
"trigger_type": "event",
|
|
6
6
|
"event_type": "item_updated",
|
|
7
|
+
"scope": "account",
|
|
7
8
|
"config": {
|
|
8
9
|
"filters": [
|
|
9
10
|
{ "field": "status", "value": "resolved", "operator": "$eq" },
|
|
10
|
-
{ "field": "type_id", "
|
|
11
|
+
{ "field": "type_id", "type_slug": "support_ticket", "operator": "$eq" }
|
|
11
12
|
],
|
|
12
13
|
"debounce_ms": 5000,
|
|
13
14
|
"entity_type": "items",
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
"description": "Processes funnel signals to update lead scores and lifecycle stages",
|
|
24
25
|
"trigger_type": "event",
|
|
25
26
|
"event_type": "item_created",
|
|
27
|
+
"scope": "account",
|
|
26
28
|
"config": {
|
|
27
29
|
"filters": {},
|
|
28
30
|
"type_slug": "funnel_signal",
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
"description": "Hourly dashboard metric aggregation",
|
|
38
40
|
"trigger_type": "cron",
|
|
39
41
|
"event_type": null,
|
|
42
|
+
"scope": "account",
|
|
40
43
|
"config": {
|
|
41
44
|
"function": "funnel-timers.aggregation",
|
|
42
45
|
"schedule": "0 * * * *",
|
|
@@ -52,6 +55,7 @@
|
|
|
52
55
|
"description": "Daily recalculation of account ratings for score decay",
|
|
53
56
|
"trigger_type": "cron",
|
|
54
57
|
"event_type": null,
|
|
58
|
+
"scope": "account",
|
|
55
59
|
"config": {
|
|
56
60
|
"function": "funnel-timers.scoreDecay",
|
|
57
61
|
"schedule": "59 23 * * *",
|
|
@@ -67,6 +71,7 @@
|
|
|
67
71
|
"description": "Daily purge of expired anonymous sessions",
|
|
68
72
|
"trigger_type": "cron",
|
|
69
73
|
"event_type": null,
|
|
74
|
+
"scope": "account",
|
|
70
75
|
"config": {
|
|
71
76
|
"function": "funnel-timers.sessionCleanup",
|
|
72
77
|
"schedule": "0 2 * * *",
|