vesper-wizard 2.0.5 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +300 -37
  3. package/build/cache/cdn.js +34 -0
  4. package/build/cache/service.js +63 -0
  5. package/build/cleaning/cleaner.js +81 -0
  6. package/build/cleaning/evaluator.js +89 -0
  7. package/build/cleaning/executor.js +62 -0
  8. package/build/cleaning/exporter.js +87 -0
  9. package/build/cleaning/planner.js +127 -0
  10. package/build/cleaning/rules.js +57 -0
  11. package/build/cleaning/types.js +1 -0
  12. package/build/cloud/adapters/local.js +37 -0
  13. package/build/cloud/adapters/s3.js +24 -0
  14. package/build/cloud/adapters/supabase.js +49 -0
  15. package/build/cloud/storage-manager.js +26 -0
  16. package/build/cloud/types.js +1 -0
  17. package/build/compliance/service.js +73 -0
  18. package/build/compliance/store.js +80 -0
  19. package/build/compliance/types.js +1 -0
  20. package/build/config/config-manager.js +221 -0
  21. package/build/config/secure-keys.js +51 -0
  22. package/build/config/user-config.js +48 -0
  23. package/build/data/processing-worker.js +23 -0
  24. package/build/data/streaming.js +38 -0
  25. package/build/data/worker-pool.js +39 -0
  26. package/build/export/exporter.js +69 -0
  27. package/build/export/packager.js +100 -0
  28. package/build/export/types.js +1 -0
  29. package/build/fusion/aligner.js +56 -0
  30. package/build/fusion/deduplicator.js +69 -0
  31. package/build/fusion/engine.js +69 -0
  32. package/build/fusion/harmonizer.js +39 -0
  33. package/build/fusion/orchestrator.js +86 -0
  34. package/build/fusion/types.js +1 -0
  35. package/build/gateway/unified-dataset-gateway.js +409 -0
  36. package/build/index.js +2704 -0
  37. package/build/ingestion/hf-downloader.js +171 -0
  38. package/build/ingestion/ingestor.js +271 -0
  39. package/build/ingestion/kaggle-downloader.js +102 -0
  40. package/build/install/install-service.js +41 -0
  41. package/build/jobs/manager.js +136 -0
  42. package/build/jobs/queue.js +59 -0
  43. package/build/jobs/types.js +1 -0
  44. package/build/lib/supabase.js +3 -0
  45. package/build/metadata/dataworld-source.js +89 -0
  46. package/build/metadata/domain.js +147 -0
  47. package/build/metadata/github-scraper.js +47 -0
  48. package/build/metadata/institutional-scrapers.js +49 -0
  49. package/build/metadata/kaggle-scraper.js +182 -0
  50. package/build/metadata/kaggle-source.js +70 -0
  51. package/build/metadata/license.js +68 -0
  52. package/build/metadata/monitoring-service.js +107 -0
  53. package/build/metadata/monitoring-store.js +78 -0
  54. package/build/metadata/monitoring-types.js +1 -0
  55. package/build/metadata/openml-source.js +87 -0
  56. package/build/metadata/quality.js +48 -0
  57. package/build/metadata/rate-limiter.js +128 -0
  58. package/build/metadata/scraper.js +377 -0
  59. package/build/metadata/store.js +340 -0
  60. package/build/metadata/types.js +1 -0
  61. package/build/metadata/uci-scraper.js +49 -0
  62. package/build/monitoring/observability.js +76 -0
  63. package/build/preparation/target-detector.js +75 -0
  64. package/build/python/__pycache__/config.cpython-312.pyc +0 -0
  65. package/build/python/asset_downloader_engine.py +92 -0
  66. package/build/python/cleaner.py +226 -0
  67. package/build/python/config.py +263 -0
  68. package/build/python/dataworld_engine.py +208 -0
  69. package/build/python/export_engine.py +243 -0
  70. package/build/python/framework_adapters.py +100 -0
  71. package/build/python/fusion_engine.py +368 -0
  72. package/build/python/github_adapter.py +106 -0
  73. package/build/python/hf_fallback.py +298 -0
  74. package/build/python/image_engine.py +86 -0
  75. package/build/python/kaggle_engine.py +295 -0
  76. package/build/python/media_engine.py +133 -0
  77. package/build/python/nasa_adapter.py +82 -0
  78. package/build/python/openml_engine.py +146 -0
  79. package/build/python/quality_engine.py +267 -0
  80. package/build/python/row_count.py +54 -0
  81. package/build/python/splitter_engine.py +283 -0
  82. package/build/python/target_engine.py +154 -0
  83. package/build/python/test_framework_adapters.py +61 -0
  84. package/build/python/test_fusion_engine.py +89 -0
  85. package/build/python/uci_adapter.py +94 -0
  86. package/build/python/vesper/__init__.py +1 -0
  87. package/build/python/vesper/__pycache__/__init__.cpython-312.pyc +0 -0
  88. package/build/python/vesper/core/__init__.py +1 -0
  89. package/build/python/vesper/core/__pycache__/__init__.cpython-312.pyc +0 -0
  90. package/build/python/vesper/core/__pycache__/asset_downloader.cpython-312.pyc +0 -0
  91. package/build/python/vesper/core/__pycache__/download_recipe.cpython-312.pyc +0 -0
  92. package/build/python/vesper/core/asset_downloader.py +675 -0
  93. package/build/python/vesper/core/download_recipe.py +104 -0
  94. package/build/python/worldbank_adapter.py +99 -0
  95. package/build/quality/analyzer.js +93 -0
  96. package/build/quality/image-analyzer.js +114 -0
  97. package/build/quality/media-analyzer.js +115 -0
  98. package/build/quality/quality-orchestrator.js +162 -0
  99. package/build/quality/types.js +1 -0
  100. package/build/scripts/build-index.js +54 -0
  101. package/build/scripts/check-db.js +73 -0
  102. package/build/scripts/check-jobs.js +24 -0
  103. package/build/scripts/check-naruto.js +17 -0
  104. package/build/scripts/cleanup-kaggle.js +41 -0
  105. package/build/scripts/demo-full-pipeline.js +62 -0
  106. package/build/scripts/demo-ui.js +58 -0
  107. package/build/scripts/e2e-demo.js +72 -0
  108. package/build/scripts/massive-scrape.js +103 -0
  109. package/build/scripts/ops-dashboard.js +33 -0
  110. package/build/scripts/repro-bug.js +37 -0
  111. package/build/scripts/repro-export-bug.js +56 -0
  112. package/build/scripts/scrape-metadata.js +100 -0
  113. package/build/scripts/search-cli.js +26 -0
  114. package/build/scripts/test-bias.js +45 -0
  115. package/build/scripts/test-caching.js +51 -0
  116. package/build/scripts/test-cleaning.js +76 -0
  117. package/build/scripts/test-cloud-storage.js +48 -0
  118. package/build/scripts/test-compliance.js +58 -0
  119. package/build/scripts/test-conversion.js +64 -0
  120. package/build/scripts/test-custom-rules.js +58 -0
  121. package/build/scripts/test-db-opt.js +63 -0
  122. package/build/scripts/test-export-custom.js +33 -0
  123. package/build/scripts/test-exporter.js +53 -0
  124. package/build/scripts/test-fusion.js +61 -0
  125. package/build/scripts/test-github.js +27 -0
  126. package/build/scripts/test-group-split.js +52 -0
  127. package/build/scripts/test-hf-download.js +29 -0
  128. package/build/scripts/test-holdout-manager.js +61 -0
  129. package/build/scripts/test-hybrid-search.js +41 -0
  130. package/build/scripts/test-image-analysis.js +50 -0
  131. package/build/scripts/test-ingestion-infra.js +39 -0
  132. package/build/scripts/test-install.js +40 -0
  133. package/build/scripts/test-institutional.js +26 -0
  134. package/build/scripts/test-integrity.js +41 -0
  135. package/build/scripts/test-jit.js +42 -0
  136. package/build/scripts/test-job-queue.js +62 -0
  137. package/build/scripts/test-kaggle-download.js +34 -0
  138. package/build/scripts/test-large-data.js +50 -0
  139. package/build/scripts/test-mcp-v5.js +74 -0
  140. package/build/scripts/test-media-analysis.js +61 -0
  141. package/build/scripts/test-monitoring.js +91 -0
  142. package/build/scripts/test-observability.js +106 -0
  143. package/build/scripts/test-packager.js +55 -0
  144. package/build/scripts/test-pipeline.js +50 -0
  145. package/build/scripts/test-planning.js +64 -0
  146. package/build/scripts/test-privacy.js +38 -0
  147. package/build/scripts/test-production-sync.js +36 -0
  148. package/build/scripts/test-quality.js +43 -0
  149. package/build/scripts/test-robust-ingestion.js +41 -0
  150. package/build/scripts/test-schema.js +45 -0
  151. package/build/scripts/test-split-validation.js +40 -0
  152. package/build/scripts/test-splitter.js +93 -0
  153. package/build/scripts/test-target-detector.js +29 -0
  154. package/build/scripts/test-uci.js +27 -0
  155. package/build/scripts/test-unified-quality.js +86 -0
  156. package/build/scripts/test-write.js +14 -0
  157. package/build/scripts/verify-integration.js +57 -0
  158. package/build/scripts/verify-priority.js +33 -0
  159. package/build/search/embedder.js +34 -0
  160. package/build/search/engine.js +152 -0
  161. package/build/search/jit-orchestrator.js +258 -0
  162. package/build/search/vector-store.js +123 -0
  163. package/build/splitting/splitter.js +82 -0
  164. package/build/splitting/types.js +1 -0
  165. package/build/tools/formatter.js +251 -0
  166. package/build/utils/downloader.js +52 -0
  167. package/build/utils/selector.js +69 -0
  168. package/mcp-config-template.json +18 -0
  169. package/package.json +101 -29
  170. package/scripts/postinstall.cjs +114 -0
  171. package/scripts/preindex_registry.cjs +157 -0
  172. package/scripts/refresh-index.cjs +87 -0
  173. package/{wizard.js → scripts/wizard.js} +99 -21
  174. package/src/python/__pycache__/config.cpython-312.pyc +0 -0
  175. package/src/python/__pycache__/export_engine.cpython-312.pyc +0 -0
  176. package/src/python/__pycache__/framework_adapters.cpython-312.pyc +0 -0
  177. package/src/python/__pycache__/fusion_engine.cpython-312.pyc +0 -0
  178. package/src/python/__pycache__/kaggle_engine.cpython-312.pyc +0 -0
  179. package/src/python/asset_downloader_engine.py +92 -0
  180. package/src/python/cleaner.py +226 -0
  181. package/src/python/config.py +263 -0
  182. package/src/python/dataworld_engine.py +208 -0
  183. package/src/python/export_engine.py +243 -0
  184. package/src/python/framework_adapters.py +100 -0
  185. package/src/python/fusion_engine.py +368 -0
  186. package/src/python/github_adapter.py +106 -0
  187. package/src/python/hf_fallback.py +298 -0
  188. package/src/python/image_engine.py +86 -0
  189. package/src/python/kaggle_engine.py +295 -0
  190. package/src/python/media_engine.py +133 -0
  191. package/src/python/nasa_adapter.py +82 -0
  192. package/src/python/openml_engine.py +146 -0
  193. package/src/python/quality_engine.py +267 -0
  194. package/src/python/row_count.py +54 -0
  195. package/src/python/splitter_engine.py +283 -0
  196. package/src/python/target_engine.py +154 -0
  197. package/src/python/test_framework_adapters.py +61 -0
  198. package/src/python/test_fusion_engine.py +89 -0
  199. package/src/python/uci_adapter.py +94 -0
  200. package/src/python/vesper/__init__.py +1 -0
  201. package/src/python/vesper/core/__init__.py +1 -0
  202. package/src/python/vesper/core/asset_downloader.py +675 -0
  203. package/src/python/vesper/core/download_recipe.py +104 -0
  204. package/src/python/worldbank_adapter.py +99 -0
  205. package/vesper-mcp-config.json +0 -6
