@opentabs-dev/opentabs-plugin-linkedin 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 +27 -140
- 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,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# LinkedIn
|
|
2
2
|
|
|
3
|
-
OpenTabs plugin for LinkedIn
|
|
3
|
+
OpenTabs plugin for LinkedIn — gives AI agents access to LinkedIn through your authenticated browser session.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
+
```bash
|
|
8
|
+
opentabs plugin install linkedin
|
|
7
9
|
```
|
|
8
|
-
linkedin/
|
|
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-linkedin",
|
|
28
|
-
"main": "dist/adapter.iife.js",
|
|
29
|
-
"opentabs": {
|
|
30
|
-
"displayName": "LinkedIn",
|
|
31
|
-
"description": "OpenTabs plugin for LinkedIn",
|
|
32
|
-
"urlPatterns": ["*://*.linkedin.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
|
-
linkedin/
|
|
48
|
-
├── package.json
|
|
49
|
-
├── icon.svg ← custom icon (optional)
|
|
50
|
-
├── icon-inactive.svg ← manual inactive override (optional, requires icon.svg)
|
|
51
|
-
├── src/
|
|
52
|
-
│ └── ...
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**How it works:**
|
|
56
10
|
|
|
57
|
-
|
|
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
|
|
14
|
+
npm install -g @opentabs-dev/opentabs-plugin-linkedin
|
|
76
15
|
```
|
|
77
16
|
|
|
78
|
-
##
|
|
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
|
-
});
|
|
98
|
-
```
|
|
17
|
+
## Setup
|
|
99
18
|
|
|
100
|
-
|
|
19
|
+
1. Open [linkedin.com](https://linkedin.com) in Chrome and log in
|
|
20
|
+
2. Open the OpenTabs side panel — the LinkedIn plugin should appear as **ready**
|
|
101
21
|
|
|
102
|
-
##
|
|
22
|
+
## Tools (6)
|
|
103
23
|
|
|
104
|
-
|
|
24
|
+
### Profile (2)
|
|
105
25
|
|
|
106
|
-
|
|
107
|
-
|
|
26
|
+
| Tool | Description | Type |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `get_current_user` | Get the current authenticated user | Read |
|
|
29
|
+
| `get_user_profile` | Get a user's LinkedIn profile | Read |
|
|
108
30
|
|
|
109
|
-
|
|
110
|
-
const token = getLocalStorage('token');
|
|
111
|
-
|
|
112
|
-
// Cookies — session tokens, JWTs
|
|
113
|
-
const session = getCookie('session_id');
|
|
114
|
-
|
|
115
|
-
// Page globals — SPA boot data (e.g., window.__APP_STATE__)
|
|
116
|
-
const appState = getPageGlobal('__APP_STATE__');
|
|
117
|
-
```
|
|
31
|
+
### Messaging (4)
|
|
118
32
|
|
|
119
|
-
|
|
33
|
+
| Tool | Description | Type |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `list_conversations` | List messaging conversations | Read |
|
|
36
|
+
| `get_conversation_messages` | Read messages in a conversation | Read |
|
|
37
|
+
| `send_message` | Send a message in a conversation | Write |
|
|
38
|
+
| `get_mailbox_counts` | Get unread message counts | Read |
|
|
120
39
|
|
|
121
|
-
|
|
40
|
+
## How It Works
|
|
122
41
|
|
|
123
|
-
|
|
42
|
+
This plugin runs inside your LinkedIn 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.
|
|
124
43
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
// src/schemas/channel.ts
|
|
129
|
-
import { z } from 'zod';
|
|
130
|
-
|
|
131
|
-
export const channelSchema = z.object({
|
|
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
|
-
```
|
|
44
|
+
## License
|
|
158
45
|
|
|
159
|
-
|
|
46
|
+
MIT
|
package/dist/adapter.iife.js
CHANGED
|
@@ -279,6 +279,8 @@
|
|
|
279
279
|
* (e.g., 'https://github.com'), not a match pattern.
|
|
280
280
|
*/
|
|
281
281
|
homepage;
|
|
282
|
+
/** Typed configuration schema — declares settings that users provide via config.json or the side panel. */
|
|
283
|
+
configSchema;
|
|
282
284
|
};
|
|
283
285
|
|
|
284
286
|
// src/linkedin-api.ts
|
|
@@ -14513,21 +14515,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14513
14515
|
};
|
|
14514
14516
|
var src_default = new LinkedInPlugin();
|
|
14515
14517
|
|
|
14516
|
-
// dist/
|
|
14518
|
+
// dist/_adapter_entry_b1869b49-2a5d-47f5-9cac-00925399e130.ts
|
|
14517
14519
|
if (!globalThis.__openTabs) {
|
|
14518
14520
|
globalThis.__openTabs = {};
|
|
14519
14521
|
} else {
|
|
14520
14522
|
const desc = Object.getOwnPropertyDescriptor(globalThis.__openTabs, "adapters");
|
|
14521
14523
|
if (desc && !desc.writable) {
|
|
14522
|
-
const
|
|
14524
|
+
const ot3 = globalThis.__openTabs;
|
|
14523
14525
|
const newAdaptersObj = {};
|
|
14524
|
-
if (
|
|
14525
|
-
for (const key of Object.keys(
|
|
14526
|
-
const d = Object.getOwnPropertyDescriptor(
|
|
14526
|
+
if (ot3.adapters) {
|
|
14527
|
+
for (const key of Object.keys(ot3.adapters)) {
|
|
14528
|
+
const d = Object.getOwnPropertyDescriptor(ot3.adapters, key);
|
|
14527
14529
|
if (d) Object.defineProperty(newAdaptersObj, key, d);
|
|
14528
14530
|
}
|
|
14529
14531
|
}
|
|
14530
|
-
globalThis.__openTabs = Object.assign({},
|
|
14532
|
+
globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdaptersObj });
|
|
14531
14533
|
}
|
|
14532
14534
|
}
|
|
14533
14535
|
if (!globalThis.__openTabs.adapters) {
|
|
@@ -14565,6 +14567,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14565
14567
|
}
|
|
14566
14568
|
};
|
|
14567
14569
|
var restoreTransport = setLogTransport ? setLogTransport(logTransport) : void 0;
|
|
14570
|
+
var ot2 = globalThis.__openTabs;
|
|
14571
|
+
ot2._notifyReadinessChanged = () => {
|
|
14572
|
+
try {
|
|
14573
|
+
const nonce = globalThis.__openTabs?._readinessNonce;
|
|
14574
|
+
if (nonce) {
|
|
14575
|
+
window.postMessage({ type: "opentabs:readiness-changed", plugin: "linkedin", nonce }, "*");
|
|
14576
|
+
}
|
|
14577
|
+
} catch {
|
|
14578
|
+
}
|
|
14579
|
+
};
|
|
14568
14580
|
var existing = adapters["linkedin"];
|
|
14569
14581
|
if (existing) {
|
|
14570
14582
|
if (typeof existing.teardown === "function") {
|
|
@@ -14576,7 +14588,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14576
14588
|
}
|
|
14577
14589
|
}
|
|
14578
14590
|
if (!Reflect.deleteProperty(adapters, "linkedin")) {
|
|
14579
|
-
const
|
|
14591
|
+
const ot3 = globalThis.__openTabs;
|
|
14580
14592
|
const newAdapters = {};
|
|
14581
14593
|
for (const key of Object.keys(adapters)) {
|
|
14582
14594
|
if (key !== "linkedin") {
|
|
@@ -14584,7 +14596,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14584
14596
|
if (desc) Object.defineProperty(newAdapters, key, desc);
|
|
14585
14597
|
}
|
|
14586
14598
|
}
|
|
14587
|
-
globalThis.__openTabs = Object.assign({},
|
|
14599
|
+
globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdapters });
|
|
14588
14600
|
}
|
|
14589
14601
|
var hasLifecycleHooks = typeof src_default.onToolInvocationStart === "function" || typeof src_default.onToolInvocationEnd === "function";
|
|
14590
14602
|
for (const tool of src_default.tools) {
|
|
@@ -14645,12 +14657,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14645
14657
|
}
|
|
14646
14658
|
}
|
|
14647
14659
|
};
|
|
14648
|
-
const
|
|
14649
|
-
if (!
|
|
14660
|
+
const ot3 = globalThis.__openTabs;
|
|
14661
|
+
if (!ot3._navigationInterceptor) {
|
|
14650
14662
|
const origPushState = history.pushState.bind(history);
|
|
14651
14663
|
const origReplaceState = history.replaceState.bind(history);
|
|
14652
14664
|
const callbacks = /* @__PURE__ */ new Map();
|
|
14653
|
-
|
|
14665
|
+
ot3._navigationInterceptor = { callbacks, origPushState, origReplaceState };
|
|
14654
14666
|
history.pushState = function(...args) {
|
|
14655
14667
|
origPushState(...args);
|
|
14656
14668
|
for (const cb of callbacks.values()) {
|
|
@@ -14664,7 +14676,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14664
14676
|
}
|
|
14665
14677
|
};
|
|
14666
14678
|
}
|
|
14667
|
-
const interceptor =
|
|
14679
|
+
const interceptor = ot3._navigationInterceptor;
|
|
14668
14680
|
interceptor.callbacks.set("linkedin", checkUrl);
|
|
14669
14681
|
window.addEventListener("popstate", checkUrl);
|
|
14670
14682
|
window.addEventListener("hashchange", checkUrl);
|
|
@@ -14719,5 +14731,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
14719
14731
|
};
|
|
14720
14732
|
delete src_default.onDeactivate;
|
|
14721
14733
|
}
|
|
14722
|
-
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["linkedin"]){var a=o.adapters["linkedin"];a.__adapterHash="
|
|
14734
|
+
})();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["linkedin"]){var a=o.adapters["linkedin"];a.__adapterHash="b526e860c8b2cfc63167d58930d0f651cdf92ef83b79225ba19c82011352adf1";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,"linkedin",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
|
|
14723
14735
|
//# sourceMappingURL=adapter.iife.js.map
|