@opentabs-dev/opentabs-plugin-npm 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,69 @@
1
- # opentabs-plugin-npm
1
+ # npm
2
2
 
3
- OpenTabs plugin for npm registry
3
+ OpenTabs plugin for npm registry — gives AI agents access to npm through your authenticated browser session.
4
4
 
5
- ## Project Structure
5
+ ## Install
6
6
 
7
+ ```bash
8
+ opentabs plugin install npm
7
9
  ```
8
- npm/
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-npm",
28
- "main": "dist/adapter.iife.js",
29
- "opentabs": {
30
- "displayName": "npm",
31
- "description": "OpenTabs plugin for npm registry",
32
- "urlPatterns": ["*://*.www.npmjs.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
- npm/
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
-
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
10
 
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-npm
76
15
  ```
77
16
 
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
- });
98
- ```
17
+ ## Setup
99
18
 
100
- Then register it in `src/index.ts` by adding it to the `tools` array.
19
+ 1. Open [www.npmjs.com](https://www.npmjs.com) in Chrome and log in
20
+ 2. Open the OpenTabs side panel — the npm plugin should appear as **ready**
101
21
 
102
- ## Authentication
22
+ ## Tools (14)
103
23
 
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:
24
+ ### Account (1)
105
25
 
106
- ```ts
107
- import { getLocalStorage, getCookie, getPageGlobal } from '@opentabs-dev/plugin-sdk';
26
+ | Tool | Description | Type |
27
+ |---|---|---|
28
+ | `get_current_user` | Get the authenticated npm user profile | Read |
108
29
 
109
- // localStorage — most common
110
- const token = getLocalStorage('token');
30
+ ### Packages (8)
111
31
 
112
- // Cookies session tokens, JWTs
113
- const session = getCookie('session_id');
32
+ | Tool | Description | Type |
33
+ |---|---|---|
34
+ | `search_packages` | Search the npm registry for packages | Read |
35
+ | `get_package` | Get npm package details | Read |
36
+ | `get_package_version` | Get details for a specific package version | Read |
37
+ | `get_package_readme` | Get the README of a package | Read |
38
+ | `get_package_downloads` | Get download stats for a package | Read |
39
+ | `get_package_dependents` | Get packages depending on a package | Read |
40
+ | `get_package_versions` | List all versions of a package | Read |
41
+ | `get_package_dependencies` | Get dependencies of a package | Read |
114
42
 
115
- // Page globals — SPA boot data (e.g., window.__APP_STATE__)
116
- const appState = getPageGlobal('__APP_STATE__');
117
- ```
43
+ ### Users (2)
118
44
 
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.
45
+ | Tool | Description | Type |
46
+ |---|---|---|
47
+ | `get_user_profile` | Get an npm user profile | Read |
48
+ | `get_user_packages` | List packages by a user | Read |
120
49
 
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.
50
+ ### Organizations (1)
122
51
 
123
- ## Shared Schemas
52
+ | Tool | Description | Type |
53
+ |---|---|---|
54
+ | `get_organization` | Get npm organization details | Read |
124
55
 
125
- When 3 or more tools share the same input or output shape, extract common Zod schemas into a shared file to avoid duplication:
56
+ ### Settings (2)
126
57
 
127
- ```ts
128
- // src/schemas/channel.ts
129
- import { z } from 'zod';
58
+ | Tool | Description | Type |
59
+ |---|---|---|
60
+ | `list_user_packages` | List your own packages | Read |
61
+ | `list_tokens` | List your npm access tokens | Read |
130
62
 
131
- export const channelSchema = z.object({
132
- id: z.string().describe('Channel ID'),
133
- name: z.string().describe('Channel name'),
134
- });
63
+ ## How It Works
135
64
 
136
- export type Channel = z.infer<typeof channelSchema>;
137
- ```
65
+ This plugin runs inside your npm 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.
138
66
 
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
- ```
67
+ ## License
158
68
 
159
- This keeps your tool schemas DRY and makes it easy to evolve shared types in one place.
69
+ MIT
@@ -391,6 +391,8 @@
391
391
  * (e.g., 'https://github.com'), not a match pattern.
392
392
  */
393
393
  homepage;
394
+ /** Typed configuration schema — declares settings that users provide via config.json or the side panel. */
395
+ configSchema;
394
396
  };
395
397
 
396
398
  // src/npm-api.ts
@@ -14790,21 +14792,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14790
14792
  };
14791
14793
  var src_default = new NpmPlugin();
14792
14794
 
14793
- // dist/_adapter_entry_3c28efb9-a62d-44e3-9e0e-9d7cd83bb765.ts
14795
+ // dist/_adapter_entry_28113744-f85c-4f8e-88b5-f40bb0e1d833.ts
14794
14796
  if (!globalThis.__openTabs) {
14795
14797
  globalThis.__openTabs = {};
14796
14798
  } else {
14797
14799
  const desc = Object.getOwnPropertyDescriptor(globalThis.__openTabs, "adapters");
14798
14800
  if (desc && !desc.writable) {
14799
- const ot2 = globalThis.__openTabs;
14801
+ const ot3 = globalThis.__openTabs;
14800
14802
  const newAdaptersObj = {};
14801
- if (ot2.adapters) {
14802
- for (const key of Object.keys(ot2.adapters)) {
14803
- const d = Object.getOwnPropertyDescriptor(ot2.adapters, key);
14803
+ if (ot3.adapters) {
14804
+ for (const key of Object.keys(ot3.adapters)) {
14805
+ const d = Object.getOwnPropertyDescriptor(ot3.adapters, key);
14804
14806
  if (d) Object.defineProperty(newAdaptersObj, key, d);
14805
14807
  }
14806
14808
  }
14807
- globalThis.__openTabs = Object.assign({}, ot2, { adapters: newAdaptersObj });
14809
+ globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdaptersObj });
14808
14810
  }
14809
14811
  }
14810
14812
  if (!globalThis.__openTabs.adapters) {
@@ -14842,6 +14844,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14842
14844
  }
14843
14845
  };
14844
14846
  var restoreTransport = setLogTransport ? setLogTransport(logTransport) : void 0;
14847
+ var ot2 = globalThis.__openTabs;
14848
+ ot2._notifyReadinessChanged = () => {
14849
+ try {
14850
+ const nonce = globalThis.__openTabs?._readinessNonce;
14851
+ if (nonce) {
14852
+ window.postMessage({ type: "opentabs:readiness-changed", plugin: "npm", nonce }, "*");
14853
+ }
14854
+ } catch {
14855
+ }
14856
+ };
14845
14857
  var existing = adapters["npm"];
14846
14858
  if (existing) {
14847
14859
  if (typeof existing.teardown === "function") {
@@ -14853,7 +14865,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14853
14865
  }
14854
14866
  }
14855
14867
  if (!Reflect.deleteProperty(adapters, "npm")) {
14856
- const ot2 = globalThis.__openTabs;
14868
+ const ot3 = globalThis.__openTabs;
14857
14869
  const newAdapters = {};
14858
14870
  for (const key of Object.keys(adapters)) {
14859
14871
  if (key !== "npm") {
@@ -14861,7 +14873,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14861
14873
  if (desc) Object.defineProperty(newAdapters, key, desc);
14862
14874
  }
14863
14875
  }
14864
- globalThis.__openTabs = Object.assign({}, ot2, { adapters: newAdapters });
14876
+ globalThis.__openTabs = Object.assign({}, ot3, { adapters: newAdapters });
14865
14877
  }
14866
14878
  var hasLifecycleHooks = typeof src_default.onToolInvocationStart === "function" || typeof src_default.onToolInvocationEnd === "function";
14867
14879
  for (const tool of src_default.tools) {
@@ -14922,12 +14934,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14922
14934
  }
14923
14935
  }
14924
14936
  };
14925
- const ot2 = globalThis.__openTabs;
14926
- if (!ot2._navigationInterceptor) {
14937
+ const ot3 = globalThis.__openTabs;
14938
+ if (!ot3._navigationInterceptor) {
14927
14939
  const origPushState = history.pushState.bind(history);
14928
14940
  const origReplaceState = history.replaceState.bind(history);
14929
14941
  const callbacks = /* @__PURE__ */ new Map();
14930
- ot2._navigationInterceptor = { callbacks, origPushState, origReplaceState };
14942
+ ot3._navigationInterceptor = { callbacks, origPushState, origReplaceState };
14931
14943
  history.pushState = function(...args) {
14932
14944
  origPushState(...args);
14933
14945
  for (const cb of callbacks.values()) {
@@ -14941,7 +14953,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14941
14953
  }
14942
14954
  };
14943
14955
  }
14944
- const interceptor = ot2._navigationInterceptor;
14956
+ const interceptor = ot3._navigationInterceptor;
14945
14957
  interceptor.callbacks.set("npm", checkUrl);
14946
14958
  window.addEventListener("popstate", checkUrl);
14947
14959
  window.addEventListener("hashchange", checkUrl);
@@ -14996,5 +15008,5 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14996
15008
  };
14997
15009
  delete src_default.onDeactivate;
14998
15010
  }
14999
- })();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["npm"]){var a=o.adapters["npm"];a.__adapterHash="5c1197b18965cefb114690b315e599d5292e7cd8a55f266d5da9419ac0f49a3a";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,"npm",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
15011
+ })();(function(){var o=(globalThis).__openTabs;if(o&&o.adapters&&o.adapters["npm"]){var a=o.adapters["npm"];a.__adapterHash="46691611e4f4bfba5aae8f762b38b99b73ac5236a0c81aa76aa2bde2ee8d6f3a";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,"npm",{value:a,writable:false,configurable:false,enumerable:true});Object.defineProperty(o,"adapters",{value:o.adapters,writable:false,configurable:false});}})();
15000
15012
  //# sourceMappingURL=adapter.iife.js.map