shellbase 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -33,7 +33,10 @@ shellbase start --dir ~/projects/my-app --name "내 노트북"
33
33
  | `shellbase login` | 구글 계정으로 로그인 |
34
34
  | `shellbase logout` | 로그아웃 |
35
35
  | `shellbase whoami` | 현재 로그인된 계정 확인 |
36
- | `shellbase start [--dir <path>] [--name <name>]` | 터미널 세션 시작 |
36
+ | `shellbase start [--dir <path>] [--name <name>] [--auto-approve]` | 터미널 세션 시작 |
37
+
38
+ `--auto-approve` 를 쓰면 폰에서 접속할 때마다 물어보지 않고 바로 승인해요. 편하지만, 내 계정에
39
+ 로그인할 수 있는 사람은 누구나 바로 입력할 수 있게 되니 신뢰할 수 있는 환경에서만 쓰세요.
37
40
 
38
41
  ## 참고
39
42
 
@@ -42,3 +45,62 @@ shellbase start --dir ~/projects/my-app --name "내 노트북"
42
45
  필요할 수 있어요.
43
46
  - 터미널 내용은 기기별 키로 암호화돼서 전송되고, 모바일에서 접속을 시도할 때마다 데스크톱에서
44
47
  직접 승인해야 입력이 가능합니다.
48
+
49
+ ## ⚠️ `shellbase start` 는 컴퓨터가 꺼지거나 재부팅되면 같이 꺼져요
50
+
51
+ 이건 각자의 컴퓨터에서 직접 관리해야 하는 부분입니다 — ShellBase 서비스가 대신 재시작해주지 않아요.
52
+ `shellbase start` 를 실행해둔 터미널을 닫거나, 컴퓨터를 껐다 켜거나, 프로세스가 죽으면 그 세션은
53
+ 끝나고, 다시 접속하려면 **직접 `shellbase start` 를 다시 실행**해야 합니다.
54
+
55
+ 컴퓨터가 꺼지지 않는 이상 자동으로 살아있게 하고 싶다면(예: 항상 켜져 있는 리눅스 서버), 아래처럼
56
+ OS의 자동 재시작 기능을 직접 설정하면 됩니다. **실제로 테스트해서 확인한 방법이에요:**
57
+
58
+ ### Linux (systemd, 실제 검증됨)
59
+
60
+ ```bash
61
+ mkdir -p ~/.config/systemd/user
62
+ cat > ~/.config/systemd/user/shellbase.service << 'EOF'
63
+ [Unit]
64
+ Description=ShellBase desktop agent
65
+ After=network-online.target
66
+
67
+ [Service]
68
+ Environment=PATH=/usr/local/bin:/usr/bin:/bin:%h/.nvm/current/bin
69
+ ExecStart=%h/.local/bin/shellbase start --dir %h --auto-approve
70
+ Restart=always
71
+ RestartSec=3
72
+
73
+ [Install]
74
+ WantedBy=default.target
75
+ EOF
76
+
77
+ systemctl --user daemon-reload
78
+ systemctl --user enable --now shellbase.service
79
+ ```
80
+
81
+ > **주의할 점 2가지 (직접 겪은 문제라 적어둡니다):**
82
+ > 1. `ExecStart` 의 `shellbase` 경로는 `which shellbase` 로 직접 확인해서 넣으세요 — systemd 서비스는
83
+ > 평소 터미널의 PATH를 그대로 물려받지 않아서, node/shellbase를 못 찾는 경우가 흔해요.
84
+ > (`Environment=PATH=...` 에도 같은 폴더를 넣어야 해요.)
85
+ > 2. 로그인 세션 없이(원격 SSH 접속도 안 하고) 완전히 백그라운드로 계속 떠있게 하려면
86
+ > `loginctl enable-linger $(whoami)` 도 한 번 실행해야 해요. 안 하면 로그아웃할 때 같이 꺼져요.
87
+ >
88
+ > `--auto-approve` 는 선택이에요 — 뺴면 폰에서 접속할 때마다 이 컴퓨터 쪽 로그(`journalctl --user -u shellbase -f`)에서 승인해야 하는데, 화면을 안 보는 서버 환경이라면 사실상 승인을 못 하게 되니 이런 무인 서버 상황에서는 `--auto-approve` 를 켜두는 걸 권장해요.
89
+
90
+ ### macOS
91
+
92
+ `launchd` 로 같은 걸 할 수 있어요 (`~/Library/LaunchAgents/` 에 `.plist` 등록, `KeepAlive: true`).
93
+ 아직 저희가 macOS에서 직접 검증은 못 했어요 — 해보시고 문제 있으면 알려주세요.
94
+
95
+ ### Windows
96
+
97
+ 작업 스케줄러(Task Scheduler)에서 "로그온 시 시작" + "실패 시 다시 시작" 옵션으로 등록하면 비슷하게
98
+ 됩니다. 아직 저희가 Windows에서 직접 검증은 못 했어요.
99
+
100
+ ### Docker로 하고 싶다면
101
+
102
+ `docker run --restart=always` 로 "꺼지면 자동 재시작"은 되지만, **컨테이너 안은 이 컴퓨터의 실제
103
+ 환경과 분리돼 있어요** — git 설정, 설치된 프로그램, 작업 폴더들을 볼륨(`-v`)으로 일일이 연결해줘야
104
+ 컨테이너 밖 진짜 내 파일들이 보여요. 이 컴퓨터 자체를 그대로 폰에서 보고 싶다면 컨테이너보다는
105
+ 위의 systemd/launchd/작업 스케줄러처럼 호스트에 직접 설치하는 쪽을 권장해요. 반대로 "이 컴퓨터와
106
+ 무관하게 항상 떠있는 별도 개발 서버"를 원하신다면 Docker도 좋은 선택이에요.
package/dist/cli.js CHANGED
@@ -27,6 +27,7 @@ program
27
27
  .description('이 컴퓨터의 터미널을 열어서 폰 접속을 받을 수 있게 시작')
