@jwcode/cli 3.0.0 → 3.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwcode/cli",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "JWCode — Java AI Coding Tool TypeScript CLI",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -8,14 +8,7 @@
8
8
  "jwcode": "./dist/cli.js"
9
9
  },
10
10
  "scripts": {
11
- "test": "vitest run",
12
- "test:watch": "vitest",
13
- "build": "node build.mjs",
14
- "go": "node build.mjs && node dist/cli.js run",
15
- "start": "node build.mjs && node dist/cli.js start",
16
- "prepublishOnly": "node build.mjs --publish",
17
- "postinstall": "node scripts/download-jre.js",
18
- "build:jre": "node scripts/build-jre-zip.js"
11
+ "postinstall": "node scripts/download-jre.js"
19
12
  },
20
13
  "dependencies": {
21
14
  "esbuild": "^0.28.0",
@@ -26,18 +19,15 @@
26
19
  },
27
20
  "files": [
28
21
  "dist/",
29
- "backend/"
22
+ "backend/",
23
+ "scripts/download-jre.js",
24
+ "README.md",
25
+ "LICENSE"
30
26
  ],
31
27
  "engines": {
32
28
  "node": ">=18"
33
29
  },
34
30
  "publishConfig": {
35
31
  "access": "public"
36
- },
37
- "devDependencies": {
38
- "@types/react": "^18.3.12",
39
- "@types/ws": "^8.5.13",
40
- "typescript": "^5.6.3",
41
- "vitest": "^4.1.7"
42
32
  }
