@j-o-r/hello-dave 0.1.1 → 0.1.5

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.
Files changed (173) hide show
  1. package/CHANGELOG.md +42 -25
  2. package/README.md +81 -221
  3. package/TODO.md +173 -35
  4. package/agents/agent_creator.js +105 -0
  5. package/agents/agent_creator.prompt.md +371 -0
  6. package/agents/ask_agent.js +64 -127
  7. package/agents/claude_agent.js +68 -0
  8. package/agents/code_agent.js +55 -135
  9. package/agents/code_agent.prompt.md +50 -0
  10. package/agents/echo_agent.js +76 -0
  11. package/agents/financial_expert.js +75 -0
  12. package/agents/gpt_agent.js +52 -103
  13. package/agents/gpt_code.js +81 -0
  14. package/agents/grok_agent.js +58 -114
  15. package/agents/minimax_agent.js +92 -0
  16. package/agents/mureka_agent.js +77 -0
  17. package/agents/planner_agent.js +172 -0
  18. package/agents/stability_agent.js +87 -0
  19. package/agents/test_agent.js +75 -157
  20. package/agents/weather_agent.js +73 -0
  21. package/agents/workflow_agent.js +189 -0
  22. package/bin/dave.js +436 -184
  23. package/docs/bin-dave.md +85 -35
  24. package/docs/cdn-ssh.md +100 -0
  25. package/docs/creating-agents.md +301 -0
  26. package/docs/creating-toolsets.md +336 -0
  27. package/docs/docs-organization.md +48 -0
  28. package/docs/project-overview.md +86 -51
  29. package/lib/API/elevenlabs.io/music.compose.md +441 -0
  30. package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
  31. package/lib/API/elevenlabs.io/music.stream.md +425 -0
  32. package/lib/API/lalal.ai/lalal.js +445 -0
  33. package/lib/API/lalal.ai/openapi.json +2614 -0
  34. package/lib/API/minimax/ImageToolset.js +82 -37
  35. package/lib/API/minimax/MusicToolset.js +125 -79
  36. package/lib/API/minimax/VideoToolset.js +170 -167
  37. package/lib/API/minimax/image.js +5 -1
  38. package/lib/API/minimax/music.js +210 -23
  39. package/lib/API/minimax/video.js +242 -53
  40. package/lib/API/mureka/MusicToolset.js +646 -0
  41. package/lib/API/mureka/README.md +41 -0
  42. package/lib/API/mureka/index.js +7 -0
  43. package/lib/API/mureka/music.js +658 -0
  44. package/lib/API/openai.com/index.js +7 -0
  45. package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
  46. package/lib/API/openai.com/video.create.character.md +40 -0
  47. package/lib/API/openai.com/video.create.md +219 -0
  48. package/lib/API/openai.com/video.delete.md +44 -0
  49. package/lib/API/openai.com/video.download.md +31 -0
  50. package/lib/API/openai.com/video.edit.md +155 -0
  51. package/lib/API/openai.com/video.extend.md +166 -0
  52. package/lib/API/openai.com/video.fetch.character.md +43 -0
  53. package/lib/API/openai.com/video.js +784 -0
  54. package/lib/API/openai.com/video.list.md +201 -0
  55. package/lib/API/openai.com/video.remix.md +175 -0
  56. package/lib/API/openai.com/video.retrieve.md +139 -0
  57. package/lib/API/openai.com/videoToolset.js +616 -0
  58. package/lib/API/stability.ai/ImageToolset.js +131 -40
  59. package/lib/API/stability.ai/MusicToolset.js +79 -47
  60. package/lib/API/stability.ai/audio.js +63 -131
  61. package/lib/API/x.ai/chat.responses.md +1040 -0
  62. package/lib/API/x.ai/image.js +229 -59
  63. package/lib/API/x.ai/imageToolset.js +376 -0
  64. package/lib/API/x.ai/index.js +1 -1
  65. package/lib/API/x.ai/responses.js +9 -18
  66. package/lib/Agent.js +271 -0
  67. package/lib/Agent.js.old +284 -0
  68. package/lib/AgentLauncher.js +593 -0
  69. package/lib/Cli.js +87 -13
  70. package/lib/Prompt.js +23 -1
  71. package/lib/Session.js +5 -4
  72. package/lib/ToolSet.js +102 -6
  73. package/lib/agentLoader.js +369 -0
  74. package/lib/cdn.js +67 -231
  75. package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
  76. package/lib/defaultToolsets.js +43 -0
  77. package/lib/fafs.js +1 -1
  78. package/lib/genericToolset.js +442 -119
  79. package/lib/handOffToolset.js +179 -0
  80. package/lib/index.js +34 -27
  81. package/lib/toolsetLoader.js +248 -0
  82. package/package.json +10 -4
  83. package/types/API/lalal.ai/lalal.d.ts +116 -0
  84. package/types/API/minimax/image.d.ts +2 -1
  85. package/types/API/minimax/music.d.ts +189 -26
  86. package/types/API/minimax/video.d.ts +100 -31
  87. package/types/API/mureka/index.d.ts +7 -0
  88. package/types/API/mureka/music.d.ts +472 -0
  89. package/types/API/openai.com/index.d.ts +7 -0
  90. package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
  91. package/types/API/openai.com/video.d.ts +409 -0
  92. package/types/API/openai.com/videoToolset.d.ts +24 -0
  93. package/types/API/stability.ai/audio.d.ts +14 -103
  94. package/types/API/stability.ai/image.d.ts +2 -2
  95. package/types/API/x.ai/image.d.ts +138 -26
  96. package/types/API/x.ai/imageToolset.d.ts +3 -0
  97. package/types/API/x.ai/index.d.ts +1 -1
  98. package/types/API/x.ai/responses.d.ts +4 -4
  99. package/types/Agent.d.ts +123 -0
  100. package/types/AgentLauncher.d.ts +250 -0
  101. package/types/Cli.d.ts +28 -8
  102. package/types/Prompt.d.ts +23 -5
  103. package/types/Session.d.ts +1 -1
  104. package/types/ToolSet.d.ts +10 -0
  105. package/types/agentLoader.d.ts +78 -0
  106. package/types/cdn.d.ts +15 -90
  107. package/types/defaultToolsets.d.ts +9 -0
  108. package/types/fafs.d.ts +1 -1
  109. package/types/genericToolset.d.ts +1 -1
  110. package/types/handOffToolset.d.ts +28 -0
  111. package/types/index.d.ts +19 -17
  112. package/types/toolsetLoader.d.ts +114 -0
  113. package/utils/format_log.js +101 -23
  114. package/utils/launch_agent.js +18 -0
  115. package/utils/list_sessions.sh +13 -5
  116. package/utils/search_sessions.sh +65 -29
  117. package/utils/toolsets.js +33 -0
  118. package/README.md.bak.1779452127 +0 -240
  119. package/agents/codeserver.sh +0 -47
  120. package/agents/daisy_agent.js +0 -173
  121. package/agents/docs_agent.js +0 -148
  122. package/agents/memory_agent.js +0 -263
  123. package/agents/minimax.js +0 -173
  124. package/agents/npm_agent.js +0 -202
  125. package/agents/prompt_agent.js +0 -133
  126. package/agents/readme_agent.js +0 -148
  127. package/agents/spawn_agent.js +0 -160
  128. package/agents/stability.js +0 -173
  129. package/agents/todo_agent.js +0 -175
  130. package/bin/codeDave +0 -58
  131. package/docs/agent-dave-websocket-protocol.md +0 -180
  132. package/docs/agent-manager.md +0 -244
  133. package/docs/codeserver-pattern.md +0 -191
  134. package/docs/generic-toolset.md +0 -326
  135. package/docs/howtos/agent-networking.md +0 -253
  136. package/docs/howtos/spawn-agents.md.bak +0 -200
  137. package/docs/howtos/spawn-agents.md.bak_new +0 -200
  138. package/docs/multi-agent-clusters.md +0 -265
  139. package/docs/music-toolsets.md +0 -137
  140. package/docs/path-resolution-best-practices.md +0 -104
  141. package/docs/plans/minimax-music-generation.md +0 -80
  142. package/docs/plans/unified-agent-architecture.md +0 -146
  143. package/docs/plans/websocket-streaming-plan.md.bak +0 -317
  144. package/docs/prompt/spawn_agent.md +0 -175
  145. package/docs/prompt/spawn_agent.md.bak +0 -201
  146. package/docs/prompt/task_clarification_and_documentation.md +0 -35
  147. package/docs/prompt-class.md +0 -141
  148. package/docs/todo-archive-infra-2026-04-21.md +0 -15
  149. package/docs/todo-archive-v0.0.8.md +0 -1
  150. package/docs/todo-archive-v0.1.0.md +0 -32
  151. package/docs/todo-archive.md +0 -44
  152. package/docs/tools-syntax-validation.md +0 -121
  153. package/docs/toolset.md +0 -164
  154. package/docs/xai-responses.md +0 -111
  155. package/docs/xai_collections.md +0 -106
  156. package/lib/API/x.ai/ImageToolset.js +0 -165
  157. package/lib/API/x.ai/text.js +0 -415
  158. package/lib/AgentClient.js +0 -248
  159. package/lib/AgentManager.js +0 -245
  160. package/lib/AgentServer.js +0 -404
  161. package/lib/wsCli.js +0 -287
  162. package/lib/wsIO.js +0 -90
  163. package/types/API/x.ai/text.d.ts +0 -286
  164. package/types/AgentClient.d.ts +0 -109
  165. package/types/AgentManager.d.ts +0 -100
  166. package/types/AgentServer.d.ts +0 -89
  167. package/types/wsCli.d.ts +0 -17
  168. package/types/wsIO.d.ts +0 -30
  169. package/utils/test.sh +0 -46
  170. /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
  171. /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
  172. /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
  173. /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
