replicas-computer 0.3.31 → 0.3.33

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/index.js CHANGED
@@ -1907,13 +1907,22 @@ async function computerBrowserStateCommand(path, options = {}) {
1907
1907
  }));
1908
1908
  console.log(JSON.stringify({ targetId: page.id ?? "unknown", ...observation }, null, 2));
1909
1909
  }
1910
- var DESKTOP_POINT_JS = `
1910
+ var VIEWPORT_ORIGIN_JS = `
1911
+ const viewportOrigin = () => {
1912
+ const zoom = window.devicePixelRatio;
1913
+ const border = Math.max(0, (window.outerWidth - window.innerWidth * zoom) / 2);
1914
+ return {
1915
+ left: window.screenX + border,
1916
+ top: window.screenY + Math.max(0, window.outerHeight - window.innerHeight * zoom - border),
1917
+ zoom,
1918
+ };
1919
+ };`;
1920
+ var DESKTOP_POINT_JS = `${VIEWPORT_ORIGIN_JS}
1911
1921
  const desktopPoint = (rect) => {
1912
- const borderX = Math.max(0, (window.outerWidth - window.innerWidth) / 2);
1913
- const topChrome = Math.max(0, window.outerHeight - window.innerHeight - borderX);
1922
+ const { left, top, zoom } = viewportOrigin();
1914
1923
  return {
1915
- x: Math.round(window.screenX + borderX + rect.x + rect.width / 2),
1916
- y: Math.round(window.screenY + topChrome + rect.y + rect.height / 2),
1924
+ x: Math.round(left + (rect.x + rect.width / 2) * zoom),
1925
+ y: Math.round(top + (rect.y + rect.height / 2) * zoom),
1917
1926
  };
1918
1927
  };`;
1919
1928
  function parseBrowserRef(value) {
@@ -3316,7 +3325,7 @@ async function computerStatusCommand() {
3316
3325
  }
3317
3326
 
3318
3327
  // src/version.ts
3319
- var COMPUTER_USE_VERSION = "0.3.31";
3328
+ var COMPUTER_USE_VERSION = "0.3.33";
3320
3329
  var COMPUTER_USE_PROTOCOL_VERSION = 1;
3321
3330
 
3322
3331
  // src/program.ts
@@ -3908,6 +3917,42 @@ function superviseEncoder(options) {
3908
3917
  // src/ice-servers.ts
3909
3918
  import { readFileSync as readFileSync7 } from "fs";
3910
3919
 
3920
+ // ../shared/src/urls.ts
3921
+ var DASHBOARD_PATH = "/dashboard";
3922
+ var WEB_APP_PATHS = {
3923
+ home: "/",
3924
+ onboarding: "/onboarding",
3925
+ workspaces: "/workspace",
3926
+ environmentSetup: "/workspace/setup",
3927
+ automations: "/automations",
3928
+ environment: "/environment",
3929
+ analytics: "/analytics",
3930
+ review: "/review",
3931
+ dashboard: DASHBOARD_PATH,
3932
+ agents: `${DASHBOARD_PATH}/agents`,
3933
+ integrations: `${DASHBOARD_PATH}/integrations`,
3934
+ integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
3935
+ integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
3936
+ integrationsSlack: `${DASHBOARD_PATH}/integrations/slack`,
3937
+ settings: `${DASHBOARD_PATH}/settings`,
3938
+ billing: `${DASHBOARD_PATH}/billing`,
3939
+ members: `${DASHBOARD_PATH}/members`,
3940
+ learnings: `${DASHBOARD_PATH}/learnings`,
3941
+ "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
3942
+ accountAgents: `${DASHBOARD_PATH}/account/agents`,
3943
+ accountProfile: `${DASHBOARD_PATH}/account/profile`,
3944
+ accountConnections: `${DASHBOARD_PATH}/account/connections`,
3945
+ accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
3946
+ accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
3947
+ accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
3948
+ fullscreenWorkspaces: "/workspaces"
3949
+ };
3950
+ var LEGACY_WEB_APP_PATHS = {
3951
+ app: "/app",
3952
+ home: "/home",
3953
+ dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
3954
+ };
3955
+
3911
3956
  // ../shared/src/desktop-stream.ts
3912
3957
  function isStreamIceServer(value) {
3913
3958
  return isRecord(value) && typeof value.urls === "string" && (value.username === void 0 || typeof value.username === "string") && (value.credential === void 0 || typeof value.credential === "string");
@@ -3971,6 +4016,10 @@ function isDesktopWebRtcFailure(value) {
3971
4016
  "codec"
3972
4017
  ].every((key) => typeof value[key] === "string" && value[key].length <= 200) && ["elapsedMs", "candidateErrors", "framesDecoded", "packetsReceived", "packetsLost", "bytesReceived", "lastFrameAgeMs", "encoderFps"].every((key) => typeof value[key] === "number" && Number.isFinite(value[key]));
3973
4018
  }
4019
+ var isDesktopRect = (value) => isRecord(value) && ["x", "y", "width", "height"].every((key) => typeof value[key] === "number" && Number.isFinite(value[key]));
4020
+ function isDesktopElement(value) {
4021
+ return isRecord(value) && isDesktopRect(value.rect) && ["url", "tag", "selector"].every((key) => typeof value[key] === "string") && ["text", "label", "source"].every((key) => value[key] === void 0 || typeof value[key] === "string") && (value.components === void 0 || Array.isArray(value.components) && value.components.every((name) => typeof name === "string"));
4022
+ }
3974
4023
 
3975
4024
  // ../shared/src/runtime-env.ts
3976
4025
  var REPLICAS_RUNTIME_ENV_ALIASES = {
@@ -4935,6 +4984,92 @@ var InputBridge = class {
4935
4984
  }
4936
4985
  };
4937
4986
 
4987
+ // src/desktop-inspect.ts
4988
+ var PAGE_GEOMETRY = `(() => {${VIEWPORT_ORIGIN_JS}
4989
+ return { visible: document.visibilityState === 'visible', focused: document.hasFocus(), innerWidth, innerHeight, ...viewportOrigin() };
4990
+ })()`;
4991
+ var DESCRIBE_ELEMENT = `function () {
4992
+ let el = this.nodeType === 1 ? this : this.parentElement;
4993
+ if (!el) return null;
4994
+ while (el.ownerSVGElement) el = el.ownerSVGElement;
4995
+ el = el.closest('a, button, label, summary, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], [role="checkbox"], [role="switch"]') || el;
4996
+ const clip = (value, max) => {
4997
+ const text = String(value || '').replace(/\\s+/g, ' ').trim();
4998
+ return !text ? undefined : text.length > max ? text.slice(0, max - 1) + '\u2026' : text;
4999
+ };
5000
+ const step = (node) => {
5001
+ if (node.id) return '#' + CSS.escape(node.id);
5002
+ const same = node.parentElement ? [...node.parentElement.children].filter((child) => child.localName === node.localName) : [];
5003
+ return node.localName + [...node.classList].filter((name) => /^[\\w-]+$/.test(name)).slice(0, 2).map((name) => '.' + name).join('')
5004
+ + (same.length > 1 ? ':nth-of-type(' + (same.indexOf(node) + 1) + ')' : '');
5005
+ };
5006
+ const steps = [];
5007
+ for (let node = el; node && node !== document.documentElement && steps.length < 4; node = node.parentElement) {
5008
+ steps.unshift(step(node));
5009
+ if (node.id) break;
5010
+ }
5011
+ const components = [];
5012
+ let source;
5013
+ try {
5014
+ const key = Object.keys(el).find((name) => name.startsWith('__reactFiber$') || name.startsWith('__reactInternalInstance$'));
5015
+ const typeName = (type, depth = 0) => typeof type === 'function' ? type.displayName || type.name
5016
+ : type && depth < 3 ? type.displayName || typeName(type.render, depth + 1) || typeName(type.type, depth + 1) : undefined;
5017
+ const host = key && el[key];
5018
+ const next = host && host._debugOwner ? (fiber) => fiber._debugOwner || fiber.owner : (fiber) => fiber.return;
5019
+ for (let fiber = host && next(host); fiber && components.length < 4; fiber = next(fiber)) {
5020
+ const name = typeof fiber.tag !== 'number' ? fiber.name : [0, 1, 11, 14, 15].includes(fiber.tag) ? typeName(fiber.type) : undefined;
5021
+ if (typeof name === 'string' && name.length > 1 && !/^_c\\d*$/.test(name) && components.at(-1) !== name) components.push(name);
5022
+ }
5023
+ const debugSource = host && host._debugSource;
5024
+ if (debugSource && debugSource.fileName) source = debugSource.fileName + ':' + debugSource.lineNumber;
5025
+ } catch {}
5026
+ const rect = el.getBoundingClientRect();
5027
+ let x = rect.x, y = rect.y;
5028
+ try {
5029
+ for (let view = window; view.frameElement; view = view.parent) {
5030
+ const frame = view.frameElement.getBoundingClientRect();
5031
+ x += frame.x + view.frameElement.clientLeft;
5032
+ y += frame.y + view.frameElement.clientTop;
5033
+ }
5034
+ } catch {}
5035
+ return {
5036
+ url: location.href,
5037
+ tag: el.localName,
5038
+ selector: steps.join(' > '),
5039
+ rect: { x, y, width: rect.width, height: rect.height },
5040
+ text: clip(el.innerText || (el.type === 'password' ? '' : el.value) || el.textContent, 120),
5041
+ label: clip(el.getAttribute('aria-label') || el.getAttribute('title') || el.getAttribute('alt') || el.getAttribute('placeholder'), 80),
5042
+ components: components.length ? components : undefined,
5043
+ source,
5044
+ };
5045
+ }`;
5046
+ async function inspectPage(session, x, y) {
5047
+ const page = chromeCallValue(await sendChromeSessionCommand(session, "Runtime.evaluate", { expression: PAGE_GEOMETRY, returnByValue: true }));
5048
+ if (!isRecord(page) || page.visible !== true) return null;
5049
+ const [left, top, zoom, innerWidth, innerHeight] = [page.left, page.top, page.zoom, page.innerWidth, page.innerHeight].map(Number);
5050
+ const pageX = (x - left) / zoom;
5051
+ const pageY = (y - top) / zoom;
5052
+ if (!(pageX >= 0 && pageY >= 0 && pageX < innerWidth && pageY < innerHeight)) return null;
5053
+ const focused = page.focused === true;
5054
+ const located = await sendChromeSessionCommand(session, "DOM.getNodeForLocation", { x: Math.floor(pageX), y: Math.floor(pageY) });
5055
+ const resolved = await sendChromeSessionCommand(session, "DOM.resolveNode", { backendNodeId: located.backendNodeId });
5056
+ const objectId = isRecord(resolved.object) ? resolved.object.objectId : void 0;
5057
+ if (typeof objectId !== "string") return { focused, element: null };
5058
+ const described = chromeCallValue(await sendChromeSessionCommand(session, "Runtime.callFunctionOn", { objectId, functionDeclaration: DESCRIBE_ELEMENT, returnByValue: true }));
5059
+ if (!isRecord(described) || !isRecord(described.rect)) return { focused, element: null };
5060
+ const { x: rectX, y: rectY, width: width2, height: height2 } = described.rect;
5061
+ const element = {
5062
+ ...described,
5063
+ rect: { x: left + Number(rectX) * zoom, y: top + Number(rectY) * zoom, width: Number(width2) * zoom, height: Number(height2) * zoom }
5064
+ };
5065
+ return { focused, element: isDesktopElement(element) ? element : null };
5066
+ }
5067
+ async function inspectDesktopPoint(x, y) {
5068
+ const pages = (await getChromePages()).flatMap((page) => page.webSocketDebuggerUrl ? [page.webSocketDebuggerUrl] : []);
5069
+ const hits = (await Promise.all(pages.map((url) => withChromeSession(url, (session) => inspectPage(session, x, y)).catch(() => null)))).filter((hit) => hit !== null);
5070
+ return hits.find((hit) => hit.focused)?.element ?? null;
5071
+ }
5072
+
4938
5073
  // src/media-server.ts
4939
5074
  var width = Number.parseInt(process.env.REPLICAS_DESKTOP_WIDTH || String(DESKTOP_VIEWER_WIDTH), 10);
4940
5075
  var height = Number.parseInt(process.env.REPLICAS_DESKTOP_HEIGHT || String(DESKTOP_VIEWER_HEIGHT), 10);
@@ -5842,6 +5977,19 @@ async function runMediaServer() {
5842
5977
  });
5843
5978
  return;
5844
5979
  }
5980
+ if (url.pathname === "/inspect") {
5981
+ const x = Number(url.searchParams.get("x"));
5982
+ const y = Number(url.searchParams.get("y"));
5983
+ response.setHeader("cache-control", "no-store");
5984
+ if (!(x >= 0 && x < width && y >= 0 && y < height)) {
5985
+ response.statusCode = 400;
5986
+ response.end("x and y must be a point on the desktop");
5987
+ return;
5988
+ }
5989
+ response.setHeader("content-type", "application/json");
5990
+ void inspectDesktopPoint(x, y).catch(() => null).then((element) => response.end(JSON.stringify(element)));
5991
+ return;
5992
+ }
5845
5993
  const requested = url.pathname === "/" ? "stream-index.html" : url.pathname.slice(1);
5846
5994
  const path = resolve3(webroot, requested);
5847
5995
  if (!path.startsWith(resolve3(webroot) + "/") || !existsSync8(path) || !statSync2(path).isFile()) {
@@ -3,6 +3,43 @@ function isRecord(value) {
3
3
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
4
  }
5
5
 
6
+ // ../shared/src/urls.ts
7
+ var DEFAULT_WEB_APP_URL = "https://app.replicas.dev";
8
+ var DASHBOARD_PATH = "/dashboard";
9
+ var WEB_APP_PATHS = {
10
+ home: "/",
11
+ onboarding: "/onboarding",
12
+ workspaces: "/workspace",
13
+ environmentSetup: "/workspace/setup",
14
+ automations: "/automations",
15
+ environment: "/environment",
16
+ analytics: "/analytics",
17
+ review: "/review",
18
+ dashboard: DASHBOARD_PATH,
19
+ agents: `${DASHBOARD_PATH}/agents`,
20
+ integrations: `${DASHBOARD_PATH}/integrations`,
21
+ integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
22
+ integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
23
+ integrationsSlack: `${DASHBOARD_PATH}/integrations/slack`,
24
+ settings: `${DASHBOARD_PATH}/settings`,
25
+ billing: `${DASHBOARD_PATH}/billing`,
26
+ members: `${DASHBOARD_PATH}/members`,
27
+ learnings: `${DASHBOARD_PATH}/learnings`,
28
+ "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
29
+ accountAgents: `${DASHBOARD_PATH}/account/agents`,
30
+ accountProfile: `${DASHBOARD_PATH}/account/profile`,
31
+ accountConnections: `${DASHBOARD_PATH}/account/connections`,
32
+ accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
33
+ accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
34
+ accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
35
+ fullscreenWorkspaces: "/workspaces"
36
+ };
37
+ var LEGACY_WEB_APP_PATHS = {
38
+ app: "/app",
39
+ home: "/home",
40
+ dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
41
+ };
42
+
6
43
  // ../shared/src/desktop-stream.ts
7
44
  function isStreamIceServer(value) {
8
45
  return isRecord(value) && typeof value.urls === "string" && (value.username === void 0 || typeof value.username === "string") && (value.credential === void 0 || typeof value.credential === "string");
@@ -50,6 +87,18 @@ var DESKTOP_WEBRTC_FAILURE_REASONS = {
50
87
  };
51
88
  var DESKTOP_QUALITY_MESSAGE = "replicas-desktop-quality";
52
89
  var DESKTOP_CONNECTION_MESSAGE = "replicas-desktop-connection";
90
+ var DESKTOP_ANNOTATE_MESSAGE = "replicas-desktop-annotate";
91
+ function isDesktopAnnotateOrigin(origin, viewerHostname) {
92
+ if (origin === new URL(DEFAULT_WEB_APP_URL).origin) return true;
93
+ return viewerHostname.split(".").at(-1) === "localhost" && URL.canParse(origin) && new URL(origin).hostname === "localhost";
94
+ }
95
+ function isDesktopAnnotateRequest(value) {
96
+ return isRecord(value) && value.type === DESKTOP_ANNOTATE_MESSAGE && typeof value.id === "number" && (value.action === "capture" || value.action === "inspect" && Number.isFinite(value.x) && Number.isFinite(value.y));
97
+ }
98
+ var isDesktopRect = (value) => isRecord(value) && ["x", "y", "width", "height"].every((key) => typeof value[key] === "number" && Number.isFinite(value[key]));
99
+ function isDesktopElement(value) {
100
+ return isRecord(value) && isDesktopRect(value.rect) && ["url", "tag", "selector"].every((key) => typeof value[key] === "string") && ["text", "label", "source"].every((key) => value[key] === void 0 || typeof value[key] === "string") && (value.components === void 0 || Array.isArray(value.components) && value.components.every((name) => typeof name === "string"));
101
+ }
53
102
 
54
103
  // src/input-validation.ts
55
104
  var finite = (value) => typeof value === "number" && Number.isFinite(value);
@@ -280,6 +329,20 @@ var announceTransport = () => {
280
329
  if (window.parent === window) return;
281
330
  window.parent.postMessage({ type: DESKTOP_TRANSPORT_MESSAGE, transport: transport ?? "connecting", reason: transportReason, path: route.path, codec: route.codec, failure, recovery }, "*");
282
331
  };
332
+ addEventListener("message", (event) => {
333
+ if (window.parent === window || event.source !== window.parent || !isDesktopAnnotateOrigin(event.origin, location.hostname) || !isDesktopAnnotateRequest(event.data)) return;
334
+ const request2 = event.data;
335
+ const reply = (result) => window.parent.postMessage({ type: DESKTOP_ANNOTATE_MESSAGE, id: request2.id, ...result }, event.origin);
336
+ if (request2.action === "capture") {
337
+ void fetch("/snapshot", { cache: "no-store" }).then((response) => response.ok ? response.blob() : null).catch(() => null).then((image) => reply({ image }));
338
+ return;
339
+ }
340
+ void fetch(`/inspect?x=${Math.round(request2.x)}&y=${Math.round(request2.y)}`).then((response) => response.ok ? response.json() : Promise.resolve(null)).catch(() => null).then((element) => reply({ element: isDesktopElement(element) ? element : null }));
341
+ });
342
+ var parentOrigin = URL.canParse(document.referrer) ? new URL(document.referrer).origin : null;
343
+ if (window.parent !== window && parentOrigin && isDesktopAnnotateOrigin(parentOrigin, location.hostname)) {
344
+ window.parent.postMessage({ type: DESKTOP_ANNOTATE_MESSAGE, ready: true }, parentOrigin);
345
+ }
283
346
  var average = (sum, count) => count ? Math.round(sum * 10 / count) / 10 : void 0;
284
347
  var reportConnection = (ended) => {
285
348
  if (window.parent === window || !config) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-computer",
3
- "version": "0.3.31",
3
+ "version": "0.3.33",
4
4
  "description": "Replicas workspace computer-use runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",