repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
The Living OS cockpit
stars
latest
clone command
git clone gitlawb://did:key:z6Mku78K...XywC/living-os-cockp...git clone gitlawb://did:key:z6Mku78K.../living-os-cockp...59751530feat: surface worker supervisor health in live work8h ago| #1 | import { promises as fs } from 'fs'; |
| #2 | import path from 'path'; |
| #3 | import { NextRequest, NextResponse } from 'next/server'; |
| #4 | import { authErrorResponse, CANONICAL_KING_ID, getUserContext } from '@/lib/user-context'; |
| #5 | |
| #6 | export const runtime = 'nodejs'; |
| #7 | |
| #8 | const KING_TRUST_ROOT = '/home/kingbau/Aethon-Core-mac-backup-20260509/data/trust/users/HED-TRUST-2025-001'; |
| #9 | const LEDGER_PATH = '/home/kingbau/Documents/Aethon-Core/data/continuity_ledger.jsonl'; |
| #10 | const PROFILE_STORE_ROOT = '/home/kingbau/.local/share/living-os-cockpit/profiles'; |
| #11 | const PROFILE_PHOTO_ROOT = '/home/kingbau/.local/share/living-os-cockpit/profile-photos'; |
| #12 | const MEMBER_REGISTRY_PATH = '/home/kingbau/.local/share/theliving-ai/registry/members.json'; |
| #13 | const LEGACY_WRONG_TRUST_NAME = 'House of Edinburgh Private Express Trust'; |
| #14 | |
| #15 | type TrustProfile = { |
| #16 | trust_id?: string; |
| #17 | name?: string; |
| #18 | status?: string; |
| #19 | ucc_filing?: string; |
| #20 | ucc_filing_state?: string; |
| #21 | trustee?: { name?: string }; |
| #22 | governing_law?: string; |
| #23 | }; |
| #24 | |
| #25 | type TrustBranding = { |
| #26 | primary_color?: string; |
| #27 | secondary_color?: string; |
| #28 | seal_url?: string | null; |
| #29 | }; |
| #30 | |
| #31 | type PaidTier = { |
| #32 | id?: string; |
| #33 | label?: string; |
| #34 | detail?: string; |
| #35 | }; |
| #36 | |
| #37 | type PmaStatus = { |
| #38 | feeUsd: number; |
| #39 | status: 'paid' | 'pending'; |
| #40 | renewalLabel: string; |
| #41 | }; |
| #42 | |
| #43 | type CooperativePool = { |
| #44 | phase: string; |
| #45 | poolPercent: number; |
| #46 | operatorPercent: number; |
| #47 | donationPercent: number; |
| #48 | memberShareLabel: string; |
| #49 | memberSharePercent: number; |
| #50 | }; |
| #51 | |
| #52 | type PersistedProfile = { |
| #53 | displayName?: string; |
| #54 | portraitLabel?: string; |
| #55 | paidTier?: PaidTier; |
| #56 | pma?: Partial<PmaStatus>; |
| #57 | cooperativePool?: Partial<CooperativePool>; |
| #58 | trust?: { |
| #59 | name?: string; |
| #60 | id?: string | null; |
| #61 | role?: string | null; |
| #62 | uccFiling?: string | null; |
| #63 | filingState?: string | null; |
| #64 | governingLaw?: string | null; |
| #65 | primaryColor?: string; |
| #66 | secondaryColor?: string; |
| #67 | sealUrl?: string | null; |
| #68 | }; |
| #69 | }; |
| #70 | |
| #71 | type ProfileResponse = { |
| #72 | userId: string; |
| #73 | displayName: string; |
| #74 | editable: boolean; |
| #75 | portraitLabel: string; |
| #76 | portraitUrl: string | null; |
| #77 | tierOptions: typeof TIER_OPTIONS; |
| #78 | paidTier: PaidTier; |
| #79 | pma: PmaStatus; |
| #80 | cooperativePool: CooperativePool; |
| #81 | sovereignStatus: { |
| #82 | id: string; |
| #83 | label: string; |
| #84 | nextMilestone: string; |
| #85 | }; |
| #86 | xp: { |
| #87 | current: number; |
| #88 | next: number; |
| #89 | }; |
| #90 | trust: { |
| #91 | name: string; |
| #92 | id?: string | null; |
| #93 | role?: string | null; |
| #94 | uccFiling?: string | null; |
| #95 | filingState?: string | null; |
| #96 | governingLaw?: string | null; |
| #97 | primaryColor: string; |
| #98 | secondaryColor: string; |
| #99 | sealUrl?: string | null; |
| #100 | }; |
| #101 | stats: { |
| #102 | posts: number; |
| #103 | interactions: number; |
| #104 | activeCampaigns: number; |
| #105 | openThreads: number; |
| #106 | }; |
| #107 | activeCampaigns: { id: string; title: string }[]; |
| #108 | badges: { label: string; kind: string }[]; |
| #109 | }; |
| #110 | |
| #111 | type RegistryMember = { |
| #112 | badge?: string; |
| #113 | badges?: string[]; |
| #114 | founding?: { |
| #115 | badge?: string; |
| #116 | origin_badge?: string; |
| #117 | }; |
| #118 | origin_founder?: boolean; |
| #119 | origin_number?: string; |
| #120 | }; |
| #121 | |
| #122 | type MemberRegistry = { |
| #123 | members?: Record<string, RegistryMember>; |
| #124 | }; |
| #125 | |
| #126 | const PROFILE_MEMBER_IDS: Record<string, string> = { |
| #127 | master: 'ORIGIN-0-00', |
| #128 | kingbau: 'ORIGIN-0-00', |
| #129 | 'ed3c37e8-715f-4f62-9823-05ec234bf073': 'ORIGIN-0-00', |
| #130 | queen_maaxx: 'ORIGIN-0-01', |
| #131 | maaxx: 'ORIGIN-0-01', |
| #132 | 'be4d6c49-6fca-4796-b13e-deb3af4d3ce6': 'ORIGIN-0-01', |
| #133 | }; |
| #134 | |
| #135 | const BADGE_LABELS: Record<string, string> = { |
| #136 | founder_executor: 'Black Origin', |
| #137 | origin_founder: 'Origin Founder', |
| #138 | founder_sovereign_court: 'Sovereign Court Founder', |
| #139 | founder_dragon: 'Founding Dragon', |
| #140 | founder_sovereign: 'Founding Sovereign', |
| #141 | }; |
| #142 | |
| #143 | async function readJson<T>(filePath: string): Promise<T | null> { |
| #144 | try { |
| #145 | return JSON.parse(await fs.readFile(filePath, 'utf8')) as T; |
| #146 | } catch { |
| #147 | return null; |
| #148 | } |
| #149 | } |
| #150 | |
| #151 | function normalizeBadgeId(value: unknown, member?: RegistryMember): string | null { |
| #152 | if (typeof value !== 'string') return null; |
| #153 | const badge = value.trim().toLowerCase(); |
| #154 | if (!badge) return null; |
| #155 | if (badge === 'founder' && member?.origin_number === '0.00') return 'founder_executor'; |
| #156 | if (badge === 'founder') return 'origin_founder'; |
| #157 | return badge.replace(/[^a-z0-9_:-]+/g, '_'); |
| #158 | } |
| #159 | |
| #160 | function formatBadgeLabel(badgeId: string) { |
| #161 | return BADGE_LABELS[badgeId] ?? badgeId |
| #162 | .replace(/_/g, ' ') |
| #163 | .split(' ') |
| #164 | .filter(Boolean) |
| #165 | .map(part => part.charAt(0).toUpperCase() + part.slice(1)) |
| #166 | .join(' '); |
| #167 | } |
| #168 | |
| #169 | function profileBadgesFromRegistry(member: RegistryMember | null, fallback: { label: string; kind: string }[]) { |
| #170 | if (!member) return fallback; |
| #171 | const ids = new Set<string>(); |
| #172 | const add = (value: unknown) => { |
| #173 | const badge = normalizeBadgeId(value, member); |
| #174 | if (badge) ids.add(badge); |
| #175 | }; |
| #176 | |
| #177 | if (member.origin_founder) { |
| #178 | add(member.origin_number === '0.00' ? 'founder_executor' : 'origin_founder'); |
| #179 | } |
| #180 | add(member.badge); |
| #181 | add(member.founding?.badge); |
| #182 | add(member.founding?.origin_badge); |
| #183 | for (const badge of member.badges ?? []) add(badge); |
| #184 | |
| #185 | const badges = Array.from(ids).map(id => ({ |
| #186 | label: formatBadgeLabel(id), |
| #187 | kind: id, |
| #188 | })); |
| #189 | return badges.length > 0 ? badges : fallback; |
| #190 | } |
| #191 | |
| #192 | async function readRegistryMemberForUser(userId: string): Promise<RegistryMember | null> { |
| #193 | const memberId = PROFILE_MEMBER_IDS[userId]; |
| #194 | if (!memberId) return null; |
| #195 | const registry = await readJson<MemberRegistry>(MEMBER_REGISTRY_PATH); |
| #196 | return registry?.members?.[memberId] ?? null; |
| #197 | } |
| #198 | |
| #199 | function titleFromProjectMarkdown(contents: string, fallback: string) { |
| #200 | const titleMatch = contents.match(/^title:\s*"?([^"\n]+)"?/m); |
| #201 | if (titleMatch?.[1]) return titleMatch[1].trim(); |
| #202 | |
| #203 | const headingMatch = contents.match(/^#\s+(.+)$/m); |
| #204 | if (headingMatch?.[1]) return headingMatch[1].trim(); |
| #205 | |
| #206 | return fallback |
| #207 | .replace(/\.md$/i, '') |
| #208 | .split('_') |
| #209 | .map(part => part.charAt(0).toUpperCase() + part.slice(1)) |
| #210 | .join(' '); |
| #211 | } |
| #212 | |
| #213 | async function readActiveProjects(vaultPath: string) { |
| #214 | const projectDir = path.join(vaultPath, 'active_projects'); |
| #215 | try { |
| #216 | const names = await fs.readdir(projectDir); |
| #217 | const projects = await Promise.all( |
| #218 | names |
| #219 | .filter(name => name.endsWith('.md')) |
| #220 | .sort() |
| #221 | .map(async name => { |
| #222 | const contents = await fs.readFile(path.join(projectDir, name), 'utf8'); |
| #223 | return { |
| #224 | id: name.replace(/\.md$/i, ''), |
| #225 | title: titleFromProjectMarkdown(contents, name), |
| #226 | }; |
| #227 | }) |
| #228 | ); |
| #229 | return projects; |
| #230 | } catch { |
| #231 | return []; |
| #232 | } |
| #233 | } |
| #234 | |
| #235 | async function countLedgerInteractions(userId: string) { |
| #236 | try { |
| #237 | const lines = (await fs.readFile(LEDGER_PATH, 'utf8')).split('\n').filter(Boolean); |
| #238 | return lines.reduce((count, line) => { |
| #239 | try { |
| #240 | const entry = JSON.parse(line); |
| #241 | const user = String(entry.user_name ?? entry.user_key ?? entry.user ?? '').toLowerCase(); |
| #242 | if (userId === 'queen_maaxx') { |
| #243 | return user.includes('maaxx') || user.startsWith('queen') ? count + 1 : count; |
| #244 | } |
| #245 | return user.includes('king') || user.includes('joel') || user.includes('bauman') || user === 'master' |
| #246 | ? count + 1 |
| #247 | : count; |
| #248 | } catch { |
| #249 | return count; |
| #250 | } |
| #251 | }, 0); |
| #252 | } catch { |
| #253 | return 0; |
| #254 | } |
| #255 | } |
| #256 | |
| #257 | const STATUS_THRESHOLDS: Record<string, number> = { |
| #258 | initiate: 0, |
| #259 | awakening: 250, |
| #260 | living: 750, |
| #261 | secured: 1500, |
| #262 | executor: 3000, |
| #263 | dragon: 5000, |
| #264 | }; |
| #265 | |
| #266 | function xpForStatus(status: string) { |
| #267 | return STATUS_THRESHOLDS[status] ?? STATUS_THRESHOLDS.initiate; |
| #268 | } |
| #269 | |
| #270 | function nextXpForStatus(status: string) { |
| #271 | const order = ['initiate', 'awakening', 'living', 'secured', 'executor', 'dragon']; |
| #272 | const index = Math.max(0, order.indexOf(status)); |
| #273 | const next = order[Math.min(order.length - 1, index + 1)]; |
| #274 | return xpForStatus(next); |
| #275 | } |
| #276 | |
| #277 | function profilePath(userId: string) { |
| #278 | return path.join(PROFILE_STORE_ROOT, `${userId}.json`); |
| #279 | } |
| #280 | |
| #281 | async function profilePhotoUrl(userId: string) { |
| #282 | const filePath = path.join(PROFILE_PHOTO_ROOT, `${userId}.image`); |
| #283 | try { |
| #284 | const stat = await fs.stat(filePath); |
| #285 | return `/api/profile/photo?ts=${Math.round(stat.mtimeMs)}`; |
| #286 | } catch { |
| #287 | return null; |
| #288 | } |
| #289 | } |
| #290 | |
| #291 | async function readPersistedProfile(userId: string): Promise<PersistedProfile> { |
| #292 | return (await readJson<PersistedProfile>(profilePath(userId))) ?? {}; |
| #293 | } |
| #294 | |
| #295 | async function writePersistedProfile(userId: string, profile: PersistedProfile) { |
| #296 | await fs.mkdir(PROFILE_STORE_ROOT, { recursive: true }); |
| #297 | const filePath = profilePath(userId); |
| #298 | await fs.writeFile(filePath, `${JSON.stringify(profile, null, 2)}\n`, 'utf8'); |
| #299 | await fs.chmod(filePath, 0o600); |
| #300 | } |
| #301 | |
| #302 | function cleanText(value: unknown, fallback = '', max = 160) { |
| #303 | if (typeof value !== 'string') return fallback; |
| #304 | const cleaned = value.replace(/\s+/g, ' ').trim(); |
| #305 | return cleaned ? cleaned.slice(0, max) : fallback; |
| #306 | } |
| #307 | |
| #308 | function cleanNullableText(value: unknown, max = 160) { |
| #309 | if (value === null) return null; |
| #310 | const cleaned = cleanText(value, '', max); |
| #311 | return cleaned || null; |
| #312 | } |
| #313 | |
| #314 | function cleanColor(value: unknown, fallback: string) { |
| #315 | if (typeof value === 'string' && /^#[0-9A-Fa-f]{6}$/.test(value.trim())) return value.trim(); |
| #316 | return fallback; |
| #317 | } |
| #318 | |
| #319 | function cleanNumber(value: unknown, fallback: number, min = 0, max = 100) { |
| #320 | const numeric = Number(value); |
| #321 | if (!Number.isFinite(numeric)) return fallback; |
| #322 | return Math.max(min, Math.min(max, numeric)); |
| #323 | } |
| #324 | |
| #325 | function cleanPaidTier(value: unknown, fallback: PaidTier): PaidTier { |
| #326 | const source = value && typeof value === 'object' ? value as Record<string, unknown> : {}; |
| #327 | const tierId = cleanText(source.id, fallback.id ?? 'founding_court', 48); |
| #328 | const option = TIER_OPTIONS.find(item => item.id === tierId); |
| #329 | return { |
| #330 | id: option?.id ?? tierId, |
| #331 | label: cleanText(source.label, option?.label ?? fallback.label ?? 'Founding', 48), |
| #332 | detail: cleanText(source.detail, option?.detail ?? fallback.detail ?? 'Sovereign Court', 96), |
| #333 | }; |
| #334 | } |
| #335 | |
| #336 | function cleanPma(value: unknown, fallback: PmaStatus): PmaStatus { |
| #337 | const source = value && typeof value === 'object' ? value as Record<string, unknown> : {}; |
| #338 | const status = source.status === 'paid' ? 'paid' : source.status === 'pending' ? 'pending' : fallback.status ?? 'pending'; |
| #339 | return { |
| #340 | feeUsd: cleanNumber(source.feeUsd, fallback.feeUsd ?? 33.33, 0, 9999), |
| #341 | status, |
| #342 | renewalLabel: cleanText(source.renewalLabel, fallback.renewalLabel ?? 'Annual PMA membership', 96), |
| #343 | }; |
| #344 | } |
| #345 | |
| #346 | function cleanCooperativePool(value: unknown, fallback: CooperativePool): CooperativePool { |
| #347 | const source = value && typeof value === 'object' ? value as Record<string, unknown> : {}; |
| #348 | return { |
| #349 | phase: cleanText(source.phase, fallback.phase ?? 'Ceiling Split', 64), |
| #350 | poolPercent: cleanNumber(source.poolPercent, fallback.poolPercent ?? 91, 0, 100), |
| #351 | operatorPercent: cleanNumber(source.operatorPercent, fallback.operatorPercent ?? 6, 0, 100), |
| #352 | donationPercent: cleanNumber(source.donationPercent, fallback.donationPercent ?? 3, 0, 100), |
| #353 | memberShareLabel: cleanText(source.memberShareLabel, fallback.memberShareLabel ?? 'Member cooperative pool', 96), |
| #354 | memberSharePercent: cleanNumber(source.memberSharePercent, fallback.memberSharePercent ?? 0, 0, 100), |
| #355 | }; |
| #356 | } |
| #357 | |
| #358 | function sanitizePatch(payload: unknown, current: ProfileResponse): PersistedProfile { |
| #359 | const source = payload && typeof payload === 'object' ? payload as Record<string, unknown> : {}; |
| #360 | const trustSource = source.trust && typeof source.trust === 'object' ? source.trust as Record<string, unknown> : {}; |
| #361 | return { |
| #362 | displayName: cleanText(source.displayName, current.displayName, 96), |
| #363 | portraitLabel: cleanText(source.portraitLabel, current.portraitLabel, 8).toUpperCase(), |
| #364 | paidTier: cleanPaidTier(source.paidTier, current.paidTier), |
| #365 | pma: cleanPma(source.pma, current.pma), |
| #366 | cooperativePool: cleanCooperativePool(source.cooperativePool, current.cooperativePool), |
| #367 | trust: { |
| #368 | name: cleanText(trustSource.name, current.trust.name, 160), |
| #369 | id: cleanNullableText(trustSource.id, 96), |
| #370 | role: cleanNullableText(trustSource.role, 96), |
| #371 | uccFiling: cleanNullableText(trustSource.uccFiling, 96), |
| #372 | filingState: cleanNullableText(trustSource.filingState, 64), |
| #373 | governingLaw: cleanNullableText(trustSource.governingLaw, 160), |
| #374 | primaryColor: cleanColor(trustSource.primaryColor, current.trust.primaryColor), |
| #375 | secondaryColor: cleanColor(trustSource.secondaryColor, current.trust.secondaryColor), |
| #376 | sealUrl: current.trust.sealUrl ?? null, |
| #377 | }, |
| #378 | }; |
| #379 | } |
| #380 | |
| #381 | const TIER_OPTIONS = [ |
| #382 | { id: 'founding_court', label: 'Founding', detail: 'Sovereign Court' }, |
| #383 | { id: 'sovereign_court', label: 'Sovereign Court', detail: 'Premium consultation tier' }, |
| #384 | { id: 'dragon', label: 'Dragon', detail: 'Expanded case capacity' }, |
| #385 | { id: 'sovereign', label: 'Sovereign', detail: 'Private member tier' }, |
| #386 | { id: 'free', label: 'Free', detail: 'PMA member entry' }, |
| #387 | ]; |
| #388 | |
| #389 | function safeTrustName(name: string | undefined | null) { |
| #390 | const cleaned = cleanText(name, '', 160); |
| #391 | if (!cleaned || cleaned === LEGACY_WRONG_TRUST_NAME) return 'King Bau Private Trust'; |
| #392 | return cleaned; |
| #393 | } |
| #394 | |
| #395 | function mergeProfile(base: ProfileResponse, persisted: PersistedProfile): ProfileResponse { |
| #396 | const paidTier = persisted.paidTier ? cleanPaidTier(persisted.paidTier, base.paidTier) : base.paidTier; |
| #397 | const pma = persisted.pma ? cleanPma(persisted.pma, base.pma) : base.pma; |
| #398 | const cooperativePool = persisted.cooperativePool |
| #399 | ? cleanCooperativePool(persisted.cooperativePool, base.cooperativePool) |
| #400 | : base.cooperativePool; |
| #401 | return { |
| #402 | ...base, |
| #403 | displayName: cleanText(persisted.displayName, base.displayName, 96), |
| #404 | portraitLabel: cleanText(persisted.portraitLabel, base.portraitLabel, 8).toUpperCase(), |
| #405 | paidTier, |
| #406 | pma, |
| #407 | cooperativePool, |
| #408 | trust: { |
| #409 | ...base.trust, |
| #410 | ...(persisted.trust ?? {}), |
| #411 | name: cleanText(persisted.trust?.name, base.trust.name, 160), |
| #412 | primaryColor: cleanColor(persisted.trust?.primaryColor, base.trust.primaryColor), |
| #413 | secondaryColor: cleanColor(persisted.trust?.secondaryColor, base.trust.secondaryColor), |
| #414 | }, |
| #415 | }; |
| #416 | } |
| #417 | |
| #418 | function assembleProfile(args: { |
| #419 | userId: string; |
| #420 | displayName: string; |
| #421 | sovereignStatus: string; |
| #422 | trust: { |
| #423 | name: string; |
| #424 | id?: string | null; |
| #425 | role?: string | null; |
| #426 | uccFiling?: string | null; |
| #427 | filingState?: string | null; |
| #428 | governingLaw?: string | null; |
| #429 | primaryColor: string; |
| #430 | secondaryColor: string; |
| #431 | sealUrl?: string | null; |
| #432 | }; |
| #433 | interactions: number; |
| #434 | activeProjects: { id: string; title: string }[]; |
| #435 | badges: { label: string; kind: string }[]; |
| #436 | paidTier: PaidTier; |
| #437 | }): ProfileResponse { |
| #438 | const nextXp = args.sovereignStatus === 'executor' ? xpForStatus('executor') : xpForStatus('living'); |
| #439 | return { |
| #440 | userId: args.userId, |
| #441 | displayName: args.displayName, |
| #442 | editable: true, |
| #443 | portraitLabel: args.displayName |
| #444 | .split(/\s+/) |
| #445 | .filter(Boolean) |
| #446 | .slice(0, 2) |
| #447 | .map(part => part[0]?.toUpperCase()) |
| #448 | .join(''), |
| #449 | portraitUrl: null, |
| #450 | tierOptions: TIER_OPTIONS, |
| #451 | paidTier: args.paidTier, |
| #452 | pma: { |
| #453 | feeUsd: 33.33, |
| #454 | status: 'pending' as const, |
| #455 | renewalLabel: 'Annual PMA membership', |
| #456 | }, |
| #457 | cooperativePool: { |
| #458 | phase: 'Ceiling Split', |
| #459 | poolPercent: 91, |
| #460 | operatorPercent: 6, |
| #461 | donationPercent: 3, |
| #462 | memberShareLabel: 'Member cooperative pool', |
| #463 | memberSharePercent: 0, |
| #464 | }, |
| #465 | sovereignStatus: { |
| #466 | id: args.sovereignStatus, |
| #467 | label: args.sovereignStatus === 'executor' ? 'Executor of Estate' : 'Awakening', |
| #468 | nextMilestone: args.sovereignStatus === 'executor' |
| #469 | ? 'Maintain estate records and active instruments' |
| #470 | : 'File Affidavit of Identity or Status', |
| #471 | }, |
| #472 | xp: { |
| #473 | current: xpForStatus(args.sovereignStatus), |
| #474 | next: nextXp, |
| #475 | }, |
| #476 | trust: args.trust, |
| #477 | stats: { |
| #478 | posts: 0, |
| #479 | interactions: args.interactions, |
| #480 | activeCampaigns: args.activeProjects.length, |
| #481 | openThreads: args.activeProjects.length, |
| #482 | }, |
| #483 | activeCampaigns: args.activeProjects, |
| #484 | badges: args.badges, |
| #485 | }; |
| #486 | } |
| #487 | |
| #488 | async function attachPhoto(profile: ProfileResponse, userId: string): Promise<ProfileResponse> { |
| #489 | return { |
| #490 | ...profile, |
| #491 | portraitUrl: await profilePhotoUrl(userId), |
| #492 | }; |
| #493 | } |
| #494 | |
| #495 | async function buildProfile() { |
| #496 | const user = await getUserContext(); |
| #497 | const activeProjects = await readActiveProjects(user.vaultPath); |
| #498 | const interactions = await countLedgerInteractions(user.legacyUserId); |
| #499 | const registryMember = await readRegistryMemberForUser(user.userId); |
| #500 | |
| #501 | if (user.canonicalMemberId === CANONICAL_KING_ID || user.vaultUserKey === 'kingbau') { |
| #502 | const profile = await readJson<TrustProfile>(path.join(KING_TRUST_ROOT, 'profile.json')); |
| #503 | const branding = await readJson<TrustBranding>(path.join(KING_TRUST_ROOT, 'branding.json')); |
| #504 | const sovereignStatus = profile?.ucc_filing && profile?.trust_id ? 'executor' : 'awakening'; |
| #505 | |
| #506 | const base = assembleProfile({ |
| #507 | userId: user.userId, |
| #508 | displayName: user.displayName, |
| #509 | paidTier: { |
| #510 | id: 'founding_court', |
| #511 | label: 'Founding', |
| #512 | detail: 'Sovereign Court', |
| #513 | }, |
| #514 | sovereignStatus, |
| #515 | interactions, |
| #516 | trust: { |
| #517 | name: safeTrustName(profile?.name), |
| #518 | id: profile?.trust_id ?? 'HED-TRUST-2025-001', |
| #519 | role: profile?.trustee?.name ?? 'King Bau, Trustee', |
| #520 | uccFiling: profile?.ucc_filing ?? '20250137020X', |
| #521 | filingState: profile?.ucc_filing_state ?? 'Texas', |
| #522 | governingLaw: profile?.governing_law ?? 'UCC Article 3 / Private Trust Law', |
| #523 | primaryColor: branding?.primary_color ?? '#C9A84C', |
| #524 | secondaryColor: branding?.secondary_color ?? '#0A0A0A', |
| #525 | sealUrl: branding?.seal_url ? '/api/profile/seal' : null, |
| #526 | }, |
| #527 | activeProjects, |
| #528 | badges: profileBadgesFromRegistry(registryMember, [ |
| #529 | { label: 'Affidavit of Identity', kind: 'status' }, |
| #530 | { label: 'UCC-1 Secured', kind: 'status' }, |
| #531 | { label: 'Trust Filed', kind: 'status' }, |
| #532 | { label: 'Founding Member', kind: 'tier' }, |
| #533 | ]), |
| #534 | }); |
| #535 | return attachPhoto(mergeProfile(base, await readPersistedProfile(user.userId)), user.userId); |
| #536 | } |
| #537 | |
| #538 | const base = assembleProfile({ |
| #539 | userId: user.userId, |
| #540 | displayName: user.displayName, |
| #541 | paidTier: { |
| #542 | id: 'founding_court', |
| #543 | label: 'Founding', |
| #544 | detail: 'Private Member', |
| #545 | }, |
| #546 | sovereignStatus: 'awakening', |
| #547 | interactions, |
| #548 | trust: { |
| #549 | name: 'Trust Estate Pending', |
| #550 | id: null, |
| #551 | role: user.displayName, |
| #552 | uccFiling: null, |
| #553 | filingState: null, |
| #554 | governingLaw: null, |
| #555 | primaryColor: '#D4AF37', |
| #556 | secondaryColor: '#050505', |
| #557 | sealUrl: null, |
| #558 | }, |
| #559 | activeProjects, |
| #560 | badges: profileBadgesFromRegistry(registryMember, [ |
| #561 | { label: 'Aethon Profile Indexed', kind: 'status' }, |
| #562 | { label: 'Active Project: Utility Bill', kind: 'status' }, |
| #563 | { label: 'Founding Member', kind: 'tier' }, |
| #564 | ]), |
| #565 | }); |
| #566 | return attachPhoto(mergeProfile(base, await readPersistedProfile(user.userId)), user.userId); |
| #567 | } |
| #568 | |
| #569 | export async function GET() { |
| #570 | try { |
| #571 | return NextResponse.json(await buildProfile()); |
| #572 | } catch (error) { |
| #573 | return authErrorResponse(error); |
| #574 | } |
| #575 | } |
| #576 | |
| #577 | export async function PUT(req: NextRequest) { |
| #578 | try { |
| #579 | const user = await getUserContext(); |
| #580 | const current = await buildProfile(); |
| #581 | const payload = await req.json().catch(() => null); |
| #582 | const next = sanitizePatch(payload, current); |
| #583 | await writePersistedProfile(user.userId, next); |
| #584 | return NextResponse.json(await buildProfile()); |
| #585 | } catch (error) { |
| #586 | return authErrorResponse(error); |
| #587 | } |
| #588 | } |
| #589 | |
| #590 | export async function PATCH(req: NextRequest) { |
| #591 | return PUT(req); |
| #592 | } |
| #593 | |
| #594 | export async function DELETE() { |
| #595 | try { |
| #596 | const user = await getUserContext(); |
| #597 | await fs.rm(profilePath(user.userId), { force: true }); |
| #598 | return NextResponse.json(await buildProfile()); |
| #599 | } catch (error) { |
| #600 | return authErrorResponse(error); |
| #601 | } |
| #602 | } |
| #603 | |
| #604 |