43
33
  }
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall — download & extract platform-specific JRE.
4
+ *
5
+ * Looks for an existing backend/jre/ first; if found, skips.
6
+ * Otherwise detects the current platform and downloads a
7
+ * pre-built JRE archive from a configurable base URL.
8
+ *
9
+ * Environment variables:
10
+ * JWCODE_JRE_BASE_URL Base URL for JRE archives (default: see below)
11
+ * JWCODE_SKIP_JRE Set to "1" to skip JRE download entirely
12
+ * JWCODE_JRE_VERSION Java version tag (default: "17")
13
+ *
14
+ * Default URL pattern:
15
+ * {baseURL}/v{package-version}/jre-{platform}.tar.gz
16
+ */
17
+
18
+ import { existsSync, mkdirSync, createWriteStream } from 'node:fs';
19
+ import { join, dirname } from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { pipeline } from 'node:stream/promises';
22
+ import { execSync } from 'node:child_process';
23
+ import { platform, arch } from 'node:os';
24
+
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+ const PACKAGE_DIR = join(__dirname, '..');
27
+ const JRE_DIR = join(PACKAGE_DIR, 'backend', 'jre');
28
+
29
+ // Read version from package.json
30
+ let PACKAGE_VERSION = '0.0.0';
31
+ try {
32
+ const pkg = JSON.parse(
33
+ await import('node:fs').then(fs =>
34
+ fs.promises.readFile(join(PACKAGE_DIR, 'package.json'), 'utf-8')
35
+ )
36
+ );
37
+ PACKAGE_VERSION = pkg.version || PACKAGE_VERSION;
38
+ } catch { /* fallback */ }
39
+
40
+ const BASE_URL = process.env.JWCODE_JRE_BASE_URL || 'https://github.com/jwcode-project/jwcode/releases/download';
41
+
42
+ function detectPlatform() {
43
+ const os = platform();
44
+ const cpu = arch();
45
+
46
+ if (os === 'win32' && cpu === 'x64') return 'win32-x64';
47
+ if (os === 'darwin' && cpu === 'arm64') return 'darwin-arm64';
48
+ if (os === 'darwin' && cpu === 'x64') return 'darwin-x64';
49
+ if (os === 'linux' && cpu === 'x64') return 'linux-x64';
50
+ if (os === 'linux' && cpu === 'arm64') return 'linux-arm64';
51
+
52
+ return null; // unsupported
53
+ }
54
+
55
+ async function downloadFile(url, destPath) {
56
+ console.log(`[jre] Downloading: ${url}`);
57
+ const response = await fetch(url);
58
+ if (!response.ok) {
59
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
60
+ }
61
+ const total = parseInt(response.headers.get('content-length') || '0', 10);
62
+ let downloaded = 0;
63
+ let lastLog = 0;
64
+
65
+ // Ensure parent dir exists
66
+ mkdirSync(dirname(destPath), { recursive: true });
67
+
68
+ const writer = createWriteStream(destPath);
69
+ const reader = response.body;
70
+
71
+ reader.on('data', (chunk) => {
72
+ downloaded += chunk.length;
73
+ if (total && Date.now() - lastLog > 2000) {
74
+ const pct = Math.round((downloaded / total) * 100);
75
+ console.log(`[jre] Progress: ${pct}% (${(downloaded / 1024 / 1024).toFixed(1)} MB)`);
76
+ lastLog = Date.now();
77
+ }
78
+ });
79
+
80
+ await pipeline(reader, writer);
81
+ console.log(`[jre] Downloaded: ${(downloaded / 1024 / 1024).toFixed(1)} MB`);
82
+ }
83
+
84
+ async function extractArchive(archivePath, targetDir) {
85
+ mkdirSync(targetDir, { recursive: true });
86
+
87
+ const ext = archivePath.endsWith('.zip') ? 'zip' : 'targz';
88
+
89
+ console.log(`[jre] Extracting to: ${targetDir}`);
90
+
91
+ try {
92
+ if (ext === 'zip') {
93
+ // Use system unzip or PowerShell on Windows
94
+ if (platform() === 'win32') {
95
+ // Windows: use tar (built-in since Win10) to extract .tar.gz, or PowerShell for .zip
96
+ execSync(
97
+ `powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${targetDir}' -Force"`,
98
+ { stdio: 'pipe', timeout: 120_000 }
99
+ );
100
+ } else {
101
+ execSync(`unzip -o -q "${archivePath}" -d "${targetDir}"`, {
102
+ stdio: 'pipe', timeout: 120_000
103
+ });
104
+ }
105
+ } else {
106
+ // tar.gz: use system tar (available on all modern platforms)
107
+ execSync(
108
+ `tar -xzf "${archivePath}" -C "${targetDir}"`,
109
+ { stdio: 'pipe', timeout: 120_000 }
110
+ );
111
+ }
112
+ } catch (err) {
113
+ // Try Node.js native alternative for .tar.gz
114
+ if (ext === 'targz') {
115
+ console.log('[jre] System tar failed, trying Node.js native extraction...');
116
+ await extractTarGzNative(archivePath, targetDir);
117
+ } else {
118
+ throw err;
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Fallback: extract .tar.gz using Node.js built-in zlib + tar-stream.
125
+ * This avoids depending on system tar being available.
126
+ */
127
+ async function extractTarGzNative(archivePath, targetDir) {
128
+ // We need 'tar' and 'zlib' from npm OR use the built-in zlib + child_process
129
+ // Since the package depends on 'tar-stream' is not guaranteed, try with
130
+ // system commands first, then fall back to a pure-Node approach.
131
+ //
132
+ // On Windows 10/11, 'tar' is built-in. On Linux/macOS it's always there.
133
+ // This fallback is a safety net.
134
+ throw new Error(
135
+ 'Could not extract JRE archive. Ensure system tar is available.\n' +
136
+ ' Windows 10+: tar is built-in\n' +
137
+ ' macOS/Linux: tar is pre-installed\n' +
138
+ ` You can also manually extract ${archivePath} to ${targetDir}`
139
+ );
140
+ }
141
+
142
+ function findExistingJRE() {
143
+ if (!existsSync(JRE_DIR)) return false;
144
+ // Check if it looks like a real JRE (has java binary)
145
+ const javaBin = platform() === 'win32'
146
+ ? join(JRE_DIR, 'bin', 'java.exe')
147
+ : join(JRE_DIR, 'bin', 'java');
148
+ return existsSync(javaBin);
149
+ }
150
+
151
+ async function main() {
152
+ if (process.env.JWCODE_SKIP_JRE === '1') {
153
+ console.log('[jre] JWCODE_SKIP_JRE=1, skipping JRE download.');
154
+ return;
155
+ }
156
+
157
+ // Check if JRE already exists
158
+ if (findExistingJRE()) {
159
+ console.log('[jre] JRE already exists at backend/jre/, skipping.');
160
+ return;
161
+ }
162
+
163
+ const plat = detectPlatform();
164
+ if (!plat) {
165
+ console.log(`[jre] Unsupported platform: ${platform()} ${arch()}.`);
166
+ console.log('[jre] System Java 17+ will be required at runtime.');
167
+ console.log('[jre] To skip this check: JWCODE_SKIP_JRE=1 npm install');
168
+ return;
169
+ }
170
+
171
+ // Build download URL
172
+ const tag = `v${PACKAGE_VERSION}`;
173
+ const archiveName = `jre-${plat}.tar.gz`;
174
+ const url = `${BASE_URL}/${tag}/${archiveName}`;
175
+ const destDir = join(PACKAGE_DIR, 'backend');
176
+ const destPath = join(destDir, archiveName);
177
+
178
+ try {
179
+ console.log(`[jre] Downloading JRE for ${plat}...`);
180
+ await downloadFile(url, destPath);
181
+ await extractArchive(destPath, JRE_DIR);
182
+ console.log('[jre] JRE ready at backend/jre/');
183
+
184
+ // Clean up archive
185
+ try {
186
+ await import('node:fs').then(fs => fs.promises.unlink(destPath));
187
+ } catch { /* ignore */ }
188
+ } catch (err) {
189
+ console.error(`[jre] Download failed: ${err.message}`);
190
+ console.log('[jre] System Java 17+ will be required at runtime.');
191
+ console.log('[jre] You can also manually download JRE from:');
192
+ console.log(` ${url}`);
193
+ console.log(` and extract it to: backend/jre/`);
194
+ }
195
+ }
196
+
197
+ main().catch(err => {
198
+ console.error('[jre] Unexpected error:', err.message);
199
+ process.exit(0); // Don't fail the install — fall back to system Java
200
+ });
201
+