@opentabs-dev/opentabs-plugin-priceline 0.0.75 → 0.0.77

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 CHANGED
@@ -1,159 +1,58 @@
1
- # opentabs-plugin-priceline
1
+ # Priceline
2
2
 
3
- OpenTabs plugin for Priceline
3
+ OpenTabs plugin for Priceline — gives AI agents access to Priceline through your authenticated browser session.
4
4
 
5
- ## Project Structure
5
+ ## Install
6
6
 
7
- ```
8
- priceline/
9
- ├── package.json # Plugin metadata (name, opentabs field, dependencies)
10
- ├── icon.svg # Optional custom icon (square SVG, max 8KB)
11
- ├── icon-inactive.svg # Optional manual inactive icon override
12
- ├── src/
13
- │ ├── index.ts # Plugin class (extends OpenTabsPlugin)
14
- │ └── tools/ # One file per tool (using defineTool)
15
- │ └── example.ts
16
- └── dist/ # Build output (generated)
17
- ├── adapter.iife.js # Bundled adapter injected into matching tabs
18
- └── tools.json # Tool schemas for MCP registration
19
- ```
20
-
21
- ## Configuration
22
-
23
- Plugin metadata is defined in `package.json` under the `opentabs` field:
24
-
25
- ```json
26
- {
27
- "name": "opentabs-plugin-priceline",
28
- "main": "dist/adapter.iife.js",
29
- "opentabs": {
30
- "displayName": "Priceline",
31
- "description": "OpenTabs plugin for Priceline",
32
- "urlPatterns": ["*://*.priceline.com/*"]
33
- }
34
- }
35
- ```
36
-
37
- - **`main`** — entry point for the bundled adapter IIFE
38
- - **`opentabs.displayName`** — human-readable name shown in the side panel
39
- - **`opentabs.description`** — short description of what the plugin does
40
- - **`opentabs.urlPatterns`** — Chrome match patterns for tabs where the adapter is injected
41
-
42
- ## Custom Icons
43
-
44
- By default, the side panel shows a colored letter avatar for your plugin. To use a custom icon, place an `icon.svg` file in the plugin root (next to `package.json`):
45
-
46
- ```
47
- priceline/
48
- ├── package.json
49
- ├── icon.svg ← custom icon (optional)
50
- ├── icon-inactive.svg ← manual inactive override (optional, requires icon.svg)
51
- ├── src/
52
- │ └── ...
7
+ ```bash
8
+ opentabs plugin install priceline
53
9
  ```
54
10
 
55
- **How it works:**
56
-
57
- - `opentabs-plugin build` reads `icon.svg`, validates it, auto-generates a grayscale inactive variant, and embeds both in `dist/tools.json`
58
- - To override the auto-generated inactive icon, provide `icon-inactive.svg` (must use only grayscale colors)
59
- - If no `icon.svg` is provided, the letter avatar is used automatically
60
-
61
- **Icon requirements:**
62
-
63
- - Square SVG with a `viewBox` attribute (e.g., `viewBox="0 0 32 32"`)
64
- - Maximum 8 KB file size
65
- - No embedded `<image>`, `<script>`, or event handler attributes (`onclick`, etc.)
66
- - Manual `icon-inactive.svg` must use only achromatic (grayscale) colors
67
-
68
- ## Development
11
+ Or install globally via npm:
69
12
 
70
13
  ```bash
