@asiyst/sdk 0.1.6 → 0.1.9
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 +17 -3
- package/dist/index.cjs +265 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -3
- package/dist/index.d.ts +50 -3
- package/dist/index.js +262 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -69,7 +69,7 @@ type AsiystEventName = keyof AsiystEventMap;
|
|
|
69
69
|
|
|
70
70
|
type AnchorPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
71
71
|
type AssistantMode = "guided" | "assist";
|
|
72
|
-
type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
|
|
72
|
+
type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open" | "close" | "filter" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
|
|
73
73
|
type HighlightStyle = "spotlight" | "outline" | "glow" | "pointer" | "pulse" | "dim";
|
|
74
74
|
type ElementKind = "button" | "link" | "input" | "select" | "textarea" | "form" | "navigation" | "heading" | "menu" | "dialog" | "tab" | "card" | "search" | "section" | "other";
|
|
75
75
|
interface TargetRef {
|
|
@@ -82,6 +82,8 @@ type TargetInput = string | TargetRef;
|
|
|
82
82
|
interface InitOptions {
|
|
83
83
|
projectId: string;
|
|
84
84
|
publicKey: string;
|
|
85
|
+
avatarId?: string;
|
|
86
|
+
position?: AnchorPosition;
|
|
85
87
|
apiBaseUrl?: string;
|
|
86
88
|
mode?: AssistantMode;
|
|
87
89
|
allowedActions?: ActionKind[];
|
|
@@ -102,6 +104,28 @@ interface BehaviorConfig {
|
|
|
102
104
|
voiceSpeed?: number;
|
|
103
105
|
speechBubble?: boolean;
|
|
104
106
|
}
|
|
107
|
+
interface AvatarRuntimeRule {
|
|
108
|
+
action?: string;
|
|
109
|
+
allowed?: boolean;
|
|
110
|
+
target?: string;
|
|
111
|
+
route?: string;
|
|
112
|
+
routes?: string[];
|
|
113
|
+
domain?: string;
|
|
114
|
+
domains?: string[];
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
sourceId?: string;
|
|
117
|
+
dataSourceId?: string;
|
|
118
|
+
}
|
|
119
|
+
interface AvatarDataSource {
|
|
120
|
+
id: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
type?: string;
|
|
123
|
+
enabled?: boolean;
|
|
124
|
+
url?: string;
|
|
125
|
+
method?: string;
|
|
126
|
+
headers?: Record<string, string>;
|
|
127
|
+
credentials?: string;
|
|
128
|
+
}
|
|
105
129
|
interface ProjectConfig {
|
|
106
130
|
schemaVersion: number;
|
|
107
131
|
version: number;
|
|
@@ -116,6 +140,11 @@ interface ProjectConfig {
|
|
|
116
140
|
behavior: BehaviorConfig;
|
|
117
141
|
mode: AssistantMode;
|
|
118
142
|
allowedActions: ActionKind[];
|
|
143
|
+
allowedDomains: string[];
|
|
144
|
+
allowedRoutes: string[];
|
|
145
|
+
blockedRoutes: string[];
|
|
146
|
+
rules: AvatarRuntimeRule[];
|
|
147
|
+
dataSources: AvatarDataSource[];
|
|
119
148
|
elementSelectors: Record<string, string>;
|
|
120
149
|
}
|
|
121
150
|
interface Rect {
|
|
@@ -210,7 +239,7 @@ declare const Asiyst: {
|
|
|
210
239
|
off<K extends keyof AsiystEventMap>(event: K, handler: (payload: AsiystEventMap[K]) => void): void;
|
|
211
240
|
};
|
|
212
241
|
|
|
213
|
-
declare const SDK_VERSION
|
|
242
|
+
declare const SDK_VERSION: string;
|
|
214
243
|
|
|
215
244
|
declare class AsiystError extends Error {
|
|
216
245
|
readonly code: string;
|
|
@@ -263,9 +292,13 @@ declare function normalizeProjectConfig(raw: unknown): ProjectConfig;
|
|
|
263
292
|
declare function validateInitOptions(options: {
|
|
264
293
|
projectId?: unknown;
|
|
265
294
|
publicKey?: unknown;
|
|
295
|
+
avatarId?: unknown;
|
|
296
|
+
position?: unknown;
|
|
266
297
|
}): {
|
|
267
298
|
projectId: string;
|
|
268
299
|
publicKey: string;
|
|
300
|
+
avatarId?: string;
|
|
301
|
+
position?: AnchorPosition;
|
|
269
302
|
};
|
|
270
303
|
|
|
271
304
|
declare function isSafeSelector(selector: string): boolean;
|
|
@@ -299,4 +332,18 @@ declare function anchorToViewport(position: "bottom-right" | "bottom-left" | "to
|
|
|
299
332
|
y: number;
|
|
300
333
|
};
|
|
301
334
|
|
|
302
|
-
|
|
335
|
+
declare function validateWebsiteDomain(config: ProjectConfig, currentUrl: string): {
|
|
336
|
+
allowed: boolean;
|
|
337
|
+
reason?: string;
|
|
338
|
+
};
|
|
339
|
+
declare function canNavigateTo(config: ProjectConfig, targetUrl: string, currentUrl: string): {
|
|
340
|
+
allowed: boolean;
|
|
341
|
+
reason?: string;
|
|
342
|
+
};
|
|
343
|
+
declare function evaluateActionPermission(action: ActionKind, config: ProjectConfig, currentUrl: string, target?: string): {
|
|
344
|
+
allowed: boolean;
|
|
345
|
+
reason?: string;
|
|
346
|
+
};
|
|
347
|
+
declare function isActionAllowed(action: ActionKind, config: ProjectConfig, source: "developer" | "cloud" | "local", currentUrl?: string, target?: string): boolean;
|
|
348
|
+
|
|
349
|
+
export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
|
package/dist/index.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ type AsiystEventName = keyof AsiystEventMap;
|
|
|
69
69
|
|
|
70
70
|
type AnchorPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
71
71
|
type AssistantMode = "guided" | "assist";
|
|
72
|
-
type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
|
|
72
|
+
type ActionKind = "navigate" | "click" | "highlight" | "scroll" | "type" | "select" | "open" | "close" | "filter" | "open-menu" | "open-modal" | "search" | "wait" | "explain" | "complete";
|
|
73
73
|
type HighlightStyle = "spotlight" | "outline" | "glow" | "pointer" | "pulse" | "dim";
|
|
74
74
|
type ElementKind = "button" | "link" | "input" | "select" | "textarea" | "form" | "navigation" | "heading" | "menu" | "dialog" | "tab" | "card" | "search" | "section" | "other";
|
|
75
75
|
interface TargetRef {
|
|
@@ -82,6 +82,8 @@ type TargetInput = string | TargetRef;
|
|
|
82
82
|
interface InitOptions {
|
|
83
83
|
projectId: string;
|
|
84
84
|
publicKey: string;
|
|
85
|
+
avatarId?: string;
|
|
86
|
+
position?: AnchorPosition;
|
|
85
87
|
apiBaseUrl?: string;
|
|
86
88
|
mode?: AssistantMode;
|
|
87
89
|
allowedActions?: ActionKind[];
|
|
@@ -102,6 +104,28 @@ interface BehaviorConfig {
|
|
|
102
104
|
voiceSpeed?: number;
|
|
103
105
|
speechBubble?: boolean;
|
|
104
106
|
}
|
|
107
|
+
interface AvatarRuntimeRule {
|
|
108
|
+
action?: string;
|
|
109
|
+
allowed?: boolean;
|
|
110
|
+
target?: string;
|
|
111
|
+
route?: string;
|
|
112
|
+
routes?: string[];
|
|
113
|
+
domain?: string;
|
|
114
|
+
domains?: string[];
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
sourceId?: string;
|
|
117
|
+
dataSourceId?: string;
|
|
118
|
+
}
|
|
119
|
+
interface AvatarDataSource {
|
|
120
|
+
id: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
type?: string;
|
|
123
|
+
enabled?: boolean;
|
|
124
|
+
url?: string;
|
|
125
|
+
method?: string;
|
|
126
|
+
headers?: Record<string, string>;
|
|
127
|
+
credentials?: string;
|
|
128
|
+
}
|
|
105
129
|
interface ProjectConfig {
|
|
106
130
|
schemaVersion: number;
|
|
107
131
|
version: number;
|
|
@@ -116,6 +140,11 @@ interface ProjectConfig {
|
|
|
116
140
|
behavior: BehaviorConfig;
|
|
117
141
|
mode: AssistantMode;
|
|
118
142
|
allowedActions: ActionKind[];
|
|
143
|
+
allowedDomains: string[];
|
|
144
|
+
allowedRoutes: string[];
|
|
145
|
+
blockedRoutes: string[];
|
|
146
|
+
rules: AvatarRuntimeRule[];
|
|
147
|
+
dataSources: AvatarDataSource[];
|
|
119
148
|
elementSelectors: Record<string, string>;
|
|
120
149
|
}
|
|
121
150
|
interface Rect {
|
|
@@ -210,7 +239,7 @@ declare const Asiyst: {
|
|
|
210
239
|
off<K extends keyof AsiystEventMap>(event: K, handler: (payload: AsiystEventMap[K]) => void): void;
|
|
211
240
|
};
|
|
212
241
|
|
|
213
|
-
declare const SDK_VERSION
|
|
242
|
+
declare const SDK_VERSION: string;
|
|
214
243
|
|
|
215
244
|
declare class AsiystError extends Error {
|
|
216
245
|
readonly code: string;
|
|
@@ -263,9 +292,13 @@ declare function normalizeProjectConfig(raw: unknown): ProjectConfig;
|
|
|
263
292
|
declare function validateInitOptions(options: {
|
|
264
293
|
projectId?: unknown;
|
|
265
294
|
publicKey?: unknown;
|
|
295
|
+
avatarId?: unknown;
|
|
296
|
+
position?: unknown;
|
|
266
297
|
}): {
|
|
267
298
|
projectId: string;
|
|
268
299
|
publicKey: string;
|
|
300
|
+
avatarId?: string;
|
|
301
|
+
position?: AnchorPosition;
|
|
269
302
|
};
|
|
270
303
|
|
|
271
304
|
declare function isSafeSelector(selector: string): boolean;
|
|
@@ -299,4 +332,18 @@ declare function anchorToViewport(position: "bottom-right" | "bottom-left" | "to
|
|
|
299
332
|
y: number;
|
|
300
333
|
};
|
|
301
334
|
|
|
302
|
-
|
|
335
|
+
declare function validateWebsiteDomain(config: ProjectConfig, currentUrl: string): {
|
|
336
|
+
allowed: boolean;
|
|
337
|
+
reason?: string;
|
|
338
|
+
};
|
|
339
|
+
declare function canNavigateTo(config: ProjectConfig, targetUrl: string, currentUrl: string): {
|
|
340
|
+
allowed: boolean;
|
|
341
|
+
reason?: string;
|
|
342
|
+
};
|
|
343
|
+
declare function evaluateActionPermission(action: ActionKind, config: ProjectConfig, currentUrl: string, target?: string): {
|
|
344
|
+
allowed: boolean;
|
|
345
|
+
reason?: string;
|
|
346
|
+
};
|
|
347
|
+
declare function isActionAllowed(action: ActionKind, config: ProjectConfig, source: "developer" | "cloud" | "local", currentUrl?: string, target?: string): boolean;
|
|
348
|
+
|
|
349
|
+
export { type ActionKind, ActionNotAllowedError, Asiyst, AsiystError, type AsiystEventMap, type AsiystEventName, type AssistantMode, AuthenticationError, ConfigurationError, type HighlightStyle, type InitOptions, InitializationError, type MappedElement, NetworkError, type ProjectConfig, SDK_VERSION, type TargetInput, TargetNotFoundError, type TargetRef, type TaskDefinition, TaskExecutionError, TaskStatus, type TaskStep, type WebsiteMapSnapshot, type WorkflowDefinition, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,9 @@ var ALL_ACTION_KINDS = [
|
|
|
57
57
|
"scroll",
|
|
58
58
|
"type",
|
|
59
59
|
"select",
|
|
60
|
+
"open",
|
|
61
|
+
"close",
|
|
62
|
+
"filter",
|
|
60
63
|
"open-menu",
|
|
61
64
|
"open-modal",
|
|
62
65
|
"search",
|
|
@@ -66,8 +69,8 @@ var ALL_ACTION_KINDS = [
|
|
|
66
69
|
];
|
|
67
70
|
|
|
68
71
|
// src/core/constants.ts
|
|
69
|
-
var SDK_VERSION = "0.1.
|
|
70
|
-
var DEFAULT_API_BASE_URL = "https://
|
|
72
|
+
var SDK_VERSION = "0.1.9" ;
|
|
73
|
+
var DEFAULT_API_BASE_URL = "https://nqhxpgsjofzqudyqkqib.supabase.co/functions/v1/api";
|
|
71
74
|
var CONFIG_SCHEMA_VERSION = 1;
|
|
72
75
|
var HOST_ELEMENT_ID = "asiyst-host";
|
|
73
76
|
var DATA_ATTR = "data-asiyst";
|
|
@@ -122,6 +125,19 @@ var ANCHORS = [
|
|
|
122
125
|
"top-right",
|
|
123
126
|
"top-left"
|
|
124
127
|
];
|
|
128
|
+
var PUBLIC_IDENTIFIER_PATTERN = /^(?=.{1,128}$)[A-Za-z0-9_][A-Za-z0-9_-]*$/;
|
|
129
|
+
function isValidPublicIdentifier(value) {
|
|
130
|
+
if (typeof value !== "string") return false;
|
|
131
|
+
const trimmed = value.trim();
|
|
132
|
+
if (!trimmed || trimmed.length > 128) return false;
|
|
133
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(trimmed)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return PUBLIC_IDENTIFIER_PATTERN.test(trimmed);
|
|
137
|
+
}
|
|
138
|
+
function isValidProjectId(value) {
|
|
139
|
+
return isValidPublicIdentifier(value);
|
|
140
|
+
}
|
|
125
141
|
function isRecord(value) {
|
|
126
142
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
143
|
}
|
|
@@ -150,6 +166,88 @@ function parseAllowedActions(value, fallback) {
|
|
|
150
166
|
}
|
|
151
167
|
return next.length > 0 ? next : [...fallback];
|
|
152
168
|
}
|
|
169
|
+
function parseStringList(value) {
|
|
170
|
+
if (typeof value === "string") {
|
|
171
|
+
return value.split(",").map((item) => sanitizeText(item.trim(), 200)).filter(Boolean);
|
|
172
|
+
}
|
|
173
|
+
if (!Array.isArray(value)) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
return value.filter((item) => typeof item === "string").map((item) => sanitizeText(item.trim(), 200)).filter(Boolean);
|
|
177
|
+
}
|
|
178
|
+
function parseRule(value) {
|
|
179
|
+
if (!isRecord(value)) return null;
|
|
180
|
+
const action = typeof value.action === "string" ? sanitizeText(value.action, 64) : void 0;
|
|
181
|
+
if (action && !ALL_ACTION_KINDS.includes(action)) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const routeList = parseStringList(value.routes ?? value.route);
|
|
185
|
+
const domainList = parseStringList(value.domains ?? value.domain);
|
|
186
|
+
const hasAllowedValue = typeof value.allowed === "boolean";
|
|
187
|
+
const hasAnySemanticField = hasAllowedValue || typeof value.target === "string" || typeof value.route === "string" || routeList.length > 0 || typeof value.domain === "string" || domainList.length > 0 || typeof value.enabled === "boolean" || typeof value.sourceId === "string" || typeof value.dataSourceId === "string";
|
|
188
|
+
if (!hasAnySemanticField) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const rule = {
|
|
192
|
+
action,
|
|
193
|
+
allowed: typeof value.allowed === "boolean" ? value.allowed : void 0,
|
|
194
|
+
target: typeof value.target === "string" ? sanitizeText(value.target, 200) : void 0,
|
|
195
|
+
route: typeof value.route === "string" ? sanitizeText(value.route, 200) : void 0,
|
|
196
|
+
routes: routeList,
|
|
197
|
+
domain: typeof value.domain === "string" ? sanitizeText(value.domain, 200) : void 0,
|
|
198
|
+
domains: domainList,
|
|
199
|
+
enabled: typeof value.enabled === "boolean" ? value.enabled : void 0,
|
|
200
|
+
sourceId: typeof value.sourceId === "string" ? sanitizeText(value.sourceId, 128) : void 0,
|
|
201
|
+
dataSourceId: typeof value.dataSourceId === "string" ? sanitizeText(value.dataSourceId, 128) : void 0
|
|
202
|
+
};
|
|
203
|
+
if (typeof value.allowed !== "undefined" && typeof value.allowed !== "boolean") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return rule;
|
|
207
|
+
}
|
|
208
|
+
function parseRules(value) {
|
|
209
|
+
if (!Array.isArray(value)) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
const rules = [];
|
|
213
|
+
for (const entry of value) {
|
|
214
|
+
const rule = parseRule(entry);
|
|
215
|
+
if (rule) {
|
|
216
|
+
rules.push(rule);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return rules;
|
|
220
|
+
}
|
|
221
|
+
function parseDataSources(value) {
|
|
222
|
+
if (!Array.isArray(value)) {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
const next = [];
|
|
226
|
+
for (const entry of value) {
|
|
227
|
+
if (!isRecord(entry)) continue;
|
|
228
|
+
const id = typeof entry.id === "string" ? sanitizeText(entry.id, 128) : "";
|
|
229
|
+
if (!id) continue;
|
|
230
|
+
next.push({
|
|
231
|
+
id,
|
|
232
|
+
name: typeof entry.name === "string" ? sanitizeText(entry.name, 128) : void 0,
|
|
233
|
+
type: typeof entry.type === "string" ? sanitizeText(entry.type, 64) : void 0,
|
|
234
|
+
enabled: typeof entry.enabled === "boolean" ? entry.enabled : void 0,
|
|
235
|
+
url: typeof entry.url === "string" ? sanitizeText(entry.url, 400) : void 0,
|
|
236
|
+
method: typeof entry.method === "string" ? sanitizeText(entry.method, 20) : void 0,
|
|
237
|
+
headers: isRecord(entry.headers) ? (() => {
|
|
238
|
+
const safeHeaders = {};
|
|
239
|
+
for (const [key, value2] of Object.entries(entry.headers)) {
|
|
240
|
+
if (typeof value2 === "string") {
|
|
241
|
+
safeHeaders[sanitizeText(key, 64)] = sanitizeText(value2, 200);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return safeHeaders;
|
|
245
|
+
})() : void 0,
|
|
246
|
+
credentials: typeof entry.credentials === "string" ? "[redacted]" : void 0
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return next;
|
|
250
|
+
}
|
|
153
251
|
function parseTheme(value) {
|
|
154
252
|
if (!isRecord(value)) {
|
|
155
253
|
return {};
|
|
@@ -220,6 +318,11 @@ function fallbackConfig() {
|
|
|
220
318
|
},
|
|
221
319
|
mode: "guided",
|
|
222
320
|
allowedActions: ["navigate", "highlight", "scroll", "wait", "explain", "complete"],
|
|
321
|
+
allowedDomains: [],
|
|
322
|
+
allowedRoutes: [],
|
|
323
|
+
blockedRoutes: [],
|
|
324
|
+
rules: [],
|
|
325
|
+
dataSources: [],
|
|
223
326
|
elementSelectors: {}
|
|
224
327
|
};
|
|
225
328
|
}
|
|
@@ -243,22 +346,41 @@ function normalizeProjectConfig(raw) {
|
|
|
243
346
|
behavior: { ...base.behavior, ...parseBehavior(raw.behavior) },
|
|
244
347
|
mode: raw.mode === "assist" ? "assist" : "guided",
|
|
245
348
|
allowedActions: parseAllowedActions(raw.allowedActions, base.allowedActions),
|
|
349
|
+
allowedDomains: parseStringList(raw.allowedDomains ?? raw.allowed_domains),
|
|
350
|
+
allowedRoutes: parseStringList(raw.allowedRoutes ?? raw.allowed_routes),
|
|
351
|
+
blockedRoutes: parseStringList(raw.blockedRoutes ?? raw.blocked_routes),
|
|
352
|
+
rules: parseRules(raw.rules ?? raw.avatarRules),
|
|
353
|
+
dataSources: parseDataSources(raw.dataSources ?? raw.enabledDataSources),
|
|
246
354
|
elementSelectors: parseElementSelectors(raw.elementSelectors)
|
|
247
355
|
};
|
|
248
356
|
}
|
|
249
357
|
function validateInitOptions(options) {
|
|
250
|
-
|
|
251
|
-
|
|
358
|
+
const projectIdValue = typeof options.projectId === "string" ? options.projectId.trim() : "";
|
|
359
|
+
if (!projectIdValue) {
|
|
360
|
+
throw new ConfigurationError("Asiyst SDK: projectId is required.");
|
|
361
|
+
}
|
|
362
|
+
if (!isValidProjectId(projectIdValue)) {
|
|
363
|
+
throw new ConfigurationError("Asiyst SDK: projectId is invalid. Use a public Project ID.");
|
|
252
364
|
}
|
|
253
365
|
if (typeof options.publicKey !== "string" || !options.publicKey.trim()) {
|
|
254
366
|
throw new ConfigurationError("publicKey is required");
|
|
255
367
|
}
|
|
256
|
-
if (
|
|
368
|
+
if (projectIdValue.length > 128 || options.publicKey.trim().length > 256) {
|
|
257
369
|
throw new ConfigurationError("project credentials exceed allowed length");
|
|
258
370
|
}
|
|
371
|
+
const avatarId = typeof options.avatarId === "string" ? options.avatarId.trim() : "";
|
|
372
|
+
if (options.avatarId !== void 0 && (!avatarId || avatarId.length > 128 || !/^[A-Za-z0-9_-]+$/.test(avatarId))) {
|
|
373
|
+
throw new ConfigurationError("Asiyst SDK: avatarId is invalid.");
|
|
374
|
+
}
|
|
375
|
+
const position = options.position === void 0 ? void 0 : ANCHORS.includes(options.position) ? options.position : void 0;
|
|
376
|
+
if (options.position !== void 0 && !position) {
|
|
377
|
+
throw new ConfigurationError("Asiyst SDK: position is invalid.");
|
|
378
|
+
}
|
|
259
379
|
return {
|
|
260
|
-
projectId:
|
|
261
|
-
publicKey: options.publicKey.trim()
|
|
380
|
+
projectId: projectIdValue,
|
|
381
|
+
publicKey: options.publicKey.trim(),
|
|
382
|
+
avatarId: avatarId || void 0,
|
|
383
|
+
position
|
|
262
384
|
};
|
|
263
385
|
}
|
|
264
386
|
|
|
@@ -309,6 +431,7 @@ var HttpTransport = class {
|
|
|
309
431
|
"Content-Type": "application/json",
|
|
310
432
|
"X-Asiyst-Project-Id": this.options.projectId,
|
|
311
433
|
"X-Asiyst-Public-Key": this.options.publicKey,
|
|
434
|
+
...this.options.avatarId ? { "X-Asiyst-Avatar-Id": this.options.avatarId } : {},
|
|
312
435
|
"X-Asiyst-SDK-Version": SDK_VERSION
|
|
313
436
|
},
|
|
314
437
|
body: req.body === void 0 ? void 0 : JSON.stringify(req.body),
|
|
@@ -538,6 +661,7 @@ var ConfigManager = class {
|
|
|
538
661
|
return {
|
|
539
662
|
...base,
|
|
540
663
|
mode: this.options.mode ?? base.mode,
|
|
664
|
+
position: this.options.position ?? base.position,
|
|
541
665
|
allowedActions: this.options.allowedActions ?? base.allowedActions
|
|
542
666
|
};
|
|
543
667
|
}
|
|
@@ -1635,14 +1759,110 @@ var AvatarController = class {
|
|
|
1635
1759
|
};
|
|
1636
1760
|
|
|
1637
1761
|
// src/interaction/permissions.ts
|
|
1638
|
-
function
|
|
1762
|
+
function normalizeHost(host) {
|
|
1763
|
+
return host.replace(/^https?:\/\//i, "").replace(/\/$/, "").toLowerCase();
|
|
1764
|
+
}
|
|
1765
|
+
function matchesRoutePattern(pathname, pattern) {
|
|
1766
|
+
const candidate = pathname;
|
|
1767
|
+
const normalized = pattern.trim();
|
|
1768
|
+
if (!normalized) {
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
1771
|
+
const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
1772
|
+
return new RegExp(`^${escaped}$`, "i").test(candidate);
|
|
1773
|
+
}
|
|
1774
|
+
function isLikelyJavaScriptUrl(value) {
|
|
1775
|
+
const lower = value.trim().toLowerCase();
|
|
1776
|
+
return lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.includes("eval(") || lower.includes("new function") || lower.includes("document.cookie");
|
|
1777
|
+
}
|
|
1778
|
+
function validateWebsiteDomain(config, currentUrl) {
|
|
1779
|
+
const allowedDomains = config.allowedDomains.map((domain) => normalizeHost(domain));
|
|
1780
|
+
if (allowedDomains.length === 0) {
|
|
1781
|
+
return { allowed: true };
|
|
1782
|
+
}
|
|
1783
|
+
try {
|
|
1784
|
+
const parsed = new URL(currentUrl);
|
|
1785
|
+
const host = normalizeHost(parsed.host);
|
|
1786
|
+
const allowed = allowedDomains.some((domain) => host === domain || host.endsWith(`.${domain}`));
|
|
1787
|
+
if (!allowed) {
|
|
1788
|
+
return { allowed: false, reason: "DOMAIN_NOT_AUTHORIZED" };
|
|
1789
|
+
}
|
|
1790
|
+
return { allowed: true };
|
|
1791
|
+
} catch {
|
|
1792
|
+
return { allowed: false, reason: "DOMAIN_NOT_AUTHORIZED" };
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
function canNavigateTo(config, targetUrl, currentUrl) {
|
|
1796
|
+
if (!targetUrl || typeof targetUrl !== "string") {
|
|
1797
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1798
|
+
}
|
|
1799
|
+
if (isLikelyJavaScriptUrl(targetUrl)) {
|
|
1800
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1801
|
+
}
|
|
1802
|
+
let target;
|
|
1803
|
+
try {
|
|
1804
|
+
target = new URL(targetUrl, currentUrl);
|
|
1805
|
+
} catch {
|
|
1806
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1807
|
+
}
|
|
1808
|
+
if (!["http:", "https:"].includes(target.protocol)) {
|
|
1809
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1810
|
+
}
|
|
1811
|
+
const domainCheck = validateWebsiteDomain(config, currentUrl);
|
|
1812
|
+
if (!domainCheck.allowed) {
|
|
1813
|
+
return domainCheck;
|
|
1814
|
+
}
|
|
1815
|
+
const host = normalizeHost(target.host);
|
|
1816
|
+
const allowedDomains = config.allowedDomains.map((domain) => normalizeHost(domain));
|
|
1817
|
+
if (allowedDomains.length > 0 && !allowedDomains.some((domain) => host === domain || host.endsWith(`.${domain}`))) {
|
|
1818
|
+
return { allowed: false, reason: "DOMAIN_NOT_ALLOWED" };
|
|
1819
|
+
}
|
|
1820
|
+
const pathname = target.pathname || "/";
|
|
1821
|
+
if (config.blockedRoutes.some((route) => matchesRoutePattern(pathname, route))) {
|
|
1822
|
+
return { allowed: false, reason: "ROUTE_BLOCKED" };
|
|
1823
|
+
}
|
|
1824
|
+
if (config.allowedRoutes.length > 0 && !config.allowedRoutes.some((route) => matchesRoutePattern(pathname, route))) {
|
|
1825
|
+
return { allowed: false, reason: "ROUTE_NOT_PERMITTED" };
|
|
1826
|
+
}
|
|
1827
|
+
return { allowed: true };
|
|
1828
|
+
}
|
|
1829
|
+
function evaluateActionPermission(action, config, currentUrl, target) {
|
|
1830
|
+
const explicitRule = config.rules.find((rule) => rule.action === action && rule.enabled !== false);
|
|
1831
|
+
if (explicitRule && typeof explicitRule.allowed === "boolean") {
|
|
1832
|
+
if (!explicitRule.allowed) {
|
|
1833
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (!config.allowedActions.includes(action)) {
|
|
1837
|
+
return { allowed: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1838
|
+
}
|
|
1839
|
+
if (action === "navigate") {
|
|
1840
|
+
if (!target) {
|
|
1841
|
+
return { allowed: true };
|
|
1842
|
+
}
|
|
1843
|
+
return canNavigateTo(config, target, currentUrl);
|
|
1844
|
+
}
|
|
1845
|
+
const allowedDomains = config.allowedDomains;
|
|
1846
|
+
if (allowedDomains.length > 0) {
|
|
1847
|
+
const domainCheck = validateWebsiteDomain(config, currentUrl);
|
|
1848
|
+
if (!domainCheck.allowed) {
|
|
1849
|
+
return domainCheck;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return { allowed: true };
|
|
1853
|
+
}
|
|
1854
|
+
function isActionAllowed(action, config, source, currentUrl, target) {
|
|
1639
1855
|
if (source === "developer") {
|
|
1640
1856
|
return true;
|
|
1641
1857
|
}
|
|
1642
|
-
|
|
1858
|
+
if (currentUrl) {
|
|
1859
|
+
return evaluateActionPermission(action, config, currentUrl, target).allowed;
|
|
1860
|
+
}
|
|
1861
|
+
return config.allowedActions.includes(action) && !config.rules.some((rule) => rule.action === action && rule.allowed === false);
|
|
1643
1862
|
}
|
|
1644
|
-
function assertActionAllowed(action, config, source) {
|
|
1645
|
-
|
|
1863
|
+
function assertActionAllowed(action, config, source, currentUrl, target) {
|
|
1864
|
+
const allowed = isActionAllowed(action, config, source, currentUrl, target);
|
|
1865
|
+
if (!allowed) {
|
|
1646
1866
|
throw new ActionNotAllowedError(`Action "${action}" is not permitted for this project`);
|
|
1647
1867
|
}
|
|
1648
1868
|
}
|
|
@@ -1672,7 +1892,12 @@ var InteractionEngine = class {
|
|
|
1672
1892
|
}
|
|
1673
1893
|
async execute(step, source) {
|
|
1674
1894
|
const config = this.getConfig();
|
|
1675
|
-
|
|
1895
|
+
const currentUrl = this.win.location.href;
|
|
1896
|
+
const target = step.url ?? (typeof step.target === "string" ? step.target : void 0);
|
|
1897
|
+
const allowed = this.evaluatePermission(step.action, config, currentUrl, target, source);
|
|
1898
|
+
if (!allowed.ok) {
|
|
1899
|
+
return { ok: false, waitedForUser: false, allowed: false, reason: allowed.reason ?? "ACTION_NOT_PERMITTED" };
|
|
1900
|
+
}
|
|
1676
1901
|
const wait = shouldWaitForUser(step.action, config.mode, step.waitForUser);
|
|
1677
1902
|
switch (step.action) {
|
|
1678
1903
|
case "explain":
|
|
@@ -1703,6 +1928,14 @@ var InteractionEngine = class {
|
|
|
1703
1928
|
throw new TaskExecutionError("Unsupported action");
|
|
1704
1929
|
}
|
|
1705
1930
|
}
|
|
1931
|
+
evaluatePermission(action, config, currentUrl, target, source) {
|
|
1932
|
+
try {
|
|
1933
|
+
assertActionAllowed(action, config, source, currentUrl, target);
|
|
1934
|
+
return { ok: true };
|
|
1935
|
+
} catch {
|
|
1936
|
+
return { ok: false, reason: "ACTION_NOT_PERMITTED" };
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1706
1939
|
async highlight(step, source, wait) {
|
|
1707
1940
|
if (!step.target) {
|
|
1708
1941
|
throw new TaskExecutionError("Highlight requires a target");
|
|
@@ -1886,6 +2119,11 @@ var TaskEngine = class {
|
|
|
1886
2119
|
}
|
|
1887
2120
|
this.move(TaskStatus.ActionStarted);
|
|
1888
2121
|
const result = await this.interaction.execute(step, source);
|
|
2122
|
+
if (!result.ok) {
|
|
2123
|
+
this.events.emit("asiyst:task:failed", { taskId: task.id, reason: result.reason ?? "ACTION_NOT_PERMITTED" });
|
|
2124
|
+
this.analytics.track("task_failed", { taskId: task.id });
|
|
2125
|
+
return this.finish(TaskStatus.Failed, result.reason ?? "ACTION_NOT_PERMITTED");
|
|
2126
|
+
}
|
|
1889
2127
|
if (result.waitedForUser && result.elementId) {
|
|
1890
2128
|
this.move(TaskStatus.WaitingForUser);
|
|
1891
2129
|
const clicked = await this.interaction.waitForElementClick(result.elementId, step.timeoutMs ?? 3e4);
|
|
@@ -2131,7 +2369,8 @@ var AsiystRuntime = class {
|
|
|
2131
2369
|
const transport = new HttpTransport({
|
|
2132
2370
|
apiBaseUrl: options.apiBaseUrl ?? DEFAULT_API_BASE_URL,
|
|
2133
2371
|
projectId: options.projectId,
|
|
2134
|
-
publicKey: options.publicKey
|
|
2372
|
+
publicKey: options.publicKey,
|
|
2373
|
+
avatarId: options.avatarId
|
|
2135
2374
|
});
|
|
2136
2375
|
this.cloud = new CloudClient(transport, options.projectId);
|
|
2137
2376
|
this.config = new ConfigManager(options, this.cloud, this.events);
|
|
@@ -2188,6 +2427,15 @@ var AsiystRuntime = class {
|
|
|
2188
2427
|
async start() {
|
|
2189
2428
|
this.events.emit("asiyst:initialized", { projectId: this.options.projectId });
|
|
2190
2429
|
const cfg = await isolateAsync(() => this.config.refresh(), this.config.get());
|
|
2430
|
+
const domainCheck = validateWebsiteDomain(cfg, window.location.href);
|
|
2431
|
+
if (!domainCheck.allowed) {
|
|
2432
|
+
this.connectionStatus = "disconnected";
|
|
2433
|
+
this.events.emit("asiyst:error", {
|
|
2434
|
+
code: "domain_not_authorized",
|
|
2435
|
+
message: "This website is not authorized for the current Asiyst project."
|
|
2436
|
+
});
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2191
2439
|
this.avatar.applyConfig(cfg);
|
|
2192
2440
|
this.avatar.show();
|
|
2193
2441
|
try {
|
|
@@ -2382,6 +2630,6 @@ var Asiyst = {
|
|
|
2382
2630
|
}
|
|
2383
2631
|
};
|
|
2384
2632
|
|
|
2385
|
-
export { ActionNotAllowedError, Asiyst, AsiystError, AuthenticationError, ConfigurationError, InitializationError, NetworkError, SDK_VERSION, TargetNotFoundError, TaskExecutionError, TaskStatus, anchorToViewport, canTransition, computeAvatarDestination, fallbackConfig, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions };
|
|
2633
|
+
export { ActionNotAllowedError, Asiyst, AsiystError, AuthenticationError, ConfigurationError, InitializationError, NetworkError, SDK_VERSION, TargetNotFoundError, TaskExecutionError, TaskStatus, anchorToViewport, canNavigateTo, canTransition, computeAvatarDestination, evaluateActionPermission, fallbackConfig, isActionAllowed, isSafeSelector, normalizeProjectConfig, sanitizeText, validateInitOptions, validateWebsiteDomain };
|
|
2386
2634
|
//# sourceMappingURL=index.js.map
|
|
2387
2635
|
//# sourceMappingURL=index.js.map
|