@ineerajrajeev/aios-web-sdk 0.1.1

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/index.cjs ADDED
@@ -0,0 +1,732 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AIOSClient: () => AIOSClient,
24
+ DEFAULT_PAGE_POLICY: () => DEFAULT_PAGE_POLICY,
25
+ DEFAULT_UNINTEGRATED_POLICY: () => DEFAULT_UNINTEGRATED_POLICY,
26
+ EXTENSION_MESSAGE_SOURCE: () => EXTENSION_MESSAGE_SOURCE,
27
+ ExtensionEventBus: () => ExtensionEventBus,
28
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
29
+ aiOS: () => aiOS,
30
+ createDefaultPageContext: () => createDefaultPageContext
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/types.ts
35
+ var PROTOCOL_VERSION = "1";
36
+
37
+ // src/bridge.ts
38
+ function postToExtension(appId, type, payload) {
39
+ if (typeof window === "undefined") return;
40
+ const message = {
41
+ source: "aios-sdk",
42
+ type,
43
+ appId,
44
+ version: PROTOCOL_VERSION,
45
+ timestamp: Date.now(),
46
+ payload
47
+ };
48
+ window.postMessage(message, window.location.origin);
49
+ }
50
+ function syncWindowGlobals(state) {
51
+ if (typeof window === "undefined") return;
52
+ window.__aiOS_sdk__ = {
53
+ version: state.version,
54
+ appId: state.appId,
55
+ connected: true
56
+ };
57
+ if (state.policy !== void 0) window.__aiOS_policy__ = state.policy;
58
+ if (state.context !== void 0) window.__aiOS_context__ = state.context;
59
+ if (state.zones !== void 0) window.__aiOS_zones__ = state.zones;
60
+ if (state.components !== void 0) window.__aiOS_components__ = state.components;
61
+ if (state.structured !== void 0) window.__aiOS_structured_data__ = state.structured;
62
+ if (state.pending !== void 0) window.__aiOS_pending_export__ = state.pending;
63
+ }
64
+ function injectPolicyMetaTag(policy) {
65
+ if (typeof document === "undefined") return;
66
+ const existing = document.querySelector('meta[name="aios:policy"]');
67
+ if (existing) existing.remove();
68
+ const meta = document.createElement("meta");
69
+ meta.name = "aios:policy";
70
+ meta.content = JSON.stringify(policy);
71
+ document.head.appendChild(meta);
72
+ }
73
+
74
+ // src/consent.ts
75
+ var STYLE_ID = "aios-sdk-consent-styles";
76
+ function ensureStyles() {
77
+ if (document.getElementById(STYLE_ID)) return;
78
+ const style = document.createElement("style");
79
+ style.id = STYLE_ID;
80
+ style.textContent = `
81
+ .aios-consent-overlay {
82
+ position: fixed;
83
+ inset: 0;
84
+ z-index: 2147483646;
85
+ background: rgba(0, 0, 0, 0.55);
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, sans-serif;
90
+ }
91
+ .aios-consent-card {
92
+ width: min(520px, calc(100vw - 32px));
93
+ background: #111118;
94
+ color: #f5f5f7;
95
+ border: 1px solid rgba(255,255,255,0.12);
96
+ border-radius: 16px;
97
+ box-shadow: 0 24px 80px rgba(0,0,0,0.45);
98
+ padding: 24px;
99
+ }
100
+ .aios-consent-title { font-size: 20px; font-weight: 700; margin: 0 0 8px; }
101
+ .aios-consent-message { color: #a1a1aa; font-size: 14px; line-height: 1.5; margin: 0 0 16px; }
102
+ .aios-consent-section { margin: 16px 0; }
103
+ .aios-consent-section h4 {
104
+ margin: 0 0 8px;
105
+ font-size: 12px;
106
+ letter-spacing: 0.08em;
107
+ text-transform: uppercase;
108
+ color: #22d3ee;
109
+ }
110
+ .aios-consent-list { margin: 0; padding-left: 18px; color: #d4d4d8; font-size: 13px; }
111
+ .aios-consent-meta { font-size: 12px; color: #71717a; margin-top: 12px; }
112
+ .aios-consent-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
113
+ .aios-consent-btn {
114
+ border: 0;
115
+ border-radius: 10px;
116
+ padding: 10px 14px;
117
+ font-size: 14px;
118
+ cursor: pointer;
119
+ }
120
+ .aios-consent-btn-primary { background: #22d3ee; color: #041014; font-weight: 600; }
121
+ .aios-consent-btn-secondary { background: rgba(255,255,255,0.08); color: #f5f5f7; }
122
+ .aios-consent-remember { display: flex; gap: 8px; align-items: center; font-size: 12px; color: #a1a1aa; margin-top: 12px; }
123
+ `;
124
+ document.head.appendChild(style);
125
+ }
126
+ function renderSeekList(items) {
127
+ if (!items.length) return '<p class="aios-consent-meta">No additional user questions declared.</p>';
128
+ return `<ul class="aios-consent-list">${items.map(
129
+ (item) => `<li><strong>${escapeHtml(item.label)}</strong>${item.sensitive ? " (sensitive)" : ""} \u2014 ${escapeHtml(item.description)}</li>`
130
+ ).join("")}</ul>`;
131
+ }
132
+ function escapeHtml(value) {
133
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
134
+ }
135
+ function showConsentModal(preview, options = {}) {
136
+ if (typeof document === "undefined") {
137
+ return Promise.resolve({ approved: false, rememberSite: false });
138
+ }
139
+ ensureStyles();
140
+ return new Promise((resolve) => {
141
+ const overlay = document.createElement("div");
142
+ overlay.className = "aios-consent-overlay";
143
+ overlay.innerHTML = `
144
+ <div class="aios-consent-card" role="dialog" aria-modal="true" aria-labelledby="aios-consent-title">
145
+ <h3 class="aios-consent-title" id="aios-consent-title">${escapeHtml(options.title ?? preview.title)}</h3>
146
+ <p class="aios-consent-message">${escapeHtml(options.message ?? preview.message)}</p>
147
+ <div class="aios-consent-section">
148
+ <h4>Page</h4>
149
+ <p class="aios-consent-meta">${escapeHtml(preview.pageTitle)} \xB7 ${escapeHtml(preview.pageType)}</p>
150
+ </div>
151
+ <div class="aios-consent-section">
152
+ <h4>Agent intent</h4>
153
+ <p class="aios-consent-meta">${escapeHtml(preview.agentIntent)}</p>
154
+ </div>
155
+ <div class="aios-consent-section">
156
+ <h4>Information requested from you</h4>
157
+ ${renderSeekList(preview.seekFromUser)}
158
+ </div>
159
+ <div class="aios-consent-section">
160
+ <h4>Data to send</h4>
161
+ <p class="aios-consent-meta">${preview.componentCount} component(s)${preview.sensitiveFieldCount ? ` \xB7 ${preview.sensitiveFieldCount} sensitive field(s)` : ""}</p>
162
+ </div>
163
+ ${options.allowRememberSite ? `<label class="aios-consent-remember"><input type="checkbox" id="aios-consent-remember" /> Always allow this site</label>` : ""}
164
+ <div class="aios-consent-actions">
165
+ <button type="button" class="aios-consent-btn aios-consent-btn-secondary" data-action="deny">Don't send</button>
166
+ <button type="button" class="aios-consent-btn aios-consent-btn-primary" data-action="approve">Send to aiOS</button>
167
+ </div>
168
+ </div>
169
+ `;
170
+ const cleanup = (result) => {
171
+ overlay.remove();
172
+ resolve(result);
173
+ };
174
+ overlay.addEventListener("click", (event) => {
175
+ const target = event.target;
176
+ const action = target.closest("[data-action]")?.getAttribute("data-action");
177
+ if (!action) return;
178
+ const remember = !!overlay.querySelector("#aios-consent-remember")?.checked;
179
+ cleanup({ approved: action === "approve", rememberSite: remember });
180
+ });
181
+ document.body.appendChild(overlay);
182
+ });
183
+ }
184
+
185
+ // src/events.ts
186
+ var EXTENSION_MESSAGE_SOURCE = "aios-extension";
187
+ var WIRE_TO_PUBLIC = {
188
+ "aios-extension:present": "extension:present",
189
+ "aios-extension:status": "extension:status",
190
+ "aios-extension:agent-session-start": "agent:session-start",
191
+ "aios-extension:agent-active": "agent:active",
192
+ "aios-extension:agent-session-end": "agent:session-end",
193
+ "aios-extension:agent-data-access": "agent:data-access"
194
+ };
195
+ function isWireMessage(data) {
196
+ if (!data || typeof data !== "object") return false;
197
+ const msg = data;
198
+ return msg.source === EXTENSION_MESSAGE_SOURCE && typeof msg.type === "string" && msg.type in WIRE_TO_PUBLIC;
199
+ }
200
+ function defaultStatus() {
201
+ return {
202
+ extensionPresent: false,
203
+ agentSessionActive: false,
204
+ lastAgentActivityAt: null,
205
+ sessionId: null,
206
+ sdkConnected: false,
207
+ appId: null
208
+ };
209
+ }
210
+ var ExtensionEventBus = class {
211
+ constructor() {
212
+ this.listening = false;
213
+ this.status = defaultStatus();
214
+ this.handlers = /* @__PURE__ */ new Map();
215
+ this.wildcardHandlers = /* @__PURE__ */ new Set();
216
+ this.onWindowMessage = (event) => {
217
+ if (event.source !== window) return;
218
+ if (event.origin !== window.location.origin) return;
219
+ this.ingestWireMessage(event.data);
220
+ };
221
+ this.onCustomEvent = (event) => {
222
+ const detail = event.detail;
223
+ this.ingestWireMessage(detail);
224
+ };
225
+ }
226
+ startListening() {
227
+ if (this.listening || typeof window === "undefined") return;
228
+ this.listening = true;
229
+ window.addEventListener("message", this.onWindowMessage);
230
+ document.addEventListener("aios:extension", this.onCustomEvent);
231
+ }
232
+ stopListening() {
233
+ if (!this.listening || typeof window === "undefined") return;
234
+ this.listening = false;
235
+ window.removeEventListener("message", this.onWindowMessage);
236
+ document.removeEventListener("aios:extension", this.onCustomEvent);
237
+ }
238
+ on(type, handler) {
239
+ if (!this.handlers.has(type)) this.handlers.set(type, /* @__PURE__ */ new Set());
240
+ this.handlers.get(type).add(handler);
241
+ return () => this.off(type, handler);
242
+ }
243
+ onAny(handler) {
244
+ this.wildcardHandlers.add(handler);
245
+ return () => this.wildcardHandlers.delete(handler);
246
+ }
247
+ off(type, handler) {
248
+ this.handlers.get(type)?.delete(handler);
249
+ }
250
+ getStatus() {
251
+ return { ...this.status };
252
+ }
253
+ queryPresence(appId) {
254
+ if (typeof window === "undefined") return;
255
+ postToExtension(appId, "aios-sdk:presence-query", {
256
+ route: typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : "/",
257
+ url: typeof window !== "undefined" ? window.location.href : ""
258
+ });
259
+ }
260
+ ingestWireMessage(data) {
261
+ if (!isWireMessage(data)) return;
262
+ const publicType = WIRE_TO_PUBLIC[data.type];
263
+ const payload = data.payload;
264
+ this.applyStatus(publicType, payload);
265
+ const evt = {
266
+ type: publicType,
267
+ timestamp: data.timestamp,
268
+ version: data.version || PROTOCOL_VERSION,
269
+ payload
270
+ };
271
+ this.emit(evt);
272
+ }
273
+ applyStatus(type, payload) {
274
+ switch (type) {
275
+ case "extension:present": {
276
+ const p = payload;
277
+ this.status.extensionPresent = true;
278
+ if (p.contentScriptVersion) {
279
+ this.status.contentScriptVersion = p.contentScriptVersion;
280
+ }
281
+ if (p.extensionVersion) {
282
+ this.status.extensionVersion = p.extensionVersion;
283
+ }
284
+ break;
285
+ }
286
+ case "extension:status": {
287
+ const p = payload;
288
+ this.status.extensionPresent = p.present;
289
+ this.status.agentSessionActive = p.agentSessionActive;
290
+ this.status.lastAgentActivityAt = p.lastAgentActivityAt;
291
+ this.status.sessionId = p.sessionId;
292
+ this.status.sdkConnected = p.sdkConnected;
293
+ this.status.appId = p.appId;
294
+ break;
295
+ }
296
+ case "agent:session-start": {
297
+ const p = payload;
298
+ this.status.agentSessionActive = true;
299
+ this.status.sessionId = p.sessionId;
300
+ this.status.lastAgentActivityAt = Date.now();
301
+ break;
302
+ }
303
+ case "agent:active": {
304
+ const p = payload;
305
+ this.status.agentSessionActive = true;
306
+ this.status.sessionId = p.sessionId ?? this.status.sessionId;
307
+ this.status.lastAgentActivityAt = p.timestamp ?? Date.now();
308
+ if (p.appId) this.status.appId = p.appId;
309
+ if (p.sdkIntegrated) this.status.sdkConnected = true;
310
+ break;
311
+ }
312
+ case "agent:session-end": {
313
+ this.status.agentSessionActive = false;
314
+ this.status.sessionId = null;
315
+ break;
316
+ }
317
+ case "agent:data-access":
318
+ this.status.lastAgentActivityAt = Date.now();
319
+ break;
320
+ default:
321
+ break;
322
+ }
323
+ }
324
+ emit(event) {
325
+ for (const handler of this.wildcardHandlers) {
326
+ try {
327
+ handler(event);
328
+ } catch {
329
+ }
330
+ }
331
+ const typed = this.handlers.get(event.type);
332
+ if (!typed) return;
333
+ for (const handler of typed) {
334
+ try {
335
+ handler(event);
336
+ } catch {
337
+ }
338
+ }
339
+ }
340
+ };
341
+
342
+ // src/defaults.ts
343
+ var DEFAULT_PAGE_POLICY = {
344
+ allowed: true,
345
+ capabilities: {
346
+ read: true,
347
+ write: false,
348
+ execute: false,
349
+ summarize: true
350
+ },
351
+ exportMode: "structured-only"
352
+ };
353
+ var DEFAULT_UNINTEGRATED_POLICY = {
354
+ allowed: true,
355
+ capabilities: {
356
+ read: true,
357
+ write: true,
358
+ execute: true,
359
+ summarize: true
360
+ },
361
+ exportMode: "dom-allowed"
362
+ };
363
+ function createDefaultPageContext(title = "Page") {
364
+ return {
365
+ pageType: "generic",
366
+ title,
367
+ description: "Web page integrated with aiOS.",
368
+ agentIntent: "Assist the user using only exported structured data.",
369
+ policy: DEFAULT_PAGE_POLICY
370
+ };
371
+ }
372
+ function mergePolicy(base, patch) {
373
+ return {
374
+ ...base,
375
+ ...patch,
376
+ capabilities: {
377
+ ...base.capabilities,
378
+ ...patch.capabilities
379
+ }
380
+ };
381
+ }
382
+ function mergePageContext(base, patch) {
383
+ return {
384
+ ...base,
385
+ ...patch,
386
+ policy: patch.policy ? mergePolicy(base.policy, patch.policy) : base.policy,
387
+ seekFromUser: patch.seekFromUser ?? base.seekFromUser
388
+ };
389
+ }
390
+ function normalizeCapabilities(capabilities) {
391
+ return {
392
+ read: capabilities?.read ?? true,
393
+ write: capabilities?.write ?? false,
394
+ execute: capabilities?.execute ?? false,
395
+ summarize: capabilities?.summarize ?? true
396
+ };
397
+ }
398
+ function countSensitiveSeekFields(fields = []) {
399
+ return fields.filter((f) => f.sensitive).length;
400
+ }
401
+ function createApprovalId() {
402
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
403
+ return crypto.randomUUID();
404
+ }
405
+ return `aios_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
406
+ }
407
+
408
+ // src/client.ts
409
+ var REMEMBER_KEY_PREFIX = "aios:consent:";
410
+ var AIOSClient = class {
411
+ constructor() {
412
+ this.appId = "";
413
+ this.initialized = false;
414
+ this.requireConsent = true;
415
+ this.pageContext = createDefaultPageContext();
416
+ this.zones = {};
417
+ this.components = /* @__PURE__ */ new Map();
418
+ this.pageData = {};
419
+ this.schema = "page.v1";
420
+ this.pendingExport = null;
421
+ this.lastApprovedExport = null;
422
+ this.events = new ExtensionEventBus();
423
+ }
424
+ init(options) {
425
+ this.appId = options.appId;
426
+ this.requireConsent = options.requireConsent ?? true;
427
+ this.initialized = true;
428
+ syncWindowGlobals({
429
+ appId: this.appId,
430
+ version: PROTOCOL_VERSION,
431
+ policy: this.pageContext.policy,
432
+ context: this.pageContext,
433
+ zones: this.zones,
434
+ components: Object.fromEntries(this.components)
435
+ });
436
+ postToExtension(this.appId, "aios-sdk:init", {
437
+ appId: this.appId,
438
+ version: PROTOCOL_VERSION,
439
+ requireConsent: this.requireConsent
440
+ });
441
+ this.events.startListening();
442
+ this.events.onAny(() => this.syncExtensionGlobals());
443
+ this.events.queryPresence(this.appId);
444
+ return this;
445
+ }
446
+ setPagePolicy(policy) {
447
+ this.assertInitialized();
448
+ this.pageContext = {
449
+ ...this.pageContext,
450
+ policy: mergePolicy(this.pageContext.policy, policy)
451
+ };
452
+ injectPolicyMetaTag(this.pageContext.policy);
453
+ syncWindowGlobals({
454
+ appId: this.appId,
455
+ version: PROTOCOL_VERSION,
456
+ policy: this.pageContext.policy,
457
+ context: this.pageContext,
458
+ zones: this.zones,
459
+ components: Object.fromEntries(this.components),
460
+ structured: this.lastApprovedExport ?? void 0,
461
+ pending: this.pendingExport ?? void 0
462
+ });
463
+ postToExtension(this.appId, "aios-sdk:policy", this.pageContext.policy);
464
+ return this;
465
+ }
466
+ setPageContext(context) {
467
+ this.assertInitialized();
468
+ this.pageContext = mergePageContext(this.pageContext, context);
469
+ if (context.policy) injectPolicyMetaTag(this.pageContext.policy);
470
+ this.syncState();
471
+ postToExtension(this.appId, "aios-sdk:context", this.pageContext);
472
+ return this;
473
+ }
474
+ defineZones(zones) {
475
+ this.assertInitialized();
476
+ this.zones = { ...zones };
477
+ this.syncState();
478
+ postToExtension(this.appId, "aios-sdk:zones", this.zones);
479
+ return this;
480
+ }
481
+ markComponent(id, component) {
482
+ this.assertInitialized();
483
+ const descriptor = {
484
+ ...component,
485
+ id,
486
+ kind: component.kind ?? "custom",
487
+ capabilities: component.capabilities ?? ["hidden"],
488
+ visible: component.visible ?? !component.capabilities?.includes("hidden")
489
+ };
490
+ this.components.set(id, descriptor);
491
+ this.syncState();
492
+ postToExtension(this.appId, "aios-sdk:components", {
493
+ id,
494
+ component: descriptor,
495
+ all: Object.fromEntries(this.components)
496
+ });
497
+ return this;
498
+ }
499
+ unmarkComponent(id) {
500
+ this.components.delete(id);
501
+ this.syncState();
502
+ return this;
503
+ }
504
+ exportPageData(options = {}) {
505
+ this.assertInitialized();
506
+ if (options.page) this.pageContext = mergePageContext(this.pageContext, options.page);
507
+ if (options.data) this.pageData = { ...options.data };
508
+ if (options.schema) this.schema = options.schema;
509
+ if (options.components) {
510
+ for (const component of options.components) {
511
+ this.components.set(component.id, component);
512
+ }
513
+ }
514
+ if (options.zones) this.zones = { ...options.zones };
515
+ this.pendingExport = this.buildPayload({ includeConsent: false });
516
+ this.syncState();
517
+ return this;
518
+ }
519
+ prepareExport(options = {}) {
520
+ this.exportPageData(options);
521
+ if (!this.pendingExport) {
522
+ throw new Error("Failed to prepare export payload.");
523
+ }
524
+ return this.pendingExport;
525
+ }
526
+ buildConsentPreview() {
527
+ const payload = this.pendingExport ?? this.buildPayload({ includeConsent: false });
528
+ const capabilities = normalizeCapabilities(payload.page.policy.capabilities);
529
+ return {
530
+ title: "Send page data to aiOS?",
531
+ message: "Review what will be shared with your local aiOS agent via the Safari extension.",
532
+ pageType: payload.page.pageType,
533
+ pageTitle: payload.page.title,
534
+ agentIntent: payload.page.agentIntent,
535
+ seekFromUser: payload.page.seekFromUser ?? [],
536
+ sensitiveFieldCount: countSensitiveSeekFields(payload.page.seekFromUser) + payload.components.filter((c) => c.sensitive).length,
537
+ componentCount: payload.components.length,
538
+ capabilities
539
+ };
540
+ }
541
+ async requestConsent(options = {}) {
542
+ this.assertInitialized();
543
+ if (!this.pendingExport) this.prepareExport();
544
+ if (!this.requireConsent && this.hasRememberedConsent()) {
545
+ return this.flushApprovedExport({ rememberSite: true });
546
+ }
547
+ if (this.hasRememberedConsent()) {
548
+ return this.flushApprovedExport({ rememberSite: true });
549
+ }
550
+ const result = await showConsentModal(this.buildConsentPreview(), {
551
+ allowRememberSite: true,
552
+ ...options
553
+ });
554
+ if (!result.approved) {
555
+ this.pendingExport = this.buildPayload({ includeConsent: false });
556
+ this.syncState();
557
+ return false;
558
+ }
559
+ return this.flushApprovedExport({ rememberSite: result.rememberSite });
560
+ }
561
+ async flushApprovedExport(options) {
562
+ this.assertInitialized();
563
+ if (!this.pageContext.policy.allowed) {
564
+ return false;
565
+ }
566
+ const approvalId = createApprovalId();
567
+ const approvedAt = Date.now();
568
+ const payload = this.buildPayload({
569
+ includeConsent: true,
570
+ approvalId,
571
+ approvedAt,
572
+ rememberSite: options?.rememberSite
573
+ });
574
+ this.lastApprovedExport = payload;
575
+ this.pendingExport = null;
576
+ if (options?.rememberSite) {
577
+ this.rememberConsentForSite();
578
+ }
579
+ syncWindowGlobals({
580
+ appId: this.appId,
581
+ version: PROTOCOL_VERSION,
582
+ policy: this.pageContext.policy,
583
+ context: this.pageContext,
584
+ zones: this.zones,
585
+ components: Object.fromEntries(this.components),
586
+ structured: payload
587
+ });
588
+ postToExtension(this.appId, "aios-sdk:export", payload);
589
+ return true;
590
+ }
591
+ /**
592
+ * Convenience path: prepare payload, ask the user, and send only if approved.
593
+ */
594
+ async publish(options = {}) {
595
+ this.prepareExport(options);
596
+ return this.requestConsent(options);
597
+ }
598
+ getPendingExport() {
599
+ return this.pendingExport;
600
+ }
601
+ getApprovedExport() {
602
+ return this.lastApprovedExport;
603
+ }
604
+ getPageContext() {
605
+ return this.pageContext;
606
+ }
607
+ getComponents() {
608
+ return [...this.components.values()];
609
+ }
610
+ ping() {
611
+ this.assertInitialized();
612
+ postToExtension(this.appId, "aios-sdk:ping", { connected: true });
613
+ }
614
+ /**
615
+ * Subscribe to extension / agent lifecycle events (exam proctoring, compliance, UX).
616
+ * Returns an unsubscribe function.
617
+ */
618
+ onEvent(type, handler) {
619
+ this.assertInitialized();
620
+ return this.events.on(type, handler);
621
+ }
622
+ /** Fires when the aiOS browser extension content script is active on this page. */
623
+ onExtensionPresent(handler) {
624
+ return this.onEvent("extension:present", handler);
625
+ }
626
+ /** Fires when the local aiOS agent performs any tool action on this page. */
627
+ onAgentActive(handler) {
628
+ return this.onEvent("agent:active", handler);
629
+ }
630
+ /** Subscribe to every extension event. */
631
+ onAnyExtensionEvent(handler) {
632
+ this.assertInitialized();
633
+ return this.events.onAny(handler);
634
+ }
635
+ offEvent(type, handler) {
636
+ this.events.off(type, handler);
637
+ }
638
+ /** Latest extension + agent session snapshot (updated as events arrive). */
639
+ getExtensionStatus() {
640
+ return this.events.getStatus();
641
+ }
642
+ /** Ask the extension to respond with `extension:status` (also sent on init). */
643
+ queryExtensionPresence() {
644
+ this.assertInitialized();
645
+ this.events.queryPresence(this.appId);
646
+ }
647
+ syncExtensionGlobals() {
648
+ if (typeof window === "undefined" || !window.__aiOS_sdk__) return;
649
+ const status = this.events.getStatus();
650
+ window.__aiOS_sdk__.extensionPresent = status.extensionPresent;
651
+ window.__aiOS_sdk__.agentSessionActive = status.agentSessionActive;
652
+ }
653
+ buildPayload(args) {
654
+ const route = typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : "/";
655
+ const payload = {
656
+ version: PROTOCOL_VERSION,
657
+ appId: this.appId,
658
+ route,
659
+ url: typeof window !== "undefined" ? window.location.href : "",
660
+ page: this.pageContext,
661
+ data: {
662
+ schema: this.schema,
663
+ ...this.pageData
664
+ },
665
+ components: [...this.components.values()],
666
+ zones: Object.keys(this.zones).length ? this.zones : void 0,
667
+ meta: {
668
+ generatedAt: Date.now(),
669
+ source: "sdk",
670
+ trust: "verified-sdk",
671
+ consent: {
672
+ approvalId: args.approvalId ?? "",
673
+ approvedAt: args.approvedAt ?? 0,
674
+ approvedBy: "user",
675
+ rememberSite: args.rememberSite
676
+ }
677
+ }
678
+ };
679
+ if (!args.includeConsent) {
680
+ payload.meta.consent = {
681
+ approvalId: "",
682
+ approvedAt: 0,
683
+ approvedBy: "user"
684
+ };
685
+ }
686
+ return payload;
687
+ }
688
+ syncState() {
689
+ syncWindowGlobals({
690
+ appId: this.appId,
691
+ version: PROTOCOL_VERSION,
692
+ policy: this.pageContext.policy,
693
+ context: this.pageContext,
694
+ zones: this.zones,
695
+ components: Object.fromEntries(this.components),
696
+ structured: this.lastApprovedExport ?? void 0,
697
+ pending: this.pendingExport ?? void 0
698
+ });
699
+ }
700
+ assertInitialized() {
701
+ if (!this.initialized) {
702
+ throw new Error("aiOS SDK not initialized. Call aiOS.init({ appId }) first.");
703
+ }
704
+ }
705
+ rememberConsentForSite() {
706
+ if (typeof window === "undefined") return;
707
+ try {
708
+ localStorage.setItem(`${REMEMBER_KEY_PREFIX}${window.location.origin}`, "1");
709
+ } catch {
710
+ }
711
+ }
712
+ hasRememberedConsent() {
713
+ if (typeof window === "undefined") return false;
714
+ try {
715
+ return localStorage.getItem(`${REMEMBER_KEY_PREFIX}${window.location.origin}`) === "1";
716
+ } catch {
717
+ return false;
718
+ }
719
+ }
720
+ };
721
+ var aiOS = new AIOSClient();
722
+ // Annotate the CommonJS export names for ESM import in node:
723
+ 0 && (module.exports = {
724
+ AIOSClient,
725
+ DEFAULT_PAGE_POLICY,
726
+ DEFAULT_UNINTEGRATED_POLICY,
727
+ EXTENSION_MESSAGE_SOURCE,
728
+ ExtensionEventBus,
729
+ PROTOCOL_VERSION,
730
+ aiOS,
731
+ createDefaultPageContext
732
+ });