xkat-cli 1.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/README.md +40 -0
- package/bin/xkat-cli.js +51 -0
- package/package.json +35 -0
- package/scripts/install.js +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# xKat (Local Terminal Bridge)
|
|
2
|
+
|
|
3
|
+
xKat Agent는 브라우저 기반의 코딩 학습 플랫폼과 사용자의 로컬 환경을 안전하게 연결해주는 브릿지 도구입니다.
|
|
4
|
+
|
|
5
|
+
## 주요 기능
|
|
6
|
+
|
|
7
|
+
- **로컬 SSH 브릿지**: 브라우저의 웹 IDE 터미널을 로컬 셸에 직접 연결하여 실습 성능을 극대화합니다.
|
|
8
|
+
- **실시간 파일 동기화**: 로컬 작업 디렉토리의 파일 변경(VFS)을 감지하여 웹 대시보드와 즉시 동기화합니다.
|
|
9
|
+
- **보안 중심 설계**: 127.0.0.1 루프백 바인딩, Origin 검사, SSH 기반 인증을 통해 안전한 로컬 제어를 보장합니다.
|
|
10
|
+
|
|
11
|
+
## 시작하기
|
|
12
|
+
|
|
13
|
+
NPM을 통해 별도의 설치 없이 즉시 실행할 수 있습니다.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx xkat up
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
또는 전역 설치를 원할 경우:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g xkat
|
|
23
|
+
xkat up
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 시스템 요구 사항
|
|
27
|
+
|
|
28
|
+
- **OpenSSH Server**: 로컬 환경에 SSH 서버가 활성화되어 있어야 합니다.
|
|
29
|
+
- **Node.js**: 16.x 이상의 버전이 필요합니다.
|
|
30
|
+
|
|
31
|
+
## 보안 가이드라인
|
|
32
|
+
|
|
33
|
+
xKat Agent는 보안을 위해 다음과 같은 장치를 마련하고 있습니다:
|
|
34
|
+
1. **Localhost Only**: 포트 10022는 오직 로컬호스트에서만 접근 가능합니다.
|
|
35
|
+
2. **Origin Verification**: 공식 서비스 도메인에서 온 연결요청만 수락합니다.
|
|
36
|
+
3. **Audit Log**: 모든 접속 시도와 파일 변경 이벤트는 타임스탬프와 함께 로컬로그에 기록됩니다.
|
|
37
|
+
|
|
38
|
+
## 라이선스
|
|
39
|
+
|
|
40
|
+
MIT License
|
package/bin/xkat-cli.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* xkat-cli.js
|
|
5
|
+
* NPM에서 유저가 'xkat' 명령어를 입력하면 실행되는 경량 래퍼(Wrapper)입니다.
|
|
6
|
+
* 유일한 역할: postinstall에서 다운받아둔 Rust 바이너리를 찾아서 실행권한을 넘기는 것.
|
|
7
|
+
*/
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
function getBinaryName() {
|
|
14
|
+
const platform = os.platform();
|
|
15
|
+
const arch = os.arch();
|
|
16
|
+
|
|
17
|
+
const map = {
|
|
18
|
+
'darwin-arm64': 'xkat-agent-macos-aarch64',
|
|
19
|
+
'darwin-x64': 'xkat-agent-macos-x64',
|
|
20
|
+
'linux-x64': 'xkat-agent-linux-x64',
|
|
21
|
+
'linux-arm64': 'xkat-agent-linux-aarch64',
|
|
22
|
+
'win32-x64': 'xkat-agent-win-x64.exe',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const key = `${platform}-${arch}`;
|
|
26
|
+
const binary = map[key];
|
|
27
|
+
|
|
28
|
+
if (!binary) {
|
|
29
|
+
console.error(`❌ Unsupported platform: ${key}`);
|
|
30
|
+
console.error(' Supported: macOS (arm64/x64), Linux (x64/arm64), Windows (x64)');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return binary;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const binaryPath = path.join(__dirname, '..', 'binaries', getBinaryName());
|
|
38
|
+
|
|
39
|
+
if (!fs.existsSync(binaryPath)) {
|
|
40
|
+
console.error('❌ xkat-agent binary not found.');
|
|
41
|
+
console.error(' Try reinstalling: npm install -g xkat-agent');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Rust 바이너리에 모든 argv를 그대로 토스
|
|
46
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
47
|
+
stdio: 'inherit',
|
|
48
|
+
env: process.env,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
process.exit(result.status || 0);
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "xkat-cli",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "xKat Local Terminal Agent - Bridge between Web IDE and Local Shell",
|
|
5
|
+
"main": "bin/xkat-cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"xkat": "bin/xkat-cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"scripts",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"postinstall": "node ./scripts/install.js"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/somilab/xkat-agent.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"xkat",
|
|
23
|
+
"codingkat",
|
|
24
|
+
"terminal",
|
|
25
|
+
"ssh",
|
|
26
|
+
"bridge",
|
|
27
|
+
"agent"
|
|
28
|
+
],
|
|
29
|
+
"author": "SomiLab <contact@somilab.com>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/somilab/xkat-agent/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://xkat.space"
|
|
35
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install.js — postinstall 스크립트
|
|
3
|
+
* npm install 직후 자동 실행되며, 유저의 OS/아키텍처에 맞는
|
|
4
|
+
* 미리 빌드된 Rust 바이너리를 GitHub Releases(또는 R2)에서 다운로드합니다.
|
|
5
|
+
*/
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
|
|
11
|
+
const VERSION = '1.1.0';
|
|
12
|
+
const BASE_URL = `https://dl.xkat.space/v${VERSION}`;
|
|
13
|
+
|
|
14
|
+
function getPlatformTarget() {
|
|
15
|
+
const platform = os.platform();
|
|
16
|
+
const arch = os.arch();
|
|
17
|
+
|
|
18
|
+
const map = {
|
|
19
|
+
'darwin-arm64': 'xkat-agent-macos-aarch64',
|
|
20
|
+
'darwin-x64': 'xkat-agent-macos-x64',
|
|
21
|
+
'linux-x64': 'xkat-agent-linux-x64',
|
|
22
|
+
'linux-arm64': 'xkat-agent-linux-aarch64',
|
|
23
|
+
'win32-x64': 'xkat-agent-win-x64.exe',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return map[`${platform}-${arch}`] || null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function main() {
|
|
30
|
+
const target = getPlatformTarget();
|
|
31
|
+
|
|
32
|
+
if (!target) {
|
|
33
|
+
console.log(`⚠️ xkat-agent: 지원하지 않는 플랫폼입니다 (${os.platform()}-${os.arch()})`);
|
|
34
|
+
console.log(' 수동 빌드: cd apps/xkat-agent && cargo build --release');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const binDir = path.join(__dirname, '..', 'binaries');
|
|
39
|
+
const destPath = path.join(binDir, target);
|
|
40
|
+
|
|
41
|
+
// 이미 다운로드 되어 있으면 스킵
|
|
42
|
+
if (fs.existsSync(destPath)) {
|
|
43
|
+
console.log(`✅ xkat-agent 바이너리가 이미 존재합니다: ${target}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`📥 xkat-agent 다운로드 중... (${target})`);
|
|
48
|
+
|
|
49
|
+
// binaries 디렉토리 생성
|
|
50
|
+
if (!fs.existsSync(binDir)) {
|
|
51
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const url = `${BASE_URL}/${target}`;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await downloadFile(url, destPath);
|
|
58
|
+
// 실행 권한 부여 (Unix 환경에서만)
|
|
59
|
+
if (os.platform() !== 'win32') {
|
|
60
|
+
fs.chmodSync(destPath, 0o755);
|
|
61
|
+
}
|
|
62
|
+
console.log(`✅ 설치 완료! 실행: xkat up`);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.log(`⚠️ 바이너리 다운로드 실패: ${err.message}`);
|
|
65
|
+
console.log(' 로컬에서 직접 빌드하려면: cd apps/xkat-agent && cargo build --release');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function downloadFile(url, dest) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const file = fs.createWriteStream(dest);
|
|
72
|
+
https.get(url, (response) => {
|
|
73
|
+
// GitHub Releases는 302 리다이렉트를 씀
|
|
74
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
75
|
+
https.get(response.headers.location, (redirected) => {
|
|
76
|
+
redirected.pipe(file);
|
|
77
|
+
file.on('finish', () => { file.close(); resolve(); });
|
|
78
|
+
}).on('error', reject);
|
|
79
|
+
} else if (response.statusCode === 200) {
|
|
80
|
+
response.pipe(file);
|
|
81
|
+
file.on('finish', () => { file.close(); resolve(); });
|
|
82
|
+
} else {
|
|
83
|
+
reject(new Error(`HTTP ${response.statusCode}`));
|
|
84
|
+
}
|
|
85
|
+
}).on('error', reject);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main();
|