shellbase 0.3.1 → 0.4.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 CHANGED
@@ -36,10 +36,21 @@ shellbase start --dir ~/projects/my-app --name "내 노트북"
36
36
  | `shellbase login` | 구글 계정으로 로그인 |
37
37
  | `shellbase logout` | 로그아웃 |
38
38
  | `shellbase whoami` | 현재 로그인된 계정 확인 |
39
- | `shellbase start [--dir <path>] [--name <name>] [--auto-approve]` | 터미널 세션 시작 |
39
+ | `shellbase start [--dir <path>] [--name <name>] [--require-approval] [--no-restore]` | 터미널 세션 시작 |
40
40
 
41
- `--auto-approve` 쓰면 폰에서 접속할 때마다 물어보지 않고 바로 승인해요. 편하지만, 내 계정에
42
- 로그인할 수 있는 사람은 누구나 바로 입력할 수 있게 되니 신뢰할 수 있는 환경에서만 쓰세요.
41
+ ### 접속 승인 정책
42
+
43
+ **기본은 "내 계정으로 로그인했으면 바로 입력 가능"** 입니다. 이 도구의 보안 경계는 ConnectBase
44
+ 계정(구글 로그인)이에요 — 터미널 화면은 원래부터 계정 주인만 볼 수 있었고(세션 키가 내 계정만 읽을 수
45
+ 있는 자리에 있어요), 화면만 봐도 민감한 내용은 다 보이기 때문에 "보기는 되고 타이핑만 막기"는 실익이
46
+ 크지 않았습니다. 화면 앞에 사람이 없는 서버·도커에서는 승인할 방법 자체가 없기도 했고요.
47
+
48
+ 노트북처럼 한 단계 더 두고 싶다면 `--require-approval` 로 예전처럼 매번 이 컴퓨터에서 `y` 를 눌러야
49
+ 입력이 되게 할 수 있어요.
50
+
51
+ ```bash
52
+ shellbase start --require-approval
53
+ ```
43
54
 
44
55
  ### 여러 세션을 한 프로세스가 관리해요
45
56
 
@@ -80,9 +91,8 @@ shellbase start --no-restore # 복구하지 않고 이 폴더 세션 하나
80
91
  - **끄기는 진짜로 꺼요.** 컴퓨터에서 돌아가던 그 터미널이 종료되고 목록에서도 자동으로 사라져요
81
92
  (`Ctrl+C` 를 누른 것과 같아요). 프로세스가 죽었는데 목록에만 남아있는 항목은 휴지통 아이콘으로
82
93
  지울 수 있어요 — 그건 목록만 정리하는 버튼이에요.
83
- - **폰에서 세션은 자동 승인으로 열려요.** 컴퓨터 앞에 `y` 눌러줄 사람이 없기 때문이에요.
84
- (요청을 보낸 쪽이 세션의 암호화 키를 가지고 있는지 먼저 확인해요 — 그 키는 내 계정만 읽을 수
85
- 있어서, 다른 사람은 남의 세션을 열거나 끌 수 없어요.)
94
+ - **남의 세션은 열거나 없어요.** 요청을 보낸 쪽이 세션의 암호화 키를 가지고 있는지 먼저
95
+ 확인하는데, 그 키는 내 계정으로만 읽을 수 있는 자리에 있거든요.
86
96
  - **폰에서 연 세션도 같은 프로세스가 관리해요.** 그래서 컴퓨터에서 `Ctrl+C` 를 누르면 모든 세션이 함께
87
97
  종료돼요(그 대신 다음 실행 때 자동 복구됩니다). 세션을 하나만 끄고 싶으면 폰에서 그 세션의 `✕` 를
88
98
  쓰세요 — 나머지 세션은 그대로 유지돼요.
@@ -121,7 +131,7 @@ After=network-online.target
121
131
 
122
132
  [Service]
123
133
  Environment=PATH=/usr/local/bin:/usr/bin:/bin:%h/.nvm/current/bin
124
- ExecStart=%h/.local/bin/shellbase start --dir %h --auto-approve
134
+ ExecStart=%h/.local/bin/shellbase start --dir %h
125
135
  Restart=always
126
136
  RestartSec=3
127
137
 
@@ -140,7 +150,9 @@ systemctl --user enable --now shellbase.service
140
150
  > 2. 로그인 세션 없이(원격 SSH 접속도 안 하고) 완전히 백그라운드로 계속 떠있게 하려면
141
151
  > `loginctl enable-linger $(whoami)` 도 한 번 실행해야 해요. 안 하면 로그아웃할 때 같이 꺼져요.
142
152
  >
143
- > `--auto-approve` 선택이에요뺴면 폰에서 접속할 때마다 컴퓨터 쪽 로그(`journalctl --user -u shellbase -f`)에서 승인해야 하는데, 화면을 안 보는 서버 환경이라면 사실상 승인을 못 하게 되니 이런 무인 서버 상황에서는 `--auto-approve` 를 켜두는 걸 권장해요.
153
+ > 승인 옵션은 넣지 마세요 기본이 "내 계정이면 바로 입력 가능"이라 무인 서버에 그대로 맞습니다.
154
+ > (`--require-approval` 을 넣으면 접속할 때마다 이 컴퓨터에서 `y` 를 눌러야 하는데, 화면을 안 보는
155
+ > 서버에서는 사실상 승인을 못 하게 돼요.)
144
156
 
