PageSourceSearch

https://xagent.id/assets/webmcp-onboarding.js?v=webmcp-challenge-20260902-4

js xagent.id collected 2026-10-05 06:32:40 UTC 52,165 bytes, 1,213 lines download raw bytes

1(function (root, factory) {
2  "use strict";
3  var api = factory(root);
4  if (typeof module === "object" && module.exports) module.exports = api;
5  if (root) root.XAgentWebMCP = api;
6  if (root && root.document) {
7    Promise.resolve(api.initialize(root.document)).catch(function () {});
8  }
9})(typeof window !== "undefined" ? window : (typeof globalThis !== "undefined" ? globalThis : this), function (root) {
10  "use strict";
11
12  var TOOL_NAMES = [
13    "inspect_onboarding",
14    "preview_agent_id",
15    "get_install_plan",
16    "choose_verification_method",
17    "request_owner_approval",
18    "prepare_install_bundle",
19    "verify_installation",
20    "get_onboarding_status"
21  ];
22  var READ_ONLY_TOOLS = {
23    inspect_onboarding: true,
24    get_onboarding_status: true
25  };
26  var UNTRUSTED_OUTPUT_TOOLS = {
27    preview_agent_id: true,
28    get_install_plan: true,
29    request_owner_approval: true,
30    prepare_install_bundle: true,
31    verify_installation: true,
32    get_onboarding_status: true
33  };
34  var SAFE_WORKFLOW_IDS = ["dry_run", "apply", "domain_verify", "activate"];
35  var VERIFIED_STATUSES = ["verified_domain", "sdk_active", "connector_active"];
36  var CREDENTIAL_PATTERN = /(private[_-]?key|agent[_-]?key|site[_-]?token|api[_-]?key|authorization\s*:|bearer\s+)/i;
37
38  function clone(value) {
39    return value == null ? value : JSON.parse(JSON.stringify(value));
40  }
41
42  function stableStringify(value) {
43    if (value === null || typeof value === "string" || typeof value === "boolean") {
44      return JSON.stringify(value);
45    }
46    if (typeof value === "number") {
47      if (!Number.isFinite(value)) throw new TypeError("non_finite_number");
48      return JSON.stringify(value);
49    }
50    if (Array.isArray(value)) {
51      return "[" + value.map(stableStringify).join(",") + "]";
52    }
53    if (typeof value === "object") {
54      return "{" + Object.keys(value).sort().map(function (key) {
55        if (typeof value[key] === "undefined") throw new TypeError("undefined_manifest_value");
56        return JSON.stringify(key) + ":" + stableStringify(value[key]);
57      }).join(",") + "}";
58    }
59    throw new TypeError("unsupported_manifest_value");
60  }
61
62  function normalizeTargetUrl(raw) {
63    if (typeof raw !== "string" || !raw.trim() || raw.length > 2048) return "";
64    try {
65      var candidate = /^https:\/\//i.test(raw.trim()) ? raw.trim() : "https://" + raw.trim();
66      var parsed = new URL(candidate);
67      var host = parsed.hostname.toLowerCase().replace(/\.$/, "");
68      if (parsed.protocol !== "https:" || !host || parsed.username || parsed.password) return "";
69      return "https://" + host + (parsed.port ? ":" + parsed.port : "");
70    } catch (_error) {
71      return "";
72    }
73  }
74
75  function manifestValidationError(target, manifest) {
76    if (!target || !manifest || typeof manifest !== "object") {
77      return { code: "manifest_invalid", message: "The production install manifest could not be validated." };
78    }
79    if (manifest.schema !== "xagent-install-manifest-v1") {
80      return { code: "manifest_invalid", message: "The production install manifest schema is invalid." };
81    }
82    if (!manifest.release || manifest.release.ready !== true) {
83      return { code: "release_not_ready", message: "The target-bound production release is not ready." };
84    }
85    var expectedHost = new URL(target).hostname.toLowerCase().replace(/\.$/, "");
86    var manifestHost = String(manifest.target && manifest.target.host || "").toLowerCase().replace(/\.$/, "");
87    if (!manifestHost || manifestHost !== expectedHost) {
88      return { code: "target_mismatch", message: "The manifest is not bound to the selected website." };
89    }
90    if (!manifest.target.url || normalizeTargetUrl(manifest.target.url) !== target) {
91      return { code: "target_mismatch", message: "The manifest target origin does not match the selected website." };
92    }
93    return null;
94  }
95
96  async function canonicalManifestDigest(manifest, cryptoImpl) {
97    var snapshot = clone(manifest);
98    var cryptoObject = cryptoImpl || (root && root.crypto);
99    var Encoder = root && root.TextEncoder
100      ? root.TextEncoder
101      : (typeof TextEncoder !== "undefined" ? TextEncoder : null);
102    if (!cryptoObject || !cryptoObject.subtle || typeof cryptoObject.subtle.digest !== "function" || !Encoder) {
103      throw new Error("manifest_digest_unavailable");
104    }
105    var canonical = stableStringify(snapshot);
106    var digest = await cryptoObject.subtle.digest("SHA-256", new Encoder().encode(canonical));
107    var hex = Array.prototype.map.call(new Uint8Array(digest), function (byte) {
108      return byte.toString(16).padStart(2, "0");
109    }).join("");
110    return { manifest: snapshot, digest: "sha256:" + hex };
111  }
112
113  function safeModules(modules) {
114    return (Array.isArray(modules) ? modules : []).slice(0, 32).map(function (item) {
115      return {
116        id: String(item && item.id || "").slice(0, 80),
117        grant: String(item && item.grant || "").slice(0, 120),
118        default: item && item.default === true,
119        writes: (Array.isArray(item && item.writes) ? item.writes : []).slice(0, 32).map(function (path) {
120          return String(path).slice(0, 240);
121        })
122      };
123    });
124  }
125
126  function safePlan(manifest, digest) {
127    var modules = safeModules(manifest.modules);
128    var writes = [];
129    modules.forEach(function (item) {
130      if (!item.default) return;
131      item.writes.forEach(function (path) {
132        if (writes.indexOf(path) < 0) writes.push(path);
133      });
134    });
135    return {
136      schema: manifest.schema,
137      manifest_digest: digest,
138      target_host: manifest.target.host,
139      release: {
140        package: String(manifest.release.package || "").slice(0, 160),
141        version: String(manifest.release.version || "").slice(0, 80),
142        integrity: String(manifest.release.integrity || "").slice(0, 240),
143        ready: manifest.release.ready === true,
144        rollback_available: Boolean(manifest.release.rollback)
145      },
146      authority: {
147        default: manifest.authority && manifest.authority.default || "read_only",
148        mother_service_can_write_site: false,
149        customer_agent_executes: true
150      },
151      modules: modules,
152      declared_write_paths: writes,
153      owner_approval_required: writes.length > 0 || (
154        Array.isArray(manifest.workflow) && manifest.workflow.some(function (step) {
155          return step && step.owner_approval_required === true;
156        })
157      )
158    };
159  }
160
161  function safeVerification(value) {
162    var steps = {};
163    var source = value && value.steps && typeof value.steps === "object" ? value.steps : {};
164    var publicSource = value && value.public_state && typeof value.public_state === "object"
165      ? value.public_state
166      : null;
167    var publicState = publicSource ? {
168      schema: String(publicSource.schema || "").slice(0, 80),
169      state: String(publicSource.state || "unknown").slice(0, 80),
170      identity_status: String(publicSource.identity && publicSource.identity.status || "unknown").slice(0, 80),
171      identity_verified: publicSource.identity && publicSource.identity.verified === true,
172      installation_status: String(publicSource.installation && publicSource.installation.status || "unknown").slice(0, 80),
173      installed: publicSource.installation && typeof publicSource.installation.installed === "boolean"
174        ? publicSource.installation.installed
175        : null,
176      missing_data_policy: String(publicSource.missing_data_policy || "pending_not_zero").slice(0, 80)
177    } : null;
178    Object.keys(source).slice(0, 32).forEach(function (key) {
179      steps[String(key).slice(0, 80)] = source[key] === true;
180    });
181    return {
182      ok: value && value.ok === true,
183      status: String(value && value.status || "pending").slice(0, 80),
184      public_state: publicState,
185      steps: steps
186    };
187  }
188
189  function nextAction(currentState, busyOperation) {
190    if (busyOperation === "manifest_digest") return "Wait for the newest target-bound plan to finish validation.";
191    if (busyOperation === "verification") return "Wait for the independent public verification request to finish.";
192    var actions = {
193      idle: "Select the HTTPS website to onboard.",
194      target_selected: "Load the target-bound install plan or inspect existing public installation evidence.",
195      plan_ready: "Choose the public-file verification method.",
196      verification_method_selected: "Request owner approval in the visible permissions UI.",
197      approval_pending: "The website owner must review and use Continue to install.",
198      approved: "Prepare the install bundle for the customer-side Agent.",
199      bundle_ready: "The customer-side Agent may dry-run and apply the approved bundle.",
200      verifying: "Wait for the independent public refetch.",
201      verified: "Open the verified xAgent ID dashboard.",
202      blocked: "Resolve the reported validation or safety error, then reload the plan."
203    };
204    return actions[currentState] || actions.idle;
205  }
206
207  function createControllerInternal(options) {
208    options = options || {};
209    var doc = options.document || null;
210    var fetcher = options.fetch || (root && root.fetch ? root.fetch.bind(root) : null);
211    var eventTarget = options.eventTarget || root;
212    var cryptoImpl = options.crypto || (root && root.crypto);
213    var digestManifest = typeof options.digestManifest === "function"
214      ? options.digestManifest
215      : function (manifest) { return canonicalManifestDigest(manifest, cryptoImpl); };
216    var locationOrigin = options.origin || (root && root.location ? root.location.origin : "https://xagent.id");
217    var now = typeof options.now === "function" ? options.now : Date.now;
218    var nonceFactory = typeof options.nonceFactory === "function" ? options.nonceFactory : null;
219    var userActivation = options.userActivation || (root && root.navigator ? root.navigator.userActivation : null);
220    // How many times to look for the visible permissions-to-install transition
221    // after a trusted approval click before giving up on it.
222    var APPROVAL_TRANSITION_ATTEMPTS = 12;
223    var scheduleTransitionCheck = options.scheduleTransitionCheck || (
224      root && typeof root.setTimeout === "function"
225        ? function (callback) { root.setTimeout(callback, 16); }
226        : function (callback) { Promise.resolve().then(callback); }
227    );
228    var queueTask = options.queueMicrotask || (
229      root && typeof root.queueMicrotask === "function"
230        ? root.queueMicrotask.bind(root)
231        : function (callback) { Promise.resolve().then(callback); }
232    );
233    var disposed = false;
234    var planRequestSequence = 0;
235    var manifestEventSequence = 0;
236    var verificationRequestSequence = 0;
237    var listeners = [];
238    var state = {
239      state: "idle",
240      targetUrl: null,
241      manifest: null,
242      manifestDigest: null,
243      verificationMethod: null,
244      approval: null,
245      preparedBundle: null,
246      verification: null,
247      error: null,
248      busyOperation: null
249    };
250
251    function t(source) {
252      return root && root.XAgentI18n && typeof root.XAgentI18n.t === "function"
253        ? root.XAgentI18n.t(source)
254        : source;
255    }
256
257    function query(selector) {
258      return doc && typeof doc.querySelector === "function" ? doc.querySelector(selector) : null;
259    }
260
261    function queryAll(selector) {
262      if (!doc || typeof doc.querySelectorAll !== "function") return [];
263      return Array.prototype.slice.call(doc.querySelectorAll(selector) || []);
264    }
265
266    function hasCurrentApproval() {
267      return Boolean(
268        state.approval
269        && state.approval.target_url === state.targetUrl
270        && state.approval.manifest_digest === state.manifestDigest
271      );
272    }
273
274    function hasCurrentBundle() {
275      return Boolean(
276        state.preparedBundle
277        && state.preparedBundle.target_url === state.targetUrl
278        && state.preparedBundle.manifest_digest === state.manifestDigest
279        && hasCurrentApproval()
280      );
281    }
282
283    function stateSnapshot() {
284      return {
285        state: state.state,
286        target_url: state.targetUrl,
287        manifest_digest: state.manifestDigest,
288        verification_method: state.verificationMethod,
289        human_approval_present: hasCurrentApproval(),
290        approval_consumed: Boolean(state.approval && state.approval.consumed_at),
291        bundle_prepared: hasCurrentBundle(),
292        busy_operation: state.busyOperation,
293        verification: clone(state.verification),
294        error: clone(state.error),
295        next_action: nextAction(state.state, state.busyOperation)
296      };
297    }
298
299    function result(tool, ok, data, error) {
300      return {
301        ok: ok,
302        tool: tool,
303        state: state.state,
304        target_url: state.targetUrl,
305        data: data || {},
306        error: error || null,
307        next_action: nextAction(state.state, state.busyOperation)
308      };
309    }
310
311    function reject(tool, code, message) {
312      state.error = { code: code, message: message };
313      return result(tool, false, {}, clone(state.error));
314    }
315
316    function setText(selector, value) {
317      var node = query(selector);
318      if (node) node.textContent = value;
319    }
320
321    function renderCommands(manifest) {
322      var workflow = Array.isArray(manifest && manifest.workflow) ? manifest.workflow : [];
323      var steps = {};
324      workflow.forEach(function (step) {
325        if (step && SAFE_WORKFLOW_IDS.concat(["dashboard_login"]).indexOf(step.id) >= 0) {
326          steps[step.id] = step;
327        }
328      });
329      queryAll("[data-workflow-id]").forEach(function (node) {
330        var id = node.dataset ? node.dataset.workflowId : "";
331        var step = steps[id];
332        node.textContent = step && typeof step.command === "string" && step.command
333          ? step.command
334          : t("Command unavailable");
335      });
336    }
337
338    function approvalNotice() {
339      var panel = query('[data-wizard-panel="permissions"]');
340      if (!panel) return null;
341      var notice = query("[data-webmcp-approval-notice]");
342      if (!notice && doc && typeof doc.createElement === "function") {
343        notice = doc.createElement("p");
344        notice.id = "webmcp-approval-status";
345        notice.className = "manifest-status webmcp-approval-notice";
346        notice.setAttribute("data-webmcp-approval-notice", "");
347        notice.setAttribute("role", "status");
348        notice.setAttribute("aria-live", "polite");
349        var copy = typeof panel.querySelector === "function" ? panel.querySelector(".wizard-copy") : null;
350        var parent = copy || panel;
351        if (parent && typeof parent.appendChild === "function") parent.appendChild(notice);
352      }
353      return notice;
354    }
355
356    function renderApprovalNotice(mode) {
357      var notice = approvalNotice();
358      var button = query('[data-wizard-next="install"]');
359      if (!notice) return;
360      if (!mode) {
361        notice.hidden = true;
362        notice.textContent = "";
363        if (button && typeof button.getAttribute === "function" && button.getAttribute("aria-describedby") === notice.id) {
364          button.removeAttribute("aria-describedby");
365        }
366        return;
367      }
368      notice.hidden = false;
369      if (button && typeof button.setAttribute === "function") button.setAttribute("aria-describedby", notice.id);
370      if (mode === "pending") {
371        notice.textContent = t("An AI Agent is waiting for your review. Continue to install releases only the bounded command bundle; xagent.id does not execute it.");
372      } else if (mode === "approved") {
373        notice.textContent = t("Browser approval recorded for this exact plan. The Agent may now request the bounded command bundle.");
374      } else if (mode === "bundle") {
375        notice.textContent = t("The approved command bundle is ready for the customer-side Agent. No command has been executed by xagent.id.");
376      }
377    }
378
379    function renderManifestLinks(target) {
380      var manifestUrl = "/.well-known/xagent-install.json?url=" + encodeURIComponent(target);
381      queryAll("[data-manifest-link]").forEach(function (link) {
382        link.href = manifestUrl;
383      });
384    }
385
386    function renderPlan(manifest, digest, source) {
387      if (!manifest) return;
388      renderCommands(manifest);
389      renderManifestLinks(state.targetUrl);
390      setText("#release-version", String(manifest.release.version || t("Ready")));
391      setText(
392        "#manifest-integrity",
393        manifest.release.integrity
394          ? t("Published npm integrity") + " · " + String(manifest.release.integrity).slice(0, 24) + "…"
395          : t("Published integrity unavailable")
396      );
397      setText("#hero-release", t("Release") + " " + String(manifest.release.version || ""));
398      setText("#hero-host", String(manifest.target.host || ""));
399      setText("#hero-package", String(manifest.release.package || "") + "@" + String(manifest.release.version || ""));
400      var plan = safePlan(manifest, digest);
401      setText(
402        "#hero-writes",
403        plan.declared_write_paths.length
404          ? plan.declared_write_paths.length + " " + t("declared paths, approval required")
405          : t("None")
406      );
407      setText("#hero-state", t("Ready"));
408      var heroState = query("#hero-state");
409      if (heroState && heroState.classList && typeof heroState.classList.add === "function") {
410        heroState.classList.add("is-ready");
411      }
412      setText(
413        "#install-status",
414        (source === "agent" ? t("AI Agent loaded a target-bound plan for") : t("Plan loaded for"))
415          + " " + String(manifest.target.host || "") + ". "
416          + t("Review the exact permissions before installation.")
417      );
418    }
419
420    function renderFailure(message) {
421      renderCommands(null);
422      renderApprovalNotice(null);
423      setText("#release-version", t("Blocked"));
424      setText("#manifest-integrity", t("Release metadata unavailable"));
425      setText("#install-status", t(message || "Production manifest unavailable or target mismatch. Stop before installation."));
426      setText("#hero-state", t("Unavailable"));
427    }
428
429    function renderStateMessage() {
430      if (!state.targetUrl) return;
431      var host = new URL(state.targetUrl).hostname;
432      if (state.busyOperation === "manifest_digest") {
433        setText("#install-status", t("Validating the newest complete install plan for") + " " + host + "…");
434        return;
435      }
436      if (state.busyOperation === "verification") {
437        setText("#install-status", t("Checking public installation evidence for") + " " + host + "…");
438        return;
439      }
440      if (state.state === "approval_pending") {
441        setText("#install-status", t("An AI Agent loaded the plan and is waiting for owner review in Permissions."));
442        renderApprovalNotice("pending");
443      } else if (state.state === "approved") {
444        setText("#install-status", t("Browser approval is bound to the current plan. The Agent may request the bundle."));
445        renderApprovalNotice("approved");
446      } else if (state.state === "bundle_ready") {
447        setText("#install-status", t("The approved bundle is ready. xagent.id has not executed any command."));
448        renderApprovalNotice("bundle");
449      } else if (state.manifest) {
450        renderApprovalNotice(null);
451      }
452    }
453
454    function clearPlanState() {
455      state.manifest = null;
456      state.manifestDigest = null;
457      state.verificationMethod = null;
458      state.approval = null;
459      state.preparedBundle = null;
460      state.verification = null;
461      state.busyOperation = null;
462      renderApprovalNotice(null);
463    }
464
465    function invalidatePlan(code, message, targetState) {
466      clearPlanState();
467      state.state = targetState || "blocked";
468      state.error = code ? { code: code, message: message } : null;
469      renderFailure(message);
470    }
471
472    function cancelPendingRequests() {
473      planRequestSequence += 1;
474      manifestEventSequence += 1;
475      verificationRequestSequence += 1;
476      state.busyOperation = null;
477    }
478
479    function selectTarget(target, forceReset) {
480      cancelPendingRequests();
481      if (forceReset || state.targetUrl !== target) clearPlanState();
482      state.targetUrl = target;
483      state.state = "target_selected";
484      state.error = null;
485    }
486
487    function restoreStateAfterVerification(previousState) {
488      if (previousState === "approval_pending" && state.manifest && state.manifestDigest && state.verificationMethod && !hasCurrentApproval()) {
489        return "approval_pending";
490      }
491      if (previousState === "bundle_ready" && hasCurrentBundle()) return "bundle_ready";
492      if (previousState === "approved" && hasCurrentApproval()) return "approved";
493      if (previousState === "verification_method_selected" && state.manifest && state.manifestDigest && state.verificationMethod) {
494        return "verification_method_selected";
495      }
496      if (previousState === "plan_ready" && state.manifest && state.manifestDigest) return "plan_ready";
497      if (state.manifest && state.manifestDigest) {
498        if (hasCurrentBundle()) return "bundle_ready";
499        if (hasCurrentApproval()) return "approved";
500        if (state.verificationMethod) return "verification_method_selected";
501        return "plan_ready";
502      }
503      return state.targetUrl ? "target_selected" : "idle";
504    }
505
506    function updateVisibleTarget(target) {
507      var input = query("#target-url");
508      if (input) input.value = target;
509    }
510
511    function createNonce() {
512      if (nonceFactory) return String(nonceFactory());
513      if (cryptoImpl && typeof cryptoImpl.randomUUID === "function") return cryptoImpl.randomUUID();
514      if (cryptoImpl && typeof cryptoImpl.getRandomValues === "function") {
515        var bytes = new Uint8Array(16);
516        cryptoImpl.getRandomValues(bytes);
517        return Array.prototype.map.call(bytes, function (byte) {
518          return byte.toString(16).padStart(2, "0");
519        }).join("");
520      }
521      throw new Error("approval_nonce_unavailable");
522    }
523
524    function targetInputMatches() {
525      var input = query("#target-url");
526      return Boolean(input && normalizeTargetUrl(String(input.value || "")) === state.targetUrl);
527    }
528
529    function approvalButtonFromEvent(event) {
530      var target = event && event.target;
531      if (!target || typeof target.closest !== "function") return null;
532      var button = target.closest('[data-wizard-next="install"]');
533      if (!button || typeof button.closest !== "function" || !button.closest("[data-install-wizard]")) return null;
534      if (button.disabled || button.hidden) return null;
535      if (button.getAttribute && button.getAttribute("aria-disabled") === "true") return null;
536      if (button.closest("[hidden]")) return null;
537      return button;
538    }
539
540    function trustedActivationPresent(event) {
541      if (!event || event.isTrusted !== true || event.defaultPrevented === true) return false;
542      if (userActivation && userActivation.isActive !== true) return false;
543      return true;
544    }
545
546    function scheduleVisibleApproval(event) {
547      if (
548        disposed
549        || !trustedActivationPresent(event)
550        || state.state !== "approval_pending"
551        || state.busyOperation
552        || !state.manifest
553        || !state.manifestDigest
554        || !state.verificationMethod
555        || !targetInputMatches()
556      ) return;
557      var button = approvalButtonFromEvent(event);
558      if (!button) return;
559      var permissionsPanel = query('[data-wizard-panel="permissions"]');
560      if (!permissionsPanel || permissionsPanel.hidden === true) return;
561      var candidate;
562      try {
563        candidate = {
564          target_url: state.targetUrl,
565          manifest_digest: state.manifestDigest,
566          nonce: createNonce(),
567          approved_at: new Date(now()).toISOString()
568        };
569      } catch (_error) {
570        invalidatePlan("approval_nonce_unavailable", "The browser could not create an approval correlation nonce.");
571        return;
572      }
573      // The confirmation waits for the permissions-to-install transition to be on
574      // screen. A microtask alone is too early: the browser runs a microtask
575      // checkpoint between listeners, so it fires after this capture listener but
576      // before the wizard's own click handler has moved the panels, and approval
577      // never lands in a real browser. Poll instead, revalidating every
578      // precondition on each attempt, and give up if the transition never comes.
579      var attemptsLeft = APPROVAL_TRANSITION_ATTEMPTS;
580      function confirmVisibleApproval() {
581        if (
582          disposed
583          || state.state !== "approval_pending"
584          || state.busyOperation
585          || state.targetUrl !== candidate.target_url
586          || state.manifestDigest !== candidate.manifest_digest
587          || !targetInputMatches()
588        ) return;
589        var installPanel = query('[data-wizard-panel="install"]');
590        var currentPermissionsPanel = query('[data-wizard-panel="permissions"]');
591        if (!installPanel || installPanel.hidden === true || (currentPermissionsPanel && currentPermissionsPanel.hidden !== true)) {
592          attemptsLeft -= 1;
593          if (attemptsLeft > 0) scheduleTransitionCheck(confirmVisibleApproval);
594          return;
595        }
596        state.approval = candidate;
597        state.preparedBundle = null;
598        state.state = "approved";
599        state.error = null;
600        renderStateMessage();
601      }
602      queueTask(confirmVisibleApproval);
603    }
604
605    function invalidateForVisibleTargetEdit(event) {
606      var target = event && event.target;
607      if (!target || target.id !== "target-url" || !state.targetUrl) return;
608      var normalized = normalizeTargetUrl(String(target.value || ""));
609      if (normalized === state.targetUrl) return;
610      cancelPendingRequests();
611      invalidatePlan(
612        "target_changed",
613        "The website target changed. Load and approve a new target-bound plan.",
614        normalized ? "target_selected" : "idle"
615      );
616      state.targetUrl = normalized || null;
617    }
618
619    function addListener(target, name, handler, optionsValue) {
620      if (!target || typeof target.addEventListener !== "function") return;
621      target.addEventListener(name, handler, optionsValue);
622      listeners.push([target, name, handler, optionsValue]);
623    }
624
625    async function ingestManifest(targetUrl, manifest, context) {
626      context = context || {};
627      var target = normalizeTargetUrl(targetUrl || manifest && manifest.target && manifest.target.url);
628      var validation = manifestValidationError(target, manifest);
629      if (validation) {
630        if (context.current()) {
631          state.targetUrl = target || state.targetUrl;
632          invalidatePlan(validation.code, validation.message);
633        }
634        return false;
635      }
636
637      var previous = {
638        targetUrl: state.targetUrl,
639        state: state.state,
640        digest: state.manifestDigest,
641        approval: state.approval,
642        bundle: state.preparedBundle,
643        method: state.verificationMethod,
644        verification: state.verification
645      };
646      state.busyOperation = "manifest_digest";
647      renderStateMessage();
648      var digested;
649      try {
650        digested = await digestManifest(manifest);
651      } catch (_error) {
652        if (context.current()) {
653          state.targetUrl = target;
654          invalidatePlan("manifest_digest_unavailable", "The complete install manifest could not be bound to an approval digest.");
655        }
656        return false;
657      }
658      if (disposed || !context.current()) return false;
659      state.busyOperation = null;
660
661      if (previous.targetUrl === target && previous.digest === digested.digest) {
662        state.targetUrl = target;
663        state.manifest = digested.manifest;
664        state.manifestDigest = digested.digest;
665        state.verificationMethod = previous.method;
666        state.approval = previous.approval;
667        state.preparedBundle = previous.bundle;
668        state.verification = previous.verification;
669        state.state = previous.state;
670        state.error = null;
671        updateVisibleTarget(target);
672        renderPlan(state.manifest, state.manifestDigest, context.source);
673        renderStateMessage();
674        return true;
675      }
676
677      clearPlanState();
678      state.targetUrl = target;
679      state.manifest = digested.manifest;
680      state.manifestDigest = digested.digest;
681      state.state = "plan_ready";
682      state.error = null;
683      updateVisibleTarget(target);
684      verificationRequestSequence += 1;
685      renderPlan(state.manifest, state.manifestDigest, context.source);
686      return true;
687    }
688
689    function recordManifestFailure() {
690      cancelPendingRequests();
691      invalidatePlan("manifest_unavailable", "The production install manifest is unavailable.");
692    }
693
694    function dispatchManifest(manifest) {
695      if (!eventTarget || typeof eventTarget.dispatchEvent !== "function") return;
696      var EventCtor = options.CustomEvent || (root && root.CustomEvent);
697      var detail = {
698        targetUrl: state.targetUrl,
699        manifest: clone(manifest),
700        source: "xagent-webmcp-controller"
701      };
702      var event = EventCtor
703        ? new EventCtor("xagent:manifestloaded", { detail: detail })
704        : { type: "xagent:manifestloaded", detail: detail };
705      eventTarget.dispatchEvent(event);
706    }
707
708    async function inspectOnboarding() {
709      return result("inspect_onboarding", true, {
710        contract: "xagent-webmcp-onboarding-v1",
711        tools: TOOL_NAMES.slice(),
712        verification_methods: ["public_file"],
713        boundary: {
714          customer_agent_holds_credentials: true,
715          customer_agent_executes_changes: true,
716          xagent_id_public_refetch_only: true,
717          direct_dns_github_or_deploy_control: false,
718          page_script_synthetic_approval_blocked: true,
719          browser_activation_is_not_natural_person_attestation: true,
720          approval_bound_to_complete_manifest_digest: true
721        }
722      });
723    }
724
725    async function previewAgentId(input) {
726      var target = normalizeTargetUrl(input && input.target_url);
727      if (!target) return reject("preview_agent_id", "invalid_target", "Provide an HTTPS website origin without credentials.");
728      selectTarget(target, true);
729      updateVisibleTarget(target);
730      renderManifestLinks(target);
731      var previewUrl = new URL("/card", locationOrigin);
732      previewUrl.searchParams.set("url", target);
733      return result("preview_agent_id", true, {
734        preview_url: previewUrl.toString(),
735        navigation_performed: false
736      });
737    }
738
739    async function getInstallPlan(input) {
740      var target = normalizeTargetUrl(input && input.target_url);
741      if (!target) return reject("get_install_plan", "invalid_target", "Provide an HTTPS website origin without credentials.");
742      if (!fetcher) return reject("get_install_plan", "fetch_unavailable", "The browser cannot load the install manifest.");
743      selectTarget(target, true);
744      updateVisibleTarget(target);
745      renderManifestLinks(target);
746      var requestId = planRequestSequence;
747      state.busyOperation = "manifest_fetch";
748      setText("#install-status", t("Loading the production install plan…"));
749      try {
750        var response = await fetcher(
751          "/.well-known/xagent-install.json?url=" + encodeURIComponent(target),
752          { headers: { Accept: "application/json" }, credentials: "omit" }
753        );
754        if (requestId !== planRequestSequence || state.targetUrl !== target) {
755          return reject("get_install_plan", "request_superseded", "A newer website target replaced this install-plan request.");
756        }
757        if (!response || !response.ok) {
758          invalidatePlan("release_unavailable", "The production install manifest is unavailable.");
759          return result("get_install_plan", false, {}, clone(state.error));
760        }
761        var manifest = await response.json();
762        if (requestId !== planRequestSequence || state.targetUrl !== target) {
763          return reject("get_install_plan", "request_superseded", "A newer website target replaced this install-plan request.");
764        }
765        var accepted = await ingestManifest(target, manifest, {
766          requestId: requestId,
767          source: "agent",
768          current: function () {
769            return requestId === planRequestSequence && state.targetUrl === target;
770          }
771        });
772        if (!accepted) {
773          if (requestId !== planRequestSequence || state.targetUrl !== target) {
774            return reject("get_install_plan", "request_superseded", "A newer website target or manifest replaced this install-plan request.");
775          }
776          return result("get_install_plan", false, {}, clone(state.error));
777        }
778        dispatchManifest(state.manifest);
779        return result("get_install_plan", true, safePlan(state.manifest, state.manifestDigest));
780      } catch (_error) {
781        if (requestId === planRequestSequence && state.targetUrl === target) {
782          invalidatePlan("manifest_invalid", "The production install manifest could not be validated.");
783          return result("get_install_plan", false, {}, clone(state.error));
784        }
785        return reject("get_install_plan", "request_superseded", "A newer website target replaced this install-plan request.");
786      }
787    }
788
789    async function chooseVerificationMethod(input) {
790      if (state.busyOperation) return reject("choose_verification_method", "plan_busy", "The current plan operation must finish first.");
791      if (!state.manifest || !state.manifestDigest) return reject("choose_verification_method", "plan_required", "Load a target-bound install plan first.");
792      if (!input || input.method !== "public_file") return reject("choose_verification_method", "unsupported_method", "Version 1 supports only public_file verification.");
793      state.verificationMethod = "public_file";
794      state.approval = null;
795      state.preparedBundle = null;
796      state.state = "verification_method_selected";
797      state.error = null;
798      renderStateMessage();
799      return result("choose_verification_method", true, {
800        method: "public_file",
801        publisher: "customer_agent",
802        verifier: "xagent_id_public_refetch"
803      });
804    }
805
806    async function requestOwnerApproval() {
807      if (state.busyOperation) return reject("request_owner_approval", "plan_busy", "The current plan operation must finish first.");
808      if (!state.manifest || !state.manifestDigest) return reject("request_owner_approval", "plan_required", "Load a target-bound install plan first.");
809      if (!state.verificationMethod) return reject("request_owner_approval", "verification_method_required", "Choose the verification method first.");
810      state.approval = null;
811      state.preparedBundle = null;
812      state.state = "approval_pending";
813      state.error = null;
814      dispatchManifest(state.manifest);
815      var step = query('[data-wizard-step="permissions"]');
816      if (step && typeof step.click === "function") step.click();
817      var panel = query('[data-wizard-panel="permissions"]');
818      if (panel && typeof panel.scrollIntoView === "function") {
819        panel.scrollIntoView({ behavior: "smooth", block: "center" });
820      }
821      renderStateMessage();
822      var plan = safePlan(state.manifest, state.manifestDigest);
823      return result("request_owner_approval", true, {
824        approval_granted: false,
825        visible_confirmation_required: true,
826        browser_signal_limit: "not_cryptographic_proof_of_natural_person_or_owner_identity",
827        manifest_digest: state.manifestDigest,
828        requested_grants: plan.modules.filter(function (item) { return item.default; }).map(function (item) {
829          return item.grant;
830        }).filter(Boolean),
831        declared_write_paths: plan.declared_write_paths
832      });
833    }
834
835    async function prepareInstallBundle() {
836      if (state.busyOperation || state.state === "verifying") {
837        return reject("prepare_install_bundle", "plan_busy", "The current plan or verification operation is still in progress.");
838      }
839      if (!state.manifest || !state.manifestDigest) return reject("prepare_install_bundle", "plan_required", "Load a target-bound install plan first.");
840      if (state.state === "blocked") return reject("prepare_install_bundle", "plan_invalidated", "The install plan is blocked and must be loaded again.");
841      if (hasCurrentBundle()) {
842        var replay = clone(state.preparedBundle);
843        replay.idempotent_replay = true;
844        return result("prepare_install_bundle", true, replay);
845      }
846      if (state.state !== "approved" || !hasCurrentApproval()) {
847        return reject("prepare_install_bundle", "human_approval_required", "The website owner must review the visible permissions first.");
848      }
849      var byId = {};
850      (Array.isArray(state.manifest.workflow) ? state.manifest.workflow : []).forEach(function (step) {
851        if (step && SAFE_WORKFLOW_IDS.indexOf(step.id) >= 0) byId[step.id] = step;
852      });
853      var commands = [];
854      for (var i = 0; i < SAFE_WORKFLOW_IDS.length; i += 1) {
855        var id = SAFE_WORKFLOW_IDS[i];
856        var command = byId[id] && byId[id].command;
857        if (typeof command !== "string" || !command || command.length > 4096) continue;
858        if (CREDENTIAL_PATTERN.test(command)) {
859          invalidatePlan("credential_material_rejected", "The install bundle contained credential-like material and was blocked.");
860          return result("prepare_install_bundle", false, {}, clone(state.error));
861        }
862        commands.push({
863          id: id,
864          command: command,
865          owner_approval_required: id === "apply" || byId[id].owner_approval_required === true
866        });
867      }
868      if (!commands.length) {
869        invalidatePlan("bundle_unavailable", "No approved install commands are available.");
870        return result("prepare_install_bundle", false, {}, clone(state.error));
871      }
872      state.approval.consumed_at = new Date(now()).toISOString();
873      state.preparedBundle = {
874        commands: commands,
875        target_url: state.targetUrl,
876        manifest_digest: state.manifestDigest,
877        approval: {
878          source: "browser_user_activation_and_visible_transition",
879          present: true,
880          correlation_nonce: state.approval.nonce,
881          approved_at: state.approval.approved_at,
882          consumed_at: state.approval.consumed_at,
883          attestation_limit: "not_cryptographic_proof_of_natural_person_or_owner_identity"
884        },
885        customer_agent_executes: true,
886        xagent_id_executes_commands: false,
887        credentials_requested: false,
888        idempotent_replay: false
889      };
890      state.state = "bundle_ready";
891      state.error = null;
892      renderStateMessage();
893      return result("prepare_install_bundle", true, clone(state.preparedBundle));
894    }
895
896    async function verifyInstallation(input) {
897      var requested = input && input.target_url ? normalizeTargetUrl(input.target_url) : state.targetUrl;
898      if (!requested || requested !== state.targetUrl) {
899        return reject("verify_installation", "target_mismatch", "Verification must use the currently selected website target.");
900      }
901      if (!fetcher) return reject("verify_installation", "fetch_unavailable", "The browser cannot request public verification.");
902      if (state.busyOperation) return reject("verify_installation", "plan_busy", "Another plan or verification operation is already in progress.");
903      var previousState = state.state;
904      var requestId = ++verificationRequestSequence;
905      state.busyOperation = "verification";
906      state.state = "verifying";
907      state.error = null;
908      renderStateMessage();
909      try {
910        var response = await fetcher(
911          "/api/site_agents/install_status?url=" + encodeURIComponent(requested),
912          { headers: { Accept: "application/json" }, credentials: "omit" }
913        );
914        if (requestId !== verificationRequestSequence || state.targetUrl !== requested) {
915          return reject("verify_installation", "request_superseded", "A newer website target replaced this verification request.");
916        }
917        if (!response || !response.ok) {
918          state.busyOperation = null;
919          state.state = restoreStateAfterVerification(previousState);
920          renderStateMessage();
921          return reject("verify_installation", "verification_unavailable", "Public installation status is unavailable.");
922        }
923        var verification = safeVerification(await response.json());
924        if (requestId !== verificationRequestSequence || state.targetUrl !== requested) {
925          return reject("verify_installation", "request_superseded", "A newer website target replaced this verification request.");
926        }
927        state.verification = verification;
928        var publicStatus = verification.public_state && verification.public_state.state;
929        var verified = verification.ok && (
930          VERIFIED_STATUSES.indexOf(verification.status) >= 0
931          || VERIFIED_STATUSES.indexOf(publicStatus) >= 0
932        );
933        state.busyOperation = null;
934        state.state = verified ? "verified" : restoreStateAfterVerification(previousState);
935        state.error = null;
936        renderStateMessage();
937        return result("verify_installation", true, {
938          verified: verified,
939          evidence: verification,
940          restored_state: verified ? null : state.state
941        });
942      } catch (_error) {
943        if (requestId === verificationRequestSequence && state.targetUrl === requested) {
944          state.busyOperation = null;
945          state.state = restoreStateAfterVerification(previousState);
946          renderStateMessage();
947          return reject("verify_installation", "verification_invalid", "Public installation status could not be validated.");
948        }
949        return reject("verify_installation", "request_superseded", "A newer website target replaced this verification request.");
950      }
951    }
952
953    async function getOnboardingStatus() {
954      return result("get_onboarding_status", true, stateSnapshot());
955    }
956
957    function execute(name, input) {
958      var handlers = {
959        inspect_onboarding: inspectOnboarding,
960        preview_agent_id: previewAgentId,
961        get_install_plan: getInstallPlan,
962        choose_verification_method: chooseVerificationMethod,
963        request_owner_approval: requestOwnerApproval,
964        prepare_install_bundle: prepareInstallBundle,
965        verify_installation: verifyInstallation,
966        get_onboarding_status: getOnboardingStatus
967      };
968      if (!handlers[name]) {
969        return Promise.resolve(reject(String(name || "unknown"), "unknown_tool", "The requested WebMCP onboarding tool is not registered."));
970      }
971      return handlers[name](input || {});
972    }
973
974    function onManifestLoaded(event) {
975      var detail = event && event.detail || {};
976      if (detail.source === "xagent-webmcp-controller") {
977        renderPlan(detail.manifest, state.manifestDigest, "agent");
978        renderStateMessage();
979        return;
980      }
981      var target = normalizeTargetUrl(detail.targetUrl || detail.manifest && detail.manifest.target && detail.manifest.target.url);
982      var eventId = ++manifestEventSequence;
983      planRequestSequence += 1;
984      verificationRequestSequence += 1;
985      var requestId = planRequestSequence;
986      if (target && target !== state.targetUrl) {
987        clearPlanState();
988        state.targetUrl = target;
989        state.state = "target_selected";
990        state.error = null;
991      }
992      ingestManifest(detail.targetUrl, detail.manifest, {
993        eventId: eventId,
994        requestId: requestId,
995        source: "human_ui",
996        current: function () {
997          return eventId === manifestEventSequence && requestId === planRequestSequence;
998        }
999      }).catch(function () {
1000        if (eventId === manifestEventSequence && requestId === planRequestSequence) recordManifestFailure();
1001      });
1002    }
1003
1004    function onManifestFailed() {
1005      recordManifestFailure();
1006    }
1007
1008    function onLanguageChange() {
1009      if (state.manifest) renderPlan(state.manifest, state.manifestDigest, "agent");
1010      renderStateMessage();
1011    }
1012
1013    addListener(doc, "click", scheduleVisibleApproval, true);
1014    addListener(doc, "input", invalidateForVisibleTargetEdit, true);
1015    addListener(eventTarget, "xagent:manifestloaded", onManifestLoaded);
1016    addListener(eventTarget, "xagent:manifestfailed", onManifestFailed);
1017    addListener(eventTarget, "xagent:languagechange", onLanguageChange);
1018
1019    function disposeInternal() {
1020      if (disposed) return;
1021      disposed = true;
1022      cancelPendingRequests();
1023      listeners.forEach(function (entry) {
1024        if (entry[0] && typeof entry[0].removeEventListener === "function") {
1025          entry[0].removeEventListener(entry[1], entry[2], entry[3]);
1026        }
1027      });
1028      listeners = [];
1029      clearPlanState();
1030      state.targetUrl = null;
1031      state.state = "idle";
1032      state.error = null;
1033    }
1034
1035    return {
1036      publicController: {
1037        execute: execute,
1038        getState: stateSnapshot
1039      },
1040      dispose: disposeInternal
1041    };
1042  }
1043
1044  function createController(options) {
1045    return createControllerInternal(options).publicController;
1046  }
1047
1048  function schema(properties, required) {
1049    return {
1050      type: "object",
1051      properties: properties || {},
1052      required: required || [],
1053      additionalProperties: false
1054    };
1055  }
1056
1057  function toolDefinitions(controller) {
1058    var definitions = [
1059      {
1060        name: "inspect_onboarding",
1061        description: "Inspect xAgent ID onboarding capabilities and authority boundaries. This reads page state and makes no external change.",
1062        inputSchema: schema()
1063      },
1064      {
1065        name: "preview_agent_id",
1066        description: "Validate and select an HTTPS website, then return its xAgent ID card preview URL without navigating or changing the website.",
1067        inputSchema: schema({ target_url: { type: "string", maxLength: 2048, description: "HTTPS website origin controlled by the user." } }, ["target_url"])
1068      },
1069      {
1070        name: "get_install_plan",
1071        description: "Read, validate, and bind the complete production target-bound install plan. Commands are withheld until visible browser approval.",
1072        inputSchema: schema({ target_url: { type: "string", maxLength: 2048, description: "HTTPS website origin controlled by the user." } }, ["target_url"])
1073      },
1074      {
1075        name: "choose_verification_method",
1076        description: "Choose the supported public proof method in browser-local onboarding state. This does not publish proof.",
1077        inputSchema: schema({ method: { type: "string", enum: ["public_file"], description: "Public xAgent ID file verified by independent refetch." } }, ["method"])
1078      },
1079      {
1080        name: "request_owner_approval",
1081        description: "Open the visible permissions UI and request review. The result is not approval; a trusted browser activation must complete the visible transition.",
1082        inputSchema: schema()
1083      },
1084      {
1085        name: "prepare_install_bundle",
1086        description: "After browser approval bound to the complete manifest digest, return bounded commands for the customer-side Agent. This tool never executes commands or receives credentials.",
1087        inputSchema: schema()
1088      },
1089      {
1090        name: "verify_installation",
1091        description: "Ask xagent.id to independently refetch public installation evidence for the selected target. This cannot accept a browser assertion of success.",
1092        inputSchema: schema({ target_url: { type: "string", maxLength: 2048, description: "Optional HTTPS origin; it must match the selected target." } })
1093      },
1094      {
1095        name: "get_onboarding_status",
1096        description: "Read the current onboarding state and next safe action without returning install commands.",
1097        inputSchema: schema()
1098      }
1099    ];
1100    return definitions.map(function (definition) {
1101      var annotations = {};
1102      if (READ_ONLY_TOOLS[definition.name]) annotations.readOnlyHint = true;
1103      if (UNTRUSTED_OUTPUT_TOOLS[definition.name]) annotations.untrustedContentHint = true;
1104      definition.annotations = annotations;
1105      definition.execute = function (input) { return controller.execute(definition.name, input); };
1106      return definition;
1107    });
1108  }
1109
1110  var initialized = false;
1111  var initializationPromise = null;
1112  var activeController = null;
1113  var activeDispose = null;
1114  var registrationAbortController = null;
1115
1116  function safeRegistrationError(error) {
1117    return {
1118      name: String(error && error.name || "Error").slice(0, 80),
1119      message: String(error && error.message || "WebMCP tool registration failed.").slice(0, 240)
1120    };
1121  }
1122
1123  function optionsAbortController(contextRoot) {
1124    if (contextRoot && typeof contextRoot.AbortController === "function") return contextRoot.AbortController;
1125    return typeof AbortController === "function" ? AbortController : null;
1126  }
1127
1128  // WebMCP page registration. On a real page `doc` is the live document, so the
1129  // registration call below is literally document.modelContext.registerTool(...).
1130  // The `doc` parameter exists only so the Node test harness can drive the same
1131  // code with a fake document.
1132  function registerPageTool(doc, definition, options) {
1133    if (typeof document !== "undefined" && doc === document) {
1134      return document.modelContext.registerTool(definition, options);
1135    }
1136    return doc.modelContext.registerTool(definition, options);
1137  }
1138
1139  function initialize(doc) {
1140    if (initialized) {
1141      return Promise.resolve({
1142        registered: true,
1143        count: TOOL_NAMES.length,
1144        reason: "already_initialized",
1145        controller: activeController
1146      });
1147    }
1148    if (initializationPromise) return initializationPromise;
1149    if (!doc) return Promise.resolve({ registered: false, reason: "document_unavailable" });
1150    if (!doc.modelContext || typeof doc.modelContext.registerTool !== "function") {
1151      return Promise.resolve({ registered: false, reason: "webmcp_unavailable" });
1152    }
1153    var AbortControllerCtor = optionsAbortController(root);
1154    if (!AbortControllerCtor) return Promise.resolve({ registered: false, reason: "abort_controller_unavailable" });
1155
1156    initializationPromise = (async function () {
1157      var internal = createControllerInternal({ document: doc });
1158      var controller = internal.publicController;
1159      var abortController = new AbortControllerCtor();
1160      var definitions = toolDefinitions(controller);
1161      try {
1162        for (var i = 0; i < definitions.length; i += 1) {
1163          await registerPageTool(doc, definitions[i], { signal: abortController.signal });
1164        }
1165      } catch (error) {
1166        abortController.abort();
1167        internal.dispose();
1168        return {
1169          registered: false,
1170          count: 0,
1171          reason: "registration_failed",
1172          error: safeRegistrationError(error)
1173        };
1174      }
1175      activeController = controller;
1176      activeDispose = internal.dispose;
1177      registrationAbortController = abortController;
1178      initialized = true;
1179      return { registered: true, count: definitions.length, controller: activeController };
1180    })().then(function (value) {
1181      initializationPromise = null;
1182      return value;
1183    }, function (error) {
1184      initializationPromise = null;
1185      return {
1186        registered: false,
1187        count: 0,
1188        reason: "registration_failed",
1189        error: safeRegistrationError(error)
1190      };
1191    });
1192    return initializationPromise;
1193  }
1194
1195  return {
1196    TOOL_NAMES: TOOL_NAMES,
1197    READ_ONLY_TOOLS: READ_ONLY_TOOLS,
1198    normalizeTargetUrl: normalizeTargetUrl,
1199    canonicalManifestDigest: canonicalManifestDigest,
1200    createController: createController,
1201    toolDefinitions: toolDefinitions,
1202    initialize: initialize,
1203    getController: function () { return activeController; },
1204    getRegistrationStatus: function () {
1205      return {
1206        initialized: initialized,
1207        initializing: Boolean(initializationPromise),
1208        abortable: Boolean(registrationAbortController),
1209        internal_disposer_retained: Boolean(activeDispose)
1210      };
1211    }
1212  };
1213});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.