create-hedgeboard 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +114 -36
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -51,7 +51,7 @@ function parseArgs() {
|
|
|
51
51
|
npx create-hedgeboard --key YOUR_API_KEY --dir ./my-workspace
|
|
52
52
|
|
|
53
53
|
Options:
|
|
54
|
-
--key, -k Your
|
|
54
|
+
--key, -k Your HB SEC Filing Data API key (optional)
|
|
55
55
|
--dir, -d Output directory (default: ./hedgeboard)
|
|
56
56
|
--help, -h Show this help
|
|
57
57
|
`);
|
|
@@ -60,12 +60,8 @@ function parseArgs() {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
if (!opts.key) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
" Get your API key at https://www.hedgeboardhq.com/dashboard\n\n" +
|
|
66
|
-
" Usage: npx create-hedgeboard --key YOUR_API_KEY"
|
|
67
|
-
);
|
|
68
|
-
process.exit(1);
|
|
63
|
+
// Key is optional — platform works without it
|
|
64
|
+
opts.key = "";
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
return opts;
|
|
@@ -97,18 +93,85 @@ function fetchBuffer(url, headers = {}) {
|
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
// ---------------------------------------------------------------------------
|
|
100
|
-
//
|
|
96
|
+
// Recursive directory copy (pure JS, no shell — replaces cp -r)
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
function copyDirRecursive(src, dest) {
|
|
100
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
101
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
102
|
+
const srcPath = path.join(src, entry.name);
|
|
103
|
+
const destPath = path.join(dest, entry.name);
|
|
104
|
+
if (entry.isDirectory()) {
|
|
105
|
+
copyDirRecursive(srcPath, destPath);
|
|
106
|
+
} else {
|
|
107
|
+
fs.copyFileSync(srcPath, destPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// ZIP extraction (pure JS — uses only Node built-in zlib, no dependencies)
|
|
101
114
|
// ---------------------------------------------------------------------------
|
|
102
115
|
|
|
103
116
|
function extractZip(zipBuffer, destDir) {
|
|
104
|
-
|
|
105
|
-
// For robustness in production, consider using a proper zip library
|
|
106
|
-
const { execSync } = require("child_process");
|
|
107
|
-
const tmpZip = path.join(require("os").tmpdir(), `hedgeboard-${Date.now()}.zip`);
|
|
108
|
-
fs.writeFileSync(tmpZip, zipBuffer);
|
|
117
|
+
const zlib = require("zlib");
|
|
109
118
|
fs.mkdirSync(destDir, { recursive: true });
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
|
|
120
|
+
// Parse ZIP central directory (from end-of-central-directory record)
|
|
121
|
+
const buf = Buffer.isBuffer(zipBuffer) ? zipBuffer : Buffer.from(zipBuffer);
|
|
122
|
+
let offset = 0;
|
|
123
|
+
|
|
124
|
+
while (offset < buf.length - 4) {
|
|
125
|
+
// Local file header signature = 0x04034b50
|
|
126
|
+
const sig = buf.readUInt32LE(offset);
|
|
127
|
+
if (sig !== 0x04034b50) break;
|
|
128
|
+
|
|
129
|
+
const method = buf.readUInt16LE(offset + 8);
|
|
130
|
+
const compSize = buf.readUInt32LE(offset + 18);
|
|
131
|
+
const uncompSize = buf.readUInt32LE(offset + 22);
|
|
132
|
+
const nameLen = buf.readUInt16LE(offset + 26);
|
|
133
|
+
const extraLen = buf.readUInt16LE(offset + 28);
|
|
134
|
+
const nameStart = offset + 30;
|
|
135
|
+
const name = buf.toString("utf8", nameStart, nameStart + nameLen);
|
|
136
|
+
const dataStart = nameStart + nameLen + extraLen;
|
|
137
|
+
|
|
138
|
+
offset = dataStart + compSize;
|
|
139
|
+
|
|
140
|
+
// Skip directories (with path traversal guard — P3-9)
|
|
141
|
+
if (name.endsWith("/")) {
|
|
142
|
+
const dirPath = path.join(destDir, name);
|
|
143
|
+
if (!dirPath.startsWith(path.resolve(destDir) + path.sep) &&
|
|
144
|
+
dirPath !== path.resolve(destDir)) {
|
|
145
|
+
console.warn(` Skipping ${name}: path traversal detected`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Path traversal guard — prevent ../../.bashrc attacks (P0-8)
|
|
153
|
+
const filePath = path.join(destDir, name);
|
|
154
|
+
if (!filePath.startsWith(path.resolve(destDir) + path.sep) &&
|
|
155
|
+
filePath !== path.resolve(destDir)) {
|
|
156
|
+
console.warn(` Skipping ${name}: path traversal detected`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
160
|
+
|
|
161
|
+
const rawData = buf.subarray(dataStart, dataStart + compSize);
|
|
162
|
+
|
|
163
|
+
if (method === 0) {
|
|
164
|
+
// STORE — no compression
|
|
165
|
+
fs.writeFileSync(filePath, rawData);
|
|
166
|
+
} else if (method === 8) {
|
|
167
|
+
// DEFLATE — use raw inflate (no zlib header)
|
|
168
|
+
const inflated = zlib.inflateRawSync(rawData);
|
|
169
|
+
fs.writeFileSync(filePath, inflated);
|
|
170
|
+
} else {
|
|
171
|
+
// Unsupported method — skip
|
|
172
|
+
console.warn(` Skipping ${name}: unsupported compression method ${method}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
112
175
|
}
|
|
113
176
|
|
|
114
177
|
// ---------------------------------------------------------------------------
|
|
@@ -143,7 +206,7 @@ ${c.dim} ██████╔╝██║ ██║███████
|
|
|
143
206
|
${c.dim} ██╔══██╗██║ ██║██╔══██║██╔══██╗██║ ██║${c.reset}
|
|
144
207
|
${c.dim} ██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝${c.reset}
|
|
145
208
|
${c.dim} ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ${c.reset}
|
|
146
|
-
${c.dim} AI-Native Financial Intelligence — v1.0
|
|
209
|
+
${c.dim} AI-Native Financial Intelligence — v1.3.0${c.reset}
|
|
147
210
|
`);
|
|
148
211
|
}
|
|
149
212
|
|
|
@@ -156,15 +219,19 @@ async function main() {
|
|
|
156
219
|
|
|
157
220
|
banner();
|
|
158
221
|
|
|
159
|
-
// 1. Validate API key against the app
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
222
|
+
// 1. Validate API key against the app (if provided)
|
|
223
|
+
if (key) {
|
|
224
|
+
process.stdout.write(` ${c.gray}◆${c.reset} Validating API key ${c.dim}...${c.reset} `);
|
|
225
|
+
try {
|
|
226
|
+
const resp = await fetchBuffer(`${APP_URL}/api/data-sources`, {
|
|
227
|
+
"x-api-key": key,
|
|
228
|
+
});
|
|
229
|
+
const data = JSON.parse(resp.toString());
|
|
230
|
+
if (data.error) throw new Error(data.error);
|
|
231
|
+
console.log(`${c.green}✓${c.reset}`);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.log(`${c.green}✓${c.reset} ${c.dim}(offline)${c.reset}`);
|
|
234
|
+
}
|
|
168
235
|
}
|
|
169
236
|
|
|
170
237
|
// 2. Download toolkit from S3
|
|
@@ -191,8 +258,8 @@ async function main() {
|
|
|
191
258
|
if (fs.existsSync(extractedDir)) {
|
|
192
259
|
fs.mkdirSync(path.dirname(absDir), { recursive: true });
|
|
193
260
|
if (fs.existsSync(absDir)) {
|
|
194
|
-
|
|
195
|
-
|
|
261
|
+
// Merge into existing dir — pure JS, no shell (P3-8)
|
|
262
|
+
copyDirRecursive(extractedDir, absDir);
|
|
196
263
|
} else {
|
|
197
264
|
fs.renameSync(extractedDir, absDir);
|
|
198
265
|
}
|
|
@@ -205,13 +272,20 @@ async function main() {
|
|
|
205
272
|
process.exit(1);
|
|
206
273
|
}
|
|
207
274
|
|
|
208
|
-
// 4. Write .env with the user's API key
|
|
209
|
-
process.stdout.write(` ${c.gray}◆${c.reset} Configuring
|
|
275
|
+
// 4. Write .env with the user's API key (if provided)
|
|
276
|
+
process.stdout.write(` ${c.gray}◆${c.reset} Configuring workspace ${c.dim}...${c.reset} `);
|
|
210
277
|
const envPath = path.join(absDir, ".env");
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
278
|
+
if (key) {
|
|
279
|
+
fs.writeFileSync(
|
|
280
|
+
envPath,
|
|
281
|
+
`# HedgeBoard Data Sources\nHEDGEBOARD_API_KEY=${key}\nHEDGEBOARD_API_URL=${API_URL}\n`
|
|
282
|
+
);
|
|
283
|
+
} else {
|
|
284
|
+
fs.writeFileSync(
|
|
285
|
+
envPath,
|
|
286
|
+
`# HedgeBoard Data Sources\n# Add your HB SEC Filing Data key from: https://www.hedgeboardhq.com/dashboard\nHEDGEBOARD_API_KEY=\nHEDGEBOARD_API_URL=${API_URL}\n`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
215
289
|
console.log(`${c.green}✓${c.reset}`);
|
|
216
290
|
|
|
217
291
|
// 5. Summary
|
|
@@ -222,15 +296,19 @@ async function main() {
|
|
|
222
296
|
${c.dim} ─────────────────────────────────────${c.reset}
|
|
223
297
|
|
|
224
298
|
${c.bold}Directory${c.reset} ${c.cyan}${relDir}/${c.reset}
|
|
225
|
-
${
|
|
299
|
+
${key
|
|
300
|
+
? `${c.bold}API Key${c.reset} ${c.dim}${key.slice(0, 8)}${"•".repeat(12)}${key.slice(-4)}${c.reset}`
|
|
301
|
+
: `${c.bold}API Key${c.reset} ${c.dim}not set — add from dashboard${c.reset}`
|
|
302
|
+
}
|
|
226
303
|
${c.bold}API URL${c.reset} ${c.dim}${API_URL}${c.reset}
|
|
227
304
|
|
|
228
305
|
${c.bold}What's inside:${c.reset}
|
|
229
306
|
${c.gray}├──${c.reset} ${c.white}INSTRUCTIONS.md${c.reset} ${c.dim}Agent instructions & prompt${c.reset}
|
|
230
|
-
${c.gray}├──${c.reset} ${c.white}data/${c.reset} ${c.dim}
|
|
307
|
+
${c.gray}├──${c.reset} ${c.white}data/${c.reset} ${c.dim}Data source adapters${c.reset}
|
|
231
308
|
${c.gray}├──${c.reset} ${c.white}viz/${c.reset} ${c.dim}Dashboard templates & charts${c.reset}
|
|
232
309
|
${c.gray}├──${c.reset} ${c.white}brand/${c.reset} ${c.dim}Your brand settings${c.reset}
|
|
233
310
|
${c.gray}├──${c.reset} ${c.white}modules/${c.reset} ${c.dim}356 analysis recipes + indexes${c.reset}
|
|
311
|
+
${c.gray}├──${c.reset} ${c.white}docs/${c.reset} ${c.dim}Data catalog — 35 providers, 15 categories${c.reset}
|
|
234
312
|
${c.gray}└──${c.reset} ${c.white}output/${c.reset} ${c.dim}Generated dashboards land here${c.reset}
|
|
235
313
|
|
|
236
314
|
${c.dim} ─────────────────────────────────────${c.reset}
|
|
@@ -243,7 +321,7 @@ ${c.dim} ───────────────────────
|
|
|
243
321
|
${c.cyan}2.${c.reset} Ask something:
|
|
244
322
|
${c.magenta}"Give me a company overview of Apple"${c.reset}
|
|
245
323
|
|
|
246
|
-
${c.cyan}3.${c.reset}
|
|
324
|
+
${c.cyan}3.${c.reset} Connect data sources at:
|
|
247
325
|
${c.underline}https://www.hedgeboardhq.com/dashboard${c.reset}
|
|
248
326
|
`);
|
|
249
327
|
}
|