bloby-bot 0.61.0 → 0.62.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.
@@ -1 +1 @@
1
- import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-DO7g-v11.js";var o=e(t(),1),s=n(),c=({code:e,language:t,raw:n,className:c,startLine:l,lineNumbers:u,...d})=>{let{shikiTheme:f}=(0,o.useContext)(i),p=r(),[m,h]=(0,o.useState)(n);return(0,o.useEffect)(()=>{if(!p){h(n);return}let r=p.highlight({code:e,language:t,themes:f},e=>{h(e)});r&&h(r)},[e,t,f,p,n]),(0,s.jsx)(a,{className:c,language:t,lineNumbers:u,result:m,startLine:l,...d})};export{c as HighlightedCodeBlockBody};
1
+ import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-Dmxp6AFI.js";var o=e(t(),1),s=n(),c=({code:e,language:t,raw:n,className:c,startLine:l,lineNumbers:u,...d})=>{let{shikiTheme:f}=(0,o.useContext)(i),p=r(),[m,h]=(0,o.useState)(n);return(0,o.useEffect)(()=>{if(!p){h(n);return}let r=p.highlight({code:e,language:t,themes:f},e=>{h(e)});r&&h(r)},[e,t,f,p,n]),(0,s.jsx)(a,{className:c,language:t,lineNumbers:u,result:m,startLine:l,...d})};export{c as HighlightedCodeBlockBody};
@@ -0,0 +1 @@
1
+ import{i as e}from"./bloby-Dmxp6AFI.js";export{e as Mermaid};
@@ -1 +1 @@
1
- import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{E as r,t as i}from"./globals-CwR3dDCz.js";var a=e(t(),1),o=e(r(),1),s=n();function c(){return(0,s.jsx)(i,{onComplete:()=>{window.parent?.postMessage({type:`bloby:onboard-complete`},`*`)},isInitialSetup:!0})}o.createRoot(document.getElementById(`root`)).render((0,s.jsx)(a.StrictMode,{children:(0,s.jsx)(c,{})}));
1
+ import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{E as r,t as i}from"./globals-W8wOsf_q.js";var a=e(t(),1),o=e(r(),1),s=n();function c(){return(0,s.jsx)(i,{onComplete:()=>{window.parent?.postMessage({type:`bloby:onboard-complete`},`*`)},isInitialSetup:!0})}o.createRoot(document.getElementById(`root`)).render((0,s.jsx)(a.StrictMode,{children:(0,s.jsx)(c,{})}));
@@ -4,10 +4,10 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
6
6
  <title>Bloby Chat</title>
7
- <script type="module" crossorigin src="/bloby/assets/bloby-DO7g-v11.js"></script>
7
+ <script type="module" crossorigin src="/bloby/assets/bloby-Dmxp6AFI.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="/bloby/assets/jsx-runtime-C0W9Wf2W.js">
9
- <link rel="modulepreload" crossorigin href="/bloby/assets/globals-CwR3dDCz.js">
10
- <link rel="stylesheet" crossorigin href="/bloby/assets/globals-CF0bs396.css">
9
+ <link rel="modulepreload" crossorigin href="/bloby/assets/globals-W8wOsf_q.js">
10
+ <link rel="stylesheet" crossorigin href="/bloby/assets/globals-CvipyZv-.css">
11
11
  <link rel="stylesheet" crossorigin href="/bloby/assets/bloby-DkK0ymA2.css">
12
12
  </head>
13
13
  <body class="bg-background text-foreground">
@@ -4,10 +4,10 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
6
6
  <title>Bloby Setup</title>
7
- <script type="module" crossorigin src="/bloby/assets/onboard-DcGLkITd.js"></script>
7
+ <script type="module" crossorigin src="/bloby/assets/onboard-D1woNbAz.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="/bloby/assets/jsx-runtime-C0W9Wf2W.js">
9
- <link rel="modulepreload" crossorigin href="/bloby/assets/globals-CwR3dDCz.js">
10
- <link rel="stylesheet" crossorigin href="/bloby/assets/globals-CF0bs396.css">
9
+ <link rel="modulepreload" crossorigin href="/bloby/assets/globals-W8wOsf_q.js">
10
+ <link rel="stylesheet" crossorigin href="/bloby/assets/globals-CvipyZv-.css">
11
11
  </head>
12
12
  <body class="bg-background text-foreground">