145
157
  ### macOS
146
158
 
@@ -192,10 +204,8 @@ docker run -d \
192
204
  > 2. **`--user $(id -u):$(id -g)` 를 꼭 넣으세요.** 안 넣으면 컨테이너가 root로 돌아서, 터미널
193
205
  > 안에서 만들거나 수정한 파일이 호스트에서는 root 소유가 돼버려요(내 계정으로 못 지우고 수정
194
206
  > 못 하는 파일이 생김).
195
- > 3. **detached 컨테이너는 접속 승인을 물어볼 방법이 없어요** 터미널이 배경에 있어서 `y` 를
196
- > 입력할 TTY가 없거든요. `--auto-approve` 커맨드에 박아넣거나(위 예시엔 넣었어요), 폰 목록
197
- > 화면의 "새 세션 자동 승인" 토글을 컨테이너 **시작 전에** 미리 켜두세요(계정 단위 설정이라 이
198
- > 컴퓨터에도 그대로 적용돼요 — 시작한 뒤에 켜면 다음 재시작부터 적용됩니다).
207
+ > 3. **`--require-approval` 넣지 마세요.** detached 컨테이너는 `y` 입력할 TTY가 없어서 승인을
208
+ > 해요. 기본값(내 계정이면 바로 입력 가능)이 상황에 맞습니다.
199
209
 
200
210
  재부팅 시 자동 복구는 도커 데몬이 담당해요 — `systemctl is-enabled docker` 로 `enabled` 인지
201
211
  확인하세요(보통 기본값). `unless-stopped` 는 컴퓨터가 재부팅되면서 데몬이 다시 뜰 때 "그때 돌아가고
package/dist/agent.js CHANGED
@@ -7,7 +7,7 @@ import { requireClient } from './auth.js';
7
7
  import { TERMINAL_CATEGORY, CONTROL_TOKEN } from './config.js';
8
8
  import { startSleepGuard, stopSleepGuard } from './sleep-guard.js';
9
9
  import { encryptFrame, decryptFrame, generateFrameKey } from './crypto.js';
10
- import { registerDevice, heartbeatDevice, unregisterDevice, listOnlineDirs } from './devices.js';
10
+ import { registerDevice, heartbeatDevice, unregisterDevice, listOnlineDirs, renameDevice, } from './devices.js';
11
11
  import { promptApproval } from './prompt.js';
12
12
  import { touchRecentDir } from './recent-dirs.js';
13
13
  import { listDirs } from './browse.js';
@@ -315,6 +315,29 @@ export async function runAgent(options) {
315
315
  void closeSession(session, { deliberate: true });
316
316
  return;
317
317
  }
318
+ // 폰에서 세션 이름 바꾸기 — 컴퓨터를 가리키는 앞부분은 유지해서 목록의 컴퓨터 묶음이 깨지지 않게 한다
319
+ if (frame.kind === 'rename') {
320
+ const control = readControl(frame.data, session.frameKey);
321
+ if (!control || typeof control.name !== 'string')
322
+ return;
323
+ const label = control.name.trim().slice(0, 40);
324
+ if (!label)
325
+ return;
326
+ const nextName = `${hostLabel} · ${label}`;
327
+ const before = session.name;
328
+ session.name = nextName;
329
+ renameDevice(cb, session.rowId, nextName)
330
+ .then(() => {
331
+ // 재시작 복구 목록에도 바뀐 이름이 남도록 저장
332
+ persistOpenSessions();
333
+ console.log(`📱 세션 이름을 바꿨어요: "${before}" → "${nextName}"`);
334
+ })
335
+ .catch((err) => {
336
+ session.name = before;
337
+ console.error('이름 변경 실패:', err.message);
338
+ });
339
+ return;
340
+ }
318
341
  if (frame.kind === 'list_dirs') {
319
342
  const control = readControl(frame.data, session.frameKey);
320
343
  if (!control)
package/dist/auth.js CHANGED
@@ -54,8 +54,6 @@ export async function whoami() {
54
54
  const { cb, memberId } = await requireClient();
55
55
  const me = await cb.auth.getMe();
56
56
  console.log(`${me.nickname}${me.email ? ` <${me.email}>` : ''} (member_id: ${memberId})`);
57
- const autoApprove = me.custom_data?.auto_approve === true;
58
- console.log(`계정 자동 승인 설정: ${autoApprove ? '켜짐 (웹에서 껐다켤 수 있어요)' : '꺼짐'}`);
59
57
  }
60
58
  export async function requireClient() {
61
59
  const creds = loadCredentials();
package/dist/devices.js CHANGED
@@ -46,3 +46,7 @@ export async function listOnlineDirs(cb) {
46
46
  }
47
47
  return dirs;
48
48
  }
49
+ // 폰에서 세션 이름을 바꿀 때 — 목록 row 의 표시 이름만 갱신한다 (세션 자체는 그대로 유지)
50
+ export async function renameDevice(cb, rowId, deviceName) {
51
+ await cb.database.updateData(DEVICES_TABLE_ID, rowId, { data: { device_name: deviceName } });
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellbase",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "내 컴퓨터 터미널(특히 Claude Code 세션)을 폰 브라우저로 실시간 접속하게 해주는 데스크톱 에이전트",
5
5
  "type": "module",
6
6
  "bin": {