71
- npm install
72
- npm run build # tsc && opentabs-plugin build
73
- npm run dev # watch mode (tsc --watch + opentabs-plugin build --watch)
74
- npm run type-check # tsc --noEmit
75
- npm run lint # biome
76
- ```
77
-
78
- ## Adding Tools
79
-
80
- Create a new file in `src/tools/` using `defineTool`:
81
-
82
- ```ts
83
- import { z } from 'zod';
84
- import { defineTool } from '@opentabs-dev/plugin-sdk';
85
-
86
- export const myTool = defineTool({
87
- name: 'my_tool',
88
- displayName: 'My Tool',
89
- description: 'What this tool does',
90
- icon: 'wrench',
91
- input: z.object({ /* ... */ }),
92
- output: z.object({ /* ... */ }),
93
- handle: async (params) => {
94
- // Tool implementation runs in the browser tab context
95
- return { /* ... */ };
96
- },
97
- });
14
+ npm install -g @opentabs-dev/opentabs-plugin-priceline
98
15
  ```
99
16
 
100
- Then register it in `src/index.ts` by adding it to the `tools` array.
17
+ ## Setup
101
18
 
102
- ## Authentication
19
+ 1. Open [priceline.com](https://www.priceline.com) in Chrome and log in
20
+ 2. Open the OpenTabs side panel — the Priceline plugin should appear as **ready**
103
21
 
104
- Plugin tools run in the browser tab context, so they can read auth tokens directly from the page. The SDK provides utilities for the most common patterns:
22
+ ## Tools (13)
105
23
 
106
- ```ts
107
- import { getLocalStorage, getCookie, getPageGlobal } from '@opentabs-dev/plugin-sdk';
24
+ ### Search (3)
108
25
 
109
- // localStorage most common
110
- const token = getLocalStorage('token');
26
+ | Tool | Description | Type |
27
+ |---|---|---|
28
+ | `search_locations` | Search destinations by keyword | Read |
29
+ | `search_points_of_interest` | Find top attractions in a city | Read |
30
+ | `navigate_to_search` | Open hotel search results page | Write |
111
31
 
112
- // Cookies — session tokens, JWTs
113
- const session = getCookie('session_id');
32
+ ### Hotels (6)
114
33
 
115
- // Page globals SPA boot data (e.g., window.__APP_STATE__)
116
- const appState = getPageGlobal('__APP_STATE__');
117
- ```
118
-
119
- **Iframe fallback:** Some apps (e.g., Discord) delete `window.localStorage` after boot. `getLocalStorage` automatically tries a hidden same-origin iframe fallback before returning `null`, so you don't need to handle this case manually.
120
-
121
- **SPA hydration:** Auth tokens may not be available immediately on page load. Implement polling in `isReady()` to wait until the app has hydrated before your tools run. See the comments in `src/index.ts` for an example polling pattern.
34
+ | Tool | Description | Type |
35
+ |---|---|---|
36
+ | `search_hotels` | Search hotels by city and dates | Read |
37
+ | `get_hotel_descriptions` | Get short descriptions for hotels | Read |
38
+ | `get_hotel_filters` | Get available hotel search filters | Read |
39
+ | `get_merchandising_badges` | Get top-rated/top-booked badges for hotels | Read |
40
+ | `get_price_guidance` | Get hotel price trends for a city | Read |
41
+ | `navigate_to_hotel` | Open a hotel detail page | Write |
122
42
 
123
- ## Shared Schemas
43
+ ### Account (4)
124
44
 
125
- When 3 or more tools share the same input or output shape, extract common Zod schemas into a shared file to avoid duplication:
45
+ | Tool | Description | Type |
46
+ |---|---|---|
47
+ | `get_customer_profile` | Get your Priceline profile | Read |
48
+ | `get_customer_coupons` | Get your available coupons | Read |
49
+ | `get_favorite_hotels` | Get your saved hotels in a city | Read |
50
+ | `get_abandoned_items` | Get your abandoned cart items | Read |
126
51
 
127
- ```ts
128
- // src/schemas/channel.ts
129
- import { z } from 'zod';
52
+ ## How It Works
130
53
 
