upfynai-code 2.8.5 → 2.8.6

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 CHANGED
@@ -2,35 +2,35 @@
2
2
 
3
3
  > Unified AI coding interface — access AI chat, terminal, file explorer, git, and visual canvas from any browser.
4
4
 
5
- **Upfyn-Code** connects your local development machine to a browser-based UI through a secure WebSocket relay. Write code with AI assistance, run shell commands, manage files, handle git operations, and plan on a visual canvas — all from any device, anywhere.
5
+ **Upfyn-Code** connects your local development machine to a browser-based UI through a secure, encrypted connection. Write code with AI assistance, run shell commands, manage files, handle git operations, and plan on a visual canvas — all from any device, anywhere.
6
6
 
7
- Your source code **never leaves your machine**. All file operations, terminal commands, and git actions execute locally through the relay connection.
7
+ Your source code **never leaves your machine**. All file operations, terminal commands, and git actions execute locally.
8
8
 
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- npm install -g upfyn-code
12
+ npm install -g upfynai-code
13
13
  ```
14
14
 
15
15
  Or run directly with npx:
16
16
 
17
17
  ```bash
18
- npx upfyn-code
18
+ npx upfynai-code
19
19
  ```
20
20
 
21
- **Requirements:** Node.js 18 or later. Works on Windows, macOS, and Linux.
21
+ **Requirements:** Node.js 22 or later. Works on Windows, macOS, and Linux.
22
22
 
23
23
  ## Quick Start
24
24
 
25
25
  ```bash
26
26
  # 1. Authenticate with your Upfyn account
27
- upfyn-code login
27
+ uc login
28
28
 
29
29
  # 2. Connect your machine to the web interface
30
- upfyn-code connect
30
+ uc connect
31
31
 
32
32
  # 3. Open the web UI (opens automatically)
33
- upfyn-code
33
+ uc start
34
34
  ```
35
35
 
36
36
  Once connected, open [cli.upfyn.com](https://cli.upfyn.com) on any device to access your full development environment.
@@ -39,45 +39,22 @@ Once connected, open [cli.upfyn.com](https://cli.upfyn.com) on any device to acc
39
39
 
40
40
  | Command | Description |
41
41
  |---------|-------------|
42
- | `upfyn-code` | Open the hosted web interface (default) |
43
- | `upfyn-code --local` | Start a local server instead of the hosted app |
44
- | `upfyn-code login` | Authenticate with your Upfyn account |
45
- | `upfyn-code logout` | Clear saved credentials |
46
- | `upfyn-code status` | Show current auth status and configuration |
47
- | `upfyn-code connect` | Connect your local machine to the remote server |
48
- | `upfyn-code mcp` | Show MCP integration config for Claude / Cursor |
42
+ | `uc` | Launch Claude Code with Upfyn web UI in background |
43
+ | `uc start` | Start the web UI server |
44
+ | `uc connect` | Connect your local machine to the cloud interface |
45
+ | `uc login` | Authenticate with your Upfyn account |
46
+ | `uc logout` | Clear saved credentials |
47
+ | `uc status` | Show current configuration |
48
+ | `uc config` | View or set configuration |
49
+ | `uc help` | Show all available commands |
49
50
 
50
51
  ### Connect Options
51
52
 
52
53
  ```bash
