@haex-space/vault-sdk 3.2.7 → 3.3.0

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/svelte.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { H as HaexVaultSdk, S as StorageAPI } from './client-Bbm83Oy6.mjs';
1
+ import { H as HaexVaultSdk, S as StorageAPI } from './client-BW3ayV19.mjs';
2
2
  import * as drizzle_orm_sqlite_proxy from 'drizzle-orm/sqlite-proxy';
3
3
  import { Readable } from 'svelte/store';
4
- import { A as ApplicationContext, a as ExtensionInfo, H as HaexHubConfig } from './types-CDMBvvjl.mjs';
4
+ import { A as ApplicationContext, a as ExtensionInfo, H as HaexHubConfig } from './types-fHuxbqa4.mjs';
5
5
 
6
6
  /**
7
7
  * Initialize the HaexVault SDK for Svelte
package/dist/svelte.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { H as HaexVaultSdk, S as StorageAPI } from './client-exZiz0Ph.js';
1
+ import { H as HaexVaultSdk, S as StorageAPI } from './client-Cm23j1wm.js';
2
2
  import * as drizzle_orm_sqlite_proxy from 'drizzle-orm/sqlite-proxy';
3
3
  import { Readable } from 'svelte/store';
4
- import { A as ApplicationContext, a as ExtensionInfo, H as HaexHubConfig } from './types-CDMBvvjl.js';
4
+ import { A as ApplicationContext, a as ExtensionInfo, H as HaexHubConfig } from './types-fHuxbqa4.js';
5
5
 
6
6
  /**
7
7
  * Initialize the HaexVault SDK for Svelte
package/dist/svelte.js CHANGED
@@ -398,16 +398,21 @@ var HAEXTENSION_EVENTS = {
398
398
  /** File system change detected (from native file watcher) */
399
399
  FILE_CHANGED: "filesync:file-changed",
400
400
  /** Tables have been updated via sync (CRDT pull from server) */
401
- SYNC_TABLES_UPDATED: "haextension:sync:tables-updated"
401
+ SYNC_TABLES_UPDATED: "haextension:sync:tables-updated",
402
+ /** A runtime permission prompt was resolved (granted/denied) by the user.
403
+ * The SDK uses this to auto-retry the original request; extensions may also
404
+ * subscribe via `client.on(HAEXTENSION_EVENTS.PERMISSION_RESOLVED, ...)`. */
405
+ PERMISSION_RESOLVED: "extension:permission-resolved"
406
+ };
407
+ var NOTIFICATION_EVENTS = {
408
+ /** A click on one of this extension's notifications (body or action button). */
409
+ CLICK: "haextension:notification:click"
402
410
  };
