@usebitl/cli 0.1.9 → 0.1.11

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.
@@ -467,7 +467,7 @@ function readCloudConfig(home = bitlHome()) {
467
467
  }
468
468
  }
469
469
  function clientVersionHeader() {
470
- const v = "0.1.9";
470
+ const v = "0.1.11";
471
471
  return v ? { "x-bitl-client": v } : {};
472
472
  }
473
473
  function resolveTarget(home = bitlHome()) {
@@ -491,7 +491,8 @@ function resolveTarget(home = bitlHome()) {
491
491
  // ../../shared/src/node/enrich.ts
492
492
  import { execFile } from "node:child_process";
493
493
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
494
- import { basename } from "node:path";
494
+ import { homedir as homedir2 } from "node:os";
495
+ import { basename, join as join3 } from "node:path";
495
496
  var BRANCH_TTL_MS = 6e4;
496
497
  var MAX_TRANSCRIPT_BYTES2 = 20 * 1024 * 1024;
497
498
  var branchCache = /* @__PURE__ */ new Map();
@@ -506,20 +507,67 @@ function gitBranch(cwd) {
506
507
  });
507
508
  });
508
509
  }
509
- async function aiTitleFromTranscript(path) {
510
+ async function titleFromTranscript(path) {
510
511
  try {
511
512
  if ((await stat2(path)).size > MAX_TRANSCRIPT_BYTES2) return void 0;
512
513
  const text = await readFile2(path, "utf8");
513
- let title;
514
+ let aiTitle;
515
+ let customTitle;
514
516
  for (const line of text.split("\n")) {
515
- if (!line.includes('"ai-title"')) continue;
517
+ if (!line.includes('"ai-title"') && !line.includes('"custom-title"')) continue;
516
518
  try {
517
519
  const rec = JSON.parse(line);
518
- if (rec.type === "ai-title" && rec.aiTitle) title = rec.aiTitle;
520
+ if (rec.type === "ai-title" && rec.aiTitle) aiTitle = rec.aiTitle;
521
+ if (rec.type === "custom-title" && rec.customTitle?.trim()) customTitle = rec.customTitle.trim();
519
522
  } catch {
520
523
  }
521
524
  }
522
- return title;
525
+ return customTitle ?? aiTitle;
526
+ } catch {
527
+ return void 0;
528
+ }
529
+ }
530
+ async function codexThreadName(threadId, codexHome = process.env.CODEX_HOME?.trim() || join3(homedir2(), ".codex")) {
531
+ try {
532
+ const file = join3(codexHome, "session_index.jsonl");
533
+ if ((await stat2(file)).size > MAX_TRANSCRIPT_BYTES2) return void 0;
534
+ let name;
535
+ for (const line of (await readFile2(file, "utf8")).split("\n")) {
536
+ if (!line.includes(threadId)) continue;
537
+ try {
538
+ const rec = JSON.parse(line);
539
+ if (rec.id === threadId && rec.thread_name?.trim()) name = rec.thread_name.trim();
540
+ } catch {
541
+ }
542
+ }
543
+ return name;
544
+ } catch {
545
+ return void 0;
546
+ }
547
+ }
548
+ async function importSqliteQuietly() {
549
+ const emitWarning = process.emitWarning;
550
+ process.emitWarning = ((warning, ...rest) => {
551
+ if (String(typeof warning === "string" ? warning : warning.message).startsWith("SQLite is an experimental feature")) return;
552
+ emitWarning.call(process, warning, ...rest);
553
+ });
554
+ try {
555
+ return await import("node:sqlite");
556
+ } finally {
557
+ process.emitWarning = emitWarning;
558
+ }
559
+ }
560
+ async function antigravityTitle(conversationId, dbPath = join3(homedir2(), ".gemini", "antigravity-cli", "conversation_summaries.db")) {
561
+ try {
562
+ await stat2(dbPath);
563
+ const { DatabaseSync } = await importSqliteQuietly();
564
+ const db = new DatabaseSync(dbPath, { readOnly: true });
565
+ try {
566
+ const row = db.prepare("SELECT title FROM conversation_summaries WHERE conversation_id = ?").get(conversationId);
567
+ return typeof row?.title === "string" && row.title.trim() ? row.title.trim() : void 0;
568
+ } finally {
569
+ db.close();
570
+ }
523
571
  } catch {
524
572
  return void 0;
525
573
  }
@@ -553,12 +601,14 @@ async function enrichEvent(e) {
553
601
  })
554
602
  );
