@extension.dev/mcp 5.7.0 → 6.0.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +22 -4
- package/LICENSE +201 -21
- package/README.md +1 -1
- package/bin/extension-mcp.js +1 -1
- package/dist/module.js +280 -49
- package/dist/src/lib/carrier.d.ts +12 -0
- package/dist/src/lib/guest-load-oracle.d.ts +27 -0
- package/dist/src/lib/registry.d.ts +22 -4
- package/dist/src/lib/template-artifact-source.d.ts +2 -0
- package/dist/src/lib/urls-origins.d.ts +34 -0
- package/dist/src/lib/urls-paths.d.ts +56 -0
- package/dist/src/tools/open.d.ts +1 -0
- package/package.json +5 -3
- package/server.json +2 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "extension-mcp",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "MCP tools for browser extension development: scaffold from 60+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox.",
|
|
13
|
-
"version": "
|
|
13
|
+
"version": "6.0.0",
|
|
14
14
|
"category": "development",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Cezar Augusto"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extension-mcp",
|
|
3
3
|
"description": "MCP tools for browser extension development: scaffold from 60+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox. Ships /extension, /extension-add, /extension-debug, and /extension-publish commands.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 6.0.0
|
|
4
4
|
|
|
5
|
-
The live-preview carrier stops living
|
|
6
|
-
refused, and rides an engine that tells
|
|
7
|
-
extension away.
|
|
5
|
+
The server moves to Apache-2.0, and the live-preview carrier stops living
|
|
6
|
+
in your project, reports what it refused, and rides an engine that tells
|
|
7
|
+
you when the browser turned your extension away.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **License: MIT is now Apache-2.0.** Everything published up to and
|
|
12
|
+
including 5.6.1 was released under MIT and stays MIT forever; you keep
|
|
13
|
+
those rights on those versions. From 6.0.0 forward the license is
|
|
14
|
+
Apache-2.0, which adds an express patent grant and requires anyone
|
|
15
|
+
shipping a modified copy to state that they changed the files. For almost
|
|
16
|
+
every user this changes nothing about what you are allowed to do.
|
|
8
17
|
|
|
9
18
|
### Fixed
|
|
10
19
|
|
|
@@ -55,6 +64,15 @@ extension away.
|
|
|
55
64
|
again: the platform's Safari/App Store submission lane is now enabled
|
|
56
65
|
for every project, so the store enum and the per-store status report
|
|
57
66
|
treat it like chrome, firefox, and edge.
|
|
67
|
+
- `extension_list_templates` and `extension_get_template_source` resolve
|
|
68
|
+
the template catalog and sources from the pinned, content-addressed
|
|
69
|
+
corpus served at `media.extension.land`, with a commit-pinned GitHub
|
|
70
|
+
raw fallback. Both tools previously read a floating `nightly`/`main`
|
|
71
|
+
ref, so two runs could disagree; they now resolve one immutable
|
|
72
|
+
release whose files are sha256-verified at the origin.
|
|
73
|
+
- Console and dashboard links come from a shared URL contract and are
|
|
74
|
+
environment aware, so a local or development run no longer hands back
|
|
75
|
+
hardcoded production console URLs.
|
|
58
76
|
|
|
59
77
|
### Added
|
|
60
78
|
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,201 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 Cezar Augusto and the extension.dev collaborators
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -156,4 +156,4 @@ All of it rides on [Extension.js](https://github.com/extension-js/extension.js),
|
|
|
156
156
|
|
|
157
157
|
## License
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
Apache-2.0 (c) 2026 Cezar Augusto and the extension.dev collaborators. See [LICENSE](LICENSE).
|
package/bin/extension-mcp.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// ██║╚██╔╝██║██║ ██╔═══╝
|
|
6
6
|
// ██║ ╚═╝ ██║╚██████╗██║
|
|
7
7
|
// ╚═╝ ╚═╝ ╚═════╝╚═╝
|
|
8
|
-
//
|
|
8
|
+
// Apache License 2.0 (c) 2026 Cezar Augusto and the extension.dev collaborators
|
|
9
9
|
import {startServer, runCli} from '../dist/module.js'
|
|
10
10
|
|
|
11
11
|
const [, , cmd, ...rest] = process.argv
|
package/dist/module.js
CHANGED
|
@@ -138,7 +138,8 @@ __webpack_require__.r(open_namespaceObject);
|
|
|
138
138
|
__webpack_require__.d(open_namespaceObject, {
|
|
139
139
|
clampPopupBounds: ()=>clampPopupBounds,
|
|
140
140
|
handler: ()=>open_handler,
|
|
141
|
-
schema: ()=>open_schema
|
|
141
|
+
schema: ()=>open_schema,
|
|
142
|
+
sessionIsHeadless: ()=>sessionIsHeadless
|
|
142
143
|
});
|
|
143
144
|
var preview_namespaceObject = {};
|
|
144
145
|
__webpack_require__.r(preview_namespaceObject);
|
|
@@ -388,10 +389,85 @@ async function create_handler(args) {
|
|
|
388
389
|
}
|
|
389
390
|
});
|
|
390
391
|
}
|
|
392
|
+
const DEFAULT_MEDIA_ORIGIN = "https://media.extension.land";
|
|
393
|
+
const DEFAULT_CHANNEL = "latest";
|
|
394
|
+
const PINNED_COMMIT = "2d2ed9668cca002148d9eecd953a08b54d0bad9d";
|
|
395
|
+
const CHANNEL_CACHE_TTL_MS = 300000;
|
|
396
|
+
function mediaOrigin() {
|
|
397
|
+
return (process.env.EXTENSION_MEDIA_ORIGIN || "").trim() || DEFAULT_MEDIA_ORIGIN;
|
|
398
|
+
}
|
|
399
|
+
function channelName() {
|
|
400
|
+
return (process.env.EXTENSION_TEMPLATES_CHANNEL || "").trim() || DEFAULT_CHANNEL;
|
|
401
|
+
}
|
|
402
|
+
function pinnedCommitOverride() {
|
|
403
|
+
return (process.env.EXTENSION_TEMPLATES_COMMIT || "").trim();
|
|
404
|
+
}
|
|
405
|
+
function rawBaseForCommit(commit) {
|
|
406
|
+
return `https://raw.githubusercontent.com/extension-js/examples/${commit}`;
|
|
407
|
+
}
|
|
408
|
+
let releaseCache = null;
|
|
409
|
+
let releaseCacheExpiresAt = 0;
|
|
410
|
+
let releaseRequest = null;
|
|
411
|
+
function releaseForCommit(origin, commit) {
|
|
412
|
+
return {
|
|
413
|
+
commit,
|
|
414
|
+
metaUrl: `${origin}/templates/${commit}/templates-meta.json`,
|
|
415
|
+
filesBaseUrl: `${origin}/templates/${commit}/files`
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
async function resolveRelease() {
|
|
419
|
+
const now = Date.now();
|
|
420
|
+
if (releaseCache && now < releaseCacheExpiresAt) return releaseCache;
|
|
421
|
+
if (releaseRequest) return releaseRequest;
|
|
422
|
+
const origin = mediaOrigin();
|
|
423
|
+
const override = pinnedCommitOverride();
|
|
424
|
+
if (override) {
|
|
425
|
+
releaseCache = releaseForCommit(origin, override);
|
|
426
|
+
releaseCacheExpiresAt = now + CHANNEL_CACHE_TTL_MS;
|
|
427
|
+
return releaseCache;
|
|
428
|
+
}
|
|
429
|
+
releaseRequest = (async ()=>{
|
|
430
|
+
try {
|
|
431
|
+
const pointerUrl = `${origin}/templates/${channelName()}.json`;
|
|
432
|
+
const response = await fetch(pointerUrl, {
|
|
433
|
+
headers: {
|
|
434
|
+
Accept: "application/json"
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
if (!response.ok) return null;
|
|
438
|
+
const pointer = await response.json();
|
|
439
|
+
const commit = String(pointer?.commit || "").trim();
|
|
440
|
+
if (!commit) return null;
|
|
441
|
+
releaseCache = releaseForCommit(origin, commit);
|
|
442
|
+
releaseCacheExpiresAt = Date.now() + CHANNEL_CACHE_TTL_MS;
|
|
443
|
+
return releaseCache;
|
|
444
|
+
} catch {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
})();
|
|
448
|
+
try {
|
|
449
|
+
return await releaseRequest;
|
|
450
|
+
} finally{
|
|
451
|
+
releaseRequest = null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async function templateMetaUrls() {
|
|
455
|
+
const urls = [];
|
|
456
|
+
const release = await resolveRelease();
|
|
457
|
+
if (release) urls.push(release.metaUrl);
|
|
458
|
+
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/templates-meta.json`);
|
|
459
|
+
return urls;
|
|
460
|
+
}
|
|
461
|
+
async function templateFileUrls(slug, relativePath) {
|
|
462
|
+
const urls = [];
|
|
463
|
+
const release = await resolveRelease();
|
|
464
|
+
if (release) urls.push(`${release.filesBaseUrl}/${slug}/${relativePath}`);
|
|
465
|
+
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/examples/${slug}/${relativePath}`);
|
|
466
|
+
return urls;
|
|
467
|
+
}
|
|
391
468
|
const CACHE_DIR = node_path.join(node_os.homedir(), ".cache", "extension-js");
|
|
392
469
|
const CACHE_FILE = node_path.join(CACHE_DIR, "templates-meta.json");
|
|
393
470
|
const CACHE_TTL_MS = 3600000;
|
|
394
|
-
const TEMPLATES_META_URL = "https://github.com/extension-js/examples/releases/download/nightly/templates-meta.json";
|
|
395
471
|
function isCacheValid() {
|
|
396
472
|
try {
|
|
397
473
|
const stat = node_fs.statSync(CACHE_FILE);
|
|
@@ -405,17 +481,22 @@ async function fetchTemplatesMeta() {
|
|
|
405
481
|
const cached = JSON.parse(node_fs.readFileSync(CACHE_FILE, "utf8"));
|
|
406
482
|
return cached;
|
|
407
483
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
484
|
+
let lastStatus = 0;
|
|
485
|
+
for (const url of (await templateMetaUrls()))try {
|
|
486
|
+
const response = await fetch(url);
|
|
487
|
+
if (!response.ok) {
|
|
488
|
+
lastStatus = response.status;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const data = await response.json();
|
|
492
|
+
node_fs.mkdirSync(CACHE_DIR, {
|
|
493
|
+
recursive: true
|
|
494
|
+
});
|
|
495
|
+
node_fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
|
|
496
|
+
return data;
|
|
497
|
+
} catch {}
|
|
498
|
+
if (node_fs.existsSync(CACHE_FILE)) return JSON.parse(node_fs.readFileSync(CACHE_FILE, "utf8"));
|
|
499
|
+
throw new Error(`Failed to fetch templates-meta.json (${lastStatus || "network error"}).`);
|
|
419
500
|
}
|
|
420
501
|
async function listTemplates(filters) {
|
|
421
502
|
const meta = await fetchTemplatesMeta();
|
|
@@ -883,6 +964,7 @@ function isGeckoFamily(browser) {
|
|
|
883
964
|
return GECKO_FAMILY.has(browser);
|
|
884
965
|
}
|
|
885
966
|
const CARRIER_DIR_NAME = "extension-dev-live-preview";
|
|
967
|
+
const CARRIER_EXTENSION_ID = "ibppeifnekhjjjmpjfiobccjlicbmgcb";
|
|
886
968
|
const MARKER_FILE = "managed-by-extension-dev-mcp.json";
|
|
887
969
|
function deriveCarrierId(source) {
|
|
888
970
|
try {
|
|
@@ -1005,6 +1087,7 @@ function materializeCarrier(projectPath, browser) {
|
|
|
1005
1087
|
"Bridged calls run under the CARRIER's identity, not your extension's. The preview assumes a single active guest and does not namespace per-extension state, so storage, action/badge state, messaging delivery, offscreen documents and relative script paths belong to the carrier. Rows affected are badged carrier-scoped in the Trace tab.",
|
|
1006
1088
|
"Chromium-family only: Firefox has no externally_connectable channel for web pages."
|
|
1007
1089
|
],
|
|
1090
|
+
graduation: "The carrier lane is the SHARED real lane: bridged calls run as the carrier, by design (see limitations). Your guest is already loaded as ITSELF in this same session, so for its own storage, identity, badge and messaging (the isolated real thing), drive the guest directly instead of the carrier bridge: extension_storage, extension_eval and extension_dom_inspect against this projectPath all operate on the guest as itself. Start (or replace) this session with allowControl: true (or allowEval: true) to unlock them. Use the carrier bridge for the shared real-lane TRACE; use the control verbs for the guest's OWN state.",
|
|
1008
1091
|
...carrierId ? {
|
|
1009
1092
|
bridgeProtocol: {
|
|
1010
1093
|
carrierExtensionId: carrierId,
|
|
@@ -1751,6 +1834,7 @@ async function dev_handler(args) {
|
|
|
1751
1834
|
child.on("exit", ()=>removeSession(args.projectPath, browser));
|
|
1752
1835
|
await new Promise((resolve)=>setTimeout(resolve, 3000));
|
|
1753
1836
|
const earlyOutput = spawned.readOutput();
|
|
1837
|
+
const cleanOutput = denoiseEarlyOutput(earlyOutput);
|
|
1754
1838
|
if (null !== child.exitCode || null !== child.signalCode) {
|
|
1755
1839
|
const code = child.exitCode;
|
|
1756
1840
|
const signal = child.signalCode;
|
|
@@ -1763,12 +1847,12 @@ async function dev_handler(args) {
|
|
|
1763
1847
|
exitCode: code,
|
|
1764
1848
|
signal,
|
|
1765
1849
|
error: `The dev server exited during startup (${signal ? `signal ${signal}` : `exit code ${code}`}). No session is running, so extension_logs/wait/eval and the control verbs have nothing to attach to.`,
|
|
1766
|
-
output:
|
|
1850
|
+
output: cleanOutput.slice(0, 2000),
|
|
1767
1851
|
logPath,
|
|
1768
1852
|
hint: "Read `output` above for the cause: a port already in use, a manifest the build rejects, or a missing browser binary are the common ones. Fix it and call extension_dev again; extension_doctor with this projectPath will also report what the last session recorded."
|
|
1769
1853
|
});
|
|
1770
1854
|
}
|
|
1771
|
-
const compileFailed = /compiled with errors|✖✖✖|ERROR in |Module not found|NOT FOUND/i.test(
|
|
1855
|
+
const compileFailed = /compiled with errors|✖✖✖|ERROR in |Module not found|NOT FOUND/i.test(cleanOutput);
|
|
1772
1856
|
if (compileFailed) return JSON.stringify({
|
|
1773
1857
|
ok: false,
|
|
1774
1858
|
status: "compile-failed",
|
|
@@ -1776,12 +1860,12 @@ async function dev_handler(args) {
|
|
|
1776
1860
|
browser,
|
|
1777
1861
|
pid,
|
|
1778
1862
|
error: "The dev server started but the FIRST COMPILE FAILED, so the browser has nothing usable to load. The session is running; the extension is not.",
|
|
1779
|
-
output:
|
|
1863
|
+
output: cleanOutput.slice(0, 2000),
|
|
1780
1864
|
logPath,
|
|
1781
1865
|
hint: "Fix the compile error in `output` above and save: the dev server is still running and will recompile. Do not call extension_wait yet, it will report ready for a build that failed."
|
|
1782
1866
|
});
|
|
1783
1867
|
const exitStamp = args.noBrowser ? null : browserExitStamp(args.projectPath, browser, spawnedAt);
|
|
1784
|
-
const profileLockHit = !args.noBrowser && /SingletonLock|ProcessSingleton|profile[^\n]*(in use|locked)|already (open|running)/i.test(
|
|
1868
|
+
const profileLockHit = !args.noBrowser && /SingletonLock|ProcessSingleton|profile[^\n]*(in use|locked)|already (open|running)/i.test(cleanOutput);
|
|
1785
1869
|
if (exitStamp || profileLockHit) {
|
|
1786
1870
|
const profileDir = node_path.join(args.projectPath, "dist", `extension-profile-${browser}`);
|
|
1787
1871
|
return JSON.stringify({
|
|
@@ -1792,7 +1876,7 @@ async function dev_handler(args) {
|
|
|
1792
1876
|
pid,
|
|
1793
1877
|
...exitStamp ?? {},
|
|
1794
1878
|
error: `The dev server is running but the ${browser} browser it launched died during startup` + (profileLockHit ? " because its profile is locked by another browser instance." : "."),
|
|
1795
|
-
output:
|
|
1879
|
+
output: cleanOutput.slice(0, 2000),
|
|
1796
1880
|
logPath,
|
|
1797
1881
|
hint: `A locked profile means another session's browser still holds it: call extension_stop with this projectPath to kill that session, then start extension_dev again. If the lock survives a crash, remove ${profileDir} manually before retrying.`
|
|
1798
1882
|
});
|
|
@@ -1840,7 +1924,7 @@ async function dev_handler(args) {
|
|
|
1840
1924
|
} : {},
|
|
1841
1925
|
capabilities,
|
|
1842
1926
|
hint: args.noBrowser ? "Build-only session (noBrowser: true): no browser will launch, so no runtime will ever attach. extension_wait returns as soon as the first compile lands (compiled: true, browserAttached: false) instead of waiting out its budget; do not wait for a browser. The control verbs (storage/reload/open/dom_inspect/eval) need a live browser and will not work against this session. When you are done, call extension_stop to shut down the dev server." : "Use extension_wait to check when the extension is fully loaded, then extension_source_inspect to inspect the live state. " + (allowControl ? `Control channel is ON: extension_${controlVerbs.split(", ").join("/extension_")}${args.allowEval ? "/extension_eval" : ""} will work against this session.` : "Control channel is OFF: extension_storage/reload/open/dom_inspect need allowControl: true, and extension_eval needs allowEval: true (which also implies allowControl). To unlock them, call extension_dev again with the flag you need plus replace: true (it stops this session first); a plain second call is refused so the session does not fork.") + " When you are done, call extension_stop to shut down the dev server and browser.",
|
|
1843
|
-
earlyOutput:
|
|
1927
|
+
earlyOutput: cleanOutput.slice(0, 500),
|
|
1844
1928
|
logPath
|
|
1845
1929
|
});
|
|
1846
1930
|
}
|
|
@@ -1849,6 +1933,8 @@ function denoiseEarlyOutput(raw) {
|
|
|
1849
1933
|
/^npm warn Unknown project config/i,
|
|
1850
1934
|
/This will stop working in the next major version of npm/i,
|
|
1851
1935
|
/^npm warn config/i,
|
|
1936
|
+
/^npm warn exec/i,
|
|
1937
|
+
/The following package(s)? (was|were) not found and will be installed/i,
|
|
1852
1938
|
/V8: .*Invalid asm\.js/i,
|
|
1853
1939
|
/^\(node:\d+\) V8:/i,
|
|
1854
1940
|
/Use `node --trace-warnings/i,
|
|
@@ -2092,7 +2178,6 @@ async function preview_handler(args) {
|
|
|
2092
2178
|
logPath
|
|
2093
2179
|
});
|
|
2094
2180
|
}
|
|
2095
|
-
const RAW_BASE = "https://raw.githubusercontent.com/extension-js/examples/main/examples";
|
|
2096
2181
|
const get_template_source_schema = {
|
|
2097
2182
|
name: "extension_get_template_source",
|
|
2098
2183
|
description: "Read source files from a template in the extension.dev template catalog. Use this to learn implementation patterns before building something similar.",
|
|
@@ -2140,14 +2225,17 @@ async function get_template_source_handler(args) {
|
|
|
2140
2225
|
const fileContents = {};
|
|
2141
2226
|
const errors = [];
|
|
2142
2227
|
await Promise.all(args.files.map(async (filePath)=>{
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2228
|
+
const urls = await templateFileUrls(args.slug, filePath);
|
|
2229
|
+
let lastStatus = 0;
|
|
2230
|
+
for (const url of urls)try {
|
|
2145
2231
|
const response = await fetch(url);
|
|
2146
|
-
if (response.ok)
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2232
|
+
if (response.ok) {
|
|
2233
|
+
fileContents[filePath] = await response.text();
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
lastStatus = response.status;
|
|
2237
|
+
} catch {}
|
|
2238
|
+
errors.push(`${filePath}: ${lastStatus || "fetch failed"}`);
|
|
2151
2239
|
}));
|
|
2152
2240
|
return JSON.stringify({
|
|
2153
2241
|
...meta,
|
|
@@ -5303,6 +5391,11 @@ const open_schema = {
|
|
|
5303
5391
|
]
|
|
5304
5392
|
}
|
|
5305
5393
|
};
|
|
5394
|
+
function sessionIsHeadless() {
|
|
5395
|
+
if (/^(1|true)$/i.test(process.env.EXTENSION_HEADLESS ?? "")) return true;
|
|
5396
|
+
return /(^|\s|=)-{1,2}headless\b/i.test(process.env.EXTENSION_BROWSER_FLAGS ?? "");
|
|
5397
|
+
}
|
|
5398
|
+
const HEADED_RELAUNCH = "start a headed session: extension_dev with replace: true, and in the environment set EXTENSION_HEADLESS=0 AND clear EXTENSION_BROWSER_FLAGS (it may carry --headless=new, which keeps the window hidden even with EXTENSION_HEADLESS=0)";
|
|
5306
5399
|
async function open_handler(args) {
|
|
5307
5400
|
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
5308
5401
|
if (args.url) return navigateToUrl(args.projectPath, browser, args.url, args.timeout);
|
|
@@ -5347,7 +5440,7 @@ async function open_handler(args) {
|
|
|
5347
5440
|
cli.push("--browser", browser);
|
|
5348
5441
|
if (null != args.timeout) cli.push("--timeout", String(args.timeout));
|
|
5349
5442
|
const raw = await runActVerb(cli, args.projectPath, args.timeout);
|
|
5350
|
-
const headless =
|
|
5443
|
+
const headless = sessionIsHeadless();
|
|
5351
5444
|
if (headless && [
|
|
5352
5445
|
"popup",
|
|
5353
5446
|
"action",
|
|
@@ -5361,12 +5454,12 @@ async function open_handler(args) {
|
|
|
5361
5454
|
try {
|
|
5362
5455
|
const parsedFallback = JSON.parse(fallback);
|
|
5363
5456
|
if (parsedFallback?.ok) {
|
|
5364
|
-
parsedFallback.note =
|
|
5457
|
+
parsedFallback.note = `The dev browser is headless, and a real popup/sidebar window can only open in a headed session, so the surface was rendered as a tab instead. For the real window, ${HEADED_RELAUNCH}, then open the surface again without asTab.`;
|
|
5365
5458
|
return JSON.stringify(parsedFallback);
|
|
5366
5459
|
}
|
|
5367
5460
|
} catch {}
|
|
5368
5461
|
}
|
|
5369
|
-
if (!parsed.hint) parsed.hint = /user gesture/i.test(msg) ? "This surface can only open from a real user gesture, which headless automation cannot produce. Retry with asTab: true to render the surface document in a tab instead." :
|
|
5462
|
+
if (!parsed.hint) parsed.hint = /user gesture/i.test(msg) ? "This surface can only open from a real user gesture, which headless automation cannot produce. Retry with asTab: true to render the surface document in a tab instead." : `The dev browser is running headless, and a popup/sidebar window needs a headed session. Retry with asTab: true to render the surface document in a tab, or for the real window, ${HEADED_RELAUNCH}.`;
|
|
5370
5463
|
return JSON.stringify(parsed);
|
|
5371
5464
|
}
|
|
5372
5465
|
} catch {}
|
|
@@ -5752,11 +5845,78 @@ function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
|
5752
5845
|
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
5753
5846
|
return creds;
|
|
5754
5847
|
}
|
|
5755
|
-
const
|
|
5756
|
-
|
|
5848
|
+
const PROD_ORIGINS = {
|
|
5849
|
+
www: "https://www.extension.dev",
|
|
5850
|
+
console: "https://console.extension.dev",
|
|
5851
|
+
inspect: "https://inspect.extension.dev",
|
|
5852
|
+
templates: "https://templates.extension.dev",
|
|
5853
|
+
intelligence: "https://intelligence.extension.dev",
|
|
5854
|
+
registry: "https://registry.extension.land",
|
|
5855
|
+
media: "https://media.extension.land"
|
|
5856
|
+
};
|
|
5857
|
+
const DEV_LOCALHOST_ORIGINS = {
|
|
5858
|
+
www: "http://localhost:3100",
|
|
5859
|
+
console: "http://console.extension.localhost",
|
|
5860
|
+
inspect: "http://inspect.extension.localhost",
|
|
5861
|
+
templates: "http://templates.extension.localhost",
|
|
5862
|
+
intelligence: "http://intelligence.extension.localhost",
|
|
5863
|
+
registry: "https://registry.extension.land",
|
|
5864
|
+
media: "https://media.extension.land"
|
|
5865
|
+
};
|
|
5866
|
+
function strip(value) {
|
|
5867
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
5868
|
+
}
|
|
5869
|
+
function isLocalOrigin(url) {
|
|
5870
|
+
const raw = strip(url);
|
|
5871
|
+
if (!raw) return false;
|
|
5872
|
+
let host;
|
|
5873
|
+
try {
|
|
5874
|
+
host = new URL(raw).hostname;
|
|
5875
|
+
} catch {
|
|
5876
|
+
return false;
|
|
5877
|
+
}
|
|
5878
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
5879
|
+
}
|
|
5880
|
+
function resolveOrigins(overrides = {}, opts = {}) {
|
|
5881
|
+
const devLike = isLocalOrigin(overrides.www) || isLocalOrigin(overrides.console) || isLocalOrigin(opts.hint);
|
|
5882
|
+
const base = devLike ? DEV_LOCALHOST_ORIGINS : PROD_ORIGINS;
|
|
5883
|
+
const pick = (key)=>strip(overrides[key]) || base[key];
|
|
5884
|
+
return {
|
|
5885
|
+
www: pick("www"),
|
|
5886
|
+
console: pick("console"),
|
|
5887
|
+
inspect: pick("inspect"),
|
|
5888
|
+
templates: pick("templates"),
|
|
5889
|
+
intelligence: pick("intelligence"),
|
|
5890
|
+
registry: pick("registry"),
|
|
5891
|
+
media: pick("media")
|
|
5892
|
+
};
|
|
5893
|
+
}
|
|
5894
|
+
const seg = (value)=>encodeURIComponent(String(value));
|
|
5895
|
+
function urls_paths_join(base, sub) {
|
|
5896
|
+
if (!sub) return base;
|
|
5897
|
+
return `${base}/${sub.replace(/^\/+/, "")}`;
|
|
5898
|
+
}
|
|
5899
|
+
function consoleProjectPath(ref, page = "") {
|
|
5900
|
+
return urls_paths_join(`/${seg(ref.workspace)}/${seg(ref.project)}`, page);
|
|
5901
|
+
}
|
|
5902
|
+
PROD_ORIGINS.registry;
|
|
5903
|
+
function mcpOrigins(apiHint) {
|
|
5904
|
+
const www = String(apiHint || process.env.EXTENSION_DEV_API_URL || "").trim() || void 0;
|
|
5905
|
+
return resolveOrigins({
|
|
5906
|
+
www,
|
|
5907
|
+
console: process.env.EXTENSION_DEV_CONSOLE_URL,
|
|
5908
|
+
inspect: process.env.EXTENSION_DEV_INSPECT_URL,
|
|
5909
|
+
registry: process.env.EXTENSION_DEV_REGISTRY_URL,
|
|
5910
|
+
media: process.env.EXTENSION_MEDIA_ORIGIN
|
|
5911
|
+
}, {
|
|
5912
|
+
hint: www
|
|
5913
|
+
});
|
|
5914
|
+
}
|
|
5915
|
+
function consoleBase(apiHint) {
|
|
5916
|
+
return mcpOrigins(apiHint).console;
|
|
5917
|
+
}
|
|
5757
5918
|
function registryBase() {
|
|
5758
|
-
|
|
5759
|
-
return (fromEnv || REGISTRY_BASE_DEFAULT).replace(/\/+$/, "");
|
|
5919
|
+
return mcpOrigins().registry;
|
|
5760
5920
|
}
|
|
5761
5921
|
function resolveProjectRef(overrides) {
|
|
5762
5922
|
const workspace = String(overrides?.workspace || "").trim();
|
|
@@ -5777,9 +5937,10 @@ function resolveProjectRef(overrides) {
|
|
|
5777
5937
|
function registryFileUrl(ref, file) {
|
|
5778
5938
|
return `${registryBase()}/${encodeURIComponent(ref.workspace)}/${encodeURIComponent(ref.project)}/_extension-dev/${file}`;
|
|
5779
5939
|
}
|
|
5780
|
-
function consoleProjectUrl(ref, page) {
|
|
5781
|
-
|
|
5782
|
-
|
|
5940
|
+
function consoleProjectUrl(ref, page, apiHint) {
|
|
5941
|
+
const base = consoleBase(apiHint);
|
|
5942
|
+
if (!ref) return base;
|
|
5943
|
+
return `${base}${consoleProjectPath(ref, page)}`;
|
|
5783
5944
|
}
|
|
5784
5945
|
async function fetchRegistryJson(url, fetchImpl = fetch) {
|
|
5785
5946
|
let res;
|
|
@@ -5855,12 +6016,12 @@ function parseBuildIndex(json) {
|
|
|
5855
6016
|
}
|
|
5856
6017
|
return out;
|
|
5857
6018
|
}
|
|
5858
|
-
const DEFAULT_API =
|
|
6019
|
+
const DEFAULT_API = PROD_ORIGINS.www;
|
|
5859
6020
|
function tokenTtlNote(workspaceSlug, projectSlug) {
|
|
5860
6021
|
const tokensUrl = workspaceSlug && projectSlug ? consoleProjectUrl({
|
|
5861
6022
|
workspace: workspaceSlug,
|
|
5862
6023
|
project: projectSlug
|
|
5863
|
-
}, "settings/access-tokens") :
|
|
6024
|
+
}, "settings/access-tokens") : consoleBase();
|
|
5864
6025
|
return `extension.dev CLI tokens live at most 7 days (server-enforced). CI pipelines must re-mint before expiry on the console's Access tokens page: ${tokensUrl}`;
|
|
5865
6026
|
}
|
|
5866
6027
|
function resolveApiBase(api) {
|
|
@@ -6101,7 +6262,6 @@ async function publish_handler(args) {
|
|
|
6101
6262
|
}
|
|
6102
6263
|
return JSON.stringify(data);
|
|
6103
6264
|
}
|
|
6104
|
-
const release_promote_DEFAULT_API = "https://www.extension.dev";
|
|
6105
6265
|
const release_promote_schema = {
|
|
6106
6266
|
name: "extension_release_promote",
|
|
6107
6267
|
description: "Promote a built extension to a release channel (e.g. stable, preview, beta) on extension.dev, headless. Auth-gated: uses your stored login (extension_login) or a release token in EXTENSION_DEV_TOKEN (mint and revoke it in the dashboard under project settings -> Access tokens; tokens live at most 7 days, so CI must re-mint before expiry). Posts to the platform's CLI release endpoint; the project is identified by the token. Cutting a version-bump PR is not available headlessly (it writes to your source repo and needs an interactive login).",
|
|
@@ -6161,7 +6321,7 @@ async function release_promote_handler(args) {
|
|
|
6161
6321
|
const buildId = String(args.buildId || "").trim();
|
|
6162
6322
|
const channel = String(args.channel || "").trim();
|
|
6163
6323
|
if (!buildId || !channel) return release_promote_fail("ReleaseInputError", "buildId and channel are required.");
|
|
6164
|
-
const apiCheck = safeApiBase(
|
|
6324
|
+
const apiCheck = safeApiBase(resolveApiBase(args.api));
|
|
6165
6325
|
if (!apiCheck.ok) return release_promote_fail("ReleaseConfigError", apiCheck.message);
|
|
6166
6326
|
const url = `${apiCheck.base}/api/cli/release/promote`;
|
|
6167
6327
|
const body = {
|
|
@@ -6199,7 +6359,7 @@ async function release_promote_handler(args) {
|
|
|
6199
6359
|
const enrich = {};
|
|
6200
6360
|
const ref = resolveProjectRef();
|
|
6201
6361
|
if (404 === res.status || "UNKNOWN_BUILD" === code) {
|
|
6202
|
-
enrich.buildsPageUrl = consoleProjectUrl(ref, "builds");
|
|
6362
|
+
enrich.buildsPageUrl = consoleProjectUrl(ref, "builds", args.api);
|
|
6203
6363
|
enrich.hint = "Run extension_release_list to see this project's channels, their promoted shas, and recent builds.";
|
|
6204
6364
|
if (ref) {
|
|
6205
6365
|
const channelsUrl = registryFileUrl(ref, "channels.json");
|
|
@@ -6299,7 +6459,6 @@ async function release_list_handler(args) {
|
|
|
6299
6459
|
if (!buildsRes.ok) result.buildsUnavailable = `builds/index.json unreadable: ${buildsRes.message}`;
|
|
6300
6460
|
return JSON.stringify(result);
|
|
6301
6461
|
}
|
|
6302
|
-
const deploy_DEFAULT_API = "https://www.extension.dev";
|
|
6303
6462
|
function storeMdWarnings(browsers, cwd) {
|
|
6304
6463
|
const wantsFirefox = browsers.includes("firefox");
|
|
6305
6464
|
const wantsEdge = browsers.includes("edge");
|
|
@@ -6389,7 +6548,7 @@ async function deploy_handler(args) {
|
|
|
6389
6548
|
if (0 === browsers.length) return deploy_fail("DeployInputError", 'browsers is required (e.g. ["chrome","firefox","edge","safari"]).');
|
|
6390
6549
|
const buildSha = String(args.buildSha || "").trim();
|
|
6391
6550
|
if (!buildSha) return deploy_fail("DeployInputError", "buildSha is required (the built commit to submit).");
|
|
6392
|
-
const apiCheck = safeApiBase(
|
|
6551
|
+
const apiCheck = safeApiBase(resolveApiBase(args.api));
|
|
6393
6552
|
if (!apiCheck.ok) return deploy_fail("DeployConfigError", apiCheck.message);
|
|
6394
6553
|
const url = `${apiCheck.base}/api/cli/stores/submit`;
|
|
6395
6554
|
const dryRun = false !== args.dryRun;
|
|
@@ -6434,7 +6593,7 @@ async function deploy_handler(args) {
|
|
|
6434
6593
|
};
|
|
6435
6594
|
if (dryRun) {
|
|
6436
6595
|
const ref = resolveProjectRef();
|
|
6437
|
-
const consoleStoresUrl = consoleProjectUrl(ref, "stores");
|
|
6596
|
+
const consoleStoresUrl = consoleProjectUrl(ref, "stores", args.api);
|
|
6438
6597
|
const storeModeNote = `Store publish mode (draft / skip-publish / live) is not readable with the CLI token, so it cannot be verified from here; check per-store settings at ${consoleStoresUrl}.`;
|
|
6439
6598
|
let health = null;
|
|
6440
6599
|
let healthUnreadable = null;
|
|
@@ -6710,6 +6869,63 @@ async function store_status_handler(args) {
|
|
|
6710
6869
|
if (!submissionsRes.ok && 404 !== submissionsRes.status) result.submissionsUnavailable = `stores/submissions.json unreadable: ${submissionsRes.message}`;
|
|
6711
6870
|
return JSON.stringify(result);
|
|
6712
6871
|
}
|
|
6872
|
+
const ENGINE_COMPANION_IDS = new Set([
|
|
6873
|
+
"kgdaecdpfkikjncaalnmmnjjfpofkcbl",
|
|
6874
|
+
CARRIER_EXTENSION_ID
|
|
6875
|
+
]);
|
|
6876
|
+
const EXTENSION_URL = /^chrome-extension:\/\/([a-p]{32})\//i;
|
|
6877
|
+
async function verifyGuestLoaded(projectPath, browser, options) {
|
|
6878
|
+
let cdpPort;
|
|
6879
|
+
try {
|
|
6880
|
+
const resolved = await resolveCdpPort(projectPath, browser, {
|
|
6881
|
+
waitMs: options?.waitMs ?? 0
|
|
6882
|
+
});
|
|
6883
|
+
if (!resolved) return {
|
|
6884
|
+
checked: false,
|
|
6885
|
+
loaded: false,
|
|
6886
|
+
guestTargets: [],
|
|
6887
|
+
guestIds: [],
|
|
6888
|
+
reason: "No CDP port in the session's ready contract, so the browser's target list could not be queried (a headless Chromium session still exposes one; a gecko/Firefox session does not)."
|
|
6889
|
+
};
|
|
6890
|
+
cdpPort = resolved.port;
|
|
6891
|
+
const timeoutMs = options?.timeoutMs ?? 3000;
|
|
6892
|
+
const targets = await Promise.race([
|
|
6893
|
+
CDPClient.discoverTargets(cdpPort),
|
|
6894
|
+
new Promise((_, reject)=>setTimeout(()=>reject(new Error(`CDP /json timed out after ${timeoutMs}ms`)), timeoutMs))
|
|
6895
|
+
]);
|
|
6896
|
+
const guestTargets = [];
|
|
6897
|
+
for (const t of targets){
|
|
6898
|
+
const match = EXTENSION_URL.exec(String(t.url ?? ""));
|
|
6899
|
+
if (!match) continue;
|
|
6900
|
+
const id = match[1].toLowerCase();
|
|
6901
|
+
if (!ENGINE_COMPANION_IDS.has(id)) guestTargets.push({
|
|
6902
|
+
id,
|
|
6903
|
+
type: String(t.type),
|
|
6904
|
+
url: String(t.url)
|
|
6905
|
+
});
|
|
6906
|
+
}
|
|
6907
|
+
const guestIds = [
|
|
6908
|
+
...new Set(guestTargets.map((t)=>t.id))
|
|
6909
|
+
];
|
|
6910
|
+
return {
|
|
6911
|
+
checked: true,
|
|
6912
|
+
loaded: guestTargets.length > 0,
|
|
6913
|
+
guestTargets,
|
|
6914
|
+
guestIds,
|
|
6915
|
+
cdpPort,
|
|
6916
|
+
reason: guestTargets.length > 0 ? `The browser lists ${guestIds.length} extension target${1 === guestIds.length ? "" : "s"} that are not the engine companion (${guestIds.join(", ")}), so the guest is loaded.` : "The browser's target list has no chrome-extension:// target other than the engine's devtools companion. On Chrome this is the signature of a silently rejected --load-extension (extension.js BUGS_TO_FIX §83): the CLI and ready.json cannot see it."
|
|
6917
|
+
};
|
|
6918
|
+
} catch (err) {
|
|
6919
|
+
return {
|
|
6920
|
+
checked: false,
|
|
6921
|
+
loaded: false,
|
|
6922
|
+
guestTargets: [],
|
|
6923
|
+
guestIds: [],
|
|
6924
|
+
cdpPort,
|
|
6925
|
+
reason: `Could not query the browser's target list${cdpPort ? ` on CDP port ${cdpPort}` : ""}: ${err?.message ?? String(err)}.`
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6713
6929
|
function doctor_readReadyContract(projectPath, browser) {
|
|
6714
6930
|
try {
|
|
6715
6931
|
const raw = node_fs.readFileSync(node_path.resolve(projectPath, "dist", "extension-js", browser, "ready.json"), "utf8");
|
|
@@ -6905,7 +7121,7 @@ function wait_isAlive(pid) {
|
|
|
6905
7121
|
}
|
|
6906
7122
|
const wait_schema = {
|
|
6907
7123
|
name: "extension_wait",
|
|
6908
|
-
description: "Wait for a running dev or start session to be ready. Polls the ready.json contract file and returns structured status with
|
|
7124
|
+
description: "Wait for a running dev or start session to be ready. Polls the ready.json contract file and returns structured status with these facts: compiled (the compiler finished), browserAttached (the engine's runtime executor connected), and for a browser session guestLoaded (the browser's OWN target list actually shows your extension). guestLoaded is the trustworthy load signal: it catches a silently rejected --load-extension that leaves ready.json stamped attached with empty logs (extension.js BUGS_TO_FIX §83); it is null when it could not be checked (no CDP port, e.g. a gecko session). Every result reports budgetMs (this call's wait budget) and elapsedMs; on status:'timeout' call again to keep waiting (polling resumes on the same contract). In a noBrowser (build-only) session it returns as soon as the compile lands instead of waiting for a browser that will never attach. Ports in the result come from the ready contract, so they always match what the dev server actually bound.",
|
|
6909
7125
|
inputSchema: {
|
|
6910
7126
|
type: "object",
|
|
6911
7127
|
properties: {
|
|
@@ -6981,10 +7197,20 @@ async function wait_handler(args) {
|
|
|
6981
7197
|
continue;
|
|
6982
7198
|
}
|
|
6983
7199
|
const runtimeErrors = recentErrorLogs(args.projectPath, browser, 3);
|
|
7200
|
+
const guestCheck = await verifyGuestLoaded(args.projectPath, browser);
|
|
7201
|
+
const warnings = [];
|
|
7202
|
+
if (runtimeErrors.length) warnings.push(`Compiled and attached, but the extension is throwing at runtime (${runtimeErrors.length} recent error event${1 === runtimeErrors.length ? "" : "s"} above). Check extension_logs (level: error) or extension_doctor before trusting this session.`);
|
|
7203
|
+
if (guestCheck.checked && !guestCheck.loaded) warnings.push("The engine reports the runtime attached, but the browser's own target list shows no chrome-extension:// target for your extension, only the engine companion. This is the signature of a silently rejected --load-extension (extension.js BUGS_TO_FIX §83): the CLI and ready.json cannot see it, and the control verbs will fail against a guest that is not there. Check the manifest and extension_logs.");
|
|
6984
7204
|
return JSON.stringify({
|
|
6985
7205
|
status: "ready",
|
|
6986
7206
|
compiled: true,
|
|
6987
7207
|
browserAttached: true,
|
|
7208
|
+
guestLoaded: guestCheck.checked ? guestCheck.loaded : null,
|
|
7209
|
+
...guestCheck.checked ? {
|
|
7210
|
+
guestIds: guestCheck.guestIds
|
|
7211
|
+
} : {
|
|
7212
|
+
guestLoadNote: guestCheck.reason
|
|
7213
|
+
},
|
|
6988
7214
|
command: contract.command,
|
|
6989
7215
|
browser: contract.browser,
|
|
6990
7216
|
port: contract.port,
|
|
@@ -6996,8 +7222,10 @@ async function wait_handler(args) {
|
|
|
6996
7222
|
budgetMs,
|
|
6997
7223
|
elapsedMs: Date.now() - start,
|
|
6998
7224
|
...runtimeErrors.length ? {
|
|
6999
|
-
runtimeErrors
|
|
7000
|
-
|
|
7225
|
+
runtimeErrors
|
|
7226
|
+
} : {},
|
|
7227
|
+
...warnings.length ? {
|
|
7228
|
+
warning: warnings.join(" ")
|
|
7001
7229
|
} : {}
|
|
7002
7230
|
});
|
|
7003
7231
|
}
|
|
@@ -7696,7 +7924,10 @@ const logout_schema = {
|
|
|
7696
7924
|
};
|
|
7697
7925
|
async function logout_handler() {
|
|
7698
7926
|
const creds = readCredentials();
|
|
7699
|
-
const revokeUrl = creds?.workspaceSlug && creds?.projectSlug ?
|
|
7927
|
+
const revokeUrl = creds?.workspaceSlug && creds?.projectSlug ? consoleProjectUrl({
|
|
7928
|
+
workspace: creds.workspaceSlug,
|
|
7929
|
+
project: creds.projectSlug
|
|
7930
|
+
}, "settings/access-tokens") : null;
|
|
7700
7931
|
const result = clearCredentials();
|
|
7701
7932
|
return JSON.stringify({
|
|
7702
7933
|
ok: true,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* localhost) can then watch the session's real-lane trace and pair with it.
|
|
8
8
|
*/
|
|
9
9
|
export declare const CARRIER_DIR_NAME = "extension-dev-live-preview";
|
|
10
|
+
export declare const CARRIER_EXTENSION_ID = "ibppeifnekhjjjmpjfiobccjlicbmgcb";
|
|
10
11
|
/** Where the carrier lands inside a project. */
|
|
11
12
|
export declare function carrierPath(projectPath: string): string;
|
|
12
13
|
/** True only for a directory carrying our marker, i.e. ours to delete. */
|
|
@@ -49,6 +50,17 @@ export type CarrierMaterialization = {
|
|
|
49
50
|
* was written down anywhere the caller would look.
|
|
50
51
|
*/
|
|
51
52
|
limitations?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* The graduation path, stated next to the shared-identity limitation so the
|
|
55
|
+
* caller is not left at a dead end. The carrier lane is the SHARED real lane
|
|
56
|
+
* (calls run as the carrier); but the guest is ALREADY loaded as itself in
|
|
57
|
+
* this same session, so its OWN storage, identity and messaging are reached by
|
|
58
|
+
* driving the guest directly with the control verbs, not the carrier bridge.
|
|
59
|
+
* This is the agent-lane twin of the inspect Trace tab's "load as your own
|
|
60
|
+
* extension" affordance: here the extension is already loaded, so graduating
|
|
61
|
+
* means targeting it, not launching it.
|
|
62
|
+
*/
|
|
63
|
+
graduation?: string;
|
|
52
64
|
/**
|
|
53
65
|
* How to actually DRIVE the real lane. Seven personas reached a
|
|
54
66
|
* permanently empty trace and concluded the feature was broken, because
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type GuestTarget = {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
url: string;
|
|
5
|
+
};
|
|
6
|
+
export type GuestLoadCheck = {
|
|
7
|
+
checked: boolean;
|
|
8
|
+
loaded: boolean;
|
|
9
|
+
guestTargets: GuestTarget[];
|
|
10
|
+
guestIds: string[];
|
|
11
|
+
cdpPort?: number;
|
|
12
|
+
reason: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The BUGS_TO_FIX §83 oracle: ask the BROWSER whether the guest actually loaded,
|
|
16
|
+
* instead of trusting the engine's ready.json. Chrome refusing --load-extension
|
|
17
|
+
* is invisible to every engine surface (ready.json stamps status:ready, empty
|
|
18
|
+
* logs), so the only trustworthy "did the guest load" signal is a
|
|
19
|
+
* chrome-extension:// target in the browser's own CDP /json list. The engine's
|
|
20
|
+
* devtools companion is always present and is not the guest; a NON-companion
|
|
21
|
+
* extension target is. Never throws: an unreachable endpoint returns
|
|
22
|
+
* checked:false so callers can degrade instead of failing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function verifyGuestLoaded(projectPath: string, browser: string, options?: {
|
|
25
|
+
waitMs?: number;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
}): Promise<GuestLoadCheck>;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
import { type Origins } from "./urls-origins";
|
|
2
|
+
export declare const REGISTRY_BASE_DEFAULT: string;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the fleet origins from the MCP's env vars, once per call. Console,
|
|
5
|
+
* registry, and media all DERIVE from the platform base (`EXTENSION_DEV_API_URL`
|
|
6
|
+
* or a per-tool `api`): point that at a local stack and every dashboard link the
|
|
7
|
+
* MCP hands back follows to the dev host (`console.extension.localhost`) instead
|
|
8
|
+
* of silently pointing at prod. Explicit per-host env vars still win. Falls back
|
|
9
|
+
* to production when nothing is set. The dev-derivation lives in
|
|
10
|
+
* `@extensiondev/urls` so it stays identical to what the apps resolve.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mcpOrigins(apiHint?: string): Origins;
|
|
13
|
+
/** Base origin of the console dashboard, dev-aware (no trailing slash). */
|
|
14
|
+
export declare function consoleBase(apiHint?: string): string;
|
|
3
15
|
export declare function registryBase(): string;
|
|
4
16
|
export interface ProjectRef {
|
|
5
17
|
workspace: string;
|
|
@@ -17,8 +29,14 @@ export declare function resolveProjectRef(overrides?: {
|
|
|
17
29
|
}): ProjectRef | null;
|
|
18
30
|
/** URL of a file under the project's `_extension-dev/` registry directory. */
|
|
19
31
|
export declare function registryFileUrl(ref: ProjectRef, file: string): string;
|
|
20
|
-
/**
|
|
21
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Console page URL for the project (builds, releases/new, stores, ...). The
|
|
34
|
+
* origin is dev-aware; the path comes from the shared route contract so a
|
|
35
|
+
* console route rename is caught in one place. Pass `apiHint` (a tool's `api`
|
|
36
|
+
* arg) so the link matches that call's environment; otherwise it follows
|
|
37
|
+
* `EXTENSION_DEV_API_URL`/`EXTENSION_DEV_CONSOLE_URL`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function consoleProjectUrl(ref: ProjectRef | null, page: string, apiHint?: string): string;
|
|
22
40
|
export type RegistryFetchResult<T> = {
|
|
23
41
|
ok: true;
|
|
24
42
|
json: T;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The set of app/service origins the fleet links between. */
|
|
2
|
+
export interface Origins {
|
|
3
|
+
www: string;
|
|
4
|
+
console: string;
|
|
5
|
+
inspect: string;
|
|
6
|
+
templates: string;
|
|
7
|
+
intelligence: string;
|
|
8
|
+
/** Public release-state JSON host (registry.extension.land). */
|
|
9
|
+
registry: string;
|
|
10
|
+
/** Content-addressed template corpus host (media.extension.land). */
|
|
11
|
+
media: string;
|
|
12
|
+
}
|
|
13
|
+
/** Production origins. The prod fallback for every resolver in the fleet. */
|
|
14
|
+
export declare const PROD_ORIGINS: Origins;
|
|
15
|
+
export declare const DEV_LOCALHOST_ORIGINS: Origins;
|
|
16
|
+
/**
|
|
17
|
+
* True for `localhost`, `127.0.0.1`, `[::1]`, or any `*.extension.localhost`
|
|
18
|
+
* host -- the shapes the dev proxy serves. Anything unparseable is treated as
|
|
19
|
+
* not-local so an odd value falls back to prod rather than leaking a dev host.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isLocalOrigin(url: string | undefined | null): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the full origin set from a caller's overrides.
|
|
24
|
+
*
|
|
25
|
+
* Precedence per origin: explicit override -> derived base (dev vs prod) ->
|
|
26
|
+
* prod. The base is chosen by whether the environment looks local: if any of
|
|
27
|
+
* `www`/`console`/`hint` points at a local host, unset origins derive from the
|
|
28
|
+
* Caddy dev map instead of prod. This is what lets an operator set only
|
|
29
|
+
* `EXTENSION_DEV_API_URL=http://localhost:3100` and still get a console link at
|
|
30
|
+
* `console.extension.localhost` rather than one that silently points at prod.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveOrigins(overrides?: Partial<Origins>, opts?: {
|
|
33
|
+
hint?: string;
|
|
34
|
+
}): Origins;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** A project is addressed by its workspace slug + project slug across the fleet. */
|
|
2
|
+
export interface ProjectRef {
|
|
3
|
+
workspace: string;
|
|
4
|
+
project: string;
|
|
5
|
+
}
|
|
6
|
+
/** `/:workspace` (or a sub-page like `settings` / `settings/:section`). */
|
|
7
|
+
export declare function consoleWorkspacePath(workspace: string, page?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* `/:workspace/:project/<page>`. Pass a page tail from `ConsoleProjectPage`
|
|
10
|
+
* (e.g. `ConsoleProjectPage.builds`) rather than hand-typing it, so a route
|
|
11
|
+
* rename is a one-line change here instead of a scattered string edit.
|
|
12
|
+
*/
|
|
13
|
+
export declare function consoleProjectPath(ref: ProjectRef, page?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Named project-page tails. Values are the exact segments under
|
|
16
|
+
* `/:username/:reponame/` in console's route table; deep pages take ids.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ConsoleProjectPage: {
|
|
19
|
+
readonly overview: "";
|
|
20
|
+
readonly onboard: "onboard";
|
|
21
|
+
readonly activity: "activity";
|
|
22
|
+
readonly builds: "builds";
|
|
23
|
+
readonly build: (buildId: string, browser?: string) => string;
|
|
24
|
+
readonly releases: "releases";
|
|
25
|
+
readonly releasesNew: "releases/new";
|
|
26
|
+
readonly release: (releaseId: string) => string;
|
|
27
|
+
readonly stores: "stores";
|
|
28
|
+
readonly storesNew: "stores/new";
|
|
29
|
+
readonly store: (store: string) => string;
|
|
30
|
+
readonly storeSubmissions: (store: string) => string;
|
|
31
|
+
readonly storeSubmissionNew: (store: string) => string;
|
|
32
|
+
readonly storeSubmission: (store: string, submissionId: string) => string;
|
|
33
|
+
readonly projectSettings: "project-settings";
|
|
34
|
+
readonly projectSettingsSection: (section: string) => string;
|
|
35
|
+
/** Where CLI/MCP tokens are minted and revoked. */
|
|
36
|
+
readonly accessTokens: "settings/access-tokens";
|
|
37
|
+
};
|
|
38
|
+
export type QueryValue = string | number | boolean | null | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* `/new?...` -- project creation. The public deep-link contract: external
|
|
41
|
+
* READMEs use `extension.dev/new?template=<slug>`, which www normalizes to
|
|
42
|
+
* `/import` preserving every param. Do not change the `template` key.
|
|
43
|
+
*/
|
|
44
|
+
export declare function wwwNewPath(query?: Record<string, QueryValue>): string;
|
|
45
|
+
/** `/import?...` -- the ported import/deploy flow templates' Deploy buttons target. */
|
|
46
|
+
export declare function wwwImportPath(query?: Record<string, QueryValue>): string;
|
|
47
|
+
/** `/device` -- CLI/MCP device-code consent. Lives on www, never console. */
|
|
48
|
+
export declare function wwwDevicePath(): string;
|
|
49
|
+
/** `/templates` and `/templates/:slug` -- externally linkable gallery entry on www. */
|
|
50
|
+
export declare function wwwTemplatesPath(slug?: string): string;
|
|
51
|
+
export type TemplateTab = "preview" | "instructions" | "source";
|
|
52
|
+
/** `/:slug` for the default `preview` tab, else `/:slug/<tab>`. */
|
|
53
|
+
export declare function templateTabPath(slug: string, tab?: TemplateTab): string;
|
|
54
|
+
export type InspectTab = "preview" | "details" | "source" | "trace";
|
|
55
|
+
/** `/` for the default `preview` tab, else `/details` / `/source` / `/trace`. */
|
|
56
|
+
export declare function inspectTabPath(tab?: InspectTab): string;
|
package/dist/src/tools/open.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 33 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, Safari, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf). Powered by extension.dev and Extension.js.",
|
|
6
6
|
"mcpName": "io.github.extensiondev/mcp",
|
|
7
|
-
"license": "
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Cezar Augusto",
|
|
10
10
|
"email": "hello@extension.dev",
|
|
@@ -42,7 +42,9 @@
|
|
|
42
42
|
"server.json",
|
|
43
43
|
"README.md",
|
|
44
44
|
"CHANGELOG.md",
|
|
45
|
-
"LICENSE"
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"NOTICE",
|
|
47
|
+
"TRADEMARK.md"
|
|
46
48
|
],
|
|
47
49
|
"scripts": {
|
|
48
50
|
"clean": "rm -rf dist",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "
|
|
10
|
+
"version": "6.0.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@extension.dev/mcp",
|
|
16
|
-
"version": "
|
|
16
|
+
"version": "6.0.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|