@rubytech/create-maxy-code 0.1.437 → 0.1.439

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 (60) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/admin-access-password/__tests__/index.test.ts +31 -0
  3. package/payload/platform/lib/admin-access-password/dist/index.d.ts +8 -0
  4. package/payload/platform/lib/admin-access-password/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/admin-access-password/dist/index.js +5 -1
  6. package/payload/platform/lib/admin-access-password/dist/index.js.map +1 -1
  7. package/payload/platform/lib/admin-access-password/src/index.ts +14 -1
  8. package/payload/platform/lib/admins-write/__tests__/index.test.ts +27 -0
  9. package/payload/platform/lib/admins-write/dist/index.d.ts +8 -0
  10. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -1
  11. package/payload/platform/lib/admins-write/dist/index.js +5 -1
  12. package/payload/platform/lib/admins-write/dist/index.js.map +1 -1
  13. package/payload/platform/lib/admins-write/src/index.ts +14 -1
  14. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  15. package/payload/platform/plugins/admin/PLUGIN.md +4 -2
  16. package/payload/platform/plugins/admin/mcp/dist/credential-identity.d.ts +23 -0
  17. package/payload/platform/plugins/admin/mcp/dist/credential-identity.d.ts.map +1 -0
  18. package/payload/platform/plugins/admin/mcp/dist/credential-identity.js +35 -0
  19. package/payload/platform/plugins/admin/mcp/dist/credential-identity.js.map +1 -0
  20. package/payload/platform/plugins/admin/mcp/dist/index.js +110 -57
  21. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  22. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +22 -0
  23. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
  24. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +62 -2
  25. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
  26. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +7 -1
  27. package/payload/platform/plugins/docs/references/admin-identity-gate.md +40 -0
  28. package/payload/platform/plugins/docs/references/internals.md +4 -0
  29. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
  30. package/payload/platform/plugins/graph-viewer/.claude-plugin/plugin.json +13 -0
  31. package/payload/platform/plugins/graph-viewer/PLUGIN.md +1 -1
  32. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +193 -0
  33. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/package.json +3 -0
  34. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/profile.md +1 -1
  35. package/payload/platform/scripts/remediate-glsmith-identity.sh +500 -0
  36. package/payload/platform/scripts/seed-neo4j.sh +77 -12
  37. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/http-server.js +20 -4
  39. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/identity-audit.d.ts +51 -0
  41. package/payload/platform/services/claude-session-manager/dist/identity-audit.d.ts.map +1 -0
  42. package/payload/platform/services/claude-session-manager/dist/identity-audit.js +103 -0
  43. package/payload/platform/services/claude-session-manager/dist/identity-audit.js.map +1 -0
  44. package/payload/platform/services/claude-session-manager/dist/index.js +69 -0
  45. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.d.ts +25 -0
  47. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.d.ts.map +1 -0
  48. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.js +42 -0
  49. package/payload/platform/services/claude-session-manager/dist/mcp-user-binding.js.map +1 -0
  50. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.d.ts +44 -0
  51. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.d.ts.map +1 -0
  52. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.js +127 -0
  53. package/payload/platform/services/claude-session-manager/dist/redundant-operator-seat-audit.js.map +1 -0
  54. package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts +57 -0
  55. package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts.map +1 -0
  56. package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js +147 -0
  57. package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js.map +1 -0
  58. package/payload/server/{chunk-LMLVOZ6G.js → chunk-UGPQCVIW.js} +11 -38
  59. package/payload/server/maxy-edge.js +1 -1
  60. package/payload/server/server.js +15 -16
