Skip to content

Commit

Permalink
257-version-0014-updates (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mookse authored Jul 16, 2024
1 parent 350b1bb commit cbda4ff
Show file tree
Hide file tree
Showing 22 changed files with 281 additions and 290 deletions.
2 changes: 1 addition & 1 deletion inc/js/factory-class-extenders/class-extenders.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function extendClass_conversation(originClass, referencesObject) {
*/
addMessage(message){
const { id, } = message
if(this.messages.find(message=>message.id===id))
if(this.#messages.find(message=>message.id===id))
return this.messages
if(!(message instanceof this.#factory.message)){
if(typeof message!=='object')
Expand Down
3 changes: 2 additions & 1 deletion inc/js/functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ async function logout(ctx){
* @returns {Object[]} - List of hosted members available for login.
*/
async function loginSelect(ctx){
ctx.body = ctx.hostedMembers
const { avatar, } = ctx.state
ctx.body = await avatar.hostedMembers(process.env.MYLIFE_HOSTING_KEY)
}
async function members(ctx){ // members home
await ctx.render('members')
Expand Down
6 changes: 3 additions & 3 deletions inc/js/menu.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class Menu {
}
#setMenu(_Agent){
return [
{ display: `about`, route: '/about', icon: 'about', },
{ display: `walkthrough`, route: 'https://medium.com/@ewbj/mylife-we-save-your-life-480a80956a24', icon: 'gear', },
{ display: `donate`, route: 'https://gofund.me/65013d6e', icon: 'donate', },
{ display: `About`, route: '/about', icon: 'about', },
{ display: `Walkthrough`, route: 'https://medium.com/@ewbj/mylife-we-save-your-life-480a80956a24', icon: 'gear', },
{ display: `Donate`, route: 'https://gofund.me/65013d6e', icon: 'donate', },
]
}
}
Expand Down
30 changes: 13 additions & 17 deletions inc/js/mylife-agent-factory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,12 @@ class MyLifeFactory extends AgentFactory {
super(mPartitionId)
} // no init() for MyLife server
/* public functions */
async addMember(mbr_id){
if(!this.globals.isValidGuid(mbr_id))
return
// Me! Q! I do it! I do it! I do it!
/* but because Q needs to do it, how do I get up one level to avatar? */
}
/**
* Compares registration email against supplied email to confirm `true`. **Note**: does not care if user enters an improper email, it will only fail the encounter, as email structure _is_ confirmed upon initial data write.
* @param {string} email - The supplied email to confirm registration.
Expand Down Expand Up @@ -975,14 +981,9 @@ class MyLifeFactory extends AgentFactory {
async createAccount(birthdate, passphrase){
/* get registration data */
let avatarName,
memberAccount,
success = false
memberAccount = {}
/* create account core */
try {
if(!this.isMyLife) // @stub
throw new Error('MyLife server required for this request')
if(!birthdate?.length || !passphrase?.length)
throw new Error('birthdate _**and**_ passphrase required')
const { avatarName: _avatarName, email, humanName, id, interests, } = this.#registrationData
let { updates='', } = this.#registrationData
if(!id)
Expand Down Expand Up @@ -1016,24 +1017,19 @@ class MyLifeFactory extends AgentFactory {
updates,
validations,
}
memberAccount = await this.dataservices.addCore(core)
memberAccount = await this.dataservices.addCore(core) ?? {}
this.#registrationData = null
success = memberAccount.success
console.log(`MyLife Member Core created for mbr: ${ mbr_id }`, memberAccount)
} catch(error) { console.log(chalk.blueBright('createAccount()::createCore()::error'), chalk.bgRed(error)) }
} catch(error) {
console.log(chalk.blueBright('createAccount()::createCore()::error'), chalk.bgRed(error))
}
/* create avatar */
if(success){
if(Object.keys(memberAccount)?.length){
try{
const { avatar, success: _success, } = await this.dataservices.addAvatar(memberAccount?.core) ?? {}
const { mbr_id, } = avatar
success = _success
console.log(`MyLife Member Avatar created for mbr: ${ mbr_id }`) // then and only then
return await this.dataservices.addAvatar(memberAccount?.core) ?? {}
} catch(error) {
success = false // may have been truthed by _previous_ success
console.log(chalk.blueBright('createAccount()::createAvatar()::error'), chalk.bgRed(error))
}
}
return success
}
createItem(){
throw new Error('MyLife server cannot create items')
Expand Down
80 changes: 79 additions & 1 deletion inc/js/mylife-avatar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ class Avatar extends EventEmitter {
}
class Q extends Avatar {
#factory // same reference as Avatar, but wish to keep private from public interface; don't touch my factory, man!
#hostedMembers = [] // MyLife-hosted members
#llmServices // ref _could_ differ from Avatar, but for now, same
#mode = 'system' // @stub - experience mode for guests
/**
Expand Down Expand Up @@ -1146,6 +1147,70 @@ class Q extends Avatar {
chatMessage = `CREATE ACCOUNT PHASE: ${ chatMessage }`
return super.chatRequest(activeBotId, threadId, chatMessage, conversation, processStartTime)
}
/* public methods */
/**
* Add a member to the hosted members list.
* @param {string} id - The member id (mbr_id).
* @returns {void}
*/
async addMember(id){
if(!this.#hostedMembers.find(member=>member.id===id)){
const memberObject = {
mbr_id: id,
mbr_name: null,
}
const hostedMember = mAvatarDropdown(this.globals, memberObject)
if(hostedMember){
this.#hostedMembers.push(hostedMember)
this.#hostedMembers.sort((a, b) => a.name.localeCompare(b.name))
}
}
}
/**
* Set MyLife core account basics. { birthdate, passphrase, }
* @todo - move to mylife agent factory
* @param {string} birthdate - The birthdate of the member.
* @param {string} passphrase - The passphrase of the member.
* @returns {boolean} - `true` if successful
*/
async createAccount(birthdate, passphrase){
if(!birthdate?.length || !passphrase?.length)
throw new Error('birthdate _**and**_ passphrase required')
let avatar,
success = false
avatar = await this.#factory.createAccount(birthdate, passphrase)
if(Object.keys(avatar).length){
const { mbr_id, } = avatar
success = true
this.addMember(mbr_id)
console.log(`member account created: ${ mbr_id }`)
} else
console.log('member account creation failed')
return {
avatar,
success,
}
}
/**
* Returns list of Q's hostedMembers, using this.#hostedMembers, created on-demand.
* @todo - this.#hostedMembers should contain name data (more than just id) for dropdowns
* @param {Guid} key - The key to handshake against provider.
* @returns {Object[]} - List of hosted member dropdown objects { id, name, }.
*/
async hostedMembers(key){
if(!this.globals.isValidGuid(key) || key!==this.hosting_key)
throw new Error('Invalid key for hosted members.')
if(!this.#hostedMembers.length){ // on-demand creation
console.log('hostedMembers', this.#hostedMembers)
const hostedMembers = await this.#factory.hostedMembers()
if(!hostedMembers.length)
throw new Error('No hosted members found.')
this.#hostedMembers = hostedMembers
.map(avatar=>mAvatarDropdown(this.globals, avatar))
.sort((a, b) => a.name.localeCompare(b.name))
}
return this.#hostedMembers
}
/* getters/setters */
/**
* Get the "avatar's" being, or more precisely the name of the being (affiliated object) the evatar is emulating.
Expand Down Expand Up @@ -1177,6 +1242,19 @@ function mAssignGenericExperienceVariables(experienceVariables, avatar){
}
return {...experienceVariables, ...localOverrides}
}
/**
*
* @param {Globals} globals - Globals object.
* @param {object} avatar - Avatar object.
*/
function mAvatarDropdown(globals, avatar){
const { mbr_id: id, mbr_name, } = avatar
const name = globals.sysName(id)
return {
id,
name,
}
}
/**
* Validates and cleans bot object then updates or creates bot (defaults to new personal-avatar) in Cosmos and returns successful `bot` object, complete with conversation (including thread/thread_id in avatar) and gpt-assistant intelligence.
* @todo Fix occasions where there will be no object_id property to use, as it was created through a hydration method based on API usage, so will be attached to mbr_id, but NOT avatar.id
Expand Down Expand Up @@ -2197,7 +2275,7 @@ async function mValidateRegistration(activeBot, factory, validationId){
const eligible = being==='registration'
&& factory.globals.isValidEmail(registrationEmail)
if(eligible){
const successMessage = `Hello and _thank you_ for your registration, ${ humanName }!\nI'm Q, the ai-representative for MyLife, and I'm excited to help you get started, so let's do the following:\n1. Verify your email address\n2. set up your account\n3. get you started with your first MyLife experience!\n\nSo let me walk you through the process. In the chat below, please enter the email you registered with and hit the **submit** button!`
const successMessage = `Hello and _thank you_ for your registration, ${ humanName }!\nI'm Q, the ai-representative for MyLife, and I'm excited to help you get started, so let's do the following:\n1. Verify your email address\n2. set up your account\n3. get you started with your first MyLife experience!\n<br />\n<br />Let me walk you through the process.<br />In the chat below, please enter the email you registered with and hit the <b>submit</b> button!`
message = mCreateSystemMessage(activeBot, successMessage, factory)
registrationData.avatarName = avatarName ?? humanName ?? 'My AI-Agent'
registrationData.humanName = humanName
Expand Down
4 changes: 1 addition & 3 deletions inc/js/mylife-data-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ class Dataservices {
async addAvatar(core){
if(!this.isMyLife)
throw new Error('MyLife avatar required for addAvatar()', this.mbr_id)
const avatar = await this.pushItem(mAvatarProperties(core, this.globals))
console.log('addAvatar', avatar, core)
return { avatar, success: typeof avatar==='object', }
return await this.pushItem(mAvatarProperties(core, this.globals))
}
/**
* Upon MyLife account creation, generates `core` and saves to database.
Expand Down
2 changes: 1 addition & 1 deletion inc/js/mylife-llm-services.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ async function mRunFunctions(openai, run, factory, avatar){ // add avatar ref
if(!passphrase)
action += 'passphrase missing, elicit passphrase; '
try {
success = await factory.createAccount(birthdate, passphrase)
success = await avatar.createAccount(birthdate, passphrase)
action = success
? `congratulate member on creating their MyLife membership, display \`passphrase\` in bold for review (or copy/paste), and explain that once the system processes their membership they will be able to use the login button at the top right.`
: action + 'server failure for `factory.createAccount()`'
Expand Down
1 change: 1 addition & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ MYLIFE_DB_NAME=membership
MYLIFE_DB_RW=string # add your key here
MYLIFE_DB_RX=string # add your key here
MYLIFE_SERVER_MBR_ID=mylife|... # add your key here
MYLIFE_HOSTING_KEY=uuid # add your guid key here
MYLIFE_HOSTED_MBR_ID=[] # array of ids hosted on your server--OR--remove or leave empty for db host under full database container
MYLIFE_SESSION_KEY=0.0.8 # random string for resetting sessions
MYLIFE_SESSION_TIMEOUT_MS=900000
Expand Down
13 changes: 2 additions & 11 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const port = JSON.parse(process.env.PORT ?? '3000')
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const _Maht = await MyLife // Mylife is the pre-instantiated exported version of organization with very unique properties. MyLife class can protect fields that others cannot, #factory as first refactor will request
if(!process.env.MYLIFE_HOSTING_KEY || process.env.MYLIFE_HOSTING_KEY !== _Maht.avatar.hosting_key)
throw new Error('Invalid hosting key. Server will not start.')
const MemoryStore = new session.MemoryStore()
const mimeTypesToExtensions = {
// Text Formats
Expand Down Expand Up @@ -93,16 +95,6 @@ if(!fs.existsSync(uploadDir)){
app.context.MyLife = _Maht
app.context.Globals = _Maht.globals
app.context.menu = _Maht.menu
const hostedMembers = JSON.parse(process.env?.MYLIFE_HOSTED_MBR_ID ?? '[]')
if(!hostedMembers.length){
const members = ( await _Maht.hostedMembers() )
.map(member=>member.mbr_id)
hostedMembers.push(...members) // throws on empty
}
console.log(chalk.bgBlue('hosted-members', chalk.yellowBright(hostedMembers.join(', '))))
app.context.hostedMembers = hostedMembers
.sort((a, b)=>a.localeCompare(b))
.map(mbr_id=>({ 'id': mbr_id, 'name': _Maht.globals.sysName(mbr_id) }))
app.keys = [process.env.MYLIFE_SESSION_KEY ?? `mylife-session-failsafe|${_Maht.newGuid()}`]
// Enable Koa body w/ configuration
app.use(koaBody({
Expand Down Expand Up @@ -176,7 +168,6 @@ app.use(koaBody({
ctx.state.menu = ctx.MyLife.menu
if(!await ctx.state.MemberSession.requestConsent(ctx))
ctx.throw(404,'asset request rejected by consent')

await next()
})
.use(async(ctx,next) => { // alert check
Expand Down
4 changes: 2 additions & 2 deletions views/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ <h3>Our Concepts</h3>
<section id="about-products-services" class="about-products-services about-section">
<h3>Products/Services</h3>
<p>MyLife offers an array of member services focusing on the capture, curation, and storage of personal media. Our key product <b>is</b> the MyLife platform for personal avatars to embody and showcase materials and memorials. We also provide trusted partner API access and initiatives to connect students with posterity archiving. Our commitment extends to supporting artistic, technical, and educational projects aligned with our mission.</p>
<p>While you will find the most current info with Q, you can take a look at one of our <a href="./assets/pdf/MyLife_Summary.pdf">original outreach artifacts here</a>.</p>
<p>While you will find the most current info with Q, you can take a look at one of our <a href="./pdf/MyLife_Summary.pdf">original outreach artifacts here</a>.</p>
</section>
<section id="about-privacy-policy" class="about-privacy-policy about-section">
<h3>Privacy Policy</h3>
<p><a href="privacy-policy">View our evolving privacy policy</a>.</p>
</section>
<section id="about-contact" class="avout-contact about-section">
<h3>Contact</h3>
<p>To learn more about MyLife, chat with Q, but we are eager to assist you with any inquiries and provide further information about our services, currently in alpha. Email the President, Erik Jespersen at: mylife.president[at]gmail.com</p>
<p><a href="./">To learn more about MyLife, chat with Q</a>, but we are eager to assist you with any inquiries and provide further information about our services, currently in alpha. Email the President, Erik Jespersen at: mylife.president[at]gmail.com</p>
</section>
</section>
Loading

0 comments on commit cbda4ff

Please sign in to comment.