@webmcp-bridge/adapter-x 0.5.4 → 0.7.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/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +90 -14
- package/dist/adapter.js.map +1 -1
- package/package.json +4 -4
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,2BAA2B,CAAC;AAuHnF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,2BAA2B,CAAC;AAuHnF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAk3NF,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,WAAW,CA+jB3E"}
|
package/dist/adapter.js
CHANGED
|
@@ -537,7 +537,7 @@ const TOOL_DEFINITIONS = [
|
|
|
537
537
|
},
|
|
538
538
|
coverImagePath: {
|
|
539
539
|
type: "string",
|
|
540
|
-
description: "Optional absolute local image path for the article cover image.",
|
|
540
|
+
description: "Optional absolute local image path for the article cover image. X article covers work best when pre-cropped close to the editor's 5:2 aspect ratio.",
|
|
541
541
|
minLength: 1,
|
|
542
542
|
"x-uxc-kind": "file-path",
|
|
543
543
|
},
|
|
@@ -576,7 +576,7 @@ const TOOL_DEFINITIONS = [
|
|
|
576
576
|
},
|
|
577
577
|
coverImagePath: {
|
|
578
578
|
type: "string",
|
|
579
|
-
description: "Optional absolute local image path for the article cover image when creating a new draft.",
|
|
579
|
+
description: "Optional absolute local image path for the article cover image when creating a new draft. X article covers work best when pre-cropped close to the editor's 5:2 aspect ratio.",
|
|
580
580
|
minLength: 1,
|
|
581
581
|
"x-uxc-kind": "file-path",
|
|
582
582
|
},
|
|
@@ -605,7 +605,7 @@ const TOOL_DEFINITIONS = [
|
|
|
605
605
|
},
|
|
606
606
|
coverImagePath: {
|
|
607
607
|
type: "string",
|
|
608
|
-
description: "Optional absolute local image path for the article cover image.",
|
|
608
|
+
description: "Optional absolute local image path for the article cover image. X article covers work best when pre-cropped close to the editor's 5:2 aspect ratio.",
|
|
609
609
|
minLength: 1,
|
|
610
610
|
"x-uxc-kind": "file-path",
|
|
611
611
|
},
|
|
@@ -658,7 +658,7 @@ const TOOL_DEFINITIONS = [
|
|
|
658
658
|
},
|
|
659
659
|
coverImagePath: {
|
|
660
660
|
type: "string",
|
|
661
|
-
description: "Absolute local image path for the article cover image.",
|
|
661
|
+
description: "Absolute local image path for the article cover image. X article covers work best when pre-cropped close to the editor's 5:2 aspect ratio.",
|
|
662
662
|
minLength: 1,
|
|
663
663
|
"x-uxc-kind": "file-path",
|
|
664
664
|
},
|
|
@@ -2329,6 +2329,7 @@ async function withEphemeralPage(page, url, run) {
|
|
|
2329
2329
|
const context = page.context();
|
|
2330
2330
|
const ephemeralPage = await context.newPage();
|
|
2331
2331
|
try {
|
|
2332
|
+
await installEphemeralBridgeStubs(page, ephemeralPage);
|
|
2332
2333
|
await ensureNetworkCaptureInstalled(ephemeralPage);
|
|
2333
2334
|
await ephemeralPage.goto(url, { waitUntil: "domcontentloaded", timeout: 60_000 });
|
|
2334
2335
|
return await run(ephemeralPage);
|
|
@@ -2337,6 +2338,34 @@ async function withEphemeralPage(page, url, run) {
|
|
|
2337
2338
|
await ephemeralPage.close().catch(() => { });
|
|
2338
2339
|
}
|
|
2339
2340
|
}
|
|
2341
|
+
async function installEphemeralBridgeStubs(ownerPage, targetPage) {
|
|
2342
|
+
const names = (await ownerPage
|
|
2343
|
+
.evaluate(() => {
|
|
2344
|
+
const globalAny = window;
|
|
2345
|
+
return {
|
|
2346
|
+
callName: typeof globalAny.__WEBMCP_BRIDGE_CALL_NAME__ === "string" ? globalAny.__WEBMCP_BRIDGE_CALL_NAME__ : "",
|
|
2347
|
+
resourceUpdatedName: typeof globalAny.__WEBMCP_BRIDGE_NOTIFY_RESOURCE_UPDATED_NAME__ === "string"
|
|
2348
|
+
? globalAny.__WEBMCP_BRIDGE_NOTIFY_RESOURCE_UPDATED_NAME__
|
|
2349
|
+
: "",
|
|
2350
|
+
};
|
|
2351
|
+
})
|
|
2352
|
+
.catch(() => ({ callName: "", resourceUpdatedName: "" })));
|
|
2353
|
+
const callName = typeof names?.callName === "string" ? names.callName : "";
|
|
2354
|
+
const resourceUpdatedName = typeof names?.resourceUpdatedName === "string" ? names.resourceUpdatedName : "";
|
|
2355
|
+
if (callName) {
|
|
2356
|
+
await targetPage
|
|
2357
|
+
.exposeFunction(callName, async () => ({
|
|
2358
|
+
error: {
|
|
2359
|
+
code: "NOT_SUPPORTED",
|
|
2360
|
+
message: "bridge call is unavailable on ephemeral pages",
|
|
2361
|
+
},
|
|
2362
|
+
}))
|
|
2363
|
+
.catch(() => { });
|
|
2364
|
+
}
|
|
2365
|
+
if (resourceUpdatedName) {
|
|
2366
|
+
await targetPage.exposeFunction(resourceUpdatedName, async () => undefined).catch(() => { });
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2340
2369
|
function getReadPageCacheState(page) {
|
|
2341
2370
|
let state = READ_PAGE_CACHE.get(page);
|
|
2342
2371
|
if (!state) {
|
|
@@ -3418,12 +3447,17 @@ async function waitForArticleDraftPersisted(page, articleId, title) {
|
|
|
3418
3447
|
await page.keyboard.press("Escape").catch(() => { });
|
|
3419
3448
|
return await page
|
|
3420
3449
|
.waitForFunction(({ targetId, expectedTitle }) => {
|
|
3421
|
-
const
|
|
3422
|
-
const
|
|
3423
|
-
const
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3450
|
+
const normalize = (value) => value.replace(/\s+/g, " ").trim();
|
|
3451
|
+
const currentUrl = window.location.href;
|
|
3452
|
+
const titleValue = document.querySelector("textarea[placeholder='Add a title']")?.value ??
|
|
3453
|
+
document.querySelector("textarea")?.value ??
|
|
3454
|
+
"";
|
|
3455
|
+
const bodyText = normalize(document.body?.innerText || "");
|
|
3456
|
+
const onExpectedEditor = currentUrl.includes(`/compose/articles/edit/${targetId}`);
|
|
3457
|
+
const titleMatches = normalize(titleValue) === expectedTitle;
|
|
3458
|
+
const autosaveVisible = bodyText.includes("Last saved") || bodyText.includes("Saved") || bodyText.includes("Just now");
|
|
3459
|
+
return onExpectedEditor && titleMatches && autosaveVisible;
|
|
3460
|
+
}, { targetId: articleId, expectedTitle: title.trim() }, { timeout: 4_000 })
|
|
3427
3461
|
.then(() => true)
|
|
3428
3462
|
.catch(() => false);
|
|
3429
3463
|
}
|
|
@@ -3852,6 +3886,9 @@ function buildArticleEditUrl(articleId) {
|
|
|
3852
3886
|
function buildArticlePreviewUrl(articleId) {
|
|
3853
3887
|
return `https://x.com/i/articles/${encodeURIComponent(articleId)}/preview`;
|
|
3854
3888
|
}
|
|
3889
|
+
function buildArticlePublicUrl(articleId) {
|
|
3890
|
+
return `https://x.com/i/articles/${encodeURIComponent(articleId)}`;
|
|
3891
|
+
}
|
|
3855
3892
|
function isArticlePreviewUrl(url) {
|
|
3856
3893
|
try {
|
|
3857
3894
|
const parsed = new URL(url, "https://x.com");
|
|
@@ -4953,6 +4990,10 @@ async function readArticleByUrl(page, targetUrl, authorHandle) {
|
|
|
4953
4990
|
return parseArticleReadErrorCode(ownerResult) ? publicResult : ownerResult;
|
|
4954
4991
|
}
|
|
4955
4992
|
async function publishArticleEditor(page, timeoutMs) {
|
|
4993
|
+
const isTimeoutError = (error) => {
|
|
4994
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
4995
|
+
return /timeout/i.test(message);
|
|
4996
|
+
};
|
|
4956
4997
|
const editUrl = page.url();
|
|
4957
4998
|
await page
|
|
4958
4999
|
.evaluate(() => {
|
|
@@ -5029,6 +5070,7 @@ async function publishArticleEditor(page, timeoutMs) {
|
|
|
5029
5070
|
}
|
|
5030
5071
|
await page.waitForTimeout(1_000);
|
|
5031
5072
|
await clickPrimaryPublish().catch(() => false);
|
|
5073
|
+
let publishTimedOut = false;
|
|
5032
5074
|
try {
|
|
5033
5075
|
await page.waitForFunction(({ previousUrl }) => {
|
|
5034
5076
|
const currentUrl = window.location.href;
|
|
@@ -5041,8 +5083,17 @@ async function publishArticleEditor(page, timeoutMs) {
|
|
|
5041
5083
|
return (document.body?.innerText || "").includes("Published");
|
|
5042
5084
|
}, { previousUrl: editUrl }, { timeout: timeoutMs });
|
|
5043
5085
|
}
|
|
5044
|
-
catch {
|
|
5045
|
-
|
|
5086
|
+
catch (error) {
|
|
5087
|
+
if (!isTimeoutError(error)) {
|
|
5088
|
+
return {
|
|
5089
|
+
ok: false,
|
|
5090
|
+
reason: "publish_wait_failed",
|
|
5091
|
+
details: {
|
|
5092
|
+
message: error instanceof Error ? error.message : String(error ?? ""),
|
|
5093
|
+
},
|
|
5094
|
+
};
|
|
5095
|
+
}
|
|
5096
|
+
publishTimedOut = true;
|
|
5046
5097
|
}
|
|
5047
5098
|
const details = await page.evaluate(({ op }) => {
|
|
5048
5099
|
if (op !== "article_collect_publish_details") {
|
|
@@ -5065,11 +5116,30 @@ async function publishArticleEditor(page, timeoutMs) {
|
|
|
5065
5116
|
const articleId = parseArticleIdFromUrl(details.currentUrl) ??
|
|
5066
5117
|
(typeof details.editUrl === "string" ? parseArticleIdFromUrl(details.editUrl) : undefined) ??
|
|
5067
5118
|
undefined;
|
|
5068
|
-
|
|
5119
|
+
let articleUrl = typeof details.publicUrl === "string" && details.publicUrl.length > 0
|
|
5069
5120
|
? details.publicUrl
|
|
5070
5121
|
: !details.currentUrl.includes("/compose/articles/edit/")
|
|
5071
5122
|
? details.currentUrl
|
|
5072
5123
|
: undefined;
|
|
5124
|
+
if (!articleUrl && articleId) {
|
|
5125
|
+
const publicUrl = buildArticlePublicUrl(articleId);
|
|
5126
|
+
const readback = await readArticleByUrl(page, publicUrl);
|
|
5127
|
+
if (!parseArticleReadErrorCode(readback)) {
|
|
5128
|
+
articleUrl = publicUrl;
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
if (publishTimedOut && !articleUrl) {
|
|
5132
|
+
return {
|
|
5133
|
+
ok: false,
|
|
5134
|
+
reason: "publish_not_confirmed",
|
|
5135
|
+
details: {
|
|
5136
|
+
currentUrl: details.currentUrl,
|
|
5137
|
+
...(typeof details.editUrl === "string" ? { editUrl: details.editUrl } : {}),
|
|
5138
|
+
...(typeof details.publicUrl === "string" ? { publicUrl: details.publicUrl } : {}),
|
|
5139
|
+
...(articleId ? { articleId } : {}),
|
|
5140
|
+
},
|
|
5141
|
+
};
|
|
5142
|
+
}
|
|
5073
5143
|
const output = {
|
|
5074
5144
|
ok: true,
|
|
5075
5145
|
editUrl: typeof details.editUrl === "string" && details.editUrl.length > 0 ? details.editUrl : editUrl,
|
|
@@ -5227,6 +5297,7 @@ async function draftArticleMarkdown(page, markdownPath, explicitTitle, coverImag
|
|
|
5227
5297
|
const articlePage = await page.context().newPage();
|
|
5228
5298
|
let shouldClose = true;
|
|
5229
5299
|
try {
|
|
5300
|
+
await installEphemeralBridgeStubs(page, articlePage);
|
|
5230
5301
|
await ensureNetworkCaptureInstalled(articlePage);
|
|
5231
5302
|
await articlePage.goto("https://x.com/compose/articles", { waitUntil: "domcontentloaded", timeout: 60_000 });
|
|
5232
5303
|
const started = await openNewArticleEditor(articlePage);
|
|
@@ -6490,7 +6561,12 @@ export function createXAdapter(options) {
|
|
|
6490
6561
|
}
|
|
6491
6562
|
const explicitTitle = typeof args.title === "string" ? args.title.trim() : "";
|
|
6492
6563
|
const coverImagePath = typeof args.coverImagePath === "string" ? args.coverImagePath.trim() : "";
|
|
6493
|
-
|
|
6564
|
+
try {
|
|
6565
|
+
return await draftArticleMarkdown(page, markdownPath, explicitTitle || undefined, coverImagePath || undefined);
|
|
6566
|
+
}
|
|
6567
|
+
catch (error) {
|
|
6568
|
+
return errorResult("EXECUTION_FAILED", error instanceof Error ? error.message : String(error ?? ""));
|
|
6569
|
+
}
|
|
6494
6570
|
}
|
|
6495
6571
|
if (name === "article.upsertDraftMarkdown") {
|
|
6496
6572
|
const authCheck = await requireAuthenticated(page);
|