bunnyquery 1.4.2 → 1.4.3
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 +74 -4
- package/bunnyquery.js +52 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,9 @@ you can build your own chat UI on top of it. See
|
|
|
24
24
|
first load and on scroll-up.
|
|
25
25
|
- **Attachments** — drag-and-drop files and folders, per-file upload status
|
|
26
26
|
(uploading / failed / indexed), and overflow collapsing for large batches.
|
|
27
|
+
Images are read with vision/OCR, Office/text/code files are extracted
|
|
28
|
+
server-side, and PDFs are fetched by the model — see
|
|
29
|
+
[Supported file types](#supported-file-types).
|
|
27
30
|
- **Attachment parser plugins** — register a client-side parser so the widget
|
|
28
31
|
extracts text in the browser from formats the model can't otherwise read, and
|
|
29
32
|
indexes it directly. See [Attachment parser plugins](#attachment-parser-plugins).
|
|
@@ -137,12 +140,79 @@ BunnyQuery.logout();
|
|
|
137
140
|
> instance rather than re-mounting. On a successful mount it logs its version, e.g.
|
|
138
141
|
> `[bunnyquery] v1.3.5`.
|
|
139
142
|
|
|
143
|
+
## Supported file types
|
|
144
|
+
|
|
145
|
+
When a user attaches a file, BunnyQuery makes its contents available to the AI
|
|
146
|
+
automatically — detected by extension (with a MIME-type fallback), nothing to
|
|
147
|
+
configure. There are three paths, plus a couple of caveats.
|
|
148
|
+
|
|
149
|
+
### 1. Images — read directly by the model (vision + OCR)
|
|
150
|
+
|
|
151
|
+
`.jpg` · `.jpeg` · `.png` · `.gif` · `.webp`
|
|
152
|
+
|
|
153
|
+
The image is attached to the request inline, so the model both **describes the
|
|
154
|
+
picture** and **reads any text in it (OCR)**. Works on both Claude and OpenAI.
|
|
155
|
+
Only images referenced in the **most recent** message are inlined (older links
|
|
156
|
+
may have expired).
|
|
157
|
+
|
|
158
|
+
### 2. Documents, data & code — extracted on the server (inlined as text)
|
|
159
|
+
|
|
160
|
+
The skapi proxy downloads the file, extracts its text **server-side**, and
|
|
161
|
+
inlines that text into the request — the model reads it directly, with no
|
|
162
|
+
fetching. This keeps indexing consistent across model providers.
|
|
163
|
+
|
|
164
|
+
**Office & e-book** (binary/zip, parsed):
|
|
165
|
+
`.docx` · `.xlsx` · `.pptx` · `.hwp` · `.hwpx` · `.ods` · `.odt` · `.odp` · `.epub`
|
|
166
|
+
|
|
167
|
+
**Text, data, markup & source code** (decoded as text; `.html`/`.htm` have their
|
|
168
|
+
tags stripped):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
.csv .tsv .tab .txt .text .log .md .markdown .rst .json .ndjson .jsonl .geojson
|
|
172
|
+
.xml .yaml .yml .toml .ini .conf .cfg .properties .env .rtf .html .htm
|
|
173
|
+
.js .mjs .cjs .ts .tsx .jsx .py .rb .go .rs .java .kt .c .h .cpp .cc .hpp .cs
|
|
174
|
+
.php .swift .sh .bash .zsh .sql .css .scss .less .vue .svelte .tex .srt .vtt
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Plus a **MIME fallback**: any file whose content type is text-like (`text/*`,
|
|
178
|
+
`application/json`, `application/xml`, `*+json`, `*+xml`, `*+yaml`, …) is decoded
|
|
179
|
+
even when its extension isn't in the list above.
|
|
180
|
+
|
|
181
|
+
Encoding is auto-detected — UTF-8 (BOM-aware) → CP949/EUC-KR (Korean) → Latin-1.
|
|
182
|
+
Extracted text is capped at **200,000 characters**; longer files are truncated
|
|
183
|
+
with a `...[truncated for length; original N characters]` marker.
|
|
184
|
+
|
|
185
|
+
### 3. PDFs & other links — fetched by the model
|
|
186
|
+
|
|
187
|
+
`.pdf` (and any file that is neither an image nor server-extractable) is handed
|
|
188
|
+
to the model as a temporary link, which it opens with its built-in web tool:
|
|
189
|
+
**Claude** via `web_fetch`, **OpenAI** via `web_search` (external web access is
|
|
190
|
+
enabled). Both can open and read PDFs, so PDFs work on either provider.
|
|
191
|
+
|
|
192
|
+
> A provider's web tool opens document/page-style URLs such as PDFs, but not
|
|
193
|
+
> necessarily a bare *data-file* download (e.g. a raw `.csv`/`.tsv` link). That's
|
|
194
|
+
> why those data formats are extracted **server-side** (path 2 above) instead of
|
|
195
|
+
> being left to the model to fetch.
|
|
196
|
+
|
|
197
|
+
### Caveats
|
|
198
|
+
|
|
199
|
+
- **Legacy / macro Office** — `.doc` `.xls` `.ppt` (legacy binary) and `.docm`
|
|
200
|
+
`.xlsm` `.pptm` (macro-enabled) have no reliable server-side reader. They
|
|
201
|
+
upload fine but are indexed from **metadata only**; re-save as
|
|
202
|
+
`.docx` / `.xlsx` / `.pptx` (or PDF) to capture their contents.
|
|
203
|
+
- **Anything else** — a format covered by none of the above is indexed from its
|
|
204
|
+
metadata. To support it, register your own
|
|
205
|
+
[Attachment parser plugin](#attachment-parser-plugins) — it runs in the browser
|
|
206
|
+
and feeds parsed text straight into indexing.
|
|
207
|
+
|
|
140
208
|
## Attachment parser plugins
|
|
141
209
|
|
|
142
|
-
By default the chat agent reads
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
210
|
+
By default the chat agent reads images with vision/OCR, extracts
|
|
211
|
+
Office/OpenDocument/EPUB and text/data/code files on the server, and lets the
|
|
212
|
+
model fetch PDFs with its built-in web tool (`web_fetch` on Claude, `web_search`
|
|
213
|
+
on OpenAI) — see [Supported file types](#supported-file-types). For any format
|
|
214
|
+
read by **none** of these (e.g. a proprietary binary format), register a
|
|
215
|
+
**parser plugin**: it runs in the browser, turns the
|
|
146
216
|
uploaded file into text (or an HTML string), and the widget sends that content
|
|
147
217
|
**inline** for indexing — no `web_fetch`, no server extraction for that file.
|
|
148
218
|
|
package/bunnyquery.js
CHANGED
|
@@ -2084,7 +2084,7 @@ ${options.inlineContentPlaceholder}
|
|
|
2084
2084
|
(function() {
|
|
2085
2085
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
2086
2086
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
2087
|
-
var BQ_VERSION = "1.4.
|
|
2087
|
+
var BQ_VERSION = "1.4.3" ;
|
|
2088
2088
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
2089
2089
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
2090
2090
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -2492,6 +2492,43 @@ ${options.inlineContentPlaceholder}
|
|
|
2492
2492
|
var mismatched = !!tok && !!currentSub && !!tokenSub && tokenSub !== currentSub;
|
|
2493
2493
|
return expired || mismatched;
|
|
2494
2494
|
}
|
|
2495
|
+
function refreshMcpToken() {
|
|
2496
|
+
var client = getStoredMcpClient();
|
|
2497
|
+
var current = getStoredMcpToken();
|
|
2498
|
+
if (!client || !current || !current.refresh_token) return Promise.resolve(null);
|
|
2499
|
+
var body = new URLSearchParams({
|
|
2500
|
+
grant_type: "refresh_token",
|
|
2501
|
+
refresh_token: current.refresh_token,
|
|
2502
|
+
client_id: client.client_id
|
|
2503
|
+
});
|
|
2504
|
+
var headers = { "Content-Type": "application/x-www-form-urlencoded" };
|
|
2505
|
+
if (client.client_secret) {
|
|
2506
|
+
headers.Authorization = basicAuthHeader(client.client_id, client.client_secret);
|
|
2507
|
+
}
|
|
2508
|
+
return fetch(mcpBaseUrl() + "/oauth/token", {
|
|
2509
|
+
method: "POST",
|
|
2510
|
+
headers,
|
|
2511
|
+
body: body.toString()
|
|
2512
|
+
}).then(function(res) {
|
|
2513
|
+
return res.ok ? res.json() : null;
|
|
2514
|
+
}).then(function(json) {
|
|
2515
|
+
if (!json || !json.access_token) return null;
|
|
2516
|
+
var token = Object.assign({}, json, {
|
|
2517
|
+
refresh_token: json.refresh_token || current.refresh_token,
|
|
2518
|
+
expires_at: typeof json.expires_in === "number" ? Date.now() + json.expires_in * 1e3 : void 0
|
|
2519
|
+
});
|
|
2520
|
+
lsSet(skey(SK.mcpToken), JSON.stringify(token));
|
|
2521
|
+
return token;
|
|
2522
|
+
}).catch(function() {
|
|
2523
|
+
return null;
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
function ensureMcpGrantFresh() {
|
|
2527
|
+
if (!S.user || !mcpGrantNeedsRefresh(S.user)) return Promise.resolve(true);
|
|
2528
|
+
return refreshMcpToken().then(function(tok) {
|
|
2529
|
+
return !!(tok && !mcpGrantNeedsRefresh(S.user));
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2495
2532
|
function googleEnabled() {
|
|
2496
2533
|
return !!S.opts.googleClientId;
|
|
2497
2534
|
}
|
|
@@ -3759,6 +3796,8 @@ ${options.inlineContentPlaceholder}
|
|
|
3759
3796
|
}
|
|
3760
3797
|
function refreshSkapiSession() {
|
|
3761
3798
|
return S.skapi.getProfile({ refreshToken: true }).then(function() {
|
|
3799
|
+
return ensureMcpGrantFresh();
|
|
3800
|
+
}).then(function() {
|
|
3762
3801
|
return true;
|
|
3763
3802
|
}).catch(function() {
|
|
3764
3803
|
return false;
|
|
@@ -5099,9 +5138,12 @@ ${options.inlineContentPlaceholder}
|
|
|
5099
5138
|
return;
|
|
5100
5139
|
}
|
|
5101
5140
|
if (mcpGrantNeedsRefresh(user)) {
|
|
5102
|
-
return
|
|
5103
|
-
|
|
5104
|
-
return
|
|
5141
|
+
return refreshMcpToken().then(function(tok) {
|
|
5142
|
+
if (tok && !mcpGrantNeedsRefresh(user)) return enterAfterLogin();
|
|
5143
|
+
return beginMcpOAuthOnLogin("chat").catch(function(err) {
|
|
5144
|
+
console.error("[bunnyquery] MCP refresh failed", err);
|
|
5145
|
+
return enterAfterLogin();
|
|
5146
|
+
});
|
|
5105
5147
|
});
|
|
5106
5148
|
}
|
|
5107
5149
|
return enterAfterLogin();
|
|
@@ -5158,6 +5200,12 @@ ${options.inlineContentPlaceholder}
|
|
|
5158
5200
|
scheduleAttachmentOverflowRecompute();
|
|
5159
5201
|
});
|
|
5160
5202
|
}
|
|
5203
|
+
if (!S._visBound && typeof document !== "undefined" && document.addEventListener) {
|
|
5204
|
+
S._visBound = true;
|
|
5205
|
+
document.addEventListener("visibilitychange", function() {
|
|
5206
|
+
if (document.visibilityState === "visible" && S.user) ensureMcpGrantFresh();
|
|
5207
|
+
});
|
|
5208
|
+
}
|
|
5161
5209
|
boot();
|
|
5162
5210
|
return PUBLIC;
|
|
5163
5211
|
}
|