package/lib/cdn.js CHANGED
@@ -6,10 +6,8 @@
6
6
  * Designed to be used by any toolset or agent (music, images, documents, etc.).
7
7
  *
8
8
  * Core capabilities:
9
- * - Publish single or multiple files into organized project folders
10
- * - Automatically create remote directory structures
11
- * - List all existing projects on the remote server
12
- * - Delete entire projects when no longer needed
9
+ * - Publish single or multiple files into public temp folders
10
+ * - Unpublish previously published files from public temp folders
13
11
  *
14
12
  * All public URLs are built dynamically from the SSH host (assumes a web server
15
13
  * is serving the `htdocs` directory publicly).
@@ -17,25 +15,6 @@
17
15
  * @requires SSH_EP environment variable (format: ssh://user@host:port)
18
16
  * @requires Remote host must have a web server serving the 'htdocs' folder
19
17
  *
20
- * @example
21
- * import cdn from './cdn.js';
22
- *
23
- * // Publish multiple files into one project
24
- * const result = await cdn.publishToProject(
25
- * ['/tmp/audio.mp3', '/tmp/cover.jpg'],
26
- * 'my-song-cover-2026',
27
- * {
28
- * description: 'Cover of original song with new lyrics',
29
- * plan: 'Two-step cover using Minimax voice features'
30
- * }
31
- * );
32
- * console.log(result.public_urls);
33
- *
34
- * // List all projects
35
- * const projects = await cdn.listProjects();
36
- *
37
- * // Delete a project
38
- * await cdn.deleteProject('my-song-cover-2026');
39
18
  */
40
19
 
41
20
  import { SH } from '@j-o-r/sh';
@@ -53,7 +32,7 @@ import path from 'node:path';
53
32
  *
54
33
  * @example
55
34
  * // Required environment variable:
56
- * // export SSH_EP='ssh://jorrit@drive.duin.xyz:4301'
35
+ * // export SSH_EP='ssh://user@your-cdn.example.com:4301'
57
36
  */
58
37
  function getSshConfig() {
59
38
  if (!process.env.SSH_EP) {
@@ -92,6 +71,31 @@ function getSshConfig() {
92
71
  return { user, host, port, raw: ep };
93
72
  }
94
73
 
74
+ /**
75
+ * Validate a CDN-relative path and normalize it for remote use.
76
+ *
77
+ * Paths are always relative to `htdocs/aaztmp/`. Absolute paths and parent
78
+ * directory traversal are rejected so delete operations cannot escape the CDN
79
+ * temp folder.
80
+ *
81
+ * @param {string} remoteRelativePath - Relative path under htdocs/aaztmp.
82
+ * @returns {string} Safe normalized POSIX-style relative path.
83
+ * @throws {Error} If the path is empty, absolute, or escapes the CDN temp root.
84
+ */
85
+ function normalizeRemoteRelativePath(remoteRelativePath) {
86
+ if (typeof remoteRelativePath !== 'string' || !remoteRelativePath.trim()) {
87
+ throw new Error('remoteRelativePath must be a non-empty string');
88
+ }
89
+
90
+ const normalized = path.posix.normalize(remoteRelativePath.replaceAll('\\', '/'));
91
+
92
+ if (normalized === '.' || normalized.startsWith('../') || normalized === '..' || path.posix.isAbsolute(normalized)) {
93
+ throw new Error(`Invalid remoteRelativePath: "${remoteRelativePath}"`);
94
+ }
95
+
96
+ return normalized;
97
+ }
98
+
95
99
  /**
96
100
  * Ensure a remote directory exists under `htdocs/`.
97
101
  * Creates all parent directories as needed (`mkdir -p`).
@@ -101,7 +105,7 @@ function getSshConfig() {
101
105
  */
102
106
  async function ensureRemoteDir(remoteRelativeDir) {
103
107
  const ssh = getSshConfig();
104
- const remoteDir = `htdocs/${remoteRelativeDir}`;
108
+ const remoteDir = `htdocs/aaztmp/${remoteRelativeDir}`;
105
109
 
106
110
  console.log(`[cdn] Ensuring remote directory: ${remoteDir}`);
107
111
  await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "mkdir -p ${remoteDir}"`.run();
@@ -125,198 +129,8 @@ function getOutput(result) {
125
129
  }
126
130
 
127
131
  /**
128
- * List all existing projects on the remote server.
129
- *
130
- * Scans the `htdocs/projects/` directory and returns metadata for each project.
131
- * Useful when starting without prior context about what has already been published.
132
- *
133
- * @returns {Promise<Array<{slug: string, url: string, fileCount: number, created?: string}>>}
134
- * Array of project objects.
135
- *
136
- * @throws {Error} If the SSH connection or directory listing fails.
137
- *
138
- * @example
139
- * const projects = await cdn.listProjects();
140
- * console.log(projects[0].url); // https://drive.duin.xyz/projects/my-project/
141
- */
142
- async function listProjects() {
143
- const ssh = getSshConfig();
144
- const projectsBase = 'htdocs/projects';
145
-
146
- console.log(`[cdn] Listing projects from ${ssh.host}...`);
147
-
148
- try {
149
- const lsResult = await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "ls -1 ${projectsBase} 2>/dev/null || echo ''"`.run();
150
- const output = getOutput(lsResult);
151
- const slugs = output.trim().split('\n').filter(Boolean);
152
-
153
- const projects = [];
154
-
155
- for (const slug of slugs) {
156
- const projectDir = `${projectsBase}/${slug}`;
157
-
158
- const countResult = await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "ls -1 ${projectDir} 2>/dev/null | wc -l"`.run();
159
- const countOutput = getOutput(countResult);
160
- const fileCount = parseInt(countOutput.trim(), 10) || 0;
161
-
162
- let created;
163
- try {
164
- const metaResult = await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "cat ${projectDir}/meta.json 2>/dev/null || echo '{}'"`.run();
165
- const metaOutput = getOutput(metaResult);
166
- const meta = JSON.parse(metaOutput.trim());
167
- created = meta.created;
168
- } catch (_) {
169
- // meta.json may not exist yet
170
- }
171
-
172
- projects.push({
173
- slug,
174
- url: `https://${ssh.host}/projects/${slug}/`,
175
- fileCount,
176
- created
177
- });
178
- }
179
-
180
- console.log(`[cdn] Found ${projects.length} project(s)`);
181
- return projects;
182
- } catch (err) {
183
- throw new Error(`Failed to list projects: ${err.message}`);
184
- }
185
- }
186
-
187
- /**
188
- * Delete an entire project folder from the remote server.
189
- *
190
- * Permanently removes `htdocs/projects/<slug>` and all its contents.
191
- * Use with caution — this operation cannot be undone.
192
- *
193
- * @param {string} projectSlug - Project identifier (will be sanitized to kebab-case)
194
- * @returns {Promise<{deleted: boolean, project: string}>}
195
- *
196
- * @example
197
- * await cdn.deleteProject('my-old-project');
198
- */
199
- async function deleteProject(projectSlug) {
200
- const ssh = getSshConfig();
201
- const slug = projectSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
202
- const remoteDir = `htdocs/projects/${slug}`;
203
-
204
- console.log(`[cdn] Deleting project: ${remoteDir}`);
205
- await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "rm -rf ${remoteDir}"`.run();
206
-
207
- console.log(`[cdn] ✅ Project deleted: ${slug}`);
208
- return { deleted: true, project: slug };
209
- }
210
-
211
- /**
212
- * Ensure the remote project directory structure exists.
213
- * Creates `htdocs/projects/<slug>` (and the `generated` subfolder).
214
- *
215
- * @param {string} projectSlug - Project identifier (will be sanitized)
216
- * @returns {Promise<string>} The remote directory path under htdocs
217
- */
218
- async function ensureProjectStructure(projectSlug) {
219
- const slug = projectSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
220
- const remoteRelative = `projects/${slug}`;
221
- return await ensureRemoteDir(remoteRelative);
222
- }
223
-
224
- /**
225
- * Internal helper to upload a single file via SCP.
226
- *
227
- * @param {string} localPath - Local file path
228
- * @param {string} remoteFilePath - Full remote path (including htdocs)
229
- * @param {object} ssh - SSH config object from getSshConfig()
230
- */
231
- async function uploadSingleFile(localPath, remoteFilePath, ssh) {
232
- const scpTarget = `${ssh.user}@${ssh.host}:${remoteFilePath}`;
233
- console.log(`[cdn] Uploading: ${path.basename(localPath)} → ${remoteFilePath}`);
234
- await SH`scp -P ${ssh.port} ${localPath} ${scpTarget}`.run();
235
- }
236
-
237
- /**
238
- * Publish one or more local files into an organized project folder on the public CDN.
239
- *
240
- * Supports both single file (string) and multiple files (array).
241
- * Automatically creates the project directory and generates `meta.json`.
242
- * Optional `description.md` and `plan.md` files can be created.
243
- *
244
- * @param {string|string[]} localPaths - Single path or array of local file paths
245
- * @param {string} projectSlug - Project identifier (sanitized to kebab-case)
246
- * @param {Object} [options]
247
- * @param {string} [options.description] - Human-readable project description
248
- * @param {string} [options.plan] - Planning notes or step-by-step evolution
249
- * @param {string} [options.filename] - Custom filename (only used when uploading a single file)
250
- *
251
- * @returns {Promise<{public_urls: string[], project_folder: string, meta: object}>}
252
- *
253
- * @example
254
- * // Multiple files
255
- * const result = await cdn.publishToProject(
256
- * ['audio.mp3', 'cover.jpg'],
257
- * 'my-project',
258
- * { description: 'Music cover project' }
259
- * );
260
- *
261
- * // Single file with custom name
262
- * await cdn.publishToProject('/tmp/file.mp3', 'my-project', { filename: 'final.mp3' });
263
- */
264
- async function publishToProject(localPaths, projectSlug, options = {}) {
265
- const ssh = getSshConfig();
266
- const slug = projectSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
267
- const remoteDir = await ensureProjectStructure(slug);
268
-
269
- const paths = Array.isArray(localPaths) ? localPaths : [localPaths];
270
- const uploadedFiles = [];
271
-
272
- for (const localPath of paths) {
273
- const filename = options.filename && paths.length === 1
274
- ? options.filename
275
- : path.basename(localPath);
276
-
277
- const remoteFile = `${remoteDir}/${filename}`;
278
- await uploadSingleFile(localPath, remoteFile, ssh);
279
- uploadedFiles.push(filename);
280
- }
281
-
282
- const meta = {
283
- project: slug,
284
- created: new Date().toISOString(),
285
- files: uploadedFiles,
286
- source: 'cdn.js',
287
- ssh_ep: ssh.raw
288
- };
289
-
290
- console.log(`[cdn] Writing meta.json`);
291
- await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "cat > ${remoteDir}/meta.json << 'EOF'
292
- ${JSON.stringify(meta, null, 2)}
293
- EOF"`.run();
294
-
295
- if (options.description) {
296
- await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "cat > ${remoteDir}/description.md << 'EOF'
297
- ${options.description}
298
- EOF"`.run();
299
- }
300
-
301
- if (options.plan) {
302
- await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} "cat > ${remoteDir}/plan.md << 'EOF'
303
- ${options.plan}
304
- EOF"`.run();
305
- }
306
-
307
- const publicUrls = uploadedFiles.map(f => `https://${ssh.host}/projects/${slug}/${f}`);
308
- const projectFolder = `https://${ssh.host}/projects/${slug}/`;
309
-
310
- return {
311
- public_urls: publicUrls,
312
- project_folder: projectFolder,
313
- meta
314
- };
315
- }
316
-
317
- /**
318
- * Publish a single local file to an arbitrary location on the public CDN.
319
- *
132
+ * Publish a single local file to an arbitrary temp location on the public CDN.
133
+ * this folder is cleared regulary.
320
134
  * Automatically creates any missing parent directories.
321
135
  * Returns a direct public HTTPS URL.
322
136
  *
@@ -334,40 +148,62 @@ EOF"`.run();
334
148
  */
