@j-o-r/hello-dave 0.0.2 → 0.0.4

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 (161) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +445 -160
  3. package/README.md.backup +269 -0
  4. package/README.md.bak +481 -0
  5. package/README.md.bak.1774780058 +338 -0
  6. package/README.md.bak2 +455 -0
  7. package/bin/dave.js +165 -0
  8. package/docs.bak.1774780058/agent-manager.md +167 -0
  9. package/docs.bak.1774780058/agent-manager.md.bak +137 -0
  10. package/docs.bak.1774780058/agent-manager.md.bak2 +157 -0
  11. package/docs.bak.1774780058/codeserver-pattern.md +191 -0
  12. package/docs.bak.1774780058/path-resolution-best-practices.md +104 -0
  13. package/docs.bak.1774780058/project-overview.md +67 -0
  14. package/docs.bak.1774780058/project-overview.md.bak +67 -0
  15. package/docs.bak.1774780058/prompt-class.md +141 -0
  16. package/docs.bak.1774780058/prompt-class.md.bak +142 -0
  17. package/docs.bak.1774780058/tools-syntax-validation.md +121 -0
  18. package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +125 -0
  19. package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +125 -0
  20. package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +106 -0
  21. package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +106 -0
  22. package/docs.bak.1774780058/toolset.md +164 -0
  23. package/docs.bak.1774780058/toolset.md.bak +94 -0
  24. package/docs.bak.1774780058/toolset.md.bak3 +161 -0
  25. package/docs.bak.1774780058/toolset.md.bak4 +161 -0
  26. package/docs.bak.1774780058/toolset.md.bak5 +161 -0
  27. package/docs.bak.1774780058/toolset.md.bak6 +163 -0
  28. package/docs.bak.1774780058/toolset.md.bak_path +163 -0
  29. package/docs.bak.1774780058/toolset.md.bak_syntax +161 -0
  30. package/docs.bak.1774780058/xai-responses.md +111 -0
  31. package/docs.bak.1774780058/xai-responses.md.bak +107 -0
  32. package/docs.bak.1774780058/xai-responses.md.bak2 +107 -0
  33. package/docs.bak.1774780058/xai_collections.md +106 -0
  34. package/examples/ask_agent.js +137 -0
  35. package/examples/code_agent.js +149 -0
  36. package/examples/coderev_agent.js +136 -0
  37. package/examples/codeserver.sh +47 -0
  38. package/examples/daisy_agent.js +170 -0
  39. package/examples/docs_agent.js +148 -0
  40. package/examples/gpt_agent.js +125 -0
  41. package/examples/grok_agent.js +132 -0
  42. package/examples/grok_agent.js.bak +98 -0
  43. package/examples/grok_agent.js.bak.2 +99 -0
  44. package/examples/grok_agent.js.bak.3 +1 -0
  45. package/examples/grok_agent.js.bak.4 +124 -0
  46. package/examples/grok_agent.js.bak.5 +1 -0
  47. package/examples/grok_agent.js.bak.6 +1 -0
  48. package/examples/memory_agent.js +152 -0
  49. package/examples/npm_agent.js +202 -0
  50. package/examples/npm_agent.js.bak.3 +2 -0
  51. package/examples/npm_agent.js.bak.4 +205 -0
  52. package/examples/npm_agent.js.bak.5 +1 -0
  53. package/examples/npm_agent.js.bak.6 +1 -0
  54. package/examples/prompt_agent.js +133 -0
  55. package/examples/readme_agent.js +148 -0
  56. package/examples/spawn_agent.js +293 -0
  57. package/examples/test_agent.js +187 -0
  58. package/examples/todo_agent.js +175 -0
  59. package/examples.bak.1774780058/ask_agent.js +114 -0
  60. package/examples.bak.1774780058/code_agent.js +149 -0
  61. package/examples.bak.1774780058/coderev_agent.js +72 -0
  62. package/examples.bak.1774780058/codeserver.sh +47 -0
  63. package/examples.bak.1774780058/daisy_agent.js +177 -0
  64. package/examples.bak.1774780058/docs_agent.js +119 -0
  65. package/{bin/hdAsk.js → examples.bak.1774780058/gpt_agent.js} +46 -40
  66. package/examples.bak.1774780058/grok_agent.js +98 -0
  67. package/examples.bak.1774780058/memory_agent.js +112 -0
  68. package/examples.bak.1774780058/npm_agent.js +175 -0
  69. package/examples.bak.1774780058/prompt_agent.js +112 -0
  70. package/examples.bak.1774780058/readme_agent.js +144 -0
  71. package/examples.bak.1774780058/spawn_agent.js +263 -0
  72. package/examples.bak.1774780058/test_agent.js +162 -0
  73. package/examples.bak.1774780058/todo_agent.js +138 -0
  74. package/lib/API/openai.com/reponses/text.js +12 -18
  75. package/lib/API/x.ai/collections.js +354 -0
  76. package/lib/API/x.ai/files.js +218 -0
  77. package/lib/API/x.ai/responses.js +492 -0
  78. package/lib/API/x.ai/text.js +1 -1
  79. package/lib/AgentClient.js +13 -6
  80. package/lib/AgentManager.js +80 -10
  81. package/lib/AgentServer.js +50 -22
  82. package/lib/Cli.js +7 -1
  83. package/lib/Prompt.js +4 -2
  84. package/lib/ToolSet.js +2 -1
  85. package/lib/genericToolset.js +258 -88
  86. package/lib/genericToolset.js.bak_syntax +402 -0
  87. package/lib/index.js +4 -2
  88. package/lib/wsCli.js +256 -0
  89. package/lib/wsIO.js +96 -0
  90. package/package.json +26 -21
  91. package/scenarios.bak.1774780058/data/eval_node_message.json +9 -0
  92. package/scenarios.bak.1774780058/data/hist_oa.json +66 -0
  93. package/scenarios.bak.1774780058/data/o3_response1.json +96 -0
  94. package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +112 -0
  95. package/scenarios.bak.1774780058/data/tool_oa.json +96 -0
  96. package/scenarios.bak.1774780058/data/tool_xai.json +59 -0
  97. package/scenarios.bak.1774780058/data/tool_xai2.json +40 -0
  98. package/scenarios.bak.1774780058/data/xai-response-1.json +59 -0
  99. package/scenarios.bak.1774780058/data/xai-response-2.json +10 -0
  100. package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +59 -0
  101. package/scenarios.bak.1774780058/data/xai_search_response.json +58 -0
  102. package/scenarios.bak.1774780058/environment.js +10 -0
  103. package/scenarios.bak.1774780058/example.js +17 -0
  104. package/scenarios.bak.1774780058/genericToolset.test.js +182 -0
  105. package/scenarios.bak.1774780058/grok.js +113 -0
  106. package/scenarios.bak.1774780058/memory-tools.js +51 -0
  107. package/scenarios.bak.1774780058/openai-o3.js +137 -0
  108. package/scenarios.bak.1774780058/openai-prompt.js +155 -0
  109. package/scenarios.bak.1774780058/openai-session.js +148 -0
  110. package/scenarios.bak.1774780058/openai.js +102 -0
  111. package/scenarios.bak.1774780058/prompt.js +118 -0
  112. package/scenarios.bak.1774780058/promptFishbowl.js +76 -0
  113. package/scenarios.bak.1774780058/search.brave.com.js +25 -0
  114. package/scenarios.bak.1774780058/sh.js +15 -0
  115. package/scenarios.bak.1774780058/test-wsio.js +26 -0
  116. package/scenarios.bak.1774780058/testToolset.js +42 -0
  117. package/scenarios.bak.1774780058/toolset.js +16 -0
  118. package/scenarios.bak.1774780058/toolset.test.js +141 -0
  119. package/scenarios.bak.1774780058/write_file_syntax.test.js +145 -0
  120. package/scenarios.bak.1774780058/write_file_validation/README.md +30 -0
  121. package/scenarios.bak.1774780058/write_file_validation/bad.js +3 -0
  122. package/scenarios.bak.1774780058/write_file_validation/good.js +4 -0
  123. package/scenarios.bak.1774780058/write_file_validation/test.sh +43 -0
  124. package/scenarios.bak.1774780058/wsClient.js +69 -0
  125. package/scenarios.bak.1774780058/xai_responses.integration.test.js +57 -0
  126. package/scenarios.bak.1774780058/xai_responses.test.js +154 -0
  127. package/scenarios.bak.1774780058/xaicoll.js +50 -0
  128. package/scenarios.bak.1774780058/xaifiles.js +48 -0
  129. package/types/API/openai.com/reponses/text.d.ts +17 -3
  130. package/types/API/x.ai/collections.d.ts +167 -0
  131. package/types/API/x.ai/files.d.ts +84 -0
  132. package/types/API/x.ai/responses.d.ts +379 -0
  133. package/types/AgentClient.d.ts +5 -0
  134. package/types/AgentManager.d.ts +25 -31
  135. package/types/AgentServer.d.ts +5 -1
  136. package/types/Prompt.d.ts +4 -2
  137. package/types/ToolSet.d.ts +1 -0
  138. package/types/index.d.ts +4 -3
  139. package/types/wsCli.d.ts +3 -0
  140. package/types/wsIO.d.ts +26 -0
  141. package/utils/bars.js +40 -0
  142. package/utils/clear_sessions.sh +54 -0
  143. package/{bin/hdInspect.js → utils/format_log.js} +5 -0
  144. package/utils/list_sessions.sh +46 -0
  145. package/utils/search_sessions.sh +73 -0
  146. package/utils/syntax_check.sh +61 -0
  147. package/utils/test.sh +46 -0
  148. package/bin/hdClear.js +0 -13
  149. package/bin/hdCode.js +0 -115
  150. package/bin/hdConnect.js +0 -230
  151. package/bin/hdNpm.js +0 -114
  152. package/bin/hdPrompt.js +0 -108
  153. package/examples/claude-test.js +0 -89
  154. package/examples/claude.js +0 -143
  155. package/examples/gpt.js +0 -127
  156. package/examples/gpt_code.js +0 -125
  157. package/examples/gpt_note_keeping.js +0 -117
  158. package/examples/grok.js +0 -119
  159. package/examples/grok_code.js +0 -114
  160. package/examples/grok_note_keeping.js +0 -111
  161. package/module.md +0 -189
@@ -0,0 +1,354 @@
1
+ /**
2
+ * [api documentation](https://docs.x.ai/docs/guides/using-collections/api)
3
+ */
4
+ import { GLOBAL } from '../../fafs.js'
5
+ import { request as doRequest } from '@j-o-r/apiserver';
6
+ import FILES from './files.js';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ /**
10
+ * @typedef {Object} Collection
11
+ * @property {string} collection_id - Unique identifier for the collection.
12
+ * @property {string} collection_name - Name of the collection.
13
+ * @property {string} created_at - ISO datetime string when the collection was created.
14
+ * @property {string} updated_at
15
+ * @property {Object} index_configuration - Configuration for the indexing model.
16
+ * @property {string} index_configuration.model_name - Name of the embedding model.
17
+ * @property {Object} chunk_configuration - Configuration for chunking documents.
18
+ * @property {Object} chunk_configuration.tokens_configuration - Token-related chunk settings.
19
+ * @property {number} chunk_configuration.tokens_configuration.max_chunk_size_tokens - Maximum tokens per chunk.
20
+ * @property {number} chunk_configuration.tokens_configuration.chunk_overlap_tokens - Overlap tokens between chunks.
21
+ * @property {string} chunk_configuration.tokens_configuration.encoding_name - Token encoding name.
22
+ * @property {boolean} chunk_configuration.strip_whitespace - Whether to strip whitespace.
23
+ * @property {boolean} chunk_configuration.inject_name_into_chunks - Whether to inject name into chunks.
24
+ * @property {number} documents_count - Number of documents in the collection.
25
+ * @property {Array} field_definitions - Array of field definitions.
26
+ */
27
+ /**
28
+ * @typedef fileList
29
+ * @property {File[]} data
30
+ * @property {string|null} pagination_token
31
+ */
32
+ /**
33
+ * @typedef {Object} File
34
+ * @property {number} bytes - File size in bytes.
35
+ * @property {number} created_at - Unix timestamp of creation.
36
+ * @property {number|null} expires_at - Unix timestamp of expiration, or null.
37
+ * @property {string} filename - Path to the file.
38
+ * @property {string} id - Unique file identifier.
39
+ * @property {string} object - Type of object, e.g., 'file'.
40
+ * @property {string} purpose - Purpose of the file.
41
+ *//**
42
+ * @typedef {Object} SearchResult
43
+ * @property {string} document_id
44
+ * @property {string} content
45
+ * @property {Object} metadata
46
+ * @property {number} score
47
+ */
48
+
49
+ const API_BASE = 'https://api.x.ai/v1';
50
+ const MANAGEMENT_BASE = 'https://management-api.x.ai/v1';
51
+
52
+
53
+ const collectionStorage = new Map();
54
+
55
+ /**
56
+ * Get the default headers for API
57
+ * @returns {object}
58
+ */
59
+ const getAPIHeaders = () => {
60
+ if (!process.env['XAIKEY']) {
61
+ throw new Error('Missing XAIKEY! export XAIKEY=<XAIKEY>')
62
+ }
63
+ const KEY = process.env['XAIKEY'];
64
+ return {
65
+ 'Authorization': `Bearer ${KEY}`
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Get the default headers for Management API
71
+ * @returns {object}
72
+ */
73
+ const getManagementHeaders = () => {
74
+ const KEY = process.env['XAI_MANAGEMENT_KEY'] || process.env['XAIKEY'];
75
+ if (!KEY) {
76
+ throw new Error('Missing XAI_MANAGEMENT_KEY or XAIKEY!')
77
+ }
78
+ return {
79
+ 'Authorization': `Bearer ${KEY}`
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create a collection
85
+ * @param {string} name
86
+ * @returns {Promise<string>} collection_id
87
+ */
88
+ async function create(name) {
89
+ const url = `${MANAGEMENT_BASE}/collections`;
90
+ const headers = { ...getManagementHeaders(), 'Content-Type': 'application/json' };
91
+ const body = { collection_name: name };
92
+ const res = await doRequest(url, 'POST', headers, body);
93
+ if (res.status !== 200) {
94
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
95
+ }
96
+ return res.response.collection_id;
97
+ }
98
+
99
+ /**
100
+ * List collections
101
+ * @returns {Promise<Array<Collection>>}
102
+ */
103
+ async function list() {
104
+ const url = `${MANAGEMENT_BASE}/collections`;
105
+ const headers = getManagementHeaders();
106
+ const res = await doRequest(url, 'GET', headers, {});
107
+ if (res.status !== 200) {
108
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
109
+ }
110
+ return res.response.collections || [];
111
+ }
112
+
113
+ /**
114
+ * Get collection details
115
+ * @param {string} id
116
+ * @returns {Promise<Collection>}
117
+ */
118
+ async function get(id) {
119
+ const url = `${MANAGEMENT_BASE}/collections/${id}`;
120
+ const headers = getManagementHeaders();
121
+ const res = await doRequest(url, 'GET', headers, {});
122
+ if (res.status !== 200) {
123
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
124
+ }
125
+ return res.response;
126
+ }
127
+
128
+ /**
129
+ * Update collection collection name
130
+ * @param {string} id
131
+ * @param {string} name
132
+ * @returns {Promise<void>}
133
+ */
134
+ async function update(id, name) {
135
+ const url = `${MANAGEMENT_BASE}/collections/${id}`;
136
+ const headers = { ...getManagementHeaders(), 'Content-Type': 'application/json' };
137
+ const body = { collection_name: name };
138
+ const res = await doRequest(url, 'PUT', headers, body);
139
+ if (res.status !== 200) {
140
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Delete collection
146
+ * @param {string} id
147
+ * @returns {Promise<void>}
148
+ */
149
+ async function del (id) {
150
+ const url = `${MANAGEMENT_BASE}/collections/${id}`;
151
+ const headers = getManagementHeaders();
152
+ const res = await doRequest(url, 'DELETE', headers, {});
153
+ if (res.status !== 200) {
154
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Add file to collection
160
+ * @param {string} collectionId
161
+ * @param {string} fileId
162
+ * @returns {Promise<void>}
163
+ */
164
+ async function addFileToCollection(collectionId, fileId) {
165
+ const url = `${MANAGEMENT_BASE}/collections/${collectionId}/documents/${fileId}`;
166
+ const headers = getManagementHeaders();
167
+ const res = await doRequest(url, 'POST', headers, {});
168
+ if (res.status !== 200) {
169
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
170
+ }
171
+ }
172
+
173
+ // /**
174
+ // * Upload and add file to collection with optional metadata
175
+ // * @param {string} collectionId
176
+ // * @param {string} filePath
177
+ // * @param {Object} [metadata]
178
+ // * @returns {Promise<string>} file_id
179
+ // */
180
+ // async function uploadAndAddFileToCollection(collectionId, filePath, metadata = {}) {
181
+ // const fileId = (await FILES.upload(filePath)).id;
182
+ // const url = `${MANAGEMENT_BASE}/collections/${collectionId}/documents`;
183
+ // const headers = getManagementHeaders();
184
+ // const formData = new FormData();
185
+ // formData.append('name', path.basename(filePath));
186
+ // formData.append('data', fs.createReadStream(filePath));
187
+ // formData.append('content_type', 'application/octet-stream'); // or detect
188
+ // if (Object.keys(metadata).length > 0) {
189
+ // formData.append('fields', JSON.stringify(metadata));
190
+ // }
191
+ // const response = await fetch(url, {
192
+ // method: 'POST',
193
+ // headers,
194
+ // body: formData
195
+ // });
196
+ // if (!response.ok) {
197
+ // throw new Error(`${response.status}: ${await response.text()}`);
198
+ // }
199
+ // const data = await response.json();
200
+ // return fileId;
201
+ // }
202
+
203
+ // /**
204
+ // * Add files from folder to collection
205
+ // * @param {string} collectionId
206
+ // * @param {string} folderPath
207
+ // * @param {boolean} [recursive=false]
208
+ // * @param {Object} [metadata]
209
+ // * @returns {Promise<string[]>} file_ids
210
+ // */
211
+ // async function addFilesFromFolder(collectionId, folderPath, recursive = false, metadata = {}) {
212
+ // const files = [];
213
+ // const readDir = (dir) => {
214
+ // const items = fs.readdirSync(dir);
215
+ // for (const item of items) {
216
+ // const fullPath = path.join(dir, item);
217
+ // const stat = fs.statSync(fullPath);
218
+ // if (stat.isDirectory() && recursive) {
219
+ // readDir(fullPath);
220
+ // } else if (stat.isFile()) {
221
+ // files.push(fullPath);
222
+ // }
223
+ // }
224
+ // };
225
+ // readDir(folderPath);
226
+ // const fileIds = [];
227
+ // for (const filePath of files) {
228
+ // try {
229
+ // const fileId = await uploadAndAddFileToCollection(collectionId, filePath, metadata);
230
+ // fileIds.push(fileId);
231
+ // } catch (error) {
232
+ // console.error(`Failed to upload ${filePath}: ${error.message}`);
233
+ // }
234
+ // }
235
+ // return fileIds;
236
+ // }
237
+
238
+ // /**
239
+ // * Resync folder to collection (delete all and re-add)
240
+ // * @param {string} collectionId
241
+ // * @param {string} folderPath
242
+ // * @param {boolean} [recursive=false]
243
+ // * @param {Object} [metadata]
244
+ // * @returns {Promise<string[]>} file_ids
245
+ // */
246
+ // async function resyncFolder(collectionId, folderPath, recursive = false, metadata = {}) {
247
+ // // First, delete all files in collection? But API doesn't have list files in collection directly.
248
+ // // Perhaps list collections and delete them, but complicated.
249
+ // // For simplicity, assume we don't delete, just add new.
250
+ // // To resync, perhaps delete collection and recreate, but that would lose id.
251
+ // // Since no direct way, maybe just add new files.
252
+ // // Perhaps get collection details, but doesn't list files.
253
+ // // For now, just add files, assuming no duplicates.
254
+ // return addFilesFromFolder(collectionId, folderPath, recursive, metadata);
255
+ // }
256
+
257
+ /**
258
+ * Delete file from collection
259
+ * @param {string} collectionId
260
+ * @param {string} fileId
261
+ * @returns {Promise<void>}
262
+ */
263
+ async function deleteFileFromCollection(collectionId, fileId) {
264
+ const url = `${MANAGEMENT_BASE}/collections/${collectionId}/documents/${fileId}`;
265
+ const headers = getManagementHeaders();
266
+ const res = await doRequest(url, 'DELETE', headers, {});
267
+ if (res.status !== 200) {
268
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
269
+ }
270
+ }
271
+
272
+ /**
273
+ * The status of files in a collection
274
+ * @param {string} collectionId
275
+ * @returns {Promise<void>}
276
+ */
277
+ async function status(collectionId) {
278
+ const url = `${MANAGEMENT_BASE}/collections/${collectionId}/documents`;
279
+ const headers = getManagementHeaders();
280
+ const res = await doRequest(url, 'GET', headers, {});
281
+ if (res.status !== 200) {
282
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
283
+ }
284
+ return res.response
285
+ }
286
+
287
+ /**
288
+ * The status of files in a collection
289
+ * @param {string} collectionId
290
+ * @param {string} fileId
291
+ * @returns {Promise<void>}
292
+ */
293
+ async function details(collectionId,fileId) {
294
+ const url = `${MANAGEMENT_BASE}/collections/${collectionId}/documents/${fileId}`;
295
+ const headers = getManagementHeaders();
296
+ const res = await doRequest(url, 'GET', headers, {});
297
+ if (res.status !== 200) {
298
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
299
+ }
300
+ return res.response
301
+ }
302
+ /**
303
+ * Search in collections
304
+ * @param {string} query
305
+ * @param {string[]} collectionIds
306
+ * @param {Object} [options]
307
+ * @param {string} [options.retrieval_mode] - e.g., 'hybrid'
308
+ * @returns {Promise<SearchResult[]>}
309
+ */
310
+ async function searchInCollections(query, collectionIds, options = {}) {
311
+ const url = `${API_BASE}/documents/search`;
312
+ const headers = { ...getAPIHeaders(), 'Content-Type': 'application/json' };
313
+ const body = {
314
+ query,
315
+ source: {
316
+ collection_ids: collectionIds
317
+ },
318
+ ...options
319
+ };
320
+ const res = await doRequest(url, 'POST', headers, body);
321
+ if (res.status !== 200) {
322
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
323
+ }
324
+ return res.response.results || res.response;
325
+ }
326
+
327
+ /**
328
+ * @returns {Promise<Map<string, Collection>>}
329
+ */
330
+ const getStorage = async () => {
331
+ const lst = await list();
332
+ let i = 0;
333
+ const len = lst.length;
334
+ for(;i < len; i++) {
335
+ const col = lst[i];
336
+ // Decided to have the name as a key.
337
+ collectionStorage.set(col.collection_name, col);
338
+ }
339
+ return collectionStorage;
340
+ }
341
+
342
+ export default {
343
+ create,
344
+ list,
345
+ get,
346
+ status,
347
+ details,
348
+ update,
349
+ del,
350
+ addFileToCollection,
351
+ deleteFileFromCollection,
352
+ searchInCollections,
353
+ storage: getStorage
354
+ };
@@ -0,0 +1,218 @@
1
+ /**
2
+ * https://docs.x.ai/docs/guides/files
3
+ Limitations
4
+ File size: Maximum 48 MB per file
5
+ No batch requests: File attachments with document search are agentic requests and do not support batch mode (n > 1)
6
+ Agentic models only: Requires models that support agentic tool calling (e.g., grok-4-fast, grok-4)
7
+ Supported file formats:
8
+ Plain text files (.txt)
9
+ Markdown files (.md)
10
+ Code files (.py, .js, .java, etc.)
11
+ CSV files (.csv)
12
+ JSON files (.json)
13
+ PDF documents (.pdf)
14
+ And many other text-based formats
15
+
16
+ */
17
+ import { request as doRequest } from '@j-o-r/apiserver';
18
+ import fs from 'fs';
19
+
20
+
21
+ const fileStorage = new Map();
22
+
23
+ /**
24
+ * @typedef fileList
25
+ * @property {File[]} data
26
+ * @property {string|null} pagination_token
27
+ */
28
+ /**
29
+ * @typedef {Object} File
30
+ * @property {number} bytes - File size in bytes.
31
+ * @property {number} created_at - Unix timestamp of creation.
32
+ * @property {number|null} expires_at - Unix timestamp of expiration, or null.
33
+ * @property {string} filename - Path to the file.
34
+ * @property {string} id - Unique file identifier.
35
+ * @property {string} object - Type of object, e.g., 'file'.
36
+ * @property {string} purpose - Purpose of the file.
37
+ */
38
+
39
+ const API_BASE = 'https://api.x.ai/v1';
40
+ /**
41
+ * Is buffer filled with text?
42
+ * @param {Buffer} buffer
43
+ * @returns {boolean}
44
+ */
45
+ function isTextBuffer(buffer) {
46
+ const text = buffer.toString('utf8');
47
+ const nonPrintable = text.replace(/[\x20-\x7E\t\r\n]/g, '').length;
48
+ return nonPrintable / buffer.length < 0.3; // 30% threshold
49
+ }
50
+ /**
51
+ * Get the default headers for API
52
+ * @returns {object}
53
+ */
54
+ const getAPIHeaders = () => {
55
+ if (!process.env['XAIKEY']) {
56
+ throw new Error('Missing XAIKEY! export XAIKEY=<XAIKEY>')
57
+ }
58
+ const KEY = process.env['XAIKEY'];
59
+ return {
60
+ 'Authorization': `Bearer ${KEY}`
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get the default headers for Management API
66
+ * @returns {object}
67
+ */
68
+ const getManagementHeaders = () => {
69
+ const KEY = process.env['XAI_MANAGEMENT_KEY'] || process.env['XAIKEY'];
70
+ if (!KEY) {
71
+ throw new Error('Missing XAI_MANAGEMENT_KEY or XAIKEY!')
72
+ }
73
+ return {
74
+ 'Authorization': `Bearer ${KEY}`
75
+ }
76
+ }
77
+
78
+ /**
79
+ * List Files
80
+ * @param {number} limit
81
+ * @param {'asc'|'desc'} order
82
+ * @param {'created_at'|'filename'| 'size'} sort_by
83
+ * @param {string} page (pagination_token)
84
+ * @retuns {Promise<fileList>}
85
+ */
86
+ async function list(limit = 100, order = 'asc', sort_by = 'size', page = '') {
87
+ const url = `${API_BASE}/files?limit=${limit}&order=${order}&sort_by=${sort_by}&pagination_token=${page}`;
88
+ const headers = getAPIHeaders();
89
+ const res = await doRequest(url,
90
+ 'GET',
91
+ headers
92
+ );
93
+ if (res.status !== 200) {
94
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
95
+ }
96
+ return res.response;
97
+ }
98
+ /**
99
+ * Upload a file
100
+ * When content is filled this will be used as file content
101
+ * @param {string} filePath
102
+ * @param {string} [content] - optional content for a non existing file
103
+ * @returns {Promise<File>} file_id
104
+ */
105
+ async function upload(filePath, content) {
106
+ const url = `${API_BASE}/files`;
107
+ const headers = getAPIHeaders();
108
+ const formData = new FormData();
109
+ let buffer;
110
+ if (!content) {
111
+ buffer = fs.readFileSync(filePath);
112
+ } else {
113
+ buffer = Buffer.from(content, 'utf8');
114
+ }
115
+ if (buffer.length > 50331648) {
116
+ throw new Error('File too large: exceeds 48 MB limit');
117
+ }
118
+ if (!isTextBuffer(buffer)) {
119
+ throw new Error('File is not plain text based');
120
+ }
121
+ formData.append('file', new Blob([buffer]), filePath);
122
+ const result = await doRequest(url,
123
+ 'POST',
124
+ headers,
125
+ formData
126
+ );
127
+ if (result.status < 200 || result.status >= 300) {
128
+ throw new Error(`${result.status}: ${result.response}`);
129
+ }
130
+ const data = result.response;
131
+ return data;
132
+ }
133
+
134
+ /**
135
+ * Get file content
136
+ * @param {string} fileId
137
+ * @returns {Promise<*>}
138
+ */
139
+ async function get(fileId) {
140
+ const url = `${API_BASE}/files/${fileId}/content`;
141
+ const headers = getAPIHeaders();
142
+ const res = await doRequest(url,
143
+ 'GET',
144
+ headers
145
+ );
146
+ if (res.status !== 200) {
147
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
148
+ }
149
+ if (res.responseType === 'blob') {
150
+ return res.response.text();
151
+ }
152
+ return res.response;
153
+ }
154
+
155
+ /**
156
+ * Get file meta data
157
+ * @param {string} fileId
158
+ * @returns {Promise<File>}
159
+ */
160
+ async function getMeta(fileId) {
161
+ const url = `${API_BASE}/files/${fileId}`;
162
+ const headers = getAPIHeaders();
163
+ const res = await doRequest(url,
164
+ 'GET',
165
+ headers
166
+ );
167
+ if (res.status !== 200) {
168
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
169
+ }
170
+ return res.response;
171
+ }
172
+
173
+ /**
174
+ * Remove a file
175
+ * @param {string} fileId
176
+ * @returns {Promise<void>}
177
+ */
178
+ async function rm(fileId) {
179
+ const url = `${API_BASE}/files/${fileId}`;
180
+ const headers = getAPIHeaders();
181
+ const res = await doRequest(url,
182
+ 'DELETE',
183
+ headers
184
+ );
185
+ if (res.status !== 200) {
186
+ throw new Error(`${res.status}: ${JSON.stringify(res.response)}`);
187
+ }
188
+ }
189
+
190
+
191
+ /**
192
+ * Get all on-line files
193
+ * @returns {Promise<Map<string, File>>}
194
+ *
195
+ */
196
+ const getStorage = async (page = '') => {
197
+ if (!page) page = '';
198
+ const lst = await list(100, 'asc', 'size', page);
199
+ let i = 0;
200
+ const len = lst.data.length;
201
+ for (; i < len; i++) {
202
+ // console.log(list.data[i]);
203
+ fileStorage.set(lst.data[i].filename, lst.data[i]);
204
+ }
205
+ if (lst.pagination_token) {
206
+ return await getStorage(lst.pagination_token);
207
+ }
208
+ return fileStorage;
209
+ }
210
+
211
+ export default {
212
+ list,
213
+ upload,
214
+ get,
215
+ getMeta,
216
+ rm,
217
+ storage: getStorage
218
+ };