diff --git a/JS/edgechains/arakoodev/package.json b/JS/edgechains/arakoodev/package.json index 2eda371f..6c16a87e 100644 --- a/JS/edgechains/arakoodev/package.json +++ b/JS/edgechains/arakoodev/package.json @@ -13,10 +13,10 @@ "./arakooserver": "./dist/arakooserver/src/index.js", "./db": "./dist/db/src/index.js", "./scraper": "./dist/scraper/src/index.js", - "./sync-rpc": "./dist/sync-rpc/export.js" + "./sync-rpc": "./dist/sync-rpc/index.js" }, "scripts": { - "build": "rm -rf dist && tsc -b && cp -r src/sync-rpc dist/sync-rpc", + "build": "rm -rf dist && tsc -b", "lint": "eslint --ignore-path .eslintignore --ext .js,.ts", "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"", "test": "vitest" diff --git a/JS/edgechains/arakoodev/src/sync-rpc/export.js b/JS/edgechains/arakoodev/src/sync-rpc/export.js deleted file mode 100644 index cdc7da59..00000000 --- a/JS/edgechains/arakoodev/src/sync-rpc/export.js +++ /dev/null @@ -1,9 +0,0 @@ -if (process.env.arakoo) { - module.exports = function (path) { - // should not run - console.debug("This function should not run"); - }; -} else { - const createClient = require("./index.js"); - module.exports = createClient; -} diff --git a/JS/edgechains/arakoodev/src/sync-rpc/find-port.js b/JS/edgechains/arakoodev/src/sync-rpc/find-port.js deleted file mode 100644 index 97aff974..00000000 --- a/JS/edgechains/arakoodev/src/sync-rpc/find-port.js +++ /dev/null @@ -1,14 +0,0 @@ -// const getPort = require('get-port'); -// import getPort from "get-port"; -// const getPort = require("get-port"); - -import("get-port").then((mod) => { - let getPort = mod.default; - getPort() - .then((port) => process.stdout.write("" + port)) - .catch((err) => - setTimeout(() => { - throw err; - }, 0) - ); -}); diff --git a/JS/edgechains/arakoodev/src/sync-rpc/index.js b/JS/edgechains/arakoodev/src/sync-rpc/index.js deleted file mode 100644 index 51b59466..00000000 --- a/JS/edgechains/arakoodev/src/sync-rpc/index.js +++ /dev/null @@ -1,178 +0,0 @@ -// const func = require("../somejs"); -const spawn = require("child_process").spawn; -const spawnSync = require("child_process").spawnSync; - -const host = "127.0.0.1"; -const FUNCTION_PRIORITY = [nativeNC, nodeNC]; -let started = false; -const configuration = { port: null, fastestFunction: null }; - -function nodeNetCatSrc(port, input) { - // TODO: rewrite with explanation - return ( - "var c=require('net').connect(" + - port + - ",'127.0.0.1',()=>{c.pipe(process.stdout);c.end(" + - JSON.stringify(input) - .replace(/\u2028/g, "\\u2028") - .replace(/\u2029/g, "\\u2029") + - ")})" - ); -} - -function nativeNC(port, input) { - return spawnSync("nc", [host, port], { - input: input, - windowsHide: true, - maxBuffer: Infinity, - }); -} - -function nodeNC(port, input) { - const src = nodeNetCatSrc(port, input); - // console.log("Src", src) - if (src.length < 1000) { - return spawnSync(process.execPath, ["-e", src], { - windowsHide: true, - maxBuffer: Infinity, - }); - } else { - return spawnSync(process.execPath, [], { - input: src, - windowsHide: true, - maxBuffer: Infinity, - }); - } -} - -function waitForAlive(port) { - let response = null; - let err = null; - let timeout = Date.now() + 10000; - while (response !== "pong" && Date.now() < timeout) { - const result = nodeNC(port, "ping\r\n"); - response = result.stdout && result.stdout.toString(); - err = result.stderr && result.stderr.toString(); - } - if (response !== "pong") { - throw new Error( - 'Timed out waiting for sync-rpc server to start (it should respond with "pong" when sent "ping"):\n\n' + - err + - "\n" + - response - ); - } -} - -function findPort() { - const findPortResult = spawnSync(process.execPath, [require.resolve("./find-port")], { - windowsHide: true, - }); - if (findPortResult.error) { - if (typeof findPortResult.error === "string") { - throw new Error(findPortResult.error); - } - throw findPortResult.error; - } - if (findPortResult.status !== 0) { - throw new Error( - findPortResult.stderr.toString() || - "find port exited with code " + findPortResult.status - ); - } - const portString = findPortResult.stdout.toString("utf8").trim(); - if (!/^[0-9]+$/.test(portString)) { - throw new Error("Invalid port number string returned: " + portString); - } - return Number(portString); -} - -function test(fn, port) { - const result = fn(port, "ping\r\n"); - const response = result.stdout && result.stdout.toString(); - return response === "pong"; -} - -function getFastestFunction(port) { - for (let i = 0; i < FUNCTION_PRIORITY.length; i++) { - if (test(FUNCTION_PRIORITY[i], port)) { - return FUNCTION_PRIORITY[i]; - } - } -} - -function start(filename) { - if (!spawnSync) { - throw new Error( - "Sync-request requires node version 0.12 or later. If you need to use it with an older version of node\n" + - "you can `npm install sync-request@2.2.0`, which was the last version to support older versions of node." - ); - } - const port = findPort(); - const p = spawn(process.execPath, [require.resolve("./worker"), port, filename], { - stdio: "inherit", - windowsHide: true, - }); - p.unref(); - process.on("exit", () => { - // console.log("killing server") - p.kill(); - }); - waitForAlive(port); - const fastestFunction = getFastestFunction(port); - // console.log('Using ' + fastestFunction + ' for IPC'); - configuration.port = port; - configuration.fastestFunction = fastestFunction; - started = true; -} - -function sendMessage(input) { - if (!started) start(); - const res = configuration.fastestFunction(configuration.port, JSON.stringify(input) + "\r\n"); - try { - return JSON.parse(res.stdout.toString("utf8")); - } catch (error) { - if (res.error) { - if (typeof res.error === "string") res.error = new Error(res.error); - throw res.error; - } - if (res.status !== 0) { - throw new Error( - configuration.fastestFunction.name + - " failed:\n" + - (res.stdout && res.stdout.toString()) + - "\n" + - (res.stderr && res.stderr.toString()) - ); - } - throw new Error( - configuration.fastestFunction.name + - " failed:\n" + - (res.stdout && res.stdout).toString() + - "\n" + - (res.stderr && res.stderr).toString() - ); - } -} - -function extractValue(msg) { - if (!msg.s) { - const error = new Error(msg.v.message); - error.code = msg.v.code; - throw error; - } - return msg.v; -} - -function createClient(filename) { - const id = extractValue(sendMessage({ t: 1, f: filename })); - return function (args) { - return extractValue(sendMessage({ t: 0, i: id, a: args })); - }; -} - -createClient.FUNCTION_PRIORITY = FUNCTION_PRIORITY; -createClient.configuration = configuration; - -module.exports = createClient; -// let func = createClient("/home/afshan/Projects/githubClones/sync-rpc/somejs.js") diff --git a/JS/edgechains/arakoodev/src/sync-rpc/index.ts b/JS/edgechains/arakoodev/src/sync-rpc/index.ts new file mode 100644 index 00000000..c0fad336 --- /dev/null +++ b/JS/edgechains/arakoodev/src/sync-rpc/index.ts @@ -0,0 +1,2 @@ +import createSyncRPC from "./lib/sync-rpc.js"; +export { createSyncRPC }; diff --git a/JS/edgechains/arakoodev/src/sync-rpc/json.js b/JS/edgechains/arakoodev/src/sync-rpc/json.js deleted file mode 100644 index 37f17c8d..00000000 --- a/JS/edgechains/arakoodev/src/sync-rpc/json.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; - -//TODO: handle reviver/dehydrate function like normal -//and handle indentation, like normal. -//if anyone needs this... please send pull request. - -exports.stringify = function stringify(o) { - if (o && Buffer.isBuffer(o)) return JSON.stringify(":base64:" + o.toString("base64")); - - if (o && o.toJSON) o = o.toJSON(); - - if (o && "object" === typeof o) { - var s = ""; - var array = Array.isArray(o); - s = array ? "[" : "{"; - var first = true; - - for (var k in o) { - var ignore = "function" == typeof o[k] || (!array && "undefined" === typeof o[k]); - if (Object.hasOwnProperty.call(o, k) && !ignore) { - if (!first) s += ","; - first = false; - if (array) { - s += stringify(o[k]); - } else if (o[k] !== void 0) { - s += stringify(k) + ":" + stringify(o[k]); - } - } - } - - s += array ? "]" : "}"; - - return s; - } else if ("string" === typeof o) { - return JSON.stringify(/^:/.test(o) ? ":" + o : o); - } else if ("undefined" === typeof o) { - return "null"; - } else return JSON.stringify(o); -}; - -exports.parse = function (s) { - return JSON.parse(s, function (key, value) { - if ("string" === typeof value) { - if (/^:base64:/.test(value)) return new Buffer(value.substring(8), "base64"); - else return /^:/.test(value) ? value.substring(1) : value; - } - return value; - }); -}; diff --git a/JS/edgechains/arakoodev/src/sync-rpc/lib/sync-rpc.ts b/JS/edgechains/arakoodev/src/sync-rpc/lib/sync-rpc.ts new file mode 100644 index 00000000..5a821701 --- /dev/null +++ b/JS/edgechains/arakoodev/src/sync-rpc/lib/sync-rpc.ts @@ -0,0 +1,85 @@ +import { execSync } from 'child_process'; +import path from 'path'; +import fs from 'fs'; +import crypto from 'crypto'; +import os from 'os'; + +function createSyncRPC(filename: string) { + const absolutePath = path.resolve(filename); + + if (!fs.existsSync(absolutePath)) { + throw new Error(`File not found: ${absolutePath}`); + } + + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sync-rpc-')); + const hash = crypto.createHash('md5').update(absolutePath).digest('hex'); + const wrapperPath = path.join(tempDir, `wrapper_${hash}.js`); + + const wrapperCode = ` + const fn = require(${JSON.stringify(absolutePath)}); + if (typeof fn !== 'function') { + throw new Error('Exported value is not a function'); + } + process.on('message', (message) => { + fn(message) + .then(result => { + process.send({ success: true, result }); + }) + .catch(error => { + process.send({ success: false, error: error.message }); + }); + }); + `; + + fs.writeFileSync(wrapperPath, wrapperCode); + + return function syncRPC(args: any) { + const scriptPath = path.join(tempDir, `script_${Date.now()}.js`); + const scriptContent = ` + const cp = require('child_process'); + const child = cp.fork(${JSON.stringify(wrapperPath)}); + child.send(${JSON.stringify(args)}); + child.on('message', (message) => { + console.log(JSON.stringify(message)); + child.kill(); + process.exit(0); + }); + `; + + fs.writeFileSync(scriptPath, scriptContent); + + try { + const output = execSync(`node ${scriptPath}`, { + encoding: 'utf8', + stdio: ['pipe', 'pipe', 'inherit'] + }); + + fs.unlinkSync(scriptPath); + + const trimmedOutput = output.trim(); + + try { + const result = JSON.parse(trimmedOutput); + if (!result.success) { + throw new Error(result.error); + } + return JSON.stringify(result.result); + } catch (parseError: any) { + console.error('Raw output:', trimmedOutput); + throw new Error(`Failed to parse output as JSON: ${parseError.message}\nRaw output: ${trimmedOutput}`); + } + } catch (error: any) { + fs.unlinkSync(scriptPath); + if (error) { + console.error('Execution error:', error.message); + if (error.stderr) { + console.error('stderr:', error.stderr); + } + throw new Error(`Execution error: ${error.message}`); + } + throw error; + } + }; +} + +export = createSyncRPC as Function; \ No newline at end of file diff --git a/JS/edgechains/arakoodev/src/sync-rpc/worker.js b/JS/edgechains/arakoodev/src/sync-rpc/worker.js deleted file mode 100644 index a116530d..00000000 --- a/JS/edgechains/arakoodev/src/sync-rpc/worker.js +++ /dev/null @@ -1,76 +0,0 @@ -const net = require("net"); -const { fileURLToPath } = require("url"); -const INIT = 1; -const CALL = 0; -const modules = []; - -const server = net.createServer({ allowHalfOpen: true }, (c) => { - let responded = false; - function respond(data) { - if (responded) return; - responded = true; - c.end(JSON.stringify(data)); - } - let buffer = ""; - function onMessage(str) { - if (str === "ping") { - c.end("pong"); - return; - } - const req = JSON.parse(str); - if (req.t === INIT) { - // console.log("Init", req.f) - let id = init(req.f); - // console.log("ID", id) - respond({ s: true, v: id }); - } else { - let result = modules[req.i](req.a); - // console.log("typeof result ", typeof result) - // console.log("Resutl constr name",result.constructor) - result.then( - function (response) { - respond({ s: true, v: response }); - }, - function (err) { - respond({ s: false, v: { code: err.code, message: err.message } }); - } - ); - } - } - c.on("error", function (err) { - respond({ s: false, v: { code: err.code, message: err.message } }); - }); - c.on("data", function (data) { - // console.log("Data", data); - buffer += data.toString("utf8"); - // console.log("Buffer", buffer); - if (/\r\n/.test(buffer)) { - onMessage(buffer.trim()); - } - }); -}); - -function init(filename) { - let filePath; - try { - filePath = fileURLToPath(filename); - } catch (error) { - filePath = filename; - } - let module = require(filePath); - // console.log("typeof module", typeof module); - // console.log("typeof module.default", typeof module.default); - if (module && typeof module === "object" && typeof module.default === "function") { - module = module.default; - } - if (typeof module !== "function") { - throw new Error(filename + " did not export a function."); - } - const i = modules.length; - // console.log("I", i) - modules[i] = module; - return i; -} - -// server.listen(6553); -server.listen(process.argv[2]); diff --git a/JS/edgechains/examples/chat-with-llm/.gitignore b/JS/edgechains/examples/chat-with-llm/.gitignore new file mode 100644 index 00000000..db4c6d9b --- /dev/null +++ b/JS/edgechains/examples/chat-with-llm/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules \ No newline at end of file diff --git a/JS/edgechains/examples/chat-with-llm/package.json b/JS/edgechains/examples/chat-with-llm/package.json index 46131168..5e589a6a 100644 --- a/JS/edgechains/examples/chat-with-llm/package.json +++ b/JS/edgechains/examples/chat-with-llm/package.json @@ -12,7 +12,7 @@ }, "license": "ISC", "dependencies": { - "@arakoodev/edgechains.js": "^0.24.1", + "@arakoodev/edgechains.js": "file:../../arakoodev", "@arakoodev/jsonnet": "^0.24.0", "file-uri-to-path": "^2.0.0", "path": "^0.12.7", @@ -21,7 +21,8 @@ "zod": "^3.23.8" }, "devDependencies": { - "@types/node": "^20.12.12", + "@types/node": "^22.7.4", + "typescript": "^5.7.0-dev.20241007", "webpack-cli": "^5.1.4" } } diff --git a/JS/edgechains/examples/chat-with-llm/src/index.ts b/JS/edgechains/examples/chat-with-llm/src/index.ts index b0a39006..43e2708c 100644 --- a/JS/edgechains/examples/chat-with-llm/src/index.ts +++ b/JS/edgechains/examples/chat-with-llm/src/index.ts @@ -1,7 +1,7 @@ import { ArakooServer } from "@arakoodev/edgechains.js/arakooserver"; import Jsonnet from "@arakoodev/jsonnet"; -//@ts-ignore -import createClient from "@arakoodev/edgechains.js/sync-rpc"; + +import { createSyncRPC } from "@arakoodev/edgechains.js/sync-rpc"; import fileURLToPath from "file-uri-to-path"; import path from "path"; @@ -12,7 +12,7 @@ const app = server.createApp(); const jsonnet = new Jsonnet(); const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const openAICall = createClient(path.join(__dirname, "./lib/generateResponse.cjs")); +const openAICall = createSyncRPC(path.join(__dirname, "./lib/generateResponse.cjs")); app.post("/chat", async (c: any) => { try { diff --git a/JS/edgechains/examples/chat-with-llm/src/lib/generateResponse.cts b/JS/edgechains/examples/chat-with-llm/src/lib/generateResponse.cts index 53089297..a3342669 100644 --- a/JS/edgechains/examples/chat-with-llm/src/lib/generateResponse.cts +++ b/JS/edgechains/examples/chat-with-llm/src/lib/generateResponse.cts @@ -1,4 +1,4 @@ -const { OpenAI } = require("@arakoodev/edgechains.js/openai"); +const { OpenAI } = require("@arakoodev/edgechains.js/ai"); import { z } from "zod"; const schema = z.object({ diff --git a/JS/edgechains/examples/chat-with-llm/tsconfig.json b/JS/edgechains/examples/chat-with-llm/tsconfig.json index 5721c810..75cfffbf 100644 --- a/JS/edgechains/examples/chat-with-llm/tsconfig.json +++ b/JS/edgechains/examples/chat-with-llm/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2022", + "target": "ES2021", "moduleResolution": "NodeNext", "module": "NodeNext", "rootDir": "./src",