131
- export const channelSchema = z.object({
132
- id: z.string().describe('Channel ID'),
133
- name: z.string().describe('Channel name'),
134
- });
54
+ This plugin runs inside your Priceline tab through the [OpenTabs](https://opentabs.dev) Chrome extension. It uses your existing browser session — no API tokens or OAuth apps required. All operations happen as you, with your permissions.
135
55
 
136
- export type Channel = z.infer<typeof channelSchema>;
137
- ```
138
-
139
- Then import and reuse in your tools:
140
-
141
- ```ts
142
- // src/tools/list-channels.ts
143
- import { channelSchema } from '../schemas/channel.js';
144
-
145
- export const listChannels = defineTool({
146
- name: 'list_channels',
147
- displayName: 'List Channels',
148
- description: 'List all available channels',
149
- icon: 'list',
150
- input: z.object({}),
151
- output: z.object({ channels: z.array(channelSchema) }),
152
- handle: async () => {
153
- // ...
154
- return { channels: [] };
155
- },
156
- });
157
- ```
56
+ ## License
158
57
 
159
- This keeps your tool schemas DRY and makes it easy to evolve shared types in one place.
58
+ MIT
@@ -349,18 +349,18 @@
349
349
 
350
350
  // node_modules/@opentabs-dev/plugin-sdk/dist/storage.js
351
351
  var getLocalStorage = (key) => {
352
- try {
353
- return localStorage.getItem(key);
354
- } catch {
355
- }
356
352
  let storage;
357
353
  try {
358
- storage = localStorage;
354
+ storage = window.localStorage;
359
355
  } catch {
360
356
  return null;
361
357
  }
362
- if (storage !== void 0) {
363
- return null;
358
+ if (storage) {
359
+ try {
360
+ return storage.getItem(key);
361
+ } catch {
362
+ return null;
363
+ }
364
364
  }
365
365
  try {
366
366
  const iframe = document.createElement("iframe");
@@ -441,6 +441,8 @@
441
441
  * (e.g., 'https://github.com'), not a match pattern.
442
442
  */
443
443
  homepage;
444
+ /** Typed configuration schema — declares settings that users provide via config.json or the side panel. */
445
+ configSchema;
444
446
  };
445
447
 
446
448
  // src/priceline-api.ts
@@ -15000,21 +15002,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15000
15002
  };
15001
15003
  var src_default = new PricelinePlugin();
15002
15004
 
15003
- // dist/_adapter_entry_1666c222-15ce-4d06-a52e-63d7baed901f.ts
15005
+ // dist/_adapter_entry_ad661d68-a6ab-4dcc-a605-b31b19618971.ts
15004
15006
  if (!globalThis.__openTabs) {
15005
15007
  globalThis.__openTabs = {};
15006
15008
  } else {
15007
15009
  const desc = Object.getOwnPropertyDescriptor(globalThis.__openTabs, "adapters");
15008
15010
  if (desc && !desc.writable) {
15009
- const ot2 = globalThis.__openTabs;
15011
+ const ot3 = globalThis.__openTabs;
15010
15012
  const newAdaptersObj = {};
15011
- if (ot2.adapters) {
15012
- for (const key of Object.keys(ot2.adapters)) {
15013
- const d = Object.getOwnPropertyDescriptor(ot2.adapters, key);
15013
+ if (ot3.adapters) {
15014
+ for (const key of Object.keys(ot3.adapters)) {
15015
+ const d = Object.getOwnPropertyDescriptor(ot3.adapters, key);
15014
15016
  if (d) Object.defineProperty(newAdaptersObj, key, d);
15015
15017
  }
15016
15018
  }
15017
- globalThis.__openTabs = Object.assign({}, ot2, { adapters: newAdaptersObj });
15019
+ globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdaptersObj });
15018
15020
  }
15019
15021
  }
15020
15022
  if (!globalThis.__openTabs.adapters) {
@@ -15052,6 +15054,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15052
15054
  }
15053
15055
  };
15054
15056
  var restoreTransport = setLogTransport ? setLogTransport(logTransport) : void 0;
15057
+ var ot2 = globalThis.__openTabs;
15058
+ ot2._notifyReadinessChanged = () => {
15059
+ try {
15060
+ const nonce = globalThis.__openTabs?._readinessNonce;
15061
+ if (nonce) {
15062
+ window.postMessage({ type: "opentabs:readiness-changed", plugin: "priceline", nonce }, "*");
15063
+ }
15064
+ } catch {
15065
+ }
15066
+ };
15055
15067
  var existing = adapters["priceline"];
15056
15068
  if (existing) {
15057
15069
  if (typeof existing.teardown === "function") {
@@ -15063,7 +15075,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15063
15075
  }
15064
15076
  }
15065
15077
  if (!Reflect.deleteProperty(adapters, "priceline")) {
15066
- const ot2 = globalThis.__openTabs;
15078
+ const ot3 = globalThis.__openTabs;
15067
15079
  const newAdapters = {};
15068
15080
  for (const key of Object.keys(adapters)) {
15069
15081
  if (key !== "priceline") {
@@ -15071,7 +15083,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15071
15083
  if (desc) Object.defineProperty(newAdapters, key, desc);
15072
15084
  }
15073
15085
  }
15074
- globalThis.__openTabs = Object.assign({}, ot2, { adapters: newAdapters });
15086
+ globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdapters });
15075
15087
  }
15076
15088
  var hasLifecycleHooks = typeof src_default.onToolInvocationStart === "function" || typeof src_default.onToolInvocationEnd === "function";
15077
15089
  for (const tool of src_default.tools) {
@@ -15132,12 +15144,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15132
15144
  }
15133
15145
  }
15134
15146
  };
15135
- const ot2 = globalThis.__openTabs;
15136
- if (!ot2._navigationInterceptor) {
15147
+ const ot3 = globalThis.__openTabs;
15148
+ if (!ot3._navigationInterceptor) {
15137
15149
  const origPushState = history.pushState.bind(history);
15138
15150
  const origReplaceState = history.replaceState.bind(history);
15139
15151
  const callbacks = /* @__PURE__ */ new Map();
15140
- ot2._navigationInterceptor = { callbacks, origPushState, origReplaceState };
15152
+ ot3._navigationInterceptor = { callbacks, origPushState, origReplaceState };
15141
15153
  history.pushState = function(...args) {
15142
15154
  origPushState(...args);
15143
15155
  for (const cb of callbacks.values()) {
@@ -15151,7 +15163,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15151
15163
  }
15152
15164
  };
15153
15165
  }
15154
- const interceptor = ot2._navigationInterceptor;
15166
+ const interceptor = ot3._navigationInterceptor;
15155
15167
  interceptor.callbacks.set("priceline", checkUrl);
15156
15168
  window.addEventListener("popstate", checkUrl);
15157
15169
  window.addEventListener("hashchange", checkUrl);
@@ -15206,5 +15218,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15206
15218
  };
15207
15219
  delete src_default.onDeactivate;
15208
15220
  }
15209
- })();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["priceline"]){var a=o.adapters["priceline"];a.__adapterHash="bc7c3b9db6499db65c31dc87ba2a5c1e2adcde6ee3291cb356ee05bb95cd6f79";if(a.tools&&Array.isArray(a.tools)){for(var i=0;i<a.tools.length;i++){Object.freeze(a.tools[i]);}Object.freeze(a.tools);}Object.freeze(a);Object.defineProperty(o.adapters,"priceline",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
15221
+ })();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["priceline"]){var a=o.adapters["priceline"];a.__adapterHash="aa86ee49169c5d612454b2de9d35d21f28d87f9a2f36ea25c470aa42a55464a9";if(a.tools&&Array.isArray(a.tools)){for(var i=0;i<a.tools.length;i++){Object.freeze(a.tools[i]);}Object.freeze(a.tools);}Object.freeze(a);Object.defineProperty(o.adapters,"priceline",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
15210
15222
  //# sourceMappingURL=adapter.iife.js.map