@opentabs-dev/opentabs-plugin-twitch 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 +51 -131
- package/dist/adapter.iife.js +25 -13
- package/dist/adapter.iife.js.map +3 -3
- package/dist/tools.json +1 -1
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,159 +1,79 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Twitch
|
|
2
2
|
|
|
3
|
-
OpenTabs plugin for Twitch
|
|
3
|
+
OpenTabs plugin for Twitch — browse streams, search channels and games, view clips and videos — gives AI agents access to Twitch through your authenticated browser session.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
+
```bash
|
|
8
|
+
opentabs plugin install twitch
|
|
7
9
|
```
|
|
8
|
-
twitch/
|
|
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-twitch",
|
|
28
|
-
"main": "dist/adapter.iife.js",
|
|
29
|
-
"opentabs": {
|
|
30
|
-
"displayName": "Twitch",
|
|
31
|
-
"description": "OpenTabs plugin for Twitch",
|
|
32
|
-
"urlPatterns": ["*://*.twitch.tv/*"]
|
|
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
10
|
|
|
42
|
-
|
|
11
|
+
Or install globally via npm:
|
|
43
12
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
twitch/
|
|
48
|
-
├── package.json
|
|
49
|
-
├── icon.svg ← custom icon (optional)
|
|
50
|
-
├── icon-inactive.svg ← manual inactive override (optional, requires icon.svg)
|
|
51
|
-
├── src/
|
|
52
|
-
│ └── ...
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @opentabs-dev/opentabs-plugin-twitch
|
|
53
15
|
```
|
|
54
16
|
|
|
55
|
-
|
|
17
|
+
## Setup
|
|
56
18
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- If no `icon.svg` is provided, the letter avatar is used automatically
|
|
19
|
+
1. Open [twitch.tv](https://www.twitch.tv) in Chrome and log in
|
|
20
|
+
2. Open the OpenTabs side panel — the Twitch plugin should appear as **ready**
|
|
60
21
|
|
|
61
|
-
|
|
22
|
+
## Tools (14)
|
|
62
23
|
|
|
63
|
-
|
|
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
|
|
24
|
+
### Users (2)
|
|
67
25
|
|
|
68
|
-
|
|
26
|
+
| Tool | Description | Type |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `get_current_user` | Get the authenticated Twitch user profile | Read |
|
|
29
|
+
| `get_user_profile` | Get a Twitch user profile by login name | Read |
|
|
69
30
|
|
|
70
|
-
|
|
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
|
-
```
|
|
31
|
+
### Streams (3)
|
|
77
32
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
});
|
|
98
|
-
```
|
|
33
|
+
| Tool | Description | Type |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `get_top_streams` | Get top live streams by viewer count | Read |
|
|
36
|
+
| `get_streams_by_game` | Get live streams for a game or category | Read |
|
|
37
|
+
| `get_stream` | Get live stream info for a channel | Read |
|
|
99
38
|
|
|
100
|
-
|
|
39
|
+
### Games (2)
|
|
101
40
|
|
|
102
|
-
|
|
41
|
+
| Tool | Description | Type |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `get_top_games` | Get top games and categories by viewer count | Read |
|
|
44
|
+
| `get_game` | Get game/category details | Read |
|
|
103
45
|
|
|
104
|
-
|
|
46
|
+
### Search (2)
|
|
105
47
|
|
|
106
|
-
|
|
107
|
-
|
|
48
|
+
| Tool | Description | Type |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `search_channels` | Search for Twitch channels | Read |
|
|
51
|
+
| `search_categories` | Search for games and categories | Read |
|
|
108
52
|
|
|
109
|
-
|
|
110
|
-
const token = getLocalStorage('token');
|
|
53
|
+
### Clips (2)
|
|
111
54
|
|
|
112
|
-
|
|
113
|
-
|
|
55
|
+
| Tool | Description | Type |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `get_user_clips` | Get clips from a Twitch channel | Read |
|
|
58
|
+
| `get_game_clips` | Get top clips for a game or category | Read |
|
|
114
59
|
|
|
115
|
-
|
|
116
|
-
const appState = getPageGlobal('__APP_STATE__');
|
|
117
|
-
```
|
|
60
|
+
### Videos (2)
|
|
118
61
|
|
|
119
|
-
|
|
62
|
+
| Tool | Description | Type |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `get_user_videos` | Get videos from a Twitch channel | Read |
|
|
65
|
+
| `get_video` | Get details about a specific video | Read |
|
|
120
66
|
|
|
121
|
-
|
|
67
|
+
### Chat (1)
|
|
122
68
|
|
|
123
|
-
|
|
69
|
+
| Tool | Description | Type |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `get_channel_emotes` | Get subscription emotes for a channel | Read |
|
|
124
72
|
|
|
125
|
-
|
|
73
|
+
## How It Works
|
|
126
74
|
|
|
127
|
-
|
|
128
|
-
// src/schemas/channel.ts
|
|
129
|
-
import { z } from 'zod';
|
|
75
|
+
This plugin runs inside your Twitch 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.
|
|
130
76
|
|
|
131
|
-
|
|
132
|
-
id: z.string().describe('Channel ID'),
|
|
133
|
-
name: z.string().describe('Channel name'),
|
|
134
|
-
});
|
|
135
|
-
|
|
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
|
-
```
|
|
77
|
+
## License
|
|
158
78
|
|
|
159
|
-
|
|
79
|
+
MIT
|
package/dist/adapter.iife.js
CHANGED
|
@@ -373,6 +373,8 @@
|
|
|
373
373
|
* (e.g., 'https://github.com'), not a match pattern.
|
|
374
374
|
*/
|
|
375
375
|
homepage;
|
|
376
|
+
/** Typed configuration schema — declares settings that users provide via config.json or the side panel. */
|
|
377
|
+
configSchema;
|
|
376
378
|
};
|
|
377
379
|
|
|
378
380
|
// src/twitch-api.ts
|
|
@@ -14883,21 +14885,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14883
14885
|
};
|
|
14884
14886
|
var src_default = new TwitchPlugin();
|
|
14885
14887
|
|
|
14886
|
-
// dist/
|
|
14888
|
+
// dist/_adapter_entry_fc4d328f-7ceb-4051-9f8e-263218be4761.ts
|
|
14887
14889
|
if (!globalThis.__openTabs) {
|
|
14888
14890
|
globalThis.__openTabs = {};
|
|
14889
14891
|
} else {
|
|
14890
14892
|
const desc = Object.getOwnPropertyDescriptor(globalThis.__openTabs, "adapters");
|
|
14891
14893
|
if (desc && !desc.writable) {
|
|
14892
|
-
const
|
|
14894
|
+
const ot3 = globalThis.__openTabs;
|
|
14893
14895
|
const newAdaptersObj = {};
|
|
14894
|
-
if (
|
|
14895
|
-
for (const key of Object.keys(
|
|
14896
|
-
const d = Object.getOwnPropertyDescriptor(
|
|
14896
|
+
if (ot3.adapters) {
|
|
14897
|
+
for (const key of Object.keys(ot3.adapters)) {
|
|
14898
|
+
const d = Object.getOwnPropertyDescriptor(ot3.adapters, key);
|
|
14897
14899
|
if (d) Object.defineProperty(newAdaptersObj, key, d);
|
|
14898
14900
|
}
|
|
14899
14901
|
}
|
|
14900
|
-
globalThis.__openTabs = Object.assign({},
|
|
14902
|
+
globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdaptersObj });
|
|
14901
14903
|
}
|
|
14902
14904
|
}
|
|
14903
14905
|
if (!globalThis.__openTabs.adapters) {
|
|
@@ -14935,6 +14937,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14935
14937
|
}
|
|
14936
14938
|
};
|
|
14937
14939
|
var restoreTransport = setLogTransport ? setLogTransport(logTransport) : void 0;
|
|
14940
|
+
var ot2 = globalThis.__openTabs;
|
|
14941
|
+
ot2._notifyReadinessChanged = () => {
|
|
14942
|
+
try {
|
|
14943
|
+
const nonce = globalThis.__openTabs?._readinessNonce;
|
|
14944
|
+
if (nonce) {
|
|
14945
|
+
window.postMessage({ type: "opentabs:readiness-changed", plugin: "twitch", nonce }, "*");
|
|
14946
|
+
}
|
|
14947
|
+
} catch {
|
|
14948
|
+
}
|
|
14949
|
+
};
|
|
14938
14950
|
var existing = adapters["twitch"];
|
|
14939
14951
|
if (existing) {
|
|
14940
14952
|
if (typeof existing.teardown === "function") {
|
|
@@ -14946,7 +14958,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14946
14958
|
}
|
|
14947
14959
|
}
|
|
14948
14960
|
if (!Reflect.deleteProperty(adapters, "twitch")) {
|
|
14949
|
-
const
|
|
14961
|
+
const ot3 = globalThis.__openTabs;
|
|
14950
14962
|
const newAdapters = {};
|
|
14951
14963
|
for (const key of Object.keys(adapters)) {
|
|
14952
14964
|
if (key !== "twitch") {
|
|
@@ -14954,7 +14966,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14954
14966
|
if (desc) Object.defineProperty(newAdapters, key, desc);
|
|
14955
14967
|
}
|
|
14956
14968
|
}
|
|
14957
|
-
globalThis.__openTabs = Object.assign({},
|
|
14969
|
+
globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdapters });
|
|
14958
14970
|
}
|
|
14959
14971
|
var hasLifecycleHooks = typeof src_default.onToolInvocationStart === "function" || typeof src_default.onToolInvocationEnd === "function";
|
|
14960
14972
|
for (const tool of src_default.tools) {
|
|
@@ -15015,12 +15027,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15015
15027
|
}
|
|
15016
15028
|
}
|
|
15017
15029
|
};
|
|
15018
|
-
const
|
|
15019
|
-
if (!
|
|
15030
|
+
const ot3 = globalThis.__openTabs;
|
|
15031
|
+
if (!ot3._navigationInterceptor) {
|
|
15020
15032
|
const origPushState = history.pushState.bind(history);
|
|
15021
15033
|
const origReplaceState = history.replaceState.bind(history);
|
|
15022
15034
|
const callbacks = /* @__PURE__ */ new Map();
|
|
15023
|
-
|
|
15035
|
+
ot3._navigationInterceptor = { callbacks, origPushState, origReplaceState };
|
|
15024
15036
|
history.pushState = function(...args) {
|
|
15025
15037
|
origPushState(...args);
|
|
15026
15038
|
for (const cb of callbacks.values()) {
|
|
@@ -15034,7 +15046,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15034
15046
|
}
|
|
15035
15047
|
};
|
|
15036
15048
|
}
|
|
15037
|
-
const interceptor =
|
|
15049
|
+
const interceptor = ot3._navigationInterceptor;
|
|
15038
15050
|
interceptor.callbacks.set("twitch", checkUrl);
|
|
15039
15051
|
window.addEventListener("popstate", checkUrl);
|
|
15040
15052
|
window.addEventListener("hashchange", checkUrl);
|
|
@@ -15089,5 +15101,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15089
15101
|
};
|
|
15090
15102
|
delete src_default.onDeactivate;
|
|
15091
15103
|
}
|
|
15092
|
-
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["twitch"]){var a=o.adapters["twitch"];a.__adapterHash="
|
|
15104
|
+
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["twitch"]){var a=o.adapters["twitch"];a.__adapterHash="2e9b431884577e773e8559ed8b56a94db8371459f3d6da2c8c47ad1c9c9d82b9";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,"twitch",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
|
|
15093
15105
|
//# sourceMappingURL=adapter.iife.js.map
|