@rubytech/create-maxy-code 0.1.44 → 0.1.47

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 (155) hide show
  1. package/dist/index.js +8 -16
  2. package/package.json +1 -1
  3. package/payload/platform/lib/admins-write/dist/index.d.ts +0 -1
  4. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/admins-write/dist/index.js +2 -5
  6. package/payload/platform/lib/admins-write/dist/index.js.map +1 -1
  7. package/payload/platform/lib/admins-write/src/index.ts +2 -8
  8. package/payload/platform/lib/graph-mcp/dist/index.js +1 -4
  9. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  10. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +7 -6
  11. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -1
  12. package/payload/platform/lib/graph-mcp/dist/schema-cache.js +7 -6
  13. package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -1
  14. package/payload/platform/lib/graph-mcp/src/index.ts +1 -4
  15. package/payload/platform/lib/graph-mcp/src/schema-cache.ts +7 -6
  16. package/payload/platform/lib/graph-search/dist/index.d.ts +5 -5
  17. package/payload/platform/lib/graph-search/dist/index.js +4 -4
  18. package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +6 -7
  19. package/payload/platform/lib/graph-search/src/index.ts +5 -5
  20. package/payload/platform/lib/graph-trash/dist/index.d.ts +8 -12
  21. package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -1
  22. package/payload/platform/lib/graph-trash/dist/index.js +8 -12
  23. package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
  24. package/payload/platform/lib/graph-trash/src/index.ts +8 -12
  25. package/payload/platform/lib/graph-write/dist/index.d.ts +8 -9
  26. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  27. package/payload/platform/lib/graph-write/dist/index.js +9 -10
  28. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  29. package/payload/platform/lib/graph-write/src/index.ts +9 -10
  30. package/payload/platform/lib/mcp-eager/dist/index.d.ts +4 -4
  31. package/payload/platform/lib/mcp-eager/dist/index.js +4 -4
  32. package/payload/platform/lib/mcp-eager/src/index.ts +4 -4
  33. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  34. package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +138 -18
  35. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +31 -67
  36. package/payload/platform/plugins/cloudflare/scripts/__tests__/tunnel-ingress.test.ts +2 -2
  37. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +7 -9
  38. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +10 -12
  39. package/payload/platform/plugins/cloudflare/scripts/tunnel-ingress.ts +3 -3
  40. package/payload/platform/plugins/docs/references/deployment.md +0 -14
  41. package/payload/platform/plugins/docs/references/platform.md +6 -4
  42. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  43. package/payload/platform/plugins/docs/references/troubleshooting.md +5 -5
  44. package/payload/platform/plugins/loop/PLUGIN.md +67 -12
  45. package/payload/platform/plugins/loop/mcp/dist/index.js +42 -16
  46. package/payload/platform/plugins/loop/mcp/dist/index.js.map +1 -1
  47. package/payload/platform/plugins/loop/mcp/dist/tools/auto-responder.d.ts +12 -0
  48. package/payload/platform/plugins/loop/mcp/dist/tools/auto-responder.d.ts.map +1 -0
  49. package/payload/platform/plugins/loop/mcp/dist/tools/auto-responder.js +22 -0
  50. package/payload/platform/plugins/loop/mcp/dist/tools/auto-responder.js.map +1 -0
  51. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts +0 -7
  52. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts.map +1 -1
  53. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.js +4 -38
  54. package/payload/platform/plugins/loop/mcp/dist/tools/marketing-enquiry.js.map +1 -1
  55. package/payload/platform/plugins/loop/mcp/dist/tools/property-intent.d.ts +33 -0
  56. package/payload/platform/plugins/loop/mcp/dist/tools/property-intent.d.ts.map +1 -0
  57. package/payload/platform/plugins/loop/mcp/dist/tools/property-intent.js +19 -0
  58. package/payload/platform/plugins/loop/mcp/dist/tools/property-intent.js.map +1 -0
  59. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.d.ts +0 -1
  60. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -1
  61. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.js +1 -7
  62. package/payload/platform/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -1
  63. package/payload/platform/plugins/loop/mcp/dist/tools/property-sold.d.ts +9 -0
  64. package/payload/platform/plugins/loop/mcp/dist/tools/property-sold.d.ts.map +1 -0
  65. package/payload/platform/plugins/loop/mcp/dist/tools/property-sold.js +19 -0
  66. package/payload/platform/plugins/loop/mcp/dist/tools/property-sold.js.map +1 -0
  67. package/payload/platform/plugins/loop/mcp/src/__tests__/new-tool-shape.test.ts +99 -0
  68. package/payload/platform/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +12 -11
  69. package/payload/platform/plugins/loop/mcp/src/index.ts +68 -17
  70. package/payload/platform/plugins/loop/mcp/src/tools/auto-responder.ts +80 -0
  71. package/payload/platform/plugins/loop/mcp/src/tools/marketing-enquiry.ts +14 -100
  72. package/payload/platform/plugins/loop/mcp/src/tools/property-intent.ts +84 -0
  73. package/payload/platform/plugins/loop/mcp/src/tools/property-listed.ts +1 -8
  74. package/payload/platform/plugins/loop/mcp/src/tools/property-sold.ts +67 -0
  75. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +4 -3
  76. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +7 -12
  79. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-not-applicable.test.js +2 -2
  81. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +4 -4
  82. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  83. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.d.ts.map +1 -1
  84. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js +8 -9
  85. package/payload/platform/plugins/tasks/mcp/dist/tools/session-name.js.map +1 -1
  86. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.d.ts +2 -2
  87. package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-heal.js +2 -2
  88. package/payload/platform/scripts/admin-persist-audit.ts +4 -10
  89. package/payload/platform/scripts/conversation-id-allowlist.txt +0 -1
  90. package/payload/platform/scripts/lib/resolve-account-dir.sh +0 -2
  91. package/payload/platform/scripts/resume-tunnel.sh +2 -9
  92. package/payload/platform/scripts/seed-neo4j.sh +26 -104
  93. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/http-server.js +67 -5
  95. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  97. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +13 -0
  98. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  99. package/payload/platform/templates/agents/admin/IDENTITY.md +12 -12
  100. package/payload/platform/templates/agents/admin/SOUL.md +23 -1
  101. package/payload/premium-plugins/real-agent/agents/buyer-enquiry/SOUL.md +10 -0
  102. package/payload/premium-plugins/real-agent/agents/compliance.md +1 -1
  103. package/payload/premium-plugins/real-agent/agents/negotiator.md +1 -1
  104. package/payload/premium-plugins/real-agent/agents/valuer.md +1 -1
  105. package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +67 -12
  106. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js +42 -16
  107. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/index.js.map +1 -1
  108. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.d.ts +12 -0
  109. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.d.ts.map +1 -0
  110. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.js +22 -0
  111. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/auto-responder.js.map +1 -0
  112. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts +0 -7
  113. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-enquiry.d.ts.map +1 -1
  114. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-enquiry.js +4 -38
  115. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/marketing-enquiry.js.map +1 -1
  116. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-intent.d.ts +33 -0
  117. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-intent.d.ts.map +1 -0
  118. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-intent.js +19 -0
  119. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-intent.js.map +1 -0
  120. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.d.ts +0 -1
  121. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.d.ts.map +1 -1
  122. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.js +1 -7
  123. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-listed.js.map +1 -1
  124. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.d.ts +9 -0
  125. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.d.ts.map +1 -0
  126. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.js +19 -0
  127. package/payload/premium-plugins/real-agent/plugins/loop/mcp/dist/tools/property-sold.js.map +1 -0
  128. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/__tests__/new-tool-shape.test.ts +99 -0
  129. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/__tests__/swagger-write-coverage.test.ts +12 -11
  130. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/index.ts +68 -17
  131. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/auto-responder.ts +80 -0
  132. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/marketing-enquiry.ts +14 -100
  133. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-intent.ts +84 -0
  134. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-listed.ts +1 -8
  135. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-sold.ts +67 -0
  136. package/payload/server/public/assets/admin-SyUHjvNs.js +216 -0
  137. package/payload/server/public/assets/{data-CU9uH2JJ.js → data-DwQ7ZEyy.js} +1 -1
  138. package/payload/server/public/assets/{graph-BI7yVwmT.js → graph-CwZkD6GX.js} +1 -1
  139. package/payload/server/public/assets/graph-labels-CVkmOHhw.js +1 -0
  140. package/payload/server/public/assets/page-CQ7ELqGs.js +1 -0
  141. package/payload/server/public/assets/page-SOCqJGlV.js +50 -0
  142. package/payload/server/public/data.html +3 -3
  143. package/payload/server/public/graph.html +3 -3
  144. package/payload/server/public/index.html +4 -4
  145. package/payload/server/server.js +9 -46
  146. package/payload/platform/plugins/docs/references/migration-guide.md +0 -90
  147. package/payload/platform/plugins/loop/mcp/src/tools/property-request.ts +0 -42
  148. package/payload/platform/scripts/component-knowledgedoc-backfill.ts +0 -214
  149. package/payload/platform/scripts/migrate-import.sh +0 -437
  150. package/payload/platform/scripts/taskmaster-export.sh +0 -388
  151. package/payload/premium-plugins/real-agent/plugins/loop/mcp/src/tools/property-request.ts +0 -42
  152. package/payload/server/public/assets/admin-GOZURixP.js +0 -216
  153. package/payload/server/public/assets/graph-labels-bI0uSt_-.js +0 -1
  154. package/payload/server/public/assets/page-BaRMnC96.js +0 -1
  155. package/payload/server/public/assets/page-DKdUMAhI.js +0 -50
