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 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": ["member"],
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.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 '../../../lib/resolveTypeId'
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 '../../../lib/resolveTypeId'
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'
@@ -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
+ ]
@@ -0,0 +1,12 @@
1
+ [
2
+ {
3
+ "slug": "support",
4
+ "app_slug": "cortex",
5
+ "name": "Support",
6
+ "description": "Support agent with full CRM and support access",
7
+ "permissions": ["*"],
8
+ "is_system": true,
9
+ "is_active": true,
10
+ "is_protected": false
11
+ }
12
+ ]
@@ -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": "event_driven",
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", "value": "82320862-a99c-4a84-b7ed-c2832cf519cd", "operator": "$eq" }
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 * * *",