335
149
  async function publishFile(localPath, remoteRelativePath) {
336
150
  const ssh = getSshConfig();
337
- const remoteDir = path.dirname(remoteRelativePath);
151
+ const safeRemoteRelativePath = normalizeRemoteRelativePath(remoteRelativePath);
152
+ const remoteDir = path.dirname(safeRemoteRelativePath);
338
153
 
339
154
  if (remoteDir && remoteDir !== '.') {
340
155
  await ensureRemoteDir(remoteDir);
341
156
  }
342
157
 
343
- const remoteTarget = `${ssh.user}@${ssh.host}:htdocs/${remoteRelativePath}`;
158
+ const remoteTarget = `${ssh.user}@${ssh.host}:htdocs/aaztmp/${safeRemoteRelativePath}`;
344
159
 
345
160
  console.log(`[cdn] Uploading via SCP: ${localPath} → ${remoteTarget}`);
346
161
  await SH`scp -P ${ssh.port} ${localPath} ${remoteTarget}`.run();
347
162
  console.log(`[cdn] ✅ Upload successful`);
348
163
 
349
164
  return {
350
- public_url: `https://${ssh.host}/${remoteRelativePath}`,
165
+ public_url: `https://${ssh.host}/aaztmp/${safeRemoteRelativePath}`,
351
166
  remote_path: remoteTarget
352
167
  };
353
168
  }
