diff --git a/apps/shinkai-app/src/pages/Connect.tsx b/apps/shinkai-app/src/pages/Connect.tsx index f06d23da2..ee2681caa 100644 --- a/apps/shinkai-app/src/pages/Connect.tsx +++ b/apps/shinkai-app/src/pages/Connect.tsx @@ -83,7 +83,7 @@ const Connect = () => { registration_name: 'main_device', identity_type: 'device', permission_type: 'admin', - shinkai_identity: '@@node1.shinkai', // this should actually be read from ENV + shinkai_identity: '@@localhost.shinkai', // this should actually be read from ENV node_encryption_pk: '', node_signature_pk: '', profile_encryption_sk: '', @@ -656,7 +656,7 @@ function AutomaticForm() { registration_name: 'main_device', identity_type: 'device', permission_type: 'admin', - shinkai_identity: '@@node1.shinkai', // this should actually be read from ENV + shinkai_identity: '@@localhost.shinkai', // this should actually be read from ENV node_encryption_pk: '', node_signature_pk: '', profile_encryption_sk: '', diff --git a/apps/shinkai-app/src/pages/Home.tsx b/apps/shinkai-app/src/pages/Home.tsx index e47ecb1df..bd11aa32b 100644 --- a/apps/shinkai-app/src/pages/Home.tsx +++ b/apps/shinkai-app/src/pages/Home.tsx @@ -1,30 +1,153 @@ import './Home.css'; +import { zodResolver } from '@hookform/resolvers/zod'; import { IonActionSheet, IonAlert, IonButton, IonButtons, IonIcon, + IonInput, IonItem, + IonLabel, IonPage, IonText, IonTitle, } from '@ionic/react'; +import { SmartInbox } from '@shinkai_network/shinkai-message-ts/models'; +import { useUpdateInboxName } from '@shinkai_network/shinkai-node-state/lib/mutations/updateInboxName/useUpdateInboxName'; import { useGetInboxes } from '@shinkai_network/shinkai-node-state/lib/queries/getInboxes/useGetInboxes'; import { addOutline, arrowForward } from 'ionicons/icons'; +import { Edit3Icon } from 'lucide-react'; import React, { useState } from 'react'; +import { Controller, useForm } from 'react-hook-form'; import { useHistory } from 'react-router-dom'; +import { z } from 'zod'; import Avatar from '../components/ui/Avatar'; +import Button from '../components/ui/Button'; import { IonContentCustom, IonHeaderCustom } from '../components/ui/Layout'; +import { useSetup } from '../hooks/usetSetup'; import { useAuth } from '../store/auth'; +const updateInboxNameSchema = z.object({ + inboxName: z.string(), +}); + +const MessageButton = ({ inbox }: { inbox: SmartInbox }) => { + const history = useHistory(); + const auth = useAuth((state) => state.auth); + const [isEditable, setIsEditable] = useState(false); + const updateInboxNameForm = useForm>({ + resolver: zodResolver(updateInboxNameSchema), + }); + + const { inboxName: inboxNameValue } = updateInboxNameForm.watch(); + const { mutateAsync: updateInboxName } = useUpdateInboxName(); + + const onSubmit = async (data: z.infer) => { + if (!auth) return; + console.log('data', data); + await updateInboxName({ + sender: auth?.shinkai_identity ?? '', + senderSubidentity: auth?.profile, + receiver: auth.shinkai_identity, + receiverSubidentity: '', + my_device_encryption_sk: auth.my_device_encryption_sk, + my_device_identity_sk: auth.my_device_identity_sk, + node_encryption_pk: auth.node_encryption_pk, + profile_encryption_sk: auth.profile_encryption_sk, + profile_identity_sk: auth.profile_identity_sk, + inboxId: decodeURIComponent(inbox.inbox_id), + inboxName: data.inboxName, + }); + setIsEditable(false); + }; + + if (isEditable) { + return ( +
+ ( +
+ Rename inbox name + + updateInboxNameForm.setValue( + 'inboxName', + e.detail.value as string + ) + } + placeholder={decodeURIComponent(inbox.custom_name)} + value={field.value} + /> +
+ )} + /> + {inboxNameValue ? ( + + ) : ( + + )} + + ); + } + + return ( +
+ { + const encodedInboxId = inbox.inbox_id.toString().replace(/\./g, '~'); + if (encodedInboxId.startsWith('inbox')) { + history.push(`/chat/${encodeURIComponent(encodedInboxId)}`); + } else if (encodedInboxId.startsWith('job_inbox')) { + history.push(`/job-chat/${encodeURIComponent(encodedInboxId)}`); + } + }} + > + + + {decodeURIComponent(inbox.custom_name)} + + + + +
+ ); +}; + const Home: React.FC = () => { + useSetup(); const auth = useAuth((state) => state.auth); const setLogout = useAuth((state) => state.setLogout); - const { inboxIds } = useGetInboxes({ + const { inboxes } = useGetInboxes({ sender: auth?.shinkai_identity ?? '', senderSubidentity: `${auth?.profile}/device/${auth?.registration_name}`, // Assuming receiver and target_shinkai_name_profile are the same as sender @@ -74,34 +197,8 @@ const Home: React.FC = () => {
- {inboxIds?.map((inboxId) => ( - { - const encodedInboxId = inboxId.toString().replace(/\./g, '~'); - if (encodedInboxId.startsWith('inbox')) { - history.push(`/chat/${encodeURIComponent(encodedInboxId)}`); - } else if (encodedInboxId.startsWith('job_inbox')) { - history.push( - `/job-chat/${encodeURIComponent(encodedInboxId)}` - ); - } - }} - > - - - {JSON.stringify(inboxId)} - - - + {inboxes?.map((inbox) => ( + ))}
diff --git a/apps/shinkai-visor/src/components/inboxes/inboxes.tsx b/apps/shinkai-visor/src/components/inboxes/inboxes.tsx index 7d6a077b4..ef8e5beb6 100644 --- a/apps/shinkai-visor/src/components/inboxes/inboxes.tsx +++ b/apps/shinkai-visor/src/components/inboxes/inboxes.tsx @@ -1,4 +1,7 @@ -import { isJobInbox } from '@shinkai_network/shinkai-message-ts/utils'; +import { + getMessageContent, + isJobInbox, +} from '@shinkai_network/shinkai-message-ts/utils'; import { useAgents } from '@shinkai_network/shinkai-node-state/lib/queries/getAgents/useGetAgents'; import { useGetInboxes } from '@shinkai_network/shinkai-node-state/lib/queries/getInboxes/useGetInboxes'; import { @@ -33,7 +36,7 @@ export const Inboxes = () => { const dialContainerRef = useRef(null); const [dialOpened, setDialOpened] = useState(false); const sender = auth?.shinkai_identity ?? ''; - const { inboxIds } = useGetInboxes({ + const { inboxes } = useGetInboxes({ sender: auth?.shinkai_identity ?? '', senderSubidentity: `${auth?.profile}/device/${auth?.registration_name}`, // Assuming receiver and target_shinkai_name_profile are the same as sender @@ -73,27 +76,29 @@ export const Inboxes = () => { /> {!agents?.length ? ( - ) : !inboxIds?.length ? ( + ) : !inboxes?.length ? ( ) : ( <>
- {inboxIds?.map((inboxId) => ( - + {inboxes?.map((inbox) => ( + diff --git a/libs/shinkai-message-ts/package.json b/libs/shinkai-message-ts/package.json index fc42e0a49..025d4e244 100644 --- a/libs/shinkai-message-ts/package.json +++ b/libs/shinkai-message-ts/package.json @@ -1,6 +1,6 @@ { "name": "@shinkai_network/shinkai-message-ts", - "version": "0.0.12", + "version": "0.0.13", "description": "Shinkai Message TS wrapper for wasm library", "type": "module", "main": "src/index.js", diff --git a/libs/shinkai-message-ts/src/api/methods.ts b/libs/shinkai-message-ts/src/api/methods.ts index c7eb10753..76e00c0bf 100644 --- a/libs/shinkai-message-ts/src/api/methods.ts +++ b/libs/shinkai-message-ts/src/api/methods.ts @@ -5,6 +5,7 @@ import { LastMessagesFromInboxCredentialsPayload, SetupPayload, ShinkaiMessage, + SmartInbox, } from '../models'; import { APIUseRegistrationCodeSuccessResponse } from '../models/Payloads'; import { SerializedAgent } from '../models/SchemaTypes'; @@ -198,23 +199,24 @@ export const getAllInboxesForProfile = async ( receiver: string, target_shinkai_name_profile: string, setupDetailsState: CredentialsPayload -): Promise => { +): Promise => { try { - const messageStr = ShinkaiMessageBuilderWrapper.get_all_inboxes_for_profile( - setupDetailsState.my_device_encryption_sk, - setupDetailsState.my_device_identity_sk, - setupDetailsState.node_encryption_pk, - sender + '/' + sender_subidentity, - '', - receiver, - target_shinkai_name_profile - ); + const messageString = + ShinkaiMessageBuilderWrapper.get_all_inboxes_for_profile( + setupDetailsState.my_device_encryption_sk, + setupDetailsState.my_device_identity_sk, + setupDetailsState.node_encryption_pk, + sender, + sender_subidentity, + receiver, + target_shinkai_name_profile + ); - const message = JSON.parse(messageStr); + const message = JSON.parse(messageString); const apiEndpoint = ApiConfig.getInstance().getEndpoint(); const response = await fetch( - `${apiEndpoint}/v1/get_all_inboxes_for_profile`, + `${apiEndpoint}/v1/get_all_smart_inboxes_for_profile`, { method: 'POST', body: JSON.stringify(message), @@ -230,6 +232,47 @@ export const getAllInboxesForProfile = async ( } }; +export const updateInboxName = async ( + sender: string, + sender_subidentity: string, + receiver: string, + receiver_subidentity: string, + setupDetailsState: CredentialsPayload, + inboxName: string, + inboxId: string + // TODO: fix type +): Promise => { + try { + const messageString = + ShinkaiMessageBuilderWrapper.update_shinkai_inbox_name( + setupDetailsState.profile_encryption_sk, + setupDetailsState.profile_identity_sk, + setupDetailsState.node_encryption_pk, + sender, + sender_subidentity, + receiver, + "", + inboxId, + inboxName + ); + + const message = JSON.parse(messageString); + + const apiEndpoint = ApiConfig.getInstance().getEndpoint(); + const response = await fetch(`${apiEndpoint}/v1/update_smart_inbox_name`, { + method: 'POST', + body: JSON.stringify(message), + headers: { 'Content-Type': 'application/json' }, + }); + const data = await response.json(); + await handleHttpError(response); + return data.data; + } catch (error) { + console.error('Error updating inbox name:', error); + throw error; + } +}; + export const getLastMessagesFromInbox = async ( inbox: string, count: number, diff --git a/libs/shinkai-message-ts/src/models/ShinkaiMessage.ts b/libs/shinkai-message-ts/src/models/ShinkaiMessage.ts index 851f1e8a2..e329b3fc5 100644 --- a/libs/shinkai-message-ts/src/models/ShinkaiMessage.ts +++ b/libs/shinkai-message-ts/src/models/ShinkaiMessage.ts @@ -50,3 +50,9 @@ export interface RegistrationCode { encryptionPk: string; permissionType: string; } + +export type SmartInbox = { + custom_name: string; + inbox_id: string; + last_message: ShinkaiMessage; +}; \ No newline at end of file diff --git a/libs/shinkai-message-ts/src/wasm/ShinkaiMessageBuilderWrapper.ts b/libs/shinkai-message-ts/src/wasm/ShinkaiMessageBuilderWrapper.ts index e4274217f..3654aa696 100644 --- a/libs/shinkai-message-ts/src/wasm/ShinkaiMessageBuilderWrapper.ts +++ b/libs/shinkai-message-ts/src/wasm/ShinkaiMessageBuilderWrapper.ts @@ -277,8 +277,9 @@ export class ShinkaiMessageBuilderWrapper { 'None' ); builder.external_metadata(receiver, sender); + // TODO: fix encryption builder.body_encryption( - 'None' // todo: change back to encrypted + 'None' ); const message = builder.build_to_string(); @@ -464,10 +465,7 @@ export class ShinkaiMessageBuilderWrapper { 'None' ); builder.external_metadata_with_intra(receiver, sender, sender_subidentity); - // TODO: At this point we are forcing unencrypted message until we implement message response in shinkai-node - builder.body_encryption( - 'None' - ); + builder.body_encryption('None'); const message = builder.build_to_string(); @@ -501,10 +499,7 @@ export class ShinkaiMessageBuilderWrapper { ); builder.external_metadata_with_intra(receiver, sender, sender_subidentity); - // TODO: At this point we are forcing unencrypted message until we implement message response in shinkai-node - builder.body_encryption( - 'None' - ); + builder.body_encryption('None'); const message = builder.build_to_string(); @@ -542,4 +537,37 @@ export class ShinkaiMessageBuilderWrapper { const message = builder.build_to_string(); return message; } + + static update_shinkai_inbox_name( + my_encryption_secret_key: string, + my_signature_secret_key: string, + receiver_public_key: string, + sender: string, + sender_subidentity: string, + receiver: string, + receiver_subidentity: string, + inbox: string, + inbox_name: string + ): string { + const builder = new ShinkaiMessageBuilderWrapperWASM( + my_encryption_secret_key, + my_signature_secret_key, + receiver_public_key + ); + builder.message_raw_content(inbox_name); + builder.message_schema_type(MessageSchemaType.TextContent.toString()); + builder.internal_metadata_with_inbox( + sender_subidentity, + receiver_subidentity, + inbox, + 'None' + ); + builder.external_metadata_with_intra(receiver, sender, sender_subidentity); + // TODO: fix encryption + builder.body_encryption('DiffieHellmanChaChaPoly1305'); + + const message = builder.build_to_string(); + + return message; + } } diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts new file mode 100644 index 000000000..3906d0ab8 --- /dev/null +++ b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/index.ts @@ -0,0 +1,57 @@ +import { + updateInboxName as updateInboxNameApi, +} from '@shinkai_network/shinkai-message-ts/api'; +import { + type ShinkaiMessage, +} from "@shinkai_network/shinkai-message-ts/models"; +export type SmartInbox = { + custom_name: string; + inbox_id: string; + last_message: ShinkaiMessage; +}; + + +export const updateInboxName = async ({ + senderSubidentity, + sender, + receiver, + receiverSubidentity, + my_device_encryption_sk, + my_device_identity_sk, + node_encryption_pk, + profile_encryption_sk, + profile_identity_sk, // eslint-disable-next-line @typescript-eslint/no-explicit-any + inboxName, + inboxId, // eslint-disable-next-line @typescript-eslint/no-explicit-any +}: { + senderSubidentity: string; + sender: string; + receiver: string; + receiverSubidentity: string; + my_device_encryption_sk: string; + my_device_identity_sk: string; + node_encryption_pk: string; + profile_encryption_sk: string; + profile_identity_sk: string; + inboxName: string; + inboxId: string; +}) => { + const response = await updateInboxNameApi( + sender, + senderSubidentity, + receiver, + receiverSubidentity, + { + my_device_encryption_sk, + my_device_identity_sk, + node_encryption_pk, + profile_encryption_sk, + profile_identity_sk, + }, + inboxName, + inboxId + ); + console.log("updateInboxName response:", response); + + return response; +}; diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts new file mode 100644 index 000000000..7f441d27b --- /dev/null +++ b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/types.ts @@ -0,0 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export type UpdateInboxNamebInput = any; + +export type UpdateInboxNameOutput = any; diff --git a/libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts new file mode 100644 index 000000000..f14c24f03 --- /dev/null +++ b/libs/shinkai-node-state/src/lib/mutations/updateInboxName/useUpdateInboxName.ts @@ -0,0 +1,15 @@ +import type { UseMutationOptions } from "@tanstack/react-query"; +import { useMutation } from "@tanstack/react-query"; + +import { updateInboxName } from "."; +import type { UpdateInboxNamebInput,UpdateInboxNameOutput } from "./types"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Options = UseMutationOptions; + +export const useUpdateInboxName = (options?: Options) => { + return useMutation({ + mutationFn: updateInboxName, + ...options, + }); +}; diff --git a/libs/shinkai-node-state/src/lib/queries/getInboxes/index.ts b/libs/shinkai-node-state/src/lib/queries/getInboxes/index.ts index bd040b88f..d730288ae 100644 --- a/libs/shinkai-node-state/src/lib/queries/getInboxes/index.ts +++ b/libs/shinkai-node-state/src/lib/queries/getInboxes/index.ts @@ -26,5 +26,8 @@ export const getInboxes = async ({ profile_identity_sk, } ); - return inboxes.map((inbox) => encodeURIComponent(inbox)); -}; + return inboxes.map((inbox) => ({ + ...inbox, + inbox_id: encodeURIComponent(inbox.inbox_id), + custom_name: encodeURIComponent(inbox.custom_name), + }));}; diff --git a/libs/shinkai-node-state/src/lib/queries/getInboxes/useGetInboxes.ts b/libs/shinkai-node-state/src/lib/queries/getInboxes/useGetInboxes.ts index a50a23e0d..1bbf2cfa2 100644 --- a/libs/shinkai-node-state/src/lib/queries/getInboxes/useGetInboxes.ts +++ b/libs/shinkai-node-state/src/lib/queries/getInboxes/useGetInboxes.ts @@ -11,6 +11,6 @@ export const useGetInboxes = (input: GetInboxesInput) => { }); return { ...response, - inboxIds: response.data ?? [], + inboxes: response.data ?? [], }; };