@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/README.md +278 -0
- package/dist/index.cjs +732 -0
- package/dist/index.d.cts +303 -0
- package/dist/index.d.ts +303 -0
- package/dist/index.js +698 -0
- package/package.json +39 -0
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
+
});
|