555
603
  }
556
- if (e.source === "claude-code" && e.transcriptPath && (e.type === "prompt" || e.type === "turn_end")) {
557
- tasks.push(
558
- aiTitleFromTranscript(e.transcriptPath).then((title) => {
559
- if (title) extra.aiTitle = title;
560
- })
561
- );
604
+ if (e.type === "prompt" || e.type === "turn_end") {
605
+ const title = e.source === "claude-code" && e.transcriptPath ? titleFromTranscript(e.transcriptPath) : e.source === "codex" ? codexThreadName(e.sessionId) : e.source === "antigravity" ? antigravityTitle(e.sessionId) : void 0;
606
+ if (title)
607
+ tasks.push(
608
+ title.then((t) => {
609
+ if (t) extra.aiTitle = t;
610
+ })
611
+ );
562
612
  }
563
613
  await Promise.all(tasks.map((t) => t.catch(() => {
564
614
  })));
@@ -20060,7 +20060,7 @@ function removeCloudLogin(home = bitlHome()) {
20060
20060
  rmSync(cloudKeyPath(home), { force: true });
20061
20061
  }
20062
20062
  function clientVersionHeader() {
20063
- const v = "0.1.9";
20063
+ const v = "0.1.11";
20064
20064
  return v ? { "x-bitl-client": v } : {};
20065
20065
  }
20066
20066
  function resolveTarget(home = bitlHome()) {
@@ -20117,7 +20117,7 @@ async function latestVersion(home, now) {
20117
20117
  return latest;
20118
20118
  }
20119
20119
  var updateCommand = (install = process.env.BITL_INSTALL) => install === "npx" ? "npx @usebitl/cli@latest start" : "npm i -g @usebitl/cli";
20120
- async function checkCliUpdate(current = "0.1.9", opts = {}) {
20120
+ async function checkCliUpdate(current = "0.1.11", opts = {}) {
20121
20121
  if (!current || process.env.BITL_NO_UPDATE_CHECK === "1") return void 0;
20122
20122
  const latest = await latestVersion(opts.home ?? bitlHome(), opts.now ?? Date.now());
20123
20123
  if (!latest || compareVersions(latest, current) <= 0) return void 0;
@@ -10,7 +10,7 @@ import {
10
10
  checkCliUpdate,
11
11
  parseWireRecord,
12
12
  sessionKey
13
- } from "./chunk-J2A2UUOE.mjs";
13
+ } from "./chunk-V6S7JUFW.mjs";
14
14
 
15
15
  // ../../../node_modules/.pnpm/bn.js@4.12.5/node_modules/bn.js/lib/bn.js
16
16
  var require_bn = __commonJS({
package/dist/cli.mjs CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  unwrapMasterKey,
22
22
  verifyPublicKey,
23
23
  wrapMasterKey
24
- } from "./chunks/chunk-J2A2UUOE.mjs";
24
+ } from "./chunks/chunk-V6S7JUFW.mjs";
25
25
 
26
26
  // ../src/index.ts
27
27
  import { homedir as homedir2 } from "node:os";
@@ -48,6 +48,7 @@ var ko = {
48
48
  "usage.loginDefault": "\uC774 PC \uB97C \uACC4\uC815\uC5D0 \uC5F0\uACB0 (\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC2B9\uC778, \uAE30\uBCF8 {url})",
49
49
  "usage.login": "\uC774 PC \uB97C \uD074\uB77C\uC6B0\uB4DC \uACC4\uC815\uC5D0 \uC5F0\uACB0 (\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC2B9\uC778)",
50
50
  "usage.logout": "\uC5F0\uACB0 \uD574\uC81C \u2192 \uB2E4\uC2DC \uB85C\uCEEC \uC11C\uBC84\uB85C",
51
+ "usage.version": "\uC124\uCE58\uB41C bitl \uBC84\uC804",
51
52
  "usage.installClaude": "Claude Code hook \uB4F1\uB85D (\uAE30\uBCF8: ~/.claude/settings.json)",
52
53
  "usage.installCodex": "Codex hook \uB4F1\uB85D (~/.codex/config.toml)",
53
54
  "usage.installAgy": "Antigravity hook \uB4F1\uB85D (~/.gemini/config/hooks.json, CLI\xB7IDE \uACF5\uC6A9)",
@@ -120,6 +121,7 @@ var en = {
120
121
  "usage.loginDefault": "Connect this PC to your account (approve in the browser; default {url})",
121
122
  "usage.login": "Connect this PC to your cloud account (approve in the browser)",
122
123
  "usage.logout": "Disconnect \u2192 back to the local server",
124
+ "usage.version": "Installed bitl version",
123
125
  "usage.installClaude": "Add Claude Code hooks (default: ~/.claude/settings.json)",
124
126
  "usage.installCodex": "Add Codex hooks (~/.codex/config.toml)",
125
127
  "usage.installAgy": "Add Antigravity hooks (~/.gemini/config/hooks.json, shared by CLI and IDE)",
@@ -451,7 +453,7 @@ async function logout() {
451
453
  }
452
454
 
453
455
  // ../src/update.ts
454
- async function updateNotice(current = "0.1.9") {
456
+ async function updateNotice(current = "0.1.11") {
455
457
  const u = await checkCliUpdate(current);
456
458
  return u && `${t("update.available", { ...u })}
457
459
  ${t("update.changelog")}`;
@@ -481,7 +483,9 @@ var usage = () => [
481
483
  ` bitl uninstall <claude|codex|antigravity${NPM_BUILD ? "" : "|claude-desktop"}> [--project D]`,
482
484
  "",
483
485
  row("bitl remote [on|off]", t("usage.remote")),
484
- row("", t("usage.remote2"))
486
+ row("", t("usage.remote2")),
487
+ "",
488
+ row("bitl --version", t("usage.version"))
485
489
  ].join("\n");
486
490
  var serverUrl = () => process.env.BITL_URL ?? `http://127.0.0.1:${process.env.BITL_PORT ?? 7777}`;
487
491
  function option(args, name) {
@@ -605,7 +609,7 @@ async function main(argv) {
605
609
  if (String(typeof warning === "string" ? warning : warning.message).startsWith("SQLite is an experimental feature")) return;
606
610
  emitWarning(warning, ...rest2);
607
611
  });
608
- const { startServer } = await import("./chunks/src-YSXYJABQ.mjs");
612
+ const { startServer } = await import("./chunks/src-AT3GZJZ6.mjs");
609
613
  await startServer();
610
614
  console.log(t("start.pair", { url: localPairUrl() }));
611
615
  return;
@@ -624,6 +628,11 @@ async function main(argv) {
624
628
  }
625
629
  case "logout":
626
630
  return logout();
631
+ case "--version":
632
+ case "-v":
633
+ case "version":
634
+ console.log("0.1.11");
635
+ return;
627
636
  case "remote":
628
637
  case "away":
629
638
  return away(rest[0]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebitl/cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "BITL — see which of your AI coding agents (Claude Code, Codex, Antigravity) are waiting for you. Local board or usebitl.com",
5
5
  "type": "module",
6
6
  "bin": {
package/web/sw.js CHANGED
@@ -1,3 +1,3 @@
1
- (function(){var e=/^s1\.[A-Za-z0-9_-]{87}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]+$/,t=()=>globalThis.crypto.subtle,n=new TextEncoder,r=new TextDecoder(`utf-8`,{fatal:!0});function i(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function a(e){if(!/^[A-Za-z0-9_-]*$/.test(e))throw Error(`invalid base64url`);let t=atob(e.replace(/-/g,`+`).replace(/_/g,`/`)+`=`.repeat((4-e.length%4)%4));return Uint8Array.from(t,e=>e.charCodeAt(0))}function o(e){let t=e.length-1;for(;t>=0&&e[t]===0;)t--;if(t<0||e[t]!==128)throw Error(`invalid padding`);return e.subarray(0,t)}async function s(e,i,s){if(i.startsWith(`s1.`)){if(!e.inbox)throw Error(`no inbox key`);return u(e.inbox,i,s)}let c=i.split(`.`);if(c.length!==4||c[0]!==`v1`)throw Error(`unknown format`);if(c[1]!==e.kid)throw Error(`different key`);let l=await t().decrypt({name:`AES-GCM`,iv:a(c[2]),additionalData:n.encode(s)},e.enc,a(c[3]));return r.decode(o(new Uint8Array(l)))}var c={name:`ECDH`,namedCurve:`P-256`};async function l(e,r,i){let a=await t().importKey(`raw`,e,`HKDF`,!1,[`deriveKey`]);return t().deriveKey({name:`HKDF`,hash:`SHA-256`,salt:new Uint8Array(r),info:n.encode(`bitl/v1/inbox`)},a,{name:`AES-GCM`,length:256},!1,[i])}async function u(i,s,u){let d=s.split(`.`);if(d.length!==4||!e.test(s))throw Error(`unknown format`);let f=a(d[1]),p=await t().importKey(`raw`,f,c,!1,[]),m=await t().deriveBits({name:`ECDH`,public:p},i,256),h=await t().decrypt({name:`AES-GCM`,iv:a(d[2]),additionalData:n.encode(u)},await l(m,f,`decrypt`),a(d[3]));return r.decode(o(new Uint8Array(h)))}var d=e=>`approval:${e}`;async function f(e,t,n){let r=JSON.parse(await s(e,n,d(t)));if(typeof r?.tool!=`string`||typeof r.summary!=`string`)throw Error(`invalid approval request`);return r}var p=new WeakMap;function m(e){let t=p.get(e);return t||(t=globalThis.crypto.subtle.importKey(`raw`,e.master,`HKDF`,!1,[`deriveKey`]).then(e=>globalThis.crypto.subtle.deriveKey({name:`HKDF`,hash:`SHA-256`,salt:new Uint8Array,info:new TextEncoder().encode(`bitl/v1/approve`)},e,{name:`HMAC`,hash:`SHA-256`,length:256},!1,[`sign`,`verify`])),p.set(e,t)),t}var h=(e,t)=>new TextEncoder().encode(`bitl/approval/v1\n${e}\n${t}`);async function g(e,t,n){let r=await globalThis.crypto.subtle.sign(`HMAC`,await m(e),h(t,n));return i(new Uint8Array(r))}new TextEncoder;var _=`bitl-mv9jce4l`;self.addEventListener(`install`,e=>{e.waitUntil(caches.open(_).then(e=>e.addAll([`/app`,`/manifest.webmanifest`,`/icon-192.png`])).then(()=>self.skipWaiting()))}),self.addEventListener(`activate`,e=>{e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>e.startsWith(`bitl-`)&&e!==_).map(e=>caches.delete(e)))).then(()=>self.clients.claim()))}),self.addEventListener(`fetch`,e=>{let t=e.request;if(t.method!==`GET`)return;let n=new URL(t.url);if(!(n.origin!==self.location.origin||n.pathname.startsWith(`/api/`))){if(t.mode===`navigate`){e.respondWith(fetch(t).then(e=>(e.ok&&caches.open(_).then(t=>t.put(n.pathname,e.clone())),e)).catch(async()=>await caches.match(n.pathname)??await caches.match(`/app`)??Response.error()));return}n.pathname.startsWith(`/assets/`)&&e.respondWith(caches.match(t).then(e=>e??fetch(t).then(e=>(e.ok&&caches.open(_).then(n=>n.put(t,e.clone())),e))))}});function v(){return new Promise(e=>{let t=indexedDB.open(`bitl`,1);t.onupgradeneeded=()=>t.result.createObjectStore(`keys`),t.onerror=()=>e(null),t.onsuccess=()=>{let n=t.result;try{let t=n.transaction(`keys`,`readonly`).objectStore(`keys`).get(`content`);t.onsuccess=()=>e(t.result??null),t.onerror=()=>e(null)}catch{e(null)}finally{n.close()}}})}var y={ko:{headline:{permission:`권한 승인이 필요해요`,question:`질문에 답해주세요`,plan:`계획 승인이 필요해요`,elicitation:`입력이 필요해요`,input:`입력이 필요해요`},waiting:`나를 기다리고 있어요`,error:`오류로 멈췄어요`,oneWaiting:`작업 1건이 나를 기다리고 있어요`,settled:{allow:`✓ 허용됨`,deny:`✕ 거부됨`,cancelled:`터미널에서 답했어요`,expired:`시간이 지나 터미널로 넘어갔어요`},settledTitle:`승인 요청 끝남`,settledOther:`끝났어요`,approval:`승인 요청`,approvalLocked:`🔒 이 기기의 키로 요청 내용을 열 수 없어요. 눌러서 확인하세요.`,allow:`허용`,deny:`거부`,failNoKey:`이 기기에 키가 없어 답할 수 없어요. 앱을 열어 확인하세요.`,failNetwork:`보내지 못했어요 (네트워크). 앱을 열어 다시 답하세요.`,failSettled:`이미 다른 곳에서 답했거나 PC 가 터미널에서 승인하기로 했어요.`,failGone:`요청이 끝났어요 (시간이 지남).`},en:{headline:{permission:`Needs your permission`,question:`Has a question for you`,plan:`Needs plan approval`,elicitation:`Needs your input`,input:`Needs your input`},waiting:`Waiting for you`,error:`Stopped with an error`,oneWaiting:`1 task is waiting for you`,settled:{allow:`✓ Allowed`,deny:`✕ Denied`,cancelled:`Answered in the terminal`,expired:`Timed out — moved to the terminal`},settledTitle:`Approval request ended`,settledOther:`Ended`,approval:`Approval request`,approvalLocked:`🔒 This device’s key can’t decrypt the request. Tap to check.`,allow:`Allow`,deny:`Deny`,failNoKey:`No key on this device, so it can’t answer. Open the app to check.`,failNetwork:`Couldn’t send (network). Open the app and answer again.`,failSettled:`Already answered elsewhere, or the PC chose to approve in the terminal.`,failGone:`The request has ended (timed out).`}};async function b(){try{let e=await(await caches.open(`bitl.prefs`)).match(`/lang`),t=e?await e.text():``;if(t===`ko`||t===`en`)return y[t]}catch{}return self.navigator.language.toLowerCase().startsWith(`ko`)?y.ko:y.en}var x=e=>`approval:${e}`;async function S(e){let t=x(e.id),n=await b();await self.registration.showNotification(n.settledTitle,{body:n.settled[e.status]??n.settledOther,icon:`/icon-192.png`,badge:`/badge-96.png`,tag:t,silent:!0,data:{key:e.key}}),await new Promise(e=>setTimeout(e,4e3));for(let e of await self.registration.getNotifications({tag:t}))e.close()}async function C(e){let t=await v(),n=e.request;if(!n){let t=await fetch(`/api/approvals/${encodeURIComponent(e.id)}`).catch(()=>null);n=t?.ok?(await t.json()).request:void 0}let r=t&&n?await f(t,e.id,n).catch(()=>void 0):void 0;for(let t of await self.registration.getNotifications({tag:e.key}))t.close();let i=await b();await self.registration.showNotification(r?.project?`${r.project} · ${i.approval}`:i.approval,{body:r?[r.summary,r.description].filter(Boolean).join(`
1
+ (function(){var e=/^s1\.[A-Za-z0-9_-]{87}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]+$/,t=()=>globalThis.crypto.subtle,n=new TextEncoder,r=new TextDecoder(`utf-8`,{fatal:!0});function i(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function a(e){if(!/^[A-Za-z0-9_-]*$/.test(e))throw Error(`invalid base64url`);let t=atob(e.replace(/-/g,`+`).replace(/_/g,`/`)+`=`.repeat((4-e.length%4)%4));return Uint8Array.from(t,e=>e.charCodeAt(0))}function o(e){let t=e.length-1;for(;t>=0&&e[t]===0;)t--;if(t<0||e[t]!==128)throw Error(`invalid padding`);return e.subarray(0,t)}async function s(e,i,s){if(i.startsWith(`s1.`)){if(!e.inbox)throw Error(`no inbox key`);return u(e.inbox,i,s)}let c=i.split(`.`);if(c.length!==4||c[0]!==`v1`)throw Error(`unknown format`);if(c[1]!==e.kid)throw Error(`different key`);let l=await t().decrypt({name:`AES-GCM`,iv:a(c[2]),additionalData:n.encode(s)},e.enc,a(c[3]));return r.decode(o(new Uint8Array(l)))}var c={name:`ECDH`,namedCurve:`P-256`};async function l(e,r,i){let a=await t().importKey(`raw`,e,`HKDF`,!1,[`deriveKey`]);return t().deriveKey({name:`HKDF`,hash:`SHA-256`,salt:new Uint8Array(r),info:n.encode(`bitl/v1/inbox`)},a,{name:`AES-GCM`,length:256},!1,[i])}async function u(i,s,u){let d=s.split(`.`);if(d.length!==4||!e.test(s))throw Error(`unknown format`);let f=a(d[1]),p=await t().importKey(`raw`,f,c,!1,[]),m=await t().deriveBits({name:`ECDH`,public:p},i,256),h=await t().decrypt({name:`AES-GCM`,iv:a(d[2]),additionalData:n.encode(u)},await l(m,f,`decrypt`),a(d[3]));return r.decode(o(new Uint8Array(h)))}var d=e=>`approval:${e}`;async function f(e,t,n){let r=JSON.parse(await s(e,n,d(t)));if(typeof r?.tool!=`string`||typeof r.summary!=`string`)throw Error(`invalid approval request`);return r}var p=new WeakMap;function m(e){let t=p.get(e);return t||(t=globalThis.crypto.subtle.importKey(`raw`,e.master,`HKDF`,!1,[`deriveKey`]).then(e=>globalThis.crypto.subtle.deriveKey({name:`HKDF`,hash:`SHA-256`,salt:new Uint8Array,info:new TextEncoder().encode(`bitl/v1/approve`)},e,{name:`HMAC`,hash:`SHA-256`,length:256},!1,[`sign`,`verify`])),p.set(e,t)),t}var h=(e,t)=>new TextEncoder().encode(`bitl/approval/v1\n${e}\n${t}`);async function g(e,t,n){let r=await globalThis.crypto.subtle.sign(`HMAC`,await m(e),h(t,n));return i(new Uint8Array(r))}new TextEncoder;var _=`bitl-mv9kw5kp`;self.addEventListener(`install`,e=>{e.waitUntil(caches.open(_).then(e=>e.addAll([`/app`,`/manifest.webmanifest`,`/icon-192.png`])).then(()=>self.skipWaiting()))}),self.addEventListener(`activate`,e=>{e.waitUntil(caches.keys().then(e=>Promise.all(e.filter(e=>e.startsWith(`bitl-`)&&e!==_).map(e=>caches.delete(e)))).then(()=>self.clients.claim()))}),self.addEventListener(`fetch`,e=>{let t=e.request;if(t.method!==`GET`)return;let n=new URL(t.url);if(!(n.origin!==self.location.origin||n.pathname.startsWith(`/api/`))){if(t.mode===`navigate`){e.respondWith(fetch(t).then(e=>(e.ok&&caches.open(_).then(t=>t.put(n.pathname,e.clone())),e)).catch(async()=>await caches.match(n.pathname)??await caches.match(`/app`)??Response.error()));return}n.pathname.startsWith(`/assets/`)&&e.respondWith(caches.match(t).then(e=>e??fetch(t).then(e=>(e.ok&&caches.open(_).then(n=>n.put(t,e.clone())),e))))}});function v(){return new Promise(e=>{let t=indexedDB.open(`bitl`,1);t.onupgradeneeded=()=>t.result.createObjectStore(`keys`),t.onerror=()=>e(null),t.onsuccess=()=>{let n=t.result;try{let t=n.transaction(`keys`,`readonly`).objectStore(`keys`).get(`content`);t.onsuccess=()=>e(t.result??null),t.onerror=()=>e(null)}catch{e(null)}finally{n.close()}}})}var y={ko:{headline:{permission:`권한 승인이 필요해요`,question:`질문에 답해주세요`,plan:`계획 승인이 필요해요`,elicitation:`입력이 필요해요`,input:`입력이 필요해요`},waiting:`나를 기다리고 있어요`,error:`오류로 멈췄어요`,oneWaiting:`작업 1건이 나를 기다리고 있어요`,settled:{allow:`✓ 허용됨`,deny:`✕ 거부됨`,cancelled:`터미널에서 답했어요`,expired:`시간이 지나 터미널로 넘어갔어요`},settledTitle:`승인 요청 끝남`,settledOther:`끝났어요`,approval:`승인 요청`,approvalLocked:`🔒 이 기기의 키로 요청 내용을 열 수 없어요. 눌러서 확인하세요.`,allow:`허용`,deny:`거부`,failNoKey:`이 기기에 키가 없어 답할 수 없어요. 앱을 열어 확인하세요.`,failNetwork:`보내지 못했어요 (네트워크). 앱을 열어 다시 답하세요.`,failSettled:`이미 다른 곳에서 답했거나 PC 가 터미널에서 승인하기로 했어요.`,failGone:`요청이 끝났어요 (시간이 지남).`},en:{headline:{permission:`Needs your permission`,question:`Has a question for you`,plan:`Needs plan approval`,elicitation:`Needs your input`,input:`Needs your input`},waiting:`Waiting for you`,error:`Stopped with an error`,oneWaiting:`1 task is waiting for you`,settled:{allow:`✓ Allowed`,deny:`✕ Denied`,cancelled:`Answered in the terminal`,expired:`Timed out — moved to the terminal`},settledTitle:`Approval request ended`,settledOther:`Ended`,approval:`Approval request`,approvalLocked:`🔒 This device’s key can’t decrypt the request. Tap to check.`,allow:`Allow`,deny:`Deny`,failNoKey:`No key on this device, so it can’t answer. Open the app to check.`,failNetwork:`Couldn’t send (network). Open the app and answer again.`,failSettled:`Already answered elsewhere, or the PC chose to approve in the terminal.`,failGone:`The request has ended (timed out).`}};async function b(){try{let e=await(await caches.open(`bitl.prefs`)).match(`/lang`),t=e?await e.text():``;if(t===`ko`||t===`en`)return y[t]}catch{}return self.navigator.language.toLowerCase().startsWith(`ko`)?y.ko:y.en}var x=e=>`approval:${e}`;async function S(e){let t=x(e.id),n=await b();await self.registration.showNotification(n.settledTitle,{body:n.settled[e.status]??n.settledOther,icon:`/icon-192.png`,badge:`/badge-96.png`,tag:t,silent:!0,data:{key:e.key}}),await new Promise(e=>setTimeout(e,4e3));for(let e of await self.registration.getNotifications({tag:t}))e.close()}async function C(e){let t=await v(),n=e.request;if(!n){let t=await fetch(`/api/approvals/${encodeURIComponent(e.id)}`).catch(()=>null);n=t?.ok?(await t.json()).request:void 0}let r=t&&n?await f(t,e.id,n).catch(()=>void 0):void 0;for(let t of await self.registration.getNotifications({tag:e.key}))t.close();let i=await b();await self.registration.showNotification(r?.project?`${r.project} · ${i.approval}`:i.approval,{body:r?[r.summary,r.description].filter(Boolean).join(`
2
2
  `):i.approvalLocked,icon:`/icon-192.png`,badge:`/badge-96.png`,tag:x(e.id),data:{key:e.key,approvalId:e.id,readable:!!r},requireInteraction:!0,actions:r?[{action:`allow`,title:i.allow},{action:`deny`,title:i.deny}]:[{action:`deny`,title:i.deny}]})}async function w(e,t,n){let[r,i]=await Promise.all([v(),b()]),a=t=>self.registration.showNotification(i.approval,{body:t,icon:`/icon-192.png`,badge:`/badge-96.png`,tag:x(e),data:{key:n}});if(!r)return a(i.failNoKey);let o=await fetch(`/api/approvals/${encodeURIComponent(e)}/decide`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({decision:t,sig:await g(r,e,t)})}).catch(()=>null);if(!o)return a(i.failNetwork);if(o.status===409)return a(i.failSettled);if(!o.ok)return a(i.failGone)}async function T(e){if(e&&`kind`in e&&e.kind===`approval`)return C(e);if(e&&`kind`in e&&e.kind===`approval-settled`)return S(e);let t=e,n=await b();if(t&&(await self.registration.getNotifications()).some(e=>e.data?.approvalId&&e.data.key===t.key))return;if(!t||t.v!==1){await self.registration.showNotification(`BITL`,{body:n.oneWaiting,icon:`/icon-192.png`,tag:`bitl`});return}let r=await v(),i=async e=>r&&e?s(r,e,t.key).catch(()=>void 0):void 0,[a,o,c,l]=await Promise.all([i(t.sealed.title),i(t.sealed.activity),i(t.sealed.project),i(t.sealed.message)]),u=t.status===`error`?n.error:n.headline[t.reason??``]??n.waiting,d=(e,t)=>e.length>t?`${e.slice(0,t-1)}…`:e,f=[o??l,a&&a!==o?a:void 0].filter(Boolean).map(e=>d(e,140));await self.registration.showNotification(c?`${c} · ${u}`:u,{body:f.length?f.join(`
3
3
  `):n.oneWaiting,icon:`/icon-192.png`,badge:`/badge-96.png`,tag:t.key,data:{key:t.key},renotify:!0}),await self.navigator.setAppBadge?.(t.attention).catch(()=>{})}self.addEventListener(`push`,e=>{let t=null;try{t=e.data?.json()??null}catch{}e.waitUntil(T(t))}),self.addEventListener(`notificationclick`,e=>{e.notification.close();let t=e.notification.data,n=t?.key;if(t?.approvalId&&(e.action===`allow`||e.action===`deny`)){e.waitUntil(w(t.approvalId,e.action,n));return}let r=n?`/app#s=${encodeURIComponent(n)}`:`/app`;e.waitUntil((async()=>{let e=(await self.clients.matchAll({type:`window`,includeUncontrolled:!0})).find(e=>new URL(e.url).origin===self.location.origin);if(e){await e.focus(),e.postMessage({type:`open-session`,key:n});return}await self.clients.openWindow(r)})())})})();