@hynaweb/stage 1.0.5 → 1.3.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 CHANGED
@@ -3,6 +3,7 @@
3
3
  import { program } from 'commander';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
+ import os from 'os'; // Import OS to find the home directory
6
7
  import chalk from 'chalk';
7
8
  import ora from 'ora';
8
9
  import inquirer from 'inquirer';
@@ -10,39 +11,90 @@ import inquirer from 'inquirer';
10
11
  const DEPLOY_URL = "https://mczesspakhkomlnmfstw.supabase.co/functions/v1/deploy-stage";
11
12
  const DESTROY_URL = "https://mczesspakhkomlnmfstw.supabase.co/functions/v1/destroy-stage";
12
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
+
13
43
  program
14
44
  .name('hyna-stage')
15
45
  .description('Ephemeral cloud environments in 5 seconds.')
16
- .version('1.0.5');
46
+ .version('1.2.0');
17
47
 
18
48
  // ── UP COMMAND ──
19
49
  program
20
50
  .command('up')
21
51
  .description('Deploy a file to a temporary HYNA environment')
22
52
  .option('-f, --file <file>', 'File to deploy', 'index.html')
23
- .option('-k, --key <key>', 'Your HYNA API key')
53
+ .option('-t, --type <type>', 'Force project type (html, node, python, java)')
54
+ .option('-k, --key <key>', 'Your HYNA API key (overrides .bashrc)')
24
55
  .action(async (options) => {
25
- let apiKey = options.key;
56
+
57
+ // 1. Get API Key (CLI Flag -> Env Var -> Prompt & Save)
58
+ let apiKey = options.key || process.env.HYNA_API_KEY;
59
+
26
60
  if (!apiKey) {
27
61
  const answers = await inquirer.prompt([
28
62
  {
29
63
  type: 'input',
30
64
  name: 'key',
31
- message: 'Enter your HYNA API Key:',
65
+ message: 'Enter your HYNA API Key (will be saved for next time):',
32
66
  validate: (input) => input.length > 0 ? true : 'API Key is required'
33
67
  }
34
68
  ]);
35
69
  apiKey = answers.key;
70
+ process.env.HYNA_API_KEY = apiKey; // Set for current session
71
+ saveKeyToBashrc(apiKey); // Save for future sessions
36
72
  }
37
73
 
74
+ // 2. Check if file exists
38
75
  const filePath = path.resolve(process.cwd(), options.file);
39
76
  if (!fs.existsSync(filePath)) {
40
77
  console.error(chalk.red(`❌ Error: File not found -> ${options.file}`));
41
78
  process.exit(1);
42
79
  }
43
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
+
44
96
  const stats = fs.statSync(filePath);
45
- const spinner = ora('Spinning up cloud environment...').start();
97
+ const spinner = ora(`Spinning up ${projectType.toUpperCase()} environment...`).start();
46
98
 
47
99
  try {
48
100
  let fileContent = fs.readFileSync(filePath);
@@ -63,7 +115,12 @@ program
63
115
  'Content-Type': 'application/json',
64
116
  'Authorization': `Bearer ${apiKey}`
65
117
  },
66
- body: JSON.stringify({ apiKey: apiKey, code: fileContent, isBase64: isLarge })
118
+ body: JSON.stringify({
119
+ apiKey: apiKey,
120
+ code: fileContent,
121
+ isBase64: isLarge,
122
+ type: projectType
123
+ })
67
124
  });
68
125
 
69
126
  const data = await response.json();
@@ -73,8 +130,9 @@ program
73
130
 
74
131
  console.log('');
75
132
  console.log(chalk.bgBlue.white.bold(` 🔗 URL: ${data.url} `));
133
+ console.log(chalk.gray(` Type: ${data.type}`));
76
134
  console.log(chalk.gray(` Sandbox ID: ${data.sandbox_id}`));
77
- console.log(chalk.gray(` (Auto-destroys in 5 minutes)\n`));
135
+ console.log(chalk.gray(` (Auto-destroys in 1 hour)\n`)); // FIXED: Now says 1 hour
78
136
 
79
137
  } catch (err) {
80
138
  spinner.fail(chalk.red('Deployment failed'));
@@ -89,7 +147,8 @@ program
89
147
  .argument('<sandbox_id>', 'The Sandbox ID to destroy')
90
148
  .option('-k, --key <key>', 'Your HYNA API key')
91
149
  .action(async (sandboxId, options) => {
92
- let apiKey = options.key;
150
+ let apiKey = options.key || process.env.HYNA_API_KEY;
151
+
93
152
  if (!apiKey) {
94
153
  const answers = await inquirer.prompt([
95
154
  {
@@ -100,6 +159,8 @@ program
100
159
  }
101
160
  ]);
102
161
  apiKey = answers.key;
162
+ process.env.HYNA_API_KEY = apiKey;
163
+ saveKeyToBashrc(apiKey);
103
164
  }
104
165
 
105
166
  const spinner = ora(`Destroying sandbox ${sandboxId}...`).start();
package/inject_logo.py ADDED
@@ -0,0 +1,32 @@
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/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@hynaweb/stage",
3
- "version": "1.0.5",
3
+ "version": "1.3.0",
4
4
  "description": "Ephemeral cloud environments in 5 seconds.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "hyna-stage": "./index.js"
8
8
  },
9
9
  "type": "module",
10
+ "license": "UNLICENSED",
11
+ "author": "Harun (HYNAWEB)",
10
12
  "dependencies": {
11
13
  "chalk": "^5.3.0",
12
14
  "commander": "^11.0.0",