354
169
 
170
+ /**
171
+ * Delete a previously published file from the public CDN temp folder.
172
+ *
173
+ * The target path is resolved relative to `htdocs/aaztmp/`. Missing files are
174
+ * treated as already unpublished because `rm -f` is used.
175
+ *
176
+ * @param {string} remoteRelativePath - File path under htdocs/aaztmp (e.g. "tmp/quick-reference.mp3")
177
+ * @returns {Promise<{public_url: string, remote_path: string, unpublished: boolean}>}
178
+ *
179
+ * @example
180
+ * const result = await cdn.unpublishFile('tmp/quick-reference.mp3');
181
+ * console.log(result.unpublished);
182
+ */
183
+ async function unpublishFile(remoteRelativePath) {
184
+ const ssh = getSshConfig();
185
+ const safeRemoteRelativePath = normalizeRemoteRelativePath(remoteRelativePath);
186
+ const remoteFile = `htdocs/aaztmp/${safeRemoteRelativePath}`;
187
+
188
+ console.log(`[cdn] Deleting remote file: ${remoteFile}`);
189
+ await SH`ssh -p ${ssh.port} ${ssh.user}@${ssh.host} rm -f ${remoteFile}`.run();
190
+ console.log('[cdn] ✅ Delete successful');
191
+
192
+ return {
193
+ public_url: `https://${ssh.host}/aaztmp/${safeRemoteRelativePath}`,
194
+ remote_path: `${ssh.user}@${ssh.host}:${remoteFile}`,
195
+ unpublished: true
196
+ };
197
+ }
198
+
355
199
  export {
356
200
  getSshConfig,
357
- ensureRemoteDir,
358
- ensureProjectStructure,
359
- listProjects,
360
- deleteProject,
361
201
  publishFile,
362
- publishToProject
202
+ unpublishFile,
363
203
  };
