{"id":31779587,"url":"https://github.com/vbrazo/upriser-agents","last_synced_at":"2026-01-20T17:28:52.454Z","repository":{"id":316165804,"uuid":"1062253039","full_name":"vbrazo/upriser-agents","owner":"vbrazo","description":"A standalone JavaScript widget for integrating Upriser ElevenLabs ConvAI into any website.","archived":false,"fork":false,"pushed_at":"2025-09-23T03:16:51.000Z","size":7382,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-23T04:23:12.378Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vbrazo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-23T02:47:15.000Z","updated_at":"2025-09-23T03:19:07.000Z","dependencies_parsed_at":"2025-09-23T04:23:15.678Z","dependency_job_id":"254d2352-8ddd-45e7-a89f-215e1ea09675","html_url":"https://github.com/vbrazo/upriser-agents","commit_stats":null,"previous_names":["vbrazo/upriser-agent-code.js"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/vbrazo/upriser-agents","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vbrazo%2Fupriser-agents","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vbrazo%2Fupriser-agents/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vbrazo%2Fupriser-agents/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vbrazo%2Fupriser-agents/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vbrazo","download_url":"https://codeload.github.com/vbrazo/upriser-agents/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vbrazo%2Fupriser-agents/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003173,"owners_count":26083533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-10T07:52:04.027Z","updated_at":"2025-10-10T07:52:11.513Z","avatar_url":"https://github.com/vbrazo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Upriser Widget\n\nA standalone JavaScript widget for integrating Upriser ConvAI into any website. This package provides both npm module support and raw JavaScript files for maximum flexibility.\n\n## 📚 Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [NPM Module Usage](#npm-module-usage)\n  - [Raw JavaScript Usage](#raw-javascript-usage)\n- [🚀 Modal \u0026 Client Tools (NEW!)](#-modal--client-tools-new)\n- [🎨 Whitelabel Features](#-whitelabel-features)\n- [Configuration Options](#configuration-options)\n- [API Methods](#api-methods)\n- [React Integration Example](#react-integration-example)\n- [🎯 Client Tools Use Cases](#-client-tools-use-cases)\n- [Vue Integration Example](#vue-integration-example)\n- [📡 Event Handling](#-event-handling)\n- [Browser Support](#browser-support)\n- [Features](#features)\n- [Development](#development)\n- [🔄 Continuous Integration (CI)](#-continuous-integration-ci)\n- [File Structure](#file-structure)\n- [License](#license)\n- [Support](#support)\n\n## Installation\n\n### Option 1: NPM Package (Recommended for React, Vue, Angular, etc.)\n\n```bash\nnpm install @upriser.ai/widget\n```\n\n### Option 2: CDN / Direct Script Inclusion\n\n```html\n\u003c!-- Include directly in your HTML --\u003e\n\u003cscript src=\"https://unpkg.com/@upriser.ai/widget\"\u003e\u003c/script\u003e\n\n\u003c!-- Or download and host yourself --\u003e\n\u003cscript src=\"path/to/upriser-widget.js\"\u003e\u003c/script\u003e\n```\n\n## Usage\n\n### NPM Module Usage\n\n#### ES Modules (Modern bundlers like Webpack, Vite, etc.)\n\n```javascript\nimport UpriserWidget from \"@upriser.ai/widget/dist/upriser-widget.js\";\n\n// Create and initialize the widget\nconst widget = new UpriserWidget({\n  agentId: \"your-agent-id-here\",\n  fontColor: \"#ffffff\",\n  linkColor: \"#007bff\",\n});\n\n// Initialize the widget\nwidget\n  .init()\n  .then(() =\u003e {\n    console.log(\"Upriser widget initialized successfully!\");\n  })\n  .catch((error) =\u003e {\n    console.error(\"Failed to initialize widget:\", error);\n  });\n```\n\n#### CommonJS (Node.js style)\n\n```javascript\nimport UpriserWidget from \"@upriser.ai/widget/dist/upriser-widget.js\";\n\nconst widget = new UpriserWidget({\n  agentId: \"your-agent-id-here\",\n  debug: false,\n});\n\nwidget.init();\n```\n\n#### TypeScript Support\n\nThe package includes full TypeScript definitions:\n\n```typescript\nimport UpriserWidget, { UpriserWidgetConfig } from \"@upriser.ai/widget\";\n\nconst config: UpriserWidgetConfig = {\n  agentId: \"your-agent-id-here\",\n};\n\nconst widget = new UpriserWidget(config);\nwidget.init();\n```\n\n### Raw JavaScript Usage\n\n#### Basic Usage\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eMy Website\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003c!-- Your website content --\u003e\n\n    \u003c!-- Include the Upriser widget script --\u003e\n    \u003cscript src=\"https://unpkg.com/@upriser.ai/widget\"\u003e\u003c/script\u003e\n\n    \u003cscript\u003e\n      // The widget will auto-initialize with default settings\n      // Or manually initialize:\n\n      // Option 1: Use the global instance (auto-created)\n      if (window.upriserWidget) {\n        console.log(\"Widget auto-initialized\");\n      }\n\n      // Option 2: Create your own instance\n      const myWidget = new UpriserWidget({\n        agentId: \"your-custom-agent-id\",\n        fontColor: \"#ffffff\",\n        linkColor: \"#007bff\",\n      });\n      myWidget.init();\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### Custom Configuration\n\n```html\n\u003cscript\u003e\n  // Configure before the script loads (for auto-initialization)\n  window.UPRISER_WIDGET_CONFIG = {\n    agentId: \"your-agent-id-here\",\n  };\n\n  // Disable auto-initialization if you want full control\n  // window.UPRISER_DISABLE_AUTO_INIT = true;\n\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/@upriser.ai/widget\"\u003e\u003c/script\u003e\n```\n\n#### WordPress Integration\n\n```php\n// In your WordPress theme's functions.php\nfunction add_upriser_widget() {\n    ?\u003e\n    \u003cscript\u003e\n        window.UPRISER_WIDGET_CONFIG = {\n            agentId: '\u003c?php echo get_option('upriser_agent_id', 'agent_8401k5nnvgqpezf9fd17t3tb7t69'); ?\u003e'\n        };\n    \u003c/script\u003e\n    \u003cscript src=\"https://unpkg.com/@upriser.ai/widget\"\u003e\u003c/script\u003e\n    \u003c?php\n}\nadd_action('wp_footer', 'add_upriser_widget');\n```\n\n## 🚀 Modal \u0026 Client Tools (NEW!)\n\nUpriser Widget now supports advanced modal functionality and client tools for interactive experiences like demo scheduling, vehicle browsing, and custom content display!\n\n### Built-in Modal System\n\nThe widget includes a built-in modal system that can be triggered by the AI agent or programmatically:\n\n```javascript\nconst widget = new UpriserWidget({\n  agentId: \"your-agent-id\",\n  clientTools: {\n    show_demo_form: (parameters) =\u003e {\n      const url = parameters?.url || \"https://meetings.hubspot.com/default\";\n      return widget.openModal({\n        url,\n        title: \"Schedule a Demo\",\n        onClose: () =\u003e console.log(\"Demo modal closed\"),\n      });\n    },\n  },\n});\n```\n\n### Client Tools System\n\nClient tools are JavaScript functions that the AI agent can call to perform specific actions:\n\n```javascript\n// Option 1: Global client tools\nwindow.UPRISER_CLIENT_TOOLS = {\n  show_demo_form: (parameters) =\u003e {\n    const url = parameters?.url || \"https://meetings.hubspot.com/default\";\n    // Open your custom modal or use the built-in one\n    return { success: true, message: \"Demo form opened\" };\n  },\n\n  show_vehicle_page: (parameters) =\u003e {\n    if (!parameters?.url) {\n      return { success: false, message: \"URL required\" };\n    }\n    // Open vehicle details modal\n    return { success: true, message: \"Vehicle page opened\" };\n  },\n};\n\n// Option 2: Widget configuration\nconst widget = new UpriserWidget({\n  agentId: \"your-agent-id\",\n  clientTools: {\n    // Your client tools here\n  },\n});\n```\n\n## 🎨 Whitelabel Features\n\nUpriser Widget supports complete whitelabeling with custom branding and colors!\n\n### Custom HTML Element\n\n```html\n\u003cupriser-convai\n  agent-id=\"agent_8401k5nnvgqpezf9fd17t3tb7t69\"\n  font-color=\"#ffffff\"\n  link-color=\"#007bff\"\n\u003e\n\u003c/upriser-convai\u003e\n\n\u003cscript src=\"https://unpkg.com/@upriser.ai/widget\"\u003e\u003c/script\u003e\n```\n\n### Custom Element Attributes\n\n| Attribute    | Type   | Default   | Description                            |\n| ------------ | ------ | --------- | -------------------------------------- |\n| `agent-id`   | string | Required  | Upriser agent ID                       |\n| `font-color` | string | `#ffffff` | Color for \"Powered by Upriser.ai\" text |\n| `link-color` | string | `#ffffff` | Color for links to Upriser.ai          |\n\n### Dynamic Color Changes\n\nYou can change colors dynamically using JavaScript:\n\n```javascript\n// Get the widget element\nconst widget = document.querySelector(\"upriser-convai\");\n\n// Change colors\nwidget.setAttribute(\"font-color\", \"#ff6b6b\");\nwidget.setAttribute(\"link-color\", \"#4ecdc4\");\n```\n\n### JavaScript Class with Colors\n\nUse the UpriserWidget class with custom colors:\n\n```javascript\nconst widget = new UpriserWidget({\n  agentId: \"agent_8401k5nnvgqpezf9fd17t3tb7t69\",\n  fontColor: \"#ffffff\",\n  linkColor: \"#007bff\",\n});\n\nwidget.init();\n```\n\n### Color Examples\n\n```html\n\u003c!-- Blue Theme --\u003e\n\u003cupriser-convai\n  agent-id=\"agent_8401k5nnvgqpezf9fd17t3tb7t69\"\n  font-color=\"#ffffff\"\n  link-color=\"#007bff\"\n\u003e\n\u003c/upriser-convai\u003e\n\n\u003c!-- Green Theme --\u003e\n\u003cupriser-convai\n  agent-id=\"agent_8401k5nnvgqpezf9fd17t3tb7t69\"\n  font-color=\"#ffffff\"\n  link-color=\"#28a745\"\n\u003e\n\u003c/upriser-convai\u003e\n\n\u003c!-- Purple Theme --\u003e\n\u003cupriser-convai\n  agent-id=\"agent_8401k5nnvgqpezf9fd17t3tb7t69\"\n  font-color=\"#f8f9fa\"\n  link-color=\"#6f42c1\"\n\u003e\n\u003c/upriser-convai\u003e\n```\n\n### Benefits of Whitelabel Version\n\n- ✅ **Custom Colors** - Match your brand colors exactly\n- ✅ **Dynamic Updates** - Change colors on the fly\n- ✅ **Better Events** - Enhanced event handling and API\n\n## Configuration Options\n\n| Option            | Type                     | Default                                | Description                                               |\n| ----------------- | ------------------------ | -------------------------------------- | --------------------------------------------------------- |\n| `agentId`         | string                   | `'agent_8401k5nnvgqpezf9fd17t3tb7t69'` | Upriser agent ID to use                                   |\n| `widgetContainer` | HTMLElement \\| null      | `null`                                 | Container to append widget to (defaults to document.body) |\n| `debug`           | boolean                  | `false`                                | Enable debug logging                                      |\n| `fontColor`       | string                   | `'#ffffff'`                            | Custom color for \"Powered by Upriser.ai\" text             |\n| `linkColor`       | string                   | `'#ffffff'`                            | Custom color for links to Upriser.ai                      |\n| `clientTools`     | Record\u003cstring, Function\u003e | `{}`                                   | **NEW!** Custom functions that the AI agent can call      |\n\n## API Methods\n\n### `widget.init()`\n\nInitializes the widget. Returns a Promise.\n\n```javascript\nawait widget.init();\n```\n\n### `widget.updateConfig(newConfig)`\n\nUpdates the widget configuration.\n\n```javascript\nwidget.updateConfig({ debug: true, agentId: \"new-agent-id\" });\n```\n\n### `widget.test()`\n\nTests widget functionality and returns status.\n\n```javascript\nconst status = widget.test();\nconsole.log(\"Widget status:\", status);\n```\n\n### `widget.openModal(options)`\n\n**NEW!** Opens a modal with the specified URL and options.\n\n```javascript\nconst result = widget.openModal({\n  url: \"https://meetings.hubspot.com/demo\",\n  title: \"Schedule a Demo\",\n  onClose: () =\u003e console.log(\"Modal closed\"),\n});\nconsole.log(result);\n// { success: true, message: \"Modal opened successfully\" }\n```\n\n### `widget.setClientTools(clientTools)`\n\n**NEW!** Updates the client tools available to the AI agent.\n\n```javascript\nwidget.setClientTools({\n  show_demo_form: (parameters) =\u003e {\n    const url = parameters?.url || \"https://meetings.hubspot.com/default\";\n    return widget.openModal({ url, title: \"Schedule Demo\" });\n  },\n  show_vehicle_page: (parameters) =\u003e {\n    if (!parameters?.url) {\n      return { success: false, message: \"URL required\" };\n    }\n    return widget.openModal({\n      url: parameters.url,\n      title: parameters.title || \"Vehicle Details\",\n    });\n  },\n});\n```\n\n### `widget.getClientTools()`\n\n**NEW!** Returns the current client tools configuration.\n\n```javascript\nconst tools = widget.getClientTools();\nconsole.log(\"Available tools:\", Object.keys(tools));\n```\n\n### `widget.destroy()`\n\nDestroys the widget and cleans up all resources.\n\n```javascript\nwidget.destroy();\n```\n\n## React Integration Example\n\n```jsx\nimport React, { useEffect, useRef } from \"react\";\nimport UpriserWidget from \"@upriser.ai/widget/dist/upriser-widget.js\";\n\nfunction ChatWidget({ agentId, debug = false }) {\n  const widgetRef = useRef(null);\n  const containerRef = useRef(null);\n\n  useEffect(() =\u003e {\n    if (containerRef.current) {\n      const widget = new UpriserWidget({\n        agentId,\n        debug,\n        widgetContainer: containerRef.current,\n      });\n\n      widget.init().catch(console.error);\n      widgetRef.current = widget;\n\n      return () =\u003e {\n        if (widgetRef.current) {\n          widgetRef.current.destroy();\n        }\n      };\n    }\n  }, [agentId, debug]);\n\n  return \u003cdiv ref={containerRef} className=\"upriser-chat-container\" /\u003e;\n}\n\nexport default ChatWidget;\n```\n\n## 🎯 Client Tools Use Cases\n\n### Demo Scheduling Integration\n\nPerfect for sales teams and lead generation websites:\n\n```javascript\n// Setup demo scheduling client tools\nwindow.UPRISER_CLIENT_TOOLS = {\n  show_demo_form: (parameters) =\u003e {\n    const url = parameters?.url || \"https://meetings.hubspot.com/default\";\n\n    // Use the built-in modal system\n    if (window.upriserWidgetInstance) {\n      return window.upriserWidgetInstance.openModal({\n        url,\n        title: \"Schedule a Demo\",\n        onClose: () =\u003e {\n          console.log(\"Demo modal closed\");\n          // Track analytics\n        },\n      });\n    }\n\n    // Fallback to custom implementation\n    openCustomDemoModal(url);\n    return { success: true, message: \"Demo form opened\" };\n  },\n};\n\n// The AI agent can now call: \"Let me show you our demo scheduling form\"\n// This will trigger the show_demo_form client tool\n```\n\n### Vehicle Browsing Integration\n\nIdeal for automotive dealerships:\n\n```javascript\n// Setup vehicle browsing client tools\nwindow.UPRISER_CLIENT_TOOLS = {\n  show_vehicle_page: (parameters) =\u003e {\n    const url = parameters?.url;\n    const title = parameters?.title || \"Vehicle Details\";\n\n    // Validate required parameters\n    if (!url || typeof url !== \"string\") {\n      console.warn(\"show_vehicle_page requires a valid URL\");\n      return { success: false, message: \"Missing or invalid URL parameter\" };\n    }\n\n    // Use the built-in modal system\n    if (window.upriserWidgetInstance) {\n      return window.upriserWidgetInstance.openModal({\n        url,\n        title,\n        onClose: () =\u003e {\n          console.log(\"Vehicle modal closed\");\n        },\n      });\n    }\n\n    // Fallback to new tab\n    window.open(url, \"_blank\", \"noopener,noreferrer\");\n    return {\n      success: true,\n      message: \"Opened vehicle page in new tab (fallback)\",\n    };\n  },\n};\n\n// The AI agent can now call: \"Let me show you this vehicle\"\n// This will trigger the show_vehicle_page client tool with the vehicle URL\n```\n\n### Custom Modal Content\n\nYou can also create completely custom modals:\n\n```javascript\nwindow.UPRISER_CLIENT_TOOLS = {\n  show_product_catalog: (parameters) =\u003e {\n    const category = parameters?.category || \"all\";\n    const searchTerm = parameters?.search || \"\";\n\n    // Create custom modal content\n    const modalContent = document.createElement(\"div\");\n    modalContent.innerHTML = `\n      \u003cdiv class=\"product-catalog\"\u003e\n        \u003ch2\u003eProduct Catalog\u003c/h2\u003e\n        \u003cdiv class=\"search-bar\"\u003e\n          \u003cinput type=\"text\" placeholder=\"Search products...\" value=\"${searchTerm}\"\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"category-filter\"\u003e\n          \u003cselect\u003e\n            \u003coption value=\"all\"\u003eAll Categories\u003c/option\u003e\n            \u003coption value=\"electronics\" ${category === \"electronics\" ? \"selected\" : \"\"}\u003eElectronics\u003c/option\u003e\n            \u003coption value=\"clothing\" ${category === \"clothing\" ? \"selected\" : \"\"}\u003eClothing\u003c/option\u003e\n          \u003c/select\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"products-grid\"\u003e\n          \u003c!-- Your products here --\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    `;\n\n    // Use built-in modal with custom content\n    if (window.upriserWidgetInstance) {\n      const modal = window.upriserWidgetInstance.createModal({\n        title: \"Product Catalog\",\n        content: modalContent,\n        className: \"product-catalog-modal\",\n      });\n\n      if (modal) {\n        return { success: true, message: \"Product catalog opened\" };\n      }\n    }\n\n    return { success: false, message: \"Failed to open catalog\" };\n  },\n};\n```\n\n## Vue Integration Example\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv ref=\"chatContainer\" class=\"upriser-chat-container\"\u003e\u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nimport UpriserWidget from \"@upriser.ai/widget/dist/upriser-widget.js\";\n\nexport default {\n  name: \"UpriserChat\",\n  props: {\n    agentId: {\n      type: String,\n      default: \"agent_8401k5nnvgqpezf9fd17t3tb7t69\",\n    },\n    debug: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  data() {\n    return {\n      widget: null,\n    };\n  },\n  async mounted() {\n    this.widget = new UpriserWidget({\n      agentId: this.agentId,\n      debug: this.debug,\n      widgetContainer: this.$refs.chatContainer,\n    });\n\n    try {\n      await this.widget.init();\n    } catch (error) {\n      console.error(\"Failed to initialize Upriser widget:\", error);\n    }\n  },\n  beforeUnmount() {\n    if (this.widget) {\n      this.widget.destroy();\n    }\n  },\n};\n\u003c/script\u003e\n```\n\n## 📡 Event Handling\n\nThe widget dispatches custom events that you can listen for:\n\n### Modal Events\n\n```javascript\n// Listen for modal opened events\nwindow.addEventListener(\"upriser:modal-opened\", (event) =\u003e {\n  console.log(\"Modal opened:\", event.detail);\n\n  // Track analytics based on modal type\n  if (event.detail.type === \"demo\") {\n    // ...\n  } else if (event.detail.type === \"vehicle\") {\n    // ...\n  }\n});\n\n// Listen for modal closed events\nwindow.addEventListener(\"upriser:modal-closed\", (event) =\u003e {\n  console.log(\"Modal closed:\", event.detail);\n\n  // you could track analytics here\n});\n```\n\n### Widget Events\n\n```javascript\n// Listen for widget initialization\nwindow.addEventListener(\"upriser:initialized\", (event) =\u003e {\n  console.log(\"Widget initialized:\", event.detail);\n\n  // Widget is ready, you can now safely call client tools\n  const tools = event.detail.element.getClientTools();\n  console.log(\"Available client tools:\", Object.keys(tools));\n});\n\n// Listen for widget errors\nwindow.addEventListener(\"upriser:error\", (event) =\u003e {\n  console.error(\"Widget error:\", event.detail.error);\n\n  // Handle errors gracefully\n  showErrorMessage(\n    \"Chat widget encountered an error. Please refresh the page.\",\n  );\n});\n```\n\n### Client Tool Response Handling\n\n```javascript\n// Example of handling client tool responses\nwindow.UPRISER_CLIENT_TOOLS = {\n  show_demo_form: (parameters) =\u003e {\n    try {\n      const url = parameters?.url || \"https://meetings.hubspot.com/default\";\n\n      // Attempt to open modal\n      if (window.upriserWidgetInstance) {\n        const result = window.upriserWidgetInstance.openModal({\n          url,\n          title: \"Schedule a Demo\",\n        });\n\n        if (result.success) {\n          // Dispatch custom success event\n          window.dispatchEvent(\n            new CustomEvent(\"upriser:demo-opened\", {\n              detail: { url, timestamp: Date.now() },\n            }),\n          );\n        }\n\n        return result;\n      }\n\n      return { success: false, message: \"Widget not available\" };\n    } catch (error) {\n      console.error(\"Error in show_demo_form:\", error);\n\n      // Dispatch error event\n      window.dispatchEvent(\n        new CustomEvent(\"upriser:client-tool-error\", {\n          detail: { tool: \"show_demo_form\", error: error.message },\n        }),\n      );\n\n      return { success: false, message: \"Failed to open demo form\" };\n    }\n  },\n};\n```\n\n## Browser Support\n\n- Modern browsers (Chrome 60+, Firefox 55+, Safari 12+, Edge 79+)\n- Supports both ES modules and UMD\n- Works with or without build tools\n- No external dependencies\n\n## Features\n\n- 🚀 **Zero dependencies** - Works standalone\n- 📦 **Multiple formats** - UMD, ESM, and raw JavaScript\n- 🎯 **TypeScript support** - Full type definitions included\n- 🔧 **Auto-initialization** - Works out of the box\n- 🎨 **Customizable** - Override default agent and styling\n- 🧪 **Well tested** - Comprehensive test suite\n- 📱 **Responsive** - Works on desktop and mobile\n- 🪟 **Built-in modals** - **NEW!** Integrated modal system for seamless user experiences\n- 🛠️ **Client tools** - **NEW!** Custom functions that AI agents can call\n- 📡 **Event system** - **NEW!** Rich event handling for modal and widget interactions\n- 🎯 **Use case ready** - **NEW!** Pre-built patterns for demos, vehicle browsing, and more\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build all formats\nnpm run build\n\n# Run tests\nnpm test\n\n# Watch tests\nnpm test:watch\n```\n\n## 🔄 Continuous Integration (CI)\n\nThis project uses GitHub Actions for continuous integration to ensure code quality and reliability across multiple Node.js versions.\n\n### CI Pipeline Overview\n\nThe CI pipeline runs on every push and pull request to `main`, `master`, and `develop` branches, performing the following checks:\n\n#### Test Job\n- **Node.js Matrix Testing**: Tests against Node.js versions 16.x, 18.x, and 20.x\n- **Dependency Installation**: Uses `npm ci` for clean, reproducible builds\n- **Code Linting**: Runs ESLint to ensure code quality\n- **Test Execution**: Runs the complete test suite\n- **Build Verification**: Ensures all build formats compile successfully\n- **Coverage Reports**: Uploads test coverage to Codecov (Node.js 18.x only)\n\n#### Lint Job\n- **Dedicated Linting**: Separate job for thorough ESLint checks\n- **Node.js 18.x**: Uses stable Node.js version for consistent linting\n\n### Available Scripts for CI\n\n```bash\n# Core CI commands\nnpm ci                 # Clean install (used in CI)\nnpm run lint          # Run ESLint\nnpm run lint:check    # Check linting without fixing\nnpm run lint:fix      # Fix linting issues automatically\nnpm test              # Run test suite\nnpm test:coverage     # Run tests with coverage report\nnpm run build         # Build all formats (UMD, ESM, Types, Raw)\n\n# Individual build commands\nnpm run build:raw     # Build raw JavaScript file\nnpm run build:umd     # Build UMD format\nnpm run build:esm     # Build ES module format\nnpm run build:types   # Generate TypeScript definitions\n```\n\n### Local Development Workflow\n\nTo match the CI environment locally:\n\n```bash\n# 1. Clean install dependencies (like CI)\nnpm ci\n\n# 2. Run linting checks\nnpm run lint:check\n\n# 3. Run tests with coverage\nnpm test:coverage\n\n# 4. Verify build works\nnpm run build\n\n# 5. Fix any linting issues\nnpm run lint:fix\n```\n\n### CI Configuration Details\n\nThe CI configuration includes:\n\n- **Automated Testing**: Ensures all tests pass before merging\n- **Multi-Node Support**: Validates compatibility across Node.js LTS versions\n- **Code Quality**: ESLint enforcement for consistent code style\n- **Build Verification**: Confirms all distribution formats build successfully\n- **Coverage Tracking**: Monitors test coverage trends via Codecov\n- **Fail-Fast**: Stops on first failure to provide quick feedback\n\n### Contributing Guidelines\n\nWhen contributing to this project:\n\n1. **Write Tests**: All new features must include comprehensive tests [[memory:9100424]]\n2. **Follow Linting**: Ensure `npm run lint:check` passes\n3. **Test Locally**: Run `npm test` before submitting PRs\n4. **Build Verification**: Confirm `npm run build` succeeds\n5. **Coverage**: Maintain or improve test coverage\n\n### CI Status\n\nThe CI pipeline must pass for all pull requests. Check the GitHub Actions tab for detailed build logs and status updates.\n\n## File Structure\n\n```\n@upriser/widget/\n├── dist/                      # Built files for npm\n│   ├── upriser-widget.js      # UMD build\n│   ├── upriser-widget.esm.js  # ES module build\n│   └── upriser-widget.d.ts    # TypeScript definitions\n├── upriser-widget.js          # Raw JavaScript file (for CDN/direct use)\n├── src/                       # Source files\n└── package.json\n```\n\n## License\n\nMIT\n\n## Support\n\nFor support, please visit [https://www.upriser.ai](https://www.upriser.ai) or create an issue in the repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvbrazo%2Fupriser-agents","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvbrazo%2Fupriser-agents","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvbrazo%2Fupriser-agents/lists"}