shellbase 0.12.0 → 0.13.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/dist/agent.js CHANGED
@@ -801,6 +801,9 @@ export async function runAgent(options) {
801
801
  const dir = typeof control.dir === 'string' && control.dir ? control.dir : liveCwd(session);
802
802
  const name = typeof control.name === 'string' ? control.name : '';
803
803
  let failure;
804
+ // 내용물이 든 폴더는 바로 지우지 않고 개수만 돌려준다 — 폰이 한 번 더 물어본 뒤
805
+ // recursive 로 다시 부른다 (browse.ts 의 deleteEntry 설명 참고)
806
+ let pending;
804
807
  if (control.op === 'rename') {
805
808
  const to = typeof control.to === 'string' ? control.to : '';
806
809
  failure = renameEntry(dir, name, to).error;
@@ -808,9 +811,14 @@ export async function runAgent(options) {
808
811
  console.log(`📱 폰에서 이름을 바꿨어요: ${name} → ${to} (${dir})`);
809
812
  }
810
813
  else if (control.op === 'delete') {
811
- failure = deleteEntry(dir, name).error;
812
- if (!failure)
813
- console.log(`📱 폰에서 지웠어요: ${name} (${dir})`);
814
+ const recursive = control.recursive === true;
815
+ const result = deleteEntry(dir, name, { recursive, protect: liveCwd(session) });
816
+ failure = result.error;
817
+ if (result.contains !== undefined)
818
+ pending = { name, contains: result.contains };
819
+ if (!failure && !pending) {
820
+ console.log(`📱 폰에서 지웠어요: ${name}${recursive ? ' (통째로)' : ''} (${dir})`);
821
+ }
814
822
  }
815
823
  else {
816
824
  failure = '알 수 없는 요청이에요.';
@@ -819,7 +827,11 @@ export async function runAgent(options) {
819
827
  void send({
820
828
  kind: 'dirs',
821
829
  to: session.deviceId,
822
- data: encryptFrame(JSON.stringify(failure ? { ...listing, error: failure } : listing), session.frameKey),
830
+ data: encryptFrame(JSON.stringify({
831
+ ...listing,
832
+ ...(failure ? { error: failure } : {}),
833
+ ...(pending ? { pending } : {}),
834
+ }), session.frameKey),
823
835
  });
824
836
  return;
825
837
  }
package/dist/browse.js CHANGED
@@ -39,7 +39,10 @@ export function renameEntry(dir, name, to) {
39
39
  // 지우기 — 파일은 바로, 폴더는 **비어 있을 때만**.
40
40
  // 폰에서 한 번 잘못 누르면 되돌릴 수 없으므로, 안에 든 것까지 통째로 지우는 일은 하지 않는다
41
41
  // (정말 필요하면 터미널에서 직접 지우면 된다).
42
- export function deleteEntry(dir, name) {
42
+ // 지우기. 폴더 안에 뭐가 들어 있으면 바로 지우지 않고 `contains`(들어 있는 개수) 돌려준다 —
43
+ // 폰에서 "안에 N개가 들어 있어요. 통째로 지울까요?" 를 한 번 더 물어본 뒤 recursive 로 다시 부른다.
44
+ // (예전에는 여기서 그냥 거절했는데, 그러면 내용물 있는 폴더는 영영 정리할 수 없었다)
45
+ export function deleteEntry(dir, name, opts = {}) {
43
46
  const target = childPath(dir, name);
44
47
  if (!target)
45
48
  return { error: '지울 수 없는 이름이에요.' };
@@ -50,13 +53,17 @@ export function deleteEntry(dir, name) {
50
53
  catch {
51
54
  return { error: '이미 없어요.' };
52
55
  }
56
+ // 지금 터미널이 그 안에서 돌고 있는 폴더는 못 지운다 — 지우면 셸이 갈 곳을 잃는다
57
+ if (opts.protect && stat.isDirectory() && isInside(opts.protect, target)) {
58
+ return { error: `지금 터미널이 "${name}" 안에서 돌고 있어요. 밖으로 나온 뒤 지워주세요.` };
59
+ }
53
60
  try {
54
61
  if (stat.isDirectory()) {
55
62
  const inside = fs.readdirSync(target);
56
- if (inside.length > 0) {
57
- return { error: `"${name}" 안에 ${inside.length}개가 들어 있어서 여기서는 못 지워요.` };
63
+ if (inside.length > 0 && !opts.recursive) {
64
+ return { contains: inside.length };
58
65
  }
59
- fs.rmdirSync(target);
66
+ fs.rmSync(target, { recursive: true, force: true });
60
67
  return {};
61
68
  }
62
69
  fs.unlinkSync(target);
@@ -66,6 +73,12 @@ export function deleteEntry(dir, name) {
66
73
  return { error: `지우지 못했어요: ${err.message}` };
67
74
  }
68
75
  }
76
+ // child 가 parent 와 같거나 그 아래에 있는가
77
+ function isInside(child, parent) {
78
+ const a = path.resolve(child);
79
+ const b = path.resolve(parent);
80
+ return a === b || a.startsWith(b.endsWith(path.sep) ? b : b + path.sep);
81
+ }
69
82
  export function listDirs(dir, opts = {}) {
70
83
  const target = path.resolve(dir);
71
84
  const up = path.dirname(target);
package/dist/config.js CHANGED
@@ -11,7 +11,9 @@ export const CLI_AUTH_SESSIONS_TABLE_ID = '01a00aa0-802d-7730-88ba-b831b7405188'
11
11
  export const WEB_APP_URL = 'https://shellbase.web.connectbase.world';
12
12
  export const WEB_LOGIN_URL = `${WEB_APP_URL}/login`;
13
13
  // 앱을 닫아둔 폰에 알림을 보내주는 서버 함수 (push.ts). 발송은 서버만 할 수 있어서 이걸 부른다.
14
- export const PUSH_FUNCTION_ID = process.env.SHELLBASE_PUSH_FUNCTION ?? 'shellbase-notify';
14
+ // 이름이 아니라 **함수 id(UUID)** 불러야 한다 — 이름으로 부르면 `invalid function ID` 가 난다(실측).
15
+ // 이 앱의 모든 사용자가 같은 함수를 부르지만, 알림은 함수가 호출자 본인(ctx.memberId)에게만 보낸다.
16
+ export const PUSH_FUNCTION_ID = process.env.SHELLBASE_PUSH_FUNCTION ?? '01a01f8f-6ef8-7335-ba55-dc095587ba6d';
15
17
  export const CONFIG_DIR = path.join(os.homedir(), '.shellbase');
16
18
  export const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json');
17
19
  // 폰이 보내는 제어 프레임(세션 끄기 / 새 세션 열기)에 함께 실어 보내는 확인 문자열.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellbase",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "내 컴퓨터 터미널(특히 Claude Code 세션)을 폰 브라우저로 실시간 접속하게 해주는 데스크톱 에이전트",
5
5
  "type": "module",
6
6
  "bin": {