
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
obi-loader
Advanced tools
A lightweight loader for the Obi Assistant widget. This package is designed to be as small as possible while still providing the functionality to load the full Obi Assistant from a CDN.
Lightweight Loader: Use this small package to dynamically load the full SDK from a CDN
Benefits of the lightweight loader:
npm install obi-loader
The package includes full TypeScript support with type definitions for all configuration options.
import { initObi, updateObi, openObiCourses, type ObiAssistantConfig, type ObiUpdateConfig } from "obi-loader"
// Type-safe configuration
const config: ObiAssistantConfig = {
apiKey: "your-api-key",
position: "bottom-right",
user: {
id: "user-123",
email: "user@example.com",
first_name: "John",
last_name: "Doe",
company: "Acme Corp",
metadata: {
plan: "premium",
},
},
}
initObi(config)
// Type-safe updates using the dedicated function (NPM only)
updateObi({
position: "bottom-left",
primaryColor: "#ff6b35",
user: {
id: "user-123",
email: "updated@example.com",
first_name: "Jane",
last_name: "Smith",
company: "TechCorp Inc",
metadata: { plan: "enterprise" },
},
// apiKey: "new-key" // ❌ TypeScript error - cannot update apiKey
})
// Open course menu with one function call
openObiCourses()
// Or use the global function if preferred
window.ObiSDK("update", {
position: "top-right",
primaryColor: "#e91e63",
})
If you're using the CDN script approach but want TypeScript support in your development environment, you can install the package just for types:
npm install --save-dev obi-loader
The package now includes global type declarations, so you automatically get type safety for both configuration and updates:
// Your TypeScript code with full type safety
window.obiWidgetConfig = {
apiKey: "your-api-key", // ✅ Type-safe
position: "bottom-right", // ✅ Autocomplete works
user: {
id: "user-123",
metadata: { plan: "premium" },
},
}
// Type-safe updates (apiKey automatically excluded)
window.ObiSDK("update", {
position: "top-right", // ✅ Autocomplete for valid positions
primaryColor: "#e91e63", // ✅ Type-checked
// apiKey: "new-key" // ❌ TypeScript error - cannot update apiKey
})
For detailed TypeScript type information and usage examples, see the TypeScript Support section above.
import { initObi } from "obi-loader"
// Initialize Obi with your configuration
initObi({
apiKey: "your-api-key",
position: "bottom-right",
user: {
id: "user-123",
email: "user@example.com",
metadata: {
plan: "premium",
},
},
})
initObi(config: ObiAssistantConfig)Initializes the Obi widget with the provided configuration. This should be called once when your application loads.
updateObi(config: Partial<ObiUpdateConfig>) (NPM only)Updates the widget configuration after initialization. This is a type-safe alternative to using window.ObiSDK("update", config).
import { updateObi } from "obi-loader"
updateObi({
position: "bottom-left",
primaryColor: "#ff6b35",
user: {
id: "user-123",
email: "updated@example.com",
},
})
Note: The apiKey, urlBlacklist, and linkOnlyAccess options cannot be updated after initialization.
openObiCourses() (NPM only)Opens the Obi course menu programmatically. This is a convenience function equivalent to updateObi({ showMenu: true }).
import { openObiCourses } from "obi-loader"
// In React
function TrainingButton() {
return <button onClick={openObiCourses}>View Training Courses</button>
}
// In Vue
export default {
methods: {
openCourseMenu: openObiCourses,
},
}
// In vanilla JavaScript
document.getElementById("training-btn").addEventListener("click", openObiCourses)
For CDN users: Use window.ObiSDK("update", { showMenu: true }) instead.
If you prefer not to install the npm package, you can use the CDN script approach directly in your HTML. This method has the same lightweight benefits and automatically loads the latest Obi SDK.
<!DOCTYPE html>
<html>
<head>
<title>Your Website</title>
</head>
<body>
<!-- Your website content -->
<!-- Configure Obi -->
<script>
window.obiWidgetConfig = {
apiKey: "your-api-key",
position: "bottom-right",
}
</script>
<!-- Load Obi Loader Script -->
<script>
!(function () {
"use strict"
var t = window
!(function () {
var n, o
try {
;(n = new URLSearchParams(location.search)),
(o = {}),
n.forEach(function (t, n) {
o[n] = t
}),
Object.keys(o).length && localStorage.setItem("obi-url-params", JSON.stringify(o))
} catch (t) {}
;(t.ObiSDK =
t.ObiSDK ||
function () {
return (t.ObiSDK.q = t.ObiSDK.q || []), t.ObiSDK.q.push(arguments)
}),
t.ObiSDK("update", t.obiWidgetConfig || {}),
fetch("https://registry.npmjs.org/obi-sdk/latest")
.then(function (t) {
return t.json()
})
.then(function (t) {
return t.version
})
.catch(function () {
return "latest"
})
.then(function (t) {
var n = document.createElement("script")
;(n.defer = !0),
(n.src = "https://unpkg.com/obi-sdk@" + t + "/dist/obi-sdk.standalone.iife.js"),
document.head.appendChild(n)
})
})()
})()
</script>
</body>
</html>
The initObi function accepts the following configuration options:
| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your Obi API key |
| position | string | No | Widget position on the screen. Options: 'bottom-right', 'bottom-left', 'bottom-center', 'top-right', 'top-left', 'top-center', 'middle-left', 'middle-right'. Default: 'bottom-right' |
| user | object | No | User information object containing: |
- id: string (required) - Unique identifier for the user | |||
- email: string (optional) - User's email address | |||
- first_name: string (optional) - User's first name | |||
- last_name: string (optional) - User's last name | |||
- company: string (optional) - User's company name | |||
- metadata: object (optional) - Additional custom user data | |||
| isActive | boolean | No | Whether the widget should be active on load. Can be used to filter experience to certain users. Default: true |
| linkOnlyAccess | boolean | No | Hide the widget unless accessed via a session link. Perfect for trial periods where only customers with direct links should see the widget. Initialization only - cannot be updated after widget loads. Default: false |
| showMenu | boolean | No | Manually show the widget menu. If the widget is not mounted, it will mount the widget and show the menu. Default: false |
| primaryColor | string | No | Custom primary color (hex code, e.g. '#9500ff'). Default: '#9500ff' |
| urlBlacklist | string[] | No | Array of URL glob patterns where the widget should not appear. Supports wildcards (* and **) for flexible URL matching. Initialization only - cannot be updated after widget loads. Default: [] |
initObi({
apiKey: "your-api-key",
position: "bottom-right",
isActive: true,
linkOnlyAccess: false, // Set to true for trial mode
showMenu: false, // Set to true to mount widget and show menu immediately
primaryColor: "#123456", // Custom primary color
urlBlacklist: [
"example.com/admin*", // Block all admin pages
"example.com/private/**", // Block all private section pages
"*/checkout", // Block checkout pages on any subdomain
"staging.*.com/**", // Block all staging environments
],
user: {
id: "user-123",
email: "user@example.com",
first_name: "John",
last_name: "Doe",
company: "Acme Inc", // Now a primary field!
metadata: {
plan: "premium",
role: "admin",
// ... any additional custom user data
},
},
})
The urlBlacklist option allows you to specify URL patterns where the widget should not appear. This is useful for excluding admin pages, checkout flows, or other sensitive areas of your website.
The blacklist supports glob patterns for flexible URL matching:
* - matches any characters except forward slashes** - matches any characters including forward slashes? - matches any single characterwindow.obiWidgetConfig = {
apiKey: "YOUR_API_KEY",
urlBlacklist: [
"example.com/admin*", // Block all admin pages
"example.com/checkout/**", // Block entire checkout flow
"*/login", // Block login pages on any subdomain
"staging.*.com/**", // Block all staging environments
"example.com/dashboard/billing", // Block specific billing page
],
}
Once the Obi widget is loaded, you can dynamically update its configuration using the global ObiSDK function.
For detailed TypeScript type information and usage examples, see the TypeScript Support section above.
// Update user information
window.ObiSDK("update", {
user: {
id: "user-456",
email: "updated@example.com",
metadata: {
subscription: "premium",
lastActivity: Date.now(),
},
},
})
// Update widget position
window.ObiSDK("update", {
position: "bottom-left",
})
// Toggle widget visibility
window.ObiSDK("update", {
isActive: false,
})
// Show menu (will mount widget if not already mounted)
window.ObiSDK("update", {
showMenu: true,
})
// Update primary colour
window.ObiSDK("update", {
primaryColor: "#ff6b35",
})
// Update multiple config options at once
window.ObiSDK("update", {
position: "top-right",
primaryColor: "#00bcd4",
isActive: true,
showMenu: true,
user: {
id: "user-123",
email: "newemail@example.com",
metadata: {
plan: "enterprise",
lastLogin: new Date().toISOString(),
},
},
})
When using the npm package, you can use either the dedicated updateObi function or the global function:
import { initObi, updateObi } from "obi-loader"
// After initialization
initObi({
apiKey: "your-api-key",
user: { id: "user-123" },
})
// Option 1: Use the dedicated updateObi function (cleaner for TypeScript)
updateObi({
user: {
id: "user-123",
email: "newemail@example.com",
first_name: "John",
last_name: "Doe",
company: "Enterprise Corp",
metadata: {
plan: "enterprise",
},
},
position: "bottom-left",
primaryColor: "#ff6b35",
showMenu: true, // Mount widget and show menu
})
// Option 2: Use the global function (works everywhere)
window.ObiSDK("update", {
position: "top-right",
primaryColor: "#00bcd4",
})
When using the CDN script approach, you can update configuration using the global ObiSDK function:
<script>
// Update configuration dynamically
window.ObiSDK("update", {
primaryColor: "#e91e63",
position: "middle-right",
isActive: true,
showMenu: false,
})
// Update user context
window.ObiSDK("update", {
user: {
id: "current-user-id",
email: "user@company.com",
first_name: "Alex",
last_name: "Johnson",
company: "Tech Solutions Inc",
metadata: {
role: "admin",
department: "engineering",
},
},
})
</script>
All configuration options from the initial setup can be updated dynamically:
| Updatable Option | Method | Description |
|---|---|---|
| user | update({ user }) | Update user ID, email, and metadata |
| position | update({ position }) | Change widget position on screen |
| isActive | update({ isActive }) | Show/hide widget |
| showMenu | update({ showMenu }) | Show widget menu (mounts widget if needed) |
| primaryColor | update({ primaryColor }) | Change the widget's primary colour |
Note: The apiKey, urlBlacklist, and linkOnlyAccess cannot be updated after initialization. If you need to change these values, you'll need to reinitialize the widget.
Beyond configuration updates, the Obi widget supports several programmatic commands for controlling sessions and interactions. These commands are available through the global window.ObiSDK() function.
Start an onboarding session programmatically. You can optionally specify which plan to launch and whether to enable the microphone automatically.
NPM Installation:
// Start a session with a specific plan and enable microphone
window.ObiSDK("startSession", {
planUuid: "your-plan-uuid-here",
withMicrophone: true,
})
// Start a session without specifying a plan (user can choose)
window.ObiSDK("startSession")
// Start with microphone enabled but no specific plan
window.ObiSDK("startSession", { withMicrophone: true })
CDN Installation:
<!-- Button to start session with specific plan -->
<button onclick="window.ObiSDK('startSession', { planUuid: 'your-plan-uuid-here', withMicrophone: true })">
Start Training
</button>
<!-- Button to start session without specific plan -->
<button onclick="window.ObiSDK('startSession')">Launch Obi</button>
Parameters:
planUuid (optional): The UUID of the specific onboarding plan to start. If not provided, the user can select from available plans.withMicrophone (optional): Boolean to automatically enable the user's microphone when the session starts. Defaults to false.Notes:
planUuid is provided, the SDK validates it against available plans for the user. If the plan is not found or not accessible, a warning is logged.isActive to true, making the widget visible.Send a text message to the Obi agent during an active session. This is useful for programmatically triggering specific interactions or providing context.
NPM Installation:
// Send a message to the agent
window.ObiSDK("say", "Can you help me with the dashboard features?")
// Send contextual help request
window.ObiSDK("say", "I'm stuck on the checkout page")
CDN Installation:
<!-- Button to request help on specific topic -->
<button onclick="window.ObiSDK('say', 'Show me how to create a new project')">Help: Create Project</button>
<!-- Link to ask about current page -->
<a href="javascript:window.ObiSDK('say', 'Explain this page to me')">Get Help</a>
Parameters:
message (required): A string containing the message to send to the agent.Notes:
End the current onboarding session programmatically.
NPM Installation:
// Stop the current session
window.ObiSDK("stopSession")
CDN Installation:
<!-- Button to end session -->
<button onclick="window.ObiSDK('stopSession')">End Training</button>
Parameters:
None.
Notes:
isActive is also set to false.All commands are fully typed with function overloads for autocomplete and type safety:
// TypeScript will provide autocomplete and type checking
window.ObiSDK("update", { showMenu: true }) // ✓ Valid
window.ObiSDK("startSession", { planUuid: "uuid", withMicrophone: true }) // ✓ Valid
window.ObiSDK("say", "Hello") // ✓ Valid
window.ObiSDK("stopSession") // ✓ Valid
// TypeScript will catch errors
window.ObiSDK("unknownCommand") // ❌ TypeScript error
window.ObiSDK("say", 123) // ❌ TypeScript error - message must be string
window.ObiSDK("startSession", { invalidOption: true }) // ❌ TypeScript error
Trigger help based on user actions or page context:
// React: Help button on specific page
function CheckoutPage() {
const handleHelp = () => {
window.ObiSDK("startSession", { withMicrophone: true })
window.ObiSDK("say", "I need help completing my purchase")
}
return <button onClick={handleHelp}>Get Checkout Help</button>
}
// Vue: Contextual help link
export default {
methods: {
requestHelp() {
window.ObiSDK("say", `Help me with ${this.currentFeature}`)
},
},
}
Create custom UI for launching specific training sessions:
// Training dashboard with multiple courses
const trainingCourses = [
{ id: "plan-uuid-1", name: "Getting Started", duration: "5 min" },
{ id: "plan-uuid-2", name: "Advanced Features", duration: "10 min" },
{ id: "plan-uuid-3", name: "Best Practices", duration: "8 min" },
]
function TrainingDashboard() {
const startCourse = (planUuid: string) => {
window.ObiSDK("startSession", {
planUuid,
withMicrophone: true,
})
}
return (
<div>
{trainingCourses.map((course) => (
<button key={course.id} onClick={() => startCourse(course.id)}>
{course.name} ({course.duration})
</button>
))}
</div>
)
}
Automatically start or stop sessions based on time or user activity:
// Start onboarding after user has been inactive for 2 minutes
let inactivityTimer: NodeJS.Timeout
function resetInactivityTimer() {
clearTimeout(inactivityTimer)
inactivityTimer = setTimeout(() => {
window.ObiSDK("startSession")
window.ObiSDK("say", "It looks like you might need some help. How can I assist you?")
}, 120000) // 2 minutes
}
// Reset timer on user activity
document.addEventListener("mousemove", resetInactivityTimer)
document.addEventListener("keypress", resetInactivityTimer)
// Auto-stop session after 30 minutes
function startTimedSession() {
window.ObiSDK("startSession")
setTimeout(() => {
window.ObiSDK("stopSession")
alert("Your training session has ended due to time limit.")
}, 1800000) // 30 minutes
}
Build an interactive help system that guides users through tasks:
// Multi-step guided tour
const tourSteps = [
"Let me show you around. First, let's look at your dashboard.",
"Now, let me explain how to create a new project.",
"Next, I'll show you how to invite team members.",
"Finally, let's review the settings page.",
]
function startGuidedTour() {
window.ObiSDK("startSession", { withMicrophone: false })
tourSteps.forEach((message, index) => {
setTimeout(() => {
window.ObiSDK("say", message)
}, index * 5000) // 5 seconds between steps
})
}
// Error recovery helper
function onError(errorType: string, errorDetails: any) {
window.ObiSDK("startSession")
window.ObiSDK(
"say",
`I encountered an error: ${errorType}. Can you help me understand what went wrong? Details: ${JSON.stringify(errorDetails)}`
)
}
NPM Installation:
import { openObiCourses } from "obi-loader"
// React
function NavigationBar() {
return (
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<button onClick={openObiCourses}>Training</button>
</nav>
)
}
// Vue
export default {
methods: {
openTraining: openObiCourses,
},
}
CDN Installation:
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="javascript:window.ObiSDK && window.ObiSDK('update', {showMenu: true})">Training</a>
</nav>
// Update user context when user logs in
function onUserLogin(userData) {
updateObi({
user: {
id: userData.id,
email: userData.email,
metadata: {
plan: userData.subscription.plan,
joinDate: userData.createdAt,
},
},
})
}
// Update user context when user upgrades plan
function onPlanUpgrade(newPlan) {
updateObi({
user: {
id: currentUser.id,
email: currentUser.email,
metadata: {
...currentUser.metadata,
plan: newPlan,
upgradeDate: new Date().toISOString(),
},
},
})
}
// Show widget only for certain user types
function updateWidgetVisibility(user) {
updateObi({
isActive: user.plan === "premium" || user.role === "admin",
})
}
// Hide widget during checkout process
function enterCheckout() {
updateObi({ isActive: false })
}
function exitCheckout() {
updateObi({ isActive: true })
}
This package doesn't bundle the full Obi SDK. Instead, it:
obi-sdk packageThis approach ensures your application always uses the latest version of Obi while keeping your bundle size minimal.
| Package | Minified Size |
|---|---|
| obi-sdk | ~200KB |
| obi-loader | ~5KB |
See LICENSE.txt for details.
FAQs
Lightweight loader for Obi Assistant
We found that obi-loader demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.