@iwo-szapar/data-mcp 0.2.1 → 0.3.2
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 +286 -0
- package/dist/server.js +2 -2
- package/dist/tools/memory/link-create.d.ts +4 -0
- package/dist/tools/memory/link-create.js +67 -0
- package/dist/tools/memory/link-delete.d.ts +4 -0
- package/dist/tools/memory/link-delete.js +27 -0
- package/dist/tools/memory/link-related.d.ts +4 -0
- package/dist/tools/memory/link-related.js +91 -0
- package/dist/tools/memory/link-suggest.d.ts +4 -0
- package/dist/tools/memory/link-suggest.js +83 -0
- package/dist/tools/register.js +10 -2
- package/migrations/pocketbase/001_core_schema.js +28 -24
- package/migrations/pocketbase/002_goals_tasks.js +18 -15
- package/migrations/pocketbase/003_contacts.js +12 -9
- package/migrations/pocketbase/004_entity_aliases.js +12 -9
- package/migrations/pocketbase/005_prospects.js +14 -11
- package/migrations/pocketbase/006_business.js +33 -29
- package/migrations/pocketbase/007_newsletter_affiliates.js +19 -16
- package/migrations/pocketbase/008_knowledge_links.js +41 -0
- package/migrations/supabase/009_align_to_production.sql +11 -14
- package/migrations/supabase/010_knowledge_links.sql +47 -0
- package/package.json +20 -5
|
@@ -2,24 +2,26 @@
|
|
|
2
2
|
* PocketBase Migration 001: Core schema
|
|
3
3
|
*
|
|
4
4
|
* Creates knowledge, decisions, sessions collections.
|
|
5
|
+
* PocketBase v0.23+ field format.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
/// <reference path="../pb_data/types.d.ts" />
|
|
8
9
|
|
|
9
10
|
migrate((app) => {
|
|
10
|
-
// knowledge collection
|
|
11
11
|
const knowledge = new Collection({
|
|
12
12
|
name: 'knowledge',
|
|
13
13
|
type: 'base',
|
|
14
|
-
|
|
15
|
-
{ name: 'type', type: 'select', required: true,
|
|
16
|
-
{ name: 'title', type: 'text', required: true,
|
|
17
|
-
{ name: 'content', type: 'editor', required: true,
|
|
18
|
-
{ name: 'summary', type: 'text',
|
|
14
|
+
fields: [
|
|
15
|
+
{ name: 'type', type: 'select', required: true, maxSelect: 1, values: ['fact', 'knowledge', 'pattern', 'insight', 'lesson', 'reference'] },
|
|
16
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
17
|
+
{ name: 'content', type: 'editor', required: true, maxSize: 50000 },
|
|
18
|
+
{ name: 'summary', type: 'text', max: 2000 },
|
|
19
19
|
{ name: 'tags', type: 'json' },
|
|
20
|
-
{ name: 'source', type: 'text',
|
|
21
|
-
{ name: 'confidence', type: 'number',
|
|
20
|
+
{ name: 'source', type: 'text', max: 500 },
|
|
21
|
+
{ name: 'confidence', type: 'number', min: 0, max: 1 },
|
|
22
22
|
{ name: 'last_validated_at', type: 'date' },
|
|
23
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
24
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
23
25
|
],
|
|
24
26
|
indexes: [
|
|
25
27
|
'CREATE INDEX idx_knowledge_type ON knowledge (type)',
|
|
@@ -28,45 +30,47 @@ migrate((app) => {
|
|
|
28
30
|
});
|
|
29
31
|
app.save(knowledge);
|
|
30
32
|
|
|
31
|
-
// decisions collection
|
|
32
33
|
const decisions = new Collection({
|
|
33
34
|
name: 'decisions',
|
|
34
35
|
type: 'base',
|
|
35
|
-
|
|
36
|
-
{ name: 'title', type: 'text', required: true,
|
|
37
|
-
{ name: 'context', type: 'editor',
|
|
36
|
+
fields: [
|
|
37
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
38
|
+
{ name: 'context', type: 'editor', maxSize: 5000 },
|
|
38
39
|
{ name: 'options_considered', type: 'json', required: true },
|
|
39
|
-
{ name: 'chosen_option', type: 'text', required: true,
|
|
40
|
-
{ name: 'rationale', type: 'editor',
|
|
41
|
-
{ name: 'outcome', type: 'editor',
|
|
40
|
+
{ name: 'chosen_option', type: 'text', required: true, max: 500 },
|
|
41
|
+
{ name: 'rationale', type: 'editor', maxSize: 5000 },
|
|
42
|
+
{ name: 'outcome', type: 'editor', maxSize: 5000 },
|
|
42
43
|
{ name: 'tags', type: 'json' },
|
|
44
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
45
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
43
46
|
],
|
|
44
47
|
});
|
|
45
48
|
app.save(decisions);
|
|
46
49
|
|
|
47
|
-
// sessions collection
|
|
48
50
|
const sessions = new Collection({
|
|
49
51
|
name: 'sessions',
|
|
50
52
|
type: 'base',
|
|
51
|
-
|
|
52
|
-
{ name: 'title', type: 'text', required: true,
|
|
53
|
-
{ name: 'summary', type: 'editor', required: true,
|
|
53
|
+
fields: [
|
|
54
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
55
|
+
{ name: 'summary', type: 'editor', required: true, maxSize: 10000 },
|
|
54
56
|
{ name: 'session_date', type: 'date' },
|
|
55
57
|
{ name: 'skills_used', type: 'json' },
|
|
56
58
|
{ name: 'files_changed', type: 'json' },
|
|
57
59
|
{ name: 'decisions_made', type: 'json' },
|
|
58
60
|
{ name: 'duration_minutes', type: 'number' },
|
|
59
|
-
{ name: 'task_id', type: 'text',
|
|
60
|
-
{ name: 'branch', type: 'text',
|
|
61
|
+
{ name: 'task_id', type: 'text', max: 100 },
|
|
62
|
+
{ name: 'branch', type: 'text', max: 200 },
|
|
61
63
|
{ name: 'patterns_learned', type: 'json' },
|
|
62
64
|
{ name: 'knowledge_created', type: 'number' },
|
|
63
65
|
{ name: 'knowledge_updated', type: 'number' },
|
|
64
66
|
{ name: 'metadata', type: 'json' },
|
|
67
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
68
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
65
69
|
],
|
|
66
70
|
});
|
|
67
71
|
app.save(sessions);
|
|
68
72
|
}, (app) => {
|
|
69
|
-
app.
|
|
70
|
-
app.
|
|
71
|
-
app.
|
|
73
|
+
const c1 = app.findCollectionByNameOrId('sessions'); if (c1) app.delete(c1);
|
|
74
|
+
const c2 = app.findCollectionByNameOrId('decisions'); if (c2) app.delete(c2);
|
|
75
|
+
const c3 = app.findCollectionByNameOrId('knowledge'); if (c3) app.delete(c3);
|
|
72
76
|
});
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 002: Goals and tasks
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
6
7
|
|
|
7
8
|
migrate((app) => {
|
|
8
|
-
// goals collection
|
|
9
9
|
const goals = new Collection({
|
|
10
10
|
name: 'goals',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'title', type: 'text', required: true,
|
|
14
|
-
{ name: 'description', type: 'editor',
|
|
15
|
-
{ name: 'timeframe', type: 'select', required: true,
|
|
16
|
-
{ name: 'status', type: 'select', required: true,
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
14
|
+
{ name: 'description', type: 'editor', maxSize: 5000 },
|
|
15
|
+
{ name: 'timeframe', type: 'select', required: true, maxSelect: 1, values: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'] },
|
|
16
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['active', 'completed', 'paused', 'abandoned'] },
|
|
17
17
|
{ name: 'key_results', type: 'json' },
|
|
18
18
|
{ name: 'tags', type: 'json' },
|
|
19
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
20
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
19
21
|
],
|
|
20
22
|
indexes: [
|
|
21
23
|
'CREATE INDEX idx_goals_status ON goals (status)',
|
|
@@ -24,18 +26,19 @@ migrate((app) => {
|
|
|
24
26
|
});
|
|
25
27
|
app.save(goals);
|
|
26
28
|
|
|
27
|
-
// tasks collection
|
|
28
29
|
const tasks = new Collection({
|
|
29
30
|
name: 'tasks',
|
|
30
31
|
type: 'base',
|
|
31
|
-
|
|
32
|
-
{ name: 'title', type: 'text', required: true,
|
|
33
|
-
{ name: 'description', type: 'editor',
|
|
34
|
-
{ name: 'status', type: 'select', required: true,
|
|
35
|
-
{ name: 'priority', type: 'select', required: true,
|
|
32
|
+
fields: [
|
|
33
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
34
|
+
{ name: 'description', type: 'editor', maxSize: 5000 },
|
|
35
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['todo', 'in_progress', 'done', 'cancelled'] },
|
|
36
|
+
{ name: 'priority', type: 'select', required: true, maxSelect: 1, values: ['low', 'medium', 'high', 'urgent'] },
|
|
36
37
|
{ name: 'due_date', type: 'date' },
|
|
37
38
|
{ name: 'tags', type: 'json' },
|
|
38
|
-
{ name: 'goal_id', type: 'text',
|
|
39
|
+
{ name: 'goal_id', type: 'text', max: 100 },
|
|
40
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
41
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
39
42
|
],
|
|
40
43
|
indexes: [
|
|
41
44
|
'CREATE INDEX idx_tasks_status ON tasks (status)',
|
|
@@ -44,6 +47,6 @@ migrate((app) => {
|
|
|
44
47
|
});
|
|
45
48
|
app.save(tasks);
|
|
46
49
|
}, (app) => {
|
|
47
|
-
app.
|
|
48
|
-
app.
|
|
50
|
+
const t = app.findCollectionByNameOrId('tasks'); if (t) app.delete(t);
|
|
51
|
+
const g = app.findCollectionByNameOrId('goals'); if (g) app.delete(g);
|
|
49
52
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 003: Contacts
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
@@ -8,16 +9,18 @@ migrate((app) => {
|
|
|
8
9
|
const contacts = new Collection({
|
|
9
10
|
name: 'contacts',
|
|
10
11
|
type: 'base',
|
|
11
|
-
|
|
12
|
-
{ name: 'name', type: 'text', required: true,
|
|
13
|
-
{ name: 'company', type: 'text',
|
|
14
|
-
{ name: 'role', type: 'text',
|
|
15
|
-
{ name: 'email', type: 'email'
|
|
16
|
-
{ name: 'phone', type: 'text',
|
|
17
|
-
{ name: 'relationship', type: 'select',
|
|
18
|
-
{ name: 'notes', type: 'editor',
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'name', type: 'text', required: true, max: 200 },
|
|
14
|
+
{ name: 'company', type: 'text', max: 200 },
|
|
15
|
+
{ name: 'role', type: 'text', max: 200 },
|
|
16
|
+
{ name: 'email', type: 'email' },
|
|
17
|
+
{ name: 'phone', type: 'text', max: 50 },
|
|
18
|
+
{ name: 'relationship', type: 'select', maxSelect: 1, values: ['colleague', 'client', 'prospect', 'partner', 'other'] },
|
|
19
|
+
{ name: 'notes', type: 'editor', maxSize: 5000 },
|
|
19
20
|
{ name: 'tags', type: 'json' },
|
|
20
21
|
{ name: 'last_contact_date', type: 'date' },
|
|
22
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
23
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
21
24
|
],
|
|
22
25
|
indexes: [
|
|
23
26
|
'CREATE INDEX idx_contacts_name ON contacts (name)',
|
|
@@ -25,5 +28,5 @@ migrate((app) => {
|
|
|
25
28
|
});
|
|
26
29
|
app.save(contacts);
|
|
27
30
|
}, (app) => {
|
|
28
|
-
app.
|
|
31
|
+
const c = app.findCollectionByNameOrId('contacts'); if (c) app.delete(c);
|
|
29
32
|
});
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 004: Entity aliases + settings
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
6
7
|
|
|
7
8
|
migrate((app) => {
|
|
8
|
-
// entity_aliases collection
|
|
9
9
|
const aliases = new Collection({
|
|
10
10
|
name: 'entity_aliases',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'canonical', type: 'text', required: true,
|
|
14
|
-
{ name: 'alias', type: 'text', required: true,
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'canonical', type: 'text', required: true, max: 100 },
|
|
14
|
+
{ name: 'alias', type: 'text', required: true, max: 200 },
|
|
15
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
16
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
15
17
|
],
|
|
16
18
|
indexes: [
|
|
17
19
|
'CREATE UNIQUE INDEX idx_entity_aliases_unique ON entity_aliases (canonical, alias)',
|
|
@@ -21,13 +23,14 @@ migrate((app) => {
|
|
|
21
23
|
});
|
|
22
24
|
app.save(aliases);
|
|
23
25
|
|
|
24
|
-
// settings collection
|
|
25
26
|
const settings = new Collection({
|
|
26
27
|
name: 'settings',
|
|
27
28
|
type: 'base',
|
|
28
|
-
|
|
29
|
-
{ name: 'key', type: 'text', required: true,
|
|
29
|
+
fields: [
|
|
30
|
+
{ name: 'key', type: 'text', required: true, max: 100 },
|
|
30
31
|
{ name: 'value', type: 'editor' },
|
|
32
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
33
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
31
34
|
],
|
|
32
35
|
indexes: [
|
|
33
36
|
'CREATE UNIQUE INDEX idx_settings_key ON settings (key)',
|
|
@@ -35,6 +38,6 @@ migrate((app) => {
|
|
|
35
38
|
});
|
|
36
39
|
app.save(settings);
|
|
37
40
|
}, (app) => {
|
|
38
|
-
app.
|
|
39
|
-
app.
|
|
41
|
+
const s = app.findCollectionByNameOrId('settings'); if (s) app.delete(s);
|
|
42
|
+
const a = app.findCollectionByNameOrId('entity_aliases'); if (a) app.delete(a);
|
|
40
43
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 005: Prospects (CRM)
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
@@ -8,20 +9,22 @@ migrate((app) => {
|
|
|
8
9
|
const prospects = new Collection({
|
|
9
10
|
name: 'prospects',
|
|
10
11
|
type: 'base',
|
|
11
|
-
|
|
12
|
-
{ name: 'name', type: 'text', required: true,
|
|
13
|
-
{ name: 'email', type: 'email'
|
|
14
|
-
{ name: 'company', type: 'text',
|
|
15
|
-
{ name: 'role', type: 'text',
|
|
16
|
-
{ name: 'stage', type: 'select', required: true,
|
|
17
|
-
{ name: 'source', type: 'text',
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'name', type: 'text', required: true, max: 200 },
|
|
14
|
+
{ name: 'email', type: 'email' },
|
|
15
|
+
{ name: 'company', type: 'text', max: 200 },
|
|
16
|
+
{ name: 'role', type: 'text', max: 200 },
|
|
17
|
+
{ name: 'stage', type: 'select', required: true, maxSelect: 1, values: ['new', 'contacted', 'responded', 'interested', 'ready_to_buy', 'proposal_sent', 'negotiating', 'closed_won', 'closed_lost', 'nurturing'] },
|
|
18
|
+
{ name: 'source', type: 'text', max: 200 },
|
|
18
19
|
{ name: 'estimated_value', type: 'number' },
|
|
19
|
-
{ name: 'next_action_type', type: 'text',
|
|
20
|
+
{ name: 'next_action_type', type: 'text', max: 100 },
|
|
20
21
|
{ name: 'next_followup_date', type: 'date' },
|
|
21
22
|
{ name: 'last_contact_date', type: 'date' },
|
|
22
|
-
{ name: 'notes', type: 'editor',
|
|
23
|
+
{ name: 'notes', type: 'editor', maxSize: 10000 },
|
|
23
24
|
{ name: 'tags', type: 'json' },
|
|
24
|
-
{ name: 'linkedin_url', type: 'url'
|
|
25
|
+
{ name: 'linkedin_url', type: 'url' },
|
|
26
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
27
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
25
28
|
],
|
|
26
29
|
indexes: [
|
|
27
30
|
'CREATE INDEX idx_prospects_stage ON prospects (stage)',
|
|
@@ -29,5 +32,5 @@ migrate((app) => {
|
|
|
29
32
|
});
|
|
30
33
|
app.save(prospects);
|
|
31
34
|
}, (app) => {
|
|
32
|
-
app.
|
|
35
|
+
const p = app.findCollectionByNameOrId('prospects'); if (p) app.delete(p);
|
|
33
36
|
});
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 006: Business collections (blog, email, content)
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
6
7
|
|
|
7
8
|
migrate((app) => {
|
|
8
|
-
// blog_posts collection
|
|
9
9
|
const blogPosts = new Collection({
|
|
10
10
|
name: 'blog_posts',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'title', type: 'text', required: true,
|
|
14
|
-
{ name: 'slug', type: 'text', required: true,
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
14
|
+
{ name: 'slug', type: 'text', required: true, max: 200 },
|
|
15
15
|
{ name: 'content', type: 'editor', required: true },
|
|
16
|
-
{ name: 'excerpt', type: 'text',
|
|
17
|
-
{ name: 'status', type: 'select', required: true,
|
|
16
|
+
{ name: 'excerpt', type: 'text', max: 500 },
|
|
17
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['draft', 'published', 'archived'] },
|
|
18
18
|
{ name: 'published_at', type: 'date' },
|
|
19
19
|
{ name: 'tags', type: 'json' },
|
|
20
|
-
{ name: 'seo_title', type: 'text',
|
|
21
|
-
{ name: 'seo_description', type: 'text',
|
|
22
|
-
{ name: 'og_image_url', type: 'url'
|
|
20
|
+
{ name: 'seo_title', type: 'text', max: 200 },
|
|
21
|
+
{ name: 'seo_description', type: 'text', max: 300 },
|
|
22
|
+
{ name: 'og_image_url', type: 'url' },
|
|
23
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
24
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
23
25
|
],
|
|
24
26
|
indexes: [
|
|
25
27
|
'CREATE UNIQUE INDEX idx_blog_posts_slug ON blog_posts (slug)',
|
|
@@ -28,24 +30,25 @@ migrate((app) => {
|
|
|
28
30
|
});
|
|
29
31
|
app.save(blogPosts);
|
|
30
32
|
|
|
31
|
-
// email_queue collection
|
|
32
33
|
const emailQueue = new Collection({
|
|
33
34
|
name: 'email_queue',
|
|
34
35
|
type: 'base',
|
|
35
|
-
|
|
36
|
-
{ name: 'to_email', type: 'email', required: true
|
|
37
|
-
{ name: 'to_name', type: 'text',
|
|
38
|
-
{ name: 'subject', type: 'text', required: true,
|
|
36
|
+
fields: [
|
|
37
|
+
{ name: 'to_email', type: 'email', required: true },
|
|
38
|
+
{ name: 'to_name', type: 'text', max: 200 },
|
|
39
|
+
{ name: 'subject', type: 'text', required: true, max: 500 },
|
|
39
40
|
{ name: 'body_html', type: 'editor', required: true },
|
|
40
41
|
{ name: 'body_text', type: 'editor' },
|
|
41
|
-
{ name: 'status', type: 'select', required: true,
|
|
42
|
-
{ name: 'sequence_id', type: 'text',
|
|
42
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['queued', 'sent', 'failed', 'bounced'] },
|
|
43
|
+
{ name: 'sequence_id', type: 'text', max: 100 },
|
|
43
44
|
{ name: 'sequence_step', type: 'number' },
|
|
44
|
-
{ name: 'prospect_id', type: 'text',
|
|
45
|
+
{ name: 'prospect_id', type: 'text', max: 100 },
|
|
45
46
|
{ name: 'scheduled_at', type: 'date' },
|
|
46
47
|
{ name: 'sent_at', type: 'date' },
|
|
47
48
|
{ name: 'error', type: 'editor' },
|
|
48
|
-
{ name: 'resend_id', type: 'text',
|
|
49
|
+
{ name: 'resend_id', type: 'text', max: 200 },
|
|
50
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
51
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
49
52
|
],
|
|
50
53
|
indexes: [
|
|
51
54
|
'CREATE INDEX idx_email_queue_status ON email_queue (status)',
|
|
@@ -53,19 +56,20 @@ migrate((app) => {
|
|
|
53
56
|
});
|
|
54
57
|
app.save(emailQueue);
|
|
55
58
|
|
|
56
|
-
// content_calendar collection
|
|
57
59
|
const contentCalendar = new Collection({
|
|
58
60
|
name: 'content_calendar',
|
|
59
61
|
type: 'base',
|
|
60
|
-
|
|
61
|
-
{ name: 'title', type: 'text', required: true,
|
|
62
|
+
fields: [
|
|
63
|
+
{ name: 'title', type: 'text', required: true, max: 500 },
|
|
62
64
|
{ name: 'content', type: 'editor' },
|
|
63
|
-
{ name: 'platform', type: 'select', required: true,
|
|
64
|
-
{ name: 'pillar', type: 'text',
|
|
65
|
-
{ name: 'status', type: 'select', required: true,
|
|
65
|
+
{ name: 'platform', type: 'select', required: true, maxSelect: 1, values: ['linkedin', 'newsletter', 'blog', 'twitter', 'other'] },
|
|
66
|
+
{ name: 'pillar', type: 'text', max: 100 },
|
|
67
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['idea', 'drafting', 'ready', 'published'] },
|
|
66
68
|
{ name: 'scheduled_date', type: 'date' },
|
|
67
|
-
{ name: 'published_url', type: 'url'
|
|
68
|
-
{ name: 'persona', type: 'text',
|
|
69
|
+
{ name: 'published_url', type: 'url' },
|
|
70
|
+
{ name: 'persona', type: 'text', max: 100 },
|
|
71
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
72
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
69
73
|
],
|
|
70
74
|
indexes: [
|
|
71
75
|
'CREATE INDEX idx_content_calendar_platform ON content_calendar (platform)',
|
|
@@ -74,7 +78,7 @@ migrate((app) => {
|
|
|
74
78
|
});
|
|
75
79
|
app.save(contentCalendar);
|
|
76
80
|
}, (app) => {
|
|
77
|
-
app.
|
|
78
|
-
app.
|
|
79
|
-
app.
|
|
81
|
+
const cc = app.findCollectionByNameOrId('content_calendar'); if (cc) app.delete(cc);
|
|
82
|
+
const eq = app.findCollectionByNameOrId('email_queue'); if (eq) app.delete(eq);
|
|
83
|
+
const bp = app.findCollectionByNameOrId('blog_posts'); if (bp) app.delete(bp);
|
|
80
84
|
});
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PocketBase Migration 007: Newsletter subscribers and affiliates
|
|
3
|
+
* PocketBase v0.23+ field format.
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
/// <reference path="../pb_data/types.d.ts" />
|
|
6
7
|
|
|
7
8
|
migrate((app) => {
|
|
8
|
-
// newsletter_subscribers collection
|
|
9
9
|
const subscribers = new Collection({
|
|
10
10
|
name: 'newsletter_subscribers',
|
|
11
11
|
type: 'base',
|
|
12
|
-
|
|
13
|
-
{ name: 'email', type: 'email', required: true
|
|
14
|
-
{ name: 'name', type: 'text',
|
|
15
|
-
{ name: 'status', type: 'select', required: true,
|
|
16
|
-
{ name: 'source', type: 'text',
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'email', type: 'email', required: true },
|
|
14
|
+
{ name: 'name', type: 'text', max: 200 },
|
|
15
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['active', 'unsubscribed', 'bounced'] },
|
|
16
|
+
{ name: 'source', type: 'text', max: 200 },
|
|
17
17
|
{ name: 'tags', type: 'json' },
|
|
18
18
|
{ name: 'subscribed_at', type: 'date' },
|
|
19
19
|
{ name: 'unsubscribed_at', type: 'date' },
|
|
20
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
21
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
20
22
|
],
|
|
21
23
|
indexes: [
|
|
22
24
|
'CREATE UNIQUE INDEX idx_newsletter_subscribers_email ON newsletter_subscribers (email)',
|
|
@@ -24,19 +26,20 @@ migrate((app) => {
|
|
|
24
26
|
});
|
|
25
27
|
app.save(subscribers);
|
|
26
28
|
|
|
27
|
-
// affiliates collection
|
|
28
29
|
const affiliates = new Collection({
|
|
29
30
|
name: 'affiliates',
|
|
30
31
|
type: 'base',
|
|
31
|
-
|
|
32
|
-
{ name: 'name', type: 'text', required: true,
|
|
33
|
-
{ name: 'email', type: 'email', required: true
|
|
34
|
-
{ name: 'code', type: 'text', required: true,
|
|
35
|
-
{ name: 'commission_rate', type: 'number',
|
|
36
|
-
{ name: 'status', type: 'select', required: true,
|
|
32
|
+
fields: [
|
|
33
|
+
{ name: 'name', type: 'text', required: true, max: 200 },
|
|
34
|
+
{ name: 'email', type: 'email', required: true },
|
|
35
|
+
{ name: 'code', type: 'text', required: true, max: 100 },
|
|
36
|
+
{ name: 'commission_rate', type: 'number', min: 0, max: 1 },
|
|
37
|
+
{ name: 'status', type: 'select', required: true, maxSelect: 1, values: ['pending', 'active', 'paused', 'terminated'] },
|
|
37
38
|
{ name: 'total_earned_cents', type: 'number' },
|
|
38
39
|
{ name: 'total_paid_cents', type: 'number' },
|
|
39
|
-
{ name: 'stripe_account_id', type: 'text',
|
|
40
|
+
{ name: 'stripe_account_id', type: 'text', max: 200 },
|
|
41
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
42
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
40
43
|
],
|
|
41
44
|
indexes: [
|
|
42
45
|
'CREATE UNIQUE INDEX idx_affiliates_email ON affiliates (email)',
|
|
@@ -45,6 +48,6 @@ migrate((app) => {
|
|
|
45
48
|
});
|
|
46
49
|
app.save(affiliates);
|
|
47
50
|
}, (app) => {
|
|
48
|
-
app.
|
|
49
|
-
app.
|
|
51
|
+
const a = app.findCollectionByNameOrId('affiliates'); if (a) app.delete(a);
|
|
52
|
+
const s = app.findCollectionByNameOrId('newsletter_subscribers'); if (s) app.delete(s);
|
|
50
53
|
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PocketBase Migration 008: knowledge_links collection
|
|
3
|
+
*
|
|
4
|
+
* Typed relationships between MemoryOS entities.
|
|
5
|
+
* PocketBase does not support pgvector — link suggestions use keyword-based
|
|
6
|
+
* text search fallback.
|
|
7
|
+
*
|
|
8
|
+
* PocketBase v0.23+ field format + native migrate() form
|
|
9
|
+
* (was previously module.exports — goja runtime panics on that).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/// <reference path="../pb_data/types.d.ts" />
|
|
13
|
+
|
|
14
|
+
migrate((app) => {
|
|
15
|
+
const links = new Collection({
|
|
16
|
+
name: 'knowledge_links',
|
|
17
|
+
type: 'base',
|
|
18
|
+
fields: [
|
|
19
|
+
{ name: 'owner_id', type: 'text', max: 100 },
|
|
20
|
+
{ name: 'source_type', type: 'text', required: true, max: 50 },
|
|
21
|
+
{ name: 'source_id', type: 'text', required: true, max: 36 },
|
|
22
|
+
{ name: 'target_type', type: 'text', required: true, max: 50 },
|
|
23
|
+
{ name: 'target_id', type: 'text', required: true, max: 36 },
|
|
24
|
+
{ name: 'relation_type', type: 'text', required: true, max: 50 },
|
|
25
|
+
{ name: 'confidence', type: 'number', min: 0, max: 1 },
|
|
26
|
+
{ name: 'notes', type: 'text', max: 500 },
|
|
27
|
+
{ name: 'auto_suggested', type: 'bool' },
|
|
28
|
+
{ name: 'created', type: 'autodate', onCreate: true },
|
|
29
|
+
{ name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
|
|
30
|
+
],
|
|
31
|
+
indexes: [
|
|
32
|
+
'CREATE INDEX idx_kl_source ON knowledge_links (owner_id, source_type, source_id)',
|
|
33
|
+
'CREATE INDEX idx_kl_target ON knowledge_links (owner_id, target_type, target_id)',
|
|
34
|
+
'CREATE UNIQUE INDEX idx_kl_unique ON knowledge_links (owner_id, source_type, source_id, target_type, target_id, relation_type)',
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
app.save(links);
|
|
38
|
+
}, (app) => {
|
|
39
|
+
const links = app.findCollectionByNameOrId('knowledge_links');
|
|
40
|
+
if (links) app.delete(links);
|
|
41
|
+
});
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
-- 5. Add decisions-specific columns: rationale, outcome_rating, session_id
|
|
9
9
|
-- 6. Make decisions.options_considered nullable (production data shows this)
|
|
10
10
|
-- 7. Make goals.timeframe nullable (not always known at creation time)
|
|
11
|
+
--
|
|
12
|
+
-- Each `text[] -> jsonb` conversion must DROP DEFAULT first, change TYPE, then
|
|
13
|
+
-- SET a jsonb DEFAULT. Postgres cannot auto-cast the text[] default '{}' to jsonb.
|
|
11
14
|
|
|
12
15
|
-- === KNOWLEDGE ===
|
|
13
|
-
|
|
16
|
+
ALTER TABLE knowledge ALTER COLUMN tags DROP DEFAULT;
|
|
14
17
|
ALTER TABLE knowledge ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
|
|
15
18
|
ALTER TABLE knowledge ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
|
|
16
|
-
-- Add missing columns
|
|
17
19
|
ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
18
20
|
ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
|
|
19
21
|
ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS source_file text;
|
|
@@ -21,14 +23,13 @@ ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS decay_score numeric;
|
|
|
21
23
|
ALTER TABLE knowledge ADD COLUMN IF NOT EXISTS triggers jsonb;
|
|
22
24
|
|
|
23
25
|
-- === DECISIONS ===
|
|
24
|
-
|
|
26
|
+
ALTER TABLE decisions ALTER COLUMN options_considered DROP DEFAULT;
|
|
25
27
|
ALTER TABLE decisions ALTER COLUMN options_considered TYPE jsonb USING to_jsonb(options_considered);
|
|
26
28
|
ALTER TABLE decisions ALTER COLUMN options_considered DROP NOT NULL;
|
|
27
29
|
ALTER TABLE decisions ALTER COLUMN options_considered SET DEFAULT '[]'::jsonb;
|
|
28
|
-
|
|
30
|
+
ALTER TABLE decisions ALTER COLUMN tags DROP DEFAULT;
|
|
29
31
|
ALTER TABLE decisions ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
|
|
30
32
|
ALTER TABLE decisions ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
|
|
31
|
-
-- Add missing columns
|
|
32
33
|
ALTER TABLE decisions ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
33
34
|
ALTER TABLE decisions ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
|
|
34
35
|
ALTER TABLE decisions ADD COLUMN IF NOT EXISTS rationale text;
|
|
@@ -36,37 +37,33 @@ ALTER TABLE decisions ADD COLUMN IF NOT EXISTS outcome_rating text;
|
|
|
36
37
|
ALTER TABLE decisions ADD COLUMN IF NOT EXISTS session_id uuid;
|
|
37
38
|
|
|
38
39
|
-- === SESSIONS ===
|
|
39
|
-
|
|
40
|
+
ALTER TABLE sessions ALTER COLUMN skills_used DROP DEFAULT;
|
|
40
41
|
ALTER TABLE sessions ALTER COLUMN skills_used TYPE jsonb USING to_jsonb(skills_used);
|
|
41
42
|
ALTER TABLE sessions ALTER COLUMN skills_used SET DEFAULT '[]'::jsonb;
|
|
43
|
+
ALTER TABLE sessions ALTER COLUMN files_changed DROP DEFAULT;
|
|
42
44
|
ALTER TABLE sessions ALTER COLUMN files_changed TYPE jsonb USING to_jsonb(files_changed);
|
|
43
45
|
ALTER TABLE sessions ALTER COLUMN files_changed SET DEFAULT '[]'::jsonb;
|
|
44
|
-
-- Add missing columns
|
|
45
46
|
ALTER TABLE sessions ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
46
47
|
|
|
47
48
|
-- === GOALS ===
|
|
48
|
-
|
|
49
|
+
ALTER TABLE goals ALTER COLUMN tags DROP DEFAULT;
|
|
49
50
|
ALTER TABLE goals ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
|
|
50
51
|
ALTER TABLE goals ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
|
|
51
|
-
-- Make timeframe nullable (not always known)
|
|
52
52
|
ALTER TABLE goals ALTER COLUMN timeframe DROP NOT NULL;
|
|
53
|
-
-- Add missing columns
|
|
54
53
|
ALTER TABLE goals ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
55
54
|
ALTER TABLE goals ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
|
|
56
55
|
|
|
57
56
|
-- === TASKS ===
|
|
58
|
-
|
|
57
|
+
ALTER TABLE tasks ALTER COLUMN tags DROP DEFAULT;
|
|
59
58
|
ALTER TABLE tasks ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
|
|
60
59
|
ALTER TABLE tasks ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
|
|
61
|
-
-- Add missing columns
|
|
62
60
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
63
61
|
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
|
|
64
62
|
|
|
65
63
|
-- === CONTACTS ===
|
|
66
|
-
|
|
64
|
+
ALTER TABLE contacts ALTER COLUMN tags DROP DEFAULT;
|
|
67
65
|
ALTER TABLE contacts ALTER COLUMN tags TYPE jsonb USING to_jsonb(tags);
|
|
68
66
|
ALTER TABLE contacts ALTER COLUMN tags SET DEFAULT '[]'::jsonb;
|
|
69
|
-
-- Add missing columns
|
|
70
67
|
ALTER TABLE contacts ADD COLUMN IF NOT EXISTS owner_id text NOT NULL DEFAULT 'default';
|
|
71
68
|
ALTER TABLE contacts ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb;
|
|
72
69
|
ALTER TABLE contacts ADD COLUMN IF NOT EXISTS last_interaction_at timestamptz;
|