From 0e21c8c2e3878422c4491532114721d54a2c5e6e Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Mon, 19 Jan 2026 10:03:34 +0900 Subject: [PATCH 1/4] Refactor file deployment logic in HandleTargetFilePath Modularized the deployment of base files, optional data files, and entrypoint in HandleTargetFilePath. Improved error handling and validation for file paths and extensions. Replaced the previous CopyDirectoryRecursive implementation with a new version supporting required/optional directories and simplified file copying logic. --- .../WelsonJS.Launcher/Program.cs | 193 +++++++++++------- 1 file changed, 123 insertions(+), 70 deletions(-) diff --git a/WelsonJS.Augmented/WelsonJS.Launcher/Program.cs b/WelsonJS.Augmented/WelsonJS.Launcher/Program.cs index 1ab087b..71167c0 100644 --- a/WelsonJS.Augmented/WelsonJS.Launcher/Program.cs +++ b/WelsonJS.Augmented/WelsonJS.Launcher/Program.cs @@ -201,57 +201,137 @@ namespace WelsonJS.Launcher private static void HandleTargetFilePath(string filePath) { - string fileExtension = Path.GetExtension(filePath); + if (string.IsNullOrWhiteSpace(filePath)) + throw new ArgumentException("filePath is null or empty.", nameof(filePath)); - if (String.IsNullOrEmpty(fileExtension)) - { - throw new ArgumentException("The file extension is null or empty"); - } + if (!File.Exists(filePath)) + throw new FileNotFoundException("Target file not found.", filePath); - if (fileExtension.Equals(".zip", StringComparison.OrdinalIgnoreCase)) - { + string ext = Path.GetExtension(filePath); + if (string.IsNullOrEmpty(ext)) + throw new ArgumentException("The file extension is null or empty.", nameof(filePath)); + + if (ext.Equals(".zip", StringComparison.OrdinalIgnoreCase)) throw new NotImplementedException("Not implemented yet."); - } - if (fileExtension.Equals(".js", StringComparison.OrdinalIgnoreCase)) + if (!ext.Equals(".js", StringComparison.OrdinalIgnoreCase)) + throw new NotSupportedException($"Unsupported file type: {ext}"); + + string instanceId = Guid.NewGuid().ToString(); + string workingDirectory = CreateInstanceDirectory(instanceId); + + string appRoot = GetAppRootDirectory(); + if (string.IsNullOrWhiteSpace(appRoot) || !Directory.Exists(appRoot)) + throw new DirectoryNotFoundException($"Application root not found: {appRoot}"); + + DeployBaseFiles(appRoot, workingDirectory); + DeployOptionalDataFiles(appRoot, workingDirectory); + DeployEntrypoint(filePath, workingDirectory); + + RecordFirstDeployTime(workingDirectory, instanceId); + + RunCommandPrompt( + workingDirectory: workingDirectory, + entryFileName: "app.js", + scriptName: "bootstrap", + isConsoleApplication: true, + isInteractiveServiceApplication: false + ); + } + + private static void DeployBaseFiles(string appRoot, string workingDirectory) + { + CopyFile( + Path.Combine(appRoot, "app.js"), + Path.Combine(workingDirectory, "app.js"), + isRequired: true + ); + + CopyDirectoryRecursive( + Path.Combine(appRoot, "app", "assets", "js"), + Path.Combine(workingDirectory, "app", "assets", "js"), + isRequired: true + ); + + CopyDirectoryRecursive( + Path.Combine(appRoot, "lib"), + Path.Combine(workingDirectory, "lib"), + isRequired: true + ); + } + + private static void DeployOptionalDataFiles(string appRoot, string workingDirectory) + { + CopyFile( + Path.Combine(appRoot, "data", "apikey.json"), + Path.Combine(workingDirectory, "data", "apikey.json"), + isRequired: false + ); + + CopyFile( + Path.Combine(appRoot, "data", "available_proxies.json"), + Path.Combine(workingDirectory, "data", "available_proxies.json"), + isRequired: false + ); + + CopyFile( + Path.Combine(appRoot, "data", "filetypes.json"), + Path.Combine(workingDirectory, "data", "filetypes.json"), + isRequired: false + ); + } + + private static void DeployEntrypoint(string sourceJsPath, string workingDirectory) + { + CopyFile( + sourceJsPath, + Path.Combine(workingDirectory, "bootstrap.js"), + isRequired: true + ); + } + + private static void CopyDirectoryRecursive( + string sourceDir, + string destinationDir, + bool isRequired = false + ) + { + if (!Directory.Exists(sourceDir)) { - string instanceId = Guid.NewGuid().ToString(); - string workingDirectory = CreateInstanceDirectory(instanceId); - - string appRoot = GetAppRootDirectory(); - string appBaseSource = Path.Combine(appRoot, "app.js"); - if (!File.Exists(appBaseSource)) - { - throw new FileNotFoundException("app.js not found in application root.", appBaseSource); - } - - string appBaseDestination = Path.Combine(workingDirectory, "app.js"); - File.Copy(appBaseSource, appBaseDestination, overwrite: true); - - string assetsSource = Path.Combine(appRoot, "app", "assets", "js"); - string assetsDestination = Path.Combine(workingDirectory, "app", "assets", "js"); - CopyDirectoryRecursive(assetsSource, assetsDestination); - - string libSource = Path.Combine(appRoot, "lib"); - string libDestination = Path.Combine(workingDirectory, "lib"); - CopyDirectoryRecursive(libSource, libDestination); - - string entrypointDestination = Path.Combine(workingDirectory, "bootstrap.js"); - File.Copy(filePath, entrypointDestination, overwrite: true); - - RecordFirstDeployTime(workingDirectory, instanceId); - - RunCommandPrompt( - workingDirectory: workingDirectory, - entryFileName: "app.js", - scriptName: "bootstrap", - isConsoleApplication: true, - isInteractiveServiceApplication: false - ); + if (isRequired) + throw new DirectoryNotFoundException($"Required directory not found: {sourceDir}"); return; } - throw new NotSupportedException($"Unsupported file type: {fileExtension}"); + Directory.CreateDirectory(destinationDir); + + foreach (string file in Directory.GetFiles(sourceDir)) + { + string destFile = Path.Combine(destinationDir, Path.GetFileName(file)); + File.Copy(file, destFile, overwrite: true); + } + + foreach (string subDir in Directory.GetDirectories(sourceDir)) + { + string destSubDir = Path.Combine(destinationDir, Path.GetFileName(subDir)); + CopyDirectoryRecursive(subDir, destSubDir, isRequired); + } + } + + private static void CopyFile(string sourcePath, string destinationPath, bool isRequired) + { + if (!File.Exists(sourcePath)) + { + if (isRequired) + throw new FileNotFoundException("Required file not found.", sourcePath); + return; + } + + string parent = Path.GetDirectoryName(destinationPath); + if (!string.IsNullOrEmpty(parent)) + Directory.CreateDirectory(parent); + + File.Copy(sourcePath, destinationPath, overwrite: true); } private static string GetAppRootDirectory() @@ -299,33 +379,6 @@ namespace WelsonJS.Launcher return workingDirectory; } - private static void CopyDirectoryRecursive(string sourceDir, string destDir) - { - if (!Directory.Exists(sourceDir)) - { - throw new DirectoryNotFoundException("Source directory not found: " + sourceDir); - } - - Directory.CreateDirectory(destDir); - - foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)) - { - string normalizedSource = sourceDir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - string relativePath = file.Substring(normalizedSource.Length + 1).TrimStart( - Path.DirectorySeparatorChar, - Path.AltDirectorySeparatorChar - ); - string targetPath = Path.Combine(destDir, relativePath); - string targetDir = Path.GetDirectoryName(targetPath); - if (!Directory.Exists(targetDir)) - { - Directory.CreateDirectory(targetDir); - } - - File.Copy(file, targetPath, overwrite: true); - } - } - public static void RunCommandPrompt(string workingDirectory, string entryFileName, string scriptName, bool isConsoleApplication = false, bool isInteractiveServiceApplication = false) { if (!isConsoleApplication) From ce7e88d02cfb9a775238857cee87be0abf232700 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Mon, 19 Jan 2026 14:57:05 +0900 Subject: [PATCH 2/4] Add FriendIAI (LGAI-EXAONE) engine integration Introduces support for the FriendIAI (LGAI-EXAONE) LLM engine, including its API endpoint, model configuration, request formatting, and response handling. Updates the version to 0.1.12 to reflect the new integration. --- lib/language-inference-engine.js | 43 +++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/language-inference-engine.js b/lib/language-inference-engine.js index 5ab9bad..29023a5 100644 --- a/lib/language-inference-engine.js +++ b/lib/language-inference-engine.js @@ -17,6 +17,7 @@ // - Moonshot: https://kimi.moonshot.cn/user/agreement/userPrivacy // - AlibabaCloud: https://www.alibabacloud.com/help/en/legal/latest/alibaba-cloud-international-website-privacy-policy // - ClovaStudio: https://clova-x.naver.com/ai_policies +// - FriendIAI: https://friendli.ai/privacypolicy // - Catswords AI: https://policy.catswords.social/site_terms.html // var HTTP = require("lib/http"); @@ -428,6 +429,46 @@ var ENGINE_PROFILES = { } } }, + "friendiai": { + "type": "llm", + "availableModels": [ + "LGAI-EXAONE/K-EXAONE-236B-A23B" + ], + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer {apikey}" + }, + "url": "https://api.friendli.ai/serverless/v1", + "wrap": function(model, message, temperature) { + return { + "model": model, + "messages": [{ + "role": "system", + "content": BIAS_MESSAGE + }, { + "role": "user", + "content": message + }], + "temperature": temperature, + "stream": false, + "parse_reasoning": true, + "chat_template_kwargs": { + "enable_thinking": true + } + }; + }, + "callback": function(response) { + if ("error" in response) { + return ["Error: " + response.error.message]; + } else { + return response.choices.reduce(function(a, x) { + a.push(x.message.content); + + return a; + }, []); + } + } + }, "catswords-ai": { "type": "llm", "availableModels": [ @@ -553,7 +594,7 @@ exports.create = function() { return new LanguageInferenceEngine(); }; -exports.VERSIONINFO = "Language Inference Engine integration version 0.1.11"; +exports.VERSIONINFO = "Language Inference Engine integration version 0.1.12"; exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.global = global; exports.require = global.require; From fde431c0691e9ccf85afa6bbd804d31c413b4557 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Mon, 19 Jan 2026 14:58:36 +0900 Subject: [PATCH 3/4] Add Friendiai API key path to apikey.json Included the Friendiai API key reference in apikey.json to support integration with the Friendiai service. --- data/apikey.json | 1 + 1 file changed, 1 insertion(+) diff --git a/data/apikey.json b/data/apikey.json index ba5b59a..1c4a751 100644 --- a/data/apikey.json +++ b/data/apikey.json @@ -8,6 +8,7 @@ "deepseek": "file:data/deepseek_apikey.txt", "moonshot": "file:data/moonshot_apikey.txt", "clovastudio": "file:data/clovastudio_apikey.txt", + "friendiai": "file:data/friendiai_apikey.txt", "catswords-ai": "file:data/catswords_ai_apikey.txt", "scrapeops": "file:data/scrapeops_apikey.txt", "serpapi": "file:data/serpapi_apikey.txt", From f84198799b8ed41b040b860db49b6f04c8158c50 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Mon, 19 Jan 2026 15:09:56 +0900 Subject: [PATCH 4/4] Add new models to friendiai availableModels list Expanded the 'availableModels' array for the 'friendiai' engine profile to include several new LLM models, increasing the range of supported models for inference. --- lib/language-inference-engine.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/language-inference-engine.js b/lib/language-inference-engine.js index 29023a5..c15a75c 100644 --- a/lib/language-inference-engine.js +++ b/lib/language-inference-engine.js @@ -432,7 +432,26 @@ var ENGINE_PROFILES = { "friendiai": { "type": "llm", "availableModels": [ - "LGAI-EXAONE/K-EXAONE-236B-A23B" + "LGAI-EXAONE/K-EXAONE-236B-A23B", + "MiniMaxAI/MiniMax-M2.1", + "zai-org/GLM-4.6", + "meta-llama-3.1-8b-instruct", + "mistralai/Magistral-Small-2506", + "skt/A.X-3.1", + "Qwen/Qwen3-235B-A22B-Thinking-2507", + "Qwen/Qwen3-235B-A22B-Instruct-2507", + "meta-llama-3.3-70b-instruct", + "mistralai/Devstral-Small-2505", + "google/gemma-3-27b-it", + "Qwen/Qwen3-32B", + "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "Qwen/Qwen3-30B-A3B", + "meta-llama/Llama-4-Maverick-17B-128E-Instruct", + "mistralai/Mistral-Small-3.1-24B-Instruct-2503", + "deepseek-ai/DeepSeek-V3.1", + "skt/A.X-4.0", + "naver-hyperclovax/HyperCLOVAX-SEED-Think-14B", + "LGAI-EXAONE/EXAONE-4.0.1-32B" ], "headers": { "Content-Type": "application/json",