@@ -1,437 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # migrate-import.sh — Import a migration bundle into a Maxy install
4
- #
5
- # Reads a migration bundle (produced by taskmaster-export.sh) and
6
- # populates a Maxy/Real Agent installation: Neo4j contacts, memory
7
- # files, media, conversations, and access PINs.
8
- #
9
- # Idempotent: running twice does not create duplicate contacts
10
- # (MERGE on telephone) or corrupt existing data (file overwrites
11
- # are safe for migration content).
12
- #
13
- # Usage:
14
- # bash migrate-import.sh <bundle-dir> <install-dir> [--account <uuid>]
15
- #
16
- # Arguments:
17
- # bundle-dir — Migration bundle directory (from taskmaster-export.sh)
18
- # install-dir — Maxy installation root (contains data/accounts/ and platform/)
19
- # --account — (Optional) Target account UUID. Required if multiple accounts exist.
20
- #
21
- # Example:
22
- # bash migrate-import.sh ./bundle ~/maxy
23
- # bash migrate-import.sh ./bundle ~/maxy --account a1b2c3d4-...
24
- # ============================================================
25
-
26
- set -euo pipefail
27
-
28
- # ------------------------------------------------------------------
29
- # Arguments
30
- # ------------------------------------------------------------------
31
- if [ $# -lt 2 ]; then
32
- echo "[import] ERROR: Usage: migrate-import.sh <bundle-dir> <install-dir> [--account <uuid>]"
33
- exit 2
34
- fi
35
-
36
- BUNDLE_DIR="$1"
37
- INSTALL_DIR="$2"
38
- ACCOUNT_FLAG=""
39
-
40
- shift 2
41
- while [ $# -gt 0 ]; do
42
- case "$1" in
43
- --account)
44
- ACCOUNT_FLAG="$2"
45
- shift 2
46
- ;;
47
- *)
48
- echo "[import] ERROR: Unknown argument: $1"
49
- exit 2
50
- ;;
51
- esac
52
- done
53
-
54
- # Validate bundle
55
- if [ ! -f "$BUNDLE_DIR/manifest.json" ]; then
56
- echo "[import] ERROR: manifest.json not found in $BUNDLE_DIR"
57
- echo "[import] ERROR: Is this a valid migration bundle?"
58
- exit 1
59
- fi
60
-
61
- # Display manifest
62
- echo "[import] Bundle: $BUNDLE_DIR"
63
- python3 -c "
64
- import json
65
- m = json.load(open('$BUNDLE_DIR/manifest.json'))
66
- print(f'[import] Source: {m.get(\"source\",\"?\")} v{m.get(\"version\",\"?\")}')
67
- print(f'[import] Customer: {m.get(\"customer\",\"?\")}')
68
- print(f'[import] Exported: {m.get(\"exportedAt\",\"?\")}')
69
- print(f'[import] Hostname: {m.get(\"hostname\",\"?\")}')
70
- "
71
-
72
- # ------------------------------------------------------------------
73
- # Discover account
74
- # ------------------------------------------------------------------
75
- ACCOUNTS_DIR="$INSTALL_DIR/data/accounts"
76
-
77
- if [ ! -d "$ACCOUNTS_DIR" ]; then
78
- echo "[import] ERROR: No accounts directory at $ACCOUNTS_DIR"
79
- echo "[import] ERROR: Is $INSTALL_DIR a Maxy installation?"
80
- exit 1
81
- fi
82
-
83
- if [ -n "$ACCOUNT_FLAG" ]; then
84
- # Explicit account UUID provided
85
- ACCOUNT_DIR="$ACCOUNTS_DIR/$ACCOUNT_FLAG"
86
- if [ ! -f "$ACCOUNT_DIR/account.json" ]; then
87
- echo "[import] ERROR: Account $ACCOUNT_FLAG not found at $ACCOUNT_DIR"
88
- exit 1
89
- fi
90
- ACCOUNT_ID="$ACCOUNT_FLAG"
91
- else
92
- # Auto-discover — must be exactly one account
93
- ACCOUNT_FILES=$(find "$ACCOUNTS_DIR" -maxdepth 2 -name "account.json" 2>/dev/null)
94
- ACCOUNT_FILE_COUNT=$(echo "$ACCOUNT_FILES" | grep -c '.' || true)
95
-
96
- if [ "$ACCOUNT_FILE_COUNT" -eq 0 ]; then
97
- echo "[import] ERROR: No accounts found in $ACCOUNTS_DIR"
98
- echo "[import] ERROR: Run the Maxy installer first."
99
- exit 1
100
- elif [ "$ACCOUNT_FILE_COUNT" -gt 1 ]; then
101
- echo "[import] ERROR: Multiple accounts found in $ACCOUNTS_DIR:"
102
- echo "$ACCOUNT_FILES" | while read -r f; do
103
- echo "[import] $(dirname "$f" | xargs basename)"
104
- done
105
- echo "[import] ERROR: Specify which account with --account <uuid>"
106
- exit 1
107
- fi
108
-
109
- ACCOUNT_DIR=$(dirname "$(echo "$ACCOUNT_FILES" | head -1)")
110
- ACCOUNT_ID=$(basename "$ACCOUNT_DIR")
111
- fi
112
-
113
- echo "[import] Target account: $ACCOUNT_ID"
114
- echo "[import] Account dir: $ACCOUNT_DIR"
115
-
116
- # ------------------------------------------------------------------
117
- # Neo4j connection
118
- # ------------------------------------------------------------------
119
- # NEO4J_URI is hard-required. The previous default
120
- # `bolt://localhost:7687` would silently route the import to the wrong Neo4j on
121
- # any brand-dedicated install, masking the actual configuration error.
122
- if [ -z "${NEO4J_URI:-}" ]; then
123
- echo "[import] ERROR: NEO4J_URI required (no default)" >&2
124
- echo "[import] ERROR: Set NEO4J_URI=bolt://localhost:<brand.neo4jPort> before running." >&2
125
- exit 1
126
- fi
127
- NEO4J_USER="${NEO4J_USER:-neo4j}"
128
-
129
- NEO4J_PASSWORD_FILE="$INSTALL_DIR/platform/config/.neo4j-password"
130
- if [ -n "${NEO4J_PASSWORD:-}" ]; then
131
- : # Explicit env var takes precedence
132
- elif [ -f "$NEO4J_PASSWORD_FILE" ]; then
133
- NEO4J_PASSWORD=$(cat "$NEO4J_PASSWORD_FILE")
134
- else
135
- echo "[import] ERROR: Neo4j password not found."
136
- echo "[import] Expected at: $NEO4J_PASSWORD_FILE"
137
- echo "[import] Or set NEO4J_PASSWORD environment variable."
138
- exit 1
139
- fi
140
-
141
- CYPHER_SHELL="cypher-shell"
142
- if ! command -v "$CYPHER_SHELL" &> /dev/null; then
143
- echo "[import] ERROR: cypher-shell not found. Install Neo4j or add cypher-shell to PATH."
144
- exit 1
145
- fi
146
-
147
- # Test connection
148
- if ! "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
149
- "RETURN 1" > /dev/null 2>&1; then
150
- echo "[import] ERROR: Cannot connect to Neo4j at $NEO4J_URI"
151
- echo "[import] ERROR: Is Neo4j running?"
152
- exit 1
153
- fi
154
- echo "[import] Neo4j connection OK ($NEO4J_URI)"
155
-
156
- # ------------------------------------------------------------------
157
- # Helper: escape a string for cypher-shell --param single-quoted value
158
- # Cypher uses backslash escaping: ' → \' and \ → \\
159
- # Uses python3 chr() codes to avoid bash quoting issues.
160
- # ------------------------------------------------------------------
161
- cypher_escape() {
162
- python3 -c "import sys; s=sys.argv[1]; s=s.replace(chr(92),chr(92)*2).replace(chr(39),chr(92)+chr(39)); print(s)" "$1"
163
- }
164
-
165
- # ------------------------------------------------------------------
166
- # 1. Import contacts
167
- # ------------------------------------------------------------------
168
- CONTACTS_DIR="$BUNDLE_DIR/contacts"
169
- CREATED=0
170
- SKIPPED=0
171
- ERRORS=0
172
-
173
- if [ -d "$CONTACTS_DIR" ]; then
174
- for CONTACT_FILE in "$CONTACTS_DIR"/*.json; do
175
- [ -f "$CONTACT_FILE" ] || continue
176
-
177
- # Extract fields from contact JSON
178
- GIVEN_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('givenName',''))" 2>/dev/null)
179
- FAMILY_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('familyName',''))" 2>/dev/null)
180
- TELEPHONE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('telephone',''))" 2>/dev/null)
181
- EMAIL=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('email',''))" 2>/dev/null)
182
- JOB_TITLE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('jobTitle',''))" 2>/dev/null)
183
-
184
- # Validate: must have givenName and at least phone or email
185
- if [ -z "$GIVEN_NAME" ]; then
186
- echo "[import] ERROR: contact skipped (no givenName): $(basename "$CONTACT_FILE")"
187
- ERRORS=$((ERRORS + 1))
188
- continue
189
- fi
190
- if [ -z "$TELEPHONE" ] && [ -z "$EMAIL" ]; then
191
- echo "[import] ERROR: contact skipped (no phone or email): $GIVEN_NAME"
192
- ERRORS=$((ERRORS + 1))
193
- continue
194
- fi
195
-
196
- # Build MERGE query with --param for safe parameterisation
197
- # MERGE on telephone (primary identifier for Taskmaster contacts)
198
- ESCAPED_GIVEN=$(cypher_escape "$GIVEN_NAME")
199
- ESCAPED_FAMILY=$(cypher_escape "$FAMILY_NAME")
200
- ESCAPED_TITLE=$(cypher_escape "$JOB_TITLE")
201
-
202
- # Build the cypher query — MERGE on telephone for dedup
203
- CYPHER_QUERY="MERGE (p:Person {telephone: \$phone, accountId: \$acct})"
204
- CYPHER_QUERY="$CYPHER_QUERY ON CREATE SET p.givenName = \$given, p.source = 'taskmaster-migration', p.status = 'customer', p.createdOn = datetime()"
205
-
206
- # Add optional fields on CREATE
207
- if [ -n "$FAMILY_NAME" ]; then
208
- CYPHER_QUERY="$CYPHER_QUERY, p.familyName = \$family"
209
- fi
210
- if [ -n "$EMAIL" ]; then
211
- CYPHER_QUERY="$CYPHER_QUERY, p.email = \$email"
212
- fi
213
- if [ -n "$JOB_TITLE" ]; then
214
- CYPHER_QUERY="$CYPHER_QUERY, p.jobTitle = \$title"
215
- fi
216
-
217
- CYPHER_QUERY="$CYPHER_QUERY RETURN p.givenName AS name, p.telephone AS phone, CASE WHEN p.createdOn = datetime() THEN 'created' ELSE 'exists' END AS status"
218
-
219
- # Execute with parameterised values
220
- PARAMS=(
221
- --param "phone => '$(cypher_escape "$TELEPHONE")'"
222
- --param "acct => '$(cypher_escape "$ACCOUNT_ID")'"
223
- --param "given => '$(cypher_escape "$GIVEN_NAME")'"
224
- )
225
- [ -n "$FAMILY_NAME" ] && PARAMS+=(--param "family => '$(cypher_escape "$FAMILY_NAME")'")
226
- [ -n "$EMAIL" ] && PARAMS+=(--param "email => '$(cypher_escape "$(echo "$EMAIL" | tr '[:upper:]' '[:lower:]')")'")
227
- [ -n "$JOB_TITLE" ] && PARAMS+=(--param "title => '$(cypher_escape "$JOB_TITLE")'")
228
-
229
- RESULT=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
230
- "${PARAMS[@]}" "$CYPHER_QUERY" 2>&1) || {
231
- echo "[import] ERROR: contact failed: $GIVEN_NAME <$TELEPHONE> — $RESULT"
232
- ERRORS=$((ERRORS + 1))
233
- continue
234
- }
235
-
236
- if echo "$RESULT" | grep -q "created"; then
237
- echo "[import] contact created: $GIVEN_NAME <$TELEPHONE>"
238
- CREATED=$((CREATED + 1))
239
- else
240
- echo "[import] contact skipped (exists): $GIVEN_NAME <$TELEPHONE>"
241
- SKIPPED=$((SKIPPED + 1))
242
- fi
243
- done
244
- fi
245
- echo "[import] contacts: $CREATED created, $SKIPPED skipped, $ERRORS errors"
246
-
247
- # ------------------------------------------------------------------
248
- # 2. Import memory files
249
- # ------------------------------------------------------------------
250
- MEMORY_DIR="$BUNDLE_DIR/memory"
251
- MEM_COUNT=0
252
-
253
- if [ -d "$MEMORY_DIR" ]; then
254
- # Create target memory directories
255
- mkdir -p "$ACCOUNT_DIR/memory"
256
-
257
- for SUBDIR in shared admin public; do
258
- if [ -d "$MEMORY_DIR/$SUBDIR" ] && [ "$(ls -A "$MEMORY_DIR/$SUBDIR" 2>/dev/null)" ]; then
259
- # Copy recursively, preserving structure
260
- mkdir -p "$ACCOUNT_DIR/memory/$SUBDIR"
261
- cp -r "$MEMORY_DIR/$SUBDIR"/* "$ACCOUNT_DIR/memory/$SUBDIR/" 2>/dev/null || true
262
- COUNT=$(find "$ACCOUNT_DIR/memory/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
263
- echo "[import] memory/$SUBDIR: $COUNT files"
264
- MEM_COUNT=$((MEM_COUNT + COUNT))
265
- fi
266
- done
267
- fi
268
- echo "[import] memory total: $MEM_COUNT files"
269
-
270
- # ------------------------------------------------------------------
271
- # 3. Import conversations
272
- # ------------------------------------------------------------------
273
- CONVOS_DIR="$BUNDLE_DIR/conversations"
274
- CONVO_COUNT=0
275
-
276
- if [ -d "$CONVOS_DIR" ]; then
277
- mkdir -p "$ACCOUNT_DIR/conversations"
278
-
279
- # Admin conversations
280
- if [ -d "$CONVOS_DIR/admin" ] && [ "$(ls -A "$CONVOS_DIR/admin" 2>/dev/null)" ]; then
281
- mkdir -p "$ACCOUNT_DIR/conversations/admin"
282
- cp "$CONVOS_DIR/admin"/* "$ACCOUNT_DIR/conversations/admin/" 2>/dev/null || true
283
- COUNT=$(find "$ACCOUNT_DIR/conversations/admin" -type f 2>/dev/null | wc -l | tr -d ' ')
284
- echo "[import] conversations/admin: $COUNT files"
285
- CONVO_COUNT=$((CONVO_COUNT + COUNT))
286
- fi
287
-
288
- # Per-user conversations
289
- if [ -d "$CONVOS_DIR/users" ]; then
290
- for USER_CONVO in "$CONVOS_DIR/users"/*/; do
291
- [ -d "$USER_CONVO" ] || continue
292
- USER_KEY=$(basename "$USER_CONVO")
293
- mkdir -p "$ACCOUNT_DIR/conversations/users/$USER_KEY"
294
- cp "$USER_CONVO"* "$ACCOUNT_DIR/conversations/users/$USER_KEY/" 2>/dev/null || true
295
- COUNT=$(find "$ACCOUNT_DIR/conversations/users/$USER_KEY" -type f 2>/dev/null | wc -l | tr -d ' ')
296
- CONVO_COUNT=$((CONVO_COUNT + COUNT))
297
- done
298
- USER_DIRS=$(find "$CONVOS_DIR/users" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
299
- echo "[import] conversations/users: $USER_DIRS directories"
300
- fi
301
-
302
- # Group conversations
303
- if [ -d "$CONVOS_DIR/groups" ]; then
304
- for GRP_CONVO in "$CONVOS_DIR/groups"/*/; do
305
- [ -d "$GRP_CONVO" ] || continue
306
- GRP_ID=$(basename "$GRP_CONVO")
307
- mkdir -p "$ACCOUNT_DIR/conversations/groups/$GRP_ID"
308
- cp -r "$GRP_CONVO"* "$ACCOUNT_DIR/conversations/groups/$GRP_ID/" 2>/dev/null || true
309
- COUNT=$(find "$ACCOUNT_DIR/conversations/groups/$GRP_ID" -type f 2>/dev/null | wc -l | tr -d ' ')
310
- CONVO_COUNT=$((CONVO_COUNT + COUNT))
311
- done
312
- GRP_DIRS=$(find "$CONVOS_DIR/groups" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
313
- echo "[import] conversations/groups: $GRP_DIRS directories"
314
- fi
315
- fi
316
- echo "[import] conversations total: $CONVO_COUNT files"
317
-
318
- # ------------------------------------------------------------------
319
- # 4. Import media files
320
- # ------------------------------------------------------------------
321
- MEDIA_DIR="$BUNDLE_DIR/media"
322
- MEDIA_COUNT=0
323
-
324
- if [ -d "$MEDIA_DIR" ]; then
325
- mkdir -p "$ACCOUNT_DIR/media"
326
-
327
- for SUBDIR in admin public; do
328
- if [ -d "$MEDIA_DIR/$SUBDIR" ] && [ "$(ls -A "$MEDIA_DIR/$SUBDIR" 2>/dev/null)" ]; then
329
- mkdir -p "$ACCOUNT_DIR/media/$SUBDIR"
330
- cp "$MEDIA_DIR/$SUBDIR"/* "$ACCOUNT_DIR/media/$SUBDIR/" 2>/dev/null || true
331
- COUNT=$(find "$ACCOUNT_DIR/media/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
332
- echo "[import] media/$SUBDIR: $COUNT files"
333
- MEDIA_COUNT=$((MEDIA_COUNT + COUNT))
334
- fi
335
- done
336
- fi
337
- echo "[import] media total: $MEDIA_COUNT files"
338
-
339
- # ------------------------------------------------------------------
340
- # 5. Import automations (workflow definitions — reference only)
341
- # ------------------------------------------------------------------
342
- AUTO_DIR="$BUNDLE_DIR/automations"
343
- AUTO_COUNT=0
344
-
345
- if [ -d "$AUTO_DIR" ] && [ "$(ls -A "$AUTO_DIR" 2>/dev/null)" ]; then
346
- mkdir -p "$ACCOUNT_DIR/memory/admin/workflows-migrated"
347
- cp "$AUTO_DIR"/* "$ACCOUNT_DIR/memory/admin/workflows-migrated/" 2>/dev/null || true
348
- AUTO_COUNT=$(find "$ACCOUNT_DIR/memory/admin/workflows-migrated" -type f 2>/dev/null | wc -l | tr -d ' ')
349
- echo "[import] automations: $AUTO_COUNT workflow definitions → memory/admin/workflows-migrated/"
350
- fi
351
-
352
- # ------------------------------------------------------------------
353
- # 6. Apply PINs (if present in bundle)
354
- # ------------------------------------------------------------------
355
- # The admin PIN lives in the brand's config directory (~/.realagent/.admin-pin
356
- # or ~/.maxy/.admin-pin), not in account.json. Derive the config dir from
357
- # the brand.json stamped into the install by the bundler.
358
- BRAND_JSON="$INSTALL_DIR/platform/config/brand.json"
359
- CONFIG_DIR_NAME=""
360
- if [ -f "$BRAND_JSON" ]; then
361
- CONFIG_DIR_NAME=$(python3 -c "import json; print(json.load(open('$BRAND_JSON')).get('configDir',''))" 2>/dev/null)
362
- fi
363
-
364
- PINS_FILE="$BUNDLE_DIR/identity/pins.json"
365
- if [ -f "$PINS_FILE" ]; then
366
- MASTER_PIN=$(python3 -c "import json; d=json.load(open('$PINS_FILE')); print(d.get('masterPin','') or '')" 2>/dev/null)
367
- if [ -n "$MASTER_PIN" ]; then
368
- if [ -n "$CONFIG_DIR_NAME" ]; then
369
- PIN_DIR="$HOME/$CONFIG_DIR_NAME"
370
- mkdir -p "$PIN_DIR"
371
- # The platform stores PINs as SHA-256 hashes, not plain text
372
- python3 -c "import hashlib; print(hashlib.sha256('$MASTER_PIN'.encode()).hexdigest())" > "$PIN_DIR/.admin-pin"
373
- echo "[import] masterPin hash written to $PIN_DIR/.admin-pin"
374
-
375
- # Auth gate is users.json-authoritative. seed-neo4j.sh creates
376
- # users.json from .admin-pin, but only at install time — and install runs
377
- # before this script writes .admin-pin. Self-contained migration writes
378
- # users.json directly, mirroring seed-neo4j.sh's migration branch.
379
- # write to the persistent location (outside the install wipe
380
- # zone) so subsequent installs do not overwrite this row with a stale backup.
381
- USERS_FILE="$PIN_DIR/users.json"
382
- if [ ! -f "$USERS_FILE" ]; then
383
- USER_ID="$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')"
384
- PIN_HASH="$(cat "$PIN_DIR/.admin-pin")"
385
- cat > "$USERS_FILE" << USERS_EOF
386
- [{"userId":"$USER_ID","name":"Owner","pin":"$PIN_HASH"}]
387
- USERS_EOF
388
- echo "[import] users.json created (userId=${USER_ID:0:8})"
389
-
390
- if [ -f "$ACCOUNT_DIR/account.json" ]; then
391
- python3 -c "
392
- import json
393
- with open('$ACCOUNT_DIR/account.json', 'r') as f:
394
- config = json.load(f)
395
- config.setdefault('admins', [])
396
- if not any(a.get('userId') == '$USER_ID' for a in config['admins']):
397
- config['admins'].append({'userId': '$USER_ID', 'role': 'owner'})
398
- with open('$ACCOUNT_DIR/account.json', 'w') as f:
399
- json.dump(config, f, indent=2)
400
- f.write('\n')
401
- "
402
- echo "[import] account.json admins updated (userId=${USER_ID:0:8} role=owner)"
403
- else
404
- echo "[import] ERROR: account.json not found at $ACCOUNT_DIR/account.json" >&2
405
- exit 1
406
- fi
407
- else
408
- echo "[import] users.json exists — skipping creation, preserving existing userId"
409
- fi
410
- else
411
- echo "[import] WARN: brand.json not found at $BRAND_JSON — cannot determine config directory for PIN"
412
- echo "[import] WARN: write the PIN manually: echo -n '$MASTER_PIN' > ~/.<brand>/.admin-pin"
413
- fi
414
- else
415
- echo "[import] No masterPin in bundle — skipping PIN application"
416
- fi
417
- else
418
- echo "[import] No pins.json in bundle — skipping PIN application"
419
- fi
420
-
421
- # ------------------------------------------------------------------
422
- # Summary
423
- # ------------------------------------------------------------------
424
- echo ""
425
- echo "[import] =================================================="
426
- echo "[import] Import complete: $ACCOUNT_ID"
427
- echo "[import] Contacts: $CREATED created, $SKIPPED skipped, $ERRORS errors"
428
- echo "[import] Memory files: $MEM_COUNT"
429
- echo "[import] Conversations: $CONVO_COUNT files"
430
- echo "[import] Media files: $MEDIA_COUNT"
431
- echo "[import] Automations: $AUTO_COUNT"
432
- echo "[import] =================================================="
433
-
434
- if [ "$ERRORS" -gt 0 ]; then
435
- echo "[import] WARNING: $ERRORS errors occurred during import — review log above"
436
- exit 1
437
- fi