13
13
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.61.0",
3
+ "version": "0.62.2",
4
4
  "releaseNotes": [
5
5
  "1. Fix: image (and audio) attachments now render in chat again — /api/files is fetched with the auth token instead of a raw <img> src that 401'd after the endpoint hardening",
6
6
  "2. Affects chat thumbnails, the image lightbox, voice-note playback, and agent image cards",
package/shared/config.ts CHANGED
@@ -38,9 +38,8 @@ export interface TelegramChannelConfig {
38
38
  allowGroups?: boolean;
39
39
  /** Assistant mode only — see ChannelConfig.allowOthersToTrigger. DANGEROUS when true. */
40
40
  allowOthersToTrigger?: boolean;
41
- /** The bot's own access token (Bot API). Provisioned via the relay manager bot (Telegram
42
- * "Managed Bots") at pairing, or supplied directly (BYO @BotFather). Held locally the Bloby
43
- * long-polls Telegram DIRECTLY with this token; the relay is NOT in the message path. */
41
+ /** The bot's own access token (Bot API), from the user's own @BotFather bot (pasted at connect).
42
+ * Held locally the Bloby long-polls Telegram DIRECTLY with this token; no relay is involved. */
44
43
  botToken?: string;
45
44
  /** The bot's @username (no @). For display + deep links. */
46
45
  botUsername?: string;
@@ -159,9 +159,8 @@ export class ChannelManager {
159
159
  await alexa.connect();
160
160
  }
161
161
 
162
- // Telegram — only when a child bot token has been provisioned (via the relay
163
- // manager bot) or supplied (BYO @BotFather). The provider long-polls Telegram
164
- // directly; the relay is out of the message path from here on.
162
+ // Telegram — only when a BYO @BotFather bot token has been supplied (via the connect
163
+ // page). The provider long-polls Telegram directly; no relay is involved.
165
164
  if (channelConfigs?.telegram?.enabled && channelConfigs.telegram.botToken && !this.providers.has('telegram')) {
166
165
  log.info('[channels] Initializing Telegram channel...');
167
166
  const telegram = new TelegramChannel(
@@ -186,9 +185,9 @@ export class ChannelManager {
186
185
  * exact mode/debounce/role gating in handleInboundMessage with no special-casing. */
187
186
  private handleTelegramMessage(msg: TelegramInbound) {
188
187
  let ownerUserId = loadConfig().channels?.telegram?.ownerUserId;
189
- // Trust-on-first-use: if provisioning didn't capture the creator's id, adopt the first
190
- // person to DM this freshly-minted private bot as the owner (its @username is random, so in
191
- // practice only the owner knows it). Guarantees channel mode never silently ignores its owner.
188
+ // Trust-on-first-use: a BYO bot starts with no owner recorded, so adopt the first person to
189
+ // DM it as the owner. (Cleared on token change so a new bot re-adopts.) Guarantees channel
190
+ // mode never silently ignores its owner.
192
191
  if (!ownerUserId && !msg.isGroup && msg.fromUserId) {
193
192
  ownerUserId = msg.fromUserId;
194
193
  const cfg = loadConfig();
@@ -2,11 +2,10 @@
2
2
  * Telegram channel provider — DIRECT Bot API, no relay in the message path.
3
3
  *
4
4
  * Unlike Alexa (relay-mediated, degenerate provider), this provider holds the
5
- * user's OWN bot token and long-polls `getUpdates` straight against
6
- * api.telegram.org. After the relay provisions the token at pairing time (via
7
- * Telegram "Managed Bots"), the relay is gone — every inbound/outbound message
8
- * (including media) flows Bloby Telegram directly. Outbound HTTPS only: works
9
- * behind NAT, no public URL, no relay egress.
5
+ * user's OWN @BotFather bot token (pasted at connect time) and long-polls
6
+ * `getUpdates` straight against api.telegram.org no relay anywhere. Every
7
+ * inbound/outbound message (including media) flows Bloby Telegram directly.
8
+ * Outbound HTTPS only: works behind NAT, no public URL, no relay egress.
10
9
  *
11
10
  * It is lighter than the WhatsApp/Baileys provider (no reverse-engineered
12
11
  * protocol, no QR, no auth-state files) — just a token string and a poll loop.
@@ -389,7 +389,7 @@ function EnvSettings({ cacheRef }: { cacheRef: MutableRefObject<EnvCache | null>
389
389
  <TriangleAlert className="h-4 w-4 shrink-0" />{loadError}
390
390
  </div>
391
391
  ) : (
392
- <div className="mt-5 space-y-5 max-h-[44vh] overflow-y-auto pr-1 -mr-1">
392
+ <div className="mt-5 space-y-5">
393
393
  {groups.length === 0 && newRows.length === 0 && (
394
394
  <div className="rounded-xl border border-white/[0.06] bg-white/[0.02] px-4 py-7 text-center">
395
395
  <KeyRound className="h-5 w-5 text-white/25 mx-auto mb-2" />
@@ -1068,7 +1068,7 @@ function PulseCronSettings({ cacheRef }: { cacheRef: MutableRefObject<PulseCronC
1068
1068
  ) : loadError ? (
1069
1069
  <div className="mt-5 flex items-center gap-2 text-[13px] text-red-400"><TriangleAlert className="h-4 w-4 shrink-0" />{loadError}</div>
1070
1070
  ) : (
1071
- <div className="mt-6 max-h-[50vh] overflow-y-auto pr-1 -mr-1">
1071
+ <div className="mt-6">
1072
1072
  <PulseSection pulse={pulse} original={originalPulse} setPulse={setPulse} onPulseSaved={(saved) => setOriginalPulse(saved)} />
1073
1073
  <CronsSection crons={crons} onLocalUpdate={(fn) => setCrons(fn)} onChanged={() => { fetchSchedule(false).catch(() => {}); }} />
1074
1074
  </div>
@@ -1244,6 +1244,8 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1244
1244
  const envCacheRef = useRef<EnvCache | null>(null);
1245
1245
  // Same pattern for the Pulse & Crons screen (step 7).
1246
1246
  const pulseCacheRef = useRef<PulseCronCache | null>(null);
1247
+ // Scrollable content region (card is viewport-capped); reset to top on each step change.
1248
+ const contentScrollRef = useRef<HTMLDivElement>(null);
1247
1249
 
1248
1250
  // Settings mode (hub-and-spoke): each screen saves itself. `settingsOrig` is the last-saved
1249
1251
  // snapshot used both to show a Save button only when a screen actually changed, and to build a
@@ -1927,7 +1929,10 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
1927
1929
  })();
1928
1930
 
1929
1931
  // Clear the transient saved/error banners whenever the user moves to another settings screen.
1930
- useEffect(() => { setSettingsSaved(false); setSettingsSaveError(''); }, [step]);
1932
+ useEffect(() => {
1933
+ setSettingsSaved(false); setSettingsSaveError('');
1934
+ contentScrollRef.current?.scrollTo({ top: 0 });
1935
+ }, [step]);
1931
1936
 
1932
1937
  const saveSettings = async () => {
1933
1938
  if (!settingsScreenDirty || settingsSaving) return;
@@ -2030,11 +2035,11 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2030
2035
  initial={{ opacity: 0, scale: 0.95 }}
2031
2036
  animate={{ opacity: 1, scale: 1 }}
2032
2037
  transition={{ duration: 0.3 }}
2033
- className="relative w-full max-w-[480px] bg-[#181818] border border-white/[0.06] rounded-[24px] shadow-2xl overflow-hidden"
2038
+ className="relative w-full max-w-[480px] max-h-[calc(100dvh_-_2rem)] flex flex-col bg-[#181818] border border-white/[0.06] rounded-[24px] shadow-2xl overflow-hidden"
2034
2039
  >
2035
2040
  {/* Onboarding: step dots. Settings: a header with hub-back, screen jump, and close. */}
2036
2041
  {isInitialSetup ? (
2037
- <div className="flex justify-center gap-2 pt-6">
2042
+ <div className="flex justify-center gap-2 pt-6 shrink-0">
2038
2043
  {Array.from({ length: TOTAL_STEPS }, (_, i) => (
2039
2044
  <div
2040
2045
  key={i}
@@ -2049,7 +2054,7 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2049
2054
  ))}
2050
2055
  </div>
2051
2056
  ) : (
2052
- <div className="flex items-center justify-between px-5 pt-4 pb-1">
2057
+ <div className="flex items-center justify-between px-5 pt-4 pb-1 shrink-0">
2053
2058
  {step === 0 ? (
2054
2059
  <span className="text-[13px] font-semibold text-white/80 pl-1">Settings</span>
2055
2060
  ) : (
@@ -2073,7 +2078,8 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
2073
2078
  </div>
2074
2079
  )}
2075
2080
 
2076
- {/* Content */}
2081
+ {/* Content — scrolls within the viewport-capped card so tall screens stay reachable. */}
2082
+ <div ref={contentScrollRef} className="flex-1 min-h-0 overflow-y-auto">
2077
2083
  <AnimatePresence mode="wait">
2078
2084
  <motion.div
2079
2085
  key={step}
@@ -4047,10 +4053,11 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
4047
4053
  {step === 7 && !isInitialSetup && <PulseCronSettings cacheRef={pulseCacheRef} />}
4048
4054
  </motion.div>
4049
4055
  </AnimatePresence>
4056
+ </div>
4050
4057
 
4051
4058
  {/* Back link — onboarding only; settings mode navigates via the header. */}
4052
4059
  {isInitialSetup && step > 0 && step < TOTAL_STEPS - 1 && (
4053
- <div className="px-8 pb-5 -mt-3">
4060
+ <div className="px-8 pb-5 pt-2 shrink-0">
4054
4061
  <button
4055
4062
  onClick={back}
4056
4063
  className="text-white/25 hover:text-white/50 text-[12px] transition-colors"
@@ -4060,10 +4067,10 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
4060
4067
  </div>
4061
4068
  )}
4062
4069
 
4063
- {/* Settings mode: per-screen Save — shown only when the current screen has changes.
4064
- (Env screen, step 6, saves itself inside EnvSettings.) */}
4070
+ {/* Settings mode: per-screen Save — sticky at the bottom, shown only when the current
4071
+ screen has changes. (Env step 6 / Pulse & Crons step 7 save themselves inside.) */}
4065
4072
  {!isInitialSetup && step >= 1 && step <= 5 && (settingsScreenDirty || settingsSaving || settingsSaved || settingsSaveError) && (
4066
- <div className="px-8 pb-6 -mt-2">
4073
+ <div className="px-8 pb-6 pt-3 shrink-0 border-t border-white/[0.06] bg-[#181818]">
4067
4074
  {settingsSaveError && (
4068
4075
  <p className="text-red-400 text-[12px] mb-2 flex items-center gap-1.5">
4069
4076
  <TriangleAlert className="h-3.5 w-3.5 shrink-0" />{settingsSaveError}
@@ -88,6 +88,14 @@ function preprocessContent(text: string): string {
88
88
  );
89
89
  }
90
90
 
91
+ const telegramLink = '[connect-telegram](/api/channels/telegram/pair-page)';
92
+ if (!out.includes(telegramLink)) {
93
+ out = out.replace(
94
+ /`?(?:http:\/\/localhost:\d+)?\/api\/channels\/telegram\/pair-page`?/g,
95
+ telegramLink,
96
+ );
97
+ }
98
+
91
99
  return out;
92
100
  }
93
101
 
@@ -381,6 +389,27 @@ export default function MessageBubble({ role, content, timestamp, hasAttachments
381
389
  </a>
382
390
  );
383
391
  }
392
+ if (href?.includes('/api/channels/telegram/pair-page')) {
393
+ return (
394
+ <a
395
+ href={href}
396
+ target="_blank"
397
+ rel="noopener noreferrer"
398
+ className="flex items-center gap-3 my-2 px-3.5 py-2.5 rounded-xl bg-[#229ED9]/10 border border-[#229ED9]/20 hover:bg-[#229ED9]/15 transition-colors no-underline"
399
+ >
400
+ <div className="flex items-center justify-center w-8 h-8 rounded-lg bg-[#229ED9] shrink-0">
401
+ <svg viewBox="0 0 24 24" className="w-4 h-4 fill-white">
402
+ <path d="M9.78 18.65l.28-4.23 7.68-6.92c.34-.31-.07-.46-.52-.19L7.74 13.3 3.64 12c-.88-.25-.89-.86.2-1.3l15.97-6.16c.73-.33 1.43.18 1.15 1.3l-2.72 12.81c-.19.91-.74 1.13-1.5.71l-4.13-3.05-1.99 1.93c-.23.23-.42.42-.83.42z"/>
403
+ </svg>
404
+ </div>
405
+ <div className="flex flex-col min-w-0">
406
+ <span className="text-[#229ED9] font-medium text-sm">Click here to connect Telegram</span>
407
+ <span className="text-muted-foreground text-xs">Opens the Telegram pairing page</span>
408
+ </div>
409
+ <ExternalLink className="w-3.5 h-3.5 text-muted-foreground/50 ml-auto shrink-0" />
410
+ </a>
411
+ );
412
+ }
384
413
  return (
385
414
  <a
386
415
  href={href}