@@ -0,0 +1,500 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================
3
+ # remediate-glsmith-identity.sh
4
+ #
5
+ # Purpose
6
+ # -------
7
+ # One-account identity-corner repair for G.L.Smith & Sons (accountId
8
+ # 2078cb54) on the SiteDesk Code install (Neo4j bolt://localhost:7689).
9
+ # Merges a fragmented owner/admin/person corner into one canonical named
10
+ # owner. Task 1569. Design + grounded live state:
11
+ # maxy-code/.docs/migrations/2026-07-glsmith-identity-remediation.md
12
+ #
13
+ # What it does (each step idempotent; logs its post-condition):
14
+ # rename-owner 3f81e7c2 "Owner" -> name "Dale Smith"
15
+ # rehome-prefs move 28 source:explicit prefs from stray profile
16
+ # (userId 436fdf2d, acct 2078cb54) to the owner profile,
17
+ # re-stamp pref.userId -> 3f81e7c2; delete the 4
18
+ # source:behavioural prefs; remove the emptied stray
19
+ # profile (clears its cross-account HAS_PROFILE edge)
20
+ # merge-person merge the second Dale Person into the owner's Dale
21
+ # Person (keep phone 447958391734); one named Dale left
22
+ # fold-dale-seat re-point 2f4a0a6b's inbound STARTED_BY to the owner,
23
+ # drop its OWNS, remove its empty profile, delete it
24
+ # retire-joel-seat delete c6a0e01a + its empty profile; drop the stray
25
+ # cross-account OWNS -> Person 324 (house Person 324 kept)
26
+ # keith-timeline create a Task RAISED_BY the owner Dale Person and
27
+ # AFFECTS Person Keith Bullot (759), from Event 1559
28
+ # account-admins account.json.admins -> [] (access is install-wide)
29
+ # reembed-needed report nodes needing a nomic-embed-text re-embed
30
+ #
31
+ # Write-gate invariant: the account never drops below one
32
+ # AdminUser{accountId} and always retains its role:'owner' node. Asserted
33
+ # before and after every destructive step.
34
+ #
35
+ # Usage
36
+ # -----
37
+ # bash remediate-glsmith-identity.sh # dry-run (default)
38
+ # bash remediate-glsmith-identity.sh --dry-run
39
+ # bash remediate-glsmith-identity.sh --verify # post-apply asserts
40
+ # bash remediate-glsmith-identity.sh --apply --dump=/path/to/neo4j.dump
41
+ # bash remediate-glsmith-identity.sh --apply --force-no-dump # no dump (loud)
42
+ #
43
+ # --dry-run Print every intended node/edge change; mutate nothing (default).
44
+ # --apply Perform the writes. Refuses to run without a valid --dump path
45
+ # (a fresh full neo4j-admin dump) unless --force-no-dump is given.
46
+ # Writes a JSON snapshot of every touched node/rel before writing.
47
+ # --verify Run the post-apply asserts; exit non-zero if any fail.
48
+ #
49
+ # Rollback
50
+ # --------
51
+ # Authoritative: restore the operator's --dump via `neo4j-admin database
52
+ # load`. Fast/targeted: the JSON snapshot at ~/glsmith-1569-snapshot-<ts>.{nodes,rels}
53
+ # records the touched nodes (elementId, labels, properties) and relationships
54
+ # (type, endpoints, properties) as they were before the writes.
55
+ #
56
+ # Reads:
57
+ # ~/sitedesk-code/platform/config/.neo4j-password Neo4j password
58
+ # ~/sitedesk-code/data/accounts/2078cb54-.../account.json
59
+ # ============================================================
60
+
61
+ set -euo pipefail
62
+
63
+ # --- Fixed identity constants (account 2078cb54) --------------------
64
+ readonly ACCT='2078cb54-08e9-49e9-bf8e-e9f3ad76ca41'
65
+ readonly OWNER='3f81e7c2-8e38-42f0-bce2-7355cd7c1ecf' # survivor, role:owner
66
+ readonly JOEL='c6a0e01a-ca12-4c6c-b8a9-0d5be937136b' # operator seat, retire
67
+ readonly DALE='2f4a0a6b-f239-461b-ba90-0d380fc72f43' # sub-account seat, fold
68
+ readonly HOUSEOP='436fdf2d-cfa7-4836-9ffa-77d91cef15f9' # house operator; stray profile userId
69
+ readonly DALE_PHONE='447958391734'
70
+
71
+ readonly PWFILE="$HOME/sitedesk-code/platform/config/.neo4j-password"
72
+ readonly ACCOUNT_JSON="$HOME/sitedesk-code/data/accounts/$ACCT/account.json"
73
+ readonly NEO4J_USER="${NEO4J_USER:-neo4j}"
74
+ readonly URI="${NEO4J_URI:-bolt://localhost:7689}"
75
+
76
+ MODE="dry-run"
77
+ DUMP_PATH=""
78
+ FORCE_NO_DUMP=0
79
+
80
+ while [ $# -gt 0 ]; do
81
+ case "$1" in
82
+ --dry-run) MODE="dry-run"; shift ;;
83
+ --apply) MODE="apply"; shift ;;
84
+ --verify) MODE="verify"; shift ;;
85
+ --dump=*) DUMP_PATH="${1#*=}"; shift ;;
86
+ --dump) DUMP_PATH="${2:-}"; shift 2 ;;
87
+ --force-no-dump) FORCE_NO_DUMP=1; shift ;;
88
+ -h|--help) sed -n '2,/^# =\{20,\}/p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
89
+ *) echo "Unknown arg: $1" >&2; exit 2 ;;
90
+ esac
91
+ done
92
+
93
+ log() { echo "[id-remediation] $*" >&2; }
94
+
95
+ # --- Connection -----------------------------------------------------
96
+ [ -f "$PWFILE" ] || { echo "Error: $PWFILE missing (run on the SiteDesk Code box)" >&2; exit 1; }
97
+ command -v cypher-shell >/dev/null 2>&1 || { echo "Error: cypher-shell not in PATH" >&2; exit 1; }
98
+ PW="$(cat "$PWFILE")"
99
+
100
+ # run_cypher: reads a cypher statement on stdin, prints --format plain output.
101
+ run_cypher() { cypher-shell -u "$NEO4J_USER" -p "$PW" -a "$URI" --format plain; }
102
+
103
+ # scalar: first data value (row 2), quotes stripped. Always exits 0 (empty result
104
+ # yields empty string) so a `set -e` assignment never aborts on no-rows.
105
+ scalar() { { tail -n +2 | tr -d '"' | grep -v '^[[:space:]]*$' | head -1; } || true; }
106
+ # int: last bare-integer token, defaulting 0.
107
+ as_int() { awk -F',' '{for(i=1;i<=NF;i++){gsub(/[^0-9-]/,"",$i); if($i ~ /^-?[0-9]+$/) v=$i}} END{print v+0}'; }
108
+
109
+ # --- Write-gate --------------------------------------------------------
110
+ # Echoes "admins=<n> owners=<n> business=<0|1>"; returns 0 iff n>=1 && owners>=1.
111
+ write_gate() {
112
+ local out n owners biz
113
+ out="$(run_cypher <<CYPHER
114
+ MATCH (a:AdminUser {accountId:'$ACCT'})
115
+ WITH count(a) AS n, sum(CASE WHEN a.role='owner' THEN 1 ELSE 0 END) AS owners
116
+ OPTIONAL MATCH (b:LocalBusiness {accountId:'$ACCT'})
117
+ RETURN n, owners, CASE WHEN count(b) > 0 THEN 1 ELSE 0 END AS business;
118
+ CYPHER
119
+ )"
120
+ n="$(echo "$out" | tail -n +2 | awk -F',' '{print $1+0}')"
121
+ owners="$(echo "$out" | tail -n +2 | awk -F',' '{print $2+0}')"
122
+ biz="$(echo "$out" | tail -n +2 | awk -F',' '{print $3+0}')"
123
+ echo "admins=$n owners=$owners business=$biz"
124
+ [ "$n" -ge 1 ] && [ "$owners" -ge 1 ]
125
+ }
126
+
127
+ assert_write_gate() {
128
+ local g; g="$(write_gate)" || { log "ABORT write-gate breached: $g"; exit 3; }
129
+ case "$g" in *business=1*) : ;; *) log "ABORT write-gate: no LocalBusiness ($g)"; exit 3 ;; esac
130
+ log "write-gate ok $g"
131
+ }
132
+
133
+ # --- Preconditions -----------------------------------------------------
134
+ preconditions() {
135
+ run_cypher <<< "RETURN 1;" >/dev/null 2>&1 || { echo "Error: cannot connect to $URI" >&2; exit 1; }
136
+ local ownerName
137
+ ownerName="$(run_cypher <<CYPHER | scalar
138
+ MATCH (a:AdminUser {userId:'$OWNER', accountId:'$ACCT', role:'owner'}) RETURN a.name;
139
+ CYPHER
140
+ )"
141
+ [ -n "$ownerName" ] || { log "ABORT precondition: survivor owner $OWNER (role:owner) not found on $ACCT"; exit 3; }
142
+ assert_write_gate
143
+ log "preconditions ok survivorOwner=$OWNER name=\"$ownerName\""
144
+ }
145
+
146
+ # ======================================================================
147
+ # Steps. Each runs a read-only DETECT (always, prints WOULD-change) and,
148
+ # when MODE=apply, the mutation. Idempotent: DETECT reports 0 -> no-op.
149
+ # ======================================================================
150
+
151
+ step_rename_owner() {
152
+ local pending
153
+ pending="$(run_cypher <<CYPHER | as_int
154
+ MATCH (a:AdminUser {userId:'$OWNER', accountId:'$ACCT'}) WHERE a.name <> 'Dale Smith' RETURN count(a);
155
+ CYPHER
156
+ )"
157
+ if [ "$pending" -eq 0 ]; then log "step=rename-owner no-op (already 'Dale Smith')"; return; fi
158
+ if [ "$MODE" = "dry-run" ]; then log "step=rename-owner WOULD set AdminUser $OWNER name -> 'Dale Smith'"; return; fi
159
+ run_cypher <<CYPHER >/dev/null
160
+ MATCH (a:AdminUser {userId:'$OWNER', accountId:'$ACCT'}) SET a.name='Dale Smith', a.updatedAt=timestamp();
161
+ CYPHER
162
+ log "step=rename-owner name=\"Dale Smith\""
163
+ }
164
+
165
+ step_rehome_prefs() {
166
+ local explicit behavioural
167
+ explicit="$(run_cypher <<CYPHER | as_int
168
+ MATCH (:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'})-[:HAS_PREFERENCE]->(p:Preference) WHERE p.source='explicit' RETURN count(p);
169
+ CYPHER
170
+ )"
171
+ behavioural="$(run_cypher <<CYPHER | as_int
172
+ MATCH (:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'})-[:HAS_PREFERENCE]->(p:Preference) WHERE p.source='behavioural' RETURN count(p);
173
+ CYPHER
174
+ )"
175
+ if [ "$explicit" -eq 0 ] && [ "$behavioural" -eq 0 ]; then
176
+ local strayGone
177
+ strayGone="$(run_cypher <<CYPHER | as_int
178
+ MATCH (u:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'}) RETURN count(u);
179
+ CYPHER
180
+ )"
181
+ [ "$strayGone" -eq 0 ] && { log "step=rehome-prefs no-op (stray profile already removed)"; return; }
182
+ fi
183
+ if [ "$MODE" = "dry-run" ]; then
184
+ local ownerNow
185
+ ownerNow="$(run_cypher <<CYPHER | as_int
186
+ MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:HAS_PROFILE]->(op:UserProfile)-[:HAS_PREFERENCE]->(p) RETURN count(p);
187
+ CYPHER
188
+ )"
189
+ log "step=rehome-prefs WOULD move explicit=$explicit -> owner profile (now has $ownerNow); delete behavioural=$behavioural; remove stray profile"
190
+ return
191
+ fi
192
+ # Move explicit prefs to the owner profile, re-stamp userId.
193
+ local moved
194
+ moved="$(run_cypher <<CYPHER | as_int
195
+ MATCH (stray:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'})-[hp:HAS_PREFERENCE]->(p:Preference) WHERE p.source='explicit'
196
+ MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:HAS_PROFILE]->(op:UserProfile)
197
+ MERGE (op)-[:HAS_PREFERENCE]->(p)
198
+ DELETE hp
199
+ SET p.userId='$OWNER', p.updatedAt=timestamp()
200
+ RETURN count(p);
201
+ CYPHER
202
+ )"
203
+ # Delete behavioural prefs.
204
+ local dropped
205
+ dropped="$(run_cypher <<CYPHER | as_int
206
+ MATCH (:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'})-[:HAS_PREFERENCE]->(p:Preference) WHERE p.source='behavioural'
207
+ DETACH DELETE p RETURN count(p);
208
+ CYPHER
209
+ )"
210
+ # Remove the now-empty stray profile (clears cross-account HAS_PROFILE).
211
+ local strayRemoved
212
+ strayRemoved="$(run_cypher <<CYPHER | as_int
213
+ MATCH (stray:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'}) WHERE NOT (stray)-[:HAS_PREFERENCE]->()
214
+ DETACH DELETE stray RETURN count(*);
215
+ CYPHER
216
+ )"
217
+ local ownerPrefs
218
+ ownerPrefs="$(run_cypher <<CYPHER | as_int
219
+ MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:HAS_PROFILE]->(op:UserProfile)-[:HAS_PREFERENCE]->(p) RETURN count(p);
220
+ CYPHER
221
+ )"
222
+ log "step=rehome-prefs moved=$moved droppedBehavioural=$dropped strayRemoved=$strayRemoved ownerProfilePrefs=$ownerPrefs"
223
+ }
224
+
225
+ step_merge_person() {
226
+ # Survivor = the Dale Person carrying the phone + WhatsApp SENT history (496).
227
+ # Loser = the bare owner-linked Dale stub (338), no phone, no outbound.
228
+ # We re-point the loser's inbound OWNS and RAISED_BY onto the survivor, then
229
+ # delete the stub. This preserves the survivor's hundreds of SENT->Message
230
+ # edges intact (never delete the message-history node).
231
+ local dalePersons stubCount
232
+ dalePersons="$(run_cypher <<CYPHER | as_int
233
+ MATCH (p:Person {accountId:'$ACCT'}) WHERE p.givenName='Dale' AND p.familyName='Smith' RETURN count(p);
234
+ CYPHER
235
+ )"
236
+ if [ "$dalePersons" -le 1 ]; then log "step=merge-person no-op (one Dale Person already)"; return; fi
237
+ # A loser is a BARE Dale stub only: not the phone-holder, no phone of its own,
238
+ # and no SENT history. A real Dale contact with its own phone or messages can
239
+ # never be the loser, so a name collision is left untouched. The grounded
240
+ # premise is exactly one such stub (338); abort on any other count.
241
+ stubCount="$(run_cypher <<CYPHER | as_int
242
+ MATCH (surv:Person {accountId:'$ACCT'}) WHERE surv.givenName='Dale' AND surv.familyName='Smith' AND surv.telephone='$DALE_PHONE'
243
+ MATCH (loser:Person {accountId:'$ACCT'}) WHERE loser.givenName='Dale' AND loser.familyName='Smith' AND elementId(loser) <> elementId(surv) AND loser.telephone IS NULL AND NOT (loser)-[:SENT]->(:Message)
244
+ RETURN count(loser);
245
+ CYPHER
246
+ )"
247
+ if [ "$stubCount" -ne 1 ]; then
248
+ log "step=merge-person ABORT expected exactly one bare Dale stub to merge, found $stubCount (dalePersons=$dalePersons); state drift, inspect before applying"
249
+ exit 3
250
+ fi
251
+ if [ "$MODE" = "dry-run" ]; then
252
+ log "step=merge-person WOULD keep the Dale Person with phone $DALE_PHONE + message history (survivor), re-point the bare stub's OWNS + RAISED_BY onto it, then delete the stub"
253
+ return
254
+ fi
255
+ local movedOwns movedRaised
256
+ movedOwns="$(run_cypher <<CYPHER | as_int
257
+ MATCH (surv:Person {accountId:'$ACCT'}) WHERE surv.givenName='Dale' AND surv.familyName='Smith' AND surv.telephone='$DALE_PHONE'
258
+ MATCH (loser:Person {accountId:'$ACCT'}) WHERE loser.givenName='Dale' AND loser.familyName='Smith' AND elementId(loser) <> elementId(surv) AND loser.telephone IS NULL AND NOT (loser)-[:SENT]->(:Message)
259
+ MATCH (a)-[o:OWNS]->(loser)
260
+ MERGE (a)-[:OWNS]->(surv)
261
+ DELETE o
262
+ RETURN count(*);
263
+ CYPHER
264
+ )"
265
+ movedRaised="$(run_cypher <<CYPHER | as_int
266
+ MATCH (surv:Person {accountId:'$ACCT'}) WHERE surv.givenName='Dale' AND surv.familyName='Smith' AND surv.telephone='$DALE_PHONE'
267
+ MATCH (loser:Person {accountId:'$ACCT'}) WHERE loser.givenName='Dale' AND loser.familyName='Smith' AND elementId(loser) <> elementId(surv) AND loser.telephone IS NULL AND NOT (loser)-[:SENT]->(:Message)
268
+ MATCH (t)-[r:RAISED_BY]->(loser)
269
+ MERGE (t)-[:RAISED_BY]->(surv)
270
+ DELETE r
271
+ RETURN count(*);
272
+ CYPHER
273
+ )"
274
+ # Delete the now-detached bare stub (same narrow predicate: never the survivor,
275
+ # never a Dale with its own phone or message history).
276
+ local deleted
277
+ deleted="$(run_cypher <<CYPHER | as_int
278
+ MATCH (surv:Person {accountId:'$ACCT'}) WHERE surv.givenName='Dale' AND surv.familyName='Smith' AND surv.telephone='$DALE_PHONE'
279
+ MATCH (loser:Person {accountId:'$ACCT'}) WHERE loser.givenName='Dale' AND loser.familyName='Smith' AND elementId(loser) <> elementId(surv) AND loser.telephone IS NULL AND NOT (loser)-[:SENT]->(:Message)
280
+ DETACH DELETE loser
281
+ RETURN count(*);
282
+ CYPHER
283
+ )"
284
+ local sent ownerOwns
285
+ sent="$(run_cypher <<CYPHER | as_int
286
+ MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:OWNS]->(p:Person)-[:SENT]->(:Message) RETURN count(*);
287
+ CYPHER
288
+ )"
289
+ ownerOwns="$(run_cypher <<CYPHER | as_int
290
+ MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:OWNS]->(p:Person) RETURN count(p);
291
+ CYPHER
292
+ )"
293
+ log "step=merge-person survivor=phone+history stubsDeleted=$deleted ownsMoved=$movedOwns raisedByMoved=$movedRaised ownerOwnsPersons=$ownerOwns survivorSentEdges=$sent"
294
+ }
295
+
296
+ step_fold_dale_seat() {
297
+ local present
298
+ present="$(run_cypher <<CYPHER | as_int
299
+ MATCH (a:AdminUser {userId:'$DALE', accountId:'$ACCT'}) RETURN count(a);
300
+ CYPHER
301
+ )"
302
+ if [ "$present" -eq 0 ]; then log "step=fold-dale-seat no-op (seat already folded)"; return; fi
303
+ local startedBy
304
+ startedBy="$(run_cypher <<CYPHER | as_int
305
+ MATCH (x)-[:STARTED_BY]->(a:AdminUser {userId:'$DALE', accountId:'$ACCT'}) RETURN count(x);
306
+ CYPHER
307
+ )"
308
+ if [ "$MODE" = "dry-run" ]; then log "step=fold-dale-seat WOULD re-point startedBy=$startedBy to owner, remove empty profile, delete seat $DALE"; return; fi
309
+ # Re-point inbound STARTED_BY sessions to the owner.
310
+ local moved
311
+ moved="$(run_cypher <<CYPHER | as_int
312
+ MATCH (x)-[r:STARTED_BY]->(dale:AdminUser {userId:'$DALE', accountId:'$ACCT'})
313
+ MATCH (owner:AdminUser {userId:'$OWNER', accountId:'$ACCT'})
314
+ MERGE (x)-[:STARTED_BY]->(owner)
315
+ DELETE r
316
+ RETURN count(x);
317
+ CYPHER
318
+ )"
319
+ # Remove the empty profile (guarded: only if it holds no preferences), then the
320
+ # seat (DETACH DELETE drops its OWNS edges).
321
+ run_cypher <<CYPHER >/dev/null
322
+ MATCH (dale:AdminUser {userId:'$DALE', accountId:'$ACCT'})-[:HAS_PROFILE]->(dp:UserProfile)
323
+ WHERE NOT (dp)-[:HAS_PREFERENCE]->()
324
+ DETACH DELETE dp;
325
+ CYPHER
326
+ run_cypher <<CYPHER >/dev/null
327
+ MATCH (dale:AdminUser {userId:'$DALE', accountId:'$ACCT'}) DETACH DELETE dale;
328
+ CYPHER
329
+ assert_write_gate
330
+ local surviving; surviving="$(write_gate | sed 's/.*admins=\([0-9]*\).*/\1/')"
331
+ log "step=fold-dale-seat startedByMoved=$moved seatRemoved=1 survivingAdminUsers=$surviving"
332
+ }
333
+
334
+ step_retire_joel_seat() {
335
+ local present
336
+ present="$(run_cypher <<CYPHER | as_int
337
+ MATCH (a:AdminUser {userId:'$JOEL', accountId:'$ACCT'}) RETURN count(a);
338
+ CYPHER
339
+ )"
340
+ if [ "$present" -eq 0 ]; then log "step=retire-joel-seat no-op (seat already retired)"; return; fi
341
+ if [ "$MODE" = "dry-run" ]; then log "step=retire-joel-seat WOULD delete seat $JOEL + empty profile, drop stray cross-account OWNS -> house Person 324 (324 kept)"; return; fi
342
+ run_cypher <<CYPHER >/dev/null
343
+ MATCH (joel:AdminUser {userId:'$JOEL', accountId:'$ACCT'})-[:HAS_PROFILE]->(jp:UserProfile)
344
+ WHERE NOT (jp)-[:HAS_PREFERENCE]->()
345
+ DETACH DELETE jp;
346
+ CYPHER
347
+ run_cypher <<CYPHER >/dev/null
348
+ MATCH (joel:AdminUser {userId:'$JOEL', accountId:'$ACCT'}) DETACH DELETE joel;
349
+ CYPHER
350
+ assert_write_gate
351
+ local surviving; surviving="$(write_gate | sed 's/.*admins=\([0-9]*\).*/\1/')"
352
+ log "step=retire-joel-seat seatRemoved=1 housePersonRetained=324 survivingAdminUsers=$surviving"
353
+ }
354
+
355
+ step_keith_timeline() {
356
+ local exists
357
+ exists="$(run_cypher <<CYPHER | as_int
358
+ MATCH (t:Task {remediationKey:'1569-keith-bathroom-tap', accountId:'$ACCT'}) RETURN count(t);
359
+ CYPHER
360
+ )"
361
+ if [ "$exists" -gt 0 ]; then log "step=keith-timeline no-op (task already present)"; return; fi
362
+ if [ "$MODE" = "dry-run" ]; then log "step=keith-timeline WOULD create Task RAISED_BY owner Dale Person, AFFECTS Person Keith Bullot (759)"; return; fi
363
+ local taskId
364
+ taskId="$(run_cypher <<CYPHER | scalar
365
+ MATCH (owner:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:OWNS]->(dale:Person)
366
+ MATCH (keith:Person {accountId:'$ACCT'}) WHERE keith.givenName='Keith' AND keith.familyName='Bullot'
367
+ MERGE (t:Task {remediationKey:'1569-keith-bathroom-tap', accountId:'$ACCT'})
368
+ ON CREATE SET t.taskId=randomUUID(), t.name='Text Keith Bullot about the bathroom tap',
369
+ t.description='Contact Keith Bullot regarding the bathroom tap.', t.status='pending', t.priority='normal',
370
+ t.createdAt=timestamp(), t.updatedAt=timestamp(),
371
+ t.createdByTool='remediate-glsmith-identity', t.createdBySession='task-1569', t.createdByAgent='id-remediation'
372
+ MERGE (t)-[:AFFECTS]->(keith)
373
+ MERGE (t)-[:RAISED_BY]->(dale)
374
+ RETURN t.taskId;
375
+ CYPHER
376
+ )"
377
+ log "step=keith-timeline taskCreated=1 taskId=$taskId raisedBy=owner-dale-person affects=keith-759"
378
+ }
379
+
380
+ step_account_admins() {
381
+ [ -f "$ACCOUNT_JSON" ] || { log "step=account-admins SKIP (account.json not found at $ACCOUNT_JSON)"; return; }
382
+ local already
383
+ already="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print("yes" if d.get("admins")==[] else "no")' "$ACCOUNT_JSON")"
384
+ if [ "$already" = "yes" ]; then log "step=account-admins no-op (already [])"; return; fi
385
+ if [ "$MODE" = "dry-run" ]; then
386
+ local cur; cur="$(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print([a.get("userId","")[:8] for a in d.get("admins",[])])' "$ACCOUNT_JSON")"
387
+ log "step=account-admins WOULD set admins [] (currently $cur)"; return
388
+ fi
389
+ local ts; ts="$(date +%Y%m%d-%H%M%S)"
390
+ cp "$ACCOUNT_JSON" "$ACCOUNT_JSON.bak-$ts"
391
+ python3 -c 'import json,sys
392
+ p=sys.argv[1]; d=json.load(open(p)); d["admins"]=[]
393
+ json.dump(d, open(p,"w"), indent=2)' "$ACCOUNT_JSON"
394
+ log "step=account-admins admins=[] backup=$ACCOUNT_JSON.bak-$ts"
395
+ }
396
+
397
+ step_reembed_needed() {
398
+ local keithTask
399
+ keithTask="$(run_cypher <<CYPHER | scalar
400
+ MATCH (t:Task {remediationKey:'1569-keith-bathroom-tap', accountId:'$ACCT'}) RETURN t.taskId;
401
+ CYPHER
402
+ )"
403
+ log "step=reembed-needed nodes=\"AdminUser:$OWNER(name-changed),Task:${keithTask:-<pending>}(new)\" note=raw-cypher-writes-carry-no-embedding"
404
+ }
405
+
406
+ # --- Rollback guard + snapshot (apply only) -------------------------
407
+ rollback_guard() {
408
+ if [ -n "$DUMP_PATH" ]; then
409
+ [ -s "$DUMP_PATH" ] || { echo "Error: --dump path '$DUMP_PATH' is missing or empty" >&2; exit 1; }
410
+ log "rollback dump provided path=$DUMP_PATH bytes=$(wc -c < "$DUMP_PATH")"
411
+ elif [ "$FORCE_NO_DUMP" -eq 1 ]; then
412
+ log "WARNING proceeding WITHOUT a neo4j dump (--force-no-dump); JSON snapshot is the only rollback reference"
413
+ else
414
+ echo "Error: --apply needs --dump=/path/to/neo4j.dump (fresh full dump) or --force-no-dump" >&2
415
+ echo " Take a dump on the box, e.g.:" >&2
416
+ echo " systemctl stop neo4j-sitedesk-code.service && \\" >&2
417
+ echo " neo4j-admin database dump neo4j --to-path=\$HOME/glsmith-1569-dump && \\" >&2
418
+ echo " systemctl start neo4j-sitedesk-code.service" >&2
419
+ exit 1
420
+ fi
421
+ }
422
+
423
+ write_snapshot() {
424
+ local ts snap
425
+ ts="$(date +%Y%m%d-%H%M%S)"; snap="$HOME/glsmith-1569-snapshot-$ts.json"
426
+ # Nodes: the whole sub-account plus the house-side nodes this script touches
427
+ # (house Person 324 via Joel's phone, house Dale Person 194 via Dale's phone,
428
+ # house operator 436fdf2d whose cross-account HAS_PROFILE is cleared).
429
+ run_cypher <<CYPHER > "$snap.nodes"
430
+ MATCH (n) WHERE n.accountId='$ACCT'
431
+ OR (n:Person AND n.telephone IN ['$DALE_PHONE','447504472444'])
432
+ OR (n:AdminUser AND n.userId IN ['$OWNER','$JOEL','$DALE','$HOUSEOP'])
433
+ RETURN elementId(n) AS eid, labels(n) AS labels, properties(n) AS props;
434
+ CYPHER
435
+ # Rels: every relationship anchored on a touched node, with its properties, so
436
+ # a re-pointed or dropped edge can be recreated exactly.
437
+ run_cypher <<CYPHER > "$snap.rels"
438
+ MATCH (n)-[r]-(m) WHERE n.accountId='$ACCT'
439
+ OR (n:AdminUser AND n.userId IN ['$OWNER','$JOEL','$DALE','$HOUSEOP'])
440
+ OR (n:Person AND n.telephone IN ['$DALE_PHONE','447504472444'])
441
+ RETURN DISTINCT type(r) AS type, elementId(startNode(r)) AS start, elementId(endNode(r)) AS end, properties(r) AS props;
442
+ CYPHER
443
+ log "snapshot nodes+rels written prefix=$snap"
444
+ }
445
+
446
+ # --- Verify ----------------------------------------------------------
447
+ verify() {
448
+ local fail=0
449
+ check() { # desc, actual, expected
450
+ if [ "$2" = "$3" ]; then log "verify OK $1 ($2)"; else log "verify FAIL $1 (got '$2' want '$3')"; fail=1; fi
451
+ }
452
+ local ownerCount ownerName ownerOwns ownerPrefs strayCount dalePersons joelCount daleCount gate
453
+ ownerCount="$(run_cypher <<< "MATCH (a:AdminUser {accountId:'$ACCT', role:'owner'}) RETURN count(a);" | as_int)"
454
+ ownerName="$(run_cypher <<< "MATCH (a:AdminUser {accountId:'$ACCT', role:'owner'}) RETURN a.name;" | scalar)"
455
+ ownerOwns="$(run_cypher <<< "MATCH (a:AdminUser {accountId:'$ACCT', role:'owner'})-[:OWNS]->(p:Person) RETURN count(p);" | as_int)"
456
+ ownerPrefs="$(run_cypher <<< "MATCH (:AdminUser {userId:'$OWNER', accountId:'$ACCT'})-[:HAS_PROFILE]->(op)-[:HAS_PREFERENCE]->(p) RETURN count(p);" | as_int)"
457
+ strayCount="$(run_cypher <<< "MATCH (u:UserProfile {userId:'$HOUSEOP', accountId:'$ACCT'}) RETURN count(u);" | as_int)"
458
+ dalePersons="$(run_cypher <<< "MATCH (p:Person {accountId:'$ACCT'}) WHERE p.givenName='Dale' AND p.familyName='Smith' RETURN count(p);" | as_int)"
459
+ joelCount="$(run_cypher <<< "MATCH (a:AdminUser {userId:'$JOEL', accountId:'$ACCT'}) RETURN count(a);" | as_int)"
460
+ daleCount="$(run_cypher <<< "MATCH (a:AdminUser {userId:'$DALE', accountId:'$ACCT'}) RETURN count(a);" | as_int)"
461
+ gate="$(write_gate || true)"
462
+ check "one role:owner AdminUser" "$ownerCount" "1"
463
+ check "owner named Dale Smith" "$ownerName" "Dale Smith"
464
+ check "owner owns exactly one Person" "$ownerOwns" "1"
465
+ check "owner profile pref count" "$ownerPrefs" "28"
466
+ check "stray profile removed" "$strayCount" "0"
467
+ check "one Dale Person on account" "$dalePersons" "1"
468
+ check "Joel seat absent" "$joelCount" "0"
469
+ check "Dale seat absent" "$daleCount" "0"
470
+ case "$gate" in *"owners=1 business=1"*) log "verify OK write-gate ($gate)";; *) log "verify FAIL write-gate ($gate)"; fail=1;; esac
471
+ [ "$fail" -eq 0 ] && { log "verify PASS"; return 0; } || { log "verify FAILED"; return 1; }
472
+ }
473
+
474
+ # --- Main ------------------------------------------------------------
475
+ log "start mode=$MODE account=$ACCT uri=$URI"
476
+
477
+ if [ "$MODE" = "verify" ]; then verify; exit $?; fi
478
+
479
+ preconditions
480
+
481
+ if [ "$MODE" = "apply" ]; then
482
+ rollback_guard
483
+ write_snapshot
484
+ fi
485
+
486
+ step_rename_owner
487
+ step_rehome_prefs
488
+ step_merge_person
489
+ step_fold_dale_seat
490
+ step_retire_joel_seat
491
+ step_keith_timeline
492
+ step_account_admins
493
+ step_reembed_needed
494
+
495
+ if [ "$MODE" = "apply" ]; then
496
+ log "apply complete — running verify"
497
+ verify || { log "POST-APPLY VERIFY FAILED — inspect before trusting"; exit 4; }
498
+ else
499
+ log "dry-run complete — no writes performed. Diff the WOULD lines against the target end-state, then re-run with --apply --dump=<path>."
500
+ fi
@@ -220,30 +220,46 @@ fi
220
220
 
