@rubytech/create-maxy 1.0.468 → 1.0.470
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/package.json +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +110 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/public-agent-manager/skill.md +28 -4
- package/payload/platform/plugins/docs/references/migration-guide.md +82 -0
- package/payload/platform/scripts/migrate-import.sh +387 -0
- package/payload/platform/scripts/taskmaster-export.sh +387 -0
- package/payload/server/public/assets/ChatInput-CzSLcXRP.css +1 -0
- package/payload/server/public/assets/{admin-BWFKvjFU.js → admin-BVJphQeS.js} +1 -1
- package/payload/server/public/assets/public-XYSRSOuh.js +5 -0
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +78 -8
- package/payload/server/public/assets/ChatInput-BZnIVDdt.css +0 -1
- package/payload/server/public/assets/public-Dqvygu4a.js +0 -5
- /package/payload/server/public/assets/{ChatInput-DobbViSh.js → ChatInput-CeYJrCR1.js} +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
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="${NEO4J_URI:-bolt://localhost:7687}"
|
|
120
|
+
NEO4J_USER="${NEO4J_USER:-neo4j}"
|
|
121
|
+
|
|
122
|
+
NEO4J_PASSWORD_FILE="$INSTALL_DIR/platform/config/.neo4j-password"
|
|
123
|
+
if [ -n "${NEO4J_PASSWORD:-}" ]; then
|
|
124
|
+
: # Explicit env var takes precedence
|
|
125
|
+
elif [ -f "$NEO4J_PASSWORD_FILE" ]; then
|
|
126
|
+
NEO4J_PASSWORD=$(cat "$NEO4J_PASSWORD_FILE")
|
|
127
|
+
else
|
|
128
|
+
echo "[import] ERROR: Neo4j password not found."
|
|
129
|
+
echo "[import] Expected at: $NEO4J_PASSWORD_FILE"
|
|
130
|
+
echo "[import] Or set NEO4J_PASSWORD environment variable."
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
CYPHER_SHELL="cypher-shell"
|
|
135
|
+
if ! command -v "$CYPHER_SHELL" &> /dev/null; then
|
|
136
|
+
echo "[import] ERROR: cypher-shell not found. Install Neo4j or add cypher-shell to PATH."
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Test connection
|
|
141
|
+
if ! "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
|
|
142
|
+
"RETURN 1" > /dev/null 2>&1; then
|
|
143
|
+
echo "[import] ERROR: Cannot connect to Neo4j at $NEO4J_URI"
|
|
144
|
+
echo "[import] ERROR: Is Neo4j running?"
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
147
|
+
echo "[import] Neo4j connection OK ($NEO4J_URI)"
|
|
148
|
+
|
|
149
|
+
# ------------------------------------------------------------------
|
|
150
|
+
# Helper: escape a string for cypher-shell --param single-quoted value
|
|
151
|
+
# In cypher string literals, single quotes are escaped by doubling: ' → ''
|
|
152
|
+
# ------------------------------------------------------------------
|
|
153
|
+
cypher_escape() {
|
|
154
|
+
echo "$1" | sed "s/'/''/g"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# ------------------------------------------------------------------
|
|
158
|
+
# 1. Import contacts
|
|
159
|
+
# ------------------------------------------------------------------
|
|
160
|
+
CONTACTS_DIR="$BUNDLE_DIR/contacts"
|
|
161
|
+
CREATED=0
|
|
162
|
+
SKIPPED=0
|
|
163
|
+
ERRORS=0
|
|
164
|
+
|
|
165
|
+
if [ -d "$CONTACTS_DIR" ]; then
|
|
166
|
+
for CONTACT_FILE in "$CONTACTS_DIR"/*.json; do
|
|
167
|
+
[ -f "$CONTACT_FILE" ] || continue
|
|
168
|
+
|
|
169
|
+
# Extract fields from contact JSON
|
|
170
|
+
GIVEN_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('givenName',''))" 2>/dev/null)
|
|
171
|
+
FAMILY_NAME=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('familyName',''))" 2>/dev/null)
|
|
172
|
+
TELEPHONE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('telephone',''))" 2>/dev/null)
|
|
173
|
+
EMAIL=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('email',''))" 2>/dev/null)
|
|
174
|
+
JOB_TITLE=$(python3 -c "import json; d=json.load(open('$CONTACT_FILE')); print(d.get('jobTitle',''))" 2>/dev/null)
|
|
175
|
+
|
|
176
|
+
# Validate: must have givenName and at least phone or email
|
|
177
|
+
if [ -z "$GIVEN_NAME" ]; then
|
|
178
|
+
echo "[import] ERROR: contact skipped (no givenName): $(basename "$CONTACT_FILE")"
|
|
179
|
+
ERRORS=$((ERRORS + 1))
|
|
180
|
+
continue
|
|
181
|
+
fi
|
|
182
|
+
if [ -z "$TELEPHONE" ] && [ -z "$EMAIL" ]; then
|
|
183
|
+
echo "[import] ERROR: contact skipped (no phone or email): $GIVEN_NAME"
|
|
184
|
+
ERRORS=$((ERRORS + 1))
|
|
185
|
+
continue
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Build MERGE query with --param for safe parameterisation
|
|
189
|
+
# MERGE on telephone (primary identifier for Taskmaster contacts)
|
|
190
|
+
ESCAPED_GIVEN=$(cypher_escape "$GIVEN_NAME")
|
|
191
|
+
ESCAPED_FAMILY=$(cypher_escape "$FAMILY_NAME")
|
|
192
|
+
ESCAPED_TITLE=$(cypher_escape "$JOB_TITLE")
|
|
193
|
+
|
|
194
|
+
# Build the cypher query — MERGE on telephone for dedup
|
|
195
|
+
CYPHER_QUERY="MERGE (p:Person {telephone: \$phone, accountId: \$acct})"
|
|
196
|
+
CYPHER_QUERY="$CYPHER_QUERY ON CREATE SET p.givenName = \$given, p.source = 'taskmaster-migration', p.status = 'customer', p.createdOn = datetime()"
|
|
197
|
+
|
|
198
|
+
# Add optional fields on CREATE
|
|
199
|
+
if [ -n "$FAMILY_NAME" ]; then
|
|
200
|
+
CYPHER_QUERY="$CYPHER_QUERY, p.familyName = \$family"
|
|
201
|
+
fi
|
|
202
|
+
if [ -n "$EMAIL" ]; then
|
|
203
|
+
CYPHER_QUERY="$CYPHER_QUERY, p.email = \$email"
|
|
204
|
+
fi
|
|
205
|
+
if [ -n "$JOB_TITLE" ]; then
|
|
206
|
+
CYPHER_QUERY="$CYPHER_QUERY, p.jobTitle = \$title"
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
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"
|
|
210
|
+
|
|
211
|
+
# Execute with parameterised values
|
|
212
|
+
PARAMS=(
|
|
213
|
+
--param "phone => '$(cypher_escape "$TELEPHONE")'"
|
|
214
|
+
--param "acct => '$(cypher_escape "$ACCOUNT_ID")'"
|
|
215
|
+
--param "given => '$(cypher_escape "$GIVEN_NAME")'"
|
|
216
|
+
)
|
|
217
|
+
[ -n "$FAMILY_NAME" ] && PARAMS+=(--param "family => '$(cypher_escape "$FAMILY_NAME")'")
|
|
218
|
+
[ -n "$EMAIL" ] && PARAMS+=(--param "email => '$(cypher_escape "$(echo "$EMAIL" | tr '[:upper:]' '[:lower:]')")'")
|
|
219
|
+
[ -n "$JOB_TITLE" ] && PARAMS+=(--param "title => '$(cypher_escape "$JOB_TITLE")'")
|
|
220
|
+
|
|
221
|
+
RESULT=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
|
|
222
|
+
"${PARAMS[@]}" "$CYPHER_QUERY" 2>&1) || {
|
|
223
|
+
echo "[import] ERROR: contact failed: $GIVEN_NAME <$TELEPHONE> — $RESULT"
|
|
224
|
+
ERRORS=$((ERRORS + 1))
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if echo "$RESULT" | grep -q "created"; then
|
|
229
|
+
echo "[import] contact created: $GIVEN_NAME <$TELEPHONE>"
|
|
230
|
+
CREATED=$((CREATED + 1))
|
|
231
|
+
else
|
|
232
|
+
echo "[import] contact skipped (exists): $GIVEN_NAME <$TELEPHONE>"
|
|
233
|
+
SKIPPED=$((SKIPPED + 1))
|
|
234
|
+
fi
|
|
235
|
+
done
|
|
236
|
+
fi
|
|
237
|
+
echo "[import] contacts: $CREATED created, $SKIPPED skipped, $ERRORS errors"
|
|
238
|
+
|
|
239
|
+
# ------------------------------------------------------------------
|
|
240
|
+
# 2. Import memory files
|
|
241
|
+
# ------------------------------------------------------------------
|
|
242
|
+
MEMORY_DIR="$BUNDLE_DIR/memory"
|
|
243
|
+
MEM_COUNT=0
|
|
244
|
+
|
|
245
|
+
if [ -d "$MEMORY_DIR" ]; then
|
|
246
|
+
# Create target memory directories
|
|
247
|
+
mkdir -p "$ACCOUNT_DIR/memory"
|
|
248
|
+
|
|
249
|
+
for SUBDIR in shared admin public; do
|
|
250
|
+
if [ -d "$MEMORY_DIR/$SUBDIR" ] && [ "$(ls -A "$MEMORY_DIR/$SUBDIR" 2>/dev/null)" ]; then
|
|
251
|
+
# Copy recursively, preserving structure
|
|
252
|
+
mkdir -p "$ACCOUNT_DIR/memory/$SUBDIR"
|
|
253
|
+
cp -r "$MEMORY_DIR/$SUBDIR"/* "$ACCOUNT_DIR/memory/$SUBDIR/" 2>/dev/null || true
|
|
254
|
+
COUNT=$(find "$ACCOUNT_DIR/memory/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
255
|
+
echo "[import] memory/$SUBDIR: $COUNT files"
|
|
256
|
+
MEM_COUNT=$((MEM_COUNT + COUNT))
|
|
257
|
+
fi
|
|
258
|
+
done
|
|
259
|
+
fi
|
|
260
|
+
echo "[import] memory total: $MEM_COUNT files"
|
|
261
|
+
|
|
262
|
+
# ------------------------------------------------------------------
|
|
263
|
+
# 3. Import conversations
|
|
264
|
+
# ------------------------------------------------------------------
|
|
265
|
+
CONVOS_DIR="$BUNDLE_DIR/conversations"
|
|
266
|
+
CONVO_COUNT=0
|
|
267
|
+
|
|
268
|
+
if [ -d "$CONVOS_DIR" ]; then
|
|
269
|
+
mkdir -p "$ACCOUNT_DIR/conversations"
|
|
270
|
+
|
|
271
|
+
# Admin conversations
|
|
272
|
+
if [ -d "$CONVOS_DIR/admin" ] && [ "$(ls -A "$CONVOS_DIR/admin" 2>/dev/null)" ]; then
|
|
273
|
+
mkdir -p "$ACCOUNT_DIR/conversations/admin"
|
|
274
|
+
cp "$CONVOS_DIR/admin"/* "$ACCOUNT_DIR/conversations/admin/" 2>/dev/null || true
|
|
275
|
+
COUNT=$(find "$ACCOUNT_DIR/conversations/admin" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
276
|
+
echo "[import] conversations/admin: $COUNT files"
|
|
277
|
+
CONVO_COUNT=$((CONVO_COUNT + COUNT))
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# Per-user conversations
|
|
281
|
+
if [ -d "$CONVOS_DIR/users" ]; then
|
|
282
|
+
for USER_CONVO in "$CONVOS_DIR/users"/*/; do
|
|
283
|
+
[ -d "$USER_CONVO" ] || continue
|
|
284
|
+
USER_KEY=$(basename "$USER_CONVO")
|
|
285
|
+
mkdir -p "$ACCOUNT_DIR/conversations/users/$USER_KEY"
|
|
286
|
+
cp "$USER_CONVO"* "$ACCOUNT_DIR/conversations/users/$USER_KEY/" 2>/dev/null || true
|
|
287
|
+
COUNT=$(find "$ACCOUNT_DIR/conversations/users/$USER_KEY" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
288
|
+
CONVO_COUNT=$((CONVO_COUNT + COUNT))
|
|
289
|
+
done
|
|
290
|
+
USER_DIRS=$(find "$CONVOS_DIR/users" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
|
|
291
|
+
echo "[import] conversations/users: $USER_DIRS directories"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
# Group conversations
|
|
295
|
+
if [ -d "$CONVOS_DIR/groups" ]; then
|
|
296
|
+
for GRP_CONVO in "$CONVOS_DIR/groups"/*/; do
|
|
297
|
+
[ -d "$GRP_CONVO" ] || continue
|
|
298
|
+
GRP_ID=$(basename "$GRP_CONVO")
|
|
299
|
+
mkdir -p "$ACCOUNT_DIR/conversations/groups/$GRP_ID"
|
|
300
|
+
cp -r "$GRP_CONVO"* "$ACCOUNT_DIR/conversations/groups/$GRP_ID/" 2>/dev/null || true
|
|
301
|
+
COUNT=$(find "$ACCOUNT_DIR/conversations/groups/$GRP_ID" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
302
|
+
CONVO_COUNT=$((CONVO_COUNT + COUNT))
|
|
303
|
+
done
|
|
304
|
+
GRP_DIRS=$(find "$CONVOS_DIR/groups" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
|
|
305
|
+
echo "[import] conversations/groups: $GRP_DIRS directories"
|
|
306
|
+
fi
|
|
307
|
+
fi
|
|
308
|
+
echo "[import] conversations total: $CONVO_COUNT files"
|
|
309
|
+
|
|
310
|
+
# ------------------------------------------------------------------
|
|
311
|
+
# 4. Import media files
|
|
312
|
+
# ------------------------------------------------------------------
|
|
313
|
+
MEDIA_DIR="$BUNDLE_DIR/media"
|
|
314
|
+
MEDIA_COUNT=0
|
|
315
|
+
|
|
316
|
+
if [ -d "$MEDIA_DIR" ]; then
|
|
317
|
+
mkdir -p "$ACCOUNT_DIR/media"
|
|
318
|
+
|
|
319
|
+
for SUBDIR in admin public; do
|
|
320
|
+
if [ -d "$MEDIA_DIR/$SUBDIR" ] && [ "$(ls -A "$MEDIA_DIR/$SUBDIR" 2>/dev/null)" ]; then
|
|
321
|
+
mkdir -p "$ACCOUNT_DIR/media/$SUBDIR"
|
|
322
|
+
cp "$MEDIA_DIR/$SUBDIR"/* "$ACCOUNT_DIR/media/$SUBDIR/" 2>/dev/null || true
|
|
323
|
+
COUNT=$(find "$ACCOUNT_DIR/media/$SUBDIR" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
324
|
+
echo "[import] media/$SUBDIR: $COUNT files"
|
|
325
|
+
MEDIA_COUNT=$((MEDIA_COUNT + COUNT))
|
|
326
|
+
fi
|
|
327
|
+
done
|
|
328
|
+
fi
|
|
329
|
+
echo "[import] media total: $MEDIA_COUNT files"
|
|
330
|
+
|
|
331
|
+
# ------------------------------------------------------------------
|
|
332
|
+
# 5. Import automations (workflow definitions — reference only)
|
|
333
|
+
# ------------------------------------------------------------------
|
|
334
|
+
AUTO_DIR="$BUNDLE_DIR/automations"
|
|
335
|
+
AUTO_COUNT=0
|
|
336
|
+
|
|
337
|
+
if [ -d "$AUTO_DIR" ] && [ "$(ls -A "$AUTO_DIR" 2>/dev/null)" ]; then
|
|
338
|
+
mkdir -p "$ACCOUNT_DIR/memory/admin/workflows-migrated"
|
|
339
|
+
cp "$AUTO_DIR"/* "$ACCOUNT_DIR/memory/admin/workflows-migrated/" 2>/dev/null || true
|
|
340
|
+
AUTO_COUNT=$(find "$ACCOUNT_DIR/memory/admin/workflows-migrated" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
341
|
+
echo "[import] automations: $AUTO_COUNT workflow definitions → memory/admin/workflows-migrated/"
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# ------------------------------------------------------------------
|
|
345
|
+
# 6. Apply PINs (if present in bundle)
|
|
346
|
+
# ------------------------------------------------------------------
|
|
347
|
+
PINS_FILE="$BUNDLE_DIR/identity/pins.json"
|
|
348
|
+
if [ -f "$PINS_FILE" ]; then
|
|
349
|
+
MASTER_PIN=$(python3 -c "import json; d=json.load(open('$PINS_FILE')); print(d.get('masterPin','') or '')" 2>/dev/null)
|
|
350
|
+
if [ -n "$MASTER_PIN" ]; then
|
|
351
|
+
# Write PIN to account config
|
|
352
|
+
ACCOUNT_JSON="$ACCOUNT_DIR/account.json"
|
|
353
|
+
if [ -f "$ACCOUNT_JSON" ]; then
|
|
354
|
+
python3 -c "
|
|
355
|
+
import json
|
|
356
|
+
acct = json.load(open('$ACCOUNT_JSON'))
|
|
357
|
+
acct['masterPin'] = '$MASTER_PIN'
|
|
358
|
+
json.dump(acct, open('$ACCOUNT_JSON','w'), indent=2)
|
|
359
|
+
print('[import] masterPin applied to account.json')
|
|
360
|
+
"
|
|
361
|
+
else
|
|
362
|
+
echo "[import] WARN: account.json not found — PINs not applied"
|
|
363
|
+
fi
|
|
364
|
+
else
|
|
365
|
+
echo "[import] No masterPin in bundle — skipping PIN application"
|
|
366
|
+
fi
|
|
367
|
+
else
|
|
368
|
+
echo "[import] No pins.json in bundle — skipping PIN application"
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
# ------------------------------------------------------------------
|
|
372
|
+
# Summary
|
|
373
|
+
# ------------------------------------------------------------------
|
|
374
|
+
echo ""
|
|
375
|
+
echo "[import] =================================================="
|
|
376
|
+
echo "[import] Import complete: $ACCOUNT_ID"
|
|
377
|
+
echo "[import] Contacts: $CREATED created, $SKIPPED skipped, $ERRORS errors"
|
|
378
|
+
echo "[import] Memory files: $MEM_COUNT"
|
|
379
|
+
echo "[import] Conversations: $CONVO_COUNT files"
|
|
380
|
+
echo "[import] Media files: $MEDIA_COUNT"
|
|
381
|
+
echo "[import] Automations: $AUTO_COUNT"
|
|
382
|
+
echo "[import] =================================================="
|
|
383
|
+
|
|
384
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
385
|
+
echo "[import] WARNING: $ERRORS errors occurred during import — review log above"
|
|
386
|
+
exit 1
|
|
387
|
+
fi
|