@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.
- package/AGENTS.md +53 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +176 -0
- package/dist/chunks/ApiKey-B2LKEaP8.js +143 -0
- package/dist/chunks/ApiKey-B2LKEaP8.js.map +1 -0
- package/dist/chunks/ApiKeyCollection-B6Op817e.js +91 -0
- package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +1 -0
- package/dist/chunks/AuditLogCollection-BYqCj0uE.js +195 -0
- package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +1 -0
- package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +3065 -0
- package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +1 -0
- package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js +122 -0
- package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js.map +1 -0
- package/dist/chunks/ProfileCollection-DU6wUJTO.js +782 -0
- package/dist/chunks/ProfileCollection-DU6wUJTO.js.map +1 -0
- package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +120 -0
- package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +1 -0
- package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +184 -0
- package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +1 -0
- package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js +177 -0
- package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js.map +1 -0
- package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +117 -0
- package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +1 -0
- package/dist/chunks/ProfileRelationshipType-BXBLldea.js +103 -0
- package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +1 -0
- package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +48 -0
- package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +1 -0
- package/dist/chunks/index-jFtOWsAV.js +1014 -0
- package/dist/chunks/index-jFtOWsAV.js.map +1 -0
- package/dist/index.d.ts +1848 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +11829 -0
- package/dist/smrt-knowledge.json +3846 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +61 -0
- package/dist/utils.js +49 -0
- package/dist/utils.js.map +1 -0
- 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
|