@happyvertical/smrt-profiles 0.30.0

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 (42) hide show
  1. package/AGENTS.md +53 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +176 -0
  5. package/dist/chunks/ApiKey-B2LKEaP8.js +143 -0
  6. package/dist/chunks/ApiKey-B2LKEaP8.js.map +1 -0
  7. package/dist/chunks/ApiKeyCollection-B6Op817e.js +91 -0
  8. package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +1 -0
  9. package/dist/chunks/AuditLogCollection-BYqCj0uE.js +195 -0
  10. package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +1 -0
  11. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +3065 -0
  12. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +1 -0
  13. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js +122 -0
  14. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js.map +1 -0
  15. package/dist/chunks/ProfileCollection-DU6wUJTO.js +782 -0
  16. package/dist/chunks/ProfileCollection-DU6wUJTO.js.map +1 -0
  17. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +120 -0
  18. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +1 -0
  19. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +184 -0
  20. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +1 -0
  21. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js +177 -0
  22. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js.map +1 -0
  23. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +117 -0
  24. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +1 -0
  25. package/dist/chunks/ProfileRelationshipType-BXBLldea.js +103 -0
  26. package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +1 -0
  27. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +48 -0
  28. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +1 -0
  29. package/dist/chunks/index-jFtOWsAV.js +1014 -0
  30. package/dist/chunks/index-jFtOWsAV.js.map +1 -0
  31. package/dist/index.d.ts +1848 -0
  32. package/dist/index.js +70 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/manifest.json +11829 -0
  35. package/dist/smrt-knowledge.json +3846 -0
  36. package/dist/types.d.ts +41 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils.d.ts +61 -0
  40. package/dist/utils.js +49 -0
  41. package/dist/utils.js.map +1 -0
  42. package/package.json +75 -0