364
204
 
365
205
  export default {
366
206
  getSshConfig,
367
- ensureRemoteDir,
368
- ensureProjectStructure,
369
- listProjects,
370
- deleteProject,
371
207
  publishFile,
372
- publishToProject
373
- };
208
+ unpublishFile,
209
+ };
@@ -21,6 +21,17 @@ import cdn from './cdn.js';
21
21
 
22
22
  const tools = new ToolSet('auto');
23
23
 
24
+ /**
25
+ * Stringify a structured tool response with stable formatting.
26
+ * Structured CDN responses preserve exact public URLs and remote paths so the
27
+ * assistant can repeat them in normal conversation before raw tool-call history
28
+ * is pruned.
29
+ *
30
+ * @param {Record<string, *>} payload - JSON-serializable tool response payload.
31
+ * @returns {string} Formatted JSON function response.
32
+ */
33
+ const json = (payload) => JSON.stringify(payload, null, 2);
34
+
24
35
  /**
25
36
  * Single source of truth for cleaning over-escaped strings from LLMs.
26
37
  * (Duplicated here for self-containment of the CDN toolset)
@@ -89,32 +100,21 @@ const guessOverEscaping = (s) => {
89
100
  * to a remote web server defined by the SSH_EP environment variable).
90
101
  *
91
102
  * Logical workflow for LLMs:
92
- * 1. listProjects() Discover what already exists
93
- * 2. publishFile() Quick single-file exposure
94
- * 3. publishToProject() → Organize one or multiple files into a project
95
- * 4. deleteProject() → Clean up when no longer needed
103
+ * 1. publishFile() Quick single-file exposure
104
+ * 2. unpublishFile() Delete a previously published file
96
105
  *
97
106
  * Requirements:
98
107
  * - SSH_EP environment variable must be set
99
108
  * - Remote host serves the 'htdocs' folder publicly over HTTPS
100
109
  */