53
- upfyn-code connect --server <url> --key <token>
54
+ uc connect --key <token>
54
55
  ```
55
56
 
56
- - `--server <url>` — Server URL to connect to
57
- - `--key <token>` — Relay token (get from the web UI after signing in)
58
-
59
- ### MCP Integration
60
-
61
- ```bash
62
- upfyn-code mcp --server <url> --key <token>
63
- ```
64
-
65
- Outputs the MCP (Model Context Protocol) configuration JSON for use with Claude Desktop, Cursor, or other MCP-compatible tools.
66
-
67
- **Example output:**
68
-
69
- ```json
70
- {
71
- "mcpServers": {
72
- "upfyn-code": {
73
- "url": "https://your-server.com/mcp",
74
- "headers": {
75
- "Authorization": "Bearer rt_xxxxx"
76
- }
77
- }
78
- }
79
- }
80
- ```
57
+ - `--key <token>` — Your connection token (get from the web UI after signing in)
81
58
 
82
59
  ## Features
83
60
 
@@ -102,55 +79,18 @@ Connect external tools and services via the Model Context Protocol. Use Upfyn-Co
102
79
  ## How It Works
103
80
 
104
81
  1. **Install** the CLI on your development machine
105
- 2. **Authenticate** with `upfyn-code login`
106
- 3. **Connect** with `upfyn-code connect` — this creates a secure WebSocket tunnel
82
+ 2. **Authenticate** with `uc login`
83
+ 3. **Connect** with `uc connect` — creates a secure, encrypted connection
107
84
  4. **Open** [cli.upfyn.com](https://cli.upfyn.com) on any browser (desktop, tablet, or mobile)
108
85
 
109
- The CLI bridges your local filesystem, terminal, and git to the web interface. No ports are exposed and no firewall configuration is needed — the connection works through a secure relay.
110
-
111
- ```
112
- ┌─────────────┐ WebSocket ┌──────────────┐ Browser ┌──────────────┐
113
- │ Your Local │ ──────────────▶ │ Upfyn Relay │ ◀────────────── │ Web UI at │
114
- │ Machine │ (encrypted) │ Server │ │ cli.upfyn.com│
115
- └─────────────┘ └──────────────┘ └──────────────┘
116
- ```
86
+ The CLI connects your local filesystem, terminal, and git to the web interface. No ports are exposed and no firewall configuration is needed.
117
87
 
118
88
  ## Security
119
89
 
120
- - All communication is encrypted with TLS
121
- - Source code is relayed in real-time and **never stored** on servers
122
- - Authentication uses JWT tokens with httpOnly cookies
123
- - The relay connection requires both a valid session and a connection token
124
- - Self-hosting is supported for full data sovereignty
125
-
126
- ## Self-Hosting
127
-
128
- Upfyn-Code is open source under the GPL-3.0 license. You can deploy the entire stack on your own infrastructure:
129
-
130
- ```bash
131
- git clone https://github.com/AnitChaudhry/UpfynAI-Code.git
132
- cd UpfynAI-Code
133
-
134
- # Install dependencies
135
- npm install
136
-
137
- # Start the backend
138
- cd backend && npm start
139
-
140
- # Start the frontend
141
- cd frontend && npm run dev
142
- ```
143
-
144
- See the [repository](https://github.com/AnitChaudhry/UpfynAI-Code) for full deployment instructions.
145
-
146
- ## Configuration
147
-
148
- Credentials and config are stored locally at:
149
-
150
- | Platform | Path |
151
- |----------|------|
152
- | macOS / Linux | `~/.config/upfyn-code/` |
153
- | Windows | `%APPDATA%\upfyn-code\` |
90
+ - All communication is encrypted end-to-end
91
+ - Source code is **never stored** on servers
92
+ - Secure authentication with automatic session management
93
+ - Each user's environment is fully isolated
154
94
 
155
95
  ## Links
156
96
 
@@ -163,4 +103,4 @@ Credentials and config are stored locally at:
163
103
 
164
104
  GPL-3.0 — see [LICENSE](https://github.com/AnitChaudhry/UpfynAI-Code/blob/main/LICENSE) for details.
165
105
 
166
- Built by [Thinqmesh Technologies](https://cli.upfyn.com).
106
+ Built by [Upfynai-Code](https://cli.upfyn.com).
package/bin/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import { readFileSync } from 'fs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "upfynai-code",
3
- "version": "2.8.5",
3
+ "version": "2.8.6",
4
4
  "description": "Unified AI coding interface — access AI chat, terminal, file explorer, git, and visual canvas from any browser. Connect your local machine and code from anywhere.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,6 +67,9 @@
67
67
  "prompts": "^2.4.2",
68
68
  "ws": "^8.18.0"
69
69
  },
70
+ "optionalDependencies": {
71
+ "node-pty": "^1.1.0-beta34"
72
+ },
70
73
  "engines": {
71
74
  "node": ">=18.0.0"
72
75
  },
package/src/connect.js CHANGED
@@ -11,6 +11,10 @@ import { getPersistentShell } from './persistent-shell.js';
11
11
  import { needsPermission, requestPermission, handlePermissionResponse } from './permissions.js';
12
12
  import { playConnectAnimation } from './animation.js';
13
13
 
14
+ // Optional node-pty for proper terminal emulation (graceful fallback to spawn)
15
+ let pty;
16
+ try { pty = (await import('node-pty')).default; } catch { pty = null; }
17
+
14
18
  // Resolve agents: dist/agents/ (npm package) or ../../shared/agents/ (monorepo)
15
19
  const __connectDir = dirname(fileURLToPath(import.meta.url));
16
20
  const _agentsPath = existsSync(join(__connectDir, '../dist/agents/index.js'))
@@ -26,15 +30,17 @@ const activeShellSessions = new Map();
26
30
 
27
31
  /**
28
32
  * Start an interactive shell session on the local machine, relayed to the browser.
29
- * Spawns a PTY-like process and streams I/O via WebSocket.
33
+ * Uses node-pty for proper terminal emulation when available, falls back to spawn.
30
34
  */
31
35
  function handleShellSessionStart(data, ws) {
32
36
  const shellSessionId = data.requestId;
33
37
  const projectPath = data.projectPath || process.cwd();
34
38
  const isWin = process.platform === 'win32';
35
39
  const shellType = data.shellType;
40
+ const cols = data.cols || 80;
41
+ const rows = data.rows || 24;
36
42
 
37
- console.log(chalk.cyan(` [relay] Starting shell session in ${projectPath}`));
43
+ console.log(chalk.cyan(` [relay] Starting shell session in ${projectPath}${pty ? ' (pty)' : ' (spawn)'}`));
38
44
 
39
45
  let shellCmd, shellArgs;
40
46
  const provider = data.provider || 'claude';
@@ -42,8 +48,8 @@ function handleShellSessionStart(data, ws) {
42
48
 
43
49
  function getInteractiveShell(projectDir) {
44
50
  if (isWin) {
45
- if (shellType === 'cmd') return { cmd: 'cmd.exe', args: ['/K', `cd /d "${projectDir}"`] };
46
- return { cmd: 'powershell.exe', args: ['-NoExit', '-Command', `Set-Location -LiteralPath '${projectDir.replace(/'/g, "''")}'`] };
51
+ if (shellType === 'cmd') return { cmd: 'cmd.exe', args: [] };
52
+ return { cmd: 'powershell.exe', args: ['-NoExit'] };
47
53
  }
