diff --git a/main.tsx b/main.tsx index 87036d2..4d99124 100644 --- a/main.tsx +++ b/main.tsx @@ -10,7 +10,9 @@ import {cat} from './src/ipfs/cat' import {add} from './src/ipfs/add' import { ethers, Signer, Provider, JsonRpcProvider, Wallet } from 'ethers'; import ExampleClient from './artifacts/ExampleClient.json' - +import { getMolecule} from './src/plex/getMolecule' +import { getProtein} from './src/plex/getProtein' +import { runEquibind } from './src/plex/runEquibind' import { ObsidianDesciSettings, ObsidianDesciSettingTab, @@ -65,7 +67,26 @@ export default class ObsidianDesci extends Plugin { name: 'ipfsAdd - Add Json objects referenced by CID', callback: add.bind(this) }); + this.addCommand({ + id: 'getMolecule', + name: 'getMolecule -fetch an .spf file for plex', + callback: getMolecule.bind(this) + }), + this.addCommand({ + id: 'getProtein', + name: 'getProtein - fetch a .pdb file for plex', + callback: getProtein.bind(this) + }) + this.addCommand({ + id: 'runEquibind', + name: 'runEquibind - run equibind on a molecule and protein node', + callback: runEquibind.bind(this) + }) this.addSettingTab(new ObsidianDesciSettingTab(this.app, this)); + + + this.registerExtensions(["sdf"], "markdown"); + this.registerExtensions(["pdb"], "markdown"); } onunload() { diff --git a/package-lock.json b/package-lock.json index a6702a2..c340b0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@types/node": "^16.11.6", + "@types/node": "^16.18.61", "@types/pngjs": "^6.0.3", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", @@ -3687,9 +3687,9 @@ } }, "node_modules/@types/node": { - "version": "16.18.48", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz", - "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==" + "version": "16.18.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.61.tgz", + "integrity": "sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==" }, "node_modules/@types/pbkdf2": { "version": "3.1.0", diff --git a/package.json b/package.json index 9e722ea..950c6f4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "devDependencies": { - "@types/node": "^16.11.6", + "@types/node": "^16.18.61", "@types/pngjs": "^6.0.3", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", diff --git a/src/plex/equibind.py b/src/plex/equibind.py new file mode 100644 index 0000000..88544b1 --- /dev/null +++ b/src/plex/equibind.py @@ -0,0 +1,19 @@ +from plex import CoreTools, plex_init, plex_run +import sys +import json +import os + +dir_path = sys.argv[1] +protein_path = [f"{dir_path}/{sys.argv[2]}"] +small_molecule_path = [f"{dir_path}/{sys.argv[3]}"] +wallet_address = sys.argv[4] + +os.environ["RECIPIENT_WALLET"] = wallet_address + +initial_io_cid = plex_init( + CoreTools.EQUIBIND.value, + protein=protein_path, + small_molecule=small_molecule_path, +) + +completed_io_cid, io_local_filepath = plex_run(initial_io_cid, dir_path) diff --git a/src/plex/getMolecule.ts b/src/plex/getMolecule.ts new file mode 100644 index 0000000..809f123 --- /dev/null +++ b/src/plex/getMolecule.ts @@ -0,0 +1,97 @@ +import { requestUrl, Vault } from 'obsidian' +import { CanvasNode } from 'src/utils/canvas-internal' +import * as fs from 'fs' +import { + createNode, + placeholderNoteHeight, + assistantColor, + getNodeText +} from '../utils/canvas-util' + +import { CID } from 'multiformats/cid' + +export const getMolecule = async function () { + if (this.unloaded) return + + this.logDebug("attempting to fetch from ipfs") + + const canvas = this.getActiveCanvas() + if (!canvas) { + this.logDebug('No active canvas') + return + } + const selection = canvas.selection + if (selection?.size !== 1) return + const values: CanvasNode[] = Array.from(selection.values()) + const node: CanvasNode = values[0] + if (node) { + await canvas.requestSave() + await sleep(200) + + const settings = this.settings + + const nodeData = node.getData() + let nodeText = await getNodeText(node) || '' + if (nodeText.length == 0) { + this.logDebug('no node Text found') + return + } + + const created = createNode(canvas, node, + { + text: `attempting to fetch ${nodeText}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + try { + const res = await requestUrl({ + url: nodeText, + method: 'GET', + headers: { + //"Content-Type": "text/plain", + } + }) + console.log('res', res.arrayBuffer) + const regex = /\/([^/]+)$/; + const matches = nodeText.match(regex); + const filename = matches ? matches[1] : ""; + const buffer = Buffer.from(res.arrayBuffer); + fs.writeFile(`${this.app.vault.adapter.basePath}/${filename}`, buffer, 'base64', (error) => { + if (error) { + console.error('Error saving image:', error); + } else { + const file = this.app.vault.getAbstractFileByPath(`${filename}`) + const cidNode = createNode(canvas, created, + { + file, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + } + }); + + + } catch (e) { + const cideNodeError = createNode(canvas, created, + { + text: `error: ${e}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + } + } +} \ No newline at end of file diff --git a/src/plex/getProtein.ts b/src/plex/getProtein.ts new file mode 100644 index 0000000..e71d262 --- /dev/null +++ b/src/plex/getProtein.ts @@ -0,0 +1,97 @@ +import { requestUrl, Vault } from 'obsidian' +import { CanvasNode } from 'src/utils/canvas-internal' +import * as fs from 'fs' +import { + createNode, + placeholderNoteHeight, + assistantColor, + getNodeText +} from '../utils/canvas-util' + +import { CID } from 'multiformats/cid' + +export const getProtein = async function () { + if (this.unloaded) return + + this.logDebug("attempting to fetch from ipfs") + + const canvas = this.getActiveCanvas() + if (!canvas) { + this.logDebug('No active canvas') + return + } + const selection = canvas.selection + if (selection?.size !== 1) return + const values: CanvasNode[] = Array.from(selection.values()) + const node: CanvasNode = values[0] + if (node) { + await canvas.requestSave() + await sleep(200) + + const settings = this.settings + + const nodeData = node.getData() + let nodeText = await getNodeText(node) || '' + if (nodeText.length == 0) { + this.logDebug('no node Text found') + return + } + + const created = createNode(canvas, node, + { + text: `attempting to fetch ${nodeText}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + try { + const res = await requestUrl({ + url: nodeText, + method: 'GET', + headers: { + //"Content-Type": "text/plain", + } + }) + console.log('res', res.arrayBuffer) + const regex = /\/([^/]+)$/; + const matches = nodeText.match(regex); + const filename = matches ? matches[1] : ""; + const buffer = Buffer.from(res.arrayBuffer); + fs.writeFile(`${this.app.vault.adapter.basePath}/${filename}`, buffer, 'base64', (error) => { + if (error) { + console.error('Error saving image:', error); + } else { + const file = this.app.vault.getAbstractFileByPath(`${filename}`) + const cidNode = createNode(canvas, created, + { + file, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + } + }); + + + } catch (e) { + const cideNodeError = createNode(canvas, created, + { + text: `error: ${e}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + } + } +} \ No newline at end of file diff --git a/src/plex/runEquibind.ts b/src/plex/runEquibind.ts new file mode 100644 index 0000000..07398ce --- /dev/null +++ b/src/plex/runEquibind.ts @@ -0,0 +1,140 @@ +import { requestUrl, Vault } from 'obsidian' +import { CanvasNode } from 'src/utils/canvas-internal' +import * as fs from 'fs' +import { + createNode, + placeholderNoteHeight, + assistantColor, + getNodeText +} from '../utils/canvas-util' +import { exec } from 'child_process' +import { CID } from 'multiformats/cid' + +export const runEquibind = async function () { + if (this.unloaded) return + + const canvas = this.getActiveCanvas() + if (!canvas) { + this.logDebug('No active canvas') + return + } + const selection = canvas.selection + if (selection?.size !== 2) return + const values: CanvasNode[] = Array.from(selection.values()) + const node: CanvasNode = values[0] + const node2: CanvasNode = values[1] + if (node) { + await canvas.requestSave() + await sleep(200) + + const settings = this.settings + + if (!node2.filePath && !node2.filePath) { + this.logDebug('no files on path') + return + } + let proteinPath: string | undefined; + let moleculePath: string | undefined; + + if (node.filePath?.endsWith('.pdb')) { + proteinPath = node.filePath; + } else if (node2.filePath?.endsWith('.pdb')) { + proteinPath = node2.filePath; + } + + if (node.filePath?.endsWith('.sdf')) { + moleculePath = node.filePath; + } else if (node2.filePath?.endsWith('.sdf')) { + moleculePath = node2.filePath; + } + console.log('moleculePath', moleculePath) + console.log('proteinPath', proteinPath) + + + const created = createNode(canvas, node, + { + text: `attempting equibind on ${moleculePath} and ${proteinPath}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + try { + const command = `${this.app.vault.adapter.basePath}/${this.app.vault.configDir}/plugins/obsidian-desci/src/plex/equibind.py` + const equibind = exec(`python3 ${command} ${this.app.vault.adapter.basePath} ${proteinPath} ${moleculePath} ${this.settings.publicKey}`) + equibind.stdout?.on('data', (data) => { + console.log('stdout:data:', data) + const regex = /Completed IO JSON CID: (\w+)/; + const match = data.match(regex); + const cid = match ? match[1] : ""; + console.log(cid); // Output: QmbijdVrr + + const cideNodeError = createNode(canvas, created, + { + text: `${cid}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + }) + equibind.stderr?.on('data', (data) => { + console.log('stderr:data:', data) + }) + equibind.on('close', (code) => { + console.log('equibind exited with code:', code) + }) + /* + const res = await requestUrl({ + url: nodeText, + method: 'GET', + headers: { + //"Content-Type": "text/plain", + } + }) + console.log('res', res.arrayBuffer) + + const regex = /\/([^/]+)$/; + const matches = nodeText.match(regex); + const filename = matches ? matches[1] : ""; + const buffer = Buffer.from(res.arrayBuffer); + fs.writeFile(`${this.app.vault.adapter.basePath}/${filename}`, buffer, 'base64', (error) => { + if (error) { + console.error('Error saving image:', error); + } else { + const file = this.app.vault.getAbstractFileByPath(`${filename}`) + const cidNode = createNode(canvas, created, + { + file, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + } + }); + */ + + } catch (e) { + const cideNodeError = createNode(canvas, created, + { + text: `error: ${e}`, + size: { height: placeholderNoteHeight } + }, + { + color: assistantColor, + chat_role: 'assistant' + } + ) + + } + } +} \ No newline at end of file diff --git a/src/settings.ts b/src/settings.ts index 8beafce..2b266ba 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,6 +1,6 @@ import type ObsidianDesci from '../main' import { App, Plugin, PluginSettingTab, Setting } from 'obsidian'; - +import {ethers} from 'ethers' interface ChainConfig { name: string; rpcUrl: string; @@ -9,12 +9,14 @@ interface ChainConfig { export interface ObsidianDesciSettings { privateKey: string; + publicKey: string; chain: ChainConfig; kuboRpc: string; } export const DEFAULT_SETTINGS: ObsidianDesciSettings = { privateKey: '', + publicKey: '', chain: { name: 'lilypad', rpcUrl: 'http://testnet.lilypadnetwork.org:8545', @@ -44,6 +46,7 @@ export class ObsidianDesciSettingTab extends PluginSettingTab { .setValue(this.plugin.settings.privateKey) .onChange(async (value) => { this.plugin.settings.privateKey = value; + this.plugin.settings.publicKey = new ethers.Wallet(value).address, await this.plugin.saveSettings(); })); new Setting(containerEl) diff --git a/styles.css b/styles.css index 71cc60f..ec6605f 100644 --- a/styles.css +++ b/styles.css @@ -6,3 +6,12 @@ available in the app when your plugin is enabled. If your plugin does not need CSS, delete this file. */ + +div[data-path^="sdf"]{ +color: #9999996e; +display: flex; +} +div[data-path*="sdf"]{ +color: #9999996e; +display: flex; +} \ No newline at end of file