221
221
  if [ -n "$USER_ID" ]; then
222
222
  # Escape single quotes for Cypher string interpolation (e.g. O'Brien → O''Brien).
223
- USER_NAME=$(python3 -c "import json; n=json.load(open('$USERS_FILE'))[0]['name']; print(n.replace(\"'\",\"''\"))" 2>/dev/null || echo "Owner")
224
- CREATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
223
+ USER_NAME=$(python3 -c "import json; n=json.load(open('$USERS_FILE'))[0]['name']; print(n.replace(\"'\",\"''\"))" 2>/dev/null || true)
224
+ # Trim surrounding whitespace so a padded value is neither persisted verbatim
225
+ # nor able to slip a padded " Owner " past the placeholder check below — the
226
+ # same trim the shared isPlaceholderOwnerName predicate applies.
227
+ USER_NAME=$(printf '%s' "$USER_NAME" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
228
+ # Task 1570 — the AdminUser carries no name; the owner name lives only on the
229
+ # OWNS-Person that set-pin's writeAdminUserAndPerson already wrote (1567 made a
230
+ # real name mandatory there). This block only stamps accountId/role and the
231
+ # ADMIN_OF edge on that existing AdminUser. A blank or "Owner" USER_NAME means
232
+ # the install cannot supply a real owner identity, so SKIP the stamp loudly
233
+ # rather than anchor an owner seat to a placeholder. Skip, not exit: this block
234
+ # only runs when users.json exists, so the real node is untouched, the per-deploy
235
+ # reconciles below still run (including the au.name -> Person backfill), and the
236
+ # standing [identity-audit] surfaces any orphan/nameless owner. Aborting the
237
+ # whole deploy here would brick re-deploys of exactly the installs to remediate.
238
+ if [ -z "$USER_NAME" ] || [ "$USER_NAME" = "Owner" ]; then
239
+ echo " [seed] WARN: owner name is blank or the placeholder \"Owner\" (users.json[0].name for userId=${USER_ID:0:8}) — skipping owner AdminUser seed. Set a real name (change-PIN) to reconcile; the standing [identity-audit] flags it meanwhile." >&2
240
+ else
241
+ CREATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
225
242
 
226
- echo "==> Creating AdminUser node for userId=$USER_ID"
227
- # Stamp accountId + role='owner' at seed so the graph-write gate's
228
- # account-scoped MATCH (au:AdminUser {accountId: $accountId}) succeeds on the
229
- # first user-domain write. The ON MATCH branch COALESCEs to preserve any
230
- # existing non-null value.
231
- "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" << CYPHER_EOF
243
+ echo "==> Creating AdminUser node for userId=$USER_ID"
244
+ # Stamp accountId + role='owner' at seed so the graph-write gate's
245
+ # account-scoped MATCH (au:AdminUser {accountId: $accountId}) succeeds on the
246
+ # first user-domain write. The ON MATCH branch COALESCEs to preserve any
247
+ # existing non-null value.
248
+ "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" << CYPHER_EOF
232
249
  MERGE (au:AdminUser {userId: '$USER_ID'})
233
250
  ON CREATE SET au.accountId = '$ACCOUNT_ID',
234
251
  au.role = 'owner',
235
- au.name = '$USER_NAME',
236
252
  au.createdAt = '$CREATED_AT'
237
253
  ON MATCH SET au.accountId = COALESCE(au.accountId, '$ACCOUNT_ID'),
238
- au.role = COALESCE(au.role, 'owner'),
239
- au.name = '$USER_NAME'
254
+ au.role = COALESCE(au.role, 'owner')
240
255
  WITH au
241
256
  MATCH (b:LocalBusiness {accountId: '$ACCOUNT_ID'})
242
257
  MERGE (au)-[r:ADMIN_OF]->(b)
243
258
  ON CREATE SET r.role = 'owner', r.grantedAt = '$CREATED_AT'
244
259
  RETURN au.userId, b.accountId;
245
260
  CYPHER_EOF
246
- echo " [seed] AdminUser stamped userId=${USER_ID:0:8} accountId=${ACCOUNT_ID:0:8} role=owner"
261
+ echo " [seed] AdminUser stamped userId=${USER_ID:0:8} accountId=${ACCOUNT_ID:0:8} role=owner"
262
+ fi
247
263
  else
248
264
  echo " No users.json found — skipping AdminUser seed (set-pin POST creates it)"
249
265
  fi
@@ -304,4 +320,53 @@ PROG_TOTAL=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_UR
304
320
  "MATCH (s:Sale {accountId: '$ACCOUNT_ID'}) RETURN count(s)" 2>/dev/null | tail -1 | tr -d ' \r')