48
54
  const sh = shellType || process.env.SHELL || 'bash';
49
55
  return { cmd: sh, args: ['--login'] };
@@ -88,40 +94,68 @@ function handleShellSessionStart(data, ws) {
88
94
 
89
95
  ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: '' }));
90
96
 
91
- const proc = spawn(shellCmd, shellArgs, {
92
- cwd: isPlainShell && !data.initialCommand ? projectPath : undefined,
93
- env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor', FORCE_COLOR: '3' },
94
- stdio: ['pipe', 'pipe', 'pipe'],
95
- });
97
+ // Use node-pty for proper terminal emulation (resize, isatty, line discipline)
98
+ if (pty) {
99
+ const proc = pty.spawn(shellCmd, shellArgs, {
100
+ name: 'xterm-256color',
101
+ cols,
102
+ rows,
103
+ cwd: projectPath,
104
+ env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor', FORCE_COLOR: '3' },
105
+ });
96
106
 
97
- activeShellSessions.set(shellSessionId, { proc, projectPath });
107
+ activeShellSessions.set(shellSessionId, { proc, projectPath, isPty: true });
98
108
 
99
- proc.stdout.on('data', (chunk) => {
100
- if (ws.readyState === WebSocket.OPEN) {
101
- ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: chunk.toString() }));
102
- }
103
- });
109
+ proc.onData((chunk) => {
110
+ if (ws.readyState === WebSocket.OPEN) {
111
+ ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: chunk }));
112
+ }
113
+ });
104
114
 
