@extension.dev/mcp 6.1.0 → 6.3.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 +82 -0
- package/dist/module.js +757 -193
- package/dist/rslib-runtime.js +2 -0
- package/dist/src/lib/registry-access.d.ts +45 -0
- package/dist/src/lib/registry.d.ts +33 -6
- package/dist/src/tools/release-list.d.ts +5 -0
- package/dist/src/tools/store-status.d.ts +5 -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
package/dist/module.js
CHANGED
|
@@ -16,6 +16,394 @@ 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.3.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
|
+
userland: "https://extension.dev",
|
|
59
|
+
registry: "https://registry.extension.land",
|
|
60
|
+
media: "https://media.extension.land"
|
|
61
|
+
};
|
|
62
|
+
const DEV_LOCALHOST_ORIGINS = {
|
|
63
|
+
www: "http://localhost:3100",
|
|
64
|
+
console: "http://console.extension.localhost",
|
|
65
|
+
inspect: "http://inspect.extension.localhost",
|
|
66
|
+
preview: "http://preview.extension.localhost",
|
|
67
|
+
templates: "http://templates.extension.localhost",
|
|
68
|
+
intelligence: "http://intelligence.extension.localhost",
|
|
69
|
+
userland: "http://userland.extension.localhost",
|
|
70
|
+
registry: "https://registry.extension.land",
|
|
71
|
+
media: "https://media.extension.land"
|
|
72
|
+
};
|
|
73
|
+
function strip(value) {
|
|
74
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
75
|
+
}
|
|
76
|
+
function isLocalOrigin(url) {
|
|
77
|
+
const raw = strip(url);
|
|
78
|
+
if (!raw) return false;
|
|
79
|
+
let host;
|
|
80
|
+
try {
|
|
81
|
+
host = new URL(raw).hostname;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
86
|
+
}
|
|
87
|
+
function resolveOrigins(overrides = {}, opts = {}) {
|
|
88
|
+
const devLike = isLocalOrigin(overrides.www) || isLocalOrigin(overrides.console) || isLocalOrigin(opts.hint);
|
|
89
|
+
const base = devLike ? DEV_LOCALHOST_ORIGINS : PROD_ORIGINS;
|
|
90
|
+
const pick = (key)=>strip(overrides[key]) || base[key];
|
|
91
|
+
return {
|
|
92
|
+
www: pick("www"),
|
|
93
|
+
console: pick("console"),
|
|
94
|
+
inspect: pick("inspect"),
|
|
95
|
+
preview: pick("preview"),
|
|
96
|
+
templates: pick("templates"),
|
|
97
|
+
intelligence: pick("intelligence"),
|
|
98
|
+
userland: pick("userland"),
|
|
99
|
+
registry: pick("registry"),
|
|
100
|
+
media: pick("media")
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
exports.DEV_LOCALHOST_ORIGINS = __nested_rspack_exports__.DEV_LOCALHOST_ORIGINS;
|
|
104
|
+
exports.PROD_ORIGINS = __nested_rspack_exports__.PROD_ORIGINS;
|
|
105
|
+
exports.isLocalOrigin = __nested_rspack_exports__.isLocalOrigin;
|
|
106
|
+
exports.resolveOrigins = __nested_rspack_exports__.resolveOrigins;
|
|
107
|
+
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
108
|
+
"DEV_LOCALHOST_ORIGINS",
|
|
109
|
+
"PROD_ORIGINS",
|
|
110
|
+
"isLocalOrigin",
|
|
111
|
+
"resolveOrigins"
|
|
112
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
113
|
+
Object.defineProperty(exports, "__esModule", {
|
|
114
|
+
value: true
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
"./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/paths.cjs" (__unused_rspack_module, exports) {
|
|
118
|
+
var __nested_rspack_require_18_37__ = {};
|
|
119
|
+
(()=>{
|
|
120
|
+
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
121
|
+
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, {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: definition[key]
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
})();
|
|
127
|
+
(()=>{
|
|
128
|
+
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
129
|
+
})();
|
|
130
|
+
(()=>{
|
|
131
|
+
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
132
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
133
|
+
value: 'Module'
|
|
134
|
+
});
|
|
135
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
136
|
+
value: true
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
})();
|
|
140
|
+
var __nested_rspack_exports__ = {};
|
|
141
|
+
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
142
|
+
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
143
|
+
ConsoleProjectPage: ()=>ConsoleProjectPage,
|
|
144
|
+
consoleProjectPath: ()=>consoleProjectPath,
|
|
145
|
+
consoleWorkspacePath: ()=>consoleWorkspacePath,
|
|
146
|
+
inspectTabPath: ()=>inspectTabPath,
|
|
147
|
+
previewSharePath: ()=>previewSharePath,
|
|
148
|
+
templateTabPath: ()=>templateTabPath,
|
|
149
|
+
withQuery: ()=>withQuery,
|
|
150
|
+
wwwDevicePath: ()=>wwwDevicePath,
|
|
151
|
+
wwwImportPath: ()=>wwwImportPath,
|
|
152
|
+
wwwNewPath: ()=>wwwNewPath,
|
|
153
|
+
wwwTemplatesPath: ()=>wwwTemplatesPath
|
|
154
|
+
});
|
|
155
|
+
const seg = (value)=>encodeURIComponent(String(value));
|
|
156
|
+
function join(base, sub) {
|
|
157
|
+
if (!sub) return base;
|
|
158
|
+
return `${base}/${sub.replace(/^\/+/, "")}`;
|
|
159
|
+
}
|
|
160
|
+
function consoleWorkspacePath(workspace, page = "") {
|
|
161
|
+
return join(`/${seg(workspace)}`, page);
|
|
162
|
+
}
|
|
163
|
+
function consoleProjectPath(ref, page = "") {
|
|
164
|
+
return join(`/${seg(ref.workspace)}/${seg(ref.project)}`, page);
|
|
165
|
+
}
|
|
166
|
+
const ConsoleProjectPage = {
|
|
167
|
+
overview: "",
|
|
168
|
+
onboard: "onboard",
|
|
169
|
+
activity: "activity",
|
|
170
|
+
builds: "builds",
|
|
171
|
+
build: (buildId, browser)=>browser ? `builds/${seg(buildId)}/${seg(browser)}` : `builds/${seg(buildId)}`,
|
|
172
|
+
releases: "releases",
|
|
173
|
+
releasesNew: "releases/new",
|
|
174
|
+
release: (releaseId)=>`releases/${seg(releaseId)}`,
|
|
175
|
+
stores: "stores",
|
|
176
|
+
storesNew: "stores/new",
|
|
177
|
+
store: (store)=>`stores/${seg(store)}`,
|
|
178
|
+
storeSubmissions: (store)=>`stores/${seg(store)}/submissions`,
|
|
179
|
+
storeSubmissionNew: (store)=>`stores/${seg(store)}/submissions/new`,
|
|
180
|
+
storeSubmission: (store, submissionId)=>`stores/${seg(store)}/submissions/${seg(submissionId)}`,
|
|
181
|
+
projectSettings: "project-settings",
|
|
182
|
+
projectSettingsSection: (section)=>`project-settings/${seg(section)}`,
|
|
183
|
+
accessTokens: "settings/access-tokens"
|
|
184
|
+
};
|
|
185
|
+
function withQuery(path, query) {
|
|
186
|
+
if (!query) return path;
|
|
187
|
+
const params = new URLSearchParams();
|
|
188
|
+
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
189
|
+
const qs = params.toString();
|
|
190
|
+
return qs ? `${path}?${qs}` : path;
|
|
191
|
+
}
|
|
192
|
+
function wwwNewPath(query) {
|
|
193
|
+
return withQuery("/new", query);
|
|
194
|
+
}
|
|
195
|
+
function wwwImportPath(query) {
|
|
196
|
+
return withQuery("/import", query);
|
|
197
|
+
}
|
|
198
|
+
function wwwDevicePath() {
|
|
199
|
+
return "/device";
|
|
200
|
+
}
|
|
201
|
+
function wwwTemplatesPath(slug) {
|
|
202
|
+
return slug ? `/templates/${seg(slug)}` : "/templates";
|
|
203
|
+
}
|
|
204
|
+
function templateTabPath(slug, tab = "preview") {
|
|
205
|
+
return "preview" === tab ? `/${seg(slug)}` : `/${seg(slug)}/${tab}`;
|
|
206
|
+
}
|
|
207
|
+
function inspectTabPath(tab = "preview") {
|
|
208
|
+
return "preview" === tab ? "/" : `/${tab}`;
|
|
209
|
+
}
|
|
210
|
+
function previewSharePath(previewId) {
|
|
211
|
+
return `/?preview=${seg(previewId)}`;
|
|
212
|
+
}
|
|
213
|
+
exports.ConsoleProjectPage = __nested_rspack_exports__.ConsoleProjectPage;
|
|
214
|
+
exports.consoleProjectPath = __nested_rspack_exports__.consoleProjectPath;
|
|
215
|
+
exports.consoleWorkspacePath = __nested_rspack_exports__.consoleWorkspacePath;
|
|
216
|
+
exports.inspectTabPath = __nested_rspack_exports__.inspectTabPath;
|
|
217
|
+
exports.previewSharePath = __nested_rspack_exports__.previewSharePath;
|
|
218
|
+
exports.templateTabPath = __nested_rspack_exports__.templateTabPath;
|
|
219
|
+
exports.withQuery = __nested_rspack_exports__.withQuery;
|
|
220
|
+
exports.wwwDevicePath = __nested_rspack_exports__.wwwDevicePath;
|
|
221
|
+
exports.wwwImportPath = __nested_rspack_exports__.wwwImportPath;
|
|
222
|
+
exports.wwwNewPath = __nested_rspack_exports__.wwwNewPath;
|
|
223
|
+
exports.wwwTemplatesPath = __nested_rspack_exports__.wwwTemplatesPath;
|
|
224
|
+
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
225
|
+
"ConsoleProjectPage",
|
|
226
|
+
"consoleProjectPath",
|
|
227
|
+
"consoleWorkspacePath",
|
|
228
|
+
"inspectTabPath",
|
|
229
|
+
"previewSharePath",
|
|
230
|
+
"templateTabPath",
|
|
231
|
+
"withQuery",
|
|
232
|
+
"wwwDevicePath",
|
|
233
|
+
"wwwImportPath",
|
|
234
|
+
"wwwNewPath",
|
|
235
|
+
"wwwTemplatesPath"
|
|
236
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
237
|
+
Object.defineProperty(exports, "__esModule", {
|
|
238
|
+
value: true
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
"./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/userland.cjs" (__unused_rspack_module, exports) {
|
|
242
|
+
var __nested_rspack_require_18_37__ = {};
|
|
243
|
+
(()=>{
|
|
244
|
+
__nested_rspack_require_18_37__.d = (exports1, definition)=>{
|
|
245
|
+
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, {
|
|
246
|
+
enumerable: true,
|
|
247
|
+
get: definition[key]
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
})();
|
|
251
|
+
(()=>{
|
|
252
|
+
__nested_rspack_require_18_37__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
253
|
+
})();
|
|
254
|
+
(()=>{
|
|
255
|
+
__nested_rspack_require_18_37__.r = (exports1)=>{
|
|
256
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
257
|
+
value: 'Module'
|
|
258
|
+
});
|
|
259
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
260
|
+
value: true
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
})();
|
|
264
|
+
var __nested_rspack_exports__ = {};
|
|
265
|
+
__nested_rspack_require_18_37__.r(__nested_rspack_exports__);
|
|
266
|
+
__nested_rspack_require_18_37__.d(__nested_rspack_exports__, {
|
|
267
|
+
userlandBuildUrl: ()=>userlandBuildUrl,
|
|
268
|
+
USERLAND_BUILD_TABS: ()=>USERLAND_BUILD_TABS,
|
|
269
|
+
userlandUrl: ()=>userlandUrl,
|
|
270
|
+
UserlandProjectPage: ()=>UserlandProjectPage,
|
|
271
|
+
UserlandDialog: ()=>UserlandDialog,
|
|
272
|
+
userlandChannelUrl: ()=>userlandChannelUrl,
|
|
273
|
+
userlandProjectPath: ()=>userlandProjectPath,
|
|
274
|
+
isUserlandBuildTab: ()=>isUserlandBuildTab,
|
|
275
|
+
userlandDialogUrl: ()=>userlandDialogUrl,
|
|
276
|
+
userlandHostMode: ()=>userlandHostMode,
|
|
277
|
+
userlandOrigin: ()=>userlandOrigin
|
|
278
|
+
});
|
|
279
|
+
const PROD_ORIGINS = {
|
|
280
|
+
www: "https://www.extension.dev",
|
|
281
|
+
console: "https://console.extension.dev",
|
|
282
|
+
inspect: "https://inspect.extension.dev",
|
|
283
|
+
preview: "https://preview.extension.dev",
|
|
284
|
+
templates: "https://templates.extension.dev",
|
|
285
|
+
intelligence: "https://intelligence.extension.dev",
|
|
286
|
+
userland: "https://extension.dev",
|
|
287
|
+
registry: "https://registry.extension.land",
|
|
288
|
+
media: "https://media.extension.land"
|
|
289
|
+
};
|
|
290
|
+
function strip(value) {
|
|
291
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
292
|
+
}
|
|
293
|
+
function isLocalOrigin(url) {
|
|
294
|
+
const raw = strip(url);
|
|
295
|
+
if (!raw) return false;
|
|
296
|
+
let host;
|
|
297
|
+
try {
|
|
298
|
+
host = new URL(raw).hostname;
|
|
299
|
+
} catch {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
return "localhost" === host || "127.0.0.1" === host || "::1" === host || "[::1]" === host || "extension.localhost" === host || host.endsWith(".extension.localhost");
|
|
303
|
+
}
|
|
304
|
+
function withQuery(path, query) {
|
|
305
|
+
if (!query) return path;
|
|
306
|
+
const params = new URLSearchParams();
|
|
307
|
+
for (const [key, value] of Object.entries(query))if (null != value && "" !== value) params.set(key, String(value));
|
|
308
|
+
const qs = params.toString();
|
|
309
|
+
return qs ? `${path}?${qs}` : path;
|
|
310
|
+
}
|
|
311
|
+
const userland_seg = (value)=>encodeURIComponent(String(value));
|
|
312
|
+
const USERLAND_BUILD_TABS = [
|
|
313
|
+
"preview",
|
|
314
|
+
"whats-new",
|
|
315
|
+
"usage"
|
|
316
|
+
];
|
|
317
|
+
function isUserlandBuildTab(value) {
|
|
318
|
+
return USERLAND_BUILD_TABS.includes(value);
|
|
319
|
+
}
|
|
320
|
+
const UserlandDialog = {
|
|
321
|
+
run: "run",
|
|
322
|
+
integrity: "integrity"
|
|
323
|
+
};
|
|
324
|
+
const DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
325
|
+
function userland_strip(value) {
|
|
326
|
+
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
327
|
+
}
|
|
328
|
+
function userlandHostMode(base) {
|
|
329
|
+
return isLocalOrigin(userland_strip(base) || PROD_ORIGINS.userland) ? "path" : "subdomain";
|
|
330
|
+
}
|
|
331
|
+
function userlandOrigin(workspace, options = {}) {
|
|
332
|
+
const base = userland_strip(options.base) || PROD_ORIGINS.userland;
|
|
333
|
+
if ("path" === userlandHostMode(base)) return base;
|
|
334
|
+
const slug = String(workspace ?? "").trim().toLowerCase();
|
|
335
|
+
if (!DNS_LABEL.test(slug)) throw new Error(`Workspace slug ${JSON.stringify(workspace)} cannot be a subdomain label; userland production hosts are <workspace>.extension.dev.`);
|
|
336
|
+
const url = new URL(base);
|
|
337
|
+
url.hostname = `${slug}.${url.hostname}`;
|
|
338
|
+
return userland_strip(url.toString());
|
|
339
|
+
}
|
|
340
|
+
const UserlandProjectPage = {
|
|
341
|
+
overview: "",
|
|
342
|
+
build: (buildId)=>`builds/${userland_seg(buildId)}`,
|
|
343
|
+
buildTab: (buildId, tab = "preview")=>"preview" === tab ? `builds/${userland_seg(buildId)}` : `builds/${userland_seg(buildId)}/${tab}`,
|
|
344
|
+
browserBuild: (buildId, browser)=>`builds/${userland_seg(buildId)}/${userland_seg(browser)}`,
|
|
345
|
+
channel: (channel)=>`channels/${userland_seg(channel)}`,
|
|
346
|
+
channelShortcut: (channel)=>userland_seg(channel),
|
|
347
|
+
channelBrowser: (browser, channel)=>`channels/${userland_seg(browser)}/${userland_seg(channel)}`,
|
|
348
|
+
version: (version)=>`versions/${userland_seg(version)}`,
|
|
349
|
+
versionBrowser: (browser, version)=>`versions/${userland_seg(browser)}/${userland_seg(version)}`
|
|
350
|
+
};
|
|
351
|
+
function userlandProjectPath(ref, page = "", options = {}) {
|
|
352
|
+
const head = "subdomain" === userlandHostMode(options.base) ? `/${userland_seg(ref.project)}` : `/${userland_seg(ref.workspace)}/${userland_seg(ref.project)}`;
|
|
353
|
+
const tail = String(page ?? "").replace(/^\/+/, "");
|
|
354
|
+
return tail ? `${head}/${tail}` : head;
|
|
355
|
+
}
|
|
356
|
+
function userlandUrl(ref, page = "", options = {}) {
|
|
357
|
+
const origin = userlandOrigin(ref.workspace, options);
|
|
358
|
+
const path = userlandProjectPath(ref, page, options);
|
|
359
|
+
return `${origin}${withQuery(path, options.query)}`;
|
|
360
|
+
}
|
|
361
|
+
function userlandBuildUrl(ref, buildId, options = {}) {
|
|
362
|
+
const page = options.browser ? UserlandProjectPage.browserBuild(buildId, options.browser) : UserlandProjectPage.build(buildId);
|
|
363
|
+
return userlandUrl(ref, page, options);
|
|
364
|
+
}
|
|
365
|
+
function userlandDialogUrl(ref, buildId, browser, dialog, options = {}) {
|
|
366
|
+
return userlandUrl(ref, UserlandProjectPage.browserBuild(buildId, browser), {
|
|
367
|
+
...options,
|
|
368
|
+
query: {
|
|
369
|
+
...options.query ?? {},
|
|
370
|
+
dialog
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
function userlandChannelUrl(ref, channel, options = {}) {
|
|
375
|
+
const page = options.browser ? UserlandProjectPage.channelBrowser(options.browser, channel) : options.shortcut ? UserlandProjectPage.channelShortcut(channel) : UserlandProjectPage.channel(channel);
|
|
376
|
+
return userlandUrl(ref, page, options);
|
|
377
|
+
}
|
|
378
|
+
exports.USERLAND_BUILD_TABS = __nested_rspack_exports__.USERLAND_BUILD_TABS;
|
|
379
|
+
exports.UserlandDialog = __nested_rspack_exports__.UserlandDialog;
|
|
380
|
+
exports.UserlandProjectPage = __nested_rspack_exports__.UserlandProjectPage;
|
|
381
|
+
exports.isUserlandBuildTab = __nested_rspack_exports__.isUserlandBuildTab;
|
|
382
|
+
exports.userlandBuildUrl = __nested_rspack_exports__.userlandBuildUrl;
|
|
383
|
+
exports.userlandChannelUrl = __nested_rspack_exports__.userlandChannelUrl;
|
|
384
|
+
exports.userlandDialogUrl = __nested_rspack_exports__.userlandDialogUrl;
|
|
385
|
+
exports.userlandHostMode = __nested_rspack_exports__.userlandHostMode;
|
|
386
|
+
exports.userlandOrigin = __nested_rspack_exports__.userlandOrigin;
|
|
387
|
+
exports.userlandProjectPath = __nested_rspack_exports__.userlandProjectPath;
|
|
388
|
+
exports.userlandUrl = __nested_rspack_exports__.userlandUrl;
|
|
389
|
+
for(var __webpack_i__ in __nested_rspack_exports__)if (-1 === [
|
|
390
|
+
"USERLAND_BUILD_TABS",
|
|
391
|
+
"UserlandDialog",
|
|
392
|
+
"UserlandProjectPage",
|
|
393
|
+
"isUserlandBuildTab",
|
|
394
|
+
"userlandBuildUrl",
|
|
395
|
+
"userlandChannelUrl",
|
|
396
|
+
"userlandDialogUrl",
|
|
397
|
+
"userlandHostMode",
|
|
398
|
+
"userlandOrigin",
|
|
399
|
+
"userlandProjectPath",
|
|
400
|
+
"userlandUrl"
|
|
401
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __nested_rspack_exports__[__webpack_i__];
|
|
402
|
+
Object.defineProperty(exports, "__esModule", {
|
|
403
|
+
value: true
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
});
|
|
19
407
|
var add_feature_namespaceObject = {};
|
|
20
408
|
__webpack_require__.r(add_feature_namespaceObject);
|
|
21
409
|
__webpack_require__.d(add_feature_namespaceObject, {
|
|
@@ -222,7 +610,7 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
222
610
|
handler: ()=>whoami_handler,
|
|
223
611
|
schema: ()=>whoami_schema
|
|
224
612
|
});
|
|
225
|
-
var package_namespaceObject = JSON.parse('{"rE":"6.
|
|
613
|
+
var package_namespaceObject = JSON.parse('{"rE":"6.3.0","El":{"OP":"4.0.16-canary.1784889479.74e12044"}}');
|
|
226
614
|
function credentialsPath() {
|
|
227
615
|
if ("win32" === process.platform) {
|
|
228
616
|
const base = process.env.APPDATA || process.env.LOCALAPPDATA || node_path.join(node_os.homedir(), "AppData", "Roaming");
|
|
@@ -295,77 +683,249 @@ function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
|
295
683
|
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
296
684
|
return creds;
|
|
297
685
|
}
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
console: "http://console.extension.localhost",
|
|
310
|
-
inspect: "http://inspect.extension.localhost",
|
|
311
|
-
templates: "http://templates.extension.localhost",
|
|
312
|
-
intelligence: "http://intelligence.extension.localhost",
|
|
313
|
-
registry: "https://registry.extension.land",
|
|
314
|
-
media: "https://media.extension.land"
|
|
315
|
-
};
|
|
316
|
-
function strip(value) {
|
|
317
|
-
return String(value ?? "").trim().replace(/\/+$/, "");
|
|
686
|
+
const origins = __webpack_require__("./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/origins.cjs");
|
|
687
|
+
const DEFAULT_API = origins.PROD_ORIGINS.www;
|
|
688
|
+
function tokenTtlNote(workspaceSlug, projectSlug) {
|
|
689
|
+
const tokensUrl = workspaceSlug && projectSlug ? consoleProjectUrl({
|
|
690
|
+
workspace: workspaceSlug,
|
|
691
|
+
project: projectSlug
|
|
692
|
+
}, "settings/access-tokens") : consoleBase();
|
|
693
|
+
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}`;
|
|
694
|
+
}
|
|
695
|
+
function resolveApiBase(api) {
|
|
696
|
+
return String(api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, "");
|
|
318
697
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
if (!raw) return false;
|
|
322
|
-
let host;
|
|
698
|
+
function safeApiBase(raw) {
|
|
699
|
+
let parsed;
|
|
323
700
|
try {
|
|
324
|
-
|
|
701
|
+
parsed = new URL(raw);
|
|
325
702
|
} catch {
|
|
326
|
-
return
|
|
703
|
+
return {
|
|
704
|
+
ok: false,
|
|
705
|
+
message: `Invalid platform URL: ${raw}`
|
|
706
|
+
};
|
|
327
707
|
}
|
|
328
|
-
|
|
708
|
+
const isLocalhost = "localhost" === parsed.hostname || "127.0.0.1" === parsed.hostname || "[::1]" === parsed.hostname || "::1" === parsed.hostname;
|
|
709
|
+
if ("https:" !== parsed.protocol && !("http:" === parsed.protocol && isLocalhost)) return {
|
|
710
|
+
ok: false,
|
|
711
|
+
message: `Refusing to send the access token to ${raw}: use https (http is allowed only for localhost).`
|
|
712
|
+
};
|
|
713
|
+
return {
|
|
714
|
+
ok: true,
|
|
715
|
+
base: `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "")
|
|
716
|
+
};
|
|
329
717
|
}
|
|
330
|
-
function
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
718
|
+
async function fetchLoginConfig(apiBase, fetchImpl = fetch) {
|
|
719
|
+
const override = String(process.env.EXTENSION_DEV_GITHUB_CLIENT_ID || "").trim();
|
|
720
|
+
if (override) return {
|
|
721
|
+
provider: "github",
|
|
722
|
+
clientId: override,
|
|
723
|
+
scope: "read:user",
|
|
724
|
+
deviceCodeUrl: "/api/cli/device/code",
|
|
725
|
+
deviceTokenUrl: "/api/cli/device/token",
|
|
726
|
+
verificationUri: "https://github.com/login/device"
|
|
727
|
+
};
|
|
728
|
+
const res = await fetchImpl(`${apiBase}/api/cli/login/config`, {
|
|
729
|
+
headers: {
|
|
730
|
+
accept: "application/json"
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
if (!res.ok) throw new Error(`Could not fetch login config from ${apiBase} (${res.status}).`);
|
|
734
|
+
const data = await res.json().catch(()=>({}));
|
|
735
|
+
const provider = "extensiondev" === data.provider ? "extensiondev" : "github";
|
|
736
|
+
const clientId = String(data.githubClientId || "").trim();
|
|
737
|
+
if ("github" === provider && !clientId) throw new Error("Login is not configured on the server (no GitHub client id). Set EXTENSION_DEV_GITHUB_CLIENT_ID to override.");
|
|
334
738
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
media: pick("media")
|
|
739
|
+
provider,
|
|
740
|
+
clientId,
|
|
741
|
+
scope: String(data.scope || "read:user"),
|
|
742
|
+
deviceCodeUrl: String(data.deviceCodeUrl || "/api/cli/device/code"),
|
|
743
|
+
deviceTokenUrl: String(data.deviceTokenUrl || "/api/cli/device/token"),
|
|
744
|
+
verificationUri: String(data.verificationUri || "https://github.com/login/device")
|
|
342
745
|
};
|
|
343
746
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (!
|
|
347
|
-
|
|
747
|
+
function persistTokenResponse(args) {
|
|
748
|
+
const token = String(args.data.token || "").trim();
|
|
749
|
+
if (!token) throw new Error("Login returned no token.");
|
|
750
|
+
const creds = {
|
|
751
|
+
version: 1,
|
|
752
|
+
token,
|
|
753
|
+
workspaceSlug: String(args.data.workspaceSlug || ""),
|
|
754
|
+
projectSlug: String(args.data.projectSlug || ""),
|
|
755
|
+
expiresAt: Number(args.data.expiresAt || 0),
|
|
756
|
+
api: args.apiBase,
|
|
757
|
+
provider: args.provider
|
|
758
|
+
};
|
|
759
|
+
writeCredentials(creds);
|
|
760
|
+
return creds;
|
|
761
|
+
}
|
|
762
|
+
async function exchangeAndPersist(args) {
|
|
763
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
764
|
+
const res = await doFetch(`${args.apiBase}/api/cli/login/exchange`, {
|
|
765
|
+
method: "POST",
|
|
766
|
+
headers: {
|
|
767
|
+
"content-type": "application/json",
|
|
768
|
+
accept: "application/json"
|
|
769
|
+
},
|
|
770
|
+
body: JSON.stringify({
|
|
771
|
+
githubToken: args.githubToken,
|
|
772
|
+
project: args.project
|
|
773
|
+
})
|
|
774
|
+
});
|
|
775
|
+
const text = await res.text();
|
|
776
|
+
let data;
|
|
777
|
+
try {
|
|
778
|
+
data = JSON.parse(text);
|
|
779
|
+
} catch {
|
|
780
|
+
data = {
|
|
781
|
+
message: text
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
if (!res.ok) throw new Error(`Login exchange failed (${res.status}): ${data.message || "unknown error"}`);
|
|
785
|
+
return persistTokenResponse({
|
|
786
|
+
apiBase: args.apiBase,
|
|
787
|
+
data,
|
|
788
|
+
provider: "github"
|
|
789
|
+
});
|
|
348
790
|
}
|
|
349
|
-
function
|
|
350
|
-
|
|
791
|
+
function _define_property(obj, key, value) {
|
|
792
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
793
|
+
value: value,
|
|
794
|
+
enumerable: true,
|
|
795
|
+
configurable: true,
|
|
796
|
+
writable: true
|
|
797
|
+
});
|
|
798
|
+
else obj[key] = value;
|
|
799
|
+
return obj;
|
|
351
800
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
801
|
+
const REFRESH_LEAD_SECONDS = 60;
|
|
802
|
+
function cacheKey(ref) {
|
|
803
|
+
return `${ref.workspace.toLowerCase()}/${ref.project.toLowerCase()}`;
|
|
804
|
+
}
|
|
805
|
+
class RegistryAccessTokens {
|
|
806
|
+
peek(ref) {
|
|
807
|
+
const entry = this.cache.get(cacheKey(ref));
|
|
808
|
+
if (!entry || "fresh" !== entry.kind) return "";
|
|
809
|
+
return entry.expiresAt - REFRESH_LEAD_SECONDS > this.nowSeconds() ? entry.token : "";
|
|
810
|
+
}
|
|
811
|
+
async get(ref, apiHint) {
|
|
812
|
+
const key = cacheKey(ref);
|
|
813
|
+
const entry = this.cache.get(key);
|
|
814
|
+
if (entry?.kind === "pending") return entry.promise;
|
|
815
|
+
const cached = this.peek(ref);
|
|
816
|
+
if (cached) return {
|
|
817
|
+
status: "ok",
|
|
818
|
+
token: cached,
|
|
819
|
+
expiresAt: this.cache.get(key).expiresAt
|
|
820
|
+
};
|
|
821
|
+
const promise = this.mint(ref, apiHint);
|
|
822
|
+
this.cache.set(key, {
|
|
823
|
+
kind: "pending",
|
|
824
|
+
promise
|
|
825
|
+
});
|
|
826
|
+
const result = await promise;
|
|
827
|
+
if ("ok" === result.status) this.cache.set(key, {
|
|
828
|
+
kind: "fresh",
|
|
829
|
+
token: result.token,
|
|
830
|
+
expiresAt: result.expiresAt
|
|
831
|
+
});
|
|
832
|
+
else this.cache.delete(key);
|
|
833
|
+
return result;
|
|
834
|
+
}
|
|
835
|
+
async mint(ref, apiHint) {
|
|
836
|
+
const creds = readCredentials();
|
|
837
|
+
const token = String(process.env.EXTENSION_DEV_TOKEN || creds?.token || "").trim();
|
|
838
|
+
if (!token) return {
|
|
839
|
+
status: "no-credential"
|
|
840
|
+
};
|
|
841
|
+
if (creds?.workspaceSlug && creds?.projectSlug && !process.env.EXTENSION_DEV_TOKEN) {
|
|
842
|
+
const same = creds.workspaceSlug.toLowerCase() === ref.workspace.toLowerCase() && creds.projectSlug.toLowerCase() === ref.project.toLowerCase();
|
|
843
|
+
if (!same) return {
|
|
844
|
+
status: "no-credential"
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
const check = safeApiBase(resolveApiBase(apiHint || creds?.api));
|
|
848
|
+
if (!check.ok) return {
|
|
849
|
+
status: "denied",
|
|
850
|
+
message: check.message
|
|
851
|
+
};
|
|
852
|
+
let res;
|
|
853
|
+
try {
|
|
854
|
+
res = await this.fetchImpl(`${check.base}/api/access-grant`, {
|
|
855
|
+
method: "POST",
|
|
856
|
+
headers: {
|
|
857
|
+
authorization: `Bearer ${token}`,
|
|
858
|
+
"content-type": "application/json"
|
|
859
|
+
},
|
|
860
|
+
body: JSON.stringify({
|
|
861
|
+
workspaceSlug: ref.workspace,
|
|
862
|
+
projectSlug: ref.project
|
|
863
|
+
})
|
|
864
|
+
});
|
|
865
|
+
} catch (err) {
|
|
866
|
+
return {
|
|
867
|
+
status: "denied",
|
|
868
|
+
message: `Could not reach the access-grant endpoint: ${err?.message || err}`
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
if (400 === res.status) return {
|
|
872
|
+
status: "public"
|
|
873
|
+
};
|
|
874
|
+
if (!res.ok) return {
|
|
875
|
+
status: "denied",
|
|
876
|
+
httpStatus: res.status,
|
|
877
|
+
message: `access-grant returned ${res.status}`
|
|
878
|
+
};
|
|
879
|
+
let data;
|
|
880
|
+
try {
|
|
881
|
+
data = JSON.parse(await res.text());
|
|
882
|
+
} catch {
|
|
883
|
+
return {
|
|
884
|
+
status: "denied",
|
|
885
|
+
message: "access-grant did not return JSON"
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
const minted = String(data?.token || "").trim();
|
|
889
|
+
if (!minted) return {
|
|
890
|
+
status: "denied",
|
|
891
|
+
message: "access-grant returned no token"
|
|
892
|
+
};
|
|
893
|
+
return {
|
|
894
|
+
status: "ok",
|
|
895
|
+
token: minted,
|
|
896
|
+
expiresAt: Number(data?.expiresAt || 0)
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
constructor(options){
|
|
900
|
+
_define_property(this, "cache", new Map());
|
|
901
|
+
_define_property(this, "fetchImpl", void 0);
|
|
902
|
+
_define_property(this, "nowSeconds", void 0);
|
|
903
|
+
this.fetchImpl = options?.fetchImpl ?? fetch;
|
|
904
|
+
this.nowSeconds = options?.nowSeconds ?? (()=>Math.floor(Date.now() / 1000));
|
|
905
|
+
}
|
|
358
906
|
}
|
|
359
|
-
|
|
360
|
-
|
|
907
|
+
const defaultRegistryAccessTokens = new RegistryAccessTokens();
|
|
908
|
+
function withAccessToken(url, token) {
|
|
909
|
+
if (!token) return url;
|
|
910
|
+
try {
|
|
911
|
+
const u = new URL(url);
|
|
912
|
+
u.searchParams.set("t", token);
|
|
913
|
+
return u.toString();
|
|
914
|
+
} catch {
|
|
915
|
+
return url;
|
|
916
|
+
}
|
|
361
917
|
}
|
|
362
|
-
|
|
918
|
+
const paths_0 = __webpack_require__("./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/paths.cjs");
|
|
919
|
+
const userland = __webpack_require__("./node_modules/.pnpm/@extension.dev+urls@0.3.0/node_modules/@extension.dev/urls/dist/userland.cjs");
|
|
920
|
+
origins.PROD_ORIGINS.registry;
|
|
363
921
|
function mcpOrigins(apiHint) {
|
|
364
922
|
const www = String(apiHint || process.env.EXTENSION_DEV_API_URL || "").trim() || void 0;
|
|
365
|
-
return resolveOrigins({
|
|
923
|
+
return (0, origins.resolveOrigins)({
|
|
366
924
|
www,
|
|
367
925
|
console: process.env.EXTENSION_DEV_CONSOLE_URL,
|
|
368
926
|
inspect: process.env.EXTENSION_DEV_INSPECT_URL,
|
|
927
|
+
preview: process.env.EXTENSION_DEV_PREVIEW_URL,
|
|
928
|
+
userland: process.env.EXTENSION_DEV_USERLAND_URL,
|
|
369
929
|
registry: process.env.EXTENSION_DEV_REGISTRY_URL,
|
|
370
930
|
media: process.env.EXTENSION_MEDIA_ORIGIN
|
|
371
931
|
}, {
|
|
@@ -400,35 +960,77 @@ function registryFileUrl(ref, file) {
|
|
|
400
960
|
function consoleProjectUrl(ref, page, apiHint) {
|
|
401
961
|
const base = consoleBase(apiHint);
|
|
402
962
|
if (!ref) return base;
|
|
403
|
-
return `${base}${consoleProjectPath(ref, page)}`;
|
|
963
|
+
return `${base}${(0, paths_0.consoleProjectPath)(ref, page)}`;
|
|
964
|
+
}
|
|
965
|
+
function userlandProjectUrl(ref, page = "", apiHint) {
|
|
966
|
+
if (!ref) return "";
|
|
967
|
+
try {
|
|
968
|
+
return (0, userland.userlandUrl)(ref, page, {
|
|
969
|
+
base: mcpOrigins(apiHint).userland
|
|
970
|
+
});
|
|
971
|
+
} catch {
|
|
972
|
+
return "";
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async function readJson(url, res) {
|
|
976
|
+
try {
|
|
977
|
+
const text = await res.text();
|
|
978
|
+
return {
|
|
979
|
+
ok: true,
|
|
980
|
+
json: JSON.parse(text)
|
|
981
|
+
};
|
|
982
|
+
} catch {
|
|
983
|
+
return {
|
|
984
|
+
ok: false,
|
|
985
|
+
message: `${url} did not return valid JSON`
|
|
986
|
+
};
|
|
987
|
+
}
|
|
404
988
|
}
|
|
405
|
-
async function fetchRegistryJson(url, fetchImpl = fetch) {
|
|
989
|
+
async function fetchRegistryJson(url, fetchImpl = fetch, options) {
|
|
990
|
+
const tokens = options?.tokens ?? defaultRegistryAccessTokens;
|
|
991
|
+
const ref = options?.ref ?? null;
|
|
992
|
+
const cached = ref ? tokens.peek(ref) : "";
|
|
993
|
+
const firstUrl = cached ? withAccessToken(url, cached) : url;
|
|
406
994
|
let res;
|
|
407
995
|
try {
|
|
408
|
-
res = await fetchImpl(
|
|
996
|
+
res = await fetchImpl(firstUrl);
|
|
409
997
|
} catch (err) {
|
|
410
998
|
return {
|
|
411
999
|
ok: false,
|
|
412
1000
|
message: `Could not reach ${url}: ${err?.message || err}`
|
|
413
1001
|
};
|
|
414
1002
|
}
|
|
415
|
-
if (
|
|
1003
|
+
if (res.ok) return readJson(url, res);
|
|
1004
|
+
const authFailed = 401 === res.status || 403 === res.status;
|
|
1005
|
+
if (!authFailed || !ref) return {
|
|
416
1006
|
ok: false,
|
|
417
1007
|
status: res.status,
|
|
418
1008
|
message: `${url} returned ${res.status}`
|
|
419
1009
|
};
|
|
420
|
-
|
|
421
|
-
|
|
1010
|
+
const grant = await tokens.get(ref, options?.api);
|
|
1011
|
+
if ("ok" !== grant.status) {
|
|
1012
|
+
const detail = "no-credential" === grant.status ? "This project is private. Run extension_login for it, or set EXTENSION_DEV_TOKEN." : "public" === grant.status ? "The platform reports this project is public, but the registry refused the read." : grant.message;
|
|
422
1013
|
return {
|
|
423
|
-
ok:
|
|
424
|
-
|
|
1014
|
+
ok: false,
|
|
1015
|
+
status: res.status,
|
|
1016
|
+
message: `${url} returned ${res.status}. ${detail}`
|
|
425
1017
|
};
|
|
426
|
-
}
|
|
1018
|
+
}
|
|
1019
|
+
let retried;
|
|
1020
|
+
try {
|
|
1021
|
+
retried = await fetchImpl(withAccessToken(url, grant.token));
|
|
1022
|
+
} catch (err) {
|
|
427
1023
|
return {
|
|
428
1024
|
ok: false,
|
|
429
|
-
message:
|
|
1025
|
+
message: `Could not reach ${url}: ${err?.message || err}`
|
|
430
1026
|
};
|
|
431
1027
|
}
|
|
1028
|
+
if (!retried.ok) return {
|
|
1029
|
+
ok: false,
|
|
1030
|
+
status: retried.status,
|
|
1031
|
+
message: `${url} returned ${retried.status} even with an access token`
|
|
1032
|
+
};
|
|
1033
|
+
return readJson(url, retried);
|
|
432
1034
|
}
|
|
433
1035
|
function parseChannels(json) {
|
|
434
1036
|
if (!json || "object" != typeof json || Array.isArray(json)) return [];
|
|
@@ -613,7 +1215,7 @@ async function create_handler(args) {
|
|
|
613
1215
|
const resolvedParent = args.parentDir ? node_path.resolve(args.parentDir) : process.cwd();
|
|
614
1216
|
const gitInit = node_fs.existsSync(node_path.join(result.projectPath, ".git"));
|
|
615
1217
|
const wwwOrigin = mcpOrigins().www;
|
|
616
|
-
const deployUrl = `${wwwOrigin}${wwwNewPath({
|
|
1218
|
+
const deployUrl = `${wwwOrigin}${(0, paths_0.wwwNewPath)({
|
|
617
1219
|
template: result.template
|
|
618
1220
|
})}`;
|
|
619
1221
|
return JSON.stringify({
|
|
@@ -3197,7 +3799,7 @@ function summarizeConsoleMessages(messages) {
|
|
|
3197
3799
|
topMessages: topMessages.sort((a, b)=>b.count - a.count).slice(0, 10)
|
|
3198
3800
|
};
|
|
3199
3801
|
}
|
|
3200
|
-
function
|
|
3802
|
+
function cdp_connection_define_property(obj, key, value) {
|
|
3201
3803
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
3202
3804
|
value: value,
|
|
3203
3805
|
enumerable: true,
|
|
@@ -3315,11 +3917,11 @@ class CDPConnection {
|
|
|
3315
3917
|
};
|
|
3316
3918
|
}
|
|
3317
3919
|
constructor(){
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3920
|
+
cdp_connection_define_property(this, "ws", null);
|
|
3921
|
+
cdp_connection_define_property(this, "messageId", 0);
|
|
3922
|
+
cdp_connection_define_property(this, "eventListeners", new Set());
|
|
3923
|
+
cdp_connection_define_property(this, "pendingRequests", new Map());
|
|
3924
|
+
cdp_connection_define_property(this, "consoleMessages", []);
|
|
3323
3925
|
}
|
|
3324
3926
|
}
|
|
3325
3927
|
const PAGE_HTML_SCRIPT = `(() => {
|
|
@@ -6049,110 +6651,6 @@ async function dom_inspect_handler(args) {
|
|
|
6049
6651
|
return raw;
|
|
6050
6652
|
}
|
|
6051
6653
|
}
|
|
6052
|
-
const DEFAULT_API = PROD_ORIGINS.www;
|
|
6053
|
-
function tokenTtlNote(workspaceSlug, projectSlug) {
|
|
6054
|
-
const tokensUrl = workspaceSlug && projectSlug ? consoleProjectUrl({
|
|
6055
|
-
workspace: workspaceSlug,
|
|
6056
|
-
project: projectSlug
|
|
6057
|
-
}, "settings/access-tokens") : consoleBase();
|
|
6058
|
-
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}`;
|
|
6059
|
-
}
|
|
6060
|
-
function resolveApiBase(api) {
|
|
6061
|
-
return String(api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, "");
|
|
6062
|
-
}
|
|
6063
|
-
function safeApiBase(raw) {
|
|
6064
|
-
let parsed;
|
|
6065
|
-
try {
|
|
6066
|
-
parsed = new URL(raw);
|
|
6067
|
-
} catch {
|
|
6068
|
-
return {
|
|
6069
|
-
ok: false,
|
|
6070
|
-
message: `Invalid platform URL: ${raw}`
|
|
6071
|
-
};
|
|
6072
|
-
}
|
|
6073
|
-
const isLocalhost = "localhost" === parsed.hostname || "127.0.0.1" === parsed.hostname || "[::1]" === parsed.hostname || "::1" === parsed.hostname;
|
|
6074
|
-
if ("https:" !== parsed.protocol && !("http:" === parsed.protocol && isLocalhost)) return {
|
|
6075
|
-
ok: false,
|
|
6076
|
-
message: `Refusing to send the access token to ${raw}: use https (http is allowed only for localhost).`
|
|
6077
|
-
};
|
|
6078
|
-
return {
|
|
6079
|
-
ok: true,
|
|
6080
|
-
base: `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "")
|
|
6081
|
-
};
|
|
6082
|
-
}
|
|
6083
|
-
async function fetchLoginConfig(apiBase, fetchImpl = fetch) {
|
|
6084
|
-
const override = String(process.env.EXTENSION_DEV_GITHUB_CLIENT_ID || "").trim();
|
|
6085
|
-
if (override) return {
|
|
6086
|
-
provider: "github",
|
|
6087
|
-
clientId: override,
|
|
6088
|
-
scope: "read:user",
|
|
6089
|
-
deviceCodeUrl: "/api/cli/device/code",
|
|
6090
|
-
deviceTokenUrl: "/api/cli/device/token",
|
|
6091
|
-
verificationUri: "https://github.com/login/device"
|
|
6092
|
-
};
|
|
6093
|
-
const res = await fetchImpl(`${apiBase}/api/cli/login/config`, {
|
|
6094
|
-
headers: {
|
|
6095
|
-
accept: "application/json"
|
|
6096
|
-
}
|
|
6097
|
-
});
|
|
6098
|
-
if (!res.ok) throw new Error(`Could not fetch login config from ${apiBase} (${res.status}).`);
|
|
6099
|
-
const data = await res.json().catch(()=>({}));
|
|
6100
|
-
const provider = "extensiondev" === data.provider ? "extensiondev" : "github";
|
|
6101
|
-
const clientId = String(data.githubClientId || "").trim();
|
|
6102
|
-
if ("github" === provider && !clientId) throw new Error("Login is not configured on the server (no GitHub client id). Set EXTENSION_DEV_GITHUB_CLIENT_ID to override.");
|
|
6103
|
-
return {
|
|
6104
|
-
provider,
|
|
6105
|
-
clientId,
|
|
6106
|
-
scope: String(data.scope || "read:user"),
|
|
6107
|
-
deviceCodeUrl: String(data.deviceCodeUrl || "/api/cli/device/code"),
|
|
6108
|
-
deviceTokenUrl: String(data.deviceTokenUrl || "/api/cli/device/token"),
|
|
6109
|
-
verificationUri: String(data.verificationUri || "https://github.com/login/device")
|
|
6110
|
-
};
|
|
6111
|
-
}
|
|
6112
|
-
function persistTokenResponse(args) {
|
|
6113
|
-
const token = String(args.data.token || "").trim();
|
|
6114
|
-
if (!token) throw new Error("Login returned no token.");
|
|
6115
|
-
const creds = {
|
|
6116
|
-
version: 1,
|
|
6117
|
-
token,
|
|
6118
|
-
workspaceSlug: String(args.data.workspaceSlug || ""),
|
|
6119
|
-
projectSlug: String(args.data.projectSlug || ""),
|
|
6120
|
-
expiresAt: Number(args.data.expiresAt || 0),
|
|
6121
|
-
api: args.apiBase,
|
|
6122
|
-
provider: args.provider
|
|
6123
|
-
};
|
|
6124
|
-
writeCredentials(creds);
|
|
6125
|
-
return creds;
|
|
6126
|
-
}
|
|
6127
|
-
async function exchangeAndPersist(args) {
|
|
6128
|
-
const doFetch = args.fetchImpl ?? fetch;
|
|
6129
|
-
const res = await doFetch(`${args.apiBase}/api/cli/login/exchange`, {
|
|
6130
|
-
method: "POST",
|
|
6131
|
-
headers: {
|
|
6132
|
-
"content-type": "application/json",
|
|
6133
|
-
accept: "application/json"
|
|
6134
|
-
},
|
|
6135
|
-
body: JSON.stringify({
|
|
6136
|
-
githubToken: args.githubToken,
|
|
6137
|
-
project: args.project
|
|
6138
|
-
})
|
|
6139
|
-
});
|
|
6140
|
-
const text = await res.text();
|
|
6141
|
-
let data;
|
|
6142
|
-
try {
|
|
6143
|
-
data = JSON.parse(text);
|
|
6144
|
-
} catch {
|
|
6145
|
-
data = {
|
|
6146
|
-
message: text
|
|
6147
|
-
};
|
|
6148
|
-
}
|
|
6149
|
-
if (!res.ok) throw new Error(`Login exchange failed (${res.status}): ${data.message || "unknown error"}`);
|
|
6150
|
-
return persistTokenResponse({
|
|
6151
|
-
apiBase: args.apiBase,
|
|
6152
|
-
data,
|
|
6153
|
-
provider: "github"
|
|
6154
|
-
});
|
|
6155
|
-
}
|
|
6156
6654
|
function resolveToken() {
|
|
6157
6655
|
const fromEnv = String(process.env.EXTENSION_DEV_TOKEN || "").trim();
|
|
6158
6656
|
if (fromEnv) return fromEnv;
|
|
@@ -6274,7 +6772,10 @@ async function publish_handler(args) {
|
|
|
6274
6772
|
const ref = resolveProjectRef();
|
|
6275
6773
|
if (ref) {
|
|
6276
6774
|
const buildsUrl = registryFileUrl(ref, "builds/index.json");
|
|
6277
|
-
const buildsRes = await fetchRegistryJson(buildsUrl
|
|
6775
|
+
const buildsRes = await fetchRegistryJson(buildsUrl, fetch, {
|
|
6776
|
+
ref,
|
|
6777
|
+
api: args.api
|
|
6778
|
+
});
|
|
6278
6779
|
if (buildsRes.ok) {
|
|
6279
6780
|
const items = parseBuildIndex(buildsRes.json);
|
|
6280
6781
|
const pinned = args.buildSha ? items.find((item)=>{
|
|
@@ -6396,7 +6897,10 @@ async function release_promote_handler(args) {
|
|
|
6396
6897
|
enrich.hint = "Run extension_release_list to see this project's channels, their promoted shas, and recent builds.";
|
|
6397
6898
|
if (ref) {
|
|
6398
6899
|
const channelsUrl = registryFileUrl(ref, "channels.json");
|
|
6399
|
-
const channelsRes = await fetchRegistryJson(channelsUrl
|
|
6900
|
+
const channelsRes = await fetchRegistryJson(channelsUrl, fetch, {
|
|
6901
|
+
ref,
|
|
6902
|
+
api: args.api
|
|
6903
|
+
});
|
|
6400
6904
|
if (channelsRes.ok) {
|
|
6401
6905
|
const rows = parseChannels(channelsRes.json).filter((c)=>c.sha);
|
|
6402
6906
|
enrich.validChannelShas = Object.fromEntries(rows.map((c)=>[
|
|
@@ -6419,11 +6923,23 @@ async function release_promote_handler(args) {
|
|
|
6419
6923
|
...enrich
|
|
6420
6924
|
});
|
|
6421
6925
|
}
|
|
6422
|
-
|
|
6926
|
+
const promotedRef = resolveProjectRef();
|
|
6927
|
+
const publicChannelUrl = userlandProjectUrl(promotedRef, userland.UserlandProjectPage.channel(channel), args.api);
|
|
6928
|
+
const publicBuildUrl = userlandProjectUrl(promotedRef, userland.UserlandProjectPage.build(buildId), args.api);
|
|
6929
|
+
const enriched = data && "object" == typeof data && !Array.isArray(data) ? {
|
|
6930
|
+
...data,
|
|
6931
|
+
...publicChannelUrl ? {
|
|
6932
|
+
publicChannelUrl
|
|
6933
|
+
} : {},
|
|
6934
|
+
...publicBuildUrl ? {
|
|
6935
|
+
publicBuildUrl
|
|
6936
|
+
} : {}
|
|
6937
|
+
} : data;
|
|
6938
|
+
return JSON.stringify(enriched);
|
|
6423
6939
|
}
|
|
6424
6940
|
const release_list_schema = {
|
|
6425
6941
|
name: "extension_release_list",
|
|
6426
|
-
description: "List the project's release channels (channel -> promoted build sha) and recent builds from the
|
|
6942
|
+
description: "List the project's release channels (channel -> promoted build sha) and recent builds from the registry (registry.extension.land), so you can pick a valid buildSha for extension_release_promote, extension_deploy, or extension_publish. Read-only, no dispatch. Defaults to the logged-in project (extension_login); pass workspace + project to inspect another project. Works for PRIVATE projects too when your stored login covers them. Returns the registry URLs it read, the console Builds page URL (needs a login), and a publicUrl per channel and build on the public viewer (no login needed for a public project).",
|
|
6427
6943
|
inputSchema: {
|
|
6428
6944
|
type: "object",
|
|
6429
6945
|
properties: {
|
|
@@ -6434,6 +6950,10 @@ const release_list_schema = {
|
|
|
6434
6950
|
project: {
|
|
6435
6951
|
type: "string",
|
|
6436
6952
|
description: "Project slug override (defaults to the stored login's project)."
|
|
6953
|
+
},
|
|
6954
|
+
api: {
|
|
6955
|
+
type: "string",
|
|
6956
|
+
description: "Platform base URL (defaults to https://www.extension.dev or EXTENSION_DEV_API_URL). Used to resolve link origins and, for a private project, to mint a short-lived read token."
|
|
6437
6957
|
}
|
|
6438
6958
|
},
|
|
6439
6959
|
required: []
|
|
@@ -6456,12 +6976,21 @@ async function release_list_handler(args) {
|
|
|
6456
6976
|
const metaUrl = registryFileUrl(ref, "meta.json");
|
|
6457
6977
|
const buildsUrl = registryFileUrl(ref, "builds/index.json");
|
|
6458
6978
|
const [channelsRes, metaRes, buildsRes] = await Promise.all([
|
|
6459
|
-
fetchRegistryJson(channelsUrl
|
|
6460
|
-
|
|
6461
|
-
|
|
6979
|
+
fetchRegistryJson(channelsUrl, fetch, {
|
|
6980
|
+
ref,
|
|
6981
|
+
api: args.api
|
|
6982
|
+
}),
|
|
6983
|
+
fetchRegistryJson(metaUrl, fetch, {
|
|
6984
|
+
ref,
|
|
6985
|
+
api: args.api
|
|
6986
|
+
}),
|
|
6987
|
+
fetchRegistryJson(buildsUrl, fetch, {
|
|
6988
|
+
ref,
|
|
6989
|
+
api: args.api
|
|
6990
|
+
})
|
|
6462
6991
|
]);
|
|
6463
|
-
const buildsPageUrl = consoleProjectUrl(ref, "builds");
|
|
6464
|
-
if (!channelsRes.ok && !metaRes.ok && !buildsRes.ok) return release_list_fail("ReleaseListNotFound", `No registry data for ${ref.workspace}/${ref.project} (${channelsUrl} returned ${channelsRes.status ?? "no response"}). The project may have no builds yet,
|
|
6992
|
+
const buildsPageUrl = consoleProjectUrl(ref, "builds", args.api);
|
|
6993
|
+
if (!channelsRes.ok && !metaRes.ok && !buildsRes.ok) return release_list_fail("ReleaseListNotFound", `No registry data for ${ref.workspace}/${ref.project} (${channelsUrl} returned ${channelsRes.status ?? "no response"}). The project may have no builds yet, or the workspace/project slugs may be wrong. If it is private, make sure extension_login covers this exact project (a token scoped elsewhere cannot read it). The console Builds page is the authoritative view: ${buildsPageUrl}`, {
|
|
6465
6994
|
workspace: ref.workspace,
|
|
6466
6995
|
project: ref.project,
|
|
6467
6996
|
registryUrl: channelsUrl,
|
|
@@ -6472,6 +7001,16 @@ async function release_list_handler(args) {
|
|
|
6472
7001
|
recentBuilds.sort((a, b)=>String(b.timestamp ?? "").localeCompare(String(a.timestamp ?? "")));
|
|
6473
7002
|
const meta = metaRes.ok ? metaRes.json : void 0;
|
|
6474
7003
|
const promotable = Array.from(new Set(channels.map((c)=>c.sha).filter(Boolean)));
|
|
7004
|
+
const isPrivate = "private" === String(meta?.visibility || "").toLowerCase();
|
|
7005
|
+
const publicProjectUrl = userlandProjectUrl(ref, "", args.api);
|
|
7006
|
+
const channelsWithUrls = channels.map((c)=>({
|
|
7007
|
+
...c,
|
|
7008
|
+
publicUrl: userlandProjectUrl(ref, userland.UserlandProjectPage.channel(c.channel), args.api)
|
|
7009
|
+
}));
|
|
7010
|
+
const buildsWithUrls = recentBuilds.map((b)=>({
|
|
7011
|
+
...b,
|
|
7012
|
+
publicUrl: userlandProjectUrl(ref, userland.UserlandProjectPage.build(b.sha), args.api)
|
|
7013
|
+
}));
|
|
6475
7014
|
const result = {
|
|
6476
7015
|
ok: true,
|
|
6477
7016
|
workspace: ref.workspace,
|
|
@@ -6482,10 +7021,16 @@ async function release_list_handler(args) {
|
|
|
6482
7021
|
...meta?.visibility ? {
|
|
6483
7022
|
visibility: meta.visibility
|
|
6484
7023
|
} : {},
|
|
6485
|
-
channels,
|
|
6486
|
-
recentBuilds,
|
|
7024
|
+
channels: channelsWithUrls,
|
|
7025
|
+
recentBuilds: buildsWithUrls,
|
|
6487
7026
|
registryUrl: channelsUrl,
|
|
6488
7027
|
buildsPageUrl,
|
|
7028
|
+
...publicProjectUrl ? {
|
|
7029
|
+
publicProjectUrl
|
|
7030
|
+
} : {},
|
|
7031
|
+
...publicProjectUrl ? {
|
|
7032
|
+
publicUrlNote: isPrivate ? "publicUrl links open only for workspace members. This project is private, so an outside recipient needs a share link from extension_publish." : "publicUrl links are the public build pages: no login needed, and they carry the per-browser downloads and the run locally instructions."
|
|
7033
|
+
} : {},
|
|
6489
7034
|
message: promotable.length > 0 || recentBuilds.length > 0 ? `Promotable shas: channels currently pin ${promotable.length > 0 ? promotable.join(", ") : "none"}; recent builds add ${recentBuilds.filter((b)=>"success" === b.status).map((b)=>b.sha).join(", ") || "none"}. Use one of these as buildId/buildSha for promote/deploy/publish.` : `No channels or builds are recorded on the registry yet for ${ref.workspace}/${ref.project}. Push a commit to produce a build, then check ${buildsPageUrl}.`
|
|
6490
7035
|
};
|
|
6491
7036
|
if (!channelsRes.ok) result.channelsUnavailable = `channels.json unreadable: ${channelsRes.message}`;
|
|
@@ -6633,8 +7178,14 @@ async function deploy_handler(args) {
|
|
|
6633
7178
|
let channelRows = null;
|
|
6634
7179
|
if (ref) {
|
|
6635
7180
|
const [healthRes, channelsRes] = await Promise.all([
|
|
6636
|
-
fetchRegistryJson(registryFileUrl(ref, "stores/health.json")
|
|
6637
|
-
|
|
7181
|
+
fetchRegistryJson(registryFileUrl(ref, "stores/health.json"), fetch, {
|
|
7182
|
+
ref,
|
|
7183
|
+
api: args.api
|
|
7184
|
+
}),
|
|
7185
|
+
fetchRegistryJson(registryFileUrl(ref, "channels.json"), fetch, {
|
|
7186
|
+
ref,
|
|
7187
|
+
api: args.api
|
|
7188
|
+
})
|
|
6638
7189
|
]);
|
|
6639
7190
|
if (healthRes.ok) {
|
|
6640
7191
|
const stores = healthRes.json?.stores;
|
|
@@ -6717,6 +7268,10 @@ const store_status_schema = {
|
|
|
6717
7268
|
project: {
|
|
6718
7269
|
type: "string",
|
|
6719
7270
|
description: "Project slug override (defaults to the stored login's project)."
|
|
7271
|
+
},
|
|
7272
|
+
api: {
|
|
7273
|
+
type: "string",
|
|
7274
|
+
description: "Platform base URL (defaults to https://www.extension.dev or EXTENSION_DEV_API_URL). Used to resolve link origins and, for a private project, to mint a short-lived read token."
|
|
6720
7275
|
}
|
|
6721
7276
|
},
|
|
6722
7277
|
required: []
|
|
@@ -6816,11 +7371,20 @@ async function store_status_handler(args) {
|
|
|
6816
7371
|
const healthUrl = registryFileUrl(ref, "stores/health.json");
|
|
6817
7372
|
const statusUrl = registryFileUrl(ref, "stores/status.json");
|
|
6818
7373
|
const submissionsUrl = registryFileUrl(ref, "stores/submissions.json");
|
|
6819
|
-
const consoleStoresUrl = consoleProjectUrl(ref, "stores");
|
|
7374
|
+
const consoleStoresUrl = consoleProjectUrl(ref, "stores", args.api);
|
|
6820
7375
|
const [healthRes, statusRes, submissionsRes] = await Promise.all([
|
|
6821
|
-
fetchRegistryJson(healthUrl
|
|
6822
|
-
|
|
6823
|
-
|
|
7376
|
+
fetchRegistryJson(healthUrl, fetch, {
|
|
7377
|
+
ref,
|
|
7378
|
+
api: args.api
|
|
7379
|
+
}),
|
|
7380
|
+
fetchRegistryJson(statusUrl, fetch, {
|
|
7381
|
+
ref,
|
|
7382
|
+
api: args.api
|
|
7383
|
+
}),
|
|
7384
|
+
fetchRegistryJson(submissionsUrl, fetch, {
|
|
7385
|
+
ref,
|
|
7386
|
+
api: args.api
|
|
7387
|
+
})
|
|
6824
7388
|
]);
|
|
6825
7389
|
if (!healthRes.ok && !statusRes.ok && !submissionsRes.ok) return store_status_fail("StoreStatusNotFound", `No store data on the registry for ${ref.workspace}/${ref.project} (${healthUrl} returned ${healthRes.status ?? "no response"}). The project may have no stores configured yet, be private (private registry data needs a share token), or the workspace/project slugs may be wrong. Configure stores at ${consoleStoresUrl}/new; the console Stores page is the authoritative view: ${consoleStoresUrl}`, {
|
|
6826
7390
|
workspace: ref.workspace,
|