305
321
  echo " [progression-reconcile] accountId=${ACCOUNT_ID:0:8} stalledSales=${PROG_STALLED:-0} orphanChains=${PROG_ORPHAN_CHAINS:-0} linklessChains=${PROG_LINKLESS:-0} total=${PROG_TOTAL:-0}"
306
322
 
323
+ # ------------------------------------------------------------------
324
+ # 7. One-time reconcile — AdminUser.name -> owned Person, then drop au.name (Task 1570)
325
+ # ------------------------------------------------------------------
326
+ # AdminUser carries no name; the owned Person is the sole name source. This heals
327
+ # legacy nodes globally (every account in the shared graph, not just this one).
328
+ # It must NOT touch an AdminUser that already has a named owned Person (the common
329
+ # case: a house owner with a real "Joel Smalley" Person) — only 7c drops au.name
330
+ # there. So the migrate is split so it never MERGE-duplicates a name-keyed Person:
331
+ # 7a — an owned Person exists but is nameless: populate its name.
332
+ # 7b — no owned Person exists at all: create one carrying the name.
333
+ # Owner/admin-personal Persons are keyed on {accountId} + name (writeAdminUserAndPerson,
334
+ # admin-add) or {accountId, userId} (seedAccountGraphRoot); keying a MERGE on
335
+ # {accountId, userId} would miss the former and create a duplicate, so 7a SETs the
336
+ # existing node and only 7b CREATEs (for a genuine orphan). Placeholder names
337
+ # ("Owner"/"Admin"/blank) are excluded from 7a/7b and simply dropped by 7c.
338
+ # Idempotent: once au.name is gone all three match nothing, so re-deploys are
339
+ # no-ops. Split mirrors writeAdminUserAndPerson (first ASCII space: head ->
340
+ # givenName, tail -> familyName); operator names never carry embedded tab/NBSP.
341
+ # Visibility only — never install-blocking.
342
+ IDENT_NAMED=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
343
+ --format plain \
344
+ "MATCH (au:AdminUser)-[:OWNS]->(p:Person)
345
+ WHERE p.accountId = au.accountId
346
+ AND (p.givenName IS NULL OR trim(p.givenName) = '')
347
+ AND au.name IS NOT NULL
348
+ AND trim(au.name) <> '' AND trim(au.name) <> 'Owner' AND trim(au.name) <> 'Admin'
349
+ WITH p, trim(au.name) AS nm
350
+ SET p.givenName = CASE WHEN nm CONTAINS ' ' THEN trim(split(nm, ' ')[0]) ELSE nm END,
351
+ p.familyName = CASE WHEN nm CONTAINS ' ' THEN trim(substring(nm, size(split(nm, ' ')[0]) + 1)) ELSE null END
352
+ RETURN count(p)" 2>/dev/null | tail -1 | tr -d ' \r')
353
+ IDENT_CREATED=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
354
+ --format plain \
355
+ "MATCH (au:AdminUser)
356
+ WHERE au.name IS NOT NULL
357
+ AND trim(au.name) <> '' AND trim(au.name) <> 'Owner' AND trim(au.name) <> 'Admin'
358
+ AND au.accountId IS NOT NULL
359
+ AND NOT EXISTS { MATCH (au)-[:OWNS]->(p:Person) WHERE p.accountId = au.accountId }
360
+ WITH au, trim(au.name) AS nm
361
+ CREATE (au)-[:OWNS]->(np:Person {
362
+ accountId: au.accountId, userId: au.userId, role: 'admin-personal', scope: 'admin', createdAt: datetime(),
363
+ givenName: CASE WHEN nm CONTAINS ' ' THEN trim(split(nm, ' ')[0]) ELSE nm END,
364
+ familyName: CASE WHEN nm CONTAINS ' ' THEN trim(substring(nm, size(split(nm, ' ')[0]) + 1)) ELSE null END
365
+ })
366
+ RETURN count(np)" 2>/dev/null | tail -1 | tr -d ' \r')
367
+ IDENT_DROPPED=$("$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
368
+ --format plain \
369
+ "MATCH (au:AdminUser) WHERE au.name IS NOT NULL REMOVE au.name RETURN count(au)" 2>/dev/null | tail -1 | tr -d ' \r')
370
+ echo " [identity-reconcile] namedExisting=${IDENT_NAMED:-0} createdOwned=${IDENT_CREATED:-0} droppedAuName=${IDENT_DROPPED:-0}"
371
+
307
372
  echo " Done."
@@ -1 +1 @@
1
- {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}