105
- proc.stderr.on('data', (chunk) => {
106
- if (ws.readyState === WebSocket.OPEN) {
107
- ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: chunk.toString() }));
108
- }
109
- });
115
+ proc.onExit(({ exitCode }) => {
116
+ activeShellSessions.delete(shellSessionId);
117
+ if (ws.readyState === WebSocket.OPEN) {
118
+ ws.send(JSON.stringify({ type: 'relay-shell-exited', shellSessionId, exitCode }));
119
+ }
120
+ console.log(chalk.dim(` [relay] Shell session ended (code ${exitCode})`));
121
+ });
122
+ } else {
123
+ // Fallback: spawn without PTY (text wrapping may be incorrect)
124
+ const proc = spawn(shellCmd, shellArgs, {
125
+ cwd: projectPath,
126
+ env: { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor', FORCE_COLOR: '3', COLUMNS: String(cols), LINES: String(rows) },
127
+ stdio: ['pipe', 'pipe', 'pipe'],
128
+ });
110
129
 
111
- proc.on('close', (code) => {
112
- activeShellSessions.delete(shellSessionId);
113
- if (ws.readyState === WebSocket.OPEN) {
114
- ws.send(JSON.stringify({ type: 'relay-shell-exited', shellSessionId, exitCode: code }));
115
- }
116
- console.log(chalk.dim(` [relay] Shell session ended (code ${code})`));
117
- });
130
+ activeShellSessions.set(shellSessionId, { proc, projectPath, isPty: false });
118
131
 
119
- proc.on('error', (err) => {
120
- activeShellSessions.delete(shellSessionId);
121
- if (ws.readyState === WebSocket.OPEN) {
122
- ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: `\r\n\x1b[31mError: ${err.message}\x1b[0m\r\n` }));
123
- }
124
- });
132
+ proc.stdout.on('data', (chunk) => {
133
+ if (ws.readyState === WebSocket.OPEN) {
134
+ ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: chunk.toString() }));
135
+ }
136
+ });
137
+
138
+ proc.stderr.on('data', (chunk) => {
139
+ if (ws.readyState === WebSocket.OPEN) {
140
+ ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: chunk.toString() }));
141
+ }
142
+ });
143
+
144
+ proc.on('close', (code) => {
145
+ activeShellSessions.delete(shellSessionId);
146
+ if (ws.readyState === WebSocket.OPEN) {
147
+ ws.send(JSON.stringify({ type: 'relay-shell-exited', shellSessionId, exitCode: code }));
148
+ }
149
+ console.log(chalk.dim(` [relay] Shell session ended (code ${code})`));
150
+ });
151
+
152
+ proc.on('error', (err) => {
153
+ activeShellSessions.delete(shellSessionId);
154
+ if (ws.readyState === WebSocket.OPEN) {
155
+ ws.send(JSON.stringify({ type: 'relay-shell-output', shellSessionId, data: `\r\n\x1b[31mError: ${err.message}\x1b[0m\r\n` }));
156
+ }
157
+ });
158
+ }
125
159
  }
126
160
 
127
161
  /**
@@ -292,14 +326,30 @@ export async function connect(options = {}) {
292
326
  }
293
327
  if (data.type === 'relay-shell-input') {
294
328
  const session = activeShellSessions.get(data.shellSessionId);
295
- if (session?.proc?.stdin?.writable) session.proc.stdin.write(data.data);
329
+ if (session) {
330
+ if (session.isPty) {
331
+ session.proc.write(data.data);
332
+ } else if (session.proc?.stdin?.writable) {
333
+ session.proc.stdin.write(data.data);
334
+ }
335
+ }
336
+ return;
337
+ }
338
+ if (data.type === 'relay-shell-resize') {
339
+ const session = activeShellSessions.get(data.shellSessionId);
340
+ if (session?.isPty && session.proc?.resize) {
341
+ try { session.proc.resize(data.cols || 80, data.rows || 24); } catch { /* ignore resize errors */ }
342
+ }
296
343
  return;
297
344
  }
298
- if (data.type === 'relay-shell-resize') return;
299
345
  if (data.type === 'relay-shell-kill') {
300
346
  const session = activeShellSessions.get(data.shellSessionId);
301
347
  if (session?.proc) {
302
- session.proc.kill('SIGTERM');
348
+ if (session.isPty) {
349
+ session.proc.kill();
350
+ } else {
351
+ session.proc.kill('SIGTERM');
352
+ }
303
353
  activeShellSessions.delete(data.shellSessionId);
304
354
  console.log(chalk.dim(' [relay] Shell session killed'));
305
355
  }