@j-o-r/hello-dave 0.0.9 → 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 +2 -0
- package/README.md.bak.1779452127 +240 -0
- package/TODO.md +31 -20
- package/agents/code_agent.js +6 -6
- package/agents/daisy_agent.js +10 -7
- package/agents/minimax.js +173 -0
- package/agents/spawn_agent.js +33 -10
- package/agents/stability.js +173 -0
- package/bin/codeDave +1 -1
- package/bin/dave.js +1 -1
- package/docs/dependencies.md +7 -0
- package/docs/music-toolsets.md +137 -0
- package/docs/plans/minimax-music-generation.md +80 -0
- package/docs/plans/unified-agent-architecture.md +146 -0
- package/docs/plans/websocket-streaming-plan.md.bak +317 -0
- package/docs/prompt/spawn_agent.md +46 -44
- package/docs/prompt/task_clarification_and_documentation.md +35 -0
- package/docs/todo-archive-infra-2026-04-21.md +15 -0
- package/docs/todo-archive-v0.1.0.md +32 -0
- package/lib/API/minimax/ImageToolset.js +169 -0
- package/lib/API/minimax/MusicToolset.js +290 -0
- package/lib/API/minimax/VideoToolset.js +296 -0
- package/lib/API/minimax/image.generation.md +239 -0
- package/lib/API/minimax/image.js +219 -0
- package/lib/API/minimax/image.to.image.md +257 -0
- package/lib/API/minimax/index.js +16 -0
- package/lib/API/minimax/music.cover.preprocess.md +206 -0
- package/lib/API/minimax/music.generation.md +346 -0
- package/lib/API/minimax/music.js +257 -0
- package/lib/API/minimax/music.lyrics.generation.md +205 -0
- package/lib/API/minimax/video.download.md +133 -0
- package/lib/API/minimax/video.first.last.image.md +186 -0
- package/lib/API/minimax/video.from.image.md +206 -0
- package/lib/API/minimax/video.from.subject.md +164 -0
- package/lib/API/minimax/video.generation.md +192 -0
- package/lib/API/minimax/video.js +339 -0
- package/lib/API/minimax/video.query.md +128 -0
- package/lib/API/stability.ai/ImageToolset.js +357 -0
- package/lib/API/stability.ai/MusicToolset.js +302 -0
- package/lib/API/stability.ai/audio-3.md +205 -0
- package/lib/API/stability.ai/audio.js +679 -0
- package/lib/API/stability.ai/image.js +911 -0
- package/lib/API/stability.ai/image.md +271 -0
- package/lib/API/stability.ai/index.js +11 -0
- package/lib/API/stability.ai/openapi.json +17118 -0
- package/lib/API/x.ai/ImageToolset.js +165 -0
- package/lib/API/x.ai/image.editing.md +86 -0
- package/lib/API/x.ai/image.js +393 -0
- package/lib/API/x.ai/image.md +213 -0
- package/lib/API/x.ai/image.to.generation.md +494 -0
- package/lib/API/x.ai/image.to.video.md +23 -0
- package/lib/API/x.ai/index.js +9 -0
- package/lib/AgentManager.js +1 -1
- package/lib/CdnToolset.js +191 -0
- package/lib/ToolSet.js +19 -1
- package/lib/cdn.js +373 -0
- package/lib/fafs.js +5 -3
- package/lib/genericToolset.js +75 -210
- package/lib/index.js +9 -1
- package/package.json +2 -2
- package/types/API/minimax/ImageToolset.d.ts +3 -0
- package/types/API/minimax/MusicToolset.d.ts +3 -0
- package/types/API/minimax/VideoToolset.d.ts +3 -0
- package/types/API/minimax/image.d.ts +109 -0
- package/types/API/minimax/index.d.ts +15 -0
- package/types/API/minimax/music.d.ts +46 -0
- package/types/API/minimax/video.d.ts +165 -0
- package/types/API/stability.ai/ImageToolset.d.ts +3 -0
- package/types/API/stability.ai/MusicToolset.d.ts +3 -0
- package/types/API/stability.ai/audio.d.ts +193 -0
- package/types/API/stability.ai/image.d.ts +274 -0
- package/types/API/stability.ai/index.d.ts +11 -0
- package/types/API/x.ai/ImageToolset.d.ts +3 -0
- package/types/API/x.ai/image.d.ts +82 -0
- package/types/API/x.ai/index.d.ts +9 -0
- package/types/AgentManager.d.ts +1 -1
- package/types/CdnToolset.d.ts +20 -0
- package/types/ToolSet.d.ts +8 -0
- package/types/cdn.d.ts +141 -0
- package/types/index.d.ts +8 -2
- package/utils/syntax_check.sh +59 -15
- package/docs/multi-agent-clusters.md.bak +0 -229
package/lib/genericToolset.js
CHANGED
|
@@ -7,12 +7,15 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
|
|
10
|
-
// STANDARDIZED UTILS PATHS
|
|
10
|
+
// STANDARDIZED UTILS PATHS
|
|
11
11
|
const utilsDir = path.resolve(__dirname, '..', 'utils');
|
|
12
12
|
const searchSessionsSh = path.join(utilsDir, 'search_sessions.sh');
|
|
13
13
|
const listSessionsSh = path.join(utilsDir, 'list_sessions.sh');
|
|
14
14
|
const syntaxCheckSh = path.join(utilsDir, 'syntax_check.sh');
|
|
15
15
|
|
|
16
|
+
// Do we have a SSH access point?
|
|
17
|
+
const SSH_EP = process.env.SSH_EP || '';
|
|
18
|
+
|
|
16
19
|
const user = await env();
|
|
17
20
|
const environment = `
|
|
18
21
|
Name: ${user.name}
|
|
@@ -26,47 +29,7 @@ ExternalIp: ${user.external_ip}
|
|
|
26
29
|
const tools = new ToolSet('auto');
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param {string} errorStr - Full Node.js error string.
|
|
31
|
-
* @returns {string} Simplified error.
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
|
-
const getJSError = (errorStr) => {
|
|
35
|
-
let result = '';
|
|
36
|
-
const linematch = errorStr.match(/\[eval\]:(\d+)/);
|
|
37
|
-
const lineNumber = linematch ? linematch[1] : '';
|
|
38
|
-
const match = errorStr.split(/\" \"\[eval\]:\d+/s);
|
|
39
|
-
if (match.length > 1) {
|
|
40
|
-
const res = match[1].split('\n').slice(0, -10).join('\n');
|
|
41
|
-
result = `Error: line ${lineNumber}\n${res} `;
|
|
42
|
-
} else {
|
|
43
|
-
result = errorStr;
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
};
|
|
47
|
-
// /**
|
|
48
|
-
// * Try to prevent double escaping by LLMS models
|
|
49
|
-
// * When a string stays a string it is probably double escaped
|
|
50
|
-
// * @parameter {string} s
|
|
51
|
-
// * @returns {string}
|
|
52
|
-
// */
|
|
53
|
-
// const guessOverEscaping = (s) => {
|
|
54
|
-
// let test;
|
|
55
|
-
// if (typeof s === 'string') {
|
|
56
|
-
// try {
|
|
57
|
-
// test = JSON.parse(s);
|
|
58
|
-
// } catch (_e) { }
|
|
59
|
-
// if (typeof test === 'string') {
|
|
60
|
-
// return test;
|
|
61
|
-
// }
|
|
62
|
-
// }
|
|
63
|
-
// return s;
|
|
64
|
-
// }
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Try to prevent double escaping by LLMs.
|
|
68
|
-
* Handles common patterns like \"path\", \\\"path\\\", JSON strings,
|
|
69
|
-
* and multiple layers while being much safer on complex bash/JS scripts.
|
|
32
|
+
* Single source of truth for cleaning over-escaped strings from LLMs.
|
|
70
33
|
*/
|
|
71
34
|
const guessOverEscaping = (s) => {
|
|
72
35
|
if (typeof s !== 'string') return s;
|
|
@@ -74,13 +37,12 @@ const guessOverEscaping = (s) => {
|
|
|
74
37
|
let current = s.trim();
|
|
75
38
|
const seen = new Set();
|
|
76
39
|
let iterations = 0;
|
|
77
|
-
const MAX_ITER = 6;
|
|
40
|
+
const MAX_ITER = 6;
|
|
78
41
|
|
|
79
42
|
while (iterations < MAX_ITER && !seen.has(current)) {
|
|
80
43
|
seen.add(current);
|
|
81
44
|
iterations++;
|
|
82
45
|
|
|
83
|
-
// 1. Most reliable: JSON.parse (undoes proper JSON string escaping)
|
|
84
46
|
try {
|
|
85
47
|
const parsed = JSON.parse(current);
|
|
86
48
|
if (typeof parsed === 'string') {
|
|
@@ -89,24 +51,19 @@ const guessOverEscaping = (s) => {
|
|
|
89
51
|
}
|
|
90
52
|
} catch (e) { }
|
|
91
53
|
|
|
92
|
-
// 2. Specifically strip the pattern you saw: \"...\" (including the backslashes)
|
|
93
54
|
if (current.startsWith('\\"') && current.endsWith('\\"')) {
|
|
94
55
|
current = current.slice(2, -2);
|
|
95
56
|
continue;
|
|
96
57
|
}
|
|
97
58
|
|
|
98
|
-
// 3. Strip normal outer quotes, but protect JSON objects/arrays
|
|
99
59
|
if (current.startsWith('"') && current.endsWith('"') && current.length > 2) {
|
|
100
|
-
const inner = current.slice(1, -1);
|
|
101
|
-
|
|
102
|
-
if (!trimmedInner.startsWith('{') && !trimmedInner.startsWith('[')) {
|
|
60
|
+
const inner = current.slice(1, -1).trim();
|
|
61
|
+
if (!inner.startsWith('{') && !inner.startsWith('[')) {
|
|
103
62
|
current = inner;
|
|
104
63
|
continue;
|
|
105
64
|
}
|
|
106
65
|
}
|
|
107
66
|
|
|
108
|
-
// 4. Conservative global unescape of \" → "
|
|
109
|
-
// Only applied to short strings without newlines (i.e. not full scripts)
|
|
110
67
|
if (!current.includes('\n') && (current.match(/\\"/g) || []).length >= 1) {
|
|
111
68
|
const lessEscaped = current.replace(/\\"/g, '"');
|
|
112
69
|
if (lessEscaped !== current) {
|
|
@@ -115,20 +72,31 @@ const guessOverEscaping = (s) => {
|
|
|
115
72
|
}
|
|
116
73
|
}
|
|
117
74
|
|
|
118
|
-
break;
|
|
75
|
+
break;
|
|
119
76
|
}
|
|
120
77
|
|
|
121
78
|
return current;
|
|
122
|
-
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const getJSError = (errorStr) => {
|
|
82
|
+
let result = '';
|
|
83
|
+
const linematch = errorStr.match(/\[eval\]:(\d+)/);
|
|
84
|
+
const lineNumber = linematch ? linematch[1] : '';
|
|
85
|
+
const match = errorStr.split(/" "\[eval\]:\d+/s);
|
|
86
|
+
if (match.length > 1) {
|
|
87
|
+
const res = match[1].split('\n').slice(0, -10).join('\n');
|
|
88
|
+
result = `Error: line ${lineNumber}\n${res} `;
|
|
89
|
+
} else {
|
|
90
|
+
result = errorStr;
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
123
96
|
* @module lib/genericToolset
|
|
124
|
-
* Secure utility tools
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* import tools from './lib/genericToolset.js';
|
|
128
|
-
* await tools.call('get_user_env', {});
|
|
129
|
-
*
|
|
130
|
-
* @see {@link module:./index~ToolSet}
|
|
97
|
+
* Secure utility tools.
|
|
131
98
|
*/
|
|
99
|
+
|
|
132
100
|
tools.add(
|
|
133
101
|
'javascript_interpreter',
|
|
134
102
|
'Execute ESM ES6 JavaScript on node.',
|
|
@@ -167,54 +135,45 @@ tools.add(
|
|
|
167
135
|
|
|
168
136
|
tools.add(
|
|
169
137
|
'execute_bash_script',
|
|
170
|
-
'Execute raw bash script or command (no escaping needed). Supports timeout
|
|
138
|
+
'Execute raw bash script or command (no escaping needed). Supports timeout.',
|
|
171
139
|
{
|
|
172
140
|
type: 'object',
|
|
173
141
|
properties: {
|
|
174
142
|
bash_script: {
|
|
175
143
|
type: 'string',
|
|
176
|
-
description: `
|
|
144
|
+
description: `Write raw bash exactly...`
|
|
177
145
|
},
|
|
178
|
-
timeout: {
|
|
179
|
-
|
|
180
|
-
default: 360,
|
|
181
|
-
description: 'Max execution time in seconds (default 360 seconds, 0 is no timeout). Uses @j-o-r/sh timeout (ms) with SIGTERM on expiry to prevent hangs from interactive prompts or slow commands.'
|
|
182
|
-
}
|
|
146
|
+
timeout: { type: 'number', default: 360 },
|
|
147
|
+
strict: { type: 'boolean', default: false }
|
|
183
148
|
},
|
|
184
149
|
required: ['bash_script']
|
|
185
150
|
},
|
|
186
151
|
async (params) => {
|
|
187
|
-
|
|
188
|
-
|
|
152
|
+
let bash_script = params.bash_script;
|
|
153
|
+
if (typeof bash_script !== 'string') bash_script = '';
|
|
154
|
+
// Check if the bash_script string has a shebang on the first line '#!'
|
|
155
|
+
// If not, add a bash shebang
|
|
156
|
+
const firstLine = bash_script.split('\n')[0] || '';
|
|
157
|
+
if (!firstLine.trim().startsWith('#!')) {
|
|
158
|
+
bash_script = '#!/bin/bash\n' + bash_script;
|
|
159
|
+
}
|
|
160
|
+
const timeoutSec = Number(params.timeout ?? 360);
|
|
161
|
+
const prams = params.strict ? '' : '-S error';
|
|
189
162
|
if (isNaN(timeoutSec) || timeoutSec < 0) throw new Error('Invalid timeout');
|
|
190
163
|
const timeout = timeoutSec * 1000;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return await SH`bash
|
|
194
|
-
${bash_script}
|
|
195
|
-
${delim}
|
|
196
|
-
`.options({ timeout }).run();
|
|
197
|
-
|
|
164
|
+
const valid = SH`shellcheck ${prams} -`.runSync(bash_script).stdout.toString().trim();
|
|
165
|
+
if (valid !== '') throw new Error(valid);
|
|
166
|
+
return await SH`bash`.options({ timeout }).run(bash_script);
|
|
198
167
|
}
|
|
199
168
|
);
|
|
200
169
|
|
|
201
170
|
tools.add(
|
|
202
|
-
'send_email',
|
|
203
|
-
'Send email via msmtp.',
|
|
204
|
-
{
|
|
205
|
-
type: 'object',
|
|
206
|
-
properties: {
|
|
207
|
-
to: { type: 'string', description: 'Recipient' },
|
|
208
|
-
subject: { type: 'string', description: 'Subject' },
|
|
209
|
-
body: { type: 'string', description: 'Body' }
|
|
210
|
-
},
|
|
211
|
-
required: ['to', 'subject', 'body']
|
|
212
|
-
},
|
|
171
|
+
'send_email', 'Send email via msmtp.', { type: 'object', properties: { to: { type: 'string' }, subject: { type: 'string' }, body: { type: 'string' } }, required: ['to', 'subject', 'body'] },
|
|
213
172
|
async (params) => {
|
|
214
173
|
const to = guessOverEscaping(params.to);
|
|
215
174
|
const subject = guessOverEscaping(params.subject);
|
|
216
175
|
const body = guessOverEscaping(params.body);
|
|
217
|
-
const delim = `END_EMAIL_${Date.now().toString(36)}
|
|
176
|
+
const delim = `END_EMAIL_${Date.now().toString(36)}`;
|
|
218
177
|
return await SH`msmtp ${params.to} <<'${delim}'
|
|
219
178
|
To: ${to}
|
|
220
179
|
Subject: ${subject}
|
|
@@ -226,174 +185,80 @@ ${delim}
|
|
|
226
185
|
);
|
|
227
186
|
|
|
228
187
|
tools.add(
|
|
229
|
-
'open_link',
|
|
230
|
-
|
|
231
|
-
{
|
|
232
|
-
type: 'object',
|
|
233
|
-
properties: {
|
|
234
|
-
url: { type: 'string', description: 'URL or file path' }
|
|
235
|
-
},
|
|
236
|
-
required: ['url']
|
|
237
|
-
},
|
|
238
|
-
async (params) => {
|
|
239
|
-
let url = guessOverEscaping(params.url?.trim());
|
|
240
|
-
return await SH`xdg-open ${[url]}`.run();
|
|
241
|
-
}
|
|
188
|
+
'open_link', 'Open URL/file with xdg-open.', { type: 'object', properties: { url: { type: 'string' } }, required: ['url'] },
|
|
189
|
+
async (params) => await SH`xdg-open ${[guessOverEscaping(params.url)]}`.run()
|
|
242
190
|
);
|
|
243
191
|
|
|
244
192
|
tools.add(
|
|
245
|
-
'execute_remote_script',
|
|
246
|
-
'Run bash on remote via SSH. Supports timeout to prevent hangs.',
|
|
193
|
+
'execute_remote_script', `Run bash on remote via SSH. ${SSH_EP}`,
|
|
247
194
|
{
|
|
248
195
|
type: 'object',
|
|
249
196
|
properties: {
|
|
250
|
-
url: { type: 'string', description:
|
|
251
|
-
script: { type: 'string'
|
|
252
|
-
timeout: {
|
|
253
|
-
type: 'number',
|
|
254
|
-
default: 30,
|
|
255
|
-
description: 'Max execution time in seconds (default 30). Uses @j-o-r/sh timeout (ms) with SIGTERM on SSH process expiry.'
|
|
256
|
-
}
|
|
197
|
+
url: { type: 'string', description: `ssh://user@host[:port] default: ${SSH_EP}` },
|
|
198
|
+
script: { type: 'string' },
|
|
199
|
+
timeout: { type: 'number', default: 30 }
|
|
257
200
|
},
|
|
258
201
|
required: ['url', 'script']
|
|
259
202
|
},
|
|
260
203
|
async (params) => {
|
|
261
204
|
let { url, script } = params;
|
|
205
|
+
if (!url || url === '') url = SSH_EP;
|
|
262
206
|
url = guessOverEscaping(url);
|
|
263
207
|
script = guessOverEscaping(script);
|
|
264
|
-
|
|
265
|
-
const timeoutSec = Number(params.timeout ?? 30);
|
|
266
|
-
if (isNaN(timeoutSec) || timeoutSec <= 0) throw new Error('Invalid timeout');
|
|
267
|
-
const timeoutMs = timeoutSec * 1000;
|
|
208
|
+
const timeoutMs = (Number(params.timeout ?? 30)) * 1000;
|
|
268
209
|
if (!url.startsWith('ssh://')) throw new Error('ssh://user@host[:port]');
|
|
269
210
|
const withoutProto = url.slice(6);
|
|
270
211
|
const parts = withoutProto.split(':');
|
|
271
212
|
let port = 22, userHost = withoutProto;
|
|
272
213
|
if (parts.length > 1) { userHost = parts[0]; port = parseInt(parts[1]); }
|
|
273
214
|
const [user, host] = userHost.split('@');
|
|
274
|
-
if (!user || !host) throw new Error('Invalid SSH URL');
|
|
275
215
|
return await SH`ssh -p ${port} ${user}@${host} bash`.options({ timeout: timeoutMs }).run(script);
|
|
276
216
|
}
|
|
277
217
|
);
|
|
278
218
|
|
|
279
219
|
tools.add(
|
|
280
|
-
'history_search',
|
|
281
|
-
'
|
|
282
|
-
{
|
|
283
|
-
type: 'object',
|
|
284
|
-
properties: {
|
|
285
|
-
query: { type: 'string', description: 'Query/regex or empty to list' }
|
|
286
|
-
},
|
|
287
|
-
required: []
|
|
288
|
-
},
|
|
220
|
+
'history_search', 'Search/list chat sessions in .cache/.',
|
|
221
|
+
{ type: 'object', properties: { query: { type: 'string' } } },
|
|
289
222
|
async (params) => {
|
|
290
223
|
if (typeof params.query === 'string' && params.query.trim()) {
|
|
291
|
-
|
|
292
|
-
return await SH`${searchSessionsSh} "${bashEscape(q)}"`.run();
|
|
224
|
+
return await SH`${searchSessionsSh} "${bashEscape(guessOverEscaping(params.query))}"`.run();
|
|
293
225
|
}
|
|
294
226
|
return await SH`${listSessionsSh}`.run();
|
|
295
227
|
}
|
|
296
228
|
);
|
|
297
229
|
|
|
298
230
|
tools.add(
|
|
299
|
-
'read_file',
|
|
300
|
-
'
|
|
301
|
-
{
|
|
302
|
-
type: 'object',
|
|
303
|
-
properties: {
|
|
304
|
-
file: { type: 'string', description: `Relative path. cwd: ${user.cwd}` }
|
|
305
|
-
},
|
|
306
|
-
required: ['file']
|
|
307
|
-
},
|
|
231
|
+
'read_file', 'Read file from CWD (relative path only).',
|
|
232
|
+
{ type: 'object', properties: { file: { type: 'string' } }, required: ['file'] },
|
|
308
233
|
async (params) => {
|
|
309
234
|
let file = guessOverEscaping(params.file?.trim());
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
const resolved = path.resolve(process.cwd(), file);
|
|
314
|
-
if (!resolved.startsWith(process.cwd())) throw new Error('Escapes CWD.');
|
|
315
|
-
return await fs.readFile(resolved, 'utf8');
|
|
235
|
+
if (!file || file.startsWith('/') || file.includes('..')) throw new Error('Relative CWD path only');
|
|
236
|
+
return await fs.readFile(path.resolve(process.cwd(), file), 'utf8');
|
|
316
237
|
}
|
|
317
238
|
);
|
|
318
239
|
|
|
319
240
|
tools.add(
|
|
320
|
-
'write_file',
|
|
321
|
-
'
|
|
322
|
-
{
|
|
323
|
-
type: 'object',
|
|
324
|
-
properties: {
|
|
325
|
-
file: { type: 'string', description: `Relative path. cwd: ${user.cwd}` },
|
|
326
|
-
content: { type: 'string', description: 'Raw content verbatim (no escaping).' }
|
|
327
|
-
},
|
|
328
|
-
required: ['file', 'content']
|
|
329
|
-
},
|
|
241
|
+
'write_file', 'Write/validate file in CWD.',
|
|
242
|
+
{ type: 'object', properties: { file: { type: 'string' }, content: { type: 'string' } }, required: ['file', 'content'] },
|
|
330
243
|
async (params) => {
|
|
331
|
-
let
|
|
332
|
-
let content = guessOverEscaping(params.content
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const dir = path.dirname(resolved);
|
|
339
|
-
const ext = path.extname(file);
|
|
340
|
-
let finalContent = content, validationMsg = '';
|
|
341
|
-
|
|
342
|
-
const temp1 = path.join(dir, `temp_${Date.now()}_${Math.random().toString(36).slice(2, 6)}${ext || '.tmp'}`);
|
|
343
|
-
await fs.writeFile(temp1, content, 'utf8');
|
|
344
|
-
|
|
345
|
-
try {
|
|
346
|
-
await SH`${syntaxCheckSh} ${[temp1]}`.run();
|
|
347
|
-
validationMsg = ' ✓ Syntax OK';
|
|
348
|
-
} catch (e) {
|
|
349
|
-
if (!['.js', '.mjs'].includes(ext)) {
|
|
350
|
-
await fs.unlink(temp1).catch(() => { });
|
|
351
|
-
throw new Error(`Syntax error: ${e.message}`);
|
|
352
|
-
}
|
|
353
|
-
let fixed = content.replace(/\\\\`/g, '\\`').replace(/\\\`/g, '`').replace(/\\`/g, '`').replace(/\\\$/g, '$').replace(/\\\${/g, '${');
|
|
354
|
-
const temp2 = path.join(dir, `temp_fix_${Date.now()}_${Math.random().toString(36).slice(2, 6)}.js`);
|
|
355
|
-
await fs.writeFile(temp2, fixed, 'utf8');
|
|
356
|
-
try {
|
|
357
|
-
await SH`${syntaxCheckSh} ${[temp2]}`.run();
|
|
358
|
-
finalContent = fixed;
|
|
359
|
-
await fs.rename(temp2, resolved);
|
|
360
|
-
await fs.unlink(temp1).catch(() => { });
|
|
361
|
-
validationMsg = ' ✓ Fixed JS';
|
|
362
|
-
} catch {
|
|
363
|
-
await fs.unlink(temp1).catch(() => { });
|
|
364
|
-
await fs.unlink(temp2).catch(() => { });
|
|
365
|
-
throw new Error(`Syntax error (fix failed): ${e.message}`);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (validationMsg === ' ✓ Syntax OK') await fs.rename(temp1, resolved);
|
|
370
|
-
|
|
371
|
-
if (finalContent.startsWith('#!')) {
|
|
372
|
-
await SH`chmod +x ${[resolved]}`.run();
|
|
373
|
-
validationMsg += ' ✓ +x';
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return `Wrote ${file} (${Buffer.byteLength(finalContent, 'utf8')} bytes).${validationMsg}`;
|
|
244
|
+
let fileParam = guessOverEscaping(params.file?.trim());
|
|
245
|
+
let content = guessOverEscaping(params.content || '');
|
|
246
|
+
if (!fileParam || fileParam.startsWith('/') || fileParam.includes('..')) throw new Error('Relative CWD path only');
|
|
247
|
+
const resolved = path.resolve(process.cwd(), fileParam);
|
|
248
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
249
|
+
await fs.writeFile(resolved, content, 'utf8');
|
|
250
|
+
return `Wrote ${fileParam} (${Buffer.byteLength(content, 'utf8')} bytes).`;
|
|
377
251
|
}
|
|
378
252
|
);
|
|
379
253
|
|
|
380
254
|
tools.add(
|
|
381
|
-
'syntax_check',
|
|
382
|
-
'
|
|
383
|
-
{
|
|
384
|
-
type: 'object',
|
|
385
|
-
properties: {
|
|
386
|
-
file: { type: 'string', description: 'Relative CWD path' }
|
|
387
|
-
},
|
|
388
|
-
required: ['file']
|
|
389
|
-
},
|
|
255
|
+
'syntax_check', 'Syntax validate file via utils/syntax_check.sh.',
|
|
256
|
+
{ type: 'object', properties: { file: { type: 'string' } }, required: ['file'] },
|
|
390
257
|
async (params) => {
|
|
391
258
|
const file = guessOverEscaping(params.file?.trim());
|
|
392
|
-
if (typeof file !== 'string' || !file) throw new Error('Relative path required.');
|
|
393
259
|
const resolved = path.resolve(process.cwd(), file);
|
|
394
|
-
|
|
395
|
-
return await SH`${syntaxCheckSh} ${[resolved]}`.run();
|
|
260
|
+
return await SH`${syntaxCheckSh} ${resolved}`.run() || 'Syntax OK';
|
|
396
261
|
}
|
|
397
262
|
);
|
|
398
263
|
|
|
399
|
-
export default tools;
|
|
264
|
+
export default tools;
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,9 @@ import {request as gpt} from './API/openai.com/reponses/text.js';
|
|
|
2
2
|
import {request as xai} from './API/x.ai/responses.js';
|
|
3
3
|
import {request as claude} from './API/anthropic.com/text.js';
|
|
4
4
|
import {request as brave} from './API/brave.com/search.js';
|
|
5
|
-
|
|
5
|
+
import minimax from './API/minimax/index.js';
|
|
6
|
+
import stability from './API/stability.ai/index.js';
|
|
7
|
+
import xai from './API/x.ai/index.js';
|
|
6
8
|
import { env, GLOBAL } from './fafs.js';
|
|
7
9
|
import ToolSet from './ToolSet.js';
|
|
8
10
|
import AgentServer from './AgentServer.js';
|
|
@@ -23,6 +25,9 @@ const API = {
|
|
|
23
25
|
search: {
|
|
24
26
|
brave
|
|
25
27
|
},
|
|
28
|
+
minimax,
|
|
29
|
+
stability,
|
|
30
|
+
xai
|
|
26
31
|
}
|
|
27
32
|
export {
|
|
28
33
|
AgentManager,
|
|
@@ -38,3 +43,6 @@ export {
|
|
|
38
43
|
wsCli,
|
|
39
44
|
wsIO
|
|
40
45
|
};
|
|
46
|
+
|
|
47
|
+
// Export the dedicated CDN toolset
|
|
48
|
+
export { default as CdnToolset } from './CdnToolset.js';
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j-o-r/hello-dave",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0
|
|
5
|
-
"description": "ESM toolkit for building AI agents
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "ESM toolkit for building AI agents, CLI xai coding agent and unified access to Grok (XAI), OpenAI, and Anthropic endpoints, music, image and video creation toolsets from minimax, stability and x.ai.",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./types/index.d.ts",
|
|
8
8
|
"typesVersions": {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get authentication headers for Minimax API.
|
|
3
|
+
*
|
|
4
|
+
* @returns {Object} Headers object with Authorization Bearer token.
|
|
5
|
+
* @throws {Error} If MINIMAX_API_KEY environment variable is not set.
|
|
6
|
+
*/
|
|
7
|
+
export function getHeaders(): Object;
|
|
8
|
+
/**
|
|
9
|
+
* Saves image data (either URL or base64) to a local temporary file.
|
|
10
|
+
* Handles both `response_format: 'url'` and `response_format: 'base64'`.
|
|
11
|
+
* Supports data URL prefix for base64.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} imageData - Either a URL or a base64-encoded string (with or without data: prefix).
|
|
14
|
+
* @param {string} [filenamePrefix='minimax-image'] - Prefix for the local filename.
|
|
15
|
+
* @param {number} [index=0] - Index for multiple images to avoid filename collisions.
|
|
16
|
+
* @returns {Promise<string>} Absolute path to the saved local file.
|
|
17
|
+
*/
|
|
18
|
+
export function saveImageToLocal(imageData: string, filenamePrefix?: string, index?: number): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Core image generation request (supports both Text-to-Image and Image-to-Image).
|
|
21
|
+
*
|
|
22
|
+
* Fully implements the official Minimax Image Generation API
|
|
23
|
+
* as documented in `lib/API/minimax/image.generation.md` and `image.to.image.md`.
|
|
24
|
+
*
|
|
25
|
+
* Uses the single endpoint `/v1/image_generation`.
|
|
26
|
+
*
|
|
27
|
+
* Supports all models, parameters, and response formats.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} prompt - Text description of the image, max 1500 characters.
|
|
30
|
+
* @param {Object} [options] - All available options from the official spec.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} [options.model='image-01'] - Model to use.
|
|
33
|
+
* Supported values:
|
|
34
|
+
* - 'image-01' (default, text-to-image and img2img)
|
|
35
|
+
* - 'image-01-live' (for image-to-image)
|
|
36
|
+
*
|
|
37
|
+
* @param {string} [options.aspect_ratio='1:1'] - Image aspect ratio.
|
|
38
|
+
* Options: '1:1', '16:9', '4:3', '3:2', '2:3', '3:4', '9:16', '21:9'
|
|
39
|
+
*
|
|
40
|
+
* @param {number} [options.width] - Image width in px (512-2048, divisible by 8).
|
|
41
|
+
* Only effective for model 'image-01'. aspect_ratio takes priority if both provided.
|
|
42
|
+
*
|
|
43
|
+
* @param {number} [options.height] - Image height in px (same rules as width).
|
|
44
|
+
*
|
|
45
|
+
* @param {string} [options.response_format='url'] - 'url' or 'base64'.
|
|
46
|
+
* Default is `'url'` (user preference).
|
|
47
|
+
* ⚠️ `url` links expire after 24 hours.
|
|
48
|
+
*
|
|
49
|
+
* @param {number} [options.seed] - Random seed for reproducibility.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} [options.n=1] - Number of images to generate (1-9).
|
|
52
|
+
*
|
|
53
|
+
* @param {boolean} [options.prompt_optimizer=false] - Enable automatic prompt optimization.
|
|
54
|
+
*
|
|
55
|
+
* @param {Array<Object>} [options.subject_reference] - For Image-to-Image.
|
|
56
|
+
* Array of subject references. Currently supports:
|
|
57
|
+
* - { type: 'character', image_file: 'https://...' or 'data:image/...;base64,...' }
|
|
58
|
+
*
|
|
59
|
+
* @param {Object} [options.extra] - Any additional parameters not yet documented.
|
|
60
|
+
*
|
|
61
|
+
* @returns {Promise<{
|
|
62
|
+
* image_urls: string[], // Array from data.image_urls (if url format)
|
|
63
|
+
* image_base64: string[], // Array from data.image_base64 (if base64 format)
|
|
64
|
+
* local_paths: string[], // Absolute paths to saved files
|
|
65
|
+
* metadata: Object, // { success_count, failed_count }
|
|
66
|
+
* id: string, // Trace ID
|
|
67
|
+
* duration: number, // API call duration in milliseconds
|
|
68
|
+
* raw: object // Full raw response
|
|
69
|
+
* }>}
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // Text-to-Image (basic)
|
|
73
|
+
* const result = await requestImage("A serene mountain landscape at sunset", {
|
|
74
|
+
* model: "image-01",
|
|
75
|
+
* aspect_ratio: "16:9",
|
|
76
|
+
* n: 2
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // Image-to-Image
|
|
81
|
+
* const result = await requestImage("A girl looking into the distance", {
|
|
82
|
+
* model: "image-01",
|
|
83
|
+
* subject_reference: [{
|
|
84
|
+
* type: "character",
|
|
85
|
+
* image_file: "https://example.com/portrait.jpg"
|
|
86
|
+
* }],
|
|
87
|
+
* n: 1
|
|
88
|
+
* });
|
|
89
|
+
*/
|
|
90
|
+
export function requestImage(prompt: string, options?: {
|
|
91
|
+
model?: string | undefined;
|
|
92
|
+
aspect_ratio?: string | undefined;
|
|
93
|
+
width?: number | undefined;
|
|
94
|
+
height?: number | undefined;
|
|
95
|
+
response_format?: string | undefined;
|
|
96
|
+
seed?: number | undefined;
|
|
97
|
+
n?: number | undefined;
|
|
98
|
+
prompt_optimizer?: boolean | undefined;
|
|
99
|
+
subject_reference?: Object[] | undefined;
|
|
100
|
+
extra?: Object | undefined;
|
|
101
|
+
}): Promise<{
|
|
102
|
+
image_urls: string[];
|
|
103
|
+
image_base64: string[];
|
|
104
|
+
local_paths: string[];
|
|
105
|
+
metadata: Object;
|
|
106
|
+
id: string;
|
|
107
|
+
duration: number;
|
|
108
|
+
raw: object;
|
|
109
|
+
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
export { music };
|
|
3
|
+
export { musicToolset };
|
|
4
|
+
export { video };
|
|
5
|
+
export { videoToolset };
|
|
6
|
+
export { image };
|
|
7
|
+
export { imageToolset };
|
|
8
|
+
}
|
|
9
|
+
export default _default;
|
|
10
|
+
import * as music from './music.js';
|
|
11
|
+
import musicToolset from './MusicToolset.js';
|
|
12
|
+
import * as video from './video.js';
|
|
13
|
+
import videoToolset from './VideoToolset.js';
|
|
14
|
+
import * as image from './image.js';
|
|
15
|
+
import imageToolset from './ImageToolset.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function getHeaders(): {
|
|
2
|
+
'Content-Type': string;
|
|
3
|
+
Authorization: string;
|
|
4
|
+
};
|
|
5
|
+
export function saveAudioToLocal(audioData: any, filenamePrefix?: string): Promise<any>;
|
|
6
|
+
export function createMusic(prompt: any, options?: {}): Promise<{
|
|
7
|
+
audio_url: any;
|
|
8
|
+
local_path: any;
|
|
9
|
+
duration: number;
|
|
10
|
+
raw: any;
|
|
11
|
+
}>;
|
|
12
|
+
export function changeMusic(prompt: any, options?: {}): Promise<{
|
|
13
|
+
audio_url: any;
|
|
14
|
+
local_path: any;
|
|
15
|
+
duration: number;
|
|
16
|
+
raw: any;
|
|
17
|
+
}>;
|
|
18
|
+
export function analyzeMusic(audioUrl: any, options?: {}): Promise<{
|
|
19
|
+
cover_feature_id: any;
|
|
20
|
+
formatted_lyrics: any;
|
|
21
|
+
structure_result: any;
|
|
22
|
+
audio_duration: any;
|
|
23
|
+
trace_id: any;
|
|
24
|
+
raw: any;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Generate complete song lyrics or edit/continue existing lyrics.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} prompt - Theme, style, or editing instruction.
|
|
30
|
+
* @param {Object} [options]
|
|
31
|
+
* @param {'write_full_song'|'edit'} [options.mode='write_full_song']
|
|
32
|
+
* @param {string} [options.lyrics] - Existing lyrics (required for edit mode)
|
|
33
|
+
* @param {string} [options.title]
|
|
34
|
+
*
|
|
35
|
+
* @returns {Promise<{song_title: string, style_tags: string, lyrics: string, raw: object}>}
|
|
36
|
+
*/
|
|
37
|
+
export function generateLyrics(prompt: string, options?: {
|
|
38
|
+
mode?: "write_full_song" | "edit" | undefined;
|
|
39
|
+
lyrics?: string | undefined;
|
|
40
|
+
title?: string | undefined;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
song_title: string;
|
|
43
|
+
style_tags: string;
|
|
44
|
+
lyrics: string;
|
|
45
|
+
raw: object;
|
|
46
|
+
}>;
|