@@ -0,0 +1,782 @@
1
+ import { resolveOwnedAssetsById, assertValidOwnedAssetRelationship, assertValidOwnedAssetSortOrder, getOwnedAssetsFromCollection, addOwnedAssetFromCollection, removeOwnedAssetFromCollection } from "@happyvertical/smrt-assets";
2
+ import { field, smrt, SmrtObject, foreignKey, oneToMany, SmrtCollection } from "@happyvertical/smrt-core";
3
+ import { definePrompt, resolvePrompt } from "@happyvertical/smrt-prompts";
4
+ import { tenantId, TenantScoped } from "@happyvertical/smrt-tenancy";
5
+ const smrtProfilesGenerateBioPrompt = definePrompt({
6
+ key: "smrtProfiles.profile.generateBio",
7
+ template: `Write a short, professional bio for this person.
8
+
9
+ Profile name: {profileName}
10
+ Profile description: {profileDescription}
11
+
12
+ Return only the bio text, with no commentary or surrounding quotation marks.`,
13
+ editable: {
14
+ template: true,
15
+ profile: true,
16
+ model: true,
17
+ params: true
18
+ }
19
+ });
20
+ function promptMessageOptions(ai) {
21
+ return {
22
+ ...ai.params || {},
23
+ ...ai.model ? { model: ai.model } : {},
24
+ ...typeof ai.temperature === "number" ? { temperature: ai.temperature } : {},
25
+ ...typeof ai.maxTokens === "number" ? { maxTokens: ai.maxTokens } : {}
26
+ };
27
+ }
28
+ var __defProp$1 = Object.defineProperty;
29
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
30
+ var __decorateClass$1 = (decorators, target, key, kind) => {
31
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
32
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
33
+ if (decorator = decorators[i])
34
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
35
+ if (kind && result) __defProp$1(target, key, result);
36
+ return result;
37
+ };
38
+ let ProfileType = class extends SmrtObject {
39
+ tenantId = null;
40
+ name = "";
41
+ description;
42
+ constructor(options = {}) {
43
+ super(options);
44
+ if (options.name) this.name = options.name;
45
+ if (options.description !== void 0)
46
+ this.description = options.description;
47
+ }
48
+ /**
49
+ * Convenience method for slug-based lookup
50
+ *
51
+ * @param slug - The slug to search for
52
+ * @returns ProfileType instance or null if not found
53
+ */
54
+ static async getBySlug(_slug) {
55
+ return null;
56
+ }
57
+ };
58
+ __decorateClass$1([
59
+ tenantId({ nullable: true })
60
+ ], ProfileType.prototype, "tenantId", 2);
61
+ __decorateClass$1([
62
+ field({ required: true })
63
+ ], ProfileType.prototype, "name", 2);
64
+ ProfileType = __decorateClass$1([
65
+ TenantScoped({ mode: "optional" }),
66
+ smrt({
67
+ tableStrategy: "sti",
68
+ api: { include: ["list", "get", "create", "update"] },
69
+ mcp: { include: ["list", "get"] },
70
+ cli: true
71
+ })
72
+ ], ProfileType);
73
+ const ProfileType$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
74
+ __proto__: null,
75
+ get ProfileType() {
76
+ return ProfileType;
77
+ }
78
+ }, Symbol.toStringTag, { value: "Module" }));
79
+ var __defProp = Object.defineProperty;
80
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
81
+ var __decorateClass = (decorators, target, key, kind) => {
82
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
83
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
84
+ if (decorator = decorators[i])
85
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
86
+ if (kind && result) __defProp(target, key, result);
87
+ return result;
88
+ };
89
+ let Profile = class extends SmrtObject {
90
+ tenantId = null;
91
+ typeId;
92
+ email;
93
+ name = "";
94
+ // Display name
95
+ description;
96
+ metadata = [];
97
+ relationshipsFrom = [];
98
+ relationshipsTo = [];
99
+ constructor(options = {}) {
100
+ super(options);
101
+ if (options.typeId !== void 0) this.typeId = options.typeId;
102
+ if (options.email !== void 0) this.email = options.email;
103
+ if (options.name) this.name = options.name;
104
+ if (options.description !== void 0)
105
+ this.description = options.description;
106
+ }
107
+ /**
108
+ * Get the profile type slug for this profile
109
+ *
110
+ * @returns The slug of the profile type
111
+ */
112
+ async getTypeSlug() {
113
+ const type = await this.loadRelated("typeId");
114
+ return type?.slug || "";
115
+ }
116
+ /**
117
+ * Set the profile type by slug
118
+ *
119
+ * @param slug - The slug of the profile type
120
+ * @throws Error if profile type not found
121
+ */
122
+ async setTypeBySlug(slug) {
123
+ const type = await ProfileType.getBySlug(slug);
124
+ if (!type) throw new Error(`Profile type '${slug}' not found`);
125
+ this.typeId = type.id;
126
+ }
127
+ /**
128
+ * Add metadata to this profile
129
+ *
130
+ * @param metafieldSlug - The slug of the metafield
131
+ * @param value - The value to set
132
+ */
133
+ async addMetadata(metafieldSlug, value) {
134
+ const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-DMKhSHXX.js").then((n) => n.b);
135
+ const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DEhmljMY.js").then((n) => n.b);
136
+ const metafieldCollection = await ProfileMetafieldCollection.create(this.options);
137
+ const metafield = await metafieldCollection.getBySlug(metafieldSlug);
138
+ if (!metafield) {
139
+ throw new Error(`Metafield '${metafieldSlug}' not found`);
140
+ }
141
+ await metafield.validateValue(value);
142
+ const metadataCollection = await ProfileMetadataCollection.create(
143
+ this.options
144
+ );
145
+ const existing = await metadataCollection.list({
146
+ where: { profileId: this.id, metafieldId: metafield.id },
147
+ limit: 1
148
+ });
149
+ if (existing.length > 0) {
150
+ const metadata = existing[0];
151
+ metadata.value = String(value);
152
+ await metadata.save();
153
+ } else {
154
+ const metadata = await metadataCollection.create({
155
+ profileId: this.id,
156
+ metafieldId: metafield.id,
157
+ value: String(value)
158
+ });
159
+ await metadata.save();
160
+ }
161
+ }
162
+ /**
163
+ * Get all metadata for this profile as key-value object
164
+ *
165
+ * @returns Object with metafield slugs as keys
166
+ */
167
+ async getMetadata() {
168
+ const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DEhmljMY.js").then((n) => n.b);
169
+ const metadataCollection = await ProfileMetadataCollection.create(
170
+ this.options
171
+ );
172
+ return await metadataCollection.getMetadataObject(this.id);
173
+ }
174
+ /**
175
+ * Update multiple metadata values
176
+ *
177
+ * @param metadata - Object with metafield slugs as keys and values
178
+ */
179
+ async updateMetadata(metadata) {
180
+ for (const [metafieldSlug, value] of Object.entries(metadata)) {
181
+ await this.addMetadata(metafieldSlug, value);
182
+ }
183
+ }
184
+ /**
185
+ * Remove metadata by metafield slug
186
+ *
187
+ * @param metafieldSlug - The slug of the metafield to remove
188
+ */
189
+ async removeMetadata(metafieldSlug) {
190
+ const { ProfileMetafieldCollection } = await import("./ProfileMetafieldCollection-DMKhSHXX.js").then((n) => n.b);
191
+ const { ProfileMetadataCollection } = await import("./ProfileMetadataCollection-DEhmljMY.js").then((n) => n.b);
192
+ const metafieldCollection = await ProfileMetafieldCollection.create(this.options);
193
+ const metafield = await metafieldCollection.getBySlug(metafieldSlug);
194
+ if (!metafield) {
195
+ throw new Error(`Metafield '${metafieldSlug}' not found`);
196
+ }
197
+ const metadataCollection = await ProfileMetadataCollection.create(
198
+ this.options
199
+ );
200
+ const existing = await metadataCollection.list({
201
+ where: { profileId: this.id, metafieldId: metafield.id },
202
+ limit: 1
203
+ });
204
+ if (existing.length > 0) {
205
+ await existing[0].delete();
206
+ }
207
+ }
208
+ async getProfileAssetCollection() {
209
+ const { ProfileAssetCollection } = await import("./ProfileAssetCollection-D_tk1kKG.js").then((n) => n.b);
210
+ return ProfileAssetCollection.create({ db: this.db });
211
+ }
212
+ async getAssets(relationship) {
213
+ if (!this.id) {
214
+ return [];
215
+ }
216
+ const profileAssets = await this.getProfileAssetCollection();
217
+ const linkedAssets = await profileAssets.byLeft(
218
+ this.id,
219
+ relationship ? { relationship } : {}
220
+ );
221
+ return resolveOwnedAssetsById(
222
+ this.db,
223
+ linkedAssets.map((link) => link.assetId),
224
+ this.tenantId
225
+ );
226
+ }
227
+ async addAsset(asset, relationship = "attachment", sortOrder = 0) {
228
+ if (!this.id || !asset.id) {
229
+ throw new Error("Cannot associate unsaved profile or asset");
230
+ }
231
+ assertValidOwnedAssetRelationship(relationship);
232
+ assertValidOwnedAssetSortOrder(sortOrder);
233
+ const profileAssets = await this.getProfileAssetCollection();
234
+ await profileAssets.attach(this.id, asset.id, {
235
+ relationship,
236
+ sortOrder,
237
+ tenantId: this.tenantId
238
+ });
239
+ }
240
+ async removeAsset(assetId, relationship) {
241
+ if (!this.id) {
242
+ return;
243
+ }
244
+ const profileAssets = await this.getProfileAssetCollection();
245
+ await profileAssets.detach(
246
+ this.id,
247
+ assetId,
248
+ relationship ? { relationship } : {}
249
+ );
250
+ }
251
+ /**
252
+ * Add a relationship to another profile
253
+ *
254
+ * @param toProfile - The target profile
255
+ * @param relationshipSlug - The type of relationship
256
+ * @param contextProfile - Optional context profile for tertiary relationships
257
+ */
258
+ async addRelationship(toProfile, relationshipSlug, contextProfile) {
259
+ const { ProfileRelationshipTypeCollection } = await import("./ProfileRelationshipTypeCollection-CF8YvLTV.js");
260
+ const { ProfileRelationshipCollection } = await import("./ProfileRelationshipCollection-C0IM8UQR.js").then((n) => n.b);
261
+ const { ProfileRelationshipType } = await import("./ProfileRelationshipType-BXBLldea.js");
262
+ const relationshipTypeCollection = await ProfileRelationshipTypeCollection.create(this.options);
263
+ const relationshipType = await relationshipTypeCollection.getBySlug(relationshipSlug);
264
+ if (!relationshipType) {
265
+ throw new Error(`Relationship type '${relationshipSlug}' not found`);
266
+ }
267
+ const relationshipCollection = await ProfileRelationshipCollection.create(this.options);
268
+ const exists = await relationshipCollection.exists(
269
+ this.id,
270
+ toProfile.id,
271
+ relationshipType.id
272
+ );
273
+ if (!exists) {
274
+ const relationship = await relationshipCollection.create({
275
+ fromProfileId: this.id,
276
+ toProfileId: toProfile.id,
277
+ typeId: relationshipType.id,
278
+ contextProfileId: contextProfile?.id
279
+ });
280
+ await relationship.save();
281
+ }
282
+ if (relationshipType.reciprocal) {
283
+ const handler = ProfileRelationshipType.getReciprocalHandler(relationshipSlug);
284
+ if (handler) {
285
+ await handler(this, toProfile, contextProfile);
286
+ }
287
+ }
288
+ }
289
+ /**
290
+ * Get all relationships for this profile
291
+ *
292
+ * @param options - Filter options (typeSlug, direction)
293
+ * @returns Array of ProfileRelationship instances
294
+ */
295
+ async getRelationships(options) {
296
+ const { ProfileRelationshipCollection } = await import("./ProfileRelationshipCollection-C0IM8UQR.js").then((n) => n.b);
297
+ const { ProfileRelationshipTypeCollection } = await import("./ProfileRelationshipTypeCollection-CF8YvLTV.js");
298
+ const relationshipCollection = await ProfileRelationshipCollection.create(this.options);
299
+ const direction = options?.direction || "all";
300
+ let typeId;
301
+ if (options?.typeSlug) {
302
+ const relationshipTypeCollection = await ProfileRelationshipTypeCollection.create(this.options);
303
+ const relationshipType = await relationshipTypeCollection.getBySlug(
304
+ options.typeSlug
305
+ );
306
+ typeId = relationshipType?.id;
307
+ }
308
+ if (direction === "from") {
309
+ return await relationshipCollection.getFromProfile(this.id, typeId);
310
+ } else if (direction === "to") {
311
+ return await relationshipCollection.getToProfile(this.id, typeId);
312
+ } else {
313
+ return await relationshipCollection.getForProfile(this.id, typeId);
314
+ }
315
+ }
316
+ /**
317
+ * Get related profiles
318
+ *
319
+ * @param relationshipSlug - Optional filter by relationship type slug
320
+ * @returns Array of related Profile instances
321
+ */
322
+ async getRelatedProfiles(relationshipSlug) {
323
+ const { ProfileCollection: ProfileCollection2 } = await Promise.resolve().then(() => ProfileCollection$1);
324
+ const relationships = await this.getRelationships({
325
+ typeSlug: relationshipSlug,
326
+ direction: "all"
327
+ });
328
+ const profileCollection = await ProfileCollection2.create(
329
+ this.options
330
+ );
331
+ const relatedProfiles = [];
332
+ const seenIds = /* @__PURE__ */ new Set();
333
+ for (const relationship of relationships) {
334
+ const fromId = String(relationship.fromProfileId);
335
+ const toId = String(relationship.toProfileId);
336
+ const thisId = String(this.id);
337
+ const otherId = fromId === thisId ? toId : fromId;
338
+ if (!seenIds.has(otherId)) {
339
+ seenIds.add(otherId);
340
+ const profile = await profileCollection.get({ id: otherId });
341
+ if (profile) {
342
+ relatedProfiles.push(profile);
343
+ }
344
+ }
345
+ }
346
+ return relatedProfiles;
347
+ }
348
+ /**
349
+ * Remove a relationship to another profile
350
+ *
351
+ * @param toProfile - The target profile
352
+ * @param relationshipSlug - The type of relationship to remove
353
+ */
354
+ async removeRelationship(toProfile, relationshipSlug) {
355
+ const { ProfileRelationshipTypeCollection } = await import("./ProfileRelationshipTypeCollection-CF8YvLTV.js");
356
+ const { ProfileRelationshipCollection } = await import("./ProfileRelationshipCollection-C0IM8UQR.js").then((n) => n.b);
357
+ const relationshipTypeCollection = await ProfileRelationshipTypeCollection.create(this.options);
358
+ const relationshipType = await relationshipTypeCollection.getBySlug(relationshipSlug);
359
+ if (!relationshipType) {
360
+ throw new Error(`Relationship type '${relationshipSlug}' not found`);
361
+ }
362
+ const relationshipCollection = await ProfileRelationshipCollection.create(this.options);
363
+ const relationships = await relationshipCollection.list({
364
+ where: {
365
+ fromProfileId: this.id,
366
+ toProfileId: toProfile.id,
367
+ typeId: relationshipType.id
368
+ }
369
+ });
370
+ for (const relationship of relationships) {
371
+ await relationship.delete();
372
+ }
373
+ if (relationshipType.reciprocal) {
374
+ const inverseRelationships = await relationshipCollection.list({
375
+ where: {
376
+ fromProfileId: toProfile.id,
377
+ toProfileId: this.id,
378
+ typeId: relationshipType.id
379
+ }
380
+ });
381
+ for (const relationship of inverseRelationships) {
382
+ await relationship.delete();
383
+ }
384
+ }
385
+ }
386
+ /**
387
+ * AI-powered: Generate a professional bio for this profile
388
+ *
389
+ * Uses the `smrtProfiles.profile.generateBio` prompt registered via
390
+ * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level
391
+ * overrides of the template, model, and parameters at runtime.
392
+ *
393
+ * @returns Generated bio text
394
+ */
395
+ async generateBio() {
396
+ const db = this.options.db ?? this.options.persistence;
397
+ const resolvedPrompt = await resolvePrompt(
398
+ smrtProfilesGenerateBioPrompt.key,
399
+ {
400
+ db,
401
+ tenantId: this.tenantId,
402
+ variables: {
403
+ profileName: this.name || "",
404
+ profileDescription: this.description || ""
405
+ }
406
+ }
407
+ );
408
+ const ai = await this.getAiClient();
409
+ const response = await ai.message(
410
+ resolvedPrompt.text,
411
+ promptMessageOptions(resolvedPrompt.ai)
412
+ );
413
+ return response.trim();
414
+ }
415
+ /**
416
+ * AI-powered: Check if profile matches criteria
417
+ *
418
+ * @param criteria - Criteria to match against
419
+ * @returns True if matches criteria
420
+ */
421
+ async matches(criteria) {
422
+ return await this.is(criteria);
423
+ }
424
+ /**
425
+ * Find profiles by metadata key-value pair
426
+ *
427
+ * @param metafieldSlug - The metafield slug to search
428
+ * @param value - The value to match
429
+ * @returns Array of matching profiles
430
+ */
431
+ static async findByMetadata(_metafieldSlug, _value) {
432
+ return [];
433
+ }
434
+ /**
435
+ * Find profiles by type slug
436
+ *
437
+ * @param typeSlug - The profile type slug
438
+ * @returns Array of matching profiles
439
+ */
440
+ static async findByType(_typeSlug) {
441
+ return [];
442
+ }
443
+ /**
444
+ * Find related profiles for a given profile
445
+ *
446
+ * @param profileId - The profile UUID
447
+ * @param relationshipSlug - Optional filter by relationship type
448
+ * @returns Array of related profiles
449
+ */
450
+ static async findRelated(_profileId, _relationshipSlug) {
451
+ return [];
452
+ }
453
+ /**
454
+ * Search profiles by email
455
+ *
456
+ * @param email - The email to search for
457
+ * @returns Profile or null if not found
458
+ */
459
+ static async searchByEmail(_email) {
460
+ return null;
461
+ }
462
+ // ========================
463
+ // Auth-related methods
464
+ // ========================
465
+ /**
466
+ * Get all API keys for this profile
467
+ *
468
+ * @returns Array of API keys
469
+ */
470
+ async getApiKeys() {
471
+ const { ApiKeyCollection } = await import("./ApiKeyCollection-B6Op817e.js");
472
+ const collection = await ApiKeyCollection.create(this.options);
473
+ return await collection.findByProfile(this.id);
474
+ }
475
+ /**
476
+ * Get active (non-revoked, non-expired) API keys for this profile
477
+ *
478
+ * @returns Array of active API keys
479
+ */
480
+ async getActiveApiKeys() {
481
+ const { ApiKeyCollection } = await import("./ApiKeyCollection-B6Op817e.js");
482
+ const collection = await ApiKeyCollection.create(this.options);
483
+ return await collection.findActiveByProfile(this.id);
484
+ }
485
+ /**
486
+ * Generate a new API key for this profile
487
+ *
488
+ * @param options - Key options (name, scopes, expiration)
489
+ * @returns The generated key (plaintext) and ApiKey record
490
+ */
491
+ async generateApiKey(options) {
492
+ const { ApiKey } = await import("./ApiKey-B2LKEaP8.js");
493
+ return await ApiKey.generate(this, {
494
+ ...options,
495
+ db: this.options?.db
496
+ });
497
+ }
498
+ /**
499
+ * Get all OIDC identities linked to this profile
500
+ *
501
+ * @returns Array of OIDC identity records
502
+ */
503
+ async getOidcIdentities() {
504
+ const { OidcIdentityCollection } = await import("./index-jFtOWsAV.js").then((n) => n.j);
505
+ const collection = await OidcIdentityCollection.create(
506
+ this.options
507
+ );
508
+ return await collection.findByProfile(this.id);
509
+ }
510
+ /**
511
+ * Link a new OIDC identity to this profile
512
+ *
513
+ * @param oidcData - OIDC provider data
514
+ * @returns The linked OIDC identity record
515
+ */
516
+ async linkOidcIdentity(oidcData) {
517
+ const { OidcIdentityCollection } = await import("./index-jFtOWsAV.js").then((n) => n.j);
518
+ const collection = await OidcIdentityCollection.create(
519
+ this.options
520
+ );
521
+ return await collection.linkToProfile(this, oidcData);
522
+ }
523
+ /**
524
+ * Get all Nostr identities linked to this profile
525
+ *
526
+ * @returns Array of Nostr identity records
527
+ */
528
+ async getNostrIdentities() {
529
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-DadQBHWy.js").then((n) => n.q);
530
+ const collection = await NostrIdentityCollection.create(
531
+ this.options
532
+ );
533
+ return await collection.findByProfile(this.id);
534
+ }
535
+ /**
536
+ * Link a new Nostr identity to this profile
537
+ *
538
+ * @param nostrData - Nostr identity data (encrypted keypair)
539
+ * @returns The linked Nostr identity record
540
+ */
541
+ async linkNostrIdentity(nostrData) {
542
+ const { NostrIdentityCollection } = await import("./NostrIdentityCollection-DadQBHWy.js").then((n) => n.q);
543
+ const collection = await NostrIdentityCollection.create(
544
+ this.options
545
+ );
546
+ return await collection.linkToProfile(this, nostrData);
547
+ }
548
+ /**
549
+ * Get audit logs for actions performed by this profile
550
+ *
551
+ * @param limit - Maximum number of logs to return
552
+ * @returns Array of audit log entries
553
+ */
554
+ async getAuditLogs(limit = 50) {
555
+ const { AuditLogCollection } = await import("./AuditLogCollection-BYqCj0uE.js").then((n) => n.b);
556
+ const collection = await AuditLogCollection.create(this.options);
557
+ return await collection.getRecentActivity(this.id, limit);
558
+ }
559
+ /**
560
+ * Record an audit log entry for an action by this profile
561
+ *
562
+ * @param options - Audit log options
563
+ * @returns The created audit log entry
564
+ */
565
+ async recordAction(options) {
566
+ const { AuditLogCollection } = await import("./AuditLogCollection-BYqCj0uE.js").then((n) => n.b);
567
+ const collection = await AuditLogCollection.create(this.options);
568
+ return await collection.record({
569
+ profile: this,
570
+ ...options
571
+ });
572
+ }
573
+ };
574
+ __decorateClass([
575
+ tenantId({ nullable: true })
576
+ ], Profile.prototype, "tenantId", 2);
577
+ __decorateClass([
578
+ foreignKey("ProfileType", { required: true })
579
+ ], Profile.prototype, "typeId", 2);
580
+ __decorateClass([
581
+ field({ unique: true })
582
+ ], Profile.prototype, "email", 2);
583
+ __decorateClass([
584
+ field({ required: true })
585
+ ], Profile.prototype, "name", 2);
586
+ __decorateClass([
587
+ oneToMany("ProfileMetadata")
588
+ ], Profile.prototype, "metadata", 2);
589
+ __decorateClass([
590
+ oneToMany("ProfileRelationship", { foreignKey: "fromProfileId" })
591
+ ], Profile.prototype, "relationshipsFrom", 2);
592
+ __decorateClass([
593
+ oneToMany("ProfileRelationship", { foreignKey: "toProfileId" })
594
+ ], Profile.prototype, "relationshipsTo", 2);
595
+ Profile = __decorateClass([
596
+ TenantScoped({ mode: "optional" }),
597
+ smrt({
598
+ tableStrategy: "sti",
599
+ api: { include: ["list", "get", "create", "update", "delete"] },
600
+ mcp: { include: ["list", "get", "create", "update"] },
601
+ cli: true
602
+ })
603
+ ], Profile);
604
+ class ProfileCollection extends SmrtCollection {
605
+ static _itemClass = Profile;
606
+ /**
607
+ * Find a profile by email address
608
+ *
609
+ * @param email - The email address to search for
610
+ * @returns The matching profile or null
611
+ */
612
+ async findByEmail(email) {
613
+ const normalizedEmail = email.toLowerCase();
614
+ const profiles = await this.list({
615
+ where: { email: normalizedEmail },
616
+ limit: 1
617
+ });
618
+ return profiles.length > 0 ? profiles[0] : null;
619
+ }
620
+ /**
621
+ * Find profiles by type slug
622
+ *
623
+ * @param typeSlug - The profile type slug to filter by
624
+ * @returns Array of matching profiles
625
+ */
626
+ async findByType(typeSlug) {
627
+ const allProfiles = await this.list({});
628
+ const filtered = [];
629
+ for (const profile of allProfiles) {
630
+ const slug = await profile.getTypeSlug();
631
+ if (slug === typeSlug) {
632
+ filtered.push(profile);
633
+ }
634
+ }
635
+ return filtered;
636
+ }
637
+ /**
638
+ * Batch get metadata for multiple profiles
639
+ *
640
+ * @param profileIds - Array of profile UUIDs
641
+ * @returns Map of profile ID to metadata object
642
+ */
643
+ async batchGetMetadata(profileIds) {
644
+ const result = /* @__PURE__ */ new Map();
645
+ for (const profileId of profileIds) {
646
+ const profile = await this.get({ id: profileId });
647
+ if (profile) {
648
+ const metadata = await profile.getMetadata();
649
+ result.set(profileId, metadata);
650
+ }
651
+ }
652
+ return result;
653
+ }
654
+ /**
655
+ * Batch update metadata for multiple profiles
656
+ *
657
+ * @param updates - Array of { profileId, data } objects
658
+ */
659
+ async batchUpdateMetadata(updates) {
660
+ for (const update of updates) {
661
+ const profile = await this.get({ id: update.profileId });
662
+ if (profile) {
663
+ await profile.updateMetadata(update.data);
664
+ }
665
+ }
666
+ }
667
+ /**
668
+ * Find related profiles for a given profile
669
+ *
670
+ * @param profileId - The profile UUID
671
+ * @param relationshipSlug - Optional filter by relationship type
672
+ * @returns Array of related profiles
673
+ */
674
+ async findRelated(profileId, relationshipSlug) {
675
+ const profile = await this.get({ id: profileId });
676
+ if (!profile) return [];
677
+ return await profile.getRelatedProfiles(relationshipSlug);
678
+ }
679
+ async getAssets(profileId, relationship) {
680
+ return getOwnedAssetsFromCollection(this, profileId, relationship);
681
+ }
682
+ async addAsset(profileId, asset, relationship = "attachment", sortOrder = 0) {
683
+ await addOwnedAssetFromCollection(
684
+ this,
685
+ "Profile",
686
+ profileId,
687
+ asset,
688
+ relationship,
689
+ sortOrder
690
+ );
691
+ }
692
+ async removeAsset(profileId, assetId, relationship) {
693
+ await removeOwnedAssetFromCollection(
694
+ this,
695
+ "Profile",
696
+ profileId,
697
+ assetId,
698
+ relationship
699
+ );
700
+ }
701
+ /**
702
+ * Get the relationship network for a profile up to a maximum depth
703
+ *
704
+ * @param profileId - The starting profile UUID
705
+ * @param options - Configuration options
706
+ * @returns Map of profile ID to depth level
707
+ */
708
+ async getRelationshipNetwork(profileId, options = {}) {
709
+ const maxDepth = options.maxDepth || 2;
710
+ const network = /* @__PURE__ */ new Map();
711
+ const visited = /* @__PURE__ */ new Set();
712
+ const queue = [
713
+ { id: profileId, depth: 0 }
714
+ ];
715
+ while (queue.length > 0) {
716
+ const current = queue.shift();
717
+ if (!current) {
718
+ break;
719
+ }
720
+ if (visited.has(current.id) || current.depth > maxDepth) {
721
+ continue;
722
+ }
723
+ visited.add(current.id);
724
+ network.set(current.id, current.depth);
725
+ if (current.depth < maxDepth) {
726
+ const related = await this.findRelated(current.id);
727
+ for (const profile of related) {
728
+ if (profile.id && !visited.has(profile.id)) {
729
+ queue.push({ id: profile.id, depth: current.depth + 1 });
730
+ }
731
+ }
732
+ }
733
+ }
734
+ return network;
735
+ }
736
+ // ─────────────────────────────────────────────────────────────────────────────
737
+ // Tenant-scoped helper methods
738
+ // ─────────────────────────────────────────────────────────────────────────────
739
+ /**
740
+ * Find all profiles belonging to a specific tenant
741
+ *
742
+ * @param tenantId - The tenant UUID to filter by
743
+ * @returns Array of profiles for this tenant
744
+ */
745
+ async findByTenant(tenantId2) {
746
+ return this.list({ where: { tenantId: tenantId2 } });
747
+ }
748
+ /**
749
+ * Find all global profiles (without a tenant)
750
+ *
751
+ * @returns Array of profiles with null tenantId
752
+ */
753
+ async findGlobal() {
754
+ return this.list({ where: { tenantId: null } });
755
+ }
756
+ /**
757
+ * Find profiles belonging to a tenant plus all global profiles
758
+ *
759
+ * @param tenantId - The tenant UUID to include
760
+ * @returns Array of tenant-specific and global profiles
761
+ */
762
+ async findWithGlobals(tenantId2) {
763
+ return this.query(
764
+ `SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
765
+ [tenantId2]
766
+ );
767
+ }
768
+ }
769
+ const ProfileCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
770
+ __proto__: null,
771
+ ProfileCollection
772
+ }, Symbol.toStringTag, { value: "Module" }));
773
+ export {
774
+ ProfileType as P,
775
+ Profile as a,
776
+ ProfileCollection as b,
777
+ ProfileType$1 as c,
778
+ ProfileCollection$1 as d,
779
+ promptMessageOptions as p,
780
+ smrtProfilesGenerateBioPrompt as s
781
+ };
782
+ //# sourceMappingURL=ProfileCollection-DU6wUJTO.js.map