tune-basic-toolset 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +517 -0
- package/package.json +35 -0
- package/src/append.schema.json +17 -0
- package/src/append.tool.js +5 -0
- package/src/head.proc.js +24 -0
- package/src/index.js +6 -0
- package/src/init.proc.js +19 -0
- package/src/jina_r.schema.json +21 -0
- package/src/jina_r.tool.js +27 -0
- package/src/js.schema.json +19 -0
- package/src/js.tool.js +60 -0
- package/src/json_format.proc.js +22 -0
- package/src/linenum.proc.js +21 -0
- package/src/list.schema.json +19 -0
- package/src/list.tool.js +20 -0
- package/src/log.proc.js +15 -0
- package/src/message.schema.json +21 -0
- package/src/message.tool.js +14 -0
- package/src/mock.proc.js +35 -0
- package/src/osa.schema.json +13 -0
- package/src/osa.tool.js +12 -0
- package/src/patch.schema.json +17 -0
- package/src/patch.tool.js +38 -0
- package/src/prop.proc.js +34 -0
- package/src/py.schema.json +17 -0
- package/src/py.tool.py +22 -0
- package/src/resolve.proc.js +10 -0
- package/src/rf.schema.json +17 -0
- package/src/rf.tool.js +20 -0
- package/src/sh.schema.json +13 -0
- package/src/sh.tool.js +12 -0
- package/src/shp.proc.js +31 -0
- package/src/slice.proc.js +55 -0
- package/src/tail.proc.js +35 -0
- package/src/text.proc.js +11 -0
- package/src/turn.schema.json +17 -0
- package/src/turn.tool.js +8 -0
- package/src/wf.schema.json +17 -0
- package/src/wf.tool.js +7 -0
package/src/js.tool.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const { parseScript } = require('esprima');
|
|
2
|
+
const { generate } = require('escodegen');
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const { writeFileSync, unlinkSync } = require('fs');
|
|
5
|
+
const { randomBytes } = require('crypto');
|
|
6
|
+
|
|
7
|
+
module.exports = async function execute({ text, inputType }, ctx) {
|
|
8
|
+
// Parse the code into an Abstract Syntax Tree (AST)
|
|
9
|
+
const ast = parseScript(text, { range: true });
|
|
10
|
+
|
|
11
|
+
if (ast.body.length === 0) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const lastNode = ast.body[ast.body.length - 1];
|
|
16
|
+
let modified = false;
|
|
17
|
+
|
|
18
|
+
// If the last statement is an expression, replace it with an assignment
|
|
19
|
+
if (lastNode.type === 'ExpressionStatement') {
|
|
20
|
+
const assignExpression = {
|
|
21
|
+
type: 'ExpressionStatement',
|
|
22
|
+
expression: {
|
|
23
|
+
type: 'AssignmentExpression',
|
|
24
|
+
operator: '=',
|
|
25
|
+
left: {
|
|
26
|
+
type: 'Identifier',
|
|
27
|
+
name: 'myVariable'
|
|
28
|
+
},
|
|
29
|
+
right: lastNode.expression
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
ast.body[ast.body.length - 1] = assignExpression;
|
|
33
|
+
modified = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const rand = randomBytes(50).toString('hex').substring(0, 10);
|
|
37
|
+
const filename = (inputType === "module") ? `tune${rand}.mjs`:`tune${rand}.js`;
|
|
38
|
+
|
|
39
|
+
let modifiedCode = generate(ast);
|
|
40
|
+
if (modified) {
|
|
41
|
+
if (inputType === "module") {
|
|
42
|
+
modifiedCode = `${modifiedCode}\nimport util from 'util';\nconsole.log(util.inspect(myVariable)) `;
|
|
43
|
+
} else {
|
|
44
|
+
modifiedCode = `${modifiedCode}\nconsole.log(require("util").inspect(myVariable))`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
writeFileSync(filename, modifiedCode);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const res = spawnSync("node", [filename],
|
|
51
|
+
{
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
input: modifiedCode,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return (res.stderr + res.stdout).trim().replace(/@/g, "\\@");
|
|
57
|
+
} finally {
|
|
58
|
+
unlinkSync(filename);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = async function json_format(node, args, ctx) {
|
|
4
|
+
if (!node) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
let response_format = { "type": "json_object" };
|
|
8
|
+
if (args.trim()) {
|
|
9
|
+
|
|
10
|
+
let schema = await ctx.resolve(args.trim());
|
|
11
|
+
if (!schema) throw Error(`schema file not found ${args.trim()}`);
|
|
12
|
+
schema = await schema.read();
|
|
13
|
+
response_format = {
|
|
14
|
+
"type": "json_schema",
|
|
15
|
+
"json_schema": JSON.parse(schema),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...node,
|
|
20
|
+
exec: async (payload, ctx) => node.exec({ ...payload, response_format }, ctx)
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* { type: "text" | "image" | "llm" | "tool" | "processor" }
|
|
3
|
+
* name
|
|
4
|
+
* read (afn )
|
|
5
|
+
*/
|
|
6
|
+
module.exports = async function(node, args) {
|
|
7
|
+
if (! node) {
|
|
8
|
+
return node
|
|
9
|
+
}
|
|
10
|
+
const res = Object.assign({}, node);
|
|
11
|
+
res.read = async function(args) {
|
|
12
|
+
let contents = await node.read(args);
|
|
13
|
+
contents = contents.split("\n");
|
|
14
|
+
const pad = `${contents.length}`.length;
|
|
15
|
+
return contents.map((item, index) => {
|
|
16
|
+
const lineNum = `${index + 1}`.padEnd(pad + 1, " ") + "|";
|
|
17
|
+
return `${lineNum} ${item}`
|
|
18
|
+
}).join("\n")
|
|
19
|
+
}
|
|
20
|
+
return res
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Manages todo lists",
|
|
3
|
+
"strict": true,
|
|
4
|
+
"parameters": {
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"text": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "lines in format 'status - item', they'll be used to update list or to insert to the list, to delete item change its status to deleted or cancelled\nThe lines should contain only those items that needs to be changed - inserted/updated/deleted"
|
|
10
|
+
},
|
|
11
|
+
"filename": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "location of the todo list file"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"required": ["text", "filename"],
|
|
17
|
+
"additionalProperties": false
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/list.tool.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function parse(text) {
|
|
2
|
+
const lines = text.split("\n").map(item => item.trim()).filter(item => item);
|
|
3
|
+
return lines.reduce((memo, line) => {
|
|
4
|
+
const idx = line.indexOf("-");
|
|
5
|
+
if (idx === -1) return memo;
|
|
6
|
+
const status = line.slice(0, idx).trim();
|
|
7
|
+
const task = line.slice(idx+1).trim();
|
|
8
|
+
memo[task] = status;
|
|
9
|
+
return memo;
|
|
10
|
+
},
|
|
11
|
+
{});
|
|
12
|
+
}
|
|
13
|
+
module.exports = async function list({text, filename}, ctx) {
|
|
14
|
+
let todo = parse(await ctx.read(filename) || "");
|
|
15
|
+
const changes = parse(text);
|
|
16
|
+
todo = { ...todo, ...changes };
|
|
17
|
+
todo = Object.keys(todo).map(key => `${todo[key]} - ${key}`).join("\n");
|
|
18
|
+
await ctx.write(filename, todo);
|
|
19
|
+
return "list updated";
|
|
20
|
+
};
|
package/src/log.proc.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const path = require("path")
|
|
3
|
+
|
|
4
|
+
module.exports = async (node, args, ctx) => ({
|
|
5
|
+
...node,
|
|
6
|
+
exec: async function(payload, ctx) {
|
|
7
|
+
const res = await node.exec(payload, ctx)
|
|
8
|
+
const body = JSON.parse(res.body)
|
|
9
|
+
payload = {...res, body};
|
|
10
|
+
const filename = args.trim() || "log.json"
|
|
11
|
+
const content = path.extname(filename) == ".chat" ? ctx.msg2text(payload.body.messages, true) : JSON.stringify(payload, null, " ")
|
|
12
|
+
fs.writeFileSync(filename, content);
|
|
13
|
+
return res
|
|
14
|
+
}
|
|
15
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "this tool sends a message to another ai chat as 'user' role, result of the tool is an 'assistant' reply",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"filename": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "File name to read/write chat history, should have .chat extension"
|
|
9
|
+
},
|
|
10
|
+
"system": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Filename that contains system prompt, required once at the beginning of simulation"
|
|
13
|
+
},
|
|
14
|
+
"text": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "User message to send"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["filename", "text"]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = async function message({ filename, system, text, stop }, ctx) {
|
|
2
|
+
let chat = await ctx.read(filename);
|
|
3
|
+
if (!chat && system) {
|
|
4
|
+
chat = `system: @@${system}`
|
|
5
|
+
}
|
|
6
|
+
chat = `${chat}\nuser:\n${text}`
|
|
7
|
+
const messages = await ctx.text2run(chat, {stop: "assistant" })
|
|
8
|
+
chat = `${chat}\n${ctx.msg2text(messages, true)}`
|
|
9
|
+
await ctx.write(filename, chat)
|
|
10
|
+
if (messages.length) {
|
|
11
|
+
return messages[messages.length - 1].content
|
|
12
|
+
}
|
|
13
|
+
return ""
|
|
14
|
+
}
|
package/src/mock.proc.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module.exports = async function mock(node, args, ctx) {
|
|
2
|
+
const re = /(?<name>\w+)\s*=/
|
|
3
|
+
let lastName;
|
|
4
|
+
let m;
|
|
5
|
+
let text = args;
|
|
6
|
+
const vars = {};
|
|
7
|
+
while ((m = re.exec(text)) !== null) {
|
|
8
|
+
const { name } = m.groups
|
|
9
|
+
|
|
10
|
+
if (lastName) {
|
|
11
|
+
vars[lastName] = text.slice(0, m.index).trim()
|
|
12
|
+
}
|
|
13
|
+
text = text.slice(m.index + m[0].length)
|
|
14
|
+
lastName = name
|
|
15
|
+
}
|
|
16
|
+
if (lastName) {
|
|
17
|
+
vars[lastName] = text.trim()
|
|
18
|
+
}
|
|
19
|
+
ctx.use((name, ctx, args)=> {
|
|
20
|
+
if (!vars.hasOwnProperty(name)) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
...node,
|
|
25
|
+
type: "text",
|
|
26
|
+
name,
|
|
27
|
+
read: async () => vars[name]
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
return {
|
|
31
|
+
...node,
|
|
32
|
+
type: "text",
|
|
33
|
+
read: async() => ""
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/osa.tool.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const util = require('util');
|
|
3
|
+
|
|
4
|
+
module.exports = async function osa({ text }) {
|
|
5
|
+
let result = "";
|
|
6
|
+
try {
|
|
7
|
+
result = execSync("osascript -", { input: text, encoding: "utf8" });
|
|
8
|
+
} catch (e) {
|
|
9
|
+
result = e.stderr + e.stdout;
|
|
10
|
+
}
|
|
11
|
+
return (result || "").replaceAll("@", "\\@");
|
|
12
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Apply patches to a specified file based on the given text",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"text": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The text containing patches to apply, formated:\n```\n<<<<<<< ORIGINAL\nold code\n=======\nnew code\n>>>>>>> UPDATED\n```"
|
|
9
|
+
},
|
|
10
|
+
"filename": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "The path to the file that needs to be patched."
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["text", "filename"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
|
|
3
|
+
// Patch tool to apply custom diffs marked with <<<<<<< ORIGINAL and >>>>>>> UPDATED
|
|
4
|
+
// Handles patches with context and applies only the segments between markers.
|
|
5
|
+
|
|
6
|
+
module.exports = async function patch({ text, filename }, ctx) {
|
|
7
|
+
// Regex to match each patch block
|
|
8
|
+
const patchRegex = /<<<<<<< ORIGINAL[^\n]*\n([\s\S]*?)=======\n([\s\S]*?)>>>>>>> UPDATED[^\n]*(?:\n|$)/g;
|
|
9
|
+
const patches = [];
|
|
10
|
+
let match;
|
|
11
|
+
|
|
12
|
+
// Extract all old/new segments
|
|
13
|
+
while ((match = patchRegex.exec(text)) !== null) {
|
|
14
|
+
const oldPart = match[1].replace(/^\n+|\n+$/g, "");
|
|
15
|
+
const newPart = match[2].replace(/^\n+|\n+$/g, "");
|
|
16
|
+
patches.push({ oldPart, newPart });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (patches.length === 0) {
|
|
20
|
+
throw new Error("No valid patch segments found");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let fileContent = await ctx.read(filename);
|
|
24
|
+
|
|
25
|
+
for (const { oldPart, newPart } of patches) {
|
|
26
|
+
// Escape regex special chars in oldPart, then allow flexible whitespace
|
|
27
|
+
const escaped = oldPart
|
|
28
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
29
|
+
.replace(/\s+/g, "\\s+");
|
|
30
|
+
const oldRegex = new RegExp(escaped, "g");
|
|
31
|
+
|
|
32
|
+
// Perform replacement using a function to avoid replacement string ambiguities
|
|
33
|
+
fileContent = fileContent.replace(oldRegex, () => newPart);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await ctx.write(filename, fileContent);
|
|
37
|
+
return "patched";
|
|
38
|
+
};
|
package/src/prop.proc.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function parseVal(value) {
|
|
2
|
+
try {
|
|
3
|
+
return JSON.parse(value);
|
|
4
|
+
} catch (e) { }
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
module.exports = async function prop(node, args, ctx) {
|
|
8
|
+
if (!node || node.type !== 'llm') {
|
|
9
|
+
return node;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const re = /(?<name>\w+)\s*=/;
|
|
13
|
+
let lastName;
|
|
14
|
+
let m;
|
|
15
|
+
let text = args;
|
|
16
|
+
const vars = {};
|
|
17
|
+
while ((m = re.exec(text)) !== null) {
|
|
18
|
+
const { name } = m.groups;
|
|
19
|
+
|
|
20
|
+
if (lastName) {
|
|
21
|
+
vars[lastName] = parseVal(text.slice(0, m.index).trim());
|
|
22
|
+
}
|
|
23
|
+
text = text.slice(m.index + m[0].length);
|
|
24
|
+
lastName = name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (lastName) {
|
|
28
|
+
vars[lastName] = parseVal(text.trim());
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
...node,
|
|
32
|
+
exec: async (payload, ctx) => node.exec({ ...payload, ...vars }, ctx)
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Execute Python code using a Node.js child process",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"text": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The Python code to execute"
|
|
9
|
+
},
|
|
10
|
+
"filename": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "filename to save script to"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["text"]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/py.tool.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def main(params):
|
|
5
|
+
if 'filename' in params:
|
|
6
|
+
with open(params['filename'], 'w') as file:
|
|
7
|
+
file.write(params['text'])
|
|
8
|
+
|
|
9
|
+
parsed = ast.parse(params['text'])
|
|
10
|
+
last_stmt = parsed.body[-1]
|
|
11
|
+
if isinstance(last_stmt, ast.Expr):
|
|
12
|
+
parsed.body[-1] = ast.Assign(
|
|
13
|
+
targets=[ast.Name(id='myvariable', ctx=ast.Store())],
|
|
14
|
+
value=last_stmt.value)
|
|
15
|
+
|
|
16
|
+
ast.fix_missing_locations(parsed)
|
|
17
|
+
compiled = compile(parsed, filename="cell.py", mode="exec")
|
|
18
|
+
namespace = {}
|
|
19
|
+
exec(compiled, namespace)
|
|
20
|
+
|
|
21
|
+
return namespace.get('myvariable', None)
|
|
22
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports = async function resolve(node, args, ctx) {
|
|
2
|
+
if (!node) {
|
|
3
|
+
return
|
|
4
|
+
}
|
|
5
|
+
if (node.type !== "text") {
|
|
6
|
+
throw Error(`can resolve only text nodes, got ${node.type}`)
|
|
7
|
+
}
|
|
8
|
+
const filename = await node.read()
|
|
9
|
+
return ctx.resolve(filename.trim())
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Read the contents of a specified file",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"filename": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The name of the file to read"
|
|
9
|
+
},
|
|
10
|
+
"linenum": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"description": "prepend every line with linenumber"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["filename"]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/rf.tool.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { relative, dirname } = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = async function readFile({ filename, linenum }, ctx) {
|
|
4
|
+
const resolved = await ctx.resolve(filename);
|
|
5
|
+
if (!resolved) {
|
|
6
|
+
return "File not found";
|
|
7
|
+
}
|
|
8
|
+
const relFile = relative(process.cwd(), filename);
|
|
9
|
+
const pathArr = [ relFile ];
|
|
10
|
+
if (resolved.type !== 'text') {
|
|
11
|
+
pathArr.push('text');
|
|
12
|
+
}
|
|
13
|
+
if (linenum) {
|
|
14
|
+
pathArr.push('linenum');
|
|
15
|
+
}
|
|
16
|
+
if (pathArr.length > 1) {
|
|
17
|
+
return`@{ ${pathArr.join(" | ")} }`;
|
|
18
|
+
}
|
|
19
|
+
return `@${relFile}`;
|
|
20
|
+
};
|
package/src/sh.tool.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const util = require('util');
|
|
3
|
+
|
|
4
|
+
module.exports = async function sh({ text }) {
|
|
5
|
+
let result = "";
|
|
6
|
+
try {
|
|
7
|
+
result = execSync(text, { encoding: "utf8" });
|
|
8
|
+
} catch (e) {
|
|
9
|
+
result = e.stderr + e.stdout;
|
|
10
|
+
}
|
|
11
|
+
return (result || "").replaceAll("@", "\\@");
|
|
12
|
+
};
|
package/src/shp.proc.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { execSync, spawnSync } = require('child_process');
|
|
2
|
+
|
|
3
|
+
const shp = async (node, args, ctx) => ({
|
|
4
|
+
type: 'text',
|
|
5
|
+
read: async () => {
|
|
6
|
+
let input = null;
|
|
7
|
+
|
|
8
|
+
if (node && node.type === 'text') {
|
|
9
|
+
input = await node.read();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let result;
|
|
13
|
+
try {
|
|
14
|
+
if (input !== null) {
|
|
15
|
+
const res = spawnSync(args.trim(), {
|
|
16
|
+
input,
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
shell: true
|
|
19
|
+
});
|
|
20
|
+
result = (res.stdout || '') + (res.stderr || '');
|
|
21
|
+
} else {
|
|
22
|
+
result = execSync(args.trim(), { encoding: 'utf8' });
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
result = e.stderr + e.stdout;
|
|
26
|
+
}
|
|
27
|
+
return result.replaceAll('@', '\\@');
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
module.exports = shp;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module.exports = async function slice(node, args, context) {
|
|
2
|
+
if (!node || node.type !== 'text') {
|
|
3
|
+
return node;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const parts = args ? args.trim().split(/\s+/) : [];
|
|
7
|
+
let start, end;
|
|
8
|
+
|
|
9
|
+
const parseIdx = (str) => {
|
|
10
|
+
const n = parseInt(str, 10);
|
|
11
|
+
if (isNaN(n)) return undefined;
|
|
12
|
+
return n;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
if (parts.length >= 1) start = parseIdx(parts[0]);
|
|
16
|
+
if (parts.length >= 2) end = parseIdx(parts[1]);
|
|
17
|
+
|
|
18
|
+
const newNode = Object.assign({}, node);
|
|
19
|
+
|
|
20
|
+
newNode.read = async () => {
|
|
21
|
+
const text = await node.read();
|
|
22
|
+
const lines = text.split(/\r?\n/);
|
|
23
|
+
const N = lines.length;
|
|
24
|
+
|
|
25
|
+
// Convert 1-based to 0-based, handle negative indices
|
|
26
|
+
let s = start;
|
|
27
|
+
let e = end;
|
|
28
|
+
|
|
29
|
+
if (s === undefined) {
|
|
30
|
+
s = 1; // default to line 1
|
|
31
|
+
}
|
|
32
|
+
s = s < 0 ? N + s : s - 1; // negative or positive 1-based
|
|
33
|
+
if (s < 0) s = 0;
|
|
34
|
+
if (s > N) s = N;
|
|
35
|
+
|
|
36
|
+
if (e !== undefined) {
|
|
37
|
+
e = e < 0 ? N + e : e; // negative or positive 1-based inclusive
|
|
38
|
+
}
|
|
39
|
+
// Since Array.slice end index is exclusive and ours is inclusive, add 0 if undefined, +0 else +0
|
|
40
|
+
// But since end is inclusive for user, exclusive for slice, +1
|
|
41
|
+
if (e !== undefined) {
|
|
42
|
+
e = e < 0 ? Math.max(0, e + 1) : e; // ensure inclusive end
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Convert to slice compatible indices
|
|
46
|
+
const result = lines.slice(
|
|
47
|
+
s,
|
|
48
|
+
e !== undefined ? e : undefined
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return result.join('\n');
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return newNode;
|
|
55
|
+
};
|
package/src/tail.proc.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module.exports = async function tail(node, args, context) {
|
|
2
|
+
if (!node || (node.type !== 'text' && node.type !== 'llm')) {
|
|
3
|
+
return node;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const n = parseInt(args, 10);
|
|
7
|
+
const count = (isNaN(n) || n <= 0) ? 20 : n;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if (node.type === 'text') {
|
|
11
|
+
return {
|
|
12
|
+
...node,
|
|
13
|
+
read: async () => {
|
|
14
|
+
const text = await node.read();
|
|
15
|
+
return text.split(/\r?\n/).slice(-count -1).join('\n');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// llm
|
|
21
|
+
return {
|
|
22
|
+
...node,
|
|
23
|
+
exec: async (payload, ctx) => {
|
|
24
|
+
let startIndex = Math.max(0, payload.messages.length - count)
|
|
25
|
+
startIndex = payload.messages.findIndex((msg, index) => (index >= startIndex) && (msg.role === 'user' || msg.role ==='assistant'))
|
|
26
|
+
const messages = payload.messages.filter(
|
|
27
|
+
(message, index) => (message.role === "system") || (index >= startIndex))
|
|
28
|
+
|
|
29
|
+
return node.exec({
|
|
30
|
+
...payload,
|
|
31
|
+
messages
|
|
32
|
+
}, ctx)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
package/src/text.proc.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Switch roles/agents",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"role": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The name of the role/agent to switch to"
|
|
9
|
+
},
|
|
10
|
+
"filename": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "save the role name to filename that keeps the current role"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["name", "filename"]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/turn.tool.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Asynchronous function to write content to a file",
|
|
3
|
+
"parameters": {
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"filename": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The name of the file to write"
|
|
9
|
+
},
|
|
10
|
+
"text": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "The content to write to the file"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["filename", "text"]
|
|
16
|
+
}
|
|
17
|
+
}
|