@haex-space/vault-sdk 3.2.7 → 3.2.8

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.mjs CHANGED
@@ -396,16 +396,17 @@ var HAEXTENSION_EVENTS = {
396
396
  /** File system change detected (from native file watcher) */
397
397
  FILE_CHANGED: "filesync:file-changed",
398
398
  /** Tables have been updated via sync (CRDT pull from server) */
399
- SYNC_TABLES_UPDATED: "haextension:sync:tables-updated"
399
+ SYNC_TABLES_UPDATED: "haextension:sync:tables-updated",
400
+ /** A runtime permission prompt was resolved (granted/denied) by the user.
401
+ * The SDK uses this to auto-retry the original request; extensions may also
402
+ * subscribe via `client.on(HAEXTENSION_EVENTS.PERMISSION_RESOLVED, ...)`. */
403
+ PERMISSION_RESOLVED: "extension:permission-resolved"
400
404
  };
401
405
  var EXTERNAL_EVENTS = {
402
406
  /** External request from authorized client */
403
407
  REQUEST: "haextension:external:request",
404
408
  /** AI action request (tool calls from AI assistant) */
405
- ACTION_REQUEST: "haextension:action:request",
406
- /** New external client requesting authorization */
407
- AUTHORIZATION_REQUEST: "external:authorization-request"
408
- };
409
+ ACTION_REQUEST: "haextension:action:request"};
409
410
  var SHELL_EVENTS = {
410
411
  /** PTY output data from a shell session */
411
412
  OUTPUT: "shell:output",
@@ -419,6 +420,9 @@ var TABLE_SEPARATOR = "__";
419
420
  function getTableName(publicKey, extensionName, tableName) {
420
421
  return `${publicKey}${TABLE_SEPARATOR}${extensionName}${TABLE_SEPARATOR}${tableName}`;
421
422
  }
423
+ function isPermissionPromptError(error) {
424
+ return typeof error === "object" && error !== null && "code" in error && error.code === 1004 /* PROMPT_REQUIRED */;
425
+ }
422
426
  var HaexVaultSdkError = class extends Error {
423
427
  constructor(code, messageKey, details) {
424
428
  super(messageKey);
@@ -1873,6 +1877,21 @@ async function initNativeMode(ctx, log, onEvent, onContextChange) {
1873
1877
  await setupTauriEventListeners(ctx, log, onEvent, onContextChange);
1874
1878
  return { extensionInfo: extensionInfo2, context: context2 };
1875
1879
  }
1880
+ async function forwardEvent(listen, log, onEvent, listenOptions, eventName, shape) {
1881
+ try {
1882
+ await listen(eventName, (event) => {
1883
+ if (event.payload == null) {
1884
+ log(`Event '${eventName}' received with no payload`);
1885
+ return;
1886
+ }
1887
+ const extra = shape ? shape(event.payload) : { data: event.payload };
1888
+ onEvent({ type: eventName, timestamp: Date.now(), ...extra });
1889
+ }, listenOptions);
1890
+ log(`Listener registered: ${eventName}`);
1891
+ } catch (error) {
1892
+ log(`Failed to register listener '${eventName}':`, error);
1893
+ }
1894
+ }
1876
1895
  async function setupTauriEventListeners(ctx, log, onEvent, onContextChange) {
1877
1896
  const { listen } = getTauriEvent();
1878
1897
  const webviewLabel = getCurrentWebviewLabel();
@@ -1904,207 +1923,29 @@ async function setupTauriEventListeners(ctx, log, onEvent, onContextChange) {
1904
1923
  } catch (error) {
1905
1924
  log("Failed to setup context change listener:", error);
1906
1925
  }
1907
- try {
1908
- await listen(EXTERNAL_EVENTS.REQUEST, (event) => {
1909
- log("====== EXTERNAL REQUEST RECEIVED ======");
1910
- log("Event payload:", JSON.stringify(event.payload, null, 2));
1911
- if (event.payload) {
1912
- onEvent({
1913
- type: EXTERNAL_EVENTS.REQUEST,
1914
- data: event.payload,
1915
- timestamp: Date.now()
1916
- });
1917
- } else {
1918
- log("External request event has no payload!");
1919
- }
1920
- }, listenOptions);
1921
- log("External request listener registered successfully");
1922
- } catch (error) {
1923
- log("Failed to setup external request listener:", error);
1924
- }
1925
- try {
1926
- await listen(EXTERNAL_EVENTS.ACTION_REQUEST, (event) => {
1927
- log("====== AI ACTION REQUEST RECEIVED ======");
1928
- log("Payload:", JSON.stringify(event.payload));
1929
- if (event.payload) {
1930
- onEvent({
1931
- type: EXTERNAL_EVENTS.ACTION_REQUEST,
1932
- data: event.payload,
1933
- timestamp: Date.now()
1934
- });
1935
- } else {
1936
- log("AI action request event has no payload!");
1937
- }
1938
- }, listenOptions);
1939
- log("AI action request listener registered successfully");
1940
- } catch (error) {
1941
- log("Failed to setup AI action request listener:", error);
1942
- }
1943
- log("Registering file change listener for:", HAEXTENSION_EVENTS.FILE_CHANGED);
1944
- try {
1945
- await listen(HAEXTENSION_EVENTS.FILE_CHANGED, (event) => {
1946
- log("File change event received:", event.payload);
1947
- if (event.payload) {
1948
- const payload = event.payload;
1949
- onEvent({
1950
- type: HAEXTENSION_EVENTS.FILE_CHANGED,
1951
- ruleId: payload.ruleId,
1952
- changeType: payload.changeType,
1953
- path: payload.path,
1954
- timestamp: Date.now()
1955
- });
1956
- }
1957
- }, listenOptions);
1958
- log("File change listener registered successfully");
1959
- } catch (error) {
1960
- log("Failed to setup file change listener:", error);
1961
- }
1962
- log("Registering sync tables updated listener for:", HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED);
1963
- try {
1964
- await listen(HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED, (event) => {
1965
- log("Sync tables updated event received:", event.payload);
1966
- if (event.payload) {
1967
- const payload = event.payload;
1968
- onEvent({
1969
- type: HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED,
1970
- data: { tables: payload.tables },
1971
- timestamp: Date.now()
1972
- });
1973
- }
1974
- }, listenOptions);
1975
- log("Sync tables updated listener registered successfully");
1976
- } catch (error) {
1977
- log("Failed to setup sync tables updated listener:", error);
1978
- }
1979
- log("Setting up LocalSend event listeners");
1980
- try {
1981
- await setupLocalSendEventListeners(log, onEvent, listenOptions);
1982
- log("LocalSend event listeners setup complete");
1983
- } catch (error) {
1984
- log("Failed to setup LocalSend event listeners:", error);
1985
- }
1986
- log("Setting up Shell event listeners");
1987
- try {
1988
- await listen(SHELL_EVENTS.OUTPUT, (event) => {
1989
- if (event.payload) {
1990
- const payload = event.payload;
1991
- onEvent({
1992
- type: SHELL_EVENTS.OUTPUT,
1993
- timestamp: Date.now(),
1994
- sessionId: payload.sessionId,
1995
- data: payload.data
1996
- });
1997
- }
1998
- }, listenOptions);
1999
- log("Shell output listener registered");
2000
- await listen(SHELL_EVENTS.EXIT, (event) => {
2001
- if (event.payload) {
2002
- const payload = event.payload;
2003
- onEvent({
2004
- type: SHELL_EVENTS.EXIT,
2005
- timestamp: Date.now(),
2006
- sessionId: payload.sessionId,
2007
- exitCode: payload.exitCode
2008
- });
2009
- }
2010
- }, listenOptions);
2011
- log("Shell exit listener registered");
2012
- } catch (error) {
2013
- log("Failed to setup Shell event listeners:", error);
2014
- }
2015
- }
2016
- async function setupLocalSendEventListeners(log, onEvent, listenOptions) {
2017
- const { listen } = getTauriEvent();
2018
- try {
2019
- await listen(LOCALSEND_EVENTS.deviceDiscovered, (event) => {
2020
- log("LocalSend device discovered:", event.payload);
2021
- if (event.payload) {
2022
- onEvent({
2023
- type: LOCALSEND_EVENTS.deviceDiscovered,
2024
- data: event.payload,
2025
- timestamp: Date.now()
2026
- });
2027
- }
2028
- }, listenOptions);
2029
- log("LocalSend device discovered listener registered");
2030
- } catch (error) {
2031
- log("Failed to setup LocalSend device discovered listener:", error);
2032
- }
2033
- try {
2034
- await listen(LOCALSEND_EVENTS.deviceLost, (event) => {
2035
- log("LocalSend device lost:", event.payload);
2036
- if (event.payload) {
2037
- onEvent({
2038
- type: LOCALSEND_EVENTS.deviceLost,
2039
- data: event.payload,
2040
- timestamp: Date.now()
2041
- });
2042
- }
2043
- }, listenOptions);
2044
- log("LocalSend device lost listener registered");
2045
- } catch (error) {
2046
- log("Failed to setup LocalSend device lost listener:", error);
2047
- }
2048
- try {
2049
- await listen(LOCALSEND_EVENTS.transferRequest, (event) => {
2050
- log("LocalSend transfer request:", event.payload);
2051
- if (event.payload) {
2052
- onEvent({
2053
- type: LOCALSEND_EVENTS.transferRequest,
2054
- data: event.payload,
2055
- timestamp: Date.now()
2056
- });
2057
- }
2058
- }, listenOptions);
2059
- log("LocalSend transfer request listener registered");
2060
- } catch (error) {
2061
- log("Failed to setup LocalSend transfer request listener:", error);
2062
- }
2063
- try {
2064
- await listen(LOCALSEND_EVENTS.transferProgress, (event) => {
2065
- log("LocalSend transfer progress event:", event);
2066
- if (event.payload) {
2067
- onEvent({
2068
- type: LOCALSEND_EVENTS.transferProgress,
2069
- data: event.payload,
2070
- timestamp: Date.now()
2071
- });
2072
- }
2073
- }, listenOptions);
2074
- log("LocalSend transfer progress listener registered");
2075
- } catch (error) {
2076
- log("Failed to setup LocalSend transfer progress listener:", error);
2077
- }
2078
- try {
2079
- await listen(LOCALSEND_EVENTS.transferComplete, (event) => {
2080
- log("LocalSend transfer complete:", event.payload);
2081
- if (event.payload) {
2082
- onEvent({
2083
- type: LOCALSEND_EVENTS.transferComplete,
2084
- data: event.payload,
2085
- timestamp: Date.now()
2086
- });
2087
- }
2088
- }, listenOptions);
2089
- log("LocalSend transfer complete listener registered");
2090
- } catch (error) {
2091
- log("Failed to setup LocalSend transfer complete listener:", error);
2092
- }
2093
- try {
2094
- await listen(LOCALSEND_EVENTS.transferFailed, (event) => {
2095
- log("LocalSend transfer failed:", event.payload);
2096
- if (event.payload) {
2097
- onEvent({
2098
- type: LOCALSEND_EVENTS.transferFailed,
2099
- data: event.payload,
2100
- timestamp: Date.now()
2101
- });
2102
- }
2103
- }, listenOptions);
2104
- log("LocalSend transfer failed listener registered");
2105
- } catch (error) {
2106
- log("Failed to setup LocalSend transfer failed listener:", error);
2107
- }
1926
+ for (const eventName of [
1927
+ HAEXTENSION_EVENTS.PERMISSION_RESOLVED,
1928
+ EXTERNAL_EVENTS.REQUEST,
1929
+ EXTERNAL_EVENTS.ACTION_REQUEST,
1930
+ ...Object.values(LOCALSEND_EVENTS)
1931
+ ]) {
1932
+ await forwardEvent(listen, log, onEvent, listenOptions, eventName);
1933
+ }
1934
+ await forwardEvent(listen, log, onEvent, listenOptions, HAEXTENSION_EVENTS.SYNC_TABLES_UPDATED, (payload) => ({
1935
+ data: { tables: payload.tables }
1936
+ }));
1937
+ await forwardEvent(listen, log, onEvent, listenOptions, HAEXTENSION_EVENTS.FILE_CHANGED, (payload) => {
1938
+ const { ruleId, changeType, path } = payload;
1939
+ return { ruleId, changeType, path };
1940
+ });
1941
+ await forwardEvent(listen, log, onEvent, listenOptions, SHELL_EVENTS.OUTPUT, (payload) => {
1942
+ const { sessionId, data } = payload;
1943
+ return { sessionId, data };
1944
+ });
1945
+ await forwardEvent(listen, log, onEvent, listenOptions, SHELL_EVENTS.EXIT, (payload) => {
1946
+ const { sessionId, exitCode } = payload;
1947
+ return { sessionId, exitCode };
1948
+ });
2108
1949
  }
2109
1950
  async function initIframeMode(ctx, log, messageHandler) {
2110
1951
  if (!isInIframe()) {
@@ -2448,6 +2289,68 @@ var AI_COMMANDS = {
2448
2289
  actionRespond: "ai_action_respond"
2449
2290
  };
2450
2291
 
2292
+ // src/client/permissionRetry.ts
2293
+ var PERMISSION_DECISION_TIMEOUT_MS = 5 * 60 * 1e3;
2294
+ var MAX_PERMISSION_RETRIES = 3;
2295
+ function permissionKey(resourceType, action, target) {
2296
+ return `${resourceType}:${action}:${target}`;
2297
+ }
2298
+ var PermissionWaiterRegistry = class {
2299
+ constructor() {
2300
+ this.waiters = /* @__PURE__ */ new Map();
2301
+ }
2302
+ /** Wait for a decision on `key`; resolves "timeout" if none arrives in time. */
2303
+ wait(key, timeoutMs = PERMISSION_DECISION_TIMEOUT_MS) {
2304
+ return new Promise((resolve) => {
2305
+ const set = this.waiters.get(key) ?? /* @__PURE__ */ new Set();
2306
+ this.waiters.set(key, set);
2307
+ const settle = (outcome) => {
2308
+ if (!set.has(callback)) return;
2309
+ set.delete(callback);
2310
+ if (set.size === 0) this.waiters.delete(key);
2311
+ clearTimeout(timer);
2312
+ resolve(outcome);
2313
+ };
2314
+ const callback = (outcome) => settle(outcome);
2315
+ const timer = setTimeout(() => settle("timeout"), timeoutMs);
2316
+ set.add(callback);
2317
+ });
2318
+ }
2319
+ /** Resolve everyone waiting on `key` with the user's decision. */
2320
+ resolve(key, decision) {
2321
+ const set = this.waiters.get(key);
2322
+ if (!set) return;
2323
+ for (const callback of [...set]) callback(decision);
2324
+ }
2325
+ };
2326
+ function toDeniedError(error) {
2327
+ return { ...error, code: 1002 /* DENIED */ };
2328
+ }
2329
+ async function withPermissionRetry(send, registry, log, timeoutMs = PERMISSION_DECISION_TIMEOUT_MS) {
2330
+ for (let attempt = 0; ; attempt++) {
2331
+ try {
2332
+ return await send();
2333
+ } catch (error) {
2334
+ if (!isPermissionPromptError(error) || attempt >= MAX_PERMISSION_RETRIES) {
2335
+ throw error;
2336
+ }
2337
+ const key = permissionKey(error.resourceType, error.action, error.target);
2338
+ log(`Permission prompt required for ${key} \u2014 waiting for user decision`);
2339
+ const outcome = await registry.wait(key, timeoutMs);
2340
+ if (outcome === "granted") {
2341
+ log(`Permission ${key} granted \u2014 retrying request`);
2342
+ continue;
2343
+ }
2344
+ if (outcome === "denied") {
2345
+ log(`Permission ${key} denied`);
2346
+ throw toDeniedError(error);
2347
+ }
2348
+ log(`Permission ${key} prompt timed out`);
2349
+ throw error;
2350
+ }
2351
+ }
2352
+ }
2353
+
2451
2354
  // src/client.ts
2452
2355
  var HaexVaultSdk = class {
2453
2356
  constructor(config = {}) {
@@ -2463,6 +2366,7 @@ var HaexVaultSdk = class {
2463
2366
  this.eventListeners = /* @__PURE__ */ new Map();
2464
2367
  this.externalRequestHandlers = /* @__PURE__ */ new Map();
2465
2368
  this.reactiveSubscribers = /* @__PURE__ */ new Set();
2369
+ this.permissionWaiters = new PermissionWaiterRegistry();
2466
2370
  // Handlers
2467
2371
  this.messageHandler = null;
2468
2372
  /**
@@ -2499,6 +2403,14 @@ var HaexVaultSdk = class {
2499
2403
  this.passwords = new PasswordsAPI(this);
2500
2404
  this.mail = new MailAPI(this);
2501
2405
  installConsoleForwarding(this.config.debug);
2406
+ this.on(HAEXTENSION_EVENTS.PERMISSION_RESOLVED, (event) => {
2407
+ const data = event.data;
2408
+ if (!data) return;
2409
+ this.permissionWaiters.resolve(
2410
+ permissionKey(data.resourceType, data.action, data.target),
2411
+ data.decision === "denied" ? "denied" : "granted"
2412
+ );
2413
+ });
2502
2414
  this.readyPromise = new Promise((resolve, reject) => {
2503
2415
  this.resolveReady = resolve;
2504
2416
  this.rejectReady = reject;
@@ -2647,24 +2559,27 @@ var HaexVaultSdk = class {
2647
2559
  // ==========================================================================
2648
2560
  async request(method, params) {
2649
2561
  const resolvedParams = params ?? {};
2650
- if (this.isNativeWindow && hasTauri()) {
2651
- const paramsWithCredentials = {
2652
- ...resolvedParams,
2653
- publicKey: this._extensionInfo?.publicKey,
2654
- name: this._extensionInfo?.name
2655
- };
2656
- return sendInvoke(method, paramsWithCredentials, this.config, this.log.bind(this));
2657
- }
2658
- const requestId = generateRequestId(++this.requestCounter);
2659
- return sendPostMessage(
2660
- method,
2661
- resolvedParams,
2662
- requestId,
2663
- this.config,
2664
- this._extensionInfo,
2665
- this.pendingRequests,
2666
- this.hostPort
2667
- );
2562
+ const send = () => {
2563
+ if (this.isNativeWindow && hasTauri()) {
2564
+ const paramsWithCredentials = {
2565
+ ...resolvedParams,
2566
+ publicKey: this._extensionInfo?.publicKey,
2567
+ name: this._extensionInfo?.name
2568
+ };
2569
+ return sendInvoke(method, paramsWithCredentials, this.config, this.log.bind(this));
2570
+ }
2571
+ const requestId = generateRequestId(++this.requestCounter);
2572
+ return sendPostMessage(
2573
+ method,
2574
+ resolvedParams,
2575
+ requestId,
2576
+ this.config,
2577
+ this._extensionInfo,
2578
+ this.pendingRequests,
2579
+ this.hostPort
2580
+ );
2581
+ };
2582
+ return withPermissionRetry(send, this.permissionWaiters, this.log.bind(this));
2668
2583
  }
2669
2584
  // ==========================================================================
2670
2585
  // Private: Initialization