Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f28c2c6b31 | ||
|
|
fffc29fc2b | ||
|
|
20fcbb2e48 | ||
|
|
f9a3ce43f5 | ||
|
|
cbd714d9ea | ||
|
|
b825b62a96 | ||
|
|
ae8ed60767 | ||
|
|
710104d709 | ||
|
|
9417b64458 | ||
|
|
4fb799112a | ||
|
|
d2cfd99a33 | ||
|
|
15c7d7f60c | ||
|
|
26e57ef44f | ||
|
|
00fb0b91cd | ||
|
|
fc1f9afbcd | ||
|
|
b3042fdf6f | ||
|
|
bf8f352347 | ||
|
|
2eea258bc4 | ||
|
|
be2725c2cb | ||
|
|
3f742ed72f |
@@ -80,7 +80,6 @@ class ShellAgentSaveVideoVHS:
|
|||||||
preview_image = os.path.relpath(preview_image)
|
preview_image = os.path.relpath(preview_image)
|
||||||
video_path = os.path.relpath(video_path)
|
video_path = os.path.relpath(video_path)
|
||||||
results = {"ui": {"image": [preview_image], "video": [video_path]}}
|
results = {"ui": {"image": [preview_image], "video": [video_path]}}
|
||||||
print(results)
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
json_type_mapipng = {
|
||||||
|
"text": "string",
|
||||||
|
"float": "number",
|
||||||
|
"integer": "integer"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShellAgentOutputText:
|
||||||
|
TYPE_STR = "text"
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
s.TYPE_STR: ("STRING", {"tooltip": f"The {s.TYPE_STR} to output."}),
|
||||||
|
"output_name": ("STRING", {"multiline": False, "default": f"output_{s.TYPE_STR}"},),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "output_var"
|
||||||
|
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
CATEGORY = "shellagent"
|
||||||
|
DESCRIPTION = "output the text"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, **kwargs):
|
||||||
|
schema = {
|
||||||
|
"title": kwargs["output_name"],
|
||||||
|
"type": json_type_mapipng[cls.TYPE_STR]
|
||||||
|
}
|
||||||
|
return schema
|
||||||
|
|
||||||
|
def output_var(self, **kwargs):
|
||||||
|
results = {"ui": {"output": [kwargs[self.TYPE_STR]]}}
|
||||||
|
return results
|
||||||
|
|
||||||
|
class ShellAgentOutputFloat(ShellAgentOutputText):
|
||||||
|
TYPE_STR = "float"
|
||||||
|
DESCRIPTION = "output the float"
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
s.TYPE_STR: ("STRING", {"tooltip": f"The {s.TYPE_STR} to output."}),
|
||||||
|
"output_name": ("STRING", {"multiline": False, "default": f"output_{s.TYPE_STR}"},),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ShellAgentOutputInteger(ShellAgentOutputText):
|
||||||
|
TYPE_STR = "integer"
|
||||||
|
DESCRIPTION = "output the integer"
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
s.TYPE_STR: ("STRING", {"tooltip": f"The {s.TYPE_STR} to output."}),
|
||||||
|
"output_name": ("STRING", {"multiline": False, "default": f"output_{s.TYPE_STR}"},),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"ShellAgentPluginOutputText": ShellAgentOutputText,
|
||||||
|
"ShellAgentPluginOutputFloat": ShellAgentOutputFloat,
|
||||||
|
"ShellAgentPluginOutputInteger": ShellAgentOutputInteger
|
||||||
|
}
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"ShellAgentPluginOutputText": "Output Text (ShellAgent Plugin)",
|
||||||
|
"ShellAgentPluginOutputFloat": "Output Float (ShellAgent Plugin)",
|
||||||
|
"ShellAgentPluginOutputInteger": "Output Integer (ShellAgent Plugin)",
|
||||||
|
}
|
||||||
+26
-11
@@ -59,6 +59,10 @@ def inspect_repo_version(module_path):
|
|||||||
"repo": "",
|
"repo": "",
|
||||||
"commit": ""
|
"commit": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not os.path.isdir(os.path.join(module_path, ".git")):
|
||||||
|
return result
|
||||||
|
|
||||||
# Get the remote repository URL
|
# Get the remote repository URL
|
||||||
try:
|
try:
|
||||||
remote_url = subprocess.check_output(
|
remote_url = subprocess.check_output(
|
||||||
@@ -87,7 +91,7 @@ def inspect_repo_version(module_path):
|
|||||||
|
|
||||||
def fetch_model_searcher_results(model_ids):
|
def fetch_model_searcher_results(model_ids):
|
||||||
import requests
|
import requests
|
||||||
url = "https://shellagent.myshell.ai/models_searcher/search_urls"
|
url = "https://models-searcher.myshell.life/search_urls"
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
@@ -96,7 +100,10 @@ def fetch_model_searcher_results(model_ids):
|
|||||||
}
|
}
|
||||||
|
|
||||||
response = requests.post(url, headers=headers, json=data)
|
response = requests.post(url, headers=headers, json=data)
|
||||||
results = [item[:10] for item in response.json()]
|
if response.status_code == 200:
|
||||||
|
results = [item[:10] for item in response.json()]
|
||||||
|
else:
|
||||||
|
results = None
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes and models at the same time
|
def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes and models at the same time
|
||||||
@@ -110,7 +117,8 @@ def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes an
|
|||||||
if node_class_type is None:
|
if node_class_type is None:
|
||||||
raise NotImplementedError(f"Missing nodes founded, please first install the missing nodes using ComfyUI Manager")
|
raise NotImplementedError(f"Missing nodes founded, please first install the missing nodes using ComfyUI Manager")
|
||||||
node_cls = NODE_CLASS_MAPPINGS[node_class_type]
|
node_cls = NODE_CLASS_MAPPINGS[node_class_type]
|
||||||
if hasattr(node_cls, "RELATIVE_PYTHON_MODULE"):
|
if hasattr(node_cls, "RELATIVE_PYTHON_MODULE") and node_cls.RELATIVE_PYTHON_MODULE.startswith("custom_nodes."):
|
||||||
|
print(node_cls.RELATIVE_PYTHON_MODULE)
|
||||||
custom_nodes.append(node_cls.RELATIVE_PYTHON_MODULE)
|
custom_nodes.append(node_cls.RELATIVE_PYTHON_MODULE)
|
||||||
if node_class_type in model_loaders_info:
|
if node_class_type in model_loaders_info:
|
||||||
for field_name, filename in node_info["inputs"].items():
|
for field_name, filename in node_info["inputs"].items():
|
||||||
@@ -144,8 +152,14 @@ def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes an
|
|||||||
print("ckpt_paths:", ckpt_paths)
|
print("ckpt_paths:", ckpt_paths)
|
||||||
custom_nodes = list(set(custom_nodes))
|
custom_nodes = list(set(custom_nodes))
|
||||||
# step 0: comfyui version
|
# step 0: comfyui version
|
||||||
comfyui_version = inspect_repo_version(BASE_PATH)
|
repo_info = inspect_repo_version(BASE_PATH)
|
||||||
|
if repo_info["repo"] == "":
|
||||||
|
repo_info["require_recheck"] = True
|
||||||
|
if repo_info["name"] in custom_dependencies["custom_nodes"]:
|
||||||
|
repo_info["repo"] = custom_dependencies["custom_nodes"][repo_info["name"]].get("repo", "")
|
||||||
|
repo_info["commit"] = custom_dependencies["custom_nodes"][repo_info["name"]].get("commit", "")
|
||||||
|
comfyui_version = repo_info
|
||||||
|
|
||||||
# step 1: custom nodes
|
# step 1: custom nodes
|
||||||
custom_nodes_list = []
|
custom_nodes_list = []
|
||||||
custom_nodes_names = []
|
custom_nodes_names = []
|
||||||
@@ -184,11 +198,12 @@ def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes an
|
|||||||
|
|
||||||
# try to fetch from myshell model searcher
|
# try to fetch from myshell model searcher
|
||||||
missing_model_results_myshell = fetch_model_searcher_results(missing_model_ids)
|
missing_model_results_myshell = fetch_model_searcher_results(missing_model_ids)
|
||||||
for missing_model_id, missing_model_urls in zip(missing_model_ids, missing_model_results_myshell):
|
if missing_model_results_myshell is not None:
|
||||||
if len(missing_model_urls) > 0:
|
for missing_model_id, missing_model_urls in zip(missing_model_ids, missing_model_results_myshell):
|
||||||
models_dict[missing_model_id]["require_recheck"] = False
|
if len(missing_model_urls) > 0:
|
||||||
models_dict[missing_model_id]["urls"] = missing_model_urls
|
models_dict[missing_model_id]["require_recheck"] = False
|
||||||
print("successfully fetch results from myshell", models_dict[missing_model_id])
|
models_dict[missing_model_id]["urls"] = missing_model_urls
|
||||||
|
print("successfully fetch results from myshell", models_dict[missing_model_id])
|
||||||
|
|
||||||
# step 3: handle local files
|
# step 3: handle local files
|
||||||
process_local_file_path_async(file_mapping_dict, max_workers=20)
|
process_local_file_path_async(file_mapping_dict, max_workers=20)
|
||||||
@@ -200,4 +215,4 @@ def resolve_dependencies(prompt, custom_dependencies): # resolve custom nodes an
|
|||||||
"models": models_dict,
|
"models": models_dict,
|
||||||
"files": files_dict,
|
"files": files_dict,
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
|
|||||||
+177
-32
@@ -37,11 +37,15 @@ app.registerExtension({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
|
if (["ShellAgentPluginOutputText", "ShellAgentPluginOutputFloat", "ShellAgentPluginOutputInteger"].indexOf(nodeData.name) > -1) {
|
||||||
|
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||||
|
this.convertWidgetToInput(this.widgets[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (["ShellAgentPluginInputText", "ShellAgentPluginInputFloat", "ShellAgentPluginInputInteger"].indexOf(nodeData.name) > -1) {
|
if (["ShellAgentPluginInputText", "ShellAgentPluginInputFloat", "ShellAgentPluginInputInteger"].indexOf(nodeData.name) > -1) {
|
||||||
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||||
const widget = this.widgets.find(w => w.name === 'choices')
|
const widget = this.widgets.find(w => w.name === 'choices')
|
||||||
|
|
||||||
this.addWidget('button', 'manage choices', null, () => {
|
this.addWidget('button', 'manage choices', null, () => {
|
||||||
const container = document.createElement("div");
|
const container = document.createElement("div");
|
||||||
Object.assign(container.style, {
|
Object.assign(container.style, {
|
||||||
@@ -93,7 +97,7 @@ app.registerExtension({
|
|||||||
try {
|
try {
|
||||||
arr = JSON.parse(widget.value)
|
arr = JSON.parse(widget.value)
|
||||||
} catch { }
|
} catch { }
|
||||||
} else if(Array.isArray(widget.value)) {
|
} else if (Array.isArray(widget.value)) {
|
||||||
arr = widget.value
|
arr = widget.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +141,27 @@ app.registerExtension({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (['LoadImage', 'LoadImageMask'].indexOf(nodeData.name) > -1) {
|
||||||
|
addMenuHandler(nodeType, function (_, options) {
|
||||||
|
options.unshift({
|
||||||
|
content: "Replace with ShellAgent Input Image",
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginInputImage", this, { before: true });
|
||||||
|
|
||||||
|
const dvn = node.widgets.find(w => w.name === 'default_value')
|
||||||
|
dvn.value = this.widgets.find(w => w.name === 'image')?.value
|
||||||
|
|
||||||
|
app.graph.links.filter(l => l != null)
|
||||||
|
.forEach(l => {
|
||||||
|
const tn = app.graph._nodes_by_id[l.target_id]
|
||||||
|
node.connect(0, tn, 0)
|
||||||
|
})
|
||||||
|
app.graph.remove(this);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeData.name === "ShellAgentPluginInputImage") {
|
if (nodeData.name === "ShellAgentPluginInputImage") {
|
||||||
if (
|
if (
|
||||||
nodeData?.input?.required?.default_value?.[1]?.image_upload === true
|
nodeData?.input?.required?.default_value?.[1]?.image_upload === true
|
||||||
@@ -151,7 +176,6 @@ app.registerExtension({
|
|||||||
if (nodeData.name === "ShellAgentPluginInputVideo") {
|
if (nodeData.name === "ShellAgentPluginInputVideo") {
|
||||||
addUploadWidget(nodeType, nodeData, "default_value");
|
addUploadWidget(nodeType, nodeData, "default_value");
|
||||||
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||||
// const pathWidget = this.widgets.find((w) => w.name === "video");
|
|
||||||
const pathWidget = this.widgets.find((w) => w.name === "default_value");
|
const pathWidget = this.widgets.find((w) => w.name === "default_value");
|
||||||
chainCallback(pathWidget, "callback", (value) => {
|
chainCallback(pathWidget, "callback", (value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -174,59 +198,180 @@ app.registerExtension({
|
|||||||
|
|
||||||
if (nodeData.name.indexOf('ShellAgentPlugin') === -1) {
|
if (nodeData.name.indexOf('ShellAgentPlugin') === -1) {
|
||||||
addMenuHandler(nodeType, function (_, options) {
|
addMenuHandler(nodeType, function (_, options) {
|
||||||
|
|
||||||
if (this.widgets) {
|
if (this.widgets) {
|
||||||
let toInput = [];
|
let toInput = [];
|
||||||
|
|
||||||
for (const w of this.widgets) {
|
for (const w of this.widgets) {
|
||||||
if (["customtext"].indexOf(w.type) > -1) {
|
if (["customtext"].indexOf(w.type) > -1) {
|
||||||
toInput.push({
|
toInput.push({
|
||||||
content: `${w.name} <- Input Text`,
|
content: w.name,
|
||||||
callback: () => {
|
submenu: {
|
||||||
this.convertWidgetToInput(w);
|
options: [
|
||||||
const node = addNode("ShellAgentPluginInputText", this, { before: true });
|
{
|
||||||
const dvn = node.widgets.find(w => w.name === 'default_value')
|
content: 'Input Text',
|
||||||
dvn.value = w.value;
|
callback: () => {
|
||||||
node.connect(0, this, this.inputs.length - 1);
|
this.convertWidgetToInput(w);
|
||||||
}
|
const node = addNode("ShellAgentPluginInputText", this, { before: true });
|
||||||
|
const dvn = node.widgets.find(w => w.name === 'default_value')
|
||||||
|
dvn.value = w.value;
|
||||||
|
node.connect(0, this, this.inputs.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (["number"].indexOf(w.type) > -1) {
|
if (["number"].indexOf(w.type) > -1) {
|
||||||
toInput.push({
|
toInput.push({
|
||||||
content: `${w.name} <- Input Interger`,
|
content: w.name,
|
||||||
callback: () => {
|
submenu: {
|
||||||
this.convertWidgetToInput(w);
|
options: [
|
||||||
const node = addNode("ShellAgentPluginInputInteger", this, { before: true });
|
{
|
||||||
const dvn = node.widgets.find(w => w.name === 'default_value')
|
content: 'Input Interger',
|
||||||
dvn.value = w.value;
|
callback: () => {
|
||||||
node.connect(0, this, this.inputs.length - 1);
|
this.convertWidgetToInput(w);
|
||||||
}
|
const node = addNode("ShellAgentPluginInputInteger", this, { before: true });
|
||||||
})
|
const dvn = node.widgets.find(w => w.name === 'default_value')
|
||||||
|
dvn.value = w.value;
|
||||||
toInput.push({
|
node.connect(0, this, this.inputs.length - 1);
|
||||||
content: `${w.name} <- Input Float`,
|
}
|
||||||
callback: () => {
|
},
|
||||||
this.convertWidgetToInput(w);
|
{
|
||||||
const node = addNode("ShellAgentPluginInputFloat", this, { before: true });
|
content: 'Input Float',
|
||||||
const dvn = node.widgets.find(w => w.name === 'default_value')
|
callback: () => {
|
||||||
dvn.value = w.value;
|
this.convertWidgetToInput(w);
|
||||||
node.connect(0, this, this.inputs.length - 1);
|
const node = addNode("ShellAgentPluginInputFloat", this, { before: true });
|
||||||
|
const dvn = node.widgets.find(w => w.name === 'default_value')
|
||||||
|
dvn.value = w.value;
|
||||||
|
node.connect(0, this, this.inputs.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toInput.length) {
|
if (toInput.length) {
|
||||||
options.unshift({
|
options.unshift({
|
||||||
content: "Convert to ShellAgent",
|
content: "Convert to ShellAgent (Input)",
|
||||||
submenu: {
|
submenu: {
|
||||||
options: toInput
|
options: toInput
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.outputs) {
|
||||||
|
let toOutput = [];
|
||||||
|
for (const o of this.outputs) {
|
||||||
|
if (o.type === 'IMAGE') {
|
||||||
|
toOutput.push({
|
||||||
|
content: o.name,
|
||||||
|
submenu: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
content: 'Save Image',
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginSaveImage", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: 'Save Images',
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginSaveImages", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.type === 'STRING') {
|
||||||
|
toOutput.push({
|
||||||
|
content: o.name,
|
||||||
|
submenu: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
content: `Output Text`,
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginOutputText", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: `Output Float`,
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginOutputFloat", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: `Output Integer`,
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginOutputInteger", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.type === "VHS_FILENAMES") {
|
||||||
|
toOutput.push({
|
||||||
|
content: o.name,
|
||||||
|
submenu: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
content: `Save Video - VHS`,
|
||||||
|
callback: () => {
|
||||||
|
const node = addNode("ShellAgentPluginSaveVideoVHS", this);
|
||||||
|
this.connect(0, node, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toOutput.length) {
|
||||||
|
options.unshift({
|
||||||
|
content: "Connect to ShellAgent (Output)",
|
||||||
|
submenu: {
|
||||||
|
options: toOutput
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
afterConfigureGraph(missingNodeTypes, app) {
|
||||||
|
function addIn(type, nodeId) {
|
||||||
|
if (LiteGraph.slot_types_default_in[type].indexOf(nodeId) === -1) {
|
||||||
|
LiteGraph.slot_types_default_in[type].unshift(nodeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addOut(type, nodeId) {
|
||||||
|
if (LiteGraph.slot_types_default_out[type].indexOf(nodeId) === -1) {
|
||||||
|
LiteGraph.slot_types_default_out[type].unshift(nodeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addIn('IMAGE', 'ShellAgentPluginInputImage')
|
||||||
|
addOut('IMAGE', 'ShellAgentPluginSaveImage')
|
||||||
|
addOut('IMAGE', 'ShellAgentPluginSaveImages')
|
||||||
|
addOut('STRING', 'ShellAgentPluginOutputInteger')
|
||||||
|
addOut('STRING', 'ShellAgentPluginOutputFloat')
|
||||||
|
addOut('STRING', 'ShellAgentPluginOutputText')
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function addMenuHandler(nodeType, cb) {
|
function addMenuHandler(nodeType, cb) {
|
||||||
|
|||||||
Reference in New Issue
Block a user