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.
@@ -17890,12 +17890,39 @@ body {
17890
17890
  }
17891
17891
  .sp-web-reauth-action {
17892
17892
  display: flex;
17893
- align-items: center;
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
- color: var(--sp-muted);
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: center;
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
- color: var(--sp-muted);
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: ${data.error || res.statusText}`
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
- const blob = await res.blob();
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({ type: "statusMessage", message: `Export failed: ${err.message}` })
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({ viewHint, resultTarget }) {
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: center;
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
- color: var(--sp-muted);
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%;