403
411
  var EXTERNAL_EVENTS = {
404
412
  /** External request from authorized client */
405
413
  REQUEST: "haextension:external:request",
406
414
  /** AI action request (tool calls from AI assistant) */
407
- ACTION_REQUEST: "haextension:action:request",
408
- /** New external client requesting authorization */
409
- AUTHORIZATION_REQUEST: "external:authorization-request"
410
- };
415
+ ACTION_REQUEST: "haextension:action:request"};
411
416
  var SHELL_EVENTS = {
412
417
  /** PTY output data from a shell session */
413
418
  OUTPUT: "shell:output",
@@ -421,6 +426,9 @@ var TABLE_SEPARATOR = "__";
421
426
  function getTableName(publicKey, extensionName, tableName) {
422
427
  return `${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}`;
423
428
  }
429
+ function isPermissionPromptError(error) {
430
+ return typeof error === "object" && error !== null && "code" in error && error.code === 1004 /* PROMPT_REQUIRED */;
431
+ }
424
432
  var HaexVaultSdkError = class extends Error {
425
433
  constructor(code, messageKey, details) {
426
434
  super(messageKey);
@@ -684,6 +692,14 @@ var MAIL_COMMANDS = {
684
692
  buildRfc822: "extension_mail_build_rfc822"
685
693
  };
686
694
 
695
+ // src/commands/notifications.ts
696
+ var NOTIFICATION_COMMANDS = {
697
+ /** Show an OS notification. Returns the assigned notification id. */
698
+ show: "extension_notifications_show",
699
+ /** Dismiss a previously shown notification (only own notifications). */
700
+ dismiss: "extension_notifications_dismiss"
701
+ };
702
+
687
703
  // src/api/storage.ts
688
704
  var StorageAPI = class {
689
705
  constructor(client) {
@@ -1758,6 +1774,38 @@ var MailAPI = class {
1758
1774
  }
1759
1775
  };
1760
1776
 
1777
+ // src/api/notifications.ts
1778
+ var NotificationsAPI = class {
1779
+ constructor(client) {
1780
+ this.client = client;
1781
+ }
1782
+ /** Show a notification. Returns its id so it can be dismissed later. */
1783
+ async show(opts) {
1784
+ return this.client.request(NOTIFICATION_COMMANDS.show, {
1785
+ options: opts
1786
+ });
1787
+ }
1788
+ /** Dismiss a previously shown notification (only own notifications). */
1789
+ async dismiss(id) {
1790
+ return this.client.request(NOTIFICATION_COMMANDS.dismiss, { id });
1791
+ }
1792
+ /**
1793
+ * Listen for clicks on this extension's notifications. Useful when the
1794
+ * extension is already open and wants to react in-app (e.g. router.push the
1795
+ * `path`) instead of relying on the host to focus the webview.
1796
+ *
1797
+ * Returns an unsubscribe function.
1798
+ */
1799
+ onClick(handler) {
1800
+ const wrapped = (event) => {
1801
+ const data = event.data;
1802
+ if (data) handler(data);
1803
+ };
1804
+ this.client.on(NOTIFICATION_EVENTS.CLICK, wrapped);
1805
+ return () => this.client.off(NOTIFICATION_EVENTS.CLICK, wrapped);
1806
+ }
1807
+ };
1808
+
1761
1809
  // src/client/tableName.ts
1762
1810
  function validatePublicKey(publicKey) {
1763
1811
  if (!publicKey || typeof publicKey !== "string" || publicKey.trim() === "") {
@@ -1875,6 +1923,21 @@ async function initNativeMode(ctx, log, onEvent, onContextChange) {
1875
1923
  await setupTauriEventListeners(ctx, log, onEvent, onContextChange);
1876
1924
  return { extensionInfo: extensionInfo2, context: context2 };
1877
1925
  }
1926
+ async function forwardEvent(listen, log, onEvent, listenOptions, eventName, shape) {
1927
+ try {
1928
+ await listen(eventName, (event) => {
1929
+ if (event.payload == null) {
1930
+ log(`Event '${eventName}' received with no payload`);
1931
+ return;
1932
+ }
1933
+ const extra = shape ? shape(event.payload) : { data: event.payload };
1934
+ onEvent({ type: eventName, timestamp: Date.now(), ...extra });
1935
+ }, listenOptions);
1936
+ log(`Listener registered: ${eventName}`);
1937
+ } catch (error) {
1938
+ log(`Failed to register listener '${eventName}':`, error);
1939
+ }
1940
+ }
1878
1941
  async function setupTauriEventListeners(ctx, log, onEvent, onContextChange) {
1879
1942
  const { listen } = getTauriEvent();
1880
1943
  const webviewLabel = getCurrentWebviewLabel();
@@ -1906,207 +1969,30 @@ async function setupTauriEventListeners(ctx, log, onEvent, onContextChange) {
1906
1969
  } catch (error) {
1907
1970
  log("Failed to setup context change listener:", error);
1908
1971
  }
1909
- try {
1910
- await listen(EXTERNAL_EVENTS.REQUEST, (event) => {
1911
- log("====== EXTERNAL REQUEST RECEIVED ======");
1912
- log("Event payload:", JSON.stringify(event.payload, null, 2));
1913
- if (event.payload) {
1914
- onEvent({
1915
- type: EXTERNAL_EVENTS.REQUEST,
1916
- data: event.payload,
1917
- timestamp: Date.now()
1918
- });
1919
- } else {
1920
- log("External request event has no payload!");
1921
- }
1922
- }, listenOptions);
1923
- log("External request listener registered successfully");
1924
- } catch (error) {
1925
- log("Failed to setup external request listener:", error);
1926
- }
1927
- try {
1928
- await listen(EXTERNAL_EVENTS.ACTION_REQUEST, (event) => {
1929
- log("====== AI ACTION REQUEST RECEIVED ======");
1930
- log("Payload:", JSON.stringify(event.payload));
1931
- if (event.payload) {
1932
- onEvent({
1933
- type: EXTERNAL_EVENTS.ACTION_REQUEST,
1934
- data: event.payload,
1935
- timestamp: Date.now()
1936
- });
1937
- } else {
1938
- log("AI action request event has no payload!");
1939
- }
1940
- }, listenOptions);
1941
- log("AI action request listener registered successfully");
1942
- } catch (error) {
1943
- log("Failed to setup AI action request listener:", error);
1944
- }
1945
- log("Registering file change listener for:", HAEXTENSION_EVENTS.FILE_CHANGED);
1946
- try {
1947
- await listen(HAEXTENSION_EVENTS.FILE_CHANGED, (event) => {
1948
- log("File change event received:", event.payload);
1949
- if (event.payload) {
1950
- const payload = event.payload;
1951
- onEvent({
1952
- type: HAEXTENSION_EVENTS.FILE_CHANGED,
1953
- ruleId: payload.ruleId,
1954
- changeType: payload.changeType,
1955
- path: payload.path,
1956
- timestamp: Date.now()
1957
- });
1958
- }
1959
- }, listenOptions);
1960
- log("File change listener registered successfully");
1961
- } catch (error) {
1962
- log("Failed to setup file change listener:", error);
1963
- }
1964
- log("Registering sync tables updated listener for:", HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED);
1965
- try {
1966
- await listen(HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED, (event) => {
1967
- log("Sync tables updated event received:", event.payload);
1968
- if (event.payload) {
1969
- const payload = event.payload;
1970
- onEvent({
1971
- type: HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED,
1972
- data: { tables: payload.tables },
1973
- timestamp: Date.now()
1974
- });
1975
- }
1976
- }, listenOptions);
1977
- log("Sync tables updated listener registered successfully");
1978
- } catch (error) {
1979
- log("Failed to setup sync tables updated listener:", error);
1980
- }
1981
- log("Setting up LocalSend event listeners");
1982
- try {
1983
- await setupLocalSendEventListeners(log, onEvent, listenOptions);
1984
- log("LocalSend event listeners setup complete");
1985
- } catch (error) {
1986
- log("Failed to setup LocalSend event listeners:", error);
1987
- }
1988
- log("Setting up Shell event listeners");
1989
- try {
1990
- await listen(SHELL_EVENTS.OUTPUT, (event) => {
1991
- if (event.payload) {
1992
- const payload = event.payload;
1993
- onEvent({
1994
- type: SHELL_EVENTS.OUTPUT,
1995
- timestamp: Date.now(),
1996
- sessionId: payload.sessionId,
1997
- data: payload.data
1998
- });
1999
- }
2000
- }, listenOptions);
2001
- log("Shell output listener registered");
2002
- await listen(SHELL_EVENTS.EXIT, (event) => {
2003
- if (event.payload) {
2004
- const payload = event.payload;
2005
- onEvent({
2006
- type: SHELL_EVENTS.EXIT,
2007
- timestamp: Date.now(),
2008
- sessionId: payload.sessionId,
2009
- exitCode: payload.exitCode
2010
- });
2011
- }
2012
- }, listenOptions);
2013
- log("Shell exit listener registered");
2014
- } catch (error) {
2015
- log("Failed to setup Shell event listeners:", error);
2016
- }
2017
- }
2018
- async function setupLocalSendEventListeners(log, onEvent, listenOptions) {
2019
- const { listen } = getTauriEvent();
2020
- try {
2021
- await listen(LOCALSEND_EVENTS.deviceDiscovered, (event) => {
2022
- log("LocalSend device discovered:", event.payload);
2023
- if (event.payload) {
2024
- onEvent({
2025
- type: LOCALSEND_EVENTS.deviceDiscovered,
2026
- data: event.payload,
2027
- timestamp: Date.now()
2028
- });
2029
- }
2030
- }, listenOptions);
2031
- log("LocalSend device discovered listener registered");
2032
- } catch (error) {
2033
- log("Failed to setup LocalSend device discovered listener:", error);
2034
- }
2035
- try {
2036
- await listen(LOCALSEND_EVENTS.deviceLost, (event) => {
2037
- log("LocalSend device lost:", event.payload);
2038
- if (event.payload) {
2039
- onEvent({
2040
- type: LOCALSEND_EVENTS.deviceLost,
2041
- data: event.payload,
2042
- timestamp: Date.now()
2043
- });
2044
- }
2045
- }, listenOptions);
2046
- log("LocalSend device lost listener registered");
2047
- } catch (error) {
2048
- log("Failed to setup LocalSend device lost listener:", error);
2049
- }
2050
- try {
2051
- await listen(LOCALSEND_EVENTS.transferRequest, (event) => {
2052
- log("LocalSend transfer request:", event.payload);
2053
- if (event.payload) {
2054
- onEvent({
2055
- type: LOCALSEND_EVENTS.transferRequest,
2056
- data: event.payload,
2057
- timestamp: Date.now()
2058
- });
2059
- }
2060
- }, listenOptions);
2061
- log("LocalSend transfer request listener registered");
2062
- } catch (error) {
2063
- log("Failed to setup LocalSend transfer request listener:", error);
2064
- }
2065
- try {
2066
- await listen(LOCALSEND_EVENTS.transferProgress, (event) => {
2067
- log("LocalSend transfer progress event:", event);
2068
- if (event.payload) {
2069
- onEvent({
2070
- type: LOCALSEND_EVENTS.transferProgress,
2071
- data: event.payload,
2072
- timestamp: Date.now()
2073
- });
2074
- }
2075
- }, listenOptions);
2076
- log("LocalSend transfer progress listener registered");
2077
- } catch (error) {
2078
- log("Failed to setup LocalSend transfer progress listener:", error);
2079
- }
2080
- try {
2081
- await listen(LOCALSEND_EVENTS.transferComplete, (event) => {
2082
- log("LocalSend transfer complete:", event.payload);
2083
- if (event.payload) {
2084
- onEvent({
2085
- type: LOCALSEND_EVENTS.transferComplete,
2086
- data: event.payload,
2087
- timestamp: Date.now()
2088
- });
2089
- }
2090
- }, listenOptions);
2091
- log("LocalSend transfer complete listener registered");
2092
- } catch (error) {
2093
- log("Failed to setup LocalSend transfer complete listener:", error);
2094
- }
2095
- try {
2096
- await listen(LOCALSEND_EVENTS.transferFailed, (event) => {
2097
- log("LocalSend transfer failed:", event.payload);
2098
- if (event.payload) {
2099
- onEvent({
2100
- type: LOCALSEND_EVENTS.transferFailed,
2101
- data: event.payload,
2102
- timestamp: Date.now()
2103
- });
2104
- }
2105
- }, listenOptions);
2106
- log("LocalSend transfer failed listener registered");
2107
- } catch (error) {
2108
- log("Failed to setup LocalSend transfer failed listener:", error);
2109
- }
1972
+ for (const eventName of [
1973
+ HAEXTENSION_EVENTS.PERMISSION_RESOLVED,
1974
+ EXTERNAL_EVENTS.REQUEST,
1975
+ EXTERNAL_EVENTS.ACTION_REQUEST,
1976
+ NOTIFICATION_EVENTS.CLICK,
1977
+ ...Object.values(LOCALSEND_EVENTS)
1978
+ ]) {
1979
+ await forwardEvent(listen, log, onEvent, listenOptions, eventName);
1980
+ }
1981
+ await forwardEvent(listen, log, onEvent, listenOptions, HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED, (payload) => ({
1982
+ data: { tables: payload.tables }
1983
+ }));
1984
+ await forwardEvent(listen, log, onEvent, listenOptions, HAEXTENSION_EVENTS.FILE_CHANGED, (payload) => {
1985
+ const { ruleId, changeType, path } = payload;
1986
+ return { ruleId, changeType, path };
1987
+ });
1988
+ await forwardEvent(listen, log, onEvent, listenOptions, SHELL_EVENTS.OUTPUT, (payload) => {
1989
+ const { sessionId, data } = payload;
1990
+ return { sessionId, data };
1991
+ });
1992
+ await forwardEvent(listen, log, onEvent, listenOptions, SHELL_EVENTS.EXIT, (payload) => {
1993
+ const { sessionId, exitCode } = payload;
1994
+ return { sessionId, exitCode };
1995
+ });
2110
1996
  }
2111
1997
  async function initIframeMode(ctx, log, messageHandler) {
2112
1998
  if (!isInIframe()) {
@@ -2450,6 +2336,68 @@ var AI_COMMANDS = {
2450
2336
  actionRespond: "ai_action_respond"
2451
2337
  };
2452
2338
 
2339
+ // src/client/permissionRetry.ts
2340
+ var PERMISSION_DECISION_TIMEOUT_MS = 5 * 60 * 1e3;
2341
+ var MAX_PERMISSION_RETRIES = 3;
2342
+ function permissionKey(resourceType, action, target) {
2343
+ return `${resourceType}:${action}:${target}`;
2344
+ }
2345
+ var PermissionWaiterRegistry = class {
2346
+ constructor() {
2347
+ this.waiters = /* @__PURE__ */ new Map();
2348
+ }
2349
+ /** Wait for a decision on `key`; resolves "timeout" if none arrives in time. */
2350
+ wait(key, timeoutMs = PERMISSION_DECISION_TIMEOUT_MS) {
2351
+ return new Promise((resolve) => {
2352
+ const set = this.waiters.get(key) ?? /* @__PURE__ */ new Set();
2353
+ this.waiters.set(key, set);
2354
+ const settle = (outcome) => {
2355
+ if (!set.has(callback)) return;
2356
+ set.delete(callback);
2357
+ if (set.size === 0) this.waiters.delete(key);
2358
+ clearTimeout(timer);
2359
+ resolve(outcome);
2360
+ };
2361
+ const callback = (outcome) => settle(outcome);
2362
+ const timer = setTimeout(() => settle("timeout"), timeoutMs);
2363
+ set.add(callback);
2364
+ });
2365
+ }
2366
+ /** Resolve everyone waiting on `key` with the user's decision. */
2367
+ resolve(key, decision) {
2368
+ const set = this.waiters.get(key);
2369
+ if (!set) return;
2370
+ for (const callback of [...set]) callback(decision);
2371
+ }
2372
+ };
2373
+ function toDeniedError(error) {
2374
+ return { ...error, code: 1002 /* DENIED */ };
2375
+ }
2376
+ async function withPermissionRetry(send, registry, log, timeoutMs = PERMISSION_DECISION_TIMEOUT_MS) {
2377
+ for (let attempt = 0; ; attempt++) {
2378
+ try {
2379
+ return await send();
2380
+ } catch (error) {
2381
+ if (!isPermissionPromptError(error) || attempt >= MAX_PERMISSION_RETRIES) {
2382
+ throw error;
2383
+ }
2384
+ const key = permissionKey(error.resourceType, error.action, error.target);
2385
+ log(`Permission prompt required for ${key} \u2014 waiting for user decision`);
2386
+ const outcome = await registry.wait(key, timeoutMs);
2387
+ if (outcome === "granted") {
2388
+ log(`Permission ${key} granted \u2014 retrying request`);
2389
+ continue;
2390
+ }
2391
+ if (outcome === "denied") {
2392
+ log(`Permission ${key} denied`);
2393
+ throw toDeniedError(error);
2394
+ }
2395
+ log(`Permission ${key} prompt timed out`);
2396
+ throw error;
2397
+ }
2398
+ }
2399
+ }
2400
+
2453
2401
  // src/client.ts
2454
2402
  var HaexVaultSdk = class {
2455
2403
  constructor(config = {}) {
@@ -2465,6 +2413,7 @@ var HaexVaultSdk = class {
2465
2413
  this.eventListeners = /* @__PURE__ */ new Map();
2466
2414
  this.externalRequestHandlers = /* @__PURE__ */ new Map();
2467
2415
  this.reactiveSubscribers = /* @__PURE__ */ new Set();
2416
+ this.permissionWaiters = new PermissionWaiterRegistry();
2468
2417
  // Handlers
2469
2418
  this.messageHandler = null;
2470
2419
  /**
@@ -2500,7 +2449,16 @@ var HaexVaultSdk = class {
2500
2449
  this.shell = new ShellAPI(this);
2501
2450
  this.passwords = new PasswordsAPI(this);
2502
2451
  this.mail = new MailAPI(this);
2452
+ this.notifications = new NotificationsAPI(this);
2503
2453
  installConsoleForwarding(this.config.debug);
2454
+ this.on(HAEXTENSION_EVENTS.PERMISSION_RESOLVED, (event) => {
2455
+ const data = event.data;
2456
+ if (!data) return;
2457
+ this.permissionWaiters.resolve(
2458
+ permissionKey(data.resourceType, data.action, data.target),
2459
+ data.decision === "denied" ? "denied" : "granted"
2460
+ );
2461
+ });
2504
2462
  this.readyPromise = new Promise((resolve, reject) => {
2505
2463
  this.resolveReady = resolve;
2506
2464
  this.rejectReady = reject;
@@ -2649,24 +2607,27 @@ var HaexVaultSdk = class {
2649
2607
  // ==========================================================================
2650
2608
  async request(method, params) {
2651
2609
  const resolvedParams = params ?? {};
2652
- if (this.isNativeWindow && hasTauri()) {
2653
- const paramsWithCredentials = {
2654
- ...resolvedParams,
2655
- publicKey: this._extensionInfo?.publicKey,
2656
- name: this._extensionInfo?.name
2657
- };
2658
- return sendInvoke(method, paramsWithCredentials, this.config, this.log.bind(this));
2659
- }
2660
- const requestId = generateRequestId(++this.requestCounter);
2661
- return sendPostMessage(
2662
- method,
2663
- resolvedParams,
2664
- requestId,
2665
- this.config,
2666
- this._extensionInfo,
2667
- this.pendingRequests,
2668
- this.hostPort
2669
- );
2610
+ const send = () => {
2611
+ if (this.isNativeWindow && hasTauri()) {
2612
+ const paramsWithCredentials = {
2613
+ ...resolvedParams,
2614
+ publicKey: this._extensionInfo?.publicKey,
2615
+ name: this._extensionInfo?.name
2616
+ };
2617
+ return sendInvoke(method, paramsWithCredentials, this.config, this.log.bind(this));
2618
+ }
2619
+ const requestId = generateRequestId(++this.requestCounter);
2620
+ return sendPostMessage(
2621
+ method,
2622
+ resolvedParams,
2623
+ requestId,
2624
+ this.config,
2625
+ this._extensionInfo,
2626
+ this.pendingRequests,
2627
+ this.hostPort
2628
+ );
2629
+ };
2630
+ return withPermissionRetry(send, this.permissionWaiters, this.log.bind(this));
2670
2631
  }
2671
2632
  // ==========================================================================
2672
2633
  // Private: Initialization