cloudforge-agent 0.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/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/files.d.ts +84 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +285 -0
- package/dist/files.js.map +1 -0
- package/dist/git.d.ts +97 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +320 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/terminal.d.ts +62 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +207 -0
- package/dist/terminal.js.map +1 -0
- package/dist/websocket.d.ts +60 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +695 -0
- package/dist/websocket.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kenzo ARAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# CloudForge Agent
|
|
2
|
+
|
|
3
|
+
Lightweight agent that connects your server to [CloudForge](https://cloud-forge.me) — remote AI coding from anywhere.
|
|
4
|
+
|
|
5
|
+
CloudForge Agent runs on your server and establishes an outbound WebSocket connection to CloudForge. Your code stays on your server; CloudForge only relays terminal I/O, file operations, and Git commands.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Terminal sessions** via node-pty (full PTY support)
|
|
10
|
+
- **File operations** — browse, read, write files remotely
|
|
11
|
+
- **Git integration** — status, add, commit, push, pull, diff, branch management
|
|
12
|
+
- **Outbound-only connection** — no firewall or port forwarding needed
|
|
13
|
+
- **Auto-reconnection** with exponential backoff
|
|
14
|
+
- **Heartbeat** with system info reporting
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- Node.js >= 18
|
|
19
|
+
- A CloudForge account and server token (get one at [cloud-forge.me](https://cloud-forge.me))
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### npx (quickest)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx cloudforge-agent --token YOUR_TOKEN
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### npm global install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g cloudforge-agent
|
|
33
|
+
cloudforge-agent --token YOUR_TOKEN
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### From source
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/rascal-3/cloudforge-agent.git
|
|
40
|
+
cd cloudforge-agent
|
|
41
|
+
npm install
|
|
42
|
+
npm run build
|
|
43
|
+
npm start -- --token YOUR_TOKEN
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Docker
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
docker run -d \
|
|
50
|
+
--name cloudforge-agent \
|
|
51
|
+
--restart unless-stopped \
|
|
52
|
+
-v /path/to/projects:/home/user \
|
|
53
|
+
cloudforge/agent:latest \
|
|
54
|
+
--token YOUR_TOKEN
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cloudforge-agent --token YOUR_TOKEN [options]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Options
|
|
64
|
+
|
|
65
|
+
| Option | Description | Default |
|
|
66
|
+
|--------|-------------|---------|
|
|
67
|
+
| `--token` | Server authentication token (required) | — |
|
|
68
|
+
| `--server` | CloudForge server URL | `https://cloud-forge.me` |
|
|
69
|
+
| `--home` | Home directory for terminal sessions | `~` |
|
|
70
|
+
| `--debug` | Enable debug logging | `false` |
|
|
71
|
+
|
|
72
|
+
### Environment variables
|
|
73
|
+
|
|
74
|
+
You can also configure via environment variables or a `.env` file:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
CLOUDFORGE_TOKEN=your_token
|
|
78
|
+
CLOUDFORGE_SERVER=https://cloud-forge.me
|
|
79
|
+
CLOUDFORGE_HOME=/home/user
|
|
80
|
+
CLOUDFORGE_DEBUG=true
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
┌─────────────────────┐ ┌─────────────────────┐
|
|
87
|
+
│ Your Browser │ │ Your Server │
|
|
88
|
+
│ (CloudForge UI) │ │ │
|
|
89
|
+
│ │ │ CloudForge Agent │
|
|
90
|
+
│ Terminal ─────────┤ ├──── node-pty │
|
|
91
|
+
│ File Tree ────────┤ WebSocket│──── File I/O │
|
|
92
|
+
│ Git Panel ────────┤◄────────►├──── Git CLI │
|
|
93
|
+
│ Code Editor ──────┤ │ │
|
|
94
|
+
└─────────────────────┘ └─────────────────────┘
|
|
95
|
+
▲ │
|
|
96
|
+
│ CloudForge SaaS │
|
|
97
|
+
└── relay only ──────┘
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- Agent connects **outbound** to CloudForge (no inbound ports needed)
|
|
101
|
+
- All code stays on your server
|
|
102
|
+
- CloudForge relays commands between your browser and the agent
|
|
103
|
+
|
|
104
|
+
## Supported AI Coding Tools
|
|
105
|
+
|
|
106
|
+
CloudForge works with any CLI tool running in the terminal:
|
|
107
|
+
|
|
108
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (Anthropic)
|
|
109
|
+
- [Codex CLI](https://github.com/openai/codex) (OpenAI)
|
|
110
|
+
- [Gemini CLI](https://github.com/google/gemini-cli) (Google)
|
|
111
|
+
- [Aider](https://aider.chat/)
|
|
112
|
+
- Any other terminal-based tool
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install
|
|
118
|
+
npm run dev # watch mode with tsx
|
|
119
|
+
npm run build # compile TypeScript
|
|
120
|
+
npm run typecheck
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
[MIT](LICENSE)
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudForge Agent Configuration
|
|
3
|
+
*/
|
|
4
|
+
export interface AgentConfig {
|
|
5
|
+
token: string;
|
|
6
|
+
serverUrl: string;
|
|
7
|
+
heartbeatInterval: number;
|
|
8
|
+
reconnectDelay: number;
|
|
9
|
+
maxReconnectDelay: number;
|
|
10
|
+
shell: string;
|
|
11
|
+
homeDir: string;
|
|
12
|
+
debug: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create agent configuration from environment and CLI options
|
|
16
|
+
*/
|
|
17
|
+
export declare function createConfig(options: {
|
|
18
|
+
token?: string;
|
|
19
|
+
serverUrl?: string;
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
}): AgentConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Get system information for heartbeat
|
|
24
|
+
*/
|
|
25
|
+
export declare function getSystemInfo(): {
|
|
26
|
+
os: string;
|
|
27
|
+
hostname: string;
|
|
28
|
+
platform: string;
|
|
29
|
+
arch: string;
|
|
30
|
+
nodeVersion: string;
|
|
31
|
+
homeDir: string;
|
|
32
|
+
shell: string;
|
|
33
|
+
};
|
|
34
|
+
export declare const VERSION = "0.1.0";
|
|
35
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf;AAiBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,GAAG,WAAW,CAsBd;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,CAUA;AAED,eAAO,MAAM,OAAO,UAAU,CAAA"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudForge Agent Configuration
|
|
3
|
+
*/
|
|
4
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
// Load .env file if exists
|
|
7
|
+
dotenvConfig();
|
|
8
|
+
const DEFAULT_SERVER_URL = 'https://cloud-forge.me';
|
|
9
|
+
const DEFAULT_HEARTBEAT_INTERVAL = 30000; // 30 seconds
|
|
10
|
+
const DEFAULT_RECONNECT_DELAY = 1000; // 1 second
|
|
11
|
+
const MAX_RECONNECT_DELAY = 30000; // 30 seconds
|
|
12
|
+
/**
|
|
13
|
+
* Get the default shell for the current platform
|
|
14
|
+
*/
|
|
15
|
+
function getDefaultShell() {
|
|
16
|
+
if (process.platform === 'win32') {
|
|
17
|
+
return process.env.COMSPEC || 'cmd.exe';
|
|
18
|
+
}
|
|
19
|
+
return process.env.SHELL || '/bin/bash';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create agent configuration from environment and CLI options
|
|
23
|
+
*/
|
|
24
|
+
export function createConfig(options) {
|
|
25
|
+
const token = options.token || process.env.CLOUDFORGE_TOKEN;
|
|
26
|
+
if (!token) {
|
|
27
|
+
throw new Error('Agent token is required. Use --token flag or set CLOUDFORGE_TOKEN environment variable.');
|
|
28
|
+
}
|
|
29
|
+
// Validate token format (cf_ prefix + 32 hex chars)
|
|
30
|
+
if (!/^cf_[a-f0-9]{32}$/.test(token)) {
|
|
31
|
+
throw new Error('Invalid token format. Token should start with "cf_" followed by 32 hex characters.');
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
token,
|
|
35
|
+
serverUrl: options.serverUrl || process.env.CLOUDFORGE_SERVER_URL || DEFAULT_SERVER_URL,
|
|
36
|
+
heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL,
|
|
37
|
+
reconnectDelay: DEFAULT_RECONNECT_DELAY,
|
|
38
|
+
maxReconnectDelay: MAX_RECONNECT_DELAY,
|
|
39
|
+
shell: getDefaultShell(),
|
|
40
|
+
homeDir: os.homedir(),
|
|
41
|
+
debug: options.debug || process.env.CLOUDFORGE_DEBUG === 'true',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get system information for heartbeat
|
|
46
|
+
*/
|
|
47
|
+
export function getSystemInfo() {
|
|
48
|
+
return {
|
|
49
|
+
os: `${os.type()} ${os.release()}`,
|
|
50
|
+
hostname: os.hostname(),
|
|
51
|
+
platform: process.platform,
|
|
52
|
+
arch: process.arch,
|
|
53
|
+
nodeVersion: process.version,
|
|
54
|
+
homeDir: os.homedir(),
|
|
55
|
+
shell: getDefaultShell(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export const VERSION = '0.1.0';
|
|
59
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,2BAA2B;AAC3B,YAAY,EAAE,CAAA;AAad,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AACnD,MAAM,0BAA0B,GAAG,KAAK,CAAA,CAAC,aAAa;AACtD,MAAM,uBAAuB,GAAG,IAAI,CAAA,CAAC,WAAW;AAChD,MAAM,mBAAmB,GAAG,KAAK,CAAA,CAAC,aAAa;AAE/C;;GAEG;AACH,SAAS,eAAe;IACtB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS,CAAA;IACzC,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW,CAAA;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAI5B;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAE3D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAA;IAC5G,CAAC;IAED,oDAAoD;IACpD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAA;IACvG,CAAC;IAED,OAAO;QACL,KAAK;QACL,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,kBAAkB;QACvF,iBAAiB,EAAE,0BAA0B;QAC7C,cAAc,EAAE,uBAAuB;QACvC,iBAAiB,EAAE,mBAAmB;QACtC,KAAK,EAAE,eAAe,EAAE;QACxB,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;QACrB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,MAAM;KAChE,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAS3B,OAAO;QACL,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;QAClC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;QACvB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;QACrB,KAAK,EAAE,eAAe,EAAE;KACzB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudForge Agent File Manager
|
|
3
|
+
* Manages file operations for remote file access
|
|
4
|
+
*/
|
|
5
|
+
import type { AgentConfig } from './config.js';
|
|
6
|
+
export interface FileEntry {
|
|
7
|
+
name: string;
|
|
8
|
+
path: string;
|
|
9
|
+
type: 'file' | 'directory' | 'symlink' | 'other';
|
|
10
|
+
size: number;
|
|
11
|
+
modified: string;
|
|
12
|
+
permissions: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ReadFileResult {
|
|
15
|
+
path: string;
|
|
16
|
+
content: string;
|
|
17
|
+
encoding: 'utf8' | 'base64';
|
|
18
|
+
size: number;
|
|
19
|
+
isBinary: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface WriteFileResult {
|
|
22
|
+
path: string;
|
|
23
|
+
success: boolean;
|
|
24
|
+
bytesWritten: number;
|
|
25
|
+
}
|
|
26
|
+
export interface FileOperationResult {
|
|
27
|
+
path: string;
|
|
28
|
+
success: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class FileManager {
|
|
32
|
+
private config;
|
|
33
|
+
private basePath;
|
|
34
|
+
constructor(config: AgentConfig);
|
|
35
|
+
/**
|
|
36
|
+
* Validate and resolve a path, preventing directory traversal attacks
|
|
37
|
+
*/
|
|
38
|
+
private resolvePath;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a file is binary based on extension
|
|
41
|
+
*/
|
|
42
|
+
private isBinaryFile;
|
|
43
|
+
/**
|
|
44
|
+
* Convert file stats to permissions string
|
|
45
|
+
*/
|
|
46
|
+
private formatPermissions;
|
|
47
|
+
/**
|
|
48
|
+
* Get file type from stats
|
|
49
|
+
*/
|
|
50
|
+
private getFileType;
|
|
51
|
+
/**
|
|
52
|
+
* List directory contents
|
|
53
|
+
*/
|
|
54
|
+
list(dirPath: string): Promise<FileEntry[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Read file contents
|
|
57
|
+
*/
|
|
58
|
+
read(filePath: string): Promise<ReadFileResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Write file contents
|
|
61
|
+
*/
|
|
62
|
+
write(filePath: string, content: string, encoding?: 'utf8' | 'base64'): Promise<WriteFileResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Delete a file or directory
|
|
65
|
+
*/
|
|
66
|
+
delete(targetPath: string, recursive?: boolean): Promise<FileOperationResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Create a directory
|
|
69
|
+
*/
|
|
70
|
+
mkdir(dirPath: string, recursive?: boolean): Promise<FileOperationResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Rename/move a file or directory
|
|
73
|
+
*/
|
|
74
|
+
rename(fromPath: string, toPath: string): Promise<FileOperationResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Get file/directory stats
|
|
77
|
+
*/
|
|
78
|
+
stat(targetPath: string): Promise<FileEntry | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Check if path exists
|
|
81
|
+
*/
|
|
82
|
+
exists(targetPath: string): Promise<boolean>;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAgB9C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAA;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,MAAM,EAAE,WAAW;IAK/B;;OAEG;IACH,OAAO,CAAC,WAAW;IAqBnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAyCjD;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsCrD;;OAEG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,QAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6B9G;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,GAAE,OAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA2B1F;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,GAAE,OAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgBrF;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAmB5E;;OAEG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsBzD;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CASnD"}
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudForge Agent File Manager
|
|
3
|
+
* Manages file operations for remote file access
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
// Maximum file size to read (5MB)
|
|
10
|
+
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
11
|
+
// Binary file extensions
|
|
12
|
+
const BINARY_EXTENSIONS = new Set([
|
|
13
|
+
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.svg',
|
|
14
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
15
|
+
'.zip', '.tar', '.gz', '.rar', '.7z',
|
|
16
|
+
'.exe', '.dll', '.so', '.dylib',
|
|
17
|
+
'.mp3', '.mp4', '.avi', '.mkv', '.mov',
|
|
18
|
+
'.ttf', '.otf', '.woff', '.woff2',
|
|
19
|
+
'.pyc', '.class', '.o', '.a',
|
|
20
|
+
]);
|
|
21
|
+
export class FileManager {
|
|
22
|
+
config;
|
|
23
|
+
basePath;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.basePath = config.homeDir;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate and resolve a path, preventing directory traversal attacks
|
|
30
|
+
*/
|
|
31
|
+
resolvePath(requestedPath) {
|
|
32
|
+
// Expand ~ to home directory
|
|
33
|
+
let expanded = requestedPath;
|
|
34
|
+
if (expanded === '~' || expanded.startsWith('~/')) {
|
|
35
|
+
expanded = expanded.replace('~', os.homedir());
|
|
36
|
+
}
|
|
37
|
+
// Normalize the path
|
|
38
|
+
const normalized = path.normalize(expanded);
|
|
39
|
+
// If it's an absolute path, use it directly
|
|
40
|
+
// If it's relative, resolve from basePath
|
|
41
|
+
const resolved = path.isAbsolute(normalized)
|
|
42
|
+
? normalized
|
|
43
|
+
: path.resolve(this.basePath, normalized);
|
|
44
|
+
// For security, we don't restrict to basePath - the agent runs on user's server
|
|
45
|
+
// User already has full access to their filesystem via terminal
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a file is binary based on extension
|
|
50
|
+
*/
|
|
51
|
+
isBinaryFile(filePath) {
|
|
52
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
53
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Convert file stats to permissions string
|
|
57
|
+
*/
|
|
58
|
+
formatPermissions(mode) {
|
|
59
|
+
const perms = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
|
|
60
|
+
const user = perms[(mode >> 6) & 7];
|
|
61
|
+
const group = perms[(mode >> 3) & 7];
|
|
62
|
+
const other = perms[mode & 7];
|
|
63
|
+
return `${user}${group}${other}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get file type from stats
|
|
67
|
+
*/
|
|
68
|
+
getFileType(stats) {
|
|
69
|
+
if (stats.isFile())
|
|
70
|
+
return 'file';
|
|
71
|
+
if (stats.isDirectory())
|
|
72
|
+
return 'directory';
|
|
73
|
+
if (stats.isSymbolicLink())
|
|
74
|
+
return 'symlink';
|
|
75
|
+
return 'other';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* List directory contents
|
|
79
|
+
*/
|
|
80
|
+
async list(dirPath) {
|
|
81
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
82
|
+
if (this.config.debug) {
|
|
83
|
+
console.log(chalk.gray(`Files list: ${resolvedPath}`));
|
|
84
|
+
}
|
|
85
|
+
const entries = await fs.readdir(resolvedPath, { withFileTypes: true });
|
|
86
|
+
const results = [];
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
try {
|
|
89
|
+
const entryPath = path.join(resolvedPath, entry.name);
|
|
90
|
+
const stats = await fs.stat(entryPath);
|
|
91
|
+
results.push({
|
|
92
|
+
name: entry.name,
|
|
93
|
+
path: entryPath,
|
|
94
|
+
type: this.getFileType(stats),
|
|
95
|
+
size: stats.size,
|
|
96
|
+
modified: stats.mtime.toISOString(),
|
|
97
|
+
permissions: this.formatPermissions(stats.mode),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
// Skip entries we can't stat (permission denied, etc.)
|
|
102
|
+
if (this.config.debug) {
|
|
103
|
+
console.log(chalk.yellow(`Cannot stat: ${entry.name}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Sort: directories first, then files, alphabetically
|
|
108
|
+
results.sort((a, b) => {
|
|
109
|
+
if (a.type === 'directory' && b.type !== 'directory')
|
|
110
|
+
return -1;
|
|
111
|
+
if (a.type !== 'directory' && b.type === 'directory')
|
|
112
|
+
return 1;
|
|
113
|
+
return a.name.localeCompare(b.name);
|
|
114
|
+
});
|
|
115
|
+
return results;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Read file contents
|
|
119
|
+
*/
|
|
120
|
+
async read(filePath) {
|
|
121
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
122
|
+
if (this.config.debug) {
|
|
123
|
+
console.log(chalk.gray(`File read: ${resolvedPath}`));
|
|
124
|
+
}
|
|
125
|
+
// Check file size first
|
|
126
|
+
const stats = await fs.stat(resolvedPath);
|
|
127
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
128
|
+
throw new Error(`File too large (${stats.size} bytes). Maximum size is ${MAX_FILE_SIZE} bytes.`);
|
|
129
|
+
}
|
|
130
|
+
const isBinary = this.isBinaryFile(resolvedPath);
|
|
131
|
+
if (isBinary) {
|
|
132
|
+
// Read as base64 for binary files
|
|
133
|
+
const buffer = await fs.readFile(resolvedPath);
|
|
134
|
+
return {
|
|
135
|
+
path: resolvedPath,
|
|
136
|
+
content: buffer.toString('base64'),
|
|
137
|
+
encoding: 'base64',
|
|
138
|
+
size: stats.size,
|
|
139
|
+
isBinary: true,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Read as UTF-8 for text files
|
|
144
|
+
const content = await fs.readFile(resolvedPath, 'utf8');
|
|
145
|
+
return {
|
|
146
|
+
path: resolvedPath,
|
|
147
|
+
content,
|
|
148
|
+
encoding: 'utf8',
|
|
149
|
+
size: stats.size,
|
|
150
|
+
isBinary: false,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Write file contents
|
|
156
|
+
*/
|
|
157
|
+
async write(filePath, content, encoding = 'utf8') {
|
|
158
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
159
|
+
if (this.config.debug) {
|
|
160
|
+
console.log(chalk.gray(`File write: ${resolvedPath}`));
|
|
161
|
+
}
|
|
162
|
+
// Ensure parent directory exists
|
|
163
|
+
const dir = path.dirname(resolvedPath);
|
|
164
|
+
await fs.mkdir(dir, { recursive: true });
|
|
165
|
+
if (encoding === 'base64') {
|
|
166
|
+
const buffer = Buffer.from(content, 'base64');
|
|
167
|
+
await fs.writeFile(resolvedPath, buffer);
|
|
168
|
+
return {
|
|
169
|
+
path: resolvedPath,
|
|
170
|
+
success: true,
|
|
171
|
+
bytesWritten: buffer.length,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
await fs.writeFile(resolvedPath, content, 'utf8');
|
|
176
|
+
return {
|
|
177
|
+
path: resolvedPath,
|
|
178
|
+
success: true,
|
|
179
|
+
bytesWritten: Buffer.byteLength(content, 'utf8'),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Delete a file or directory
|
|
185
|
+
*/
|
|
186
|
+
async delete(targetPath, recursive = false) {
|
|
187
|
+
const resolvedPath = this.resolvePath(targetPath);
|
|
188
|
+
if (this.config.debug) {
|
|
189
|
+
console.log(chalk.gray(`File delete: ${resolvedPath}, recursive=${recursive}`));
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const stats = await fs.stat(resolvedPath);
|
|
193
|
+
if (stats.isDirectory()) {
|
|
194
|
+
if (recursive) {
|
|
195
|
+
await fs.rm(resolvedPath, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
await fs.rmdir(resolvedPath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
await fs.unlink(resolvedPath);
|
|
203
|
+
}
|
|
204
|
+
return { path: resolvedPath, success: true };
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
const error = err;
|
|
208
|
+
return { path: resolvedPath, success: false, error: error.message };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create a directory
|
|
213
|
+
*/
|
|
214
|
+
async mkdir(dirPath, recursive = true) {
|
|
215
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
216
|
+
if (this.config.debug) {
|
|
217
|
+
console.log(chalk.gray(`Mkdir: ${resolvedPath}`));
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
await fs.mkdir(resolvedPath, { recursive });
|
|
221
|
+
return { path: resolvedPath, success: true };
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
const error = err;
|
|
225
|
+
return { path: resolvedPath, success: false, error: error.message };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Rename/move a file or directory
|
|
230
|
+
*/
|
|
231
|
+
async rename(fromPath, toPath) {
|
|
232
|
+
const resolvedFrom = this.resolvePath(fromPath);
|
|
233
|
+
const resolvedTo = this.resolvePath(toPath);
|
|
234
|
+
if (this.config.debug) {
|
|
235
|
+
console.log(chalk.gray(`Rename: ${resolvedFrom} -> ${resolvedTo}`));
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
// Ensure parent directory of destination exists
|
|
239
|
+
await fs.mkdir(path.dirname(resolvedTo), { recursive: true });
|
|
240
|
+
await fs.rename(resolvedFrom, resolvedTo);
|
|
241
|
+
return { path: resolvedTo, success: true };
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const error = err;
|
|
245
|
+
return { path: resolvedTo, success: false, error: error.message };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get file/directory stats
|
|
250
|
+
*/
|
|
251
|
+
async stat(targetPath) {
|
|
252
|
+
const resolvedPath = this.resolvePath(targetPath);
|
|
253
|
+
if (this.config.debug) {
|
|
254
|
+
console.log(chalk.gray(`Stat: ${resolvedPath}`));
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const stats = await fs.stat(resolvedPath);
|
|
258
|
+
return {
|
|
259
|
+
name: path.basename(resolvedPath),
|
|
260
|
+
path: resolvedPath,
|
|
261
|
+
type: this.getFileType(stats),
|
|
262
|
+
size: stats.size,
|
|
263
|
+
modified: stats.mtime.toISOString(),
|
|
264
|
+
permissions: this.formatPermissions(stats.mode),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Check if path exists
|
|
273
|
+
*/
|
|
274
|
+
async exists(targetPath) {
|
|
275
|
+
const resolvedPath = this.resolvePath(targetPath);
|
|
276
|
+
try {
|
|
277
|
+
await fs.access(resolvedPath);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
|