openaihub 1.1.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/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # OpenAI Hub npm package
2
+
3
+ Install globally:
4
+
5
+ ```bash
6
+ npm install -g openaihub
7
+ ```
8
+
9
+ Current support: Windows x64.
10
+
11
+ Runtime location after install:
12
+
13
+ ```text
14
+ %USERPROFILE%/.openaihub/npm-runtime
15
+ ```
16
+
17
+ Then run:
18
+
19
+ ```bash
20
+ openaihub
21
+ ```
22
+
23
+ Or:
24
+
25
+ ```bash
26
+ OAH
27
+ ```
package/bin/OAH.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../lib/run').main();
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../lib/run').main();
package/lib/config.js ADDED
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const os = require('node:os');
4
+ const path = require('node:path');
5
+
6
+ const packageRoot = path.resolve(__dirname, '..');
7
+ const runtimeRoot = path.join(os.homedir(), '.openaihub', 'npm-runtime');
8
+
9
+ const packageInfo = require(path.join(packageRoot, 'package.json'));
10
+
11
+ function getPlatformConfig() {
12
+ const platform = process.platform;
13
+ const arch = process.arch;
14
+
15
+ if (platform === 'win32' && arch === 'x64') {
16
+ return {
17
+ assetName: 'openaihub-windows.zip',
18
+ assetDownloadUrl: `https://github.com/gugezhanghao132-eng/openaihub/releases/download/${getTagName()}/openaihub-windows.zip`,
19
+ executableRelativePath: path.join('openaihub.exe'),
20
+ extractKind: 'zip',
21
+ runtimeKey: 'win32-x64'
22
+ };
23
+ }
24
+
25
+ throw new Error(`OpenAI Hub npm package does not support ${platform}-${arch} yet.`);
26
+ }
27
+
28
+ function getTagName() {
29
+ return `v${packageInfo.version}`;
30
+ }
31
+
32
+ module.exports = {
33
+ owner: 'gugezhanghao132-eng',
34
+ repo: 'openaihub',
35
+ packageName: packageInfo.name,
36
+ packageVersion: packageInfo.version,
37
+ packageRoot,
38
+ runtimeRoot,
39
+ getTagName,
40
+ getPlatformConfig
41
+ };
package/lib/install.js ADDED
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const fsp = require('node:fs/promises');
5
+ const https = require('node:https');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+ const { spawn } = require('node:child_process');
9
+
10
+ const {
11
+ packageVersion,
12
+ runtimeRoot,
13
+ getPlatformConfig,
14
+ } = require('./config');
15
+
16
+ function log(message) {
17
+ process.stdout.write(`[OpenAI Hub] ${message}\n`);
18
+ }
19
+
20
+ function downloadFile(url, targetPath, redirectsRemaining = 5) {
21
+ return new Promise((resolve, reject) => {
22
+ const requestUrl = (currentUrl, redirectsLeft) => {
23
+ const req = https.get(currentUrl, {
24
+ headers: {
25
+ 'User-Agent': 'OpenAIHub-npm-installer'
26
+ }
27
+ }, (res) => {
28
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
29
+ if (redirectsLeft <= 0) {
30
+ reject(new Error(`Too many redirects while downloading ${url}`));
31
+ res.resume();
32
+ return;
33
+ }
34
+ res.resume();
35
+ requestUrl(res.headers.location, redirectsLeft - 1);
36
+ return;
37
+ }
38
+
39
+ if (res.statusCode !== 200) {
40
+ reject(new Error(`Download failed: ${res.statusCode} ${currentUrl}`));
41
+ res.resume();
42
+ return;
43
+ }
44
+
45
+ const file = fs.createWriteStream(targetPath);
46
+ res.pipe(file);
47
+ file.on('finish', () => {
48
+ file.close(resolve);
49
+ });
50
+ file.on('error', (error) => {
51
+ file.close(() => fs.rmSync(targetPath, { force: true }));
52
+ reject(error);
53
+ });
54
+ });
55
+
56
+ req.setTimeout(30000, () => {
57
+ req.destroy(new Error(`Download timed out: ${currentUrl}`));
58
+ });
59
+ req.on('error', reject);
60
+ };
61
+
62
+ requestUrl(url, redirectsRemaining);
63
+ });
64
+ }
65
+
66
+ function runCommand(command, args) {
67
+ return new Promise((resolve, reject) => {
68
+ const child = spawn(command, args, {
69
+ stdio: 'inherit',
70
+ windowsHide: true
71
+ });
72
+
73
+ child.on('error', reject);
74
+ child.on('exit', (code) => {
75
+ if (code === 0) {
76
+ resolve();
77
+ return;
78
+ }
79
+ reject(new Error(`${command} exited with code ${code}`));
80
+ });
81
+ });
82
+ }
83
+
84
+ async function withRetry(action, attempts, label) {
85
+ let lastError = null;
86
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
87
+ try {
88
+ return await action();
89
+ } catch (error) {
90
+ lastError = error;
91
+ if (attempt < attempts) {
92
+ log(`${label} failed, retrying (${attempt}/${attempts - 1})...`);
93
+ await new Promise((resolve) => setTimeout(resolve, 1500));
94
+ }
95
+ }
96
+ }
97
+ throw lastError;
98
+ }
99
+
100
+ async function acquireLock(lockPath, timeoutMs = 60000) {
101
+ const startedAt = Date.now();
102
+ while (true) {
103
+ try {
104
+ return await fsp.open(lockPath, 'wx');
105
+ } catch (error) {
106
+ if (error && error.code === 'EEXIST') {
107
+ if (Date.now() - startedAt >= timeoutMs) {
108
+ throw new Error(`Timed out waiting for install lock: ${lockPath}`);
109
+ }
110
+ await new Promise((resolve) => setTimeout(resolve, 500));
111
+ continue;
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ }
117
+
118
+ async function extractArchive(archivePath, extractRoot, extractKind) {
119
+ if (extractKind === 'zip' && process.platform === 'win32') {
120
+ await runCommand('powershell.exe', [
121
+ '-NoProfile',
122
+ '-ExecutionPolicy',
123
+ 'Bypass',
124
+ '-Command',
125
+ `Expand-Archive -Path '${archivePath.replace(/'/g, "''")}' -DestinationPath '${extractRoot.replace(/'/g, "''")}' -Force`
126
+ ]);
127
+ return;
128
+ }
129
+
130
+ throw new Error(`Unsupported archive extraction for ${process.platform}: ${extractKind}`);
131
+ }
132
+
133
+ function findExecutable(extractRoot, executableName) {
134
+ const stack = [extractRoot];
135
+ while (stack.length > 0) {
136
+ const current = stack.pop();
137
+ const entries = fs.readdirSync(current, { withFileTypes: true });
138
+ for (const entry of entries) {
139
+ const fullPath = path.join(current, entry.name);
140
+ if (entry.isDirectory()) {
141
+ stack.push(fullPath);
142
+ continue;
143
+ }
144
+ if (entry.isFile() && entry.name.toLowerCase() === executableName.toLowerCase()) {
145
+ return path.dirname(fullPath);
146
+ }
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+
152
+ async function ensureInstalled(options = {}) {
153
+ const { quiet = false, force = false } = options;
154
+ const platformConfig = getPlatformConfig();
155
+ const runtimeDir = path.join(runtimeRoot, platformConfig.runtimeKey);
156
+ const executablePath = path.join(runtimeDir, platformConfig.executableRelativePath);
157
+
158
+ if (!force && fs.existsSync(executablePath)) {
159
+ return { executablePath, runtimeDir, downloaded: false };
160
+ }
161
+
162
+ const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), 'openaihub-npm-'));
163
+ const archivePath = path.join(tempRoot, platformConfig.assetName);
164
+ const extractRoot = path.join(tempRoot, 'extract');
165
+ const stageRoot = path.join(tempRoot, 'stage');
166
+ const lockPath = path.join(runtimeRoot, `${platformConfig.runtimeKey}.lock`);
167
+ let lockHandle = null;
168
+
169
+ try {
170
+ await fsp.mkdir(runtimeRoot, { recursive: true });
171
+ lockHandle = await acquireLock(lockPath);
172
+
173
+ if (!quiet) {
174
+ log(`Preparing OpenAI Hub ${packageVersion} for ${platformConfig.runtimeKey}...`);
175
+ }
176
+
177
+ await fsp.mkdir(extractRoot, { recursive: true });
178
+ await fsp.mkdir(stageRoot, { recursive: true });
179
+
180
+ if (!quiet) {
181
+ log(`Downloading ${platformConfig.assetName}...`);
182
+ }
183
+ await withRetry(() => downloadFile(platformConfig.assetDownloadUrl, archivePath), 3, 'Runtime download');
184
+
185
+ if (!quiet) {
186
+ log('Extracting runtime...');
187
+ }
188
+ await extractArchive(archivePath, extractRoot, platformConfig.extractKind);
189
+
190
+ const extractedRuntimeRoot = findExecutable(extractRoot, path.basename(platformConfig.executableRelativePath));
191
+ if (!extractedRuntimeRoot) {
192
+ throw new Error(`Executable ${platformConfig.executableRelativePath} was not found in downloaded asset.`);
193
+ }
194
+
195
+ await fsp.cp(extractedRuntimeRoot, stageRoot, { recursive: true, force: true });
196
+ await fsp.rm(runtimeDir, { recursive: true, force: true });
197
+ await fsp.mkdir(runtimeRoot, { recursive: true });
198
+ await fsp.rename(stageRoot, runtimeDir);
199
+
200
+ if (!quiet) {
201
+ log('Runtime ready.');
202
+ }
203
+
204
+ return { executablePath, runtimeDir, downloaded: true };
205
+ } finally {
206
+ if (lockHandle) {
207
+ await lockHandle.close();
208
+ }
209
+ await fsp.rm(lockPath, { force: true });
210
+ await fsp.rm(tempRoot, { recursive: true, force: true });
211
+ }
212
+ }
213
+
214
+ module.exports = {
215
+ ensureInstalled
216
+ };
package/lib/run.js ADDED
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const { spawn } = require('node:child_process');
4
+
5
+ const { ensureInstalled } = require('./install');
6
+
7
+ async function main() {
8
+ try {
9
+ const { executablePath } = await ensureInstalled({ quiet: true });
10
+ const child = spawn(executablePath, process.argv.slice(2), {
11
+ stdio: 'inherit',
12
+ windowsHide: false
13
+ });
14
+
15
+ child.on('exit', (code, signal) => {
16
+ if (signal) {
17
+ process.kill(process.pid, signal);
18
+ return;
19
+ }
20
+ process.exit(code === null ? 1 : code);
21
+ });
22
+
23
+ child.on('error', (error) => {
24
+ process.stderr.write(`[OpenAI Hub] Failed to launch runtime: ${error.message}\n`);
25
+ process.exit(1);
26
+ });
27
+ } catch (error) {
28
+ process.stderr.write(`[OpenAI Hub] ${error.message}\n`);
29
+ process.exit(1);
30
+ }
31
+ }
32
+
33
+ module.exports = {
34
+ main
35
+ };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "openaihub",
3
+ "version": "1.1.0",
4
+ "description": "OpenAI Hub CLI installer and launcher",
5
+ "license": "MIT",
6
+ "os": [
7
+ "win32"
8
+ ],
9
+ "cpu": [
10
+ "x64"
11
+ ],
12
+ "bin": {
13
+ "openaihub": "bin/openaihub.js",
14
+ "OAH": "bin/OAH.js"
15
+ },
16
+ "scripts": {
17
+ "postinstall": "node scripts/postinstall.js"
18
+ },
19
+ "files": [
20
+ "bin",
21
+ "lib",
22
+ "scripts",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const { ensureInstalled } = require('../lib/install');
4
+
5
+ ensureInstalled()
6
+ .catch((error) => {
7
+ process.stderr.write(`[OpenAI Hub] postinstall warning: ${error.message}\n`);
8
+ process.stderr.write('[OpenAI Hub] The runtime will be downloaded automatically on first launch.\n');
9
+ });