@extension.dev/mcp 6.0.0 → 6.2.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 +46 -0
- package/dist/module.js +448 -258
- package/dist/rslib-runtime.js +2 -0
- package/dist/src/lib/registry.d.ts +2 -2
- package/dist/src/lib/template-artifact-source.d.ts +2 -0
- package/package.json +2 -1
- package/server.json +2 -2
- package/dist/src/lib/urls-origins.d.ts +0 -34
- package/dist/src/lib/urls-paths.d.ts +0 -56
|
@@ -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": "6.
|
|
13
|
+
"version": "6.2.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": "6.
|
|
4
|
+
"version": "6.2.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.2.0
|
|
4
|
+
|
|
5
|
+
The URL layer stops being a copy. This server used to carry byte-identical
|
|
6
|
+
vendored mirrors of the fleet's origin resolver and path builders, kept honest
|
|
7
|
+
by a drift guard; it now depends on the published package instead.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Depends on `@extension.dev/urls` instead of vendoring it.** The mirrors at
|
|
12
|
+
`lib/urls-origins.ts` and `lib/urls-paths.ts` are gone, and `registry.ts`,
|
|
13
|
+
`login-flow.ts`, and `create.ts` import the package directly. It is bundled
|
|
14
|
+
into `dist`, so the install footprint is unchanged: no new runtime
|
|
15
|
+
dependency, same standalone server.
|
|
16
|
+
- **`preview.extension.dev` resolves like every other fleet origin.** `preview`
|
|
17
|
+
is now a first-class origin in the shared resolver, so `EXTENSION_DEV_PREVIEW_URL`
|
|
18
|
+
is honored and an unset preview host follows the same local-vs-prod signal as
|
|
19
|
+
console, inspect, and registry rather than defaulting to production.
|
|
20
|
+
|
|
21
|
+
## 6.1.0
|
|
22
|
+
|
|
23
|
+
The create flow stops dead-ending at `run dev` and points you to the web to
|
|
24
|
+
host, template source resolves whichever path the catalog listed, and the
|
|
25
|
+
links the server hands back ride the exact corpus commit it built from.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`extension_create` signposts the web deploy.** The result now carries a
|
|
30
|
+
`deployUrl` and a closing next step that says the scaffold runs locally and
|
|
31
|
+
where to open the template on the web to host it, so the local scaffold no
|
|
32
|
+
longer stops at `run dev` with nowhere to ship.
|
|
33
|
+
- **`extension_create` names the template it chose.** When no `template` is
|
|
34
|
+
passed the response now discloses the silent default instead of quietly
|
|
35
|
+
scaffolding TypeScript, and points at `extension_list_templates` to pick
|
|
36
|
+
another.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **`extension_get_template_source` resolves listed paths.** A file listed
|
|
41
|
+
with a leading `public/<slug>/` or `examples/<slug>/` prefix now strips to
|
|
42
|
+
the slug relative path both hosts actually serve, so passing back a listed
|
|
43
|
+
path no longer 404s.
|
|
44
|
+
- **`extension_list_templates` keeps the vanilla template filterable.** The
|
|
45
|
+
relabel no longer drops the framework key the filter reads.
|
|
46
|
+
- **`extension_add_feature` links ride the pinned corpus.** Feature links now
|
|
47
|
+
point at the pinned corpus commit instead of floating on `main`.
|
|
48
|
+
|
|
3
49
|
## 6.0.0
|
|
4
50
|
|
|
5
51
|
The server moves to Apache-2.0, and the live-preview carrier stops living
|
package/dist/module.js
CHANGED
|
@@ -16,6 +16,223 @@ import node_http from "node:http";
|
|
|
16
16
|
import node_net from "node:net";
|
|
17
17
|
import { extensionInstall, extensionUninstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
|
+
__webpack_require__.add({
|
|
20
|
+
"./node_modules/.pnpm/@extension.dev+urls@0.2.0/node_modules/@extension.dev/urls/dist/origins.cjs" (__unused_rspack_module, exports) {
|
|
21
|
+
var __nested_rspack_require_18_37__ = {};
|
|
22
|
+
(()=>{
|
|
23
|
+
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
24
|
+
for(var key in definition)if (__nested_rspack_require_18_37__.o(definition, key) && !__nested_rspack_require_18_37__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: definition[key]
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
})();
|
|
30
|
+
(()=>{
|
|
31
|
+
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
32
|
+
})();
|
|
33
|
+
(()=>{
|
|
34
|
+
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
35
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
36
|
+
value: 'Module'
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
39
|
+
value: true
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
var __nested_rspack_exports__ = {};
|
|
44
|
+
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
45
|
+
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
46
|
+
DEV_LOCALHOST_ORIGINS: ()=>DEV_LOCALHOST_ORIGINS,
|
|
47
|
+
PROD_ORIGINS: ()=>PROD_ORIGINS,
|
|
48
|
+
isLocalOrigin: ()=>isLocalOrigin,
|
|
49
|
+
resolveOrigins: ()=>resolveOrigins
|
|
50
|
+
});
|
|
51
|
+
const PROD_ORIGINS = {
|
|
52
|
+
www: "https://www.extension.dev",
|
|
53
|
+
console: "https://console.extension.dev",
|
|
54
|
+
inspect: "https://inspect.extension.dev",
|
|
55
|
+
preview: "https://preview.extension.dev",
|
|
56
|
+
templates: "https://templates.extension.dev",
|
|
57
|
+
intelligence: "https://intelligence.extension.dev",
|
|
58
|
+
registry: "https://registry.extension.land",
|
|
59
|
+
media: "https://media.extension.land"
|
|
60
|
+
};
|
|
61
|
+
const DEV_LOCALHOST_ORIGINS = {
|
|
62
|
+
www: "http://localhost:3100",
|
|
63
|
+
console: "http://console.extension.localhost",
|
|
64
|
+
inspect: "http://inspect.extension.localhost",
|
|
65
|
+
preview: "http://preview.extension.localhost",
|
|
66
|
+
templates: "http://templates.extension.localhost",
|
|
67
|
+
intelligence: "http://intelligence.extension.localhost",
|
|
68
|
+
registry: "https://registry.extension.land",
|
|
69
|
+
media: "https://media.extension.land"
|
|
70
|
+
};
|
|
71
|
+
function strip(value) {
|
|
72
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
73
|
+
}
|
|
74
|
+
function isLocalOrigin(url) {
|
|
75
|
+
const raw = strip(url);
|
|
76
|
+
if (!raw) return false;
|
|
77
|
+
let host;
|
|
78
|
+
try {
|
|
79
|
+
host = new URL(raw).hostname;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
84
|
+
}
|
|
85
|
+
function resolveOrigins(overrides = {}, opts = {}) {
|
|
86
|
+
const devLike = isLocalOrigin(overrides.www) || isLocalOrigin(overrides.console) || isLocalOrigin(opts.hint);
|
|
87
|
+
const base = devLike ? DEV_LOCALHOST_ORIGINS : PROD_ORIGINS;
|
|
88
|
+
const pick = (key)=>strip(overrides[key]) || base[key];
|
|
89
|
+
return {
|
|
90
|
+
www: pick("www"),
|
|
91
|
+
console: pick("console"),
|
|
92
|
+
inspect: pick("inspect"),
|
|
93
|
+
preview: pick("preview"),
|
|
94
|
+
templates: pick("templates"),
|
|
95
|
+
intelligence: pick("intelligence"),
|
|
96
|
+
registry: pick("registry"),
|
|
97
|
+
media: pick("media")
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
exports.DEV_LOCALHOST_ORIGINS = __nested_rspack_exports__.DEV_LOCALHOST_ORIGINS;
|
|
101
|
+
exports.PROD_ORIGINS = __nested_rspack_exports__.PROD_ORIGINS;
|
|
102
|
+
exports.isLocalOrigin = __nested_rspack_exports__.isLocalOrigin;
|
|
103
|
+
exports.resolveOrigins = __nested_rspack_exports__.resolveOrigins;
|
|
104
|
+
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
105
|
+
"DEV_LOCALHOST_ORIGINS",
|
|
106
|
+
"PROD_ORIGINS",
|
|
107
|
+
"isLocalOrigin",
|
|
108
|
+
"resolveOrigins"
|
|
109
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
110
|
+
Object.defineProperty(exports, "__esModule", {
|
|
111
|
+
value: true
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
"./node_modules/.pnpm/@extension.dev+urls@0.2.0/node_modules/@extension.dev/urls/dist/paths.cjs" (__unused_rspack_module, exports) {
|
|
115
|
+
var __nested_rspack_require_18_37__ = {};
|
|
116
|
+
(()=>{
|
|
117
|
+
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
118
|
+
for(var key in definition)if (__nested_rspack_require_18_37__.o(definition, key) && !__nested_rspack_require_18_37__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
119
|
+
enumerable: true,
|
|
120
|
+
get: definition[key]
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
})();
|
|
124
|
+
(()=>{
|
|
125
|
+
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
126
|
+
})();
|
|
127
|
+
(()=>{
|
|
128
|
+
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
129
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
130
|
+
value: 'Module'
|
|
131
|
+
});
|
|
132
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
133
|
+
value: true
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
})();
|
|
137
|
+
var __nested_rspack_exports__ = {};
|
|
138
|
+
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
139
|
+
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
140
|
+
ConsoleProjectPage: ()=>ConsoleProjectPage,
|
|
141
|
+
consoleProjectPath: ()=>consoleProjectPath,
|
|
142
|
+
consoleWorkspacePath: ()=>consoleWorkspacePath,
|
|
143
|
+
inspectTabPath: ()=>inspectTabPath,
|
|
144
|
+
previewSharePath: ()=>previewSharePath,
|
|
145
|
+
templateTabPath: ()=>templateTabPath,
|
|
146
|
+
wwwDevicePath: ()=>wwwDevicePath,
|
|
147
|
+
wwwImportPath: ()=>wwwImportPath,
|
|
148
|
+
wwwNewPath: ()=>wwwNewPath,
|
|
149
|
+
wwwTemplatesPath: ()=>wwwTemplatesPath
|
|
150
|
+
});
|
|
151
|
+
const seg = (value)=>encodeURIComponent(String(value));
|
|
152
|
+
function join(base, sub) {
|
|
153
|
+
if (!sub) return base;
|
|
154
|
+
return `${base}/${sub.replace(/^\/+/, "")}`;
|
|
155
|
+
}
|
|
156
|
+
function consoleWorkspacePath(workspace, page = "") {
|
|
157
|
+
return join(`/${seg(workspace)}`, page);
|
|
158
|
+
}
|
|
159
|
+
function consoleProjectPath(ref, page = "") {
|
|
160
|
+
return join(`/${seg(ref.workspace)}/${seg(ref.project)}`, page);
|
|
161
|
+
}
|
|
162
|
+
const ConsoleProjectPage = {
|
|
163
|
+
overview: "",
|
|
164
|
+
onboard: "onboard",
|
|
165
|
+
activity: "activity",
|
|
166
|
+
builds: "builds",
|
|
167
|
+
build: (buildId, browser)=>browser ? `builds/${seg(buildId)}/${seg(browser)}` : `builds/${seg(buildId)}`,
|
|
168
|
+
releases: "releases",
|
|
169
|
+
releasesNew: "releases/new",
|
|
170
|
+
release: (releaseId)=>`releases/${seg(releaseId)}`,
|
|
171
|
+
stores: "stores",
|
|
172
|
+
storesNew: "stores/new",
|
|
173
|
+
store: (store)=>`stores/${seg(store)}`,
|
|
174
|
+
storeSubmissions: (store)=>`stores/${seg(store)}/submissions`,
|
|
175
|
+
storeSubmissionNew: (store)=>`stores/${seg(store)}/submissions/new`,
|
|
176
|
+
storeSubmission: (store, submissionId)=>`stores/${seg(store)}/submissions/${seg(submissionId)}`,
|
|
177
|
+
projectSettings: "project-settings",
|
|
178
|
+
projectSettingsSection: (section)=>`project-settings/${seg(section)}`,
|
|
179
|
+
accessTokens: "settings/access-tokens"
|
|
180
|
+
};
|
|
181
|
+
function withQuery(path, query) {
|
|
182
|
+
if (!query) return path;
|
|
183
|
+
const params = new URLSearchParams();
|
|
184
|
+
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
185
|
+
const qs = params.toString();
|
|
186
|
+
return qs ? `${path}?${qs}` : path;
|
|
187
|
+
}
|
|
188
|
+
function wwwNewPath(query) {
|
|
189
|
+
return withQuery("/new", query);
|
|
190
|
+
}
|
|
191
|
+
function wwwImportPath(query) {
|
|
192
|
+
return withQuery("/import", query);
|
|
193
|
+
}
|
|
194
|
+
function wwwDevicePath() {
|
|
195
|
+
return "/device";
|
|
196
|
+
}
|
|
197
|
+
function wwwTemplatesPath(slug) {
|
|
198
|
+
return slug ? `/templates/${seg(slug)}` : "/templates";
|
|
199
|
+
}
|
|
200
|
+
function templateTabPath(slug, tab = "preview") {
|
|
201
|
+
return "preview" === tab ? `/${seg(slug)}` : `/${seg(slug)}/${tab}`;
|
|
202
|
+
}
|
|
203
|
+
function inspectTabPath(tab = "preview") {
|
|
204
|
+
return "preview" === tab ? "/" : `/${tab}`;
|
|
205
|
+
}
|
|
206
|
+
function previewSharePath(previewId) {
|
|
207
|
+
return `/?preview=${seg(previewId)}`;
|
|
208
|
+
}
|
|
209
|
+
exports.ConsoleProjectPage = __nested_rspack_exports__.ConsoleProjectPage;
|
|
210
|
+
exports.consoleProjectPath = __nested_rspack_exports__.consoleProjectPath;
|
|
211
|
+
exports.consoleWorkspacePath = __nested_rspack_exports__.consoleWorkspacePath;
|
|
212
|
+
exports.inspectTabPath = __nested_rspack_exports__.inspectTabPath;
|
|
213
|
+
exports.previewSharePath = __nested_rspack_exports__.previewSharePath;
|
|
214
|
+
exports.templateTabPath = __nested_rspack_exports__.templateTabPath;
|
|
215
|
+
exports.wwwDevicePath = __nested_rspack_exports__.wwwDevicePath;
|
|
216
|
+
exports.wwwImportPath = __nested_rspack_exports__.wwwImportPath;
|
|
217
|
+
exports.wwwNewPath = __nested_rspack_exports__.wwwNewPath;
|
|
218
|
+
exports.wwwTemplatesPath = __nested_rspack_exports__.wwwTemplatesPath;
|
|
219
|
+
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
220
|
+
"ConsoleProjectPage",
|
|
221
|
+
"consoleProjectPath",
|
|
222
|
+
"consoleWorkspacePath",
|
|
223
|
+
"inspectTabPath",
|
|
224
|
+
"previewSharePath",
|
|
225
|
+
"templateTabPath",
|
|
226
|
+
"wwwDevicePath",
|
|
227
|
+
"wwwImportPath",
|
|
228
|
+
"wwwNewPath",
|
|
229
|
+
"wwwTemplatesPath"
|
|
230
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
231
|
+
Object.defineProperty(exports, "__esModule", {
|
|
232
|
+
value: true
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
19
236
|
var add_feature_namespaceObject = {};
|
|
20
237
|
__webpack_require__.r(add_feature_namespaceObject);
|
|
21
238
|
__webpack_require__.d(add_feature_namespaceObject, {
|
|
@@ -222,7 +439,199 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
222
439
|
handler: ()=>whoami_handler,
|
|
223
440
|
schema: ()=>whoami_schema
|
|
224
441
|
});
|
|
225
|
-
var package_namespaceObject = JSON.parse('{"rE":"
|
|
442
|
+
var package_namespaceObject = JSON.parse('{"rE":"6.1.0","El":{"OP":"4.0.16-canary.1784889479.74e12044"}}');
|
|
443
|
+
function credentialsPath() {
|
|
444
|
+
if ("win32" === process.platform) {
|
|
445
|
+
const base = process.env.APPDATA || process.env.LOCALAPPDATA || node_path.join(node_os.homedir(), "AppData", "Roaming");
|
|
446
|
+
return node_path.join(base, "extension-dev", "auth.json");
|
|
447
|
+
}
|
|
448
|
+
const xdg = String(process.env.XDG_CONFIG_HOME || "").trim();
|
|
449
|
+
const base = xdg || node_path.join(node_os.homedir(), ".config");
|
|
450
|
+
return node_path.join(base, "extension-dev", "auth.json");
|
|
451
|
+
}
|
|
452
|
+
function readCredentials() {
|
|
453
|
+
try {
|
|
454
|
+
const raw = node_fs.readFileSync(credentialsPath(), "utf8");
|
|
455
|
+
const data = JSON.parse(raw);
|
|
456
|
+
if (!data || "object" != typeof data) return null;
|
|
457
|
+
if (1 !== data.version) return null;
|
|
458
|
+
const token = String(data.token || "").trim();
|
|
459
|
+
if (!token) return null;
|
|
460
|
+
const provider = "extensiondev" === data.provider || "github" === data.provider ? data.provider : void 0;
|
|
461
|
+
return {
|
|
462
|
+
version: 1,
|
|
463
|
+
token,
|
|
464
|
+
workspaceSlug: String(data.workspaceSlug || ""),
|
|
465
|
+
projectSlug: String(data.projectSlug || ""),
|
|
466
|
+
expiresAt: Number(data.expiresAt || 0),
|
|
467
|
+
api: String(data.api || ""),
|
|
468
|
+
...provider ? {
|
|
469
|
+
provider
|
|
470
|
+
} : {}
|
|
471
|
+
};
|
|
472
|
+
} catch {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function writeCredentials(creds) {
|
|
477
|
+
const file = credentialsPath();
|
|
478
|
+
const dir = node_path.dirname(file);
|
|
479
|
+
node_fs.mkdirSync(dir, {
|
|
480
|
+
recursive: true,
|
|
481
|
+
mode: 448
|
|
482
|
+
});
|
|
483
|
+
try {
|
|
484
|
+
node_fs.chmodSync(dir, 448);
|
|
485
|
+
} catch {}
|
|
486
|
+
node_fs.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", {
|
|
487
|
+
mode: 384
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
node_fs.chmodSync(file, 384);
|
|
491
|
+
} catch {}
|
|
492
|
+
return file;
|
|
493
|
+
}
|
|
494
|
+
function clearCredentials() {
|
|
495
|
+
const file = credentialsPath();
|
|
496
|
+
try {
|
|
497
|
+
node_fs.unlinkSync(file);
|
|
498
|
+
return {
|
|
499
|
+
cleared: true,
|
|
500
|
+
path: file
|
|
501
|
+
};
|
|
502
|
+
} catch {
|
|
503
|
+
return {
|
|
504
|
+
cleared: false,
|
|
505
|
+
path: file
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
510
|
+
const creds = readCredentials();
|
|
511
|
+
if (!creds) return null;
|
|
512
|
+
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
513
|
+
return creds;
|
|
514
|
+
}
|
|
515
|
+
const origins = __webpack_require__("./node_modules/.pnpm/@extension.dev+urls@0.2.0/node_modules/@extension.dev/urls/dist/origins.cjs");
|
|
516
|
+
const paths_0 = __webpack_require__("./node_modules/.pnpm/@extension.dev+urls@0.2.0/node_modules/@extension.dev/urls/dist/paths.cjs");
|
|
517
|
+
origins.PROD_ORIGINS.registry;
|
|
518
|
+
function mcpOrigins(apiHint) {
|
|
519
|
+
const www = String(apiHint || process.env.EXTENSION_DEV_API_URL || "").trim() || void 0;
|
|
520
|
+
return (0, origins.resolveOrigins)({
|
|
521
|
+
www,
|
|
522
|
+
console: process.env.EXTENSION_DEV_CONSOLE_URL,
|
|
523
|
+
inspect: process.env.EXTENSION_DEV_INSPECT_URL,
|
|
524
|
+
preview: process.env.EXTENSION_DEV_PREVIEW_URL,
|
|
525
|
+
registry: process.env.EXTENSION_DEV_REGISTRY_URL,
|
|
526
|
+
media: process.env.EXTENSION_MEDIA_ORIGIN
|
|
527
|
+
}, {
|
|
528
|
+
hint: www
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function consoleBase(apiHint) {
|
|
532
|
+
return mcpOrigins(apiHint).console;
|
|
533
|
+
}
|
|
534
|
+
function registryBase() {
|
|
535
|
+
return mcpOrigins().registry;
|
|
536
|
+
}
|
|
537
|
+
function resolveProjectRef(overrides) {
|
|
538
|
+
const workspace = String(overrides?.workspace || "").trim();
|
|
539
|
+
const project = String(overrides?.project || "").trim();
|
|
540
|
+
if (workspace && project) return {
|
|
541
|
+
workspace,
|
|
542
|
+
project
|
|
543
|
+
};
|
|
544
|
+
const creds = readCredentials();
|
|
545
|
+
const ws = workspace || String(creds?.workspaceSlug || "").trim();
|
|
546
|
+
const proj = project || String(creds?.projectSlug || "").trim();
|
|
547
|
+
if (!ws || !proj) return null;
|
|
548
|
+
return {
|
|
549
|
+
workspace: ws,
|
|
550
|
+
project: proj
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function registryFileUrl(ref, file) {
|
|
554
|
+
return `${registryBase()}/${encodeURIComponent(ref.workspace)}/${encodeURIComponent(ref.project)}/_extension-dev/${file}`;
|
|
555
|
+
}
|
|
556
|
+
function consoleProjectUrl(ref, page, apiHint) {
|
|
557
|
+
const base = consoleBase(apiHint);
|
|
558
|
+
if (!ref) return base;
|
|
559
|
+
return `${base}${(0, paths_0.consoleProjectPath)(ref, page)}`;
|
|
560
|
+
}
|
|
561
|
+
async function fetchRegistryJson(url, fetchImpl = fetch) {
|
|
562
|
+
let res;
|
|
563
|
+
try {
|
|
564
|
+
res = await fetchImpl(url);
|
|
565
|
+
} catch (err) {
|
|
566
|
+
return {
|
|
567
|
+
ok: false,
|
|
568
|
+
message: `Could not reach ${url}: ${err?.message || err}`
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (!res.ok) return {
|
|
572
|
+
ok: false,
|
|
573
|
+
status: res.status,
|
|
574
|
+
message: `${url} returned ${res.status}`
|
|
575
|
+
};
|
|
576
|
+
try {
|
|
577
|
+
const text = await res.text();
|
|
578
|
+
return {
|
|
579
|
+
ok: true,
|
|
580
|
+
json: JSON.parse(text)
|
|
581
|
+
};
|
|
582
|
+
} catch {
|
|
583
|
+
return {
|
|
584
|
+
ok: false,
|
|
585
|
+
message: `${url} did not return valid JSON`
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function parseChannels(json) {
|
|
590
|
+
if (!json || "object" != typeof json || Array.isArray(json)) return [];
|
|
591
|
+
const out = [];
|
|
592
|
+
for (const [channel, raw] of Object.entries(json)){
|
|
593
|
+
if (!raw || "object" != typeof raw) continue;
|
|
594
|
+
const row = raw;
|
|
595
|
+
const description = "string" == typeof row.description ? row.description : void 0;
|
|
596
|
+
const promotedAtField = "string" == typeof row.promotedAt && row.promotedAt ? row.promotedAt : void 0;
|
|
597
|
+
const fromDescription = description?.match(/\bon (\d{4}-\d{2}-\d{2}T[0-9:.]+Z?)/)?.[1];
|
|
598
|
+
const entry = {
|
|
599
|
+
channel,
|
|
600
|
+
sha: String(row.sha ?? "")
|
|
601
|
+
};
|
|
602
|
+
if (row.buildId) entry.buildId = String(row.buildId);
|
|
603
|
+
if (row.version) entry.version = String(row.version);
|
|
604
|
+
const promotedAt = promotedAtField || fromDescription;
|
|
605
|
+
if (promotedAt) entry.promotedAt = promotedAt;
|
|
606
|
+
if (description) entry.description = description;
|
|
607
|
+
out.push(entry);
|
|
608
|
+
}
|
|
609
|
+
return out;
|
|
610
|
+
}
|
|
611
|
+
function parseBuildIndex(json) {
|
|
612
|
+
const items = json?.items;
|
|
613
|
+
if (!Array.isArray(items)) return [];
|
|
614
|
+
const out = [];
|
|
615
|
+
for (const raw of items){
|
|
616
|
+
if (!raw || "object" != typeof raw) continue;
|
|
617
|
+
const row = raw;
|
|
618
|
+
const sha = String(row.shortSha ?? row.sha ?? row.id ?? row.buildId ?? "").trim();
|
|
619
|
+
if (!sha) continue;
|
|
620
|
+
const entry = {
|
|
621
|
+
sha
|
|
622
|
+
};
|
|
623
|
+
if (row.commit) entry.commit = String(row.commit);
|
|
624
|
+
if (row.channel) entry.channel = String(row.channel);
|
|
625
|
+
if (row.buildEnv) entry.buildEnv = String(row.buildEnv);
|
|
626
|
+
if (row.status) entry.status = String(row.status);
|
|
627
|
+
if (row.version) entry.version = String(row.version);
|
|
628
|
+
if ("string" == typeof row.message) entry.message = row.message.split("\n", 1)[0];
|
|
629
|
+
if (row.timestamp) entry.timestamp = String(row.timestamp);
|
|
630
|
+
if (Array.isArray(row.browsers)) entry.browsers = row.browsers.map((b)=>String(b)).filter(Boolean);
|
|
631
|
+
out.push(entry);
|
|
632
|
+
}
|
|
633
|
+
return out;
|
|
634
|
+
}
|
|
226
635
|
function scaffoldEnginePin(projectPath) {
|
|
227
636
|
try {
|
|
228
637
|
const pkg = JSON.parse(node_fs.readFileSync(node_path.join(projectPath, "package.json"), "utf8"));
|
|
@@ -359,6 +768,10 @@ async function create_handler(args) {
|
|
|
359
768
|
const engineWarning = pin && "latest" !== pin && !pinMatches ? `The scaffold pins "extension": "${scaffoldPin ?? "unknown"}"; the project-local engine wins over EXTENSION_MCP_CLI_VERSION=${pin}. Run \`(cd ${result.projectPath} && ${addDev(`extension@${pin}`)})\` to match the pinned engine.` : void 0;
|
|
360
769
|
const resolvedParent = args.parentDir ? node_path.resolve(args.parentDir) : process.cwd();
|
|
361
770
|
const gitInit = node_fs.existsSync(node_path.join(result.projectPath, ".git"));
|
|
771
|
+
const wwwOrigin = mcpOrigins().www;
|
|
772
|
+
const deployUrl = `${wwwOrigin}${(0, paths_0.wwwNewPath)({
|
|
773
|
+
template: result.template
|
|
774
|
+
})}`;
|
|
362
775
|
return JSON.stringify({
|
|
363
776
|
resolvedPath: result.projectPath,
|
|
364
777
|
projectPath: result.projectPath,
|
|
@@ -366,20 +779,27 @@ async function create_handler(args) {
|
|
|
366
779
|
template: result.template,
|
|
367
780
|
depsInstalled: result.depsInstalled,
|
|
368
781
|
packageManager: result.depsInstalled ? packageManager : null,
|
|
782
|
+
deployUrl,
|
|
369
783
|
defaultsApplied: {
|
|
370
784
|
parentDir: args.parentDir ? `${resolvedParent} (explicit)` : `${resolvedParent} (default: the MCP server process cwd, not yours; pass parentDir to choose)`,
|
|
785
|
+
...void 0 === args.template ? {
|
|
786
|
+
template: "typescript (default; run extension_list_templates to pick another, e.g. javascript for plain JS)"
|
|
787
|
+
} : {},
|
|
371
788
|
packageManager: `${packageManager} (auto-detected by the scaffolder, not asked)`,
|
|
372
789
|
browser: "chrome (default: extension_dev and extension_build target chrome unless you pass browser)",
|
|
373
790
|
gitInit
|
|
374
791
|
},
|
|
375
792
|
duration: Date.now() - start,
|
|
376
|
-
nextSteps:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
793
|
+
nextSteps: [
|
|
794
|
+
...result.depsInstalled ? [
|
|
795
|
+
`cd ${result.projectPath}`,
|
|
796
|
+
runDev
|
|
797
|
+
] : [
|
|
798
|
+
`cd ${result.projectPath}`,
|
|
799
|
+
`${packageManager} install`,
|
|
800
|
+
runDev
|
|
801
|
+
],
|
|
802
|
+
`To ship: extension_create scaffolds and runs locally, it does not host. Open ${deployUrl} to deploy this template to the web.`
|
|
383
803
|
],
|
|
384
804
|
...engineWarning ? {
|
|
385
805
|
engineWarning
|
|
@@ -458,11 +878,22 @@ async function templateMetaUrls() {
|
|
|
458
878
|
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/templates-meta.json`);
|
|
459
879
|
return urls;
|
|
460
880
|
}
|
|
881
|
+
function stripTemplatePathPrefix(slug, relativePath) {
|
|
882
|
+
for (const dir of [
|
|
883
|
+
"public",
|
|
884
|
+
"examples"
|
|
885
|
+
]){
|
|
886
|
+
const prefix = `${dir}/${slug}/`;
|
|
887
|
+
if (relativePath.startsWith(prefix)) return relativePath.slice(prefix.length);
|
|
888
|
+
}
|
|
889
|
+
return relativePath;
|
|
890
|
+
}
|
|
461
891
|
async function templateFileUrls(slug, relativePath) {
|
|
892
|
+
const relative = stripTemplatePathPrefix(slug, relativePath);
|
|
462
893
|
const urls = [];
|
|
463
894
|
const release = await resolveRelease();
|
|
464
|
-
if (release) urls.push(`${release.filesBaseUrl}/${slug}/${
|
|
465
|
-
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/examples/${slug}/${
|
|
895
|
+
if (release) urls.push(`${release.filesBaseUrl}/${slug}/${relative}`);
|
|
896
|
+
urls.push(`${rawBaseForCommit(PINNED_COMMIT)}/examples/${slug}/${relative}`);
|
|
466
897
|
return urls;
|
|
467
898
|
}
|
|
468
899
|
const CACHE_DIR = node_path.join(node_os.homedir(), ".cache", "extension-js");
|
|
@@ -606,7 +1037,8 @@ async function list_templates_handler(args) {
|
|
|
606
1037
|
const results = templates.map((t)=>({
|
|
607
1038
|
slug: t.slug,
|
|
608
1039
|
description: t.description,
|
|
609
|
-
uiFramework: t.uiFramework
|
|
1040
|
+
uiFramework: t.uiFramework,
|
|
1041
|
+
frameworkLabel: t.uiFramework || "vanilla",
|
|
610
1042
|
surfaces: t.surfaces,
|
|
611
1043
|
tags: t.tags,
|
|
612
1044
|
difficulty: t.difficulty,
|
|
@@ -2219,7 +2651,7 @@ async function get_template_source_handler(args) {
|
|
|
2219
2651
|
};
|
|
2220
2652
|
if (!args.files?.length) return JSON.stringify({
|
|
2221
2653
|
...meta,
|
|
2222
|
-
files: template.files,
|
|
2654
|
+
files: template.files.map((f)=>stripTemplatePathPrefix(template.slug, f)),
|
|
2223
2655
|
hint: "Pass specific file paths in the files parameter to read their contents."
|
|
2224
2656
|
});
|
|
2225
2657
|
const fileContents = {};
|
|
@@ -5773,250 +6205,7 @@ async function dom_inspect_handler(args) {
|
|
|
5773
6205
|
return raw;
|
|
5774
6206
|
}
|
|
5775
6207
|
}
|
|
5776
|
-
|
|
5777
|
-
if ("win32" === process.platform) {
|
|
5778
|
-
const base = process.env.APPDATA || process.env.LOCALAPPDATA || node_path.join(node_os.homedir(), "AppData", "Roaming");
|
|
5779
|
-
return node_path.join(base, "extension-dev", "auth.json");
|
|
5780
|
-
}
|
|
5781
|
-
const xdg = String(process.env.XDG_CONFIG_HOME || "").trim();
|
|
5782
|
-
const base = xdg || node_path.join(node_os.homedir(), ".config");
|
|
5783
|
-
return node_path.join(base, "extension-dev", "auth.json");
|
|
5784
|
-
}
|
|
5785
|
-
function readCredentials() {
|
|
5786
|
-
try {
|
|
5787
|
-
const raw = node_fs.readFileSync(credentialsPath(), "utf8");
|
|
5788
|
-
const data = JSON.parse(raw);
|
|
5789
|
-
if (!data || "object" != typeof data) return null;
|
|
5790
|
-
if (1 !== data.version) return null;
|
|
5791
|
-
const token = String(data.token || "").trim();
|
|
5792
|
-
if (!token) return null;
|
|
5793
|
-
const provider = "extensiondev" === data.provider || "github" === data.provider ? data.provider : void 0;
|
|
5794
|
-
return {
|
|
5795
|
-
version: 1,
|
|
5796
|
-
token,
|
|
5797
|
-
workspaceSlug: String(data.workspaceSlug || ""),
|
|
5798
|
-
projectSlug: String(data.projectSlug || ""),
|
|
5799
|
-
expiresAt: Number(data.expiresAt || 0),
|
|
5800
|
-
api: String(data.api || ""),
|
|
5801
|
-
...provider ? {
|
|
5802
|
-
provider
|
|
5803
|
-
} : {}
|
|
5804
|
-
};
|
|
5805
|
-
} catch {
|
|
5806
|
-
return null;
|
|
5807
|
-
}
|
|
5808
|
-
}
|
|
5809
|
-
function writeCredentials(creds) {
|
|
5810
|
-
const file = credentialsPath();
|
|
5811
|
-
const dir = node_path.dirname(file);
|
|
5812
|
-
node_fs.mkdirSync(dir, {
|
|
5813
|
-
recursive: true,
|
|
5814
|
-
mode: 448
|
|
5815
|
-
});
|
|
5816
|
-
try {
|
|
5817
|
-
node_fs.chmodSync(dir, 448);
|
|
5818
|
-
} catch {}
|
|
5819
|
-
node_fs.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", {
|
|
5820
|
-
mode: 384
|
|
5821
|
-
});
|
|
5822
|
-
try {
|
|
5823
|
-
node_fs.chmodSync(file, 384);
|
|
5824
|
-
} catch {}
|
|
5825
|
-
return file;
|
|
5826
|
-
}
|
|
5827
|
-
function clearCredentials() {
|
|
5828
|
-
const file = credentialsPath();
|
|
5829
|
-
try {
|
|
5830
|
-
node_fs.unlinkSync(file);
|
|
5831
|
-
return {
|
|
5832
|
-
cleared: true,
|
|
5833
|
-
path: file
|
|
5834
|
-
};
|
|
5835
|
-
} catch {
|
|
5836
|
-
return {
|
|
5837
|
-
cleared: false,
|
|
5838
|
-
path: file
|
|
5839
|
-
};
|
|
5840
|
-
}
|
|
5841
|
-
}
|
|
5842
|
-
function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
5843
|
-
const creds = readCredentials();
|
|
5844
|
-
if (!creds) return null;
|
|
5845
|
-
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
5846
|
-
return creds;
|
|
5847
|
-
}
|
|
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
|
-
}
|
|
5918
|
-
function registryBase() {
|
|
5919
|
-
return mcpOrigins().registry;
|
|
5920
|
-
}
|
|
5921
|
-
function resolveProjectRef(overrides) {
|
|
5922
|
-
const workspace = String(overrides?.workspace || "").trim();
|
|
5923
|
-
const project = String(overrides?.project || "").trim();
|
|
5924
|
-
if (workspace && project) return {
|
|
5925
|
-
workspace,
|
|
5926
|
-
project
|
|
5927
|
-
};
|
|
5928
|
-
const creds = readCredentials();
|
|
5929
|
-
const ws = workspace || String(creds?.workspaceSlug || "").trim();
|
|
5930
|
-
const proj = project || String(creds?.projectSlug || "").trim();
|
|
5931
|
-
if (!ws || !proj) return null;
|
|
5932
|
-
return {
|
|
5933
|
-
workspace: ws,
|
|
5934
|
-
project: proj
|
|
5935
|
-
};
|
|
5936
|
-
}
|
|
5937
|
-
function registryFileUrl(ref, file) {
|
|
5938
|
-
return `${registryBase()}/${encodeURIComponent(ref.workspace)}/${encodeURIComponent(ref.project)}/_extension-dev/${file}`;
|
|
5939
|
-
}
|
|
5940
|
-
function consoleProjectUrl(ref, page, apiHint) {
|
|
5941
|
-
const base = consoleBase(apiHint);
|
|
5942
|
-
if (!ref) return base;
|
|
5943
|
-
return `${base}${consoleProjectPath(ref, page)}`;
|
|
5944
|
-
}
|
|
5945
|
-
async function fetchRegistryJson(url, fetchImpl = fetch) {
|
|
5946
|
-
let res;
|
|
5947
|
-
try {
|
|
5948
|
-
res = await fetchImpl(url);
|
|
5949
|
-
} catch (err) {
|
|
5950
|
-
return {
|
|
5951
|
-
ok: false,
|
|
5952
|
-
message: `Could not reach ${url}: ${err?.message || err}`
|
|
5953
|
-
};
|
|
5954
|
-
}
|
|
5955
|
-
if (!res.ok) return {
|
|
5956
|
-
ok: false,
|
|
5957
|
-
status: res.status,
|
|
5958
|
-
message: `${url} returned ${res.status}`
|
|
5959
|
-
};
|
|
5960
|
-
try {
|
|
5961
|
-
const text = await res.text();
|
|
5962
|
-
return {
|
|
5963
|
-
ok: true,
|
|
5964
|
-
json: JSON.parse(text)
|
|
5965
|
-
};
|
|
5966
|
-
} catch {
|
|
5967
|
-
return {
|
|
5968
|
-
ok: false,
|
|
5969
|
-
message: `${url} did not return valid JSON`
|
|
5970
|
-
};
|
|
5971
|
-
}
|
|
5972
|
-
}
|
|
5973
|
-
function parseChannels(json) {
|
|
5974
|
-
if (!json || "object" != typeof json || Array.isArray(json)) return [];
|
|
5975
|
-
const out = [];
|
|
5976
|
-
for (const [channel, raw] of Object.entries(json)){
|
|
5977
|
-
if (!raw || "object" != typeof raw) continue;
|
|
5978
|
-
const row = raw;
|
|
5979
|
-
const description = "string" == typeof row.description ? row.description : void 0;
|
|
5980
|
-
const promotedAtField = "string" == typeof row.promotedAt && row.promotedAt ? row.promotedAt : void 0;
|
|
5981
|
-
const fromDescription = description?.match(/\bon (\d{4}-\d{2}-\d{2}T[0-9:.]+Z?)/)?.[1];
|
|
5982
|
-
const entry = {
|
|
5983
|
-
channel,
|
|
5984
|
-
sha: String(row.sha ?? "")
|
|
5985
|
-
};
|
|
5986
|
-
if (row.buildId) entry.buildId = String(row.buildId);
|
|
5987
|
-
if (row.version) entry.version = String(row.version);
|
|
5988
|
-
const promotedAt = promotedAtField || fromDescription;
|
|
5989
|
-
if (promotedAt) entry.promotedAt = promotedAt;
|
|
5990
|
-
if (description) entry.description = description;
|
|
5991
|
-
out.push(entry);
|
|
5992
|
-
}
|
|
5993
|
-
return out;
|
|
5994
|
-
}
|
|
5995
|
-
function parseBuildIndex(json) {
|
|
5996
|
-
const items = json?.items;
|
|
5997
|
-
if (!Array.isArray(items)) return [];
|
|
5998
|
-
const out = [];
|
|
5999
|
-
for (const raw of items){
|
|
6000
|
-
if (!raw || "object" != typeof raw) continue;
|
|
6001
|
-
const row = raw;
|
|
6002
|
-
const sha = String(row.shortSha ?? row.sha ?? row.id ?? row.buildId ?? "").trim();
|
|
6003
|
-
if (!sha) continue;
|
|
6004
|
-
const entry = {
|
|
6005
|
-
sha
|
|
6006
|
-
};
|
|
6007
|
-
if (row.commit) entry.commit = String(row.commit);
|
|
6008
|
-
if (row.channel) entry.channel = String(row.channel);
|
|
6009
|
-
if (row.buildEnv) entry.buildEnv = String(row.buildEnv);
|
|
6010
|
-
if (row.status) entry.status = String(row.status);
|
|
6011
|
-
if (row.version) entry.version = String(row.version);
|
|
6012
|
-
if ("string" == typeof row.message) entry.message = row.message.split("\n", 1)[0];
|
|
6013
|
-
if (row.timestamp) entry.timestamp = String(row.timestamp);
|
|
6014
|
-
if (Array.isArray(row.browsers)) entry.browsers = row.browsers.map((b)=>String(b)).filter(Boolean);
|
|
6015
|
-
out.push(entry);
|
|
6016
|
-
}
|
|
6017
|
-
return out;
|
|
6018
|
-
}
|
|
6019
|
-
const DEFAULT_API = PROD_ORIGINS.www;
|
|
6208
|
+
const DEFAULT_API = origins.PROD_ORIGINS.www;
|
|
6020
6209
|
function tokenTtlNote(workspaceSlug, projectSlug) {
|
|
6021
6210
|
const tokensUrl = workspaceSlug && projectSlug ? consoleProjectUrl({
|
|
6022
6211
|
workspace: workspaceSlug,
|
|
@@ -7263,6 +7452,7 @@ async function wait_handler(args) {
|
|
|
7263
7452
|
hint: "Still building, call extension_wait again to keep waiting (it resumes polling the same contract). If it never readies, check the dev process with extension_doctor."
|
|
7264
7453
|
});
|
|
7265
7454
|
}
|
|
7455
|
+
const EXAMPLES_TREE_BASE = `https://github.com/extension-js/examples/tree/${PINNED_COMMIT}/examples`;
|
|
7266
7456
|
const add_feature_schema = {
|
|
7267
7457
|
name: "extension_add_feature",
|
|
7268
7458
|
description: "Plan a new feature surface for an existing extension. Returns step-by-step instructions, the manifest additions to make, and reference templates from the extension.dev catalog. Does not modify files; apply the returned plan yourself.",
|
|
@@ -7506,7 +7696,7 @@ async function add_feature_handler(args) {
|
|
|
7506
7696
|
framework,
|
|
7507
7697
|
referenceTemplate: {
|
|
7508
7698
|
slug: templateSlug,
|
|
7509
|
-
repositoryUrl:
|
|
7699
|
+
repositoryUrl: `${EXAMPLES_TREE_BASE}/${templateSlug}`,
|
|
7510
7700
|
referenceFiles: referenceFiles.filter((f)=>f.includes(featureDir) || f.includes("manifest"))
|
|
7511
7701
|
},
|
|
7512
7702
|
manifestUpdates,
|
|
@@ -7520,7 +7710,7 @@ async function add_feature_handler(args) {
|
|
|
7520
7710
|
"2. Create the following files in your project:",
|
|
7521
7711
|
...filesToCreate.map((f)=>` - ${f.path} (${f.hint})`),
|
|
7522
7712
|
"sidebar" === args.feature ? "3. Add background.ts to handle sidebar open: chromium uses chrome.sidePanel.setPanelBehavior, firefox uses browser.sidebarAction.open()" : "",
|
|
7523
|
-
`4. Reference template source:
|
|
7713
|
+
`4. Reference template source: ${EXAMPLES_TREE_BASE}/${templateSlug}/src`,
|
|
7524
7714
|
"5. Run npm run dev to test"
|
|
7525
7715
|
].filter(Boolean),
|
|
7526
7716
|
hint: conflicts.length ? `Warning: ${conflicts.length} file(s) already exist and would be overwritten.` : "No conflicts detected. Safe to create all files."
|
package/dist/rslib-runtime.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
var __webpack_modules__ = {};
|
|
1
2
|
var __webpack_module_cache__ = {};
|
|
2
3
|
function __webpack_require__(moduleId) {
|
|
3
4
|
var cachedModule = __webpack_module_cache__[moduleId];
|
|
@@ -8,6 +9,7 @@ function __webpack_require__(moduleId) {
|
|
|
8
9
|
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
9
10
|
return module.exports;
|
|
10
11
|
}
|
|
12
|
+
__webpack_require__.m = __webpack_modules__;
|
|
11
13
|
(()=>{
|
|
12
14
|
__webpack_require__.add = function(modules) {
|
|
13
15
|
Object.assign(__webpack_require__.m, modules);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Origins } from "
|
|
1
|
+
import { type Origins } from "@extension.dev/urls/origins";
|
|
2
2
|
export declare const REGISTRY_BASE_DEFAULT: string;
|
|
3
3
|
/**
|
|
4
4
|
* Resolve the fleet origins from the MCP's env vars, once per call. Console,
|
|
@@ -7,7 +7,7 @@ export declare const REGISTRY_BASE_DEFAULT: string;
|
|
|
7
7
|
* MCP hands back follows to the dev host (`console.extension.localhost`) instead
|
|
8
8
|
* of silently pointing at prod. Explicit per-host env vars still win. Falls back
|
|
9
9
|
* to production when nothing is set. The dev-derivation lives in
|
|
10
|
-
* `@
|
|
10
|
+
* `@extension.dev/urls` so it stays identical to what the apps resolve.
|
|
11
11
|
*/
|
|
12
12
|
export declare function mcpOrigins(apiHint?: string): Origins;
|
|
13
13
|
/** Base origin of the console dashboard, dev-aware (no trailing slash). */
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export declare const PINNED_COMMIT = "2d2ed9668cca002148d9eecd953a08b54d0bad9d";
|
|
1
2
|
export declare function templateMetaUrls(): Promise<string[]>;
|
|
3
|
+
export declare function stripTemplatePathPrefix(slug: string, relativePath: string): string;
|
|
2
4
|
export declare function templateFileUrls(slug: string, relativePath: string): Promise<string[]>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.2.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
7
|
"license": "Apache-2.0",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"ws": "^8.20.0"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
|
+
"@extension.dev/urls": "^0.2.0",
|
|
100
101
|
"@rslib/core": "^0.19.4",
|
|
101
102
|
"@types/cross-spawn": "^6.0.6",
|
|
102
103
|
"@types/node": "^25.2.0",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "6.
|
|
10
|
+
"version": "6.2.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": "6.
|
|
16
|
+
"version": "6.2.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
@@ -1,34 +0,0 @@
|
|
|
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;
|
|
@@ -1,56 +0,0 @@
|
|
|
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;
|