eidoncore 3.3.7 → 3.7.2

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 (3) hide show
  1. package/bin.js +38 -38
  2. package/install.js +250 -199
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -1,38 +1,38 @@
1
- #!/usr/bin/env node
2
- // ============================================================================
3
- // Eidon — bin shim
4
- //
5
- // This file is the `bin.eidon` entry point registered with npm.
6
- // It locates the native binary that postinstall downloaded and
7
- // spawns it with all arguments forwarded. Exit code is preserved.
8
- //
9
- // No npm dependencies — only Node.js built-ins.
10
- // ============================================================================
11
- 'use strict';
12
-
13
- const path = require('path');
14
- const fs = require('fs');
15
- const { spawnSync } = require('child_process');
16
-
17
- const BINARY_NAME = process.platform === 'win32' ? 'eidon.exe' : 'eidon';
18
- const BINARY_PATH = path.join(__dirname, BINARY_NAME);
19
-
20
- if (!fs.existsSync(BINARY_PATH)) {
21
- console.error(
22
- '\n Eidon binary not found.\n' +
23
- ' Run: npm install -g eidon (postinstall will re-download it)\n'
24
- );
25
- process.exit(1);
26
- }
27
-
28
- const result = spawnSync(BINARY_PATH, process.argv.slice(2), {
29
- stdio: 'inherit',
30
- env: process.env,
31
- });
32
-
33
- if (result.error) {
34
- console.error(`\n Failed to launch eidon: ${result.error.message}\n`);
35
- process.exit(1);
36
- }
37
-
38
- process.exit(result.status ?? 0);
1
+ #!/usr/bin/env node
2
+ // ============================================================================
3
+ // Eidon — bin shim
4
+ //
5
+ // This file is the `bin.eidon` entry point registered with npm.
6
+ // It locates the native binary that postinstall downloaded and
7
+ // spawns it with all arguments forwarded. Exit code is preserved.
8
+ //
9
+ // No npm dependencies — only Node.js built-ins.
10
+ // ============================================================================
11
+ 'use strict';
12
+
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+ const { spawnSync } = require('child_process');
16
+
17
+ const BINARY_NAME = process.platform === 'win32' ? 'eidon.exe' : 'eidon';
18
+ const BINARY_PATH = path.join(__dirname, BINARY_NAME);
19
+
20
+ if (!fs.existsSync(BINARY_PATH)) {
21
+ console.error(
22
+ '\n Eidon binary not found.\n' +
23
+ ' Run: npm install -g eidon (postinstall will re-download it)\n'
24
+ );
25
+ process.exit(1);
26
+ }
27
+
28
+ const result = spawnSync(BINARY_PATH, process.argv.slice(2), {
29
+ stdio: 'inherit',
30
+ env: process.env,
31
+ });
32
+
33
+ if (result.error) {
34
+ console.error(`\n Failed to launch eidon: ${result.error.message}\n`);
35
+ process.exit(1);
36
+ }
37
+
38
+ process.exit(result.status ?? 0);
package/install.js CHANGED
@@ -1,199 +1,250 @@
1
- #!/usr/bin/env node
2
- // ============================================================================
3
- // Eidon — postinstall binary downloader
4
- //
5
- // Runs automatically after `npm install -g eidon`.
6
- // Downloads the correct pre-compiled Eidon binary for the current platform,
7
- // verifies its SHA-256 checksum, and saves it next to this file.
8
- //
9
- // No npm dependencies — only Node.js built-ins.
10
- // ============================================================================
11
- 'use strict';
12
-
13
- const https = require('https');
14
- const fs = require('fs');
15
- const path = require('path');
16
- const crypto = require('crypto');
17
- const os = require('os');
18
- const { execFileSync } = require('child_process');
19
-
20
- const RELEASES_BASE = 'https://releases.eidon.dev';
21
- const BINARY_NAME = process.platform === 'win32' ? 'eidon.exe' : 'eidon';
22
- const BINARY_PATH = path.join(__dirname, BINARY_NAME);
23
-
24
- // ── Platform detection ────────────────────────────────────────────────────
25
- function getPlatform() {
26
- const p = process.platform;
27
- const a = process.arch;
28
- if (p === 'darwin') return 'macos-arm64'; // ARM64 binary runs on both Apple Silicon and Intel (Rosetta 2)
29
- if (p === 'linux' && a === 'x64') return 'linux-x64';
30
- if (p === 'linux' && a === 'arm64') return 'linux-arm64';
31
- if (p === 'win32' && a === 'x64') return 'windows-x64';
32
- throw new Error(
33
- `Eidon: unsupported platform ${p}/${a}.\n` +
34
- `Please download manually from ${RELEASES_BASE}`
35
- );
36
- }
37
-
38
- // ── HTTPS helpers (no external deps) ────────────────────────────────────
39
- function fetchJson(url) {
40
- return new Promise((resolve, reject) => {
41
- const req = https.get(url, { timeout: 15000 }, res => {
42
- if (res.statusCode === 301 || res.statusCode === 302) {
43
- return fetchJson(res.headers.location).then(resolve, reject);
44
- }
45
- if (res.statusCode !== 200) {
46
- return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
47
- }
48
- let body = '';
49
- res.on('data', chunk => { body += chunk; });
50
- res.on('end', () => {
51
- try { resolve(JSON.parse(body)); }
52
- catch (e) { reject(new Error(`Failed to parse manifest JSON: ${e.message}`)); }
53
- });
54
- });
55
- req.on('error', reject);
56
- req.on('timeout', () => { req.destroy(); reject(new Error('Request timed out')); });
57
- });
58
- }
59
-
60
- function downloadFile(url, dest) {
61
- return new Promise((resolve, reject) => {
62
- const file = fs.createWriteStream(dest);
63
- function follow(href) {
64
- const req = https.get(href, { timeout: 120000 }, res => {
65
- if (res.statusCode === 301 || res.statusCode === 302) {
66
- file.destroy();
67
- return follow(res.headers.location);
68
- }
69
- if (res.statusCode !== 200) {
70
- file.destroy();
71
- fs.unlink(dest, () => {});
72
- return reject(new Error(`HTTP ${res.statusCode} downloading binary`));
73
- }
74
- const total = parseInt(res.headers['content-length'] || '0', 10);
75
- let received = 0;
76
- let lastPct = -1;
77
- res.on('data', chunk => {
78
- received += chunk.length;
79
- if (total > 0) {
80
- const pct = Math.floor((received / total) * 20); // 20-char bar
81
- if (pct !== lastPct) {
82
- lastPct = pct;
83
- const bar = '█'.repeat(pct) + '░'.repeat(20 - pct);
84
- process.stdout.write(`\r [${bar}] ${Math.floor(received / 1024)}KB`);
85
- }
86
- }
87
- });
88
- res.pipe(file);
89
- res.on('end', () => { process.stdout.write('\n'); });
90
- file.on('finish', () => file.close(resolve));
91
- file.on('error', err => { fs.unlink(dest, () => {}); reject(err); });
92
- });
93
- req.on('error', err => { fs.unlink(dest, () => {}); reject(err); });
94
- req.on('timeout', () => { req.destroy(); reject(new Error('Download timed out')); });
95
- }
96
- follow(url);
97
- });
98
- }
99
-
100
- // ── SHA-256 verification ─────────────────────────────────────────────────
101
- function sha256File(filePath) {
102
- return new Promise((resolve, reject) => {
103
- const hash = crypto.createHash('sha256');
104
- const stream = fs.createReadStream(filePath);
105
- stream.on('data', chunk => hash.update(chunk));
106
- stream.on('end', () => resolve(hash.digest('hex')));
107
- stream.on('error', reject);
108
- });
109
- }
110
-
111
- // ── Main ─────────────────────────────────────────────────────────────────
112
- async function main() {
113
- // Skip if binary already exists and matches (e.g. re-running postinstall)
114
- if (fs.existsSync(BINARY_PATH)) {
115
- try {
116
- execFileSync(BINARY_PATH, ['--version'], { stdio: 'ignore' });
117
- console.log(' Eidon binary already installed and working.');
118
- return;
119
- } catch {
120
- // Binary exists but broken — re-download
121
- fs.unlinkSync(BINARY_PATH);
122
- }
123
- }
124
-
125
- console.log('\n Installing Eidon...');
126
-
127
- const platform = getPlatform();
128
- console.log(` Platform : ${platform}`);
129
-
130
- // Allow pinning: EIDON_VERSION=3.2.0 npm install -g eidon
131
- const requestedVersion = process.env.EIDON_VERSION || '';
132
- const manifestUrl = requestedVersion
133
- ? `${RELEASES_BASE}/${requestedVersion}.json`
134
- : `${RELEASES_BASE}/latest.json`;
135
-
136
- console.log(' Fetching release manifest...');
137
- const manifest = await fetchJson(manifestUrl);
138
- const version = manifest.version;
139
- const asset = manifest.assets && manifest.assets[platform];
140
-
141
- if (!asset || !asset.url || !asset.sha256) {
142
- throw new Error(
143
- `No release asset found for platform "${platform}" in manifest.\n` +
144
- `Please download manually from ${RELEASES_BASE}`
145
- );
146
- }
147
-
148
- console.log(` Version : v${version}`);
149
- console.log(` Downloading binary...`);
150
-
151
- const tmpPath = BINARY_PATH + '.download';
152
- try {
153
- await downloadFile(asset.url, tmpPath);
154
- } catch (err) {
155
- fs.unlink(tmpPath, () => {});
156
- throw err;
157
- }
158
-
159
- // Verify SHA-256
160
- console.log(' Verifying SHA-256...');
161
- const actual = await sha256File(tmpPath);
162
- const expected = asset.sha256.toLowerCase();
163
- if (actual !== expected) {
164
- fs.unlinkSync(tmpPath);
165
- throw new Error(
166
- `SHA-256 mismatch — aborting (possible tampered binary)\n` +
167
- ` Expected: ${expected}\n` +
168
- ` Got : ${actual}`
169
- );
170
- }
171
- console.log(' SHA-256 verified.');
172
-
173
- // Move into place and make executable
174
- fs.renameSync(tmpPath, BINARY_PATH);
175
- if (process.platform !== 'win32') fs.chmodSync(BINARY_PATH, 0o755);
176
-
177
- // Smoke test
178
- try {
179
- const ver = execFileSync(BINARY_PATH, ['--version'], { encoding: 'utf8' }).trim();
180
- console.log(` Verified : ${ver}`);
181
- } catch {
182
- console.warn(' WARNING: Binary installed but smoke test failed. Try running: eidon --version');
183
- }
184
-
185
- console.log(`\n Eidon v${version} ready.\n`);
186
- console.log(' Quick start:');
187
- console.log(' eidon init # configure your LLM (one-time wizard)');
188
- console.log(' eidon analyze # index your repo — run once');
189
- console.log(' eidon install-mcp # connect Cursor, VS Code, Claude Code...');
190
- console.log(' eidon doctor # health check before every session\n');
191
- }
192
-
193
- main().catch(err => {
194
- console.error(`\n Eidon install failed: ${err.message}\n`);
195
- console.error(` Download manually: ${RELEASES_BASE}`);
196
- // Exit 0 — don't block npm install for users who don't need the binary yet
197
- // (e.g. CI installs that only need the package metadata)
198
- process.exit(0);
199
- });
1
+ #!/usr/bin/env node
2
+ // ============================================================================
3
+ // Eidon — postinstall binary downloader
4
+ //
5
+ // Runs automatically after `npm install -g eidon`.
6
+ // Downloads the correct pre-compiled Eidon binary for the current platform,
7
+ // verifies its SHA-256 checksum, and saves it next to this file.
8
+ //
9
+ // No npm dependencies — only Node.js built-ins.
10
+ // ============================================================================
11
+ 'use strict';
12
+
13
+ const https = require('https');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const crypto = require('crypto');
17
+ const os = require('os');
18
+ const { execFileSync } = require('child_process');
19
+
20
+ const RELEASES_BASE = 'https://releases.eidon.dev';
21
+ const BINARY_NAME = process.platform === 'win32' ? 'eidon.exe' : 'eidon';
22
+ const BINARY_PATH = path.join(__dirname, BINARY_NAME);
23
+
24
+ // Ed25519 public key (DER/SPKI, hex-encoded) — matches the private key stored in
25
+ // GitHub Actions secret MANIFEST_SIGNING_KEY. Together they ensure that even if
26
+ // releases.eidon.dev is fully compromised, an attacker cannot serve a fake manifest
27
+ // because they cannot forge a valid Ed25519 signature for the private key.
28
+ // To rotate: run scripts/gen-manifest-keypair.js, update this constant, and update
29
+ // the matching constant in src/cli/update.ts. Then set the new private key in
30
+ // GitHub Actions secrets before your next release.
31
+ const MANIFEST_PUBKEY_HEX = '302a300506032b657003210009880b8bfc7ae6dcc3a9e6dd207709b25df819bd19a94e5acb5b1b0d343dc5cd';
32
+
33
+ // ── Platform detection ────────────────────────────────────────────────────
34
+ function getPlatform() {
35
+ const p = process.platform;
36
+ const a = process.arch;
37
+ if (p === 'darwin') return 'macos-arm64'; // ARM64 binary runs on both Apple Silicon and Intel (Rosetta 2)
38
+ if (p === 'linux' && a === 'x64') return 'linux-x64';
39
+ if (p === 'linux' && a === 'arm64') return 'linux-arm64';
40
+ if (p === 'win32' && a === 'x64') return 'windows-x64';
41
+ throw new Error(
42
+ `Eidon: unsupported platform ${p}/${a}.\n` +
43
+ `Please download manually from ${RELEASES_BASE}`
44
+ );
45
+ }
46
+
47
+ // ── HTTPS helpers (no external deps) ────────────────────────────────────
48
+ function fetchText(url) {
49
+ return new Promise((resolve, reject) => {
50
+ if (!url.startsWith('https://')) {
51
+ return reject(new Error(`Refusing non-HTTPS URL: ${url}`));
52
+ }
53
+ const req = https.get(url, { timeout: 15000 }, res => {
54
+ if (res.statusCode === 301 || res.statusCode === 302) {
55
+ const loc = res.headers.location || '';
56
+ if (!loc.startsWith('https://')) {
57
+ return reject(new Error(`Redirect to non-HTTPS blocked: ${loc}`));
58
+ }
59
+ return fetchText(loc).then(resolve, reject);
60
+ }
61
+ if (res.statusCode !== 200) {
62
+ res.resume();
63
+ return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
64
+ }
65
+ const chunks = [];
66
+ res.on('data', chunk => chunks.push(chunk));
67
+ res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
68
+ });
69
+ req.on('error', reject);
70
+ req.on('timeout', () => { req.destroy(); reject(new Error('Request timed out')); });
71
+ });
72
+ }
73
+
74
+ // Fetch + Ed25519 verify a manifest JSON.
75
+ // 1. Fetch <url> — raw manifest text (must be exactly what was signed)
76
+ // 2. Fetch <url>.sig — hex-encoded Ed25519 signature (soft-fail: warn if missing)
77
+ // 3. Verify signature — hard-fail if signature is present but invalid
78
+ // 4. Parse + return — JSON is only parsed after integrity is confirmed
79
+ async function fetchAndVerifyManifest(url) {
80
+ const manifestText = await fetchText(url);
81
+
82
+ // Attempt to fetch the signature (non-fatal if server doesn't have it yet)
83
+ let sigHex = null;
84
+ try { sigHex = (await fetchText(url + '.sig')).trim(); } catch { /* no sig file */ }
85
+
86
+ if (sigHex) {
87
+ try {
88
+ const pubKey = { key: Buffer.from(MANIFEST_PUBKEY_HEX, 'hex'), format: 'der', type: 'spki' };
89
+ const sig = Buffer.from(sigHex, 'hex');
90
+ const valid = crypto.verify(null, Buffer.from(manifestText), pubKey, sig);
91
+ if (!valid) {
92
+ throw new Error(
93
+ 'Ed25519 signature verification FAILED manifest may be tampered.\n' +
94
+ 'Refusing to install. Please report this at https://github.com/BAGADIR/Eidon/issues'
95
+ );
96
+ }
97
+ console.log(' Manifest signature verified ✓');
98
+ } catch (err) {
99
+ if (err.message.includes('signature verification FAILED')) throw err;
100
+ // Crypto error (bad key format, etc.) — log and continue with SHA-256 only
101
+ console.warn(` Warning: could not verify manifest signature (${err.message}). Proceeding with SHA-256 only.`);
102
+ }
103
+ } else {
104
+ console.warn(' Warning: manifest signature not found (.sig). Proceeding with SHA-256 verification only.');
105
+ }
106
+
107
+ try { return JSON.parse(manifestText); }
108
+ catch (e) { throw new Error(`Failed to parse manifest JSON: ${e.message}`); }
109
+ }
110
+
111
+ function downloadFile(url, dest) {
112
+ return new Promise((resolve, reject) => {
113
+ const file = fs.createWriteStream(dest);
114
+ function follow(href) {
115
+ const req = https.get(href, { timeout: 120000 }, res => {
116
+ if (res.statusCode === 301 || res.statusCode === 302) {
117
+ file.destroy();
118
+ return follow(res.headers.location);
119
+ }
120
+ if (res.statusCode !== 200) {
121
+ file.destroy();
122
+ fs.unlink(dest, () => {});
123
+ return reject(new Error(`HTTP ${res.statusCode} downloading binary`));
124
+ }
125
+ const total = parseInt(res.headers['content-length'] || '0', 10);
126
+ let received = 0;
127
+ let lastPct = -1;
128
+ res.on('data', chunk => {
129
+ received += chunk.length;
130
+ if (total > 0) {
131
+ const pct = Math.floor((received / total) * 20); // 20-char bar
132
+ if (pct !== lastPct) {
133
+ lastPct = pct;
134
+ const bar = '█'.repeat(pct) + '░'.repeat(20 - pct);
135
+ process.stdout.write(`\r [${bar}] ${Math.floor(received / 1024)}KB`);
136
+ }
137
+ }
138
+ });
139
+ res.pipe(file);
140
+ res.on('end', () => { process.stdout.write('\n'); });
141
+ file.on('finish', () => file.close(resolve));
142
+ file.on('error', err => { fs.unlink(dest, () => {}); reject(err); });
143
+ });
144
+ req.on('error', err => { fs.unlink(dest, () => {}); reject(err); });
145
+ req.on('timeout', () => { req.destroy(); reject(new Error('Download timed out')); });
146
+ }
147
+ follow(url);
148
+ });
149
+ }
150
+
151
+ // ── SHA-256 verification ─────────────────────────────────────────────────
152
+ function sha256File(filePath) {
153
+ return new Promise((resolve, reject) => {
154
+ const hash = crypto.createHash('sha256');
155
+ const stream = fs.createReadStream(filePath);
156
+ stream.on('data', chunk => hash.update(chunk));
157
+ stream.on('end', () => resolve(hash.digest('hex')));
158
+ stream.on('error', reject);
159
+ });
160
+ }
161
+
162
+ // ── Main ─────────────────────────────────────────────────────────────────
163
+ async function main() {
164
+ // Skip if binary already exists and matches (e.g. re-running postinstall)
165
+ if (fs.existsSync(BINARY_PATH)) {
166
+ try {
167
+ execFileSync(BINARY_PATH, ['--version'], { stdio: 'ignore' });
168
+ console.log(' Eidon binary already installed and working.');
169
+ return;
170
+ } catch {
171
+ // Binary exists but broken — re-download
172
+ fs.unlinkSync(BINARY_PATH);
173
+ }
174
+ }
175
+
176
+ console.log('\n Installing Eidon...');
177
+
178
+ const platform = getPlatform();
179
+ console.log(` Platform : ${platform}`);
180
+
181
+ // Allow pinning: EIDON_VERSION=3.2.0 npm install -g eidon
182
+ const requestedVersion = process.env.EIDON_VERSION || '';
183
+ const manifestUrl = requestedVersion
184
+ ? `${RELEASES_BASE}/${requestedVersion}.json`
185
+ : `${RELEASES_BASE}/latest.json`;
186
+
187
+ console.log(' Fetching release manifest...');
188
+ const manifest = await fetchAndVerifyManifest(manifestUrl);
189
+ const version = manifest.version;
190
+ const asset = manifest.assets && manifest.assets[platform];
191
+
192
+ if (!asset || !asset.url || !asset.sha256) {
193
+ throw new Error(
194
+ `No release asset found for platform "${platform}" in manifest.\n` +
195
+ `Please download manually from ${RELEASES_BASE}`
196
+ );
197
+ }
198
+
199
+ console.log(` Version : v${version}`);
200
+ console.log(` Downloading binary...`);
201
+
202
+ const tmpPath = BINARY_PATH + '.download';
203
+ try {
204
+ await downloadFile(asset.url, tmpPath);
205
+ } catch (err) {
206
+ fs.unlink(tmpPath, () => {});
207
+ throw err;
208
+ }
209
+
210
+ // Verify SHA-256
211
+ console.log(' Verifying SHA-256...');
212
+ const actual = await sha256File(tmpPath);
213
+ const expected = asset.sha256.toLowerCase();
214
+ if (actual !== expected) {
215
+ fs.unlinkSync(tmpPath);
216
+ throw new Error(
217
+ `SHA-256 mismatch — aborting (possible tampered binary)\n` +
218
+ ` Expected: ${expected}\n` +
219
+ ` Got : ${actual}`
220
+ );
221
+ }
222
+ console.log(' SHA-256 verified.');
223
+
224
+ // Move into place and make executable
225
+ fs.renameSync(tmpPath, BINARY_PATH);
226
+ if (process.platform !== 'win32') fs.chmodSync(BINARY_PATH, 0o755);
227
+
228
+ // Smoke test
229
+ try {
230
+ const ver = execFileSync(BINARY_PATH, ['--version'], { encoding: 'utf8' }).trim();
231
+ console.log(` Verified : ${ver}`);
232
+ } catch {
233
+ console.warn(' WARNING: Binary installed but smoke test failed. Try running: eidon --version');
234
+ }
235
+
236
+ console.log(`\n Eidon v${version} ready.\n`);
237
+ console.log(' Quick start:');
238
+ console.log(' eidon init # configure your LLM (one-time wizard)');
239
+ console.log(' eidon analyze # index your repo — run once');
240
+ console.log(' eidon install-mcp # connect Cursor, VS Code, Claude Code...');
241
+ console.log(' eidon doctor # health check before every session\n');
242
+ }
243
+
244
+ main().catch(err => {
245
+ console.error(`\n Eidon install failed: ${err.message}\n`);
246
+ console.error(` Download manually: ${RELEASES_BASE}`);
247
+ // Exit 0 — don't block npm install for users who don't need the binary yet
248
+ // (e.g. CI installs that only need the package metadata)
249
+ process.exit(0);
250
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eidoncore",
3
- "version": "3.3.7",
3
+ "version": "3.7.2",
4
4
  "description": "Your AI finally knows your whole codebase. Structural graph intelligence for every AI tool you use.",
5
5
  "keywords": [
6
6
  "ai",