@ateam-ai-mobile/expo 0.1.19 → 0.1.21

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.
Files changed (76) hide show
  1. package/dist/aiLive/provider.d.mts +20 -0
  2. package/dist/aiLive/provider.d.ts +20 -0
  3. package/dist/aiLive/provider.js +400 -0
  4. package/dist/aiLive/provider.js.map +1 -0
  5. package/dist/aiLive/provider.mjs +15 -0
  6. package/dist/aiLive/provider.mjs.map +1 -0
  7. package/dist/bootstrap.js +350 -3
  8. package/dist/bootstrap.js.map +1 -1
  9. package/dist/bootstrap.mjs +17 -12
  10. package/dist/bootstrap.mjs.map +1 -1
  11. package/dist/capabilities/index.d.mts +40 -1
  12. package/dist/capabilities/index.d.ts +40 -1
  13. package/dist/capabilities/index.js +50 -14
  14. package/dist/capabilities/index.js.map +1 -1
  15. package/dist/capabilities/index.mjs +49 -32
  16. package/dist/capabilities/index.mjs.map +1 -1
  17. package/dist/{chunk-42FP5DTO.mjs → chunk-2ZQEQJQ6.mjs} +1 -1
  18. package/dist/chunk-2ZQEQJQ6.mjs.map +1 -0
  19. package/dist/chunk-AE55BKYV.mjs +24 -0
  20. package/dist/chunk-AE55BKYV.mjs.map +1 -0
  21. package/dist/chunk-BAZFMTCC.mjs +815 -0
  22. package/dist/chunk-BAZFMTCC.mjs.map +1 -0
  23. package/dist/{chunk-Z5AI2SXX.mjs → chunk-C62NQA4F.mjs} +203 -1004
  24. package/dist/chunk-C62NQA4F.mjs.map +1 -0
  25. package/dist/{chunk-DE6Y4VRI.mjs → chunk-GA7DS3PA.mjs} +1 -1
  26. package/dist/{chunk-DE6Y4VRI.mjs.map → chunk-GA7DS3PA.mjs.map} +1 -1
  27. package/dist/chunk-GW7YGXOW.mjs +47 -0
  28. package/dist/chunk-GW7YGXOW.mjs.map +1 -0
  29. package/dist/chunk-JUBLG3PN.mjs +351 -0
  30. package/dist/chunk-JUBLG3PN.mjs.map +1 -0
  31. package/dist/{chunk-Z3P7RUCC.mjs → chunk-OEX4OKYQ.mjs} +13 -13
  32. package/dist/{chunk-6JX222YX.mjs → chunk-WQK6PUBN.mjs} +9 -2
  33. package/dist/chunk-WQK6PUBN.mjs.map +1 -0
  34. package/dist/device/createDeviceProvider.js.map +1 -1
  35. package/dist/device/createDeviceProvider.mjs +7 -6
  36. package/dist/device-bridge/index.mjs +8 -6
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +1 -1
  39. package/dist/push/index.d.mts +24 -0
  40. package/dist/push/index.d.ts +24 -0
  41. package/dist/push/index.js +224 -0
  42. package/dist/push/index.js.map +1 -0
  43. package/dist/push/index.mjs +174 -0
  44. package/dist/push/index.mjs.map +1 -0
  45. package/dist/push/silentSync.d.mts +8 -0
  46. package/dist/push/silentSync.d.ts +8 -0
  47. package/dist/push/silentSync.js +1075 -0
  48. package/dist/push/silentSync.js.map +1 -0
  49. package/dist/push/silentSync.mjs +67 -0
  50. package/dist/push/silentSync.mjs.map +1 -0
  51. package/dist/vision/index.d.mts +7 -1
  52. package/dist/vision/index.d.ts +7 -1
  53. package/dist/vision/index.js +8 -0
  54. package/dist/vision/index.js.map +1 -1
  55. package/dist/vision/index.mjs +3 -1
  56. package/ios/AdaVisionModule.swift +44 -1
  57. package/package.json +28 -6
  58. package/src/aiLive/provider.ts +242 -0
  59. package/src/aiLive/streamProtocol.ts +90 -0
  60. package/src/bootstrap.ts +7 -1
  61. package/src/capabilities/choices.ts +65 -0
  62. package/src/capabilities/config.ts +5 -4
  63. package/src/capabilities/index.ts +2 -0
  64. package/src/capabilities/picturesToAi.ts +40 -0
  65. package/src/capabilities/store.ts +38 -37
  66. package/src/index.ts +3 -0
  67. package/src/push/index.ts +18 -0
  68. package/src/push/messaging.ts +49 -0
  69. package/src/push/registration.ts +31 -0
  70. package/src/push/silentSync.ts +83 -0
  71. package/src/push/usePushNotifications.ts +224 -0
  72. package/src/vision/index.ts +14 -0
  73. package/dist/chunk-42FP5DTO.mjs.map +0 -1
  74. package/dist/chunk-6JX222YX.mjs.map +0 -1
  75. package/dist/chunk-Z5AI2SXX.mjs.map +0 -1
  76. /package/dist/{chunk-Z3P7RUCC.mjs.map → chunk-OEX4OKYQ.mjs.map} +0 -0
@@ -1,6 +1,3 @@
1
- import {
2
- getToday
3
- } from "./chunk-KOXF2FZM.mjs";
4
1
  import {
5
2
  collectHistory,
6
3
  collectMoodExtras,
@@ -8,6 +5,9 @@ import {
8
5
  hasPermission,
9
6
  requestPermission
10
7
  } from "./chunk-XQSY6MHP.mjs";
8
+ import {
9
+ getToday
10
+ } from "./chunk-KOXF2FZM.mjs";
11
11
  import {
12
12
  reverseGeocode
13
13
  } from "./chunk-7FWYEHN5.mjs";
@@ -189,438 +189,176 @@ var RelayClient = class {
189
189
  }
190
190
  };
191
191
 