101
110
 
102
- tools.add(
103
- 'list_projects',
104
- 'List all existing projects published on the remote public server.\n' +
105
- 'Returns slug, public URL, file count, and creation date for each project.\n' +
106
- 'Use this first when you have no prior context about what has already been published.',
107
- { type: 'object', properties: {} },
108
- async () => {
109
- return await cdn.listProjects();
110
- }
111
- );
112
111
 
113
112
  tools.add(
114
113
  'publish_file',
115
- 'Publish a single local file to a public HTTPS URL via SSH.\n' +
114
+ 'Publish a single local file to a public temp folder HTTPS URL via SSH.\n' +
116
115
  'Automatically creates any missing parent directories.\n' +
117
- 'Best for quick one-off references or when you only need a direct link to one file.',
116
+ 'Best for quick one-off references or when you only need a direct link to one file.\n' +
117
+ 'Returns structured JSON with public_url and remote_path. After success, include the exact public_url in your assistant response so it remains available after old tool calls are pruned.',
118
118
  {
119
119
  type: 'object',
120
120
  properties: {
@@ -126,66 +126,49 @@ tools.add(
126
126
  async (params) => {
127
127
  let local = guessOverEscaping(params.localPath).trim();
128
128
  let remote = guessOverEscaping(params.remoteRelativePath).trim();
129
- return await cdn.publishFile(local, remote);
129
+ const result = await cdn.publishFile(local, remote);
130
+ return json({
131
+ tool: 'publish_file',
132
+ success: true,
133
+ localPath: local,
134
+ remoteRelativePath: remote,
135
+ public_url: result.public_url,
136
+ publicUrl: result.public_url,
137
+ remote_path: result.remote_path,
138
+ note: 'Assistant: tell the user the exact public_url. Use public_url as the durable reference for later image_url/audio_url/file URL inputs.'
139
+ });
130
140
  }
131
141
  );
132
142
 
133
- tools.add(
134
- 'publish_to_project',
135
- 'Publish one or multiple local files into a structured public project folder.\n' +
136
- 'Creates the project directory, uploads all files, and generates meta.json.\n' +
137
- 'Optional description.md and plan.md can be added for context.\n' +
138
- 'Returns an array of public URLs plus the project folder URL.\n' +
139
- 'Recommended when you want to keep related files together for remote AI inspection.',
140
- {
141
- type: 'object',
142
- properties: {
143
- localPaths: {
144
- oneOf: [
145
- { type: 'string', description: 'Single file path' },
146
- { type: 'array', items: { type: 'string' }, description: 'Array of file paths' }
147
- ]
148
- },
149
- projectSlug: { type: 'string', description: 'Project folder name (will be sanitized to kebab-case)' },
150
- options: {
151
- type: 'object',
152
- properties: {
153
- description: { type: 'string', description: 'Human-readable description of the project' },
154
- plan: { type: 'string', description: 'Planning notes or step-by-step evolution' },
155
- filename: { type: 'string', description: 'Custom filename (only used for single-file uploads)' }
156
- }
157
- }
158
- },
159
- required: ['localPaths', 'projectSlug']
160
- },
161
- async (params) => {
162
- let localPaths = guessOverEscaping(params.localPaths);
163
- if (typeof localPaths === 'string') localPaths = [localPaths];
164
-
165
- let projectSlug = guessOverEscaping(params.projectSlug).trim();
166
- let options = params.options || {};
167
- if (typeof options === 'string') options = JSON.parse(guessOverEscaping(options));
168
-
169
- return await cdn.publishToProject(localPaths, projectSlug, options);
170
- }
171
- );
172
143
 
173
144
  tools.add(
174
- 'delete_project',
175
- 'Permanently delete an entire project folder from the remote public server.\n' +
176
- 'Removes the project and all its files.\n' +
177
- 'Use this to clean up temporary or obsolete projects.',
145
+ 'unpublish_file',
146
+ 'Delete a previously published file from the public temp folder via SSH.\n' +
147
+ 'The path is relative to htdocs/aaztmp and must match the remoteRelativePath used for publish_file.\n' +
148
+ 'Missing files are treated as already unpublished.\n' +
149
+ 'Returns structured JSON with public_url, remote_path, and unpublished status. After success, tell the user the exact public_url that was removed.',
178
150
  {
179
151
  type: 'object',
180
152
  properties: {
181
- projectSlug: { type: 'string', description: 'Project slug to delete' }
153
+ remoteRelativePath: { type: 'string', description: 'File path under htdocs/aaztmp to delete (e.g. "tmp/reference.mp3" or "projects/my-project/audio.wav")' }
182
154
  },
183
- required: ['projectSlug']
155
+ required: ['remoteRelativePath']
184
156
  },
185
157
  async (params) => {
186
- let slug = guessOverEscaping(params.projectSlug).trim();
187
- return await cdn.deleteProject(slug);
158
+ let remote = guessOverEscaping(params.remoteRelativePath).trim();
159
+ const result = await cdn.unpublishFile(remote);
160
+ return json({
161
+ tool: 'unpublish_file',
162
+ success: true,
163
+ remoteRelativePath: remote,
164
+ public_url: result.public_url,
165
+ publicUrl: result.public_url,
166
+ remote_path: result.remote_path,
167
+ unpublished: result.unpublished,
168
+ note: 'Assistant: tell the user the exact public_url that was removed and do not use it as a durable reference anymore.'
169
+ });
188
170
  }
189
171
  );
190
172
 
173
+
191
174
  export default tools;
@@ -0,0 +1,43 @@
1
+ import minimax from './API/minimax/index.js';
2
+ import mureka from './API/mureka/index.js';
3
+ import stability from './API/stability.ai/index.js';
4
+ import xaitools from './API/x.ai/index.js';
5
+ import openaitools from './API/openai.com/index.js';
6
+ import genTools from './genericToolset.js';
7
+ import cdnTools from './cdnToolset.js';
8
+ import handoffTools, { createHandOffToolset } from './handOffToolset.js';
9
+
10
+ /**
11
+ * Default framework-owned toolset tree exposed as API.toolset.
12
+ *
13
+ * @type {Record<string, Record<string, any>>}
14
+ */
15
+ const defaultToolsets = {
16
+ minimax: {
17
+ music: minimax.musicToolset,
18
+ image: minimax.imageToolset,
19
+ video: minimax.videoToolset
20
+ },
21
+ stability: {
22
+ image: stability.imageToolset,
23
+ music: stability.musicToolset
24
+ },
25
+ mureka: {
26
+ music: mureka.musicToolset
27
+ },
28
+ xai: {
29
+ image: xaitools.imageToolset
30
+ },
31
+ openai: {
32
+ video: openaitools.videoToolset
33
+ },
34
+ generic: {
35
+ bash: genTools,
36
+ cdn: cdnTools,
37
+ handoff: handoffTools,
38
+ createHandoff: createHandOffToolset
39
+ }
40
+ };
41
+
42
+ export { createHandOffToolset };
43
+ export default defaultToolsets;
package/lib/fafs.js CHANGED
@@ -79,7 +79,7 @@ async function systemInfo() {
79
79
 
80
80
  /**
81
81
  * Gathers comprehensive environment information: user name, fresh system details, IP-based geolocation,
82
- * and normalized current working directory. Results are cached in ~/.cache/hello-dave/env for 31 days,
82
+ * and normalized current working directory. Results are cached in ~/.cache/@j-o-r/hello-dave/env for 31 days,
83
83
  * refreshed on expiry. On cache hit, updates with fresh system info and cwd.
84
84
  *
85
85
  * @returns {Promise<EnvironmentInfo>} The environment information object.