shellbase 0.10.0 → 0.11.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
@@ -19,6 +19,7 @@ import { listDirs } from './browse.js';
19
19
  import { readTextFile, writeTextFile, chunkContent } from './files.js';
20
20
  import { AUDIO_CHUNK_TTL_MS, MAX_AUDIO_BASE64, STT_SETUP_HINT, findSttServer, transcribe, } from './stt.js';
21
21
  import { loadOpenSessions, saveOpenSessions } from './open-sessions.js';
22
+ import { pushSessionDone } from './push.js';
22
23
  import { IMAGE_CHUNK_TTL_MS, pruneImages, saveImage } from './images.js';
23
24
  import { watchDoneNotes } from './notify.js';
24
25
  import { hookAsked, hookState, installHook, rememberHookAsked } from './claude-hook.js';
@@ -262,10 +263,11 @@ export async function runAgent(options) {
262
263
  session.pendingFlush = setTimeout(() => void flush(session), wait);
263
264
  };
264
265
  // ── 작업 완료 알림 ──────────────────────────────────────────────────────────
265
- // 폰에 가지 방법으로 알린다:
266
+ // 폰에 가지 방법으로 알린다:
266
267
  // ① 실시간 신호 — 앱을 켜두고 있으면 곧바로 뜬다
267
268
  // ② 목록 row 의 done_at — 나중에 앱을 열어봐도 "그 사이에 끝났다" 는 표시가 남아 있다
268
- // ②가 없으면 폰이 꺼져 있던 동안 끝난 작업을 영영 모르게 되므로 남긴다.
269
+ // 푸시 앱을 완전히 닫아둔 폰에도 알림이 뜬다 (이 제품의 핵심: 자리를 지켜도 된다)
270
+ // ①만 있으면 앱을 켜두고 지켜봐야 하고, ②만 있으면 직접 열어봐야 알 수 있다.
269
271
  const announceDone = (session, why) => {
270
272
  if (session.closing)
271
273
  return;
@@ -277,6 +279,8 @@ export async function runAgent(options) {
277
279
  // 목록에 못 남겨도 실시간 신호는 이미 갔다 — 세션 동작에는 영향 없음
278
280
  });
279
281
  }
282
+ // 발송은 서버 함수가 한다 (push.ts 설명 참고). 함수가 없는 계정에서도 조용히 넘어간다.
283
+ void pushSessionDone(cb, { deviceId: session.deviceId, name: session.name });
280
284
  };
281
285
  // 출력이 이어지는지 끊겼는지를 따라가며, 조용해지는 순간을 잡는다 (규칙은 위 상수 설명 참고)
282
286
  const trackBusy = (session, bytes) => {
package/dist/config.js CHANGED
@@ -8,7 +8,10 @@ export const TERMINAL_CATEGORY = 'shellbase-terminal';
8
8
  export const DEVICES_TABLE_ID = '01a009c7-a33d-79ca-9eeb-f9dc2d442c1b';
9
9
  // CLI 구글 로그인 중계용 (google-auth.ts) — 짧게 살고 암호화된 채로만 저장됨.
10
10
  export const CLI_AUTH_SESSIONS_TABLE_ID = '01a00aa0-802d-7730-88ba-b831b7405188';
11
- export const WEB_LOGIN_URL = 'https://shellbase.web.connectbase.world/login';
11
+ export const WEB_APP_URL = 'https://shellbase.web.connectbase.world';
12
+ export const WEB_LOGIN_URL = `${WEB_APP_URL}/login`;
13
+ // 앱을 닫아둔 폰에 알림을 보내주는 서버 함수 (push.ts). 발송은 서버만 할 수 있어서 이걸 부른다.
14
+ export const PUSH_FUNCTION_ID = process.env.SHELLBASE_PUSH_FUNCTION ?? 'shellbase-notify';
12
15
  export const CONFIG_DIR = path.join(os.homedir(), '.shellbase');
13
16
  export const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json');
14
17
  // 폰이 보내는 제어 프레임(세션 끄기 / 새 세션 열기)에 함께 실어 보내는 확인 문자열.
package/dist/push.js ADDED
@@ -0,0 +1,51 @@
1
+ import { PUSH_FUNCTION_ID, WEB_APP_URL } from './config.js';
2
+ // "앱을 완전히 닫아둔 폰"에 알림을 보내는 길.
3
+ //
4
+ // 발송 자체는 서버(ConnectBase Function)만 할 수 있다 — 브라우저·에이전트가 쓰는 공개키로는
5
+ // 푸시 발송이 막혀 있기 때문(권한이 새는 걸 막으려고 SDK 가 명시적으로 차단한다).
6
+ // 그래서 에이전트는 "끝났다"고 함수를 부르기만 하고, 누구에게 보낼지는 서버가 호출자 신원
7
+ // (ctx.memberId)으로 판단한다. 즉 남의 폰으로는 보낼 수 없다.
8
+ //
9
+ // 함수가 아직 배포 안 된 계정에서도 에이전트는 멀쩡히 돌아야 하므로, 실패는 조용히 넘긴다.
10
+ // 함수가 아직 배포 안 됐으면 매번 찔러봐야 낭비다. 그렇다고 영영 포기하면, 나중에 함수를
11
+ // 배포해도 에이전트를 껐다 켜기 전까지 알림이 안 온다 — 그래서 "한동안 쉬었다 다시 해본다".
12
+ let quietUntil = 0;
13
+ let consecutiveFailures = 0;
14
+ const GIVE_UP_AFTER = 5;
15
+ const RETRY_AFTER_MS = 10 * 60 * 1000;
16
+ function looksMissing(message) {
17
+ const lowered = message.toLowerCase();
18
+ return (lowered.includes('invalid function') ||
19
+ lowered.includes('not found') ||
20
+ lowered.includes('404') ||
21
+ lowered.includes('does not exist'));
22
+ }
23
+ export async function pushSessionDone(cb, params) {
24
+ if (Date.now() < quietUntil)
25
+ return;
26
+ if (consecutiveFailures >= GIVE_UP_AFTER) {
27
+ // 연달아 실패하는 중 — 잠깐 쉬었다가 처음부터 다시 세어본다
28
+ quietUntil = Date.now() + RETRY_AFTER_MS;
29
+ consecutiveFailures = 0;
30
+ return;
31
+ }
32
+ try {
33
+ await cb.functions.invoke(PUSH_FUNCTION_ID, {
34
+ kind: 'session_done',
35
+ device_id: params.deviceId,
36
+ name: params.name,
37
+ url: `${WEB_APP_URL}/session/${params.deviceId}`,
38
+ });
39
+ consecutiveFailures = 0;
40
+ }
41
+ catch (err) {
42
+ const message = err.message ?? '';
43
+ if (looksMissing(message)) {
44
+ // 아직 함수를 안 만든 계정 — 10분 뒤에 한 번 더 해본다 (그 사이 배포했을 수 있으므로)
45
+ quietUntil = Date.now() + RETRY_AFTER_MS;
46
+ consecutiveFailures = 0;
47
+ return;
48
+ }
49
+ consecutiveFailures += 1;
50
+ }
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellbase",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "내 컴퓨터 터미널(특히 Claude Code 세션)을 폰 브라우저로 실시간 접속하게 해주는 데스크톱 에이전트",
5
5
  "type": "module",
6
6
  "bin": {