@hynaweb/stage 1.3.0 → 2.0.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/bin/cli.js +202 -90
- package/package.json +24 -11
- package/Test#1/dummy.html +0 -84
- package/Test#1/index.js +0 -128
- package/Test#1/package.json +0 -16
- package/dashboard.html +0 -240
- package/dummy.html +0 -85
- package/hyna-deploy.zip +0 -0
- package/hyna-jam.zip +0 -0
- package/hyna-live.zip +0 -0
- package/hyna-stage-v1.zip +0 -0
- package/index.html +0 -128
- package/index.js +0 -189
- package/inject_logo.py +0 -32
- package/logo.png +0 -0
package/index.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { program } from 'commander';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import os from 'os'; // Import OS to find the home directory
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
import ora from 'ora';
|
|
9
|
-
import inquirer from 'inquirer';
|
|
10
|
-
|
|
11
|
-
const DEPLOY_URL = "https://mczesspakhkomlnmfstw.supabase.co/functions/v1/deploy-stage";
|
|
12
|
-
const DESTROY_URL = "https://mczesspakhkomlnmfstw.supabase.co/functions/v1/destroy-stage";
|
|
13
|
-
|
|
14
|
-
// Helper function to auto-detect file type
|
|
15
|
-
function detectType(file) {
|
|
16
|
-
const ext = path.extname(file).toLowerCase();
|
|
17
|
-
if (['.html', '.htm', '.css'].includes(ext)) return 'html';
|
|
18
|
-
if (['.js', '.cjs', '.mjs'].includes(ext)) return 'node';
|
|
19
|
-
if (['.py'].includes(ext)) return 'python';
|
|
20
|
-
if (['.java'].includes(ext)) return 'java';
|
|
21
|
-
return null; // Unknown extension
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Helper function to save the key to .bashrc
|
|
25
|
-
function saveKeyToBashrc(key) {
|
|
26
|
-
try {
|
|
27
|
-
const bashrcPath = path.join(os.homedir(), '.bashrc');
|
|
28
|
-
const saveString = `\n# HynaStage API Key\nexport HYNA_API_KEY="${key}"\n`;
|
|
29
|
-
|
|
30
|
-
// Check if it already exists to avoid duplicates
|
|
31
|
-
if (fs.existsSync(bashrcPath)) {
|
|
32
|
-
const content = fs.readFileSync(bashrcPath, 'utf8');
|
|
33
|
-
if (content.includes('HYNA_API_KEY=')) return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
fs.appendFileSync(bashrcPath, saveString);
|
|
37
|
-
console.log(chalk.green('✅ API Key saved to ~/.bashrc! You won\'t need to enter it again in new sessions.'));
|
|
38
|
-
} catch (e) {
|
|
39
|
-
console.log(chalk.yellow('⚠️ Could not save key to .bashrc.'));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
program
|
|
44
|
-
.name('hyna-stage')
|
|
45
|
-
.description('Ephemeral cloud environments in 5 seconds.')
|
|
46
|
-
.version('1.2.0');
|
|
47
|
-
|
|
48
|
-
// ── UP COMMAND ──
|
|
49
|
-
program
|
|
50
|
-
.command('up')
|
|
51
|
-
.description('Deploy a file to a temporary HYNA environment')
|
|
52
|
-
.option('-f, --file <file>', 'File to deploy', 'index.html')
|
|
53
|
-
.option('-t, --type <type>', 'Force project type (html, node, python, java)')
|
|
54
|
-
.option('-k, --key <key>', 'Your HYNA API key (overrides .bashrc)')
|
|
55
|
-
.action(async (options) => {
|
|
56
|
-
|
|
57
|
-
// 1. Get API Key (CLI Flag -> Env Var -> Prompt & Save)
|
|
58
|
-
let apiKey = options.key || process.env.HYNA_API_KEY;
|
|
59
|
-
|
|
60
|
-
if (!apiKey) {
|
|
61
|
-
const answers = await inquirer.prompt([
|
|
62
|
-
{
|
|
63
|
-
type: 'input',
|
|
64
|
-
name: 'key',
|
|
65
|
-
message: 'Enter your HYNA API Key (will be saved for next time):',
|
|
66
|
-
validate: (input) => input.length > 0 ? true : 'API Key is required'
|
|
67
|
-
}
|
|
68
|
-
]);
|
|
69
|
-
apiKey = answers.key;
|
|
70
|
-
process.env.HYNA_API_KEY = apiKey; // Set for current session
|
|
71
|
-
saveKeyToBashrc(apiKey); // Save for future sessions
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 2. Check if file exists
|
|
75
|
-
const filePath = path.resolve(process.cwd(), options.file);
|
|
76
|
-
if (!fs.existsSync(filePath)) {
|
|
77
|
-
console.error(chalk.red(`❌ Error: File not found -> ${options.file}`));
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// 3. Determine Project Type
|
|
82
|
-
let projectType = options.type || detectType(options.file);
|
|
83
|
-
|
|
84
|
-
if (!projectType) {
|
|
85
|
-
const typeAnswer = await inquirer.prompt([
|
|
86
|
-
{
|
|
87
|
-
type: 'list',
|
|
88
|
-
name: 'type',
|
|
89
|
-
message: 'What is the type of the project?',
|
|
90
|
-
choices: ['html', 'node', 'python', 'java']
|
|
91
|
-
}
|
|
92
|
-
]);
|
|
93
|
-
projectType = typeAnswer.type;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const stats = fs.statSync(filePath);
|
|
97
|
-
const spinner = ora(`Spinning up ${projectType.toUpperCase()} environment...`).start();
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
let fileContent = fs.readFileSync(filePath);
|
|
101
|
-
const isLarge = stats.size > 50000;
|
|
102
|
-
|
|
103
|
-
if (isLarge) {
|
|
104
|
-
spinner.text = 'Encoding large file for transport...';
|
|
105
|
-
fileContent = fileContent.toString('base64');
|
|
106
|
-
} else {
|
|
107
|
-
fileContent = fileContent.toString('utf-8');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
spinner.text = 'Deploying to E2B MicroVM...';
|
|
111
|
-
|
|
112
|
-
const response = await fetch(DEPLOY_URL, {
|
|
113
|
-
method: 'POST',
|
|
114
|
-
headers: {
|
|
115
|
-
'Content-Type': 'application/json',
|
|
116
|
-
'Authorization': `Bearer ${apiKey}`
|
|
117
|
-
},
|
|
118
|
-
body: JSON.stringify({
|
|
119
|
-
apiKey: apiKey,
|
|
120
|
-
code: fileContent,
|
|
121
|
-
isBase64: isLarge,
|
|
122
|
-
type: projectType
|
|
123
|
-
})
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const data = await response.json();
|
|
127
|
-
if (!response.ok) throw new Error(data.error || `Server responded with ${response.status}`);
|
|
128
|
-
|
|
129
|
-
spinner.succeed(chalk.green('Environment is LIVE!'));
|
|
130
|
-
|
|
131
|
-
console.log('');
|
|
132
|
-
console.log(chalk.bgBlue.white.bold(` 🔗 URL: ${data.url} `));
|
|
133
|
-
console.log(chalk.gray(` Type: ${data.type}`));
|
|
134
|
-
console.log(chalk.gray(` Sandbox ID: ${data.sandbox_id}`));
|
|
135
|
-
console.log(chalk.gray(` (Auto-destroys in 1 hour)\n`)); // FIXED: Now says 1 hour
|
|
136
|
-
|
|
137
|
-
} catch (err) {
|
|
138
|
-
spinner.fail(chalk.red('Deployment failed'));
|
|
139
|
-
console.error(chalk.red(`\n❌ ${err.message}\n`));
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ── DOWN COMMAND ──
|
|
144
|
-
program
|
|
145
|
-
.command('down')
|
|
146
|
-
.description('Destroy an active environment early')
|
|
147
|
-
.argument('<sandbox_id>', 'The Sandbox ID to destroy')
|
|
148
|
-
.option('-k, --key <key>', 'Your HYNA API key')
|
|
149
|
-
.action(async (sandboxId, options) => {
|
|
150
|
-
let apiKey = options.key || process.env.HYNA_API_KEY;
|
|
151
|
-
|
|
152
|
-
if (!apiKey) {
|
|
153
|
-
const answers = await inquirer.prompt([
|
|
154
|
-
{
|
|
155
|
-
type: 'input',
|
|
156
|
-
name: 'key',
|
|
157
|
-
message: 'Enter your HYNA API Key:',
|
|
158
|
-
validate: (input) => input.length > 0 ? true : 'API Key is required'
|
|
159
|
-
}
|
|
160
|
-
]);
|
|
161
|
-
apiKey = answers.key;
|
|
162
|
-
process.env.HYNA_API_KEY = apiKey;
|
|
163
|
-
saveKeyToBashrc(apiKey);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const spinner = ora(`Destroying sandbox ${sandboxId}...`).start();
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
const response = await fetch(DESTROY_URL, {
|
|
170
|
-
method: 'POST',
|
|
171
|
-
headers: {
|
|
172
|
-
'Content-Type': 'application/json',
|
|
173
|
-
'Authorization': `Bearer ${apiKey}`
|
|
174
|
-
},
|
|
175
|
-
body: JSON.stringify({ sandbox_id: sandboxId })
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const data = await response.json();
|
|
179
|
-
if (!response.ok) throw new Error(data.error || 'Failed to destroy sandbox');
|
|
180
|
-
|
|
181
|
-
spinner.succeed(chalk.green('Environment destroyed successfully!'));
|
|
182
|
-
console.log(chalk.gray(` The E2B VM has been terminated.\n`));
|
|
183
|
-
} catch (err) {
|
|
184
|
-
spinner.fail(chalk.red('Destroy failed'));
|
|
185
|
-
console.error(chalk.red(`\n❌ ${err.message}\n`));
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
program.parse();
|
package/inject_logo.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import re
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
try:
|
|
6
|
-
if not os.path.exists('logo.png'):
|
|
7
|
-
print("Error: logo.png not found in this folder!")
|
|
8
|
-
exit()
|
|
9
|
-
|
|
10
|
-
with open('logo.png', 'rb') as f:
|
|
11
|
-
b64 = base64.b64encode(f.read()).decode('utf-8')
|
|
12
|
-
data_uri = f"data:image/png;base64,{b64}"
|
|
13
|
-
|
|
14
|
-
for filename in ['index.html', 'dashboard.html']:
|
|
15
|
-
if not os.path.exists(filename): continue
|
|
16
|
-
|
|
17
|
-
with open(filename, 'r') as f:
|
|
18
|
-
content = f.read()
|
|
19
|
-
|
|
20
|
-
# 1. Replace the standard img tag
|
|
21
|
-
content = content.replace('<img src="logo.png" class="nav-logo">', f'<img src="{data_uri}" class="nav-logo">')
|
|
22
|
-
content = content.replace('<img src="logo.png" class="hero-logo">', f'<img src="{data_uri}" class="hero-logo">')
|
|
23
|
-
|
|
24
|
-
# 2. Replace the fallback SVG block if it exists
|
|
25
|
-
content = re.sub(r'<div class="logo-container">.*?</div>', f'<div class="logo-container"><img src="{data_uri}" class="nav-logo"> HYNA-Stage</div>', content, flags=re.DOTALL)
|
|
26
|
-
|
|
27
|
-
with open(filename, 'w') as f:
|
|
28
|
-
f.write(content)
|
|
29
|
-
|
|
30
|
-
print("✅ Success! Logo baked directly into index.html and dashboard.html")
|
|
31
|
-
except Exception as e:
|
|
32
|
-
print(f"Error: {e}")
|
package/logo.png
DELETED
|
Binary file
|