tune-sdk 0.2.4 → 0.2.6
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/.github/workflows/publish.yml +27 -0
- package/README.md +4 -6
- package/dist/cli.js +8 -7
- package/dist/tune.js +6 -6
- package/docs/vscode.md +133 -0
- package/package.json +2 -5
- package/tools/README.md +0 -636
- package/tools/append.schema.json +0 -17
- package/tools/append.tool.mjs +0 -5
- package/tools/brave.schema.json +0 -13
- package/tools/brave.tool.mjs +0 -32
- package/tools/claude.txt +0 -1
- package/tools/clipboard.txt +0 -1
- package/tools/default.llm.js +0 -2
- package/tools/dev.txt +0 -6
- package/tools/echo.txt +0 -1
- package/tools/editor-filename.chat +0 -10
- package/tools/gemini-dev.txt +0 -7
- package/tools/gemini25.txt +0 -1
- package/tools/gemini_files.schema.json +0 -13
- package/tools/gemini_files.tool.mjs +0 -204
- package/tools/gemini_ocr.schema.json +0 -21
- package/tools/gemini_ocr.tool.mjs +0 -211
- package/tools/gemini_tts.schema.json +0 -59
- package/tools/gemini_tts.tool.mjs +0 -175
- package/tools/gemini_veo.schema.json +0 -12
- package/tools/gemini_veo.tool.mjs +0 -233
- package/tools/groq_whisper.schema.json +0 -48
- package/tools/groq_whisper.tool.mjs +0 -59
- package/tools/head.proc.js +0 -24
- package/tools/init.proc.js +0 -19
- package/tools/jina_r.schema.json +0 -21
- package/tools/jina_r.tool.mjs +0 -27
- package/tools/js.schema.json +0 -19
- package/tools/js.tool.mjs +0 -60
- package/tools/json_format.proc.mjs +0 -22
- package/tools/linenum.proc.js +0 -21
- package/tools/list.schema.json +0 -19
- package/tools/list.tool.mjs +0 -20
- package/tools/llm-utils.js +0 -150
- package/tools/log.proc.js +0 -15
- package/tools/mcp.proc.mjs +0 -174
- package/tools/message.schema.json +0 -21
- package/tools/message.tool.js +0 -14
- package/tools/mock.proc.js +0 -35
- package/tools/nu.schema.json +0 -13
- package/tools/nu.tool.mjs +0 -14
- package/tools/openai.js +0 -27
- package/tools/openai_imgen.schema.json +0 -35
- package/tools/openai_imgen.tool.mjs +0 -83
- package/tools/openai_stt.schema.json +0 -49
- package/tools/openai_stt.tool.mjs +0 -66
- package/tools/openai_tts.schema.json +0 -26
- package/tools/openai_tts.tool.mjs +0 -26
- package/tools/osa.schema.json +0 -13
- package/tools/osa.tool.mjs +0 -12
- package/tools/package.json +0 -7
- package/tools/patch.schema.json +0 -17
- package/tools/patch.tool.mjs +0 -38
- package/tools/prop.proc.mjs +0 -34
- package/tools/py.schema.json +0 -17
- package/tools/py.tool.py +0 -22
- package/tools/queryimage.schema.json +0 -17
- package/tools/queryimage.tool.chat +0 -4
- package/tools/resolve.proc.js +0 -10
- package/tools/rf.schema.json +0 -17
- package/tools/rf.tool.mjs +0 -21
- package/tools/schema.schema.json +0 -13
- package/tools/schema.tool.chat +0 -81
- package/tools/sh.schema.json +0 -13
- package/tools/sh.tool.mjs +0 -12
- package/tools/short.txt +0 -1
- package/tools/shp.proc.mjs +0 -31
- package/tools/slice.proc.js +0 -55
- package/tools/tail.proc.js +0 -35
- package/tools/text.proc.js +0 -13
- package/tools/turn.schema.json +0 -17
- package/tools/turn.tool.mjs +0 -8
- package/tools/wf.schema.json +0 -17
- package/tools/wf.tool.mjs +0 -16
- package/tools/yandex_tts.schema.json +0 -41
- package/tools/yandex_tts.tool.mjs +0 -31
package/tools/js.tool.mjs
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { parseScript } from 'esprima';
|
|
2
|
-
import { generate } from 'escodegen';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { writeFileSync, unlinkSync } from 'node:fs';
|
|
5
|
-
import { randomBytes } from 'crypto';
|
|
6
|
-
|
|
7
|
-
export default 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
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
|
|
3
|
-
export default 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
|
-
}
|
package/tools/linenum.proc.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
}
|
package/tools/list.schema.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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/tools/list.tool.mjs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
export default 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/tools/llm-utils.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
|
|
4
|
-
// Create cache directory if it doesn't exist
|
|
5
|
-
const cacheDir = path.join(__dirname, ".cache");
|
|
6
|
-
if (!fs.existsSync(cacheDir)) {
|
|
7
|
-
fs.mkdirSync(cacheDir, { recursive: true });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Creates a model cache manager for a specific provider
|
|
12
|
-
* @param {string} providerName - Name of the provider (used for cache file)
|
|
13
|
-
* @param {function} fetchModelsFunction - Function to fetch models from API
|
|
14
|
-
* @returns {function} getModels function that handles caching
|
|
15
|
-
*/
|
|
16
|
-
function createModelCache(providerName, fetchModelsFunction) {
|
|
17
|
-
const cacheFile = path.join(cacheDir, `${providerName}_models.json`);
|
|
18
|
-
let cache;
|
|
19
|
-
|
|
20
|
-
return async function getModels(...args) {
|
|
21
|
-
if (cache) {
|
|
22
|
-
return cache;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Check if cache exists and is less than an hour old
|
|
26
|
-
if (fs.existsSync(cacheFile)) {
|
|
27
|
-
const stats = fs.statSync(cacheFile);
|
|
28
|
-
const cacheAge = Date.now() - stats.mtimeMs;
|
|
29
|
-
const oneHourMs = 60 * 60 * 1000; // 1 hour in milliseconds
|
|
30
|
-
|
|
31
|
-
if (cacheAge < oneHourMs) {
|
|
32
|
-
try {
|
|
33
|
-
const cachedData = fs.readFileSync(cacheFile, "utf8");
|
|
34
|
-
cache = JSON.parse(cachedData);
|
|
35
|
-
return cache;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
console.warn(`Error reading cache for ${providerName}:`, error);
|
|
38
|
-
// Continue to fetch from API if cache reading fails
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Fetch from API if cache doesn't exist, is too old, or couldn't be read
|
|
44
|
-
try {
|
|
45
|
-
const models = await fetchModelsFunction(...args);
|
|
46
|
-
cache = models;
|
|
47
|
-
fs.writeFileSync(cacheFile, JSON.stringify(models, null, " "), "utf8");
|
|
48
|
-
return models;
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error(`Error fetching models for ${providerName}:`, error);
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Creates a context provider function with standard environment and regex matching
|
|
58
|
-
* @param {string} providerName - Name of the provider
|
|
59
|
-
* @param {Object} options - Configuration options
|
|
60
|
-
* @returns {function} Provider context function
|
|
61
|
-
*/
|
|
62
|
-
function createProviderContext(providerName, options) {
|
|
63
|
-
const {
|
|
64
|
-
apiKeyEnv,
|
|
65
|
-
modelMatcher,
|
|
66
|
-
modelFilter,
|
|
67
|
-
createExecFunction,
|
|
68
|
-
apiModelFetcher
|
|
69
|
-
} = options;
|
|
70
|
-
|
|
71
|
-
const getModels = createModelCache(providerName, apiModelFetcher);
|
|
72
|
-
const envr = /^[A-Z_0-9]+$/;
|
|
73
|
-
|
|
74
|
-
return async function providerContext(name, args) {
|
|
75
|
-
// respond only to llm request or if type is 'any'
|
|
76
|
-
if (args.type !== 'any' && args.type !== 'llm') {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const context = this;
|
|
81
|
-
if (envr.test(name)) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Check if this model name should be handled by this provider
|
|
86
|
-
if (modelMatcher && !modelMatcher(name)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const apiKey = await context.read(apiKeyEnv);
|
|
91
|
-
if (!apiKey) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const models = await getModels(apiKey);
|
|
97
|
-
|
|
98
|
-
// Filter models based on name and args
|
|
99
|
-
let matchedModels = [];
|
|
100
|
-
if (modelFilter) {
|
|
101
|
-
matchedModels = modelFilter(models, name, args);
|
|
102
|
-
} else {
|
|
103
|
-
// Default filter by exact match or regex
|
|
104
|
-
let re;
|
|
105
|
-
if (args.match === "regex") {
|
|
106
|
-
re = new RegExp(name);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
matchedModels = models.filter((item) => {
|
|
110
|
-
if (args.match === "exact" && item.id === name) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
if (re) {
|
|
114
|
-
return re.test(item.id);
|
|
115
|
-
}
|
|
116
|
-
return false;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (!matchedModels.length) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (args.output === 'all') {
|
|
125
|
-
return matchedModels.map(model => ({
|
|
126
|
-
type: "llm",
|
|
127
|
-
name: model.id || model.name
|
|
128
|
-
}));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const model = matchedModels[0];
|
|
132
|
-
return {
|
|
133
|
-
type: "llm",
|
|
134
|
-
exec: async (payload) => {
|
|
135
|
-
// Get a fresh key in case it's rotated
|
|
136
|
-
const key = await this.read(apiKeyEnv);
|
|
137
|
-
return createExecFunction(model, payload, key, this);
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
} catch (e ) {
|
|
141
|
-
console.log(e)
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
module.exports = {
|
|
148
|
-
createModelCache,
|
|
149
|
-
createProviderContext
|
|
150
|
-
};
|
package/tools/log.proc.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
})
|
package/tools/mcp.proc.mjs
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
|
|
9
|
-
/*
|
|
10
|
-
const client = new Client({
|
|
11
|
-
name: "Tune",
|
|
12
|
-
version: "1.0.0"
|
|
13
|
-
})
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
async function makeEnv(ctx) {
|
|
17
|
-
const TUNE_PATH = await ctx.read('TUNE_PATH');
|
|
18
|
-
if (!TUNE_PATH) {
|
|
19
|
-
throw Error('TUNE_PATH not set')
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return ctx.stack
|
|
24
|
-
.filter(item => item.dirname)
|
|
25
|
-
.map(item => item.dirname)
|
|
26
|
-
.reverse()
|
|
27
|
-
.concat(TUNE_PATH.split(path.delimiter))
|
|
28
|
-
.reduce((env, dir, index, arr) => {
|
|
29
|
-
if (arr.indexOf(dir) < index) {
|
|
30
|
-
return env
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const envFile = path.join(dir, ".env")
|
|
34
|
-
|
|
35
|
-
if (!fs.existsSync(envFile)) {
|
|
36
|
-
return env
|
|
37
|
-
}
|
|
38
|
-
fs.readFileSync(envFile, 'utf8')
|
|
39
|
-
.split('\n')
|
|
40
|
-
.forEach(line => {
|
|
41
|
-
const m = line.match(/^\s*(\w+)\s*=\s*["']?(.*?)\s*["']?$/)
|
|
42
|
-
if (!m) return
|
|
43
|
-
env[m[1]] = m[2]
|
|
44
|
-
})
|
|
45
|
-
return env
|
|
46
|
-
|
|
47
|
-
}, process.env)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function getMcpClient(args, env) {
|
|
51
|
-
const client = {}
|
|
52
|
-
const [command, ...rest] = args.trim().split(/\s+/);
|
|
53
|
-
const process = spawn(command, rest, { env })
|
|
54
|
-
let chunks = []
|
|
55
|
-
const nodes = []
|
|
56
|
-
const callbacks = {}
|
|
57
|
-
function onError(err ) {
|
|
58
|
-
console.log("onError", err)
|
|
59
|
-
if (client.onError) {
|
|
60
|
-
client.onError(err)
|
|
61
|
-
}
|
|
62
|
-
Object.keys(callbacks).forEach(mid=> {
|
|
63
|
-
callbacks[mid].reject(err)
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
process.stdout.on('data', (data) => {
|
|
67
|
-
try {
|
|
68
|
-
chunks.push(data)
|
|
69
|
-
let buf = Buffer.concat(chunks)
|
|
70
|
-
let index
|
|
71
|
-
while ((index = buf.indexOf('\n')) !== -1) {
|
|
72
|
-
const line = buf.toString("utf8", 0, index);
|
|
73
|
-
buf = buf.subarray(index + 1);
|
|
74
|
-
const msg = JSON.parse(line)
|
|
75
|
-
if (msg.id && callbacks[msg.id]) {
|
|
76
|
-
const { resolve, reject} = callbacks[msg.id]
|
|
77
|
-
if (msg.result) {
|
|
78
|
-
resolve(msg.result)
|
|
79
|
-
}
|
|
80
|
-
if (msg.error) {
|
|
81
|
-
reject(msg.error)
|
|
82
|
-
}
|
|
83
|
-
delete callbacks[msg.id]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
chunks = [buf]
|
|
87
|
-
} catch (e) {
|
|
88
|
-
onError(e)
|
|
89
|
-
process.kill()
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
let msgId = 1
|
|
94
|
-
client.request = async (params) =>
|
|
95
|
-
new Promise((resolve, reject) => {
|
|
96
|
-
const payload = JSON.stringify({
|
|
97
|
-
...params,
|
|
98
|
-
jsonrpc: "2.0",
|
|
99
|
-
id: msgId,
|
|
100
|
-
})
|
|
101
|
-
process.stdin.write(`${payload}\n`)
|
|
102
|
-
callbacks[msgId] = {resolve, reject}
|
|
103
|
-
msgId++
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
const errChunks = []
|
|
107
|
-
process.stderr.on('data', (data) => {
|
|
108
|
-
errChunks.push(data)
|
|
109
|
-
console.log(data.toString('utf8'))
|
|
110
|
-
});
|
|
111
|
-
process.on('error', onError);
|
|
112
|
-
process.on('close', (code) => {
|
|
113
|
-
client.err = Buffer.concat(errChunks).toString('utf8')
|
|
114
|
-
client.data = Buffer.concat(chunks).toString('utf8')
|
|
115
|
-
if (code !== 0) {
|
|
116
|
-
return onError(new Error(client.err))
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
let res = await client.request({
|
|
120
|
-
method: "initialize",
|
|
121
|
-
params: {
|
|
122
|
-
protocolVersion: "2024-11-05",
|
|
123
|
-
capabilities: { tools: {} },
|
|
124
|
-
clientInfo: { name: "Tune", version: "1.0.0"}
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
client.server = res
|
|
128
|
-
|
|
129
|
-
client.close = () => process.kill()
|
|
130
|
-
return client
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export default async function mcp(node, args, ctx) {
|
|
134
|
-
const env = await makeEnv(ctx)
|
|
135
|
-
const client = await getMcpClient(args, env)
|
|
136
|
-
if (! client.server.capabilities.tools) {
|
|
137
|
-
throw Error(`${args} does not support tools`)
|
|
138
|
-
}
|
|
139
|
-
//client.onError()
|
|
140
|
-
|
|
141
|
-
let res = await client.request({ method: "tools/list"})
|
|
142
|
-
//const name = args.trim().toLowerCase().replace(/\W+/g, "_")
|
|
143
|
-
//fs.writeFileSync(path.join(__dirname, `${name}-mcp.json` ), JSON.stringify(res.tools, null, " "))
|
|
144
|
-
return res.tools.map(item => ({
|
|
145
|
-
type: "tool",
|
|
146
|
-
name: item.name,
|
|
147
|
-
schema: {
|
|
148
|
-
name: item.name,
|
|
149
|
-
description: item.description,
|
|
150
|
-
parameters: item.inputSchema
|
|
151
|
-
},
|
|
152
|
-
exec: async (args) => {
|
|
153
|
-
let res = await client.request({method: "tools/call", params: {
|
|
154
|
-
name: item.name,
|
|
155
|
-
arguments: args
|
|
156
|
-
}})
|
|
157
|
-
res = res.toolResult || res
|
|
158
|
-
const content = res.content
|
|
159
|
-
.map(item => {
|
|
160
|
-
if (item.type == "text") {
|
|
161
|
-
return item.text.replaceAll("@", "\\@")
|
|
162
|
-
}
|
|
163
|
-
// not supported yet
|
|
164
|
-
return ""
|
|
165
|
-
})
|
|
166
|
-
.join("\n")
|
|
167
|
-
|
|
168
|
-
if (res.isError) {
|
|
169
|
-
throw Error(`error calling ${item.name}\n${content}`)
|
|
170
|
-
}
|
|
171
|
-
return content
|
|
172
|
-
}
|
|
173
|
-
}))
|
|
174
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
}
|
package/tools/message.tool.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
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/tools/mock.proc.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
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/tools/nu.schema.json
DELETED
package/tools/nu.tool.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import util from 'node:util'
|
|
3
|
-
|
|
4
|
-
export default async function nu({ text }) {
|
|
5
|
-
let result = ""
|
|
6
|
-
try {
|
|
7
|
-
result = spawnSync("nu",
|
|
8
|
-
["-c", text, "--error-style", "plain"],
|
|
9
|
-
{ encoding: "utf8", shell: false })
|
|
10
|
-
} catch (e) {
|
|
11
|
-
result = e.stderr + e.stdout
|
|
12
|
-
}
|
|
13
|
-
return (result.stdout || result.stderr || "").replaceAll("@", "\\@");
|
|
14
|
-
}
|
package/tools/openai.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module.exports = function(props, transform) {
|
|
2
|
-
const { auth_key, url } = props;
|
|
3
|
-
delete props.auth_key;
|
|
4
|
-
delete props.url;
|
|
5
|
-
return async function(payload, ctx) {
|
|
6
|
-
const key = auth_key || await ctx.read('OPENAI_KEY');
|
|
7
|
-
if (typeof(transform) === 'function') {
|
|
8
|
-
payload = transform(payload)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const result = ({
|
|
12
|
-
url: url || "https://api.openai.com/v1/chat/completions",
|
|
13
|
-
method: "POST",
|
|
14
|
-
headers: {
|
|
15
|
-
"content-type": "application/json",
|
|
16
|
-
authorization: `Bearer ${key}`
|
|
17
|
-
},
|
|
18
|
-
body: JSON.stringify({
|
|
19
|
-
...props,
|
|
20
|
-
...payload,
|
|
21
|
-
messages: payload.messages.filter(msg => msg.role !== 'comment'),
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
// console.log(result)
|
|
25
|
-
return result
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
{
|
|
3
|
-
"description": "generate or change an image using openai image generation",
|
|
4
|
-
"parameters": {
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"text": {
|
|
8
|
-
"type": "string",
|
|
9
|
-
"description": "The prompt text for image generation. or details on what to change on existing images."
|
|
10
|
-
},
|
|
11
|
-
"filename": {
|
|
12
|
-
"type": "string",
|
|
13
|
-
"description": "The filename where the generated image will be saved."
|
|
14
|
-
},
|
|
15
|
-
"quality": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"enum": ["auto", "low", "medium", "high"],
|
|
18
|
-
"description": "Quality of the result, auto is default"
|
|
19
|
-
},
|
|
20
|
-
"size": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"enum": ["auto", "square", "landscape", "portrait"],
|
|
23
|
-
"description": "size of the image, auto is default"
|
|
24
|
-
},
|
|
25
|
-
"images": {
|
|
26
|
-
"type": "array",
|
|
27
|
-
"description": "Use it if you want to change existing image, or provide more context to newly generated image. If provided, the API will use the edits endpoint instead of generations. ",
|
|
28
|
-
"items": {
|
|
29
|
-
"type": "string"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"required": ["text", "filename"]
|
|
34
|
-
}
|
|
35
|
-
}
|