grain-tools 0.10.19 → 0.10.20
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/connection-setup.html +29 -2
- package/dist/credential-page.html +29 -2
- package/dist/mcp-app.html +1 -1
- package/dist/web-app.html +164 -9
- package/out/server/standalone.js +3580 -2308
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -17890,12 +17890,39 @@ body {
|
|
|
17890
17890
|
}
|
|
17891
17891
|
.sp-web-reauth-action {
|
|
17892
17892
|
display: flex;
|
|
17893
|
-
align-items:
|
|
17893
|
+
align-items: stretch;
|
|
17894
17894
|
gap: 8px;
|
|
17895
|
+
margin-block: 4px;
|
|
17896
|
+
}
|
|
17897
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
17898
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
17899
|
+
display: inline-flex;
|
|
17900
|
+
align-items: center;
|
|
17901
|
+
padding: 0 14px;
|
|
17902
|
+
min-height: 36px;
|
|
17903
|
+
border-radius: 6px;
|
|
17904
|
+
font-size: 14px;
|
|
17905
|
+
}
|
|
17906
|
+
.sp-web-reauth-command,
|
|
17907
|
+
.sp-web-reauth-detail code {
|
|
17908
|
+
font-variant-ligatures: none;
|
|
17909
|
+
letter-spacing: 0;
|
|
17895
17910
|
}
|
|
17896
17911
|
.sp-web-reauth-detail {
|
|
17897
|
-
|
|
17912
|
+
max-width: 52ch;
|
|
17913
|
+
color: var(--grain-ink-muted);
|
|
17914
|
+
font-size: 13px;
|
|
17915
|
+
line-height: 1.6;
|
|
17916
|
+
}
|
|
17917
|
+
.sp-web-reauth-detail code {
|
|
17918
|
+
padding: 1px 6px;
|
|
17919
|
+
border: 1px solid var(--grain-border-subtle);
|
|
17920
|
+
border-radius: 4px;
|
|
17921
|
+
background: var(--grain-surface);
|
|
17922
|
+
color: var(--grain-ink);
|
|
17923
|
+
font-family: var(--sp-font-mono, monospace);
|
|
17898
17924
|
font-size: 12px;
|
|
17925
|
+
white-space: nowrap;
|
|
17899
17926
|
}
|
|
17900
17927
|
.sp-web-connection-item {
|
|
17901
17928
|
width: 100%;
|
|
@@ -4757,12 +4757,39 @@ body {
|
|
|
4757
4757
|
}
|
|
4758
4758
|
.sp-web-reauth-action {
|
|
4759
4759
|
display: flex;
|
|
4760
|
-
align-items:
|
|
4760
|
+
align-items: stretch;
|
|
4761
4761
|
gap: 8px;
|
|
4762
|
+
margin-block: 4px;
|
|
4763
|
+
}
|
|
4764
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
4765
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
4766
|
+
display: inline-flex;
|
|
4767
|
+
align-items: center;
|
|
4768
|
+
padding: 0 14px;
|
|
4769
|
+
min-height: 36px;
|
|
4770
|
+
border-radius: 6px;
|
|
4771
|
+
font-size: 14px;
|
|
4772
|
+
}
|
|
4773
|
+
.sp-web-reauth-command,
|
|
4774
|
+
.sp-web-reauth-detail code {
|
|
4775
|
+
font-variant-ligatures: none;
|
|
4776
|
+
letter-spacing: 0;
|
|
4762
4777
|
}
|
|
4763
4778
|
.sp-web-reauth-detail {
|
|
4764
|
-
|
|
4779
|
+
max-width: 52ch;
|
|
4780
|
+
color: var(--grain-ink-muted);
|
|
4781
|
+
font-size: 13px;
|
|
4782
|
+
line-height: 1.6;
|
|
4783
|
+
}
|
|
4784
|
+
.sp-web-reauth-detail code {
|
|
4785
|
+
padding: 1px 6px;
|
|
4786
|
+
border: 1px solid var(--grain-border-subtle);
|
|
4787
|
+
border-radius: 4px;
|
|
4788
|
+
background: var(--grain-surface);
|
|
4789
|
+
color: var(--grain-ink);
|
|
4790
|
+
font-family: var(--sp-font-mono, monospace);
|
|
4765
4791
|
font-size: 12px;
|
|
4792
|
+
white-space: nowrap;
|
|
4766
4793
|
}
|
|
4767
4794
|
.sp-web-connection-item {
|
|
4768
4795
|
width: 100%;
|
package/dist/mcp-app.html
CHANGED
|
@@ -146644,7 +146644,7 @@ function CloseIcon({ size = 10 }) {
|
|
|
146644
146644
|
}
|
|
146645
146645
|
const PATHS = {
|
|
146646
146646
|
play: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 3.5v9l7-4.5-7-4.5z" }),
|
|
146647
|
-
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1" }),
|
|
146647
|
+
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1", fill: "currentColor", stroke: "none" }),
|
|
146648
146648
|
copy: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
146649
146649
|
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
|
|
146650
146650
|
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M10.5 3.5v-.5a1 1 0 0 0-1-1H3.5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h.5" })
|
package/dist/web-app.html
CHANGED
|
@@ -13121,6 +13121,7 @@ const TELEMETRY_EVENTS = [
|
|
|
13121
13121
|
...RESULT_ROUTING_SCHEMA.events,
|
|
13122
13122
|
"daemon_crashed",
|
|
13123
13123
|
"service_lifecycle",
|
|
13124
|
+
"engine_update",
|
|
13124
13125
|
"mcp_http_error",
|
|
13125
13126
|
"duckdb_provision_completed",
|
|
13126
13127
|
"duckdb_runtime_state",
|
|
@@ -13232,6 +13233,11 @@ const TELEMETRY_STATES = [
|
|
|
13232
13233
|
"daemon",
|
|
13233
13234
|
"standalone_mcp",
|
|
13234
13235
|
"loop",
|
|
13236
|
+
"install_grain_panel",
|
|
13237
|
+
"extension_too_old",
|
|
13238
|
+
"grain_needs_update",
|
|
13239
|
+
"updating_grain",
|
|
13240
|
+
"reconnecting_to_grain",
|
|
13235
13241
|
"unknown"
|
|
13236
13242
|
];
|
|
13237
13243
|
const TELEMETRY_SOURCES = [
|
|
@@ -13480,7 +13486,6 @@ const TELEMETRY_ERROR_CODES = [
|
|
|
13480
13486
|
const TELEMETRY_LOOP_EXECUTORS = ["jules", "antigravity", "claude", "none", "unknown"];
|
|
13481
13487
|
const TELEMETRY_ERROR_IDS = [
|
|
13482
13488
|
/** Writing the installed daemon version pointer failed. */
|
|
13483
|
-
"daemon_install_registration_failed",
|
|
13484
13489
|
/** The daemon binary was missing from every registered installation path. */
|
|
13485
13490
|
"daemon_binary_missing",
|
|
13486
13491
|
/** The installed daemon is newer than this client can safely use. */
|
|
@@ -13529,6 +13534,7 @@ const TELEMETRY_ERROR_IDS = [
|
|
|
13529
13534
|
* A capacity effect, not a query or connector failure.
|
|
13530
13535
|
*/
|
|
13531
13536
|
"session_pruned_during_query",
|
|
13537
|
+
"query_result_tab_limit",
|
|
13532
13538
|
/**
|
|
13533
13539
|
* No connection profile could be resolved for the query: the request carried
|
|
13534
13540
|
* none, and the daemon had none stored to fall back on. Nothing was ever sent
|
|
@@ -13565,7 +13571,16 @@ const TELEMETRY_ERROR_IDS = [
|
|
|
13565
13571
|
/** The supervisor rejected starting the installed service. */
|
|
13566
13572
|
"service_supervisor_start_failed",
|
|
13567
13573
|
/** The supervisor accepted the service, but it never published a reachable endpoint. */
|
|
13568
|
-
"service_started_unreachable"
|
|
13574
|
+
"service_started_unreachable",
|
|
13575
|
+
/** The release manifest could not be fetched or parsed during an update check. */
|
|
13576
|
+
"update_manifest_unreachable",
|
|
13577
|
+
/** The downloaded update artifact did not match the release manifest's SHA-256. */
|
|
13578
|
+
"update_checksum_mismatch",
|
|
13579
|
+
/** The update artifact download or its SHA-256/size verification failed. */
|
|
13580
|
+
"update_download_failed",
|
|
13581
|
+
/** The launcher restarted the previous payload after the new one failed to become ready. */
|
|
13582
|
+
"update_rollback"
|
|
13583
|
+
/** The engine exited before the attempted trigger could be classified elsewhere. */
|
|
13569
13584
|
];
|
|
13570
13585
|
const TELEMETRY_ERROR_KINDS = [
|
|
13571
13586
|
"auth",
|
|
@@ -13869,13 +13884,35 @@ function useWebCommandDispatcher(dispatch) {
|
|
|
13869
13884
|
const data = await res.json().catch(() => ({}));
|
|
13870
13885
|
dispatch({
|
|
13871
13886
|
type: "statusMessage",
|
|
13872
|
-
message: `Export failed
|
|
13887
|
+
message: `Export failed. Very large results can fail to export; try adding a LIMIT or selecting fewer columns. ${data.error || res.statusText}`
|
|
13873
13888
|
});
|
|
13874
13889
|
return;
|
|
13875
13890
|
}
|
|
13876
13891
|
const disposition = res.headers.get("Content-Disposition") || "";
|
|
13877
13892
|
const filename = /filename="?([^"]+)"?/.exec(disposition)?.[1] || "export.csv";
|
|
13878
|
-
|
|
13893
|
+
if (!res.body) {
|
|
13894
|
+
throw new Error("No response body");
|
|
13895
|
+
}
|
|
13896
|
+
const reader = res.body.getReader();
|
|
13897
|
+
let bytesReceived = 0;
|
|
13898
|
+
let lastUpdate = Date.now();
|
|
13899
|
+
dispatch({ type: "statusMessage", message: "Exporting…" });
|
|
13900
|
+
const chunks = [];
|
|
13901
|
+
while (true) {
|
|
13902
|
+
const { done, value } = await reader.read();
|
|
13903
|
+
if (done) break;
|
|
13904
|
+
if (value) {
|
|
13905
|
+
chunks.push(value);
|
|
13906
|
+
bytesReceived += value.length;
|
|
13907
|
+
const now = Date.now();
|
|
13908
|
+
if (now - lastUpdate >= 500) {
|
|
13909
|
+
const mb = (bytesReceived / (1024 * 1024)).toFixed(1);
|
|
13910
|
+
dispatch({ type: "statusMessage", message: `Exporting… ${mb} MB` });
|
|
13911
|
+
lastUpdate = now;
|
|
13912
|
+
}
|
|
13913
|
+
}
|
|
13914
|
+
}
|
|
13915
|
+
const blob = new Blob(chunks);
|
|
13879
13916
|
const url = URL.createObjectURL(blob);
|
|
13880
13917
|
const anchor = document.createElement("a");
|
|
13881
13918
|
anchor.href = url;
|
|
@@ -13884,8 +13921,12 @@ function useWebCommandDispatcher(dispatch) {
|
|
|
13884
13921
|
anchor.click();
|
|
13885
13922
|
anchor.remove();
|
|
13886
13923
|
URL.revokeObjectURL(url);
|
|
13924
|
+
dispatch({ type: "statusMessage", message: `Exported ${filename}` });
|
|
13887
13925
|
}).catch(
|
|
13888
|
-
(err) => dispatch({
|
|
13926
|
+
(err) => dispatch({
|
|
13927
|
+
type: "statusMessage",
|
|
13928
|
+
message: `Export failed. Very large results can fail to export; try adding a LIMIT or selecting fewer columns. ${err.message}`
|
|
13929
|
+
})
|
|
13889
13930
|
);
|
|
13890
13931
|
return;
|
|
13891
13932
|
}
|
|
@@ -148982,7 +149023,7 @@ function getConnectorGroup(id) {
|
|
|
148982
149023
|
}
|
|
148983
149024
|
const PATHS = {
|
|
148984
149025
|
play: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 3.5v9l7-4.5-7-4.5z" }),
|
|
148985
|
-
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1" }),
|
|
149026
|
+
stop: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1", fill: "currentColor", stroke: "none" }),
|
|
148986
149027
|
copy: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
148987
149028
|
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
|
|
148988
149029
|
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M10.5 3.5v-.5a1 1 0 0 0-1-1H3.5a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h.5" })
|
|
@@ -152775,6 +152816,52 @@ function useWebAppInitialTarget(isReady, viewHint, resultTarget, tabs, showQuery
|
|
|
152775
152816
|
}
|
|
152776
152817
|
}, [isReady, resultTarget, tabs, showQuery]);
|
|
152777
152818
|
}
|
|
152819
|
+
const DRAFTS_KEY = "grain:web-drafts";
|
|
152820
|
+
function saveWebDraft(draft, sessionId) {
|
|
152821
|
+
const allDrafts = loadAllDrafts();
|
|
152822
|
+
const sessionDrafts = allDrafts[sessionId] ?? {};
|
|
152823
|
+
sessionDrafts[draft.id] = draft;
|
|
152824
|
+
allDrafts[sessionId] = sessionDrafts;
|
|
152825
|
+
writeStoredValue(DRAFTS_KEY, JSON.stringify(allDrafts));
|
|
152826
|
+
}
|
|
152827
|
+
function loadAllSessionDrafts(sessionId) {
|
|
152828
|
+
const allDrafts = loadAllDrafts();
|
|
152829
|
+
return Object.values(allDrafts[sessionId] ?? {});
|
|
152830
|
+
}
|
|
152831
|
+
function deleteWebDraft(id, sessionId) {
|
|
152832
|
+
const allDrafts = loadAllDrafts();
|
|
152833
|
+
if (allDrafts[sessionId]?.[id]) {
|
|
152834
|
+
delete allDrafts[sessionId][id];
|
|
152835
|
+
writeStoredValue(DRAFTS_KEY, JSON.stringify(allDrafts));
|
|
152836
|
+
}
|
|
152837
|
+
}
|
|
152838
|
+
function loadAllDrafts() {
|
|
152839
|
+
const stored = readStoredValue(DRAFTS_KEY);
|
|
152840
|
+
if (!stored) return {};
|
|
152841
|
+
try {
|
|
152842
|
+
return JSON.parse(stored);
|
|
152843
|
+
} catch {
|
|
152844
|
+
return {};
|
|
152845
|
+
}
|
|
152846
|
+
}
|
|
152847
|
+
function flushDrafts({
|
|
152848
|
+
tabs,
|
|
152849
|
+
queryConnections
|
|
152850
|
+
}) {
|
|
152851
|
+
tabs.forEach((tab) => {
|
|
152852
|
+
if (!tab.sessionId || tab.sessionId === WEB_SESSION_ID) {
|
|
152853
|
+
saveWebDraft(
|
|
152854
|
+
{
|
|
152855
|
+
id: tab.id,
|
|
152856
|
+
title: tab.title,
|
|
152857
|
+
sql: tab.sql,
|
|
152858
|
+
connectionId: queryConnections[tab.id] || "built-in-duckdb"
|
|
152859
|
+
},
|
|
152860
|
+
WEB_SESSION_ID
|
|
152861
|
+
);
|
|
152862
|
+
}
|
|
152863
|
+
});
|
|
152864
|
+
}
|
|
152778
152865
|
const THEME_KEY = "grain:web-theme";
|
|
152779
152866
|
const SIDEBAR_DEFAULT_WIDTH = 220;
|
|
152780
152867
|
const SIDEBAR_MIN_WIDTH = 160;
|
|
@@ -152782,7 +152869,10 @@ const SIDEBAR_MAX_WIDTH = 400;
|
|
|
152782
152869
|
const SIDEBAR_AUTO_COLLAPSE_QUERY = "(max-width: 640px)";
|
|
152783
152870
|
const SIDEBAR_WIDTH_STORAGE_KEY = "sql-preview:web-sidebar-width";
|
|
152784
152871
|
const CONNECTIONS_COLLAPSED_KEY = "grain:web-connections-collapsed";
|
|
152785
|
-
function WebApp({
|
|
152872
|
+
function WebApp({
|
|
152873
|
+
viewHint,
|
|
152874
|
+
resultTarget
|
|
152875
|
+
}) {
|
|
152786
152876
|
const { onMessage, isReady, sendMessage, callServerTool, track } = useHostContext();
|
|
152787
152877
|
const session = useWebSession();
|
|
152788
152878
|
const {
|
|
@@ -152854,6 +152944,7 @@ function WebApp({ viewHint, resultTarget }) {
|
|
|
152854
152944
|
sendMessage("tabClosed", { tabId: id });
|
|
152855
152945
|
}
|
|
152856
152946
|
closeTab(id);
|
|
152947
|
+
deleteWebDraft(id, WEB_SESSION_ID);
|
|
152857
152948
|
}
|
|
152858
152949
|
};
|
|
152859
152950
|
const reopenQuery = () => {
|
|
@@ -152975,6 +153066,43 @@ function WebApp({ viewHint, resultTarget }) {
|
|
|
152975
153066
|
if (isReady) void fetchConnections();
|
|
152976
153067
|
}, [isReady, fetchConnections]);
|
|
152977
153068
|
useWebAppInitialTarget(isReady, viewHint, resultTarget, tabs, showQuery, openSettingsPanel);
|
|
153069
|
+
const draftsRestoredRef = reactExports.useRef(false);
|
|
153070
|
+
reactExports.useEffect(() => {
|
|
153071
|
+
if (!isReady || draftsRestoredRef.current) return;
|
|
153072
|
+
draftsRestoredRef.current = true;
|
|
153073
|
+
const drafts = loadAllSessionDrafts(WEB_SESSION_ID);
|
|
153074
|
+
const storeState = useAppStore.getState();
|
|
153075
|
+
drafts.forEach((draft) => {
|
|
153076
|
+
const existingTab = storeState.tabs.find((t) => t.id === draft.id);
|
|
153077
|
+
if (existingTab) {
|
|
153078
|
+
storeState.updateTab(draft.id, { title: draft.title, sql: draft.sql });
|
|
153079
|
+
setQueryConnections((current2) => ({ ...current2, [draft.id]: draft.connectionId }));
|
|
153080
|
+
} else {
|
|
153081
|
+
const parsed = parseProjectedTabId(draft.id);
|
|
153082
|
+
const remoteId = parsed?.tabId ?? crypto.randomUUID();
|
|
153083
|
+
storeState.addTab(draft.sql, draft.id, draft.title, void 0, {
|
|
153084
|
+
sessionId: WEB_SESSION_ID,
|
|
153085
|
+
remoteId
|
|
153086
|
+
});
|
|
153087
|
+
setQueryConnections((current2) => ({ ...current2, [draft.id]: draft.connectionId }));
|
|
153088
|
+
}
|
|
153089
|
+
});
|
|
153090
|
+
}, [isReady]);
|
|
153091
|
+
const draftsRef = reactExports.useRef({
|
|
153092
|
+
tabs,
|
|
153093
|
+
queryConnections
|
|
153094
|
+
});
|
|
153095
|
+
draftsRef.current = { tabs, queryConnections };
|
|
153096
|
+
reactExports.useEffect(() => {
|
|
153097
|
+
if (!isReady) return;
|
|
153098
|
+
const timeout = setTimeout(() => flushDrafts(draftsRef.current), 300);
|
|
153099
|
+
return () => clearTimeout(timeout);
|
|
153100
|
+
}, [tabs, queryConnections, isReady]);
|
|
153101
|
+
reactExports.useEffect(() => {
|
|
153102
|
+
const onPageHide = () => flushDrafts(draftsRef.current);
|
|
153103
|
+
window.addEventListener("pagehide", onPageHide);
|
|
153104
|
+
return () => window.removeEventListener("pagehide", onPageHide);
|
|
153105
|
+
}, []);
|
|
152978
153106
|
reactExports.useEffect(() => {
|
|
152979
153107
|
const mediaQuery = window.matchMedia(SIDEBAR_AUTO_COLLAPSE_QUERY);
|
|
152980
153108
|
const handleChange = (e2) => {
|
|
@@ -167004,12 +167132,39 @@ body {
|
|
|
167004
167132
|
}
|
|
167005
167133
|
.sp-web-reauth-action {
|
|
167006
167134
|
display: flex;
|
|
167007
|
-
align-items:
|
|
167135
|
+
align-items: stretch;
|
|
167008
167136
|
gap: 8px;
|
|
167137
|
+
margin-block: 4px;
|
|
167138
|
+
}
|
|
167139
|
+
.sp-web-reauth-action .sp-web-reauth-command,
|
|
167140
|
+
.sp-web-reauth-action .sp-web-secondary-button {
|
|
167141
|
+
display: inline-flex;
|
|
167142
|
+
align-items: center;
|
|
167143
|
+
padding: 0 14px;
|
|
167144
|
+
min-height: 36px;
|
|
167145
|
+
border-radius: 6px;
|
|
167146
|
+
font-size: 14px;
|
|
167147
|
+
}
|
|
167148
|
+
.sp-web-reauth-command,
|
|
167149
|
+
.sp-web-reauth-detail code {
|
|
167150
|
+
font-variant-ligatures: none;
|
|
167151
|
+
letter-spacing: 0;
|
|
167009
167152
|
}
|
|
167010
167153
|
.sp-web-reauth-detail {
|
|
167011
|
-
|
|
167154
|
+
max-width: 52ch;
|
|
167155
|
+
color: var(--grain-ink-muted);
|
|
167156
|
+
font-size: 13px;
|
|
167157
|
+
line-height: 1.6;
|
|
167158
|
+
}
|
|
167159
|
+
.sp-web-reauth-detail code {
|
|
167160
|
+
padding: 1px 6px;
|
|
167161
|
+
border: 1px solid var(--grain-border-subtle);
|
|
167162
|
+
border-radius: 4px;
|
|
167163
|
+
background: var(--grain-surface);
|
|
167164
|
+
color: var(--grain-ink);
|
|
167165
|
+
font-family: var(--sp-font-mono, monospace);
|
|
167012
167166
|
font-size: 12px;
|
|
167167
|
+
white-space: nowrap;
|
|
167013
167168
|
}
|
|
167014
167169
|
.sp-web-connection-item {
|
|
167015
167170
|
width: 100%;
|