192
- // src/device-bridge/actions/messaging.ts
193
- import * as SMS from "expo-sms";
194
- async function execute(actionId, params) {
195
- const phone = params.phone;
196
- const message = params.message;
197
- if (!phone || !message) {
198
- return {
199
- id: actionId,
200
- ok: false,
201
- error: "Missing phone or message parameter",
202
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
203
- };
204
- }
192
+ // src/device-bridge/collectors/location.ts
193
+ var location_exports = {};
194
+ __export(location_exports, {
195
+ collectCurrent: () => collectCurrent,
196
+ getPosition: () => getPosition,
197
+ hasPermission: () => hasPermission2,
198
+ requestPermission: () => requestPermission2,
199
+ watchPosition: () => watchPosition
200
+ });
201
+ import * as ExpoLocation from "expo-location";
202
+ async function requestPermission2() {
203
+ const { status: fg } = await ExpoLocation.requestForegroundPermissionsAsync();
204
+ if (fg !== "granted") return false;
205
205
  try {
206
- const available = await SMS.isAvailableAsync();
207
- if (!available) {
208
- return {
209
- id: actionId,
210
- ok: false,
211
- error: "SMS not available on this device",
212
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
213
- };
214
- }
215
- const { result } = await SMS.sendSMSAsync([phone], message);
216
- return {
217
- id: actionId,
218
- ok: result === "sent" || result === "unknown",
219
- // 'unknown' on Android means it was sent
220
- result: {
221
- to: params.contact_name || phone,
222
- phone,
223
- message,
224
- status: result,
225
- sent_at: (/* @__PURE__ */ new Date()).toISOString()
226
- },
227
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
228
- };
229
- } catch (error) {
230
- return {
231
- id: actionId,
232
- ok: false,
233
- error: `SMS send failed: ${error instanceof Error ? error.message : String(error)}`,
234
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
235
- };
206
+ await ExpoLocation.requestBackgroundPermissionsAsync();
207
+ } catch {
236
208
  }
209
+ return fg === "granted";
237
210
  }
238
-
239
- // src/device-bridge/actions/calendar-actions.ts
240
- import * as ExpoCalendar from "expo-calendar";
241
- async function getDefaultCalendarId() {
242
- const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
243
- const defaultCal = calendars.find((c) => c.isPrimary) || calendars[0];
244
- return defaultCal?.id || null;
211
+ async function hasPermission2() {
212
+ const { status } = await ExpoLocation.getForegroundPermissionsAsync();
213
+ return status === "granted";
245
214
  }
246
- async function executeCreate(actionId, params) {
247
- const title = params.title;
248
- const date = params.date;
249
- const startTime = params.start_time;
250
- const endTime = params.end_time;
251
- if (!title || !date || !startTime) {
252
- return {
253
- id: actionId,
254
- ok: false,
255
- error: "Missing title, date, or start_time",
256
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
257
- };
258
- }
215
+ async function collectCurrent() {
216
+ const location = await ExpoLocation.getCurrentPositionAsync({
217
+ accuracy: ExpoLocation.Accuracy.Balanced
218
+ });
219
+ let place = "Current Location";
220
+ let address = `${location.coords.latitude.toFixed(4)}, ${location.coords.longitude.toFixed(4)}`;
221
+ let locationType = "unknown";
259
222
  try {
260
- const calendarId = await getDefaultCalendarId();
261
- if (!calendarId) {
262
- return {
263
- id: actionId,
264
- ok: false,
265
- error: "No calendar found on device",
266
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
267
- };
268
- }
269
- const [year, month, day] = date.split("-").map(Number);
270
- const [startH, startM] = startTime.split(":").map(Number);
271
- const startDate = new Date(year, month - 1, day, startH, startM);
272
- let endDate;
273
- if (endTime) {
274
- const [endH, endM] = endTime.split(":").map(Number);
275
- endDate = new Date(year, month - 1, day, endH, endM);
276
- } else {
277
- endDate = new Date(startDate.getTime() + 60 * 60 * 1e3);
278
- }
279
- const eventId = await ExpoCalendar.createEventAsync(calendarId, {
280
- title,
281
- startDate,
282
- endDate,
283
- location: params.location || void 0,
284
- notes: params.notes || void 0,
285
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
223
+ const geocoded = await reverseGeocode({
224
+ latitude: location.coords.latitude,
225
+ longitude: location.coords.longitude
286
226
  });
287
- return {
288
- id: actionId,
289
- ok: true,
290
- result: {
291
- event_id: eventId,
292
- title,
293
- date,
294
- start: startTime,
295
- end: endTime || formatTime(endDate),
296
- message: `Event "${title}" created successfully`
297
- },
298
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
299
- };
300
- } catch (error) {
301
- return {
302
- id: actionId,
303
- ok: false,
304
- error: `Calendar create failed: ${error instanceof Error ? error.message : String(error)}`,
305
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
306
- };
307
- }
308
- }
309
- async function executeReschedule(actionId, params) {
310
- const eventId = params.event_id;
311
- if (!eventId) {
312
- return {
313
- id: actionId,
314
- ok: false,
315
- error: "Missing event_id",
316
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
317
- };
318
- }
319
- try {
320
- const updates = {};
321
- if (params.new_date && params.new_start_time) {
322
- const [year, month, day] = params.new_date.split("-").map(Number);
323
- const [h, m] = params.new_start_time.split(":").map(Number);
324
- updates.startDate = new Date(year, month - 1, day, h, m);
325
- if (params.new_end_time) {
326
- const [eh, em] = params.new_end_time.split(":").map(Number);
327
- updates.endDate = new Date(year, month - 1, day, eh, em);
328
- } else {
329
- updates.endDate = new Date(updates.startDate.getTime() + 60 * 60 * 1e3);
330
- }
227
+ if (geocoded) {
228
+ place = geocoded.name || geocoded.street || place;
229
+ address = [
230
+ geocoded.streetNumber,
231
+ geocoded.street,
232
+ geocoded.city,
233
+ geocoded.region
234
+ ].filter(Boolean).join(", ") || address;
235
+ locationType = classifyLocation(geocoded);
331
236
  }
332
- await ExpoCalendar.updateEventAsync(eventId, updates);
333
- return {
334
- id: actionId,
335
- ok: true,
336
- result: { event_id: eventId, message: "Event rescheduled successfully" },
337
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
338
- };
339
- } catch (error) {
340
- return {
341
- id: actionId,
342
- ok: false,
343
- error: `Calendar reschedule failed: ${error instanceof Error ? error.message : String(error)}`,
344
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
345
- };
237
+ } catch {
346
238
  }
239
+ return {
240
+ ok: true,
241
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
242
+ location: {
243
+ place,
244
+ address,
245
+ lat: location.coords.latitude,
246
+ lng: location.coords.longitude,
247
+ type: locationType
248
+ }
249
+ };
347
250
  }
348
- async function executeCancel(actionId, params) {
349
- const eventId = params.event_id;
350
- if (!eventId) {
351
- return {
352
- id: actionId,
353
- ok: false,
354
- error: "Missing event_id",
355
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
356
- };
357
- }
358
- try {
359
- await ExpoCalendar.deleteEventAsync(eventId);
360
- return {
361
- id: actionId,
362
- ok: true,
363
- result: { event_id: eventId, message: "Event cancelled successfully" },
364
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
365
- };
366
- } catch (error) {
367
- return {
368
- id: actionId,
369
- ok: false,
370
- error: `Calendar cancel failed: ${error instanceof Error ? error.message : String(error)}`,
371
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
372
- };
251
+ async function ensurePermission() {
252
+ if (await hasPermission2()) return;
253
+ const granted = await requestPermission2();
254
+ if (!granted) {
255
+ throw new Error(
256
+ "Location permission was not granted. Enable it for this app in iOS Settings > Privacy > Location Services."
257
+ );
373
258
  }
374
259
  }
375
- function formatTime(date) {
376
- return date.toLocaleTimeString("en-US", {
377
- hour: "2-digit",
378
- minute: "2-digit",
379
- hour12: false
260
+ async function getPosition(opts = {}) {
261
+ await ensurePermission();
262
+ const fix = await ExpoLocation.getCurrentPositionAsync({
263
+ accuracy: opts.accuracy === "high" ? ExpoLocation.Accuracy.High : ExpoLocation.Accuracy.Balanced
380
264
  });
265
+ return toPosition(fix);
381
266
  }
382
-
383
- // src/device-bridge/actions/dnd.ts
384
- var currentDndState = {
385
- enabled: false,
386
- schedule: { start: "22:00", end: "07:00" },
387
- allowlist: []
388
- };
389
- async function execute2(actionId, params) {
390
- try {
391
- if (params.enabled !== void 0) {
392
- currentDndState.enabled = params.enabled;
393
- }
394
- if (params.schedule_start) {
395
- currentDndState.schedule.start = params.schedule_start;
396
- }
397
- if (params.schedule_end) {
398
- currentDndState.schedule.end = params.schedule_end;
399
- }
400
- if (params.allowlist) {
401
- currentDndState.allowlist = params.allowlist;
402
- }
403
- return {
404
- id: actionId,
405
- ok: true,
406
- result: {
407
- message: `DND ${currentDndState.enabled ? "enabled" : "disabled"}`,
408
- dnd: currentDndState
409
- },
410
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
411
- };
412
- } catch (error) {
413
- return {
414
- id: actionId,
415
- ok: false,
416
- error: `DND set failed: ${error instanceof Error ? error.message : String(error)}`,
417
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
418
- };
419
- }
267
+ function toPosition(fix) {
268
+ return {
269
+ latitude: fix.coords.latitude,
270
+ longitude: fix.coords.longitude,
271
+ altitude: fix.coords.altitude ?? void 0,
272
+ accuracy: nonNegative(fix.coords.accuracy),
273
+ timestamp: fix.timestamp ?? Date.now(),
274
+ speed: nonNegative(fix.coords.speed),
275
+ course: nonNegative(fix.coords.heading)
276
+ };
420
277
  }
421
-
422
- // src/device-bridge/actions/navigation.ts
423
- import * as Linking from "expo-linking";
424
- import { Platform } from "react-native";
425
- async function execute3(actionId, params) {
426
- const destination = params.destination;
427
- const mode = params.mode || "driving";
428
- if (!destination) {
429
- return {
430
- id: actionId,
431
- ok: false,
432
- error: "Missing destination parameter",
433
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
434
- };
435
- }
436
- try {
437
- const encodedDest = encodeURIComponent(destination);
438
- let url;
439
- if (Platform.OS === "ios") {
440
- const modeMap = {
441
- driving: "d",
442
- transit: "r",
443
- walking: "w"
444
- };
445
- url = `maps://maps.apple.com/?daddr=${encodedDest}&dirflg=${modeMap[mode] || "d"}`;
446
- } else {
447
- const modeMap = {
448
- driving: "driving",
449
- transit: "transit",
450
- walking: "walking"
451
- };
452
- url = `google.navigation:q=${encodedDest}&mode=${modeMap[mode] || "driving"}`;
453
- }
454
- const canOpen = await Linking.canOpenURL(url);
455
- if (!canOpen) {
456
- const webModeMap = {
457
- driving: "driving",
458
- transit: "transit",
459
- walking: "walking"
460
- };
461
- url = `https://www.google.com/maps/dir/?api=1&destination=${encodedDest}&travelmode=${webModeMap[mode] || "driving"}`;
462
- }
463
- await Linking.openURL(url);
464
- return {
465
- id: actionId,
466
- ok: true,
467
- result: {
468
- message: `Navigation started to ${destination} via ${mode}`,
469
- destination,
470
- mode,
471
- status: "navigating"
472
- },
473
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
474
- };
475
- } catch (error) {
476
- return {
477
- id: actionId,
478
- ok: false,
479
- error: `Navigation failed: ${error instanceof Error ? error.message : String(error)}`,
480
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
481
- };
482
- }
278
+ function nonNegative(v) {
279
+ return typeof v === "number" && v >= 0 ? v : void 0;
483
280
  }
484
-
485
- // src/device-bridge/actions/health-actions.ts
486
- async function executeToday(actionId) {
487
- try {
488
- const today = await collectToday();
489
- return {
490
- id: actionId,
491
- ok: true,
492
- result: { today },
493
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
494
- };
495
- } catch (e) {
496
- return {
497
- id: actionId,
498
- ok: false,
499
- error: String(e?.message || e),
500
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
501
- };
502
- }
281
+ var WATCH_TIER = {
282
+ navigation: ExpoLocation.Accuracy.BestForNavigation,
283
+ high: ExpoLocation.Accuracy.High,
284
+ balanced: ExpoLocation.Accuracy.Balanced,
285
+ low: ExpoLocation.Accuracy.Low
286
+ };
287
+ function watchPosition(cb, opts) {
288
+ let subscription = null;
289
+ let cancelled = false;
290
+ (async () => {
291
+ try {
292
+ await ensurePermission();
293
+ if (cancelled) return;
294
+ subscription = await ExpoLocation.watchPositionAsync(
295
+ { accuracy: WATCH_TIER[opts?.accuracy ?? "balanced"] },
296
+ (fix) => cb(toPosition(fix))
297
+ );
298
+ if (cancelled) {
299
+ subscription?.remove();
300
+ subscription = null;
301
+ }
302
+ } catch (err) {
303
+ console.warn("[location] watchPosition failed:", err?.message || String(err));
304
+ }
305
+ })();
306
+ return () => {
307
+ cancelled = true;
308
+ subscription?.remove();
309
+ subscription = null;
310
+ };
503
311
  }
504
- async function executeHistory(actionId, params) {
505
- try {
506
- const days = Number(params?.days) || 30;
507
- const list = await collectHistory(days);
508
- return {
509
- id: actionId,
510
- ok: true,
511
- result: { days: list },
512
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
513
- };
514
- } catch (e) {
515
- return {
516
- id: actionId,
517
- ok: false,
518
- error: String(e?.message || e),
519
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
520
- };
521
- }
312
+ function classifyLocation(geocoded) {
313
+ const name = (geocoded.name || "").toLowerCase();
314
+ const street = (geocoded.street || "").toLowerCase();
315
+ if (name.includes("home") || name.includes("residence")) return "home";
316
+ if (name.includes("office") || name.includes("workplace") || name.includes("tech park")) return "work";
317
+ if (name.includes("school") || name.includes("elementary") || name.includes("university")) return "school";
318
+ if (name.includes("gym") || name.includes("fitness")) return "fitness";
319
+ if (name.includes("hospital") || name.includes("clinic") || name.includes("dental")) return "medical";
320
+ return "other";
522
321
  }
523
322
 
524
- // src/device-bridge/actions/queue-poller.ts
525
- var QueuePoller = class {
526
- constructor(config) {
527
- this.timer = null;
528
- this.running = false;
529
- this.relayClient = config.relayClient;
530
- this.pollInterval = config.pollInterval ?? 5e3;
531
- this.onActionExecuted = config.onActionExecuted;
532
- this.onError = config.onError;
533
- }
534
- /**
535
- * Start polling for actions.
536
- */
537
- start() {
538
- if (this.running) return;
539
- this.running = true;
540
- this.poll();
541
- this.timer = setInterval(() => this.poll(), this.pollInterval);
542
- }
543
- /**
544
- * Stop polling.
545
- */
546
- stop() {
547
- this.running = false;
548
- if (this.timer) {
549
- clearInterval(this.timer);
550
- this.timer = null;
551
- }
552
- }
553
- /**
554
- * Check if poller is running.
555
- */
556
- isRunning() {
557
- return this.running;
558
- }
559
- /**
560
- * Execute a single poll cycle.
561
- */
562
- async poll() {
563
- if (!this.running) return;
323
+ // src/device-bridge/collectors/connectivity.ts
324
+ var connectivity_exports = {};
325
+ __export(connectivity_exports, {
326
+ collectConnectivity: () => collectConnectivity
327
+ });
328
+ import * as ExpoNetwork from "expo-network";
329
+ async function collectConnectivity() {
330
+ const networkState = await ExpoNetwork.getNetworkStateAsync();
331
+ let type = "none";
332
+ let wifiName;
333
+ let cellularType;
334
+ if (networkState.type === ExpoNetwork.NetworkStateType.WIFI) {
335
+ type = "wifi";
564
336
  try {
565
- const response = await this.relayClient.pollActions();
566
- if (!response.ok || !response.data) return;
567
- for (const action of response.data) {
568
- if (action.status && action.status !== "pending") continue;
569
- const result = await this.executeAction(action);
570
- await this.relayClient.completeAction(result);
571
- this.onActionExecuted?.(result);
572
- }
573
- } catch (error) {
574
- this.onError?.(
575
- error instanceof Error ? error : new Error(String(error))
576
- );
337
+ wifiName = await getWifiName();
338
+ } catch {
339
+ wifiName = void 0;
577
340
  }
341
+ } else if (networkState.type === ExpoNetwork.NetworkStateType.CELLULAR) {
342
+ type = "cellular";
343
+ cellularType = getCellularGeneration();
578
344
  }
579
- /**
580
- * Route an action to the appropriate executor.
581
- */
582
- async executeAction(action) {
583
- const { id, type, params } = action;
584
- try {
585
- switch (type) {
586
- case "messaging.send":
587
- return await execute(id, params);
588
- case "calendar.create":
589
- return await executeCreate(id, params);
590
- case "calendar.reschedule":
591
- return await executeReschedule(id, params);
592
- case "calendar.cancel":
593
- return await executeCancel(id, params);
594
- case "dnd.set":
595
- return await execute2(id, params);
596
- case "navigation.start":
597
- return await execute3(id, params);
598
- case "health.today":
599
- return await executeToday(id);
600
- case "health.history":
601
- return await executeHistory(id, params);
602
- default:
603
- return {
604
- id,
605
- ok: false,
606
- error: `Unknown action type: ${type}`,
607
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
608
- };
609
- }
610
- } catch (error) {
611
- return {
612
- id,
613
- ok: false,
614
- error: `Action execution failed: ${error instanceof Error ? error.message : String(error)}`,
615
- executed_at: (/* @__PURE__ */ new Date()).toISOString()
616
- };
345
+ return {
346
+ ok: true,
347
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
348
+ connectivity: {
349
+ type,
350
+ is_connected: networkState.isConnected ?? false,
351
+ ...wifiName && { wifi_name: wifiName },
352
+ ...cellularType && { cellular_type: cellularType }
617
353
  }
618
- }
619
- };
620
-
621
- // src/device-bridge/background.ts
622
- import * as TaskManager from "expo-task-manager";
623
- import * as BackgroundFetch from "expo-background-fetch";
354
+ };
355
+ }
356
+ async function getWifiName() {
357
+ return void 0;
358
+ }
359
+ function getCellularGeneration() {
360
+ return "LTE";
361
+ }
624
362
 
625
363
  // src/device-bridge/capabilities.ts
626
364
  var capabilities_exports = {};
@@ -638,29 +376,29 @@ __export(capabilities_exports, {
638
376
  });
639
377
 
640
378
  // src/device-bridge/collectors/calendar.ts
641
- import * as ExpoCalendar2 from "expo-calendar";
642
- async function requestPermission2() {
643
- const { status } = await ExpoCalendar2.requestCalendarPermissionsAsync();
379
+ import * as ExpoCalendar from "expo-calendar";
380
+ async function requestPermission3() {
381
+ const { status } = await ExpoCalendar.requestCalendarPermissionsAsync();
644
382
  return status === "granted";
645
383
  }
646
- async function hasPermission2() {
647
- const { status } = await ExpoCalendar2.getCalendarPermissionsAsync();
384
+ async function hasPermission3() {
385
+ const { status } = await ExpoCalendar.getCalendarPermissionsAsync();
648
386
  return status === "granted";
649
387
  }
650
388
  async function collectToday2() {
651
389
  const now = /* @__PURE__ */ new Date();
652
390
  const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
653
391
  const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);
654
- const calendars = await ExpoCalendar2.getCalendarsAsync(ExpoCalendar2.EntityTypes.EVENT);
392
+ const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
655
393
  const userCalendarIds = getUserCalendarIds(calendars);
656
- const events = await ExpoCalendar2.getEventsAsync(userCalendarIds, startOfDay, endOfDay);
394
+ const events = await ExpoCalendar.getEventsAsync(userCalendarIds, startOfDay, endOfDay);
657
395
  const filtered = filterHolidays(events);
658
396
  const mapped = filtered.map((e) => ({
659
397
  id: e.id,
660
398
  title: e.title || "Untitled",
661
399
  date: startOfDay.toISOString().slice(0, 10),
662
- start: formatTime2(new Date(e.startDate)),
663
- end: formatTime2(new Date(e.endDate)),
400
+ start: formatTime(new Date(e.startDate)),
401
+ end: formatTime(new Date(e.endDate)),
664
402
  location: e.location || "",
665
403
  attendees: (e.attendees || []).map((a) => a.name || a.email || "Unknown"),
666
404
  notes: e.notes || ""
@@ -678,9 +416,9 @@ async function collectUpcoming(daysAhead = 3) {
678
416
  const now = /* @__PURE__ */ new Date();
679
417
  const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
680
418
  const endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + daysAhead + 1);
681
- const calendars = await ExpoCalendar2.getCalendarsAsync(ExpoCalendar2.EntityTypes.EVENT);
419
+ const calendars = await ExpoCalendar.getCalendarsAsync(ExpoCalendar.EntityTypes.EVENT);
682
420
  const userCalendarIds = getUserCalendarIds(calendars);
683
- const events = await ExpoCalendar2.getEventsAsync(userCalendarIds, tomorrow, endDate);
421
+ const events = await ExpoCalendar.getEventsAsync(userCalendarIds, tomorrow, endDate);
684
422
  const filtered = filterHolidays(events);
685
423
  const mapped = filtered.map((e) => {
686
424
  const startDt = new Date(e.startDate);
@@ -688,8 +426,8 @@ async function collectUpcoming(daysAhead = 3) {
688
426
  id: e.id,
689
427
  title: e.title || "Untitled",
690
428
  date: startDt.toISOString().slice(0, 10),
691
- start: formatTime2(startDt),
692
- end: formatTime2(new Date(e.endDate)),
429
+ start: formatTime(startDt),
430
+ end: formatTime(new Date(e.endDate)),
693
431
  location: e.location || "",
694
432
  attendees: (e.attendees || []).map((a) => a.name || a.email || "Unknown"),
695
433
  notes: e.notes || ""
@@ -732,7 +470,7 @@ function filterHolidays(events) {
732
470
  return true;
733
471
  });
734
472
  }
735
- function formatTime2(date) {
473
+ function formatTime(date) {
736
474
  return date.toLocaleTimeString("en-US", {
737
475
  hour: "2-digit",
738
476
  minute: "2-digit",
@@ -744,11 +482,11 @@ function formatTime2(date) {
744
482
  import * as ExpoContacts from "expo-contacts";
745
483
  import AsyncStorage from "@react-native-async-storage/async-storage";
746
484
  var HASH_STORE_KEY = "device-bridge.contacts.hashes.v1";
747
- async function requestPermission3() {
485
+ async function requestPermission4() {
748
486
  const { status } = await ExpoContacts.requestPermissionsAsync();
749
487
  return status === "granted";
750
488
  }
751
- async function hasPermission3() {
489
+ async function hasPermission4() {
752
490
  const { status } = await ExpoContacts.getPermissionsAsync();
753
491
  return status === "granted";
754
492
  }
@@ -814,169 +552,38 @@ async function collectAll() {
814
552
  if (!fullSync && added === 0 && updated === 0 && removed === 0) {
815
553
  return null;
816
554
  }
817
- try {
818
- await AsyncStorage.setItem(HASH_STORE_KEY, JSON.stringify(currentHashes));
819
- } catch {
820
- }
821
- return contacts;
822
- }
823
- function fnv1a(str) {
824
- let h = 2166136261;
825
- for (let i = 0; i < str.length; i++) {
826
- h ^= str.charCodeAt(i);
827
- h = Math.imul(h, 16777619);
828
- }
829
- return (h >>> 0).toString(16).padStart(8, "0");
830
- }
831
- function hashContact(c) {
832
- const parts = [
833
- c.id,
834
- (c.name || "").trim(),
835
- c.phone || "\0",
836
- (c.email || "\0").toLowerCase(),
837
- c.birthday || "\0"
838
- ];
839
- return fnv1a(parts.join(""));
840
- }
841
- function formatBirthday(birthday) {
842
- if (!birthday) return null;
843
- const { year, month, day } = birthday;
844
- if (month === void 0 || day === void 0) return null;
845
- const m = String(month + 1).padStart(2, "0");
846
- const d = String(day).padStart(2, "0");
847
- if (!year) return `--${m}-${d}`;
848
- return `${year}-${m}-${d}`;
849
- }
850
-
851
- // src/device-bridge/collectors/location.ts
852
- var location_exports = {};
853
- __export(location_exports, {
854
- collectCurrent: () => collectCurrent,
855
- getPosition: () => getPosition,
856
- hasPermission: () => hasPermission4,
857
- requestPermission: () => requestPermission4,
858
- watchPosition: () => watchPosition
859
- });
860
- import * as ExpoLocation from "expo-location";
861
- async function requestPermission4() {
862
- const { status: fg } = await ExpoLocation.requestForegroundPermissionsAsync();
863
- if (fg !== "granted") return false;
864
- try {
865
- await ExpoLocation.requestBackgroundPermissionsAsync();
866
- } catch {
867
- }
868
- return fg === "granted";
869
- }
870
- async function hasPermission4() {
871
- const { status } = await ExpoLocation.getForegroundPermissionsAsync();
872
- return status === "granted";
873
- }
874
- async function collectCurrent() {
875
- const location = await ExpoLocation.getCurrentPositionAsync({
876
- accuracy: ExpoLocation.Accuracy.Balanced
877
- });
878
- let place = "Current Location";
879
- let address = `${location.coords.latitude.toFixed(4)}, ${location.coords.longitude.toFixed(4)}`;
880
- let locationType = "unknown";
881
- try {
882
- const geocoded = await reverseGeocode({
883
- latitude: location.coords.latitude,
884
- longitude: location.coords.longitude
885
- });
886
- if (geocoded) {
887
- place = geocoded.name || geocoded.street || place;
888
- address = [
889
- geocoded.streetNumber,
890
- geocoded.street,
891
- geocoded.city,
892
- geocoded.region
893
- ].filter(Boolean).join(", ") || address;
894
- locationType = classifyLocation(geocoded);
895
- }
555
+ try {
556
+ await AsyncStorage.setItem(HASH_STORE_KEY, JSON.stringify(currentHashes));
896
557
  } catch {
897
558
  }
898
- return {
899
- ok: true,
900
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
901
- location: {
902
- place,
903
- address,
904
- lat: location.coords.latitude,
905
- lng: location.coords.longitude,
906
- type: locationType
907
- }
908
- };
559
+ return contacts;
909
560
  }
910
- async function ensurePermission() {
911
- if (await hasPermission4()) return;
912
- const granted = await requestPermission4();
913
- if (!granted) {
914
- throw new Error(
915
- "Location permission was not granted. Enable it for this app in iOS Settings > Privacy > Location Services."
916
- );
561
+ function fnv1a(str) {
562
+ let h = 2166136261;
563
+ for (let i = 0; i < str.length; i++) {
564
+ h ^= str.charCodeAt(i);
565
+ h = Math.imul(h, 16777619);
917
566
  }
567
+ return (h >>> 0).toString(16).padStart(8, "0");
918
568
  }
919
- async function getPosition(opts = {}) {
920
- await ensurePermission();
921
- const fix = await ExpoLocation.getCurrentPositionAsync({
922
- accuracy: opts.accuracy === "high" ? ExpoLocation.Accuracy.High : ExpoLocation.Accuracy.Balanced
923
- });
924
- return toPosition(fix);
925
- }
926
- function toPosition(fix) {
927
- return {
928
- latitude: fix.coords.latitude,
929
- longitude: fix.coords.longitude,
930
- altitude: fix.coords.altitude ?? void 0,
931
- accuracy: nonNegative(fix.coords.accuracy),
932
- timestamp: fix.timestamp ?? Date.now(),
933
- speed: nonNegative(fix.coords.speed),
934
- course: nonNegative(fix.coords.heading)
935
- };
936
- }
937
- function nonNegative(v) {
938
- return typeof v === "number" && v >= 0 ? v : void 0;
939
- }
940
- var WATCH_TIER = {
941
- navigation: ExpoLocation.Accuracy.BestForNavigation,
942
- high: ExpoLocation.Accuracy.High,
943
- balanced: ExpoLocation.Accuracy.Balanced,
944
- low: ExpoLocation.Accuracy.Low
945
- };
946
- function watchPosition(cb, opts) {
947
- let subscription = null;
948
- let cancelled = false;
949
- (async () => {
950
- try {
951
- await ensurePermission();
952
- if (cancelled) return;
953
- subscription = await ExpoLocation.watchPositionAsync(
954
- { accuracy: WATCH_TIER[opts?.accuracy ?? "balanced"] },
955
- (fix) => cb(toPosition(fix))
956
- );
957
- if (cancelled) {
958
- subscription?.remove();
959
- subscription = null;
960
- }
961
- } catch (err) {
962
- console.warn("[location] watchPosition failed:", err?.message || String(err));
963
- }
964
- })();
965
- return () => {
966
- cancelled = true;
967
- subscription?.remove();
968
- subscription = null;
969
- };
569
+ function hashContact(c) {
570
+ const parts = [
571
+ c.id,
572
+ (c.name || "").trim(),
573
+ c.phone || "\0",
574
+ (c.email || "\0").toLowerCase(),
575
+ c.birthday || "\0"
576
+ ];
577
+ return fnv1a(parts.join(""));
970
578
  }
971
- function classifyLocation(geocoded) {
972
- const name = (geocoded.name || "").toLowerCase();
973
- const street = (geocoded.street || "").toLowerCase();
974
- if (name.includes("home") || name.includes("residence")) return "home";
975
- if (name.includes("office") || name.includes("workplace") || name.includes("tech park")) return "work";
976
- if (name.includes("school") || name.includes("elementary") || name.includes("university")) return "school";
977
- if (name.includes("gym") || name.includes("fitness")) return "fitness";
978
- if (name.includes("hospital") || name.includes("clinic") || name.includes("dental")) return "medical";
979
- return "other";
579
+ function formatBirthday(birthday) {
580
+ if (!birthday) return null;
581
+ const { year, month, day } = birthday;
582
+ if (month === void 0 || day === void 0) return null;
583
+ const m = String(month + 1).padStart(2, "0");
584
+ const d = String(day).padStart(2, "0");
585
+ if (!year) return `--${m}-${d}`;
586
+ return `${year}-${m}-${d}`;
980
587
  }
981
588
 
982
589
  // src/device-bridge/collectors/battery.ts
@@ -1025,46 +632,6 @@ function mapBatteryState(state) {
1025
632
  }
1026
633
  }
1027
634
 
1028
- // src/device-bridge/collectors/connectivity.ts
1029
- var connectivity_exports = {};
1030
- __export(connectivity_exports, {
1031
- collectConnectivity: () => collectConnectivity
1032
- });
1033
- import * as ExpoNetwork from "expo-network";
1034
- async function collectConnectivity() {
1035
- const networkState = await ExpoNetwork.getNetworkStateAsync();
1036
- let type = "none";
1037
- let wifiName;
1038
- let cellularType;
1039
- if (networkState.type === ExpoNetwork.NetworkStateType.WIFI) {
1040
- type = "wifi";
1041
- try {
1042
- wifiName = await getWifiName();
1043
- } catch {
1044
- wifiName = void 0;
1045
- }
1046
- } else if (networkState.type === ExpoNetwork.NetworkStateType.CELLULAR) {
1047
- type = "cellular";
1048
- cellularType = getCellularGeneration();
1049
- }
1050
- return {
1051
- ok: true,
1052
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1053
- connectivity: {
1054
- type,
1055
- is_connected: networkState.isConnected ?? false,
1056
- ...wifiName && { wifi_name: wifiName },
1057
- ...cellularType && { cellular_type: cellularType }
1058
- }
1059
- };
1060
- }
1061
- async function getWifiName() {
1062
- return void 0;
1063
- }
1064
- function getCellularGeneration() {
1065
- return "LTE";
1066
- }
1067
-
1068
635
  // src/device-bridge/collectors/notifications.ts
1069
636
  import * as ExpoNotifications from "expo-notifications";
1070
637
  async function requestPermission5() {
@@ -1186,8 +753,8 @@ function calendarCapability() {
1186
753
  icon: "calendar",
1187
754
  requiresPermission: true,
1188
755
  permissionState: "not_requested",
1189
- requestPermission: requestPermission2,
1190
- checkPermission: hasPermission2,
756
+ requestPermission: requestPermission3,
757
+ checkPermission: hasPermission3,
1191
758
  collect: async () => {
1192
759
  const [today, upcoming] = await Promise.all([
1193
760
  collectToday2(),
@@ -1208,8 +775,8 @@ function contactsCapability() {
1208
775
  icon: "people",
1209
776
  requiresPermission: true,
1210
777
  permissionState: "not_requested",
1211
- requestPermission: requestPermission3,
1212
- checkPermission: hasPermission3,
778
+ requestPermission: requestPermission4,
779
+ checkPermission: hasPermission4,
1213
780
  collect: async () => {
1214
781
  const list = await collectAll();
1215
782
  if (list === null) return null;
@@ -1228,8 +795,8 @@ function locationCapability() {
1228
795
  icon: "location",
1229
796
  requiresPermission: true,
1230
797
  permissionState: "not_requested",
1231
- requestPermission: requestPermission4,
1232
- checkPermission: hasPermission4,
798
+ requestPermission: requestPermission2,
799
+ checkPermission: hasPermission2,
1233
800
  collect: collectCurrent,
1234
801
  defaultInterval: 3e4
1235
802
  // 30 sec
@@ -1361,6 +928,8 @@ function allBuiltIn() {
1361
928
  }
1362
929
 
1363
930
  // src/device-bridge/background.ts
931
+ import * as TaskManager from "expo-task-manager";
932
+ import * as BackgroundFetch from "expo-background-fetch";
1364
933
  var BACKGROUND_TASK_NAME = "DEVICE_BRIDGE_BACKGROUND_SYNC";
1365
934
  var CAPABILITY_FACTORIES = {
1366
935
  calendar: calendarCapability,
@@ -1427,384 +996,14 @@ async function collectAndPush(relay, domain) {
1427
996
  return result.ok;
1428
997
  }
1429
998
 
1430
- // src/device-bridge/biometrics.ts
1431
- var biometrics_exports = {};
1432
- __export(biometrics_exports, {
1433
- authenticate: () => authenticate,
1434
- capabilities: () => capabilities
1435
- });
1436
- import * as LocalAuthentication from "expo-local-authentication";
1437
- async function capabilities() {
1438
- const [hasHardware, isEnrolled, types] = await Promise.all([
1439
- LocalAuthentication.hasHardwareAsync(),
1440
- LocalAuthentication.isEnrolledAsync(),
1441
- LocalAuthentication.supportedAuthenticationTypesAsync()
1442
- ]);
1443
- const supportedTypes = types.map((t) => {
1444
- if (t === LocalAuthentication.AuthenticationType.FINGERPRINT) return "fingerprint";
1445
- if (t === LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION) return "face";
1446
- if (t === LocalAuthentication.AuthenticationType.IRIS) return "iris";
1447
- return "none";
1448
- }).filter((t) => t !== "none");
1449
- return { hasHardware, isEnrolled, supportedTypes };
1450
- }
1451
- async function authenticate(opts) {
1452
- try {
1453
- const res = await LocalAuthentication.authenticateAsync({
1454
- promptMessage: opts.reason,
1455
- cancelLabel: opts.cancelLabel,
1456
- disableDeviceFallback: opts.fallbackToPasscode === false
1457
- });
1458
- if (res.success) return { success: true };
1459
- return { success: false, error: res.error || "unknown" };
1460
- } catch (e) {
1461
- return { success: false, error: String(e?.message || e) };
1462
- }
1463
- }
1464
-
1465
- // src/device-bridge/sharing.ts
1466
- var sharing_exports = {};
1467
- __export(sharing_exports, {
1468
- isAvailable: () => isAvailable,
1469
- shareFile: () => shareFile
1470
- });
1471
- import * as Sharing from "expo-sharing";
1472
- async function shareFile(fileUri, opts = {}) {
1473
- try {
1474
- const available = await Sharing.isAvailableAsync();
1475
- if (!available) return { ok: false, error: "sharing_unavailable" };
1476
- await Sharing.shareAsync(fileUri, opts);
1477
- return { ok: true };
1478
- } catch (e) {
1479
- return { ok: false, error: String(e?.message || e) };
1480
- }
1481
- }
1482
- async function isAvailable() {
1483
- return Sharing.isAvailableAsync();
1484
- }
1485
-
1486
- // src/device-bridge/scanner.ts
1487
- var scanner_exports = {};
1488
- __export(scanner_exports, {
1489
- CameraView: () => CameraView,
1490
- normalizeResult: () => normalizeResult,
1491
- requestPermissions: () => requestPermissions,
1492
- useCameraPermissions: () => useCameraPermissions
1493
- });
1494
- import { CameraView, useCameraPermissions } from "expo-camera";
1495
- async function requestPermissions() {
1496
- const { Camera: Camera2 } = await import("expo-camera");
1497
- const { status } = await Camera2.requestCameraPermissionsAsync();
1498
- return { granted: status === "granted", status };
1499
- }
1500
- function normalizeResult(r) {
1501
- return { type: r.type, data: r.data };
1502
- }
1503
-
1504
- // src/device-bridge/index.ts
1505
- var DeviceBridge = class {
1506
- constructor(config) {
1507
- // Capability registry
1508
- this.caps = /* @__PURE__ */ new Map();
1509
- this.syncTimers = /* @__PURE__ */ new Map();
1510
- this.subscriptions = /* @__PURE__ */ new Map();
1511
- this.domainStatus = /* @__PURE__ */ new Map();
1512
- this.running = false;
1513
- this.config = config;
1514
- this.relay = new RelayClient({
1515
- relayUrl: config.relayUrl,
1516
- deviceId: config.deviceId,
1517
- tenant: config.tenant,
1518
- actorId: config.actorId,
1519
- apiKey: config.apiKey
1520
- });
1521
- this.queuePoller = new QueuePoller({
1522
- relayClient: this.relay,
1523
- pollInterval: 5e3,
1524
- onActionExecuted: () => this.notifyStatusChange(),
1525
- onError: (error) => this.config.onError?.({
1526
- domain: "actions",
1527
- message: error.message,
1528
- code: "ACTION_ERROR",
1529
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1530
- })
1531
- });
1532
- }
1533
- // ─── Capability Registration ───
1534
- /**
1535
- * Register a capability. Does NOT request permission — just makes it known.
1536
- * Can be called before or after startSync().
1537
- */
1538
- register(cap) {
1539
- this.caps.set(cap.id, cap);
1540
- this.domainStatus.set(cap.id, {
1541
- domain: cap.id,
1542
- lastSync: null,
1543
- state: "idle"
1544
- });
1545
- }
1546
- /**
1547
- * Register multiple capabilities at once.
1548
- */
1549
- registerAll(caps) {
1550
- for (const cap of caps) {
1551
- this.register(cap);
1552
- }
1553
- }
1554
- /**
1555
- * Get a registered capability by ID.
1556
- * Use this to read name/description/reason for your permission UI.
1557
- */
1558
- getCapability(id) {
1559
- return this.caps.get(id);
1560
- }
1561
- /**
1562
- * Get all registered capabilities with their current status.
1563
- */
1564
- listCapabilities() {
1565
- return Array.from(this.caps.values()).map((cap) => ({
1566
- id: cap.id,
1567
- name: cap.name,
1568
- permission: cap.permissionState,
1569
- sync: this.domainStatus.get(cap.id) || {
1570
- domain: cap.id,
1571
- lastSync: null,
1572
- state: "idle"
1573
- }
1574
- }));
1575
- }
1576
- // ─── Permissions (host app controls the flow) ───
1577
- /**
1578
- * Request permission for ONE capability.
1579
- * Call after showing your own explanation screen.
1580
- * If bridge is already running and granted, syncing starts automatically.
1581
- */
1582
- async requestPermission(capId) {
1583
- const cap = this.caps.get(capId);
1584
- if (!cap) throw new Error(`Capability "${capId}" not registered`);
1585
- if (!cap.requiresPermission) {
1586
- cap.permissionState = "not_needed";
1587
- return true;
1588
- }
1589
- const granted = await cap.requestPermission();
1590
- cap.permissionState = granted ? "granted" : "denied";
1591
- if (granted && this.running) {
1592
- this.startCapabilitySync(cap);
1593
- }
1594
- this.notifyStatusChange();
1595
- return granted;
1596
- }
1597
- /**
1598
- * Check permission WITHOUT prompting.
1599
- */
1600
- async checkPermission(capId) {
1601
- const cap = this.caps.get(capId);
1602
- if (!cap) throw new Error(`Capability "${capId}" not registered`);
1603
- if (!cap.requiresPermission) return "not_needed";
1604
- const granted = await cap.checkPermission();
1605
- cap.permissionState = granted ? "granted" : cap.permissionState;
1606
- return cap.permissionState;
1607
- }
1608
- /**
1609
- * Request ALL permissions sequentially (one at a time, not bombarded).
1610
- */
1611
- async requestAllPermissions() {
1612
- const results = /* @__PURE__ */ new Map();
1613
- for (const [id, cap] of this.caps) {
1614
- if (!cap.requiresPermission) {
1615
- cap.permissionState = "not_needed";
1616
- results.set(id, true);
1617
- continue;
1618
- }
1619
- const granted = await cap.requestPermission();
1620
- cap.permissionState = granted ? "granted" : "denied";
1621
- results.set(id, granted);
1622
- }
1623
- this.notifyStatusChange();
1624
- return results;
1625
- }
1626
- /**
1627
- * Get capabilities that still need permission.
1628
- * Use this to drive a "next permission" onboarding flow.
1629
- */
1630
- getPendingPermissions() {
1631
- return Array.from(this.caps.values()).filter(
1632
- (cap) => cap.requiresPermission && cap.permissionState === "not_requested"
1633
- );
1634
- }
1635
- // ─── Sync Control ───
1636
- /**
1637
- * Start syncing all permitted capabilities + action queue poller.
1638
- */
1639
- async startSync() {
1640
- if (this.running) return;
1641
- this.running = true;
1642
- for (const [, cap] of this.caps) {
1643
- const permitted = !cap.requiresPermission || cap.permissionState === "granted" || cap.permissionState === "not_needed";
1644
- if (permitted) {
1645
- this.startCapabilitySync(cap);
1646
- } else {
1647
- this.updateDomainStatus(cap.id, "error", "Permission not granted");
1648
- }
1649
- }
1650
- this.queuePoller.start();
1651
- if (this.config.tenant && this.config.apiKey) {
1652
- registerBackgroundSync({
1653
- relayUrl: this.config.relayUrl,
1654
- deviceId: this.config.deviceId,
1655
- tenant: this.config.tenant,
1656
- actorId: this.config.actorId,
1657
- apiKey: this.config.apiKey
1658
- }).catch((e) => console.warn("[adas/bridge] registerBackgroundSync failed:", e?.message || e));
1659
- }
1660
- this.notifyStatusChange();
1661
- }
1662
- /**
1663
- * Stop all syncing.
1664
- */
1665
- stopSync() {
1666
- this.running = false;
1667
- for (const [id, timer] of this.syncTimers) {
1668
- clearInterval(timer);
1669
- this.updateDomainStatus(id, "stopped");
1670
- }
1671
- this.syncTimers.clear();
1672
- for (const unsub of this.subscriptions.values()) {
1673
- try {
1674
- unsub();
1675
- } catch {
1676
- }
1677
- }
1678
- this.subscriptions.clear();
1679
- this.queuePoller.stop();
1680
- unregisterBackgroundSync().catch((e) => console.warn("[adas/bridge] unregisterBackgroundSync failed:", e?.message || e));
1681
- this.notifyStatusChange();
1682
- }
1683
- /**
1684
- * Force immediate sync of all permitted capabilities.
1685
- */
1686
- async syncNow() {
1687
- const promises = Array.from(this.caps.values()).filter((c) => c.permissionState === "granted" || c.permissionState === "not_needed").map((c) => this.syncCapability(c));
1688
- await Promise.allSettled(promises);
1689
- }
1690
- /**
1691
- * Force immediate sync of a single capability by id. Used by the host to
1692
- * refresh fast-moving sources (battery) on app-foreground so the relay
1693
- * gets a fresh reading the moment the user opens the app.
1694
- */
1695
- async syncCapabilityNow(capId) {
1696
- const cap = this.caps.get(capId);
1697
- if (!cap) return;
1698
- const permitted = !cap.requiresPermission || cap.permissionState === "granted" || cap.permissionState === "not_needed";
1699
- if (!permitted) return;
1700
- await this.syncCapability(cap);
1701
- }
1702
- /**
1703
- * Get current bridge status.
1704
- */
1705
- getStatus() {
1706
- return {
1707
- running: this.running,
1708
- capabilities: this.listCapabilities(),
1709
- lastFullSync: this.getLastFullSync(),
1710
- pendingActions: this.relay.getOfflineQueueSize()
1711
- };
1712
- }
1713
- /**
1714
- * Check if the relay is reachable.
1715
- */
1716
- async checkConnectivity() {
1717
- return this.relay.ping();
1718
- }
1719
- // ─── Internal ───
1720
- startCapabilitySync(cap) {
1721
- if (this.syncTimers.has(cap.id)) return;
1722
- const interval = this.config.syncIntervals?.[cap.id] ?? cap.defaultInterval;
1723
- this.syncCapability(cap);
1724
- const timer = setInterval(() => this.syncCapability(cap), interval);
1725
- this.syncTimers.set(cap.id, timer);
1726
- if (cap.subscribe && !this.subscriptions.has(cap.id)) {
1727
- try {
1728
- const unsub = cap.subscribe(() => {
1729
- if (this.running) this.syncCapability(cap);
1730
- });
1731
- this.subscriptions.set(cap.id, unsub);
1732
- } catch (err) {
1733
- const message = err instanceof Error ? err.message : String(err);
1734
- this.config.onError?.({
1735
- domain: cap.id,
1736
- message: `subscribe failed: ${message}`,
1737
- code: "SUBSCRIBE_ERROR",
1738
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1739
- });
1740
- }
1741
- }
1742
- }
1743
- async syncCapability(cap) {
1744
- if (!this.running) return;
1745
- this.updateDomainStatus(cap.id, "syncing");
1746
- try {
1747
- const data = await cap.collect();
1748
- if (data === null) {
1749
- this.updateDomainStatus(cap.id, "idle");
1750
- return;
1751
- }
1752
- const result = await this.relay.pushDomain(cap.id, data);
1753
- if (result.ok) {
1754
- this.updateDomainStatus(cap.id, "idle");
1755
- } else {
1756
- this.updateDomainStatus(cap.id, "error", result.error);
1757
- this.config.onError?.({
1758
- domain: cap.id,
1759
- message: result.error || "Push failed",
1760
- code: "RELAY_ERROR",
1761
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1762
- });
1763
- }
1764
- } catch (error) {
1765
- const message = error instanceof Error ? error.message : String(error);
1766
- this.updateDomainStatus(cap.id, "error", message);
1767
- this.config.onError?.({
1768
- domain: cap.id,
1769
- message,
1770
- code: "COLLECTOR_ERROR",
1771
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1772
- });
1773
- }
1774
- }
1775
- updateDomainStatus(id, state, error) {
1776
- const current = this.domainStatus.get(id);
1777
- this.domainStatus.set(id, {
1778
- domain: id,
1779
- lastSync: state === "idle" ? (/* @__PURE__ */ new Date()).toISOString() : current?.lastSync ?? null,
1780
- state,
1781
- error,
1782
- itemCount: current?.itemCount
1783
- });
1784
- }
1785
- getLastFullSync() {
1786
- const syncs = Array.from(this.domainStatus.values()).map((d) => d.lastSync).filter((s) => s !== null);
1787
- if (syncs.length === 0) return null;
1788
- return syncs.sort()[0];
1789
- }
1790
- notifyStatusChange() {
1791
- this.config.onStatusChange?.(this.getStatus());
1792
- }
1793
- };
1794
-
1795
999
  export {
1796
1000
  RelayClient,
1797
- QueuePoller,
1798
1001
  location_exports,
1799
1002
  connectivity_exports,
1800
1003
  capabilities_exports,
1801
1004
  BACKGROUND_TASK_NAME,
1802
1005
  registerBackgroundSync,
1803
1006
  unregisterBackgroundSync,
1804
- syncDomains,
1805
- biometrics_exports,
1806
- sharing_exports,
1807
- scanner_exports,
1808
- DeviceBridge
1007
+ syncDomains
1809
1008
  };
1810
- //# sourceMappingURL=chunk-Z5AI2SXX.mjs.map
1009
+ //# sourceMappingURL=chunk-C62NQA4F.mjs.map