wield-mcp 0.1.1 → 0.1.2
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"build":"0.1.
|
|
1
|
+
{"build":"0.1.15+20261107220223.8c74"}
|
|
@@ -3061,6 +3061,14 @@
|
|
|
3061
3061
|
if (res.error.code === "login_required") return false;
|
|
3062
3062
|
return null;
|
|
3063
3063
|
}
|
|
3064
|
+
function pageOf(base) {
|
|
3065
|
+
try {
|
|
3066
|
+
const u = new URL(base);
|
|
3067
|
+
return { origin: u.origin, host: u.host };
|
|
3068
|
+
} catch {
|
|
3069
|
+
return { origin: "", host: "" };
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3064
3072
|
var Run = class {
|
|
3065
3073
|
constructor(recipe, adapters, opts) {
|
|
3066
3074
|
this.recipe = recipe;
|
|
@@ -3130,12 +3138,12 @@
|
|
|
3130
3138
|
this.staticChecks();
|
|
3131
3139
|
await this.resolveBase();
|
|
3132
3140
|
const params = prepareParams(this.recipe.params, rawParams);
|
|
3133
|
-
const ctx = { params, vars: {}, steps: {}, now: this.opts.now ? this.opts.now() : /* @__PURE__ */ new Date() };
|
|
3141
|
+
const ctx = { params, vars: {}, steps: {}, now: this.opts.now ? this.opts.now() : /* @__PURE__ */ new Date(), page: pageOf(this.base) };
|
|
3134
3142
|
await this.derive(ctx);
|
|
3135
3143
|
if (this.recipe.login_check && (this.recipe.requires_login || loginOnly)) await this.loginCheck(ctx);
|
|
3136
3144
|
await this.runSteps(ctx);
|
|
3137
3145
|
this.currentStep = void 0;
|
|
3138
|
-
let output = this.q({ params: ctx.params, steps: ctx.steps, now: localIsoNow(ctx.now) }, this.recipe.output.value, "output");
|
|
3146
|
+
let output = this.q({ params: ctx.params, steps: ctx.steps, now: localIsoNow(ctx.now), page: ctx.page }, this.recipe.output.value, "output");
|
|
3139
3147
|
output = redactDeep(normalizeValue(output), this.secrets);
|
|
3140
3148
|
const duration_ms = Date.now() - this.t0;
|
|
3141
3149
|
this.emit("done", "info", `ok in ${duration_ms} ms (${this.requests} requests)`);
|
|
@@ -3413,7 +3421,7 @@
|
|
|
3413
3421
|
let summary = this.recipe.task;
|
|
3414
3422
|
if (this.recipe.confirm?.summary) {
|
|
3415
3423
|
try {
|
|
3416
|
-
summary = toText(renderString(this.recipe.confirm.summary, { params: ctx.params, steps: ctx.steps, now: ctx.now }));
|
|
3424
|
+
summary = toText(renderString(this.recipe.confirm.summary, { params: ctx.params, steps: ctx.steps, now: ctx.now, page: ctx.page }));
|
|
3417
3425
|
} catch {
|
|
3418
3426
|
summary = this.recipe.confirm.summary;
|
|
3419
3427
|
}
|
|
@@ -3754,7 +3762,7 @@
|
|
|
3754
3762
|
var enc = new TextEncoder();
|
|
3755
3763
|
|
|
3756
3764
|
// apps/extension/src/shared/types.ts
|
|
3757
|
-
var BUILD_ID = "0.1.
|
|
3765
|
+
var BUILD_ID = "0.1.15+20261107220223.8c74";
|
|
3758
3766
|
var RELOAD_HINT = "Reload the Wield extension once: open chrome://extensions (vivaldi://extensions in Vivaldi) and click the reload arrow on the Wield card, then try again.";
|
|
3759
3767
|
var LIMITS2 = {
|
|
3760
3768
|
steps: 12,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Wield",
|
|
4
4
|
"short_name": "Wield",
|
|
5
5
|
"description": "Lets your AI agent use websites through your own browser and logins. Your passwords never leave your device.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.15",
|
|
7
7
|
"minimum_chrome_version": "137",
|
|
8
8
|
"permissions": [
|
|
9
9
|
"nativeMessaging",
|
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
"storage"
|
|
13
13
|
],
|
|
14
14
|
"host_permissions": [
|
|
15
|
+
"https://en.zalando.de/*",
|
|
15
16
|
"https://energie.check24.de/*",
|
|
16
17
|
"https://fahrplan.oebb.at/*",
|
|
18
|
+
"https://regiojet.cz/*",
|
|
17
19
|
"https://shop.flixbus.de/*",
|
|
18
20
|
"https://suchen.mobile.de/*",
|
|
19
21
|
"https://www.11880.com/*",
|
|
20
22
|
"https://www.2dehands.be/*",
|
|
21
23
|
"https://www.airbnb.com/*",
|
|
24
|
+
"https://www.allrecipes.com/*",
|
|
22
25
|
"https://www.alternate.de/*",
|
|
23
26
|
"https://www.amazon.ca/*",
|
|
24
27
|
"https://www.amazon.co.uk/*",
|
|
@@ -56,6 +59,7 @@
|
|
|
56
59
|
"https://www.eventim.de/*",
|
|
57
60
|
"https://www.finn.no/*",
|
|
58
61
|
"https://www.gelbeseiten.de/*",
|
|
62
|
+
"https://www.giallozafferano.it/*",
|
|
59
63
|
"https://www.gumtree.com/*",
|
|
60
64
|
"https://www.hugendubel.de/*",
|
|
61
65
|
"https://www.idealo.de/*",
|
|
@@ -70,10 +74,13 @@
|
|
|
70
74
|
"https://www.kijiji.ca/*",
|
|
71
75
|
"https://www.kleinanzeigen.de/*",
|
|
72
76
|
"https://www.lefrecce.it/*",
|
|
77
|
+
"https://www.lidl.de/*",
|
|
73
78
|
"https://www.lieferando.de/*",
|
|
79
|
+
"https://www.local.ch/*",
|
|
74
80
|
"https://www.marktplaats.nl/*",
|
|
75
81
|
"https://www.marmiton.org/*",
|
|
76
82
|
"https://www.mvg.de/*",
|
|
83
|
+
"https://www.mydealz.de/*",
|
|
77
84
|
"https://www.ns.nl/*",
|
|
78
85
|
"https://www.olx.pl/*",
|
|
79
86
|
"https://www.onthemarket.com/*",
|
|
@@ -84,6 +91,7 @@
|
|
|
84
91
|
"https://www.reed.co.uk/*",
|
|
85
92
|
"https://www.rightmove.co.uk/*",
|
|
86
93
|
"https://www.ryanair.com/*",
|
|
94
|
+
"https://www.spareroom.co.uk/*",
|
|
87
95
|
"https://www.stepstone.de/*",
|
|
88
96
|
"https://www.thomann.de/*",
|
|
89
97
|
"https://www.thuisbezorgd.nl/*",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "https://wield.run/schema/recipe/v1.json",
|
|
3
3
|
"id": "chefkoch.de/search-recipes",
|
|
4
|
-
"version":
|
|
4
|
+
"version": 2,
|
|
5
5
|
"site": {
|
|
6
6
|
"id": "chefkoch.de",
|
|
7
7
|
"name": "Chefkoch",
|
|
@@ -209,10 +209,10 @@
|
|
|
209
209
|
"method": "browser-inspect",
|
|
210
210
|
"recorded_at": "2026-11-07T12:27:00+01:00",
|
|
211
211
|
"recorded_by": "C4",
|
|
212
|
-
"notes": "spikes/chefkoch.md. Public JSON search gateway used by chefkoch.de; CORS allows https://www.chefkoch.de (with credentials) and no cookies or tokens are needed, so credentials omit. Non-null campaign = brand-sponsored recipe; isPlus = paywalled. difficulty 1/2/3 = simpel/normal/pfiffig."
|
|
212
|
+
"notes": "spikes/chefkoch.md. Public JSON search gateway used by chefkoch.de; CORS allows https://www.chefkoch.de (with credentials) and no cookies or tokens are needed, so credentials omit. Non-null campaign = brand-sponsored recipe; isPlus = paywalled. difficulty 1/2/3 = simpel/normal/pfiffig. v2 (2026-11-07 22:38): tos.notes only; requests and output unchanged."
|
|
213
213
|
},
|
|
214
214
|
"tos": {
|
|
215
215
|
"reviewed": false,
|
|
216
|
-
"notes": "Chefkoch's terms restrict automated use of its content. One user-initiated search at a time from the user's browser (1 request); recipe texts stay on chefkoch.de via the link."
|
|
216
|
+
"notes": "Chefkoch's robots.txt disallows AI training crawlers (ClaudeBot, anthropic-ai, Claude-Web). Chefkoch's terms restrict automated use of its content. One user-initiated search at a time from the user's browser (1 request); recipe texts stay on chefkoch.de via the link."
|
|
217
217
|
}
|
|
218
218
|
}
|
package/dist/extension/sw.js
CHANGED
|
@@ -114,12 +114,14 @@ var REGISTRY_KEYS = {
|
|
|
114
114
|
// packages/recipe/src/features.ts
|
|
115
115
|
var INTERPRETER_FEATURES = [
|
|
116
116
|
/** extract.html / html_items fields with parse: "json" (and path): per-element JSON attributes (CHANGES 2026-11-07 C1). */
|
|
117
|
-
"html_json"
|
|
117
|
+
"html_json",
|
|
118
|
+
/** page.host / page.origin in templates and context expressions: the language host the run landed on (CHANGES 22:39 C4). */
|
|
119
|
+
"page_context"
|
|
118
120
|
];
|
|
119
121
|
|
|
120
122
|
// apps/extension/src/shared/types.ts
|
|
121
123
|
var PROTOCOL_VERSION = 1;
|
|
122
|
-
var BUILD_ID = "0.1.
|
|
124
|
+
var BUILD_ID = "0.1.15+20261107220223.8c74";
|
|
123
125
|
var RELOAD_HINT = "Reload the Wield extension once: open chrome://extensions (vivaldi://extensions in Vivaldi) and click the reload arrow on the Wield card, then try again.";
|
|
124
126
|
var LIMITS2 = {
|
|
125
127
|
steps: 12,
|
|
@@ -794,9 +796,14 @@ chrome.tabs.onUpdated.addListener((tabId, info) => {
|
|
|
794
796
|
function originPattern(origin) {
|
|
795
797
|
return `${origin}/*`;
|
|
796
798
|
}
|
|
797
|
-
|
|
798
|
-
const
|
|
799
|
-
return
|
|
799
|
+
function siteOrigins(origin, localeHosts = []) {
|
|
800
|
+
const port = new URL(origin).port;
|
|
801
|
+
return [origin, ...localeHosts.map((h) => `https://${h}${port ? `:${port}` : ""}`)];
|
|
802
|
+
}
|
|
803
|
+
async function tabsForOrigin(origin, localeHosts = []) {
|
|
804
|
+
const origins = siteOrigins(origin, localeHosts);
|
|
805
|
+
const tabs = await chrome.tabs.query({ url: origins.map(originPattern) });
|
|
806
|
+
return tabs.filter((t) => t.id !== void 0 && t.url && origins.includes(new URL(t.url).origin));
|
|
800
807
|
}
|
|
801
808
|
function waitComplete(tabId, timeoutMs) {
|
|
802
809
|
return new Promise((resolve, reject) => {
|
|
@@ -839,11 +846,12 @@ async function acquireTab(origin, signal, startPath, opts = {}) {
|
|
|
839
846
|
const url = startUrl(origin, startPath);
|
|
840
847
|
const owned = await ownedTabs();
|
|
841
848
|
const stale = await staleTabs();
|
|
842
|
-
const
|
|
849
|
+
const accepted = siteOrigins(origin, opts.localeHosts);
|
|
850
|
+
const candidates = (await tabsForOrigin(origin, opts.localeHosts)).filter((t) => !t.discarded && !stale.has(t.id)).sort((a, b) => score(b, owned) - score(a, owned));
|
|
843
851
|
const pick = opts.forceNew ? void 0 : candidates[0];
|
|
844
852
|
if (pick?.id !== void 0) {
|
|
845
853
|
if (pick.status !== "complete") await waitComplete(pick.id, LIMITS2.tabLoadMs);
|
|
846
|
-
return { tabId: pick.id, opened: false, owned: owned.has(pick.id) };
|
|
854
|
+
return { tabId: pick.id, opened: false, owned: owned.has(pick.id), ...pick.url ? { host: new URL(pick.url).host } : {} };
|
|
847
855
|
}
|
|
848
856
|
let tab;
|
|
849
857
|
try {
|
|
@@ -859,11 +867,11 @@ async function acquireTab(origin, signal, startPath, opts = {}) {
|
|
|
859
867
|
});
|
|
860
868
|
const done = await waitComplete(tabId, LIMITS2.tabLoadMs);
|
|
861
869
|
const finalOrigin = done.url ? new URL(done.url).origin : "";
|
|
862
|
-
if (finalOrigin
|
|
870
|
+
if (!accepted.includes(finalOrigin)) {
|
|
863
871
|
throw new ExecError("tab_error", `${origin} redirected the tab to ${finalOrigin || "a site Wield has no access to"}; the recipe's site.origin needs updating`);
|
|
864
872
|
}
|
|
865
873
|
if (!signal.aborted) await sleep2(LIMITS2.tabSettleMs);
|
|
866
|
-
return { tabId, opened: true, owned: true };
|
|
874
|
+
return { tabId, opened: true, owned: true, ...done.url ? { host: new URL(done.url).host } : {} };
|
|
867
875
|
}
|
|
868
876
|
function score(t, owned) {
|
|
869
877
|
let s = 0;
|
|
@@ -923,6 +931,12 @@ function checkRecipe(r) {
|
|
|
923
931
|
const site = recipe.site;
|
|
924
932
|
if (!site || !isAllowedOrigin(site.origin)) bad("site.origin must be https://host");
|
|
925
933
|
if (!Array.isArray(site.origins) || site.origins.length === 0 || !site.origins.every(isAllowedOrigin)) bad("site.origins must list https origins");
|
|
934
|
+
if (site.locale_hosts !== void 0) {
|
|
935
|
+
if (!Array.isArray(site.locale_hosts) || site.locale_hosts.length > 20) bad("site.locale_hosts must be a list of up to 20 host names");
|
|
936
|
+
for (const h of site.locale_hosts) {
|
|
937
|
+
if (typeof h !== "string" || !/^[a-z0-9-]+(\.[a-z0-9-]+)+$/.test(h) || !(h === site.id || h.endsWith(`.${site.id}`))) bad(`site.locale_hosts: "${String(h)}" must be an exact host name under ${site.id}`);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
926
940
|
if (!Array.isArray(recipe.steps) || recipe.steps.length < 1) bad("recipe has no steps");
|
|
927
941
|
if (recipe.steps.length > LIMITS2.steps) throw new ExecError("limit_exceeded", `recipe has ${recipe.steps.length} steps; the limit is ${LIMITS2.steps}`);
|
|
928
942
|
for (const s of [...recipe.steps, ...recipe.login_check ? [recipe.login_check] : []]) {
|
|
@@ -1078,13 +1092,13 @@ function execute(opts) {
|
|
|
1078
1092
|
if (tabId === void 0) {
|
|
1079
1093
|
await ensureHostAccess(recipe, opts.run_id, budget, abort.signal, opts.emit);
|
|
1080
1094
|
const t0 = Date.now();
|
|
1081
|
-
const tab = await acquireTab(recipe.site.origin, abort.signal, recipe.site.start_path, { forceNew: opts.forceNewTab });
|
|
1095
|
+
const tab = await acquireTab(recipe.site.origin, abort.signal, recipe.site.start_path, { forceNew: opts.forceNewTab, localeHosts: recipe.site.locale_hosts ?? [] });
|
|
1082
1096
|
tabId = tab.tabId;
|
|
1083
1097
|
if (opts.meta) {
|
|
1084
1098
|
opts.meta.owned = tab.owned;
|
|
1085
1099
|
if (tab.opened) opts.meta.openedTabId = tab.tabId;
|
|
1086
1100
|
}
|
|
1087
|
-
const host = new URL(recipe.site.origin).host;
|
|
1101
|
+
const host = tab.host ?? new URL(recipe.site.origin).host;
|
|
1088
1102
|
opts.emit({ level: "info", kind: "tab", msg: tab.opened ? `opened background tab ${host} (${Date.now() - t0} ms)` : `using open tab ${host}` });
|
|
1089
1103
|
}
|
|
1090
1104
|
if (opts.meta) opts.meta.tabId = tabId;
|
package/dist/index.js
CHANGED
|
@@ -27797,7 +27797,7 @@ function makeCatalog(cfg, http) {
|
|
|
27797
27797
|
}
|
|
27798
27798
|
|
|
27799
27799
|
// src/server.ts
|
|
27800
|
-
var VERSION = "0.1.
|
|
27800
|
+
var VERSION = "0.1.2";
|
|
27801
27801
|
var OFFLINE_HINT = "Open Chrome with the Wield extension installed";
|
|
27802
27802
|
function offlineHint(home) {
|
|
27803
27803
|
if (!existsSync7(paths(home).bridgeLauncher)) {
|
|
@@ -28116,11 +28116,17 @@ function createWieldServer(d) {
|
|
|
28116
28116
|
const missing = await missingFeatures(recipe);
|
|
28117
28117
|
if (missing.length) {
|
|
28118
28118
|
let older;
|
|
28119
|
-
if (version2 === void 0 && !fallback
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28119
|
+
if (version2 === void 0 && !fallback) {
|
|
28120
|
+
const candidates = [.../* @__PURE__ */ new Set([lastGood, recipe.version - 1, recipe.version - 2])].filter((v) => typeof v === "number" && v >= 1 && v < recipe.version);
|
|
28121
|
+
for (const v of candidates) {
|
|
28122
|
+
try {
|
|
28123
|
+
const o = await d.source.recipe(recipe_id, v);
|
|
28124
|
+
if (!(await missingFeatures(o.recipe)).length) {
|
|
28125
|
+
older = o;
|
|
28126
|
+
break;
|
|
28127
|
+
}
|
|
28128
|
+
} catch {
|
|
28129
|
+
}
|
|
28124
28130
|
}
|
|
28125
28131
|
}
|
|
28126
28132
|
if (!older) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wield-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"mcpName": "run.wield/wield",
|
|
5
5
|
"description": "Websites as tools for your AI agent (MCP). Runs signed recipes in your own browser with your own session: your logins never leave your computer.",
|
|
6
6
|
"keywords": [
|