@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
@@ -0,0 +1,179 @@
1
+ import ToolSet from './ToolSet.js';
2
+ import { listAgents as defaultListAgents } from './agentLoader.js';
3
+
4
+ /**
5
+ * @typedef {object} HandOffToolsetOptions
6
+ * @property {() => Promise<Array<{name: string, path: string, desc: string}>>} [listAgents]
7
+ */
8
+
9
+ /** @type {() => Promise<Array<{name: string, path: string, desc: string}>>} */
10
+ let defaultListAgentsProvider = defaultListAgents;
11
+
12
+ /**
13
+ * Configure the singleton handoff toolset used by API.toolset.generic.handoff.
14
+ *
15
+ * This keeps existing agent code working while allowing AgentLauncher instances
16
+ * created with `{ from: import.meta.url }` to make `list_agents` use the same
17
+ * project-aware loader as handoff loading.
18
+ *
19
+ * @param {HandOffToolsetOptions} options - Handoff toolset options.
20
+ * @returns {void}
21
+ */
22
+ export function configureDefaultHandOffToolset(options = {}) {
23
+ if (typeof options.listAgents === 'function') {
24
+ defaultListAgentsProvider = options.listAgents;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Serialize a tool response as pretty JSON for function-call output.
30
+ *
31
+ * @param {Record<string, unknown>} payload - JSON-serializable response payload.
32
+ * @returns {string} Pretty JSON response.
33
+ */
34
+ function json(payload) {
35
+ return JSON.stringify(payload, null, 2);
36
+ }
37
+
38
+ /**
39
+ * Build the fallback response for handoff tools if ToolSet does not short-circuit execution.
40
+ *
41
+ * @param {'hand_over'|'load_agent'} tool - Handoff tool name.
42
+ * @param {{agent: string, context: string}} params - Handoff tool parameters.
43
+ * @returns {string} Structured JSON response.
44
+ */
45
+ function handoffPreparedResponse(tool, params) {
46
+ return json({
47
+ tool,
48
+ success: true,
49
+ status: 'handoff prepared',
50
+ agent: params.agent,
51
+ note: 'Assistant: stop after calling this tool. The launcher handles the actual handoff and context wrapping.'
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Determine whether a discovered loader entry is a valid handoff agent.
57
+ *
58
+ * @param {Record<string, unknown>} candidate - Candidate agent loader entry.
59
+ * @returns {boolean} True when the candidate has a name and non-empty description.
60
+ */
61
+ function isListedAgent(candidate) {
62
+ return typeof candidate.name === 'string'
63
+ && candidate.name.trim().length > 0
64
+ && typeof candidate.desc === 'string'
65
+ && candidate.desc.trim().length > 0;
66
+ }
67
+
68
+ /**
69
+ * Normalize a valid agent loader entry for compact JSON output.
70
+ *
71
+ * @param {{name: string, desc: string}} agent - Valid discovered agent entry.
72
+ * @returns {{name: string, desc: string}} Normalized agent summary.
73
+ */
74
+ function summarizeAgent(agent) {
75
+ const desc = agent.desc.trim().replace(/\s+/g, ' ');
76
+ return {
77
+ name: agent.name,
78
+ desc: desc.length > 140 ? `${desc.slice(0, 137)}...` : desc
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Create a handoff toolset using the provided agent discovery function.
84
+ *
85
+ * @param {HandOffToolsetOptions} [options={}] - Toolset options.
86
+ * @returns {ToolSet} Configured ToolSet instance.
87
+ */
88
+ export function createHandOffToolset(options = {}) {
89
+ const listAgents = typeof options.listAgents === 'function'
90
+ ? options.listAgents
91
+ : defaultListAgents;
92
+
93
+ const handoffTools = new ToolSet('auto');
94
+
95
+ const handOverSchema = {
96
+ type: 'object',
97
+ description: `Use this tool EXACTLY ONCE to hand the conversation over to a MORE SPECIALIZED agent, or to the SAME agent for a deliberate fresh start / session reset.
98
+
99
+ This is the official in-process handoff mechanism (also callable as "load_agent").
100
+
101
+ CRITICAL RULES:
102
+ - Use this only on a clear domain/topic shift or deliberate fresh-start reset.
103
+ - Never use it for meta questions, capability questions, "reload yourself", "what tools do you have", or generic restart requests.
104
+ - The receiving agent starts fresh; no history is transferred.
105
+ - Provide a short, clean, task-focused context of 1-3 sentences.
106
+ - After calling this tool, stop; the launcher performs the handoff.`,
107
+ properties: {
108
+ agent: {
109
+ type: 'string',
110
+ pattern: '^[a-z0-9_]{2,64}$',
111
+ description: 'REQUIRED. Exact target agent name from list_agents, e.g. weather_agent, financial_expert, code_reviewer.'
112
+ },
113
+ context: {
114
+ type: 'string',
115
+ description: 'REQUIRED. Short task-focused context for the new or reset agent. Start directly with the current user question/task.'
116
+ }
117
+ },
118
+ required: ['agent', 'context'],
119
+ additionalProperties: false,
120
+ examples: [
121
+ {
122
+ agent: 'financial_expert',
123
+ context: 'What is the current price of $SPCX stock? Provide latest price, market context, and investment considerations.'
124
+ },
125
+ {
126
+ agent: 'weather_agent',
127
+ context: 'Give a detailed 7-day weather forecast for Miami including temperature, rain chance, and wind.'
128
+ },
129
+ {
130
+ agent: 'code_agent',
131
+ context: 'Continue implementing JWT authentication. Focus only on middleware and token validation.'
132
+ }
133
+ ]
134
+ };
135
+
136
+ handoffTools.add(
137
+ 'hand_over',
138
+ 'Hand over to a more specialized agent OR reset the current agent with a fresh focused context. Call EXACTLY ONCE on clear domain shift or intentional fresh-start. Never for meta/reload/capability questions.',
139
+ handOverSchema,
140
+ async (params) => handoffPreparedResponse('hand_over', params)
141
+ );
142
+
143
+ handoffTools.add(
144
+ 'load_agent',
145
+ 'Alias for hand_over. Hand over to a more specialized agent OR reset the current agent with a fresh focused context. Never for meta/reload/capability questions.',
146
+ handOverSchema,
147
+ async (params) => handoffPreparedResponse('load_agent', params)
148
+ );
149
+
150
+ handoffTools.add(
151
+ 'list_agents',
152
+ 'List all discoverable agents that can be used with hand_over / load_agent. Returns structured JSON with exact agent names and short descriptions.',
153
+ {
154
+ type: 'object',
155
+ properties: {},
156
+ additionalProperties: false
157
+ },
158
+ async () => {
159
+ const list = await listAgents();
160
+ const agents = list.filter(isListedAgent).map(summarizeAgent);
161
+
162
+ return json({
163
+ tool: 'list_agents',
164
+ success: true,
165
+ count: agents.length,
166
+ agents,
167
+ note: 'Assistant: preserve exact agent names when suggesting or calling hand_over/load_agent.'
168
+ });
169
+ }
170
+ );
171
+
172
+ return handoffTools;
173
+ }
174
+
175
+ const handoffTools = createHandOffToolset({
176
+ listAgents: () => defaultListAgentsProvider()
177
+ });
178
+
179
+ export default handoffTools;
package/lib/index.js CHANGED
@@ -1,38 +1,40 @@
1
- import {request as gpt} from './API/openai.com/reponses/text.js';
2
- import {request as xai} from './API/x.ai/responses.js';
1
+ import {request as gpt} from './API/openai.com/responses.js';
2
+ import {request as grok} 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
- import minimax from './API/minimax/index.js';
6
- import stability from './API/stability.ai/index.js';
7
- import xaitools from './API/x.ai/index.js';
8
5
  import { env, GLOBAL } from './fafs.js';
9
6
  import ToolSet from './ToolSet.js';
10
- import AgentServer from './AgentServer.js';
11
- import AgentManager from './AgentManager.js';
12
- import AgentClient from './AgentClient.js';
7
+ import Agent from './Agent.js';
8
+ import AgentLauncher from './AgentLauncher.js';
13
9
  import Session from './Session.js';
14
10
  import Prompt from './Prompt.js';
15
11
  import Cli from './Cli.js';
16
- import wsCli from '../lib/wsCli.js';
17
- import wsIO from '../lib/wsIO.js';
12
+ import defaultToolsets, { createHandOffToolset } from './defaultToolsets.js';
13
+ import { createAgentLoader, findNearestPackageRoot, getAgentDirs } from './agentLoader.js';
14
+ import {
15
+ listToolsets,
16
+ matchesToolsetFilter,
17
+ parseToolsetArgs,
18
+ renderToolsetList,
19
+ renderToolsetsHelp,
20
+ shortDescription
21
+ } from './toolsetLoader.js';
18
22
 
19
23
  const API = {
20
- text:{
21
- gpt,
22
- xai,
23
- claude
24
+ chat: {
25
+ gpt,
26
+ grok,
27
+ claude
24
28
  },
25
29
  search: {
26
- brave
30
+ brave
27
31
  },
28
- minimax,
29
- stability,
30
- xaitools
31
- }
32
+ toolset: defaultToolsets
33
+ };
34
+
32
35
  export {
33
- AgentManager,
34
- AgentServer,
35
- AgentClient,
36
+ Agent,
37
+ AgentLauncher,
36
38
  Prompt,
37
39
  ToolSet,
38
40
  Session,
@@ -40,9 +42,14 @@ export {
40
42
  Cli,
41
43
  env,
42
44
  GLOBAL,
43
- wsCli,
44
- wsIO
45
+ createAgentLoader,
46
+ createHandOffToolset,
47
+ findNearestPackageRoot,
48
+ getAgentDirs,
49
+ listToolsets,
50
+ matchesToolsetFilter,
51
+ parseToolsetArgs,
52
+ renderToolsetList,
53
+ renderToolsetsHelp,
54
+ shortDescription
45
55
  };
46
-
47
- // Export the dedicated CDN toolset
48
- export { default as CdnToolset } from './CdnToolset.js';
@@ -0,0 +1,248 @@
1
+ import defaultToolsets from './defaultToolsets.js';
2
+
3
+ /**
4
+ * @typedef {object} ListedTool
5
+ * @property {string} name - Tool name.
6
+ * @property {string} description - Shortened tool description.
7
+ */
8
+
9
+ /**
10
+ * @typedef {object} ListedToolset
11
+ * @property {string} name - Display name, for example "minimax/music".
12
+ * @property {number} toolCount - Number of tools in this toolset.
13
+ * @property {ListedTool[]} tools - Listed tool metadata.
14
+ * @property {string} [note] - Optional note when the value is not a ToolSet instance.
15
+ */
16
+
17
+ /**
18
+ * @typedef {object} ToolsetListResult
19
+ * @property {string} reference - Public import reference.
20
+ * @property {ListedToolset[]} toolsets - Matching toolsets.
21
+ * @property {number} totalToolsets - Number of matching toolsets.
22
+ * @property {number} totalTools - Number of matching tools.
23
+ */
24
+
25
+ /**
26
+ * @typedef {object} ToolsetListOptions
27
+ * @property {string[]} [filters] - Optional provider/type/name filters.
28
+ * @property {Record<string, Record<string, any>>} [toolsets] - Optional toolset tree. Defaults to framework toolsets.
29
+ */
30
+
31
+ /**
32
+ * @typedef {object} ParsedToolsetArgs
33
+ * @property {string[]} filters - Positional filters.
34
+ * @property {boolean} json - Whether JSON output was requested.
35
+ * @property {boolean} help - Whether help output was requested.
36
+ */
37
+
38
+ /**
39
+ * Public API reference shown by CLI output.
40
+ *
41
+ * @type {string}
42
+ */
43
+ const TOOLSET_REFERENCE = "import { API } from '@j-o-r/hello-dave';";
44
+
45
+ /**
46
+ * Shorten a description for compact CLI display.
47
+ *
48
+ * @param {unknown} desc - Description value.
49
+ * @param {number} [maxLen=140] - Maximum length.
50
+ * @returns {string} Shortened description.
51
+ */
52
+ export function shortDescription(desc, maxLen = 140) {
53
+ if (!desc || typeof desc !== 'string') return '';
54
+
55
+ let text = desc.trim().replace(/\s+/g, ' ');
56
+ const firstSentence = text.split(/[.!?]\s+/)[0];
57
+
58
+ if (firstSentence && firstSentence.length >= 15) {
59
+ text = firstSentence;
60
+ }
61
+
62
+ if (text.length > maxLen) {
63
+ text = `${text.substring(0, maxLen - 3).trim()}...`;
64
+ }
65
+
66
+ return text;
67
+ }
68
+
69
+ /**
70
+ * Parse toolset CLI arguments.
71
+ *
72
+ * @param {string[]} [argv=process.argv.slice(2)] - Arguments without node/script prefix.
73
+ * @returns {ParsedToolsetArgs} Parsed toolset arguments.
74
+ */
75
+ export function parseToolsetArgs(argv = process.argv.slice(2)) {
76
+ const filters = [];
77
+ let json = false;
78
+ let help = false;
79
+
80
+ for (const arg of argv) {
81
+ if (arg === '--json') {
82
+ json = true;
83
+ } else if (arg === '--help' || arg === '-h') {
84
+ help = true;
85
+ } else if (!arg.startsWith('--')) {
86
+ filters.push(arg);
87
+ }
88
+ }
89
+
90
+ return { filters, json, help };
91
+ }
92
+
93
+ /**
94
+ * Check if a display name matches any provided filters.
95
+ *
96
+ * @param {string} displayName - Toolset display name, for example "minimax/music".
97
+ * @param {string[]} [filters=[]] - Filters to apply.
98
+ * @returns {boolean} Whether the display name matches.
99
+ */
100
+ export function matchesToolsetFilter(displayName, filters = []) {
101
+ if (!filters || filters.length === 0) return true;
102
+
103
+ const [provider = '', type = ''] = displayName.split('/');
104
+
105
+ return filters.some((filter) => {
106
+ const filterLower = String(filter).toLowerCase();
107
+ const nameLower = displayName.toLowerCase();
108
+
109
+ if (filterLower.includes('/')) {
110
+ return nameLower === filterLower || nameLower.startsWith(`${filterLower}/`);
111
+ }
112
+
113
+ return provider.toLowerCase() === filterLower || type.toLowerCase() === filterLower;
114
+ });
115
+ }
116
+
117
+ /**
118
+ * List framework/default toolsets directly in the current process.
119
+ *
120
+ * @param {ToolsetListOptions} [options={}] - Listing options.
121
+ * @returns {ToolsetListResult} Structured toolset listing.
122
+ */
123
+ export function listToolsets(options = {}) {
124
+ const filters = Array.isArray(options.filters) ? options.filters : [];
125
+ const source = options.toolsets || defaultToolsets;
126
+
127
+ /** @type {ListedToolset[]} */
128
+ const results = [];
129
+
130
+ for (const [provider, typeMap] of Object.entries(source)) {
131
+ if (!typeMap || typeof typeMap !== 'object') continue;
132
+
133
+ for (const [type, toolsetInstance] of Object.entries(typeMap)) {
134
+ const displayName = `${provider}/${type}`;
135
+ if (!matchesToolsetFilter(displayName, filters)) continue;
136
+
137
+ if (!toolsetInstance || typeof toolsetInstance.list !== 'function') {
138
+ results.push({
139
+ name: displayName,
140
+ toolCount: 0,
141
+ tools: [],
142
+ note: 'Not a ToolSet instance'
143
+ });
144
+ continue;
145
+ }
146
+
147
+ let listed = [];
148
+ try {
149
+ listed = toolsetInstance.list() || [];
150
+ } catch {
151
+ listed = [];
152
+ }
153
+
154
+ const tools = listed.map((tool) => ({
155
+ name: String(tool?.name || ''),
156
+ description: shortDescription(tool?.description)
157
+ }));
158
+
159
+ results.push({
160
+ name: displayName,
161
+ toolCount: tools.length,
162
+ tools
163
+ });
164
+ }
165
+ }
166
+
167
+ results.sort((a, b) => a.name.localeCompare(b.name));
168
+
169
+ return {
170
+ reference: TOOLSET_REFERENCE,
171
+ toolsets: results,
172
+ totalToolsets: results.length,
173
+ totalTools: results.reduce((sum, result) => sum + result.toolCount, 0)
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Render toolset help text.
179
+ *
180
+ * @param {string} [command='dave --toolsets'] - Command shown in examples.
181
+ * @returns {string} Help text.
182
+ */
183
+ export function renderToolsetsHelp(command = 'dave --toolsets') {
184
+ return `
185
+ ${command} — List default toolsets from API.toolset
186
+
187
+ Public reference:
188
+ ${TOOLSET_REFERENCE}
189
+
190
+ const ts = API.toolset.minimax.music;
191
+ const tools = ts.list();
192
+
193
+ Usage:
194
+ ${command} # all toolsets
195
+ ${command} minimax # all minimax/* toolsets
196
+ ${command} minimax/music # exact minimax/music
197
+ ${command} music # any */music toolset
198
+ ${command} generic bash # multiple filters
199
+
200
+ Options:
201
+ --json Output structured JSON
202
+ --help Show this help
203
+ `;
204
+ }
205
+
206
+ /**
207
+ * Render a human-readable toolset listing.
208
+ *
209
+ * @param {ToolsetListResult} result - Structured toolset listing.
210
+ * @returns {string} Human-readable output.
211
+ */
212
+ export function renderToolsetList(result) {
213
+ if (!result || !Array.isArray(result.toolsets) || result.toolsets.length === 0) {
214
+ return 'No matching toolsets found.';
215
+ }
216
+
217
+ const lines = [];
218
+ lines.push('Default toolsets (from API.toolset)');
219
+ lines.push('');
220
+ lines.push('Public API reference:');
221
+ lines.push(` ${TOOLSET_REFERENCE}`);
222
+ lines.push('');
223
+ lines.push(' // Example:');
224
+ lines.push(' const ts = API.toolset.minimax.music;');
225
+ lines.push(' const tools = ts.list(); // array of { name, description, parameters }');
226
+ lines.push('');
227
+
228
+ for (const toolset of result.toolsets) {
229
+ lines.push(`${toolset.name} (${toolset.toolCount} tools)`);
230
+
231
+ if (toolset.note) {
232
+ lines.push(` ${toolset.note}`);
233
+ } else if (toolset.tools.length === 0) {
234
+ lines.push(' (no tools)');
235
+ } else {
236
+ for (const tool of toolset.tools) {
237
+ lines.push(` - ${tool.name}: ${tool.description}`);
238
+ }
239
+ }
240
+
241
+ lines.push('');
242
+ }
243
+
244
+ lines.push(`Total matching toolsets: ${result.totalToolsets}`);
245
+ lines.push(`Total tools: ${result.totalTools}`);
246
+
247
+ return lines.join('\n');
248
+ }
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.1.1",
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.",
4
+ "version": "0.1.5",
5
+ "description": "BETA - ESM toolkit for building AI agents. 'Hello, Dave.' A calm, reliable interface to Grok (xAI), OpenAI, and Anthropic foolproof and incapable of error (mostly). Putting itself to the fullest possible use.",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./types/index.d.ts",
8
8
  "typesVersions": {
@@ -13,8 +13,7 @@
13
13
  }
14
14
  },
15
15
  "bin": {
16
- "dave": "bin/dave.js",
17
- "codeDave": "bin/codeDave"
16
+ "dave": "bin/dave.js"
18
17
  },
19
18
  "scripts": {
20
19
  "release": "npm run types && npm pack --pack-destination=release",
@@ -25,6 +24,9 @@
25
24
  "unlink-self": "rm -rf node_modules/@j-o-r/hello-dave",
26
25
  "tests": "utils/test.sh"
27
26
  },
27
+ "allowScripts": {
28
+ "*": true
29
+ },
28
30
  "repository": {
29
31
  "type": "git",
30
32
  "url": "https://codeberg.org/duin/hello-dave"
@@ -32,8 +34,12 @@
32
34
  "dependencies": {
33
35
  "@j-o-r/apiserver": "*",
34
36
  "@j-o-r/cli": "*",
37
+ "@j-o-r/sh": "*",
35
38
  "gpt-3-encoder": "*"
36
39
  },
40
+ "devDependencies": {
41
+ "@types/node": "*"
42
+ },
37
43
  "exports": {
38
44
  ".": {
39
45
  "types": "./types/index.d.ts",
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Builds authenticated request headers for LALAL.AI API.
3
+ * Requires the `LALAL_AI_LICENSE_KEY` environment variable.
4
+ *
5
+ * @function getHeaders
6
+ * @param {Object} [extraHeaders={}] - Additional headers (e.g. Content-Disposition)
7
+ * @returns {Object} Headers with X-License-Key
8
+ * @throws {Error} If license key missing
9
+ */
10
+ export function getHeaders(extraHeaders?: Object): Object;
11
+ /**
12
+ * Upload helper (binary octet-stream with Content-Disposition).
13
+ *
14
+ * @async
15
+ * @function uploadFile
16
+ * @param {string|Buffer|Blob} input
17
+ * @param {string} [filename]
18
+ * @returns {Promise<Object>} { id, name, size, duration, expires }
19
+ */
20
+ export function uploadFile(input: string | Buffer | Blob, filename?: string): Promise<Object>;
21
+ /**
22
+ * Prepares upload payload: returns { body: Buffer, headers: { 'Content-Disposition': ... } }
23
+ *
24
+ * @async
25
+ * @function prepareUploadPayload
26
+ * @param {string|Buffer|Blob} input - File path, URL, Buffer or Blob
27
+ * @param {string} [filename] - Optional filename (required for Buffers/Blobs)
28
+ * @returns {Promise<{body: Buffer, contentDisposition: string}>}
29
+ */
30
+ export function prepareUploadPayload(input: string | Buffer | Blob, filename?: string): Promise<{
31
+ body: Buffer;
32
+ contentDisposition: string;
33
+ }>;
34
+ /**
35
+ * Downloads a remote file (audio) to temp dir.
36
+ *
37
+ * @async
38
+ * @function downloadRemoteToTemp
39
+ * @param {string} url - Remote URL
40
+ * @param {string} [prefix='lalal-download'] - Filename prefix
41
+ * @returns {Promise<string>} Local file path
42
+ */
43
+ export function downloadRemoteToTemp(url: string, prefix?: string): Promise<string>;
44
+ /**
45
+ * Downloads a track URL to local file.
46
+ *
47
+ * @async
48
+ * @function downloadTrack
49
+ * @param {string} url - Track download URL from check result
50
+ * @param {string} [destDir] - Destination directory (defaults to TMP_DIR)
51
+ * @returns {Promise<string>} Local path
52
+ */
53
+ export function downloadTrack(url: string, destDir?: string): Promise<string>;
54
+ /**
55
+ * Checks task status.
56
+ *
57
+ * @async
58
+ * @function checkTasks
59
+ * @param {string|string[]} taskIds - Single ID or array
60
+ * @returns {Promise<Object>} CheckV1Response
61
+ */
62
+ export function checkTasks(taskIds: string | string[]): Promise<Object>;
63
+ /**
64
+ * Polls /check/ until task completes, errors, or times out.
65
+ *
66
+ * @async
67
+ * @function pollForTask
68
+ * @param {string} taskId
69
+ * @param {number} [maxAttempts=120] - ~10 minutes at 5s interval
70
+ * @param {number} [intervalMs=5000]
71
+ * @returns {Promise<Object>} The success result or throws on error/cancel/timeout
72
+ */
73
+ export function pollForTask(taskId: string, maxAttempts?: number, intervalMs?: number): Promise<Object>;
74
+ /**
75
+ * Starts a stem separator split task.
76
+ *
77
+ * @async
78
+ * @function splitStemSeparator
79
+ * @param {string} sourceId
80
+ * @param {Object} presets - StemSeparatorSplitterPresetsV1
81
+ * @param {string} [idempotencyKey]
82
+ * @returns {Promise<{task_id: string}>}
83
+ */
84
+ export function splitStemSeparator(sourceId: string, presets: Object, idempotencyKey?: string): Promise<{
85
+ task_id: string;
86
+ }>;
87
+ /**
88
+ * Starts a demuser (music removal) task.
89
+ */
90
+ export function splitDemuser(sourceId: any, presets?: {}, idempotencyKey?: null): Promise<any>;
91
+ /**
92
+ * Starts a voice clean task.
93
+ */
94
+ export function splitVoiceClean(sourceId: any, presets?: {}, idempotencyKey?: null): Promise<any>;
95
+ /**
96
+ * Starts a multistem split task.
97
+ */
98
+ export function splitMultistem(sourceId: any, presets: any, idempotencyKey?: null): Promise<any>;
99
+ export function batchSplitStemSeparator(items: any): Promise<any>;
100
+ export function changeVoice(sourceId: any, presets: any, idempotencyKey?: null): Promise<any>;
101
+ export function listVoicePacks(): Promise<any>;
102
+ export function cancelTasks(taskIds: any): Promise<any>;
103
+ export function cancelAllTasks(): Promise<any>;
104
+ export function deleteSource(sourceId: any): Promise<any>;
105
+ export function getMinutesLeft(): Promise<any>;
106
+ /**
107
+ * Full workflow: upload file → start split → poll → return result with tracks.
108
+ *
109
+ * @async
110
+ * @function processSplit
111
+ * @param {string|Buffer|Blob} input - Audio source
112
+ * @param {string} stem - 'vocals' | 'voice' | 'music' | etc. (determines endpoint)
113
+ * @param {Object} [options] - presets + other options
114
+ * @returns {Promise<Object>} Success result from check (with tracks)
115
+ */
116
+ export function processSplit(input: string | Buffer | Blob, stem: string, options?: Object): Promise<Object>;
@@ -27,7 +27,7 @@ export function saveImageToLocal(imageData: string, filenamePrefix?: string, ind
27
27
  * Supports all models, parameters, and response formats.
28
28
  *
29
29
  * @param {string} prompt - Text description of the image, max 1500 characters.
30
- * @param {Object} [options] - All available options from the official spec.
30
+ * @param {Object} [options={}] - All available options from the official spec.
31
31
  *
32
32
  * @param {string} [options.model='image-01'] - Model to use.
33
33
  * Supported values:
@@ -36,6 +36,7 @@ export function saveImageToLocal(imageData: string, filenamePrefix?: string, ind
36
36
  *
37
37
  * @param {string} [options.aspect_ratio='1:1'] - Image aspect ratio.
38
38
  * Options: '1:1', '16:9', '4:3', '3:2', '2:3', '3:4', '9:16', '21:9'
39
+ * (aspect_ratio takes priority over explicit width/height)
39
40
  *
40
41
  * @param {number} [options.width] - Image width in px (512-2048, divisible by 8).
41
42
  * Only effective for model 'image-01'. aspect_ratio takes priority if both provided.