28
28
  .option('--dir <path>', '이 폴더에서 터미널을 시작해요 (기본: 현재 폴더)')
29
29
  .option('--name <name>', '폰에서 보여줄 이 기기의 이름 (기본: 컴퓨터 이름)')
30
+ .option('--auto-approve', '폰에서 접속할 때마다 묻지 않고 자동으로 승인해요 (편하지만, 내 계정에 접속하는 누구나 곧바로 입력할 수 있게 되니 주의)')
30
31
  .action(async (opts) => {
31
32
  await startSession(opts);
32
33
  });
@@ -13,10 +13,11 @@ function load() {
13
13
  }
14
14
  }
15
15
  // 지금 시작한 폴더를 맨 앞으로 올리고, 홈 디렉터리는 항상 후보에 포함시킴 (모바일 폴더 선택용)
16
+ // cwd가 홈 폴더 그 자체인 경우도 있어서(Set으로) 중복은 항상 걸러냄
16
17
  export function touchRecentDir(cwd) {
17
18
  const home = os.homedir();
18
- const existing = load().filter((dir) => dir !== cwd && dir !== home);
19
- const updated = [cwd, home, ...existing].slice(0, MAX_ENTRIES);
19
+ const existing = load();
20
+ const updated = [...new Set([cwd, home, ...existing])].slice(0, MAX_ENTRIES);
20
21
  fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
21
22
  fs.writeFileSync(RECENT_DIRS_PATH, JSON.stringify(updated, null, 2), { mode: 0o600 });
22
23
  return updated;
package/dist/session.js CHANGED
@@ -21,6 +21,9 @@ export async function startSession(options) {
21
21
  const deviceName = options.name ?? device.deviceName;
22
22
  const cwd = options.dir ? path.resolve(options.dir) : process.cwd();
23
23
  console.log(`"${deviceName}" 세션을 시작해요 (작업 폴더: ${cwd})`);
24
+ if (options.autoApprove) {
25
+ console.log('⚠️ --auto-approve 켜짐 — 접속 요청을 묻지 않고 자동 승인해요. 내 계정에 들어올 수 있는 사람은 누구나 곧바로 입력할 수 있어요.');
26
+ }
24
27
  const deviceRowId = await registerDevice(cb, {
25
28
  ownerId: memberId,
26
29
  deviceId: device.deviceId,
@@ -89,7 +92,10 @@ export async function startSession(options) {
89
92
  if (approvalInFlight)
90
93
  return;
91
94
  approvalInFlight = true;
92
- promptApproval('\n📱 모바일에서 접속을 요청했어요. 입력을 허용할까요? (y/N, 30초 내 미응답 시 자동 거부): ', APPROVAL_TIMEOUT_MS)
95
+ const approval = options.autoApprove
96
+ ? Promise.resolve(true)
97
+ : promptApproval('\n📱 모바일에서 접속을 요청했어요. 입력을 허용할까요? (y/N, 30초 내 미응답 시 자동 거부): ', APPROVAL_TIMEOUT_MS);
98
+ approval
93
99
  .then((approved) => {
94
100
  inputApproved = approved;
95
101
  approvalInFlight = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellbase",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "내 컴퓨터 터미널(특히 Claude Code 세션)을 폰 브라우저로 실시간 접속하게 해주는 데스크톱 에이전트",
5
5
  "type": "module",
6
6
  "bin": {