@fmode/studio 0.0.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/README.md +208 -0
  2. package/bin/fmode.js +350 -0
  3. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # @fmode/studio
2
+
3
+ **AI PaaS IDE for Vibe Coding** - A cross-platform CLI tool that provides a powerful development environment powered by AI.
4
+
5
+ ## Quick Start
6
+
7
+ The fastest way to get started is using npx (no installation required):
8
+
9
+ ```bash
10
+ npx -y @fmode/studio
11
+ ```
12
+
13
+ This will:
14
+ 1. Download and run the latest version automatically
15
+ 2. Start the development server on port 16666
16
+ 3. Open your browser to the IDE
17
+
18
+ ## Installation
19
+
20
+ ### Global Installation (Recommended for frequent use)
21
+
22
+ ```bash
23
+ npm install -g @fmode/studio
24
+ ```
25
+
26
+ Once installed, you can run:
27
+
28
+ ```bash
29
+ fmode
30
+ ```
31
+
32
+ ### Using npx (Run without installing)
33
+
34
+ ```bash
35
+ npx @fmode/studio
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### Basic Usage
41
+
42
+ Start the server with default settings (port 16666, host 0.0.0.0):
43
+
44
+ ```bash
45
+ fmode
46
+ # or
47
+ npx @fmode/studio
48
+ ```
49
+
50
+ ### Custom Port
51
+
52
+ Start on a specific port:
53
+
54
+ ```bash
55
+ fmode --port 16666
56
+ # or
57
+ npx @fmode/studio --port 16666
58
+ ```
59
+
60
+ ### Custom Host
61
+
62
+ Start on a specific host:
63
+
64
+ ```bash
65
+ fmode --host localhost --port 16666
66
+ # or
67
+ npx @fmode/studio --host localhost --port 16666
68
+ ```
69
+
70
+ ### Environment Variables
71
+
72
+ You can also configure the server using environment variables:
73
+
74
+ ```bash
75
+ PORT=16666 HOST=localhost fmode
76
+ ```
77
+
78
+ Available environment variables:
79
+ - `PORT` - Server port (default: 16666)
80
+ - `HOST` - Server host (default: 0.0.0.0)
81
+ - `VITE_IS_PLATFORM` - Set to 'true' for platform mode
82
+
83
+ ## Command Line Options
84
+
85
+ | Option | Alias | Description |
86
+ |--------|-------|-------------|
87
+ | `--port <PORT>` | `-p` | Set the server port (default: 16666) |
88
+ | `--host <HOST>` | `-h` | Set the server host (default: 0.0.0.0) |
89
+ | `--help` | | Show help message |
90
+ | `--version` | `-v` | Show version information |
91
+ | `-y` | | Auto-confirm (used with npx) |
92
+
93
+ ## Examples
94
+
95
+ ### Development Workflow
96
+
97
+ ```bash
98
+ # Start the development server
99
+ fmode
100
+
101
+ # Start on port 16666 for local development
102
+ fmode --port 16666
103
+
104
+ # Start on localhost only
105
+ fmode --host localhost --port 16666
106
+ ```
107
+
108
+ ### With npx (No Installation)
109
+
110
+ ```bash
111
+ # Quick start with auto-confirm
112
+ npx -y @fmode/studio
113
+
114
+ # Specify custom port
115
+ npx @fmode/studio --port 16666
116
+
117
+ # Full options
118
+ npx @fmode/studio --host localhost --port 16666
119
+ ```
120
+
121
+ ## Platform Support
122
+
123
+ @fmode/studio provides native executables for the following platforms:
124
+
125
+ | Platform | Architecture | Executable |
126
+ |----------|--------------|------------|
127
+ | Windows | x64 | fmode-win-x64.exe |
128
+ | Windows | ARM64 | fmode-win-arm64.exe |
129
+ | Linux | x64 | fmode-linux-x64 |
130
+ | Linux | ARM64 | fmode-linux-arm64 |
131
+ | macOS | x64 (Intel) | fmode-macos-x64 |
132
+ | macOS | ARM64 (Apple Silicon) | fmode-macos-arm64 |
133
+
134
+ The CLI automatically detects your platform and uses the appropriate executable.
135
+
136
+ ## Requirements
137
+
138
+ - **Node.js**: >= 18.0.0
139
+ - **Bun**: >= 1.0.0 (optional, for development)
140
+ - **OS**: Windows, macOS, or Linux
141
+ - **CPU**: x64 or ARM64
142
+
143
+ ## Data Storage
144
+
145
+ Application data is stored in your user data directory:
146
+
147
+ - **Windows**: `%APPDATA%\fmode-studio`
148
+ - **macOS**: `~/Library/Application Support/fmode-studio`
149
+ - **Linux**: `~/.config/fmode-studio`
150
+
151
+ ## Troubleshooting
152
+
153
+ ### Port Already in Use
154
+
155
+ If you see an error that the port is already in use:
156
+
157
+ ```bash
158
+ fmode --port 16666
159
+ ```
160
+
161
+ ### Permission Issues
162
+
163
+ On Linux/macOS, if you get permission errors, make sure the executable has execute permissions:
164
+
165
+ ```bash
166
+ chmod +x node_modules/@fmode/studio/dist/bin/fmode-*
167
+ ```
168
+
169
+ ### Fallback to Node.js
170
+
171
+ If the native executable is not available for your platform, the CLI will automatically fall back to running with Bun (if installed).
172
+
173
+ ## Version Check
174
+
175
+ Check your installed version:
176
+
177
+ ```bash
178
+ fmode --version
179
+ # or
180
+ npx @fmode/studio --version
181
+ ```
182
+
183
+ ## Help
184
+
185
+ Display help information:
186
+
187
+ ```bash
188
+ fmode --help
189
+ # or
190
+ npx @fmode/studio --help
191
+ ```
192
+
193
+ ## License
194
+
195
+ © 2026 未来飞马® All rights reserved.
196
+
197
+ ## Author
198
+
199
+ Fmode Studio
200
+
201
+ - [Fmode Inc](https://www.npmjs.com/~fmode)
202
+ - [RyaneMax](https://github.com/ryanemax)
203
+
204
+ ## Links
205
+
206
+ - Repository: https://www.npmjs.com/package/@fmode/studio
207
+ - Issues: https://app.fmode.cn/dev/studio
208
+
package/bin/fmode.js ADDED
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Fmode Studio CLI Entry Point
5
+ * npm/npx compatible entry point
6
+ *
7
+ * Features:
8
+ * - Version checking against repos.fmode.cn
9
+ * - Automatic binary download and caching
10
+ * - Update command for manual updates
11
+ * - Fallback to Node.js/Bun execution
12
+ *
13
+ * Usage:
14
+ * npx @fmode/studio
15
+ * npx @fmode/studio -y
16
+ * npx @fmode/studio update
17
+ * fmode
18
+ */
19
+
20
+ import { spawn } from 'child_process';
21
+ import { fileURLToPath } from 'url';
22
+ import { dirname, join } from 'path';
23
+ import { existsSync, mkdirSync, chmodSync, renameSync } from 'fs';
24
+ import { createReadStream, createWriteStream } from 'fs';
25
+ import { pipeline } from 'stream/promises';
26
+ import { unlink, readFile } from 'fs/promises';
27
+
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = dirname(__filename);
30
+ const rootDir = dirname(__dirname);
31
+
32
+ // Configuration
33
+ const REPOS_BASE_URL = 'https://repos.fmode.cn/x/fmode-studio';
34
+ const MANIFEST_URL = `${REPOS_BASE_URL}/manifest.json`;
35
+ const CACHE_DIR = join(rootDir, '.cache', 'binaries');
36
+
37
+ // Platform detection
38
+ const platform = process.platform;
39
+ const arch = process.arch;
40
+
41
+ // Map platform/arch to executable name
42
+ function getExecutableName() {
43
+ const ext = platform === 'win32' ? '.exe' : '';
44
+
45
+ if (platform === 'win32' && arch === 'x64') {
46
+ return `fmode-win-x64${ext}`;
47
+ } else if (platform === 'win32' && arch === 'arm64') {
48
+ return `fmode-win-arm64${ext}`;
49
+ } else if (platform === 'linux' && arch === 'x64') {
50
+ return 'fmode-linux-x64';
51
+ } else if (platform === 'linux' && arch === 'arm64') {
52
+ return 'fmode-linux-arm64';
53
+ } else if (platform === 'darwin' && arch === 'x64') {
54
+ return 'fmode-macos-x64';
55
+ } else if (platform === 'darwin' && arch === 'arm64') {
56
+ return 'fmode-macos-arm64';
57
+ } else {
58
+ throw new Error(`Unsupported platform: ${platform} ${arch}`);
59
+ }
60
+ }
61
+
62
+ // Get local package version
63
+ async function getLocalVersion() {
64
+ const packageJson = join(rootDir, 'package.json');
65
+ try {
66
+ const content = await readFile(packageJson, 'utf-8');
67
+ const pkg = JSON.parse(content);
68
+ return pkg.version;
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+
74
+ // Fetch remote manifest from repos.fmode.cn
75
+ async function fetchRemoteManifest() {
76
+ try {
77
+ const response = await fetch(MANIFEST_URL);
78
+ if (!response.ok) {
79
+ throw new Error(`HTTP ${response.status}`);
80
+ }
81
+ return await response.json();
82
+ } catch (error) {
83
+ console.warn(`Warning: Could not fetch remote manifest: ${error.message}`);
84
+ return null;
85
+ }
86
+ }
87
+
88
+ // Compare versions (returns positive if a > b, negative if a < b, 0 if equal)
89
+ function compareVersions(a, b) {
90
+ const partsA = a.split('.').map(Number);
91
+ const partsB = b.split('.').map(Number);
92
+
93
+ for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
94
+ const partA = partsA[i] || 0;
95
+ const partB = partsB[i] || 0;
96
+ if (partA !== partB) {
97
+ return partA - partB;
98
+ }
99
+ }
100
+ return 0;
101
+ }
102
+
103
+ // Download binary from repos.fmode.cn
104
+ async function downloadBinary(version, executableName) {
105
+ const url = `${REPOS_BASE_URL}/${version}/${executableName}`;
106
+ const tempPath = join(CACHE_DIR, `${executableName}.tmp`);
107
+ const finalPath = join(CACHE_DIR, executableName);
108
+
109
+ console.log(`Downloading ${url}...`);
110
+
111
+ try {
112
+ const response = await fetch(url);
113
+ if (!response.ok) {
114
+ throw new Error(`HTTP ${response.status}`);
115
+ }
116
+
117
+ // Ensure cache directory exists
118
+ if (!existsSync(CACHE_DIR)) {
119
+ mkdirSync(CACHE_DIR, { recursive: true });
120
+ }
121
+
122
+ // Download to temp file
123
+ const fileStream = createWriteStream(tempPath);
124
+ await pipeline(response.body, fileStream);
125
+
126
+ // Make executable (Unix-like systems)
127
+ if (platform !== 'win32') {
128
+ chmodSync(tempPath, 0o755);
129
+ }
130
+
131
+ // Move to final path
132
+ if (existsSync(finalPath)) {
133
+ await unlink(finalPath);
134
+ }
135
+ renameSync(tempPath, finalPath);
136
+
137
+ console.log(`Downloaded to ${finalPath}`);
138
+ return finalPath;
139
+ } catch (error) {
140
+ // Clean up temp file on error
141
+ if (existsSync(tempPath)) {
142
+ await unlink(tempPath).catch(() => {});
143
+ }
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ // Check for updates
149
+ async function checkForUpdates() {
150
+ const localVersion = await getLocalVersion();
151
+ const manifest = await fetchRemoteManifest();
152
+
153
+ if (!manifest || !manifest.version) {
154
+ console.log('Could not check for updates.');
155
+ return null;
156
+ }
157
+
158
+ if (!localVersion) {
159
+ console.log(`Remote version: ${manifest.version}`);
160
+ console.log('Local version: unknown');
161
+ return manifest.version;
162
+ }
163
+
164
+ const comparison = compareVersions(manifest.version, localVersion);
165
+
166
+ if (comparison > 0) {
167
+ console.log(`Update available: ${localVersion} -> ${manifest.version}`);
168
+ return manifest.version;
169
+ } else if (comparison === 0) {
170
+ console.log(`Already up to date: ${localVersion}`);
171
+ return null;
172
+ } else {
173
+ console.log(`Local version is newer: ${localVersion} > ${manifest.version}`);
174
+ return null;
175
+ }
176
+ }
177
+
178
+ // Run the executable
179
+ async function runExecutable(executablePath, args) {
180
+ const child = spawn(executablePath, args, {
181
+ stdio: 'inherit',
182
+ env: { ...process.env }
183
+ });
184
+
185
+ return new Promise((resolve, reject) => {
186
+ child.on('exit', (code) => {
187
+ resolve(code ?? 0);
188
+ process.exit(code ?? 0);
189
+ });
190
+
191
+ child.on('error', (err) => {
192
+ reject(err);
193
+ });
194
+ });
195
+ }
196
+
197
+ // Main execution
198
+ async function main() {
199
+ const args = process.argv.slice(2);
200
+
201
+ // Handle -y flag (auto-confirm, no-op in our case)
202
+ const yesIndex = args.indexOf('-y');
203
+ if (yesIndex !== -1) {
204
+ args.splice(yesIndex, 1);
205
+ }
206
+
207
+ // Handle update command
208
+ if (args[0] === 'update') {
209
+ console.log('Checking for updates...');
210
+ const newVersion = await checkForUpdates();
211
+
212
+ if (newVersion) {
213
+ const executableName = getExecutableName();
214
+ console.log(`\nDownloading version ${newVersion}...`);
215
+ try {
216
+ await downloadBinary(newVersion, executableName);
217
+ console.log('\n✓ Update complete!');
218
+ } catch (error) {
219
+ console.error(`\n✗ Download failed: ${error.message}`);
220
+ process.exit(1);
221
+ }
222
+ }
223
+ process.exit(0);
224
+ }
225
+
226
+ // Handle --version flag
227
+ if (args.includes('--version') || args.includes('-v')) {
228
+ const localVersion = await getLocalVersion();
229
+ if (localVersion) {
230
+ console.log(`@fmode/studio v${localVersion}`);
231
+ } else {
232
+ console.log('@fmode/studio (version unknown)');
233
+ }
234
+ process.exit(0);
235
+ }
236
+
237
+ // Handle --help flag
238
+ if (args.includes('--help') || (args.includes('-h') && !args.includes('--host'))) {
239
+ console.log(`
240
+ Fmode Code UI Server
241
+
242
+ USAGE:
243
+ fmode [OPTIONS]
244
+ npx @fmode/studio [OPTIONS]
245
+ fmode update Check and install updates
246
+
247
+ OPTIONS:
248
+ -p, --port <PORT> Set the server port (default: 6666)
249
+ -h, --host <HOST> Set the server host (default: 0.0.0.0)
250
+ --help Show this help message
251
+ --version, -v Show version information
252
+ -y Auto-confirm (no-op, for compatibility)
253
+ update Check and install updates
254
+
255
+ ENVIRONMENT VARIABLES:
256
+ PORT Server port (default: 6666)
257
+ HOST Server host (default: 0.0.0.0)
258
+ VITE_IS_PLATFORM Set to 'true' for platform mode
259
+
260
+ EXAMPLES:
261
+ fmode # Start with default settings
262
+ fmode --port 8080 # Start on port 8080
263
+ npx @fmode/studio -y # Start with npx (auto-confirm)
264
+ npx @fmode/studio --port 3000 # Start on port 3000
265
+ fmode update # Check for updates
266
+
267
+ UPDATE MECHANISM:
268
+ The CLI automatically downloads the latest binary from repos.fmode.cn
269
+ and caches it locally. Use 'fmode update' to manually check for updates.
270
+ `);
271
+ process.exit(0);
272
+ }
273
+
274
+ const executableName = getExecutableName();
275
+ let executablePath = null;
276
+
277
+ // Priority 1: Try cached binary (most recent download)
278
+ const cachedPath = join(CACHE_DIR, executableName);
279
+ if (existsSync(cachedPath)) {
280
+ executablePath = cachedPath;
281
+ console.log(`Using cached binary: ${cachedPath}`);
282
+ }
283
+
284
+ // Priority 2: Try bundled binary (shipped with npm package)
285
+ if (!executablePath) {
286
+ const bundledPath = join(rootDir, 'dist', 'bin', executableName);
287
+ if (existsSync(bundledPath)) {
288
+ executablePath = bundledPath;
289
+ }
290
+ }
291
+
292
+ // Priority 3: Download from repos.fmode.cn
293
+ if (!executablePath) {
294
+ console.log('No local binary found. Downloading...');
295
+ try {
296
+ const manifest = await fetchRemoteManifest();
297
+ if (manifest && manifest.version) {
298
+ executablePath = await downloadBinary(manifest.version, executableName);
299
+ }
300
+ } catch (error) {
301
+ console.warn(`Could not download binary: ${error.message}`);
302
+ }
303
+ }
304
+
305
+ // If we have an executable, run it
306
+ if (executablePath) {
307
+ try {
308
+ await runExecutable(executablePath, args);
309
+ return;
310
+ } catch (err) {
311
+ console.error('Failed to start native executable:', err.message);
312
+ // Fall through to Node.js/Bun fallback
313
+ }
314
+ }
315
+
316
+ // Priority 4: Fallback to Node.js/Bun
317
+ fallbackToNode(args);
318
+ }
319
+
320
+ function fallbackToNode(args) {
321
+ const cliPath = join(rootDir, 'cli.ts');
322
+
323
+ if (existsSync(cliPath)) {
324
+ console.log('Running with bun...');
325
+ const child = spawn('bun', [cliPath, ...args], {
326
+ stdio: 'inherit',
327
+ env: { ...process.env }
328
+ });
329
+
330
+ child.on('exit', (code) => {
331
+ process.exit(code ?? 0);
332
+ });
333
+
334
+ child.on('error', (err) => {
335
+ console.error('Failed to start with bun:', err.message);
336
+ console.error('Please install bun: https://bun.sh');
337
+ process.exit(1);
338
+ });
339
+ } else {
340
+ console.error(`Error: Fmode Studio not found for platform ${platform} ${arch}`);
341
+ console.error('Please reinstall the package or report this issue.');
342
+ process.exit(1);
343
+ }
344
+ }
345
+
346
+ // Run main function
347
+ main().catch((error) => {
348
+ console.error('Fatal error:', error);
349
+ process.exit(1);
350
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@fmode/studio",
3
+ "version": "0.0.2",
4
+ "description": "AI PaaS IDE for Vibe Coding - Cross-platform CLI tool",
5
+ "license": "ISC",
6
+ "author": "Fmode Studio Team",
7
+ "type": "module",
8
+ "bin": {
9
+ "fmode": "./bin/fmode.js",
10
+ "@fmode/studio": "./bin/fmode.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "bin",
15
+ "*.js",
16
+ "api"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18.0.0",
20
+ "bun": ">=1.0.0"
21
+ },
22
+ "os": [
23
+ "darwin",
24
+ "linux",
25
+ "win32"
26
+ ],
27
+ "cpu": [
28
+ "x64",
29
+ "arm64"
30
+ ],
31
+ "dependencies": {
32
+ "@aws-sdk/client-s3": "^3.965.0",
33
+ "@siteboon/claude-code-ui": "^1.13.6",
34
+ "bun-pty": "^0.4.6"
35
+ }
36
+ }