@@ -0,0 +1,221 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ export class ConfigManager {
5
+ getHomeDir() {
6
+ return os.homedir();
7
+ }
8
+ /**
9
+ * All known coding agent configuration paths.
10
+ * Based on the `add-mcp` standard used by the MCP ecosystem.
11
+ */
12
+ getAllAgents() {
13
+ const home = this.getHomeDir();
14
+ const isWin = process.platform === "win32";
15
+ const isMac = process.platform === "darwin";
16
+ const appData = process.env.APPDATA || path.join(home, "AppData", "Roaming");
17
+ const agents = [
18
+ // Claude Code: ~/.claude.json
19
+ {
20
+ name: "Claude Code",
21
+ path: path.join(home, ".claude.json"),
22
+ format: "json-mcpServers",
23
+ serverType: "stdio",
24
+ },
25
+ // Claude Desktop: %APPDATA%/Claude/claude_desktop_config.json
26
+ {
27
+ name: "Claude Desktop",
28
+ path: isWin
29
+ ? path.join(appData, "Claude", "claude_desktop_config.json")
30
+ : isMac
31
+ ? path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")
32
+ : path.join(home, ".config", "claude", "claude_desktop_config.json"),
33
+ format: "json-mcpServers",
34
+ serverType: "stdio",
35
+ },
36
+ // Cursor: ~/.cursor/mcp.json
37
+ {
38
+ name: "Cursor",
39
+ path: path.join(home, ".cursor", "mcp.json"),
40
+ format: "json-mcpServers",
41
+ serverType: "stdio",
42
+ },
43
+ // VS Code: %APPDATA%/Code/User/mcp.json (uses "servers" key, NOT "mcpServers")
44
+ {
45
+ name: "VS Code",
46
+ path: isWin
47
+ ? path.join(appData, "Code", "User", "mcp.json")
48
+ : isMac
49
+ ? path.join(home, "Library", "Application Support", "Code", "User", "mcp.json")
50
+ : path.join(home, ".config", "Code", "User", "mcp.json"),
51
+ format: "json-servers",
52
+ serverType: "stdio",
53
+ },
54
+ // Codex: ~/.codex/config.toml
55
+ {
56
+ name: "Codex",
57
+ path: path.join(home, ".codex", "config.toml"),
58
+ format: "toml",
59
+ serverType: "stdio",
60
+ },
61
+ // Antigravity / Gemini CLI: ~/.gemini/settings.json
62
+ {
63
+ name: "Antigravity",
64
+ path: path.join(home, ".gemini", "settings.json"),
65
+ format: "json-mcpServers",
66
+ serverType: "stdio",
67
+ },
68
+ ];
69
+ return agents;
70
+ }
71
+ /**
72
+ * Detect which agents are installed on this machine.
73
+ */
74
+ detectIDEs() {
75
+ const agents = this.getAllAgents();
76
+ const detected = [];
77
+ for (const agent of agents) {
78
+ const configDir = path.dirname(agent.path);
79
+ const configExists = fs.existsSync(agent.path);
80
+ const dirExists = fs.existsSync(configDir);
81
+ if (configExists || dirExists) {
82
+ detected.push(agent);
83
+ }
84
+ }
85
+ return detected;
86
+ }
87
+ /**
88
+ * Build the server config object for Vesper.
89
+ */
90
+ getServerConfig() {
91
+ const isWin = process.platform === "win32";
92
+ return {
93
+ command: isWin ? "npx.cmd" : "npx",
94
+ args: ["-y", "-p", "@vespermcp/mcp-server@latest", "vespermcp"],
95
+ };
96
+ }
97
+ /**
98
+ * Install Vesper to a single agent configuration.
99
+ */
100
+ async installTo(agent) {
101
+ try {
102
+ console.log(`[Vesper Setup] Installing to ${agent.name}...`);
103
+ if (agent.format === "toml") {
104
+ return this.installToToml(agent);
105
+ }
106
+ // JSON-based agents
107
+ let config = {};
108
+ if (fs.existsSync(agent.path)) {
109
+ try {
110
+ const content = fs.readFileSync(agent.path, "utf-8").trim();
111
+ config = content ? JSON.parse(content) : {};
112
+ }
113
+ catch (e) {
114
+ console.warn(`[Vesper Setup] Could not parse ${agent.path}, starting fresh`);
115
+ config = {};
116
+ }
117
+ }
118
+ else {
119
+ fs.mkdirSync(path.dirname(agent.path), { recursive: true });
120
+ }
121
+ const vesperConfig = this.getServerConfig();
122
+ const serverEntry = {
123
+ command: vesperConfig.command,
124
+ args: vesperConfig.args,
125
+ };
126
+ // Determine the correct key based on agent format
127
+ if (agent.format === "json-servers") {
128
+ // VS Code native MCP format: { "servers": { "vesper": { "type": "stdio", "command": ..., "args": ... } } }
129
+ if (!config.servers)
130
+ config.servers = {};
131
+ config.servers["vesper"] = {
132
+ type: "stdio",
133
+ ...serverEntry,
134
+ };
135
+ console.log(` ✓ Added to "servers" key (VS Code native format)`);
136
+ }
137
+ else {
138
+ // Standard mcpServers format: { "mcpServers": { "vesper": { "command": ..., "args": ... } } }
139
+ if (!config.mcpServers)
140
+ config.mcpServers = {};
141
+ config.mcpServers["vesper"] = serverEntry;
142
+ console.log(` ✓ Added to "mcpServers" key`);
143
+ }
144
+ fs.writeFileSync(agent.path, JSON.stringify(config, null, 2), "utf8");
145
+ const size = fs.statSync(agent.path).size;
146
+ console.log(` ✓ ${agent.name}: ${agent.path} (${size} bytes)`);
147
+ return true;
148
+ }
149
+ catch (error) {
150
+ console.error(` ✗ Failed to install to ${agent.name}:`, error);
151
+ return false;
152
+ }
153
+ }
154
+ /**
155
+ * Install to TOML-based agents (Codex).
156
+ */
157
+ installToToml(agent) {
158
+ try {
159
+ let content = "";
160
+ if (fs.existsSync(agent.path)) {
161
+ content = fs.readFileSync(agent.path, "utf-8");
162
+ }
163
+ else {
164
+ fs.mkdirSync(path.dirname(agent.path), { recursive: true });
165
+ }
166
+ // Check if vesper is already configured
167
+ if (content.includes("[mcp_servers.vesper]")) {
168
+ console.log(` ✓ ${agent.name}: Already configured`);
169
+ return true;
170
+ }
171
+ const vesperConfig = this.getServerConfig();
172
+ const isWin = process.platform === "win32";
173
+ const tomlEntry = `
174
+ [mcp_servers.vesper]
175
+ command = "${vesperConfig.command}"
176
+ args = [${vesperConfig.args.map(a => `"${a}"`).join(", ")}]
177
+ `;
178
+ content += tomlEntry;
179
+ fs.writeFileSync(agent.path, content, "utf8");
180
+ console.log(` ✓ ${agent.name}: ${agent.path}`);
181
+ return true;
182
+ }
183
+ catch (error) {
184
+ console.error(` ✗ Failed to install to ${agent.name}:`, error);
185
+ return false;
186
+ }
187
+ }
188
+ /**
189
+ * Install Vesper to ALL detected agents automatically.
190
+ */
191
+ async installToAll() {
192
+ const agents = this.detectIDEs();
193
+ const success = [];
194
+ const failed = [];
195
+ console.log(`\n📦 Installing Vesper to ${agents.length} detected agents...\n`);
196
+ for (const agent of agents) {
197
+ const result = await this.installTo(agent);
198
+ if (result) {
199
+ success.push(agent.name);
200
+ }
201
+ else {
202
+ failed.push(agent.name);
203
+ }
204
+ }
205
+ console.log(`\n┌─────────────────────────────────────────────┐`);
206
+ console.log(`│ Installation Summary │`);
207
+ console.log(`├─────────────────────────────────────────────┤`);
208
+ if (success.length > 0) {
209
+ for (const name of success) {
210
+ console.log(`│ ✓ ${name.padEnd(40)}│`);
211
+ }
212
+ }
213
+ if (failed.length > 0) {
214
+ for (const name of failed) {
215
+ console.log(`│ ✗ ${name.padEnd(40)}│`);
216
+ }
217
+ }
218
+ console.log(`└─────────────────────────────────────────────┘\n`);
219
+ return { success, failed };
220
+ }
221
+ }
@@ -0,0 +1,51 @@
1
+ import { spawnSync } from "child_process";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ export class SecureKeysManager {
5
+ pythonPath = "python";
6
+ scriptPath;
7
+ constructor(buildDir = process.cwd()) {
8
+ const homeDir = process.env.HOME || process.env.USERPROFILE || buildDir;
9
+ const dataRoot = path.join(homeDir, ".vesper");
10
+ const scriptPath0 = path.resolve(dataRoot, "python", "config.py");
11
+ const scriptPath1 = path.resolve(buildDir, "python", "config.py");
12
+ const scriptPath2 = path.resolve(buildDir, "..", "src", "python", "config.py");
13
+ if (fs.existsSync(scriptPath0))
14
+ this.scriptPath = scriptPath0;
15
+ else if (fs.existsSync(scriptPath1))
16
+ this.scriptPath = scriptPath1;
17
+ else if (fs.existsSync(scriptPath2))
18
+ this.scriptPath = scriptPath2;
19
+ else
20
+ this.scriptPath = scriptPath0;
21
+ if (process.platform === "win32")
22
+ this.pythonPath = "py";
23
+ }
24
+ getAll() {
25
+ const result = spawnSync(this.pythonPath, [this.scriptPath, "all"], { encoding: "utf-8" });
26
+ if (result.status !== 0)
27
+ return {};
28
+ try {
29
+ const parsed = JSON.parse(result.stdout || "{}");
30
+ if (!parsed.ok)
31
+ return {};
32
+ return (parsed.data || {});
33
+ }
34
+ catch {
35
+ return {};
36
+ }
37
+ }
38
+ set(name, value) {
39
+ const result = spawnSync(this.pythonPath, [this.scriptPath, "set", name, value], { encoding: "utf-8" });
40
+ if (result.status !== 0) {
41
+ return { ok: false, error: result.stderr || "Failed to store key" };
42
+ }
43
+ try {
44
+ const parsed = JSON.parse(result.stdout || "{}");
45
+ return { ok: !!parsed.ok, method: parsed.method, error: parsed.error };
46
+ }
47
+ catch {
48
+ return { ok: false, error: "Invalid response from config helper" };
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,48 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ export class UserConfigStore {
5
+ configPath;
6
+ constructor(dataRoot) {
7
+ const homeDir = os.homedir() || process.env.HOME || process.env.USERPROFILE || process.cwd();
8
+ const root = dataRoot || path.join(homeDir, ".vesper");
9
+ if (!fs.existsSync(root)) {
10
+ fs.mkdirSync(root, { recursive: true });
11
+ }
12
+ this.configPath = path.join(root, "config.json");
13
+ }
14
+ getPath() {
15
+ return this.configPath;
16
+ }
17
+ load() {
18
+ if (!fs.existsSync(this.configPath))
19
+ return {};
20
+ try {
21
+ const raw = fs.readFileSync(this.configPath, "utf8");
22
+ const parsed = JSON.parse(raw);
23
+ return parsed || {};
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ save(config) {
30
+ const parent = path.dirname(this.configPath);
31
+ if (!fs.existsSync(parent))
32
+ fs.mkdirSync(parent, { recursive: true });
33
+ fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf8");
34
+ }
35
+ setKaggleCredentials(username, key) {
36
+ const cfg = this.load();
37
+ cfg.kaggle = { username, key };
38
+ this.save(cfg);
39
+ }
40
+ getKaggleCredentials() {
41
+ const cfg = this.load();
42
+ return cfg.kaggle || {};
43
+ }
44
+ hasKaggleCredentials() {
45
+ const creds = this.getKaggleCredentials();
46
+ return !!(creds.username && creds.key);
47
+ }
48
+ }
@@ -0,0 +1,23 @@
1
+ import { parentPort } from "worker_threads";
2
+ if (parentPort) {
3
+ parentPort.on("message", (data) => {
4
+ const { records, task, params } = data;
5
+ let results = [];
6
+ switch (task) {
7
+ case "transform":
8
+ results = records.map(r => {
9
+ // Simulated transformation (e.g. alignment/harmonization logic)
10
+ const updated = { ...r, processed_at: new Date().toISOString() };
11
+ // Complex CPU bound work simulation
12
+ let sum = 0;
13
+ for (let i = 0; i < 1000; i++)
14
+ sum += i;
15
+ return updated;
16
+ });
17
+ break;
18
+ default:
19
+ results = records;
20
+ }
21
+ parentPort?.postMessage(results);
22
+ });
23
+ }
@@ -0,0 +1,38 @@
1
+ export class StreamProcessor {
2
+ /**
3
+ * Processes a readable stream in chunks.
4
+ */
5
+ static async processInChunks(stream, options, processor) {
6
+ let chunk = [];
7
+ const results = [];
8
+ let totalProcessed = 0;
9
+ for await (const record of stream) {
10
+ // Sampling logic
11
+ if (options.samplePercentage !== undefined) {
12
+ if (Math.random() * 100 > options.samplePercentage) {
13
+ continue;
14
+ }
15
+ }
16
+ chunk.push(record);
17
+ if (chunk.length >= options.chunkSize) {
18
+ const processedChunk = await processor(chunk);
19
+ results.push(...processedChunk);
20
+ totalProcessed += chunk.length;
21
+ chunk = [];
22
+ }
23
+ }
24
+ // Process remaining
25
+ if (chunk.length > 0) {
26
+ const processedChunk = await processor(chunk);
27
+ results.push(...processedChunk);
28
+ totalProcessed += chunk.length;
29
+ }
30
+ return results;
31
+ }
32
+ /**
33
+ * Creates a sampler stream that only emits a percentage of records.
34
+ */
35
+ static createSampler(percentage) {
36
+ return () => Math.random() * 100 <= percentage;
37
+ }
38
+ }
@@ -0,0 +1,39 @@
1
+ import { Worker } from "worker_threads";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ export class WorkerPool {
6
+ poolSize;
7
+ workers = [];
8
+ queue = [];
9
+ activeWorkers = 0;
10
+ constructor(poolSize = 4) {
11
+ this.poolSize = poolSize;
12
+ const workerPath = path.resolve(__dirname, "processing-worker.js");
13
+ // Note: In development with tsx, we might need to handle .ts vs .js
14
+ }
15
+ async process(records, task = "transform") {
16
+ return new Promise((resolve, reject) => {
17
+ const worker = new Worker(new URL("./processing-worker.ts", import.meta.url), {
18
+ execArgv: ["--import", "tsx"]
19
+ });
20
+ worker.postMessage({ records, task });
21
+ worker.on("message", (results) => {
22
+ worker.terminate();
23
+ resolve(results);
24
+ });
25
+ worker.on("error", reject);
26
+ });
27
+ }
28
+ /**
29
+ * Splits records into chunks and processes them in parallel across workers.
30
+ */
31
+ async processParallel(records, chunkSize) {
32
+ const chunks = [];
33
+ for (let i = 0; i < records.length; i += chunkSize) {
34
+ chunks.push(records.slice(i, i + chunkSize));
35
+ }
36
+ const results = await Promise.all(chunks.map(chunk => this.process(chunk)));
37
+ return results.flat();
38
+ }
39
+ }
@@ -0,0 +1,69 @@
1
+ import { spawn } from "child_process";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ export class DataExporter {
5
+ pythonPath = "python";
6
+ scriptPath;
7
+ constructor(buildDir = process.cwd()) {
8
+ const homeDir = process.env.HOME || process.env.USERPROFILE || buildDir;
9
+ const dataRoot = path.join(homeDir, ".vesper");
10
+ const scriptPath0 = path.resolve(dataRoot, "python", "export_engine.py");
11
+ const scriptPath1 = path.resolve(buildDir, "python", "export_engine.py");
12
+ const scriptPath2 = path.resolve(buildDir, "..", "src", "python", "export_engine.py");
13
+ const scriptPath3 = path.resolve(buildDir, "..", "python", "export_engine.py");
14
+ if (fs.existsSync(scriptPath0)) {
15
+ this.scriptPath = scriptPath0;
16
+ }
17
+ else if (fs.existsSync(scriptPath1)) {
18
+ this.scriptPath = scriptPath1;
19
+ }
20
+ else if (fs.existsSync(scriptPath2)) {
21
+ this.scriptPath = scriptPath2;
22
+ }
23
+ else if (fs.existsSync(scriptPath3)) {
24
+ this.scriptPath = scriptPath3;
25
+ }
26
+ else {
27
+ this.scriptPath = scriptPath0;
28
+ }
29
+ // Detect Python command
30
+ if (process.platform === "win32") {
31
+ this.pythonPath = "py";
32
+ }
33
+ }
34
+ /**
35
+ * Exports a dataset file to a specified format
36
+ */
37
+ async export(inputFile, outputFile, format, options = {}) {
38
+ return new Promise((resolve, reject) => {
39
+ if (!fs.existsSync(inputFile)) {
40
+ reject(new Error(`Input file not found: ${inputFile}`));
41
+ return;
42
+ }
43
+ const args = [this.scriptPath, inputFile, outputFile, format, JSON.stringify(options)];
44
+ const process = spawn(this.pythonPath, args);
45
+ let stdout = "";
46
+ let stderr = "";
47
+ process.stdout.on("data", (data) => stdout += data.toString());
48
+ process.stderr.on("data", (data) => stderr += data.toString());
49
+ process.on("close", (code) => {
50
+ if (code !== 0) {
51
+ reject(new Error(`Export failed: ${stderr || stdout}`));
52
+ return;
53
+ }
54
+ try {
55
+ const result = JSON.parse(stdout);
56
+ if (result.error) {
57
+ reject(new Error(result.error));
58
+ }
59
+ else {
60
+ resolve(result);
61
+ }
62
+ }
63
+ catch (e) {
64
+ reject(new Error(`Failed to parse export output: ${stdout}`));
65
+ }
66
+ });
67
+ });
68
+ }
69
+ }
@@ -0,0 +1,100 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import crypto from "crypto";
4
+ /**
5
+ * MetadataPackager bundles data files with metadata and quality reports.
6
+ * Follows a simplified Frictionless Data Package standard.
7
+ */
8
+ export class MetadataPackager {
9
+ /**
10
+ * Creates a data package in the target directory
11
+ */
12
+ async createPackage(targetDir, dataFiles, metadata, extraArtifacts) {
13
+ try {
14
+ if (!fs.existsSync(targetDir)) {
15
+ fs.mkdirSync(targetDir, { recursive: true });
16
+ }
17
+ const resources = [];
18
+ // 1. Process Data Files
19
+ for (const file of dataFiles) {
20
+ if (!fs.existsSync(file.path)) {
21
+ throw new Error(`Data file not found: ${file.path}`);
22
+ }
23
+ const fileName = path.basename(file.path);
24
+ const destPath = path.join(targetDir, fileName);
25
+ // Copy file to package dir
26
+ fs.copyFileSync(file.path, destPath);
27
+ // Compute hash and size
28
+ const fileBuffer = fs.readFileSync(destPath);
29
+ const hash = crypto.createHash("sha256").update(fileBuffer).digest("hex");
30
+ const stats = fs.statSync(destPath);
31
+ resources.push({
32
+ name: file.name,
33
+ path: fileName,
34
+ format: file.format,
35
+ mediatype: this.getMediaType(file.format),
36
+ bytes: stats.size,
37
+ hash: `sha256:${hash}`
38
+ });
39
+ }
40
+ // 2. Add Extra Artifacts
41
+ if (extraArtifacts?.qualityReport) {
42
+ const qrPath = path.join(targetDir, "quality_report.json");
43
+ fs.writeFileSync(qrPath, JSON.stringify(extraArtifacts.qualityReport, null, 2));
44
+ resources.push({
45
+ name: "quality-report",
46
+ path: "quality_report.json",
47
+ format: "json"
48
+ });
49
+ }
50
+ if (extraArtifacts?.cleaningLog) {
51
+ const clPath = path.join(targetDir, "cleaning_log.json");
52
+ fs.writeFileSync(clPath, JSON.stringify(extraArtifacts.cleaningLog, null, 2));
53
+ resources.push({
54
+ name: "cleaning-log",
55
+ path: "cleaning_log.json",
56
+ format: "json"
57
+ });
58
+ }
59
+ // 3. Generate Manifest (datapackage.json)
60
+ const manifest = {
61
+ profile: "tabular-data-package",
62
+ name: metadata.name.toLowerCase().replace(/\s+/g, "-"),
63
+ title: metadata.name,
64
+ description: metadata.description,
65
+ version: metadata.version,
66
+ homepage: "https://github.com/vesper-data",
67
+ license: metadata.license,
68
+ author: metadata.author,
69
+ keywords: metadata.tags,
70
+ created: new Date().toISOString(),
71
+ resources: resources
72
+ };
73
+ const manifestPath = path.join(targetDir, "datapackage.json");
74
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
75
+ return {
76
+ success: true,
77
+ packagePath: targetDir,
78
+ manifest: manifest
79
+ };
80
+ }
81
+ catch (e) {
82
+ return {
83
+ success: false,
84
+ packagePath: targetDir,
85
+ manifest: null,
86
+ error: e.message
87
+ };
88
+ }
89
+ }
90
+ getMediaType(format) {
91
+ switch (format.toLowerCase()) {
92
+ case "csv": return "text/csv";
93
+ case "parquet": return "application/x-parquet";
94
+ case "jsonl": return "application/x-jsonlines";
95
+ case "arrow": return "application/x-apache-arrow-file";
96
+ case "json": return "application/json";
97
+ default: return "application/octet-stream";
98
+ }
99
+ }
100
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ export class SchemaAligner {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ /**
7
+ * Finds common columns across datasets and creates a mapping to canonical names.
8
+ */
9
+ align(datasets) {
10
+ const alignment = {};
11
+ for (const ds of datasets) {
12
+ alignment[ds.id] = {};
13
+ if (!ds.columns)
14
+ continue;
15
+ for (const col of ds.columns) {
16
+ const canonicalName = this.getCanonicalName(col.name);
17
+ if (canonicalName) {
18
+ alignment[ds.id][col.name] = canonicalName;
19
+ }
20
+ }
21
+ }
22
+ return alignment;
23
+ }
24
+ /**
25
+ * Resolves the most specific common type for a canonical column.
26
+ */
27
+ resolveType(canonicalName, types) {
28
+ if (this.config.type_overrides[canonicalName]) {
29
+ return this.config.type_overrides[canonicalName];
30
+ }
31
+ const uniqueTypes = new Set(types.map(t => t.toLowerCase()));
32
+ if (uniqueTypes.size === 1) {
33
+ return Array.from(uniqueTypes)[0];
34
+ }
35
+ if (uniqueTypes.has("float") || uniqueTypes.has("number")) {
36
+ return "number";
37
+ }
38
+ return "string"; // Default to string for mixed types
39
+ }
40
+ getCanonicalName(colName) {
41
+ // Direct match with any canonical name
42
+ for (const canonical in this.config.column_aliases) {
43
+ if (canonical.toLowerCase() === colName.toLowerCase()) {
44
+ return canonical;
45
+ }
46
+ // Match with aliases
47
+ const aliases = this.config.column_aliases[canonical];
48
+ if (aliases.some((a) => a.toLowerCase() === colName.toLowerCase())) {
49
+ return canonical;
50
+ }
51
+ }
52
+ // If no alias, use the name itself if it's already one of the canonical names we expect
53
+ // This is a bit recursive, let's simplify.
54
+ return null;
55
+ }
56
+ }