@simular-ai/sai-mcp 0.1.2 → 0.2.1

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/README.md CHANGED
@@ -10,7 +10,7 @@ You need Node.js 22.12 or newer and a Sai API key (`sapi_…`). Sign in at
10
10
  [platform.simular.ai](https://platform.simular.ai) and create one under
11
11
  [API Keys](https://platform.simular.ai/api-keys). It is shown once.
12
12
 
13
- Docs: [docs.simular.ai/sai-api](https://docs.simular.ai/sai-api/introduction)
13
+ Docs: [platform.simular.ai/documentation](https://platform.simular.ai/documentation)
14
14
 
15
15
  ## Install
16
16
 
@@ -44,6 +44,8 @@ Or in `~/.codex/config.toml`:
44
44
  [mcp_servers.sai]
45
45
  command = "npx"
46
46
  args = ["-y", "@simular-ai/sai-mcp"]
47
+ # sai_task_wait holds for up to 110 s; Codex's default tool timeout is 60 s.
48
+ tool_timeout_sec = 120
47
49
 
48
50
  [mcp_servers.sai.env]
49
51
  SAI_API_KEY = "sapi_YOUR_KEY"
@@ -91,14 +93,31 @@ you a link to finish it and keeps waiting. Sai remembers earlier tasks on the
91
93
  same computer; `new_session: true` starts fresh.
92
94
 
93
95
  There is no screenshot tool: Sai sees the screen itself and reports in text.
94
- Watch it work live in the [Playground](https://platform.simular.ai/playground).
96
+
97
+ ## Watch Sai work
98
+
99
+ Each task comes with a live view: the computer's screen, Sai's steps,
100
+ permission requests you can answer, and the result.
101
+
102
+ - **Apps that show MCP Apps** (Claude desktop and web, ChatGPT, Cursor, VS
103
+ Code): the view appears in the conversation under the task. In Claude,
104
+ **Open in a window** pops it out so it stays in sight while you chat.
105
+ - **Everywhere else** (Claude Code, Codex): your agent gives you a
106
+ `watch_url` link to the same view, served by this server on your own
107
+ computer (`127.0.0.1`). Your API key never reaches the browser. Codex shows
108
+ the view inline but keeps it offline, so open the link for the live screen.
109
+ - Or open the [Playground](https://platform.simular.ai/playground).
110
+
111
+ One view watches a computer at a time. If another one takes over the screen,
112
+ the view says so and offers **Watch here**.
95
113
 
96
114
  ## Environment
97
115
 
98
- | Variable | Required | Meaning |
99
- | ------------- | -------- | -------------------------------------------------------------------- |
100
- | `SAI_API_KEY` | yes | Your Sai API key (`sapi_…`). |
101
- | `SAI_PUSH` | no | `1` turns on Claude Code channels (below). Leave it unset otherwise. |
116
+ | Variable | Required | Meaning |
117
+ | ---------------- | -------- | -------------------------------------------------------------------- |
118
+ | `SAI_API_KEY` | yes | Your Sai API key (`sapi_…`). |
119
+ | `SAI_PUSH` | no | `1` turns on Claude Code channels (below). Leave it unset otherwise. |
120
+ | `SAI_LOCAL_VIEW` | no | `0` turns off the local `watch_url` page. |
102
121
 
103
122
  ## Claude Code channels (research preview)
104
123
 
package/SKILL.md CHANGED
@@ -84,4 +84,4 @@ Build JSON bodies with `jq -n` as above so user text with quotes never breaks th
84
84
 
85
85
  ## Reference
86
86
 
87
- Full API documentation: https://docs.simular.ai/sai-api/introduction. Endpoints used here: `POST /v1/agents/message`, `GET /v1/agents/events`, `POST /v1/agents/approve`, `POST /v1/agents/abort`, `GET /v1/agents/machines`, `GET /v1/agents/models`, `GET /v1/agents/account`.
87
+ Full API documentation: https://platform.simular.ai/documentation. Endpoints used here: `POST /v1/agents/message`, `GET /v1/agents/events`, `POST /v1/agents/approve`, `POST /v1/agents/abort`, `GET /v1/agents/machines`, `GET /v1/agents/models`, `GET /v1/agents/account`.
package/dist/server.js CHANGED
@@ -27213,6 +27213,19 @@ var SaiApi = class {
27213
27213
  });
27214
27214
  return machines;
27215
27215
  }
27216
+ /**
27217
+ * `POST /machines/:id/live` — a view-only Guacamole tunnel for the caller's
27218
+ * own machine, for the MCP Apps task view. The URL carries a session token:
27219
+ * hand it to the view only, never to the model or a log.
27220
+ */
27221
+ liveScreen(machineId) {
27222
+ return this.json(`/v1/agents/machines/${encodeURIComponent(machineId)}/live`, {
27223
+ method: "POST",
27224
+ headers: this.headers(),
27225
+ signal: AbortSignal.timeout(2e4),
27226
+ prefix: "Live screen unavailable"
27227
+ });
27228
+ }
27216
27229
  /** GET /models — the catalog a task may name, with per-plan `allowed`. */
27217
27230
  async models() {
27218
27231
  const res = await this.request("/v1/agents/models", {
@@ -27341,6 +27354,7 @@ function readConfig(env = process.env) {
27341
27354
  // ceiling. `SAI_EVENTS_API=0` falls back to collecting the SSE stream, for a
27342
27355
  // server predating A1/A2.
27343
27356
  eventsApi: env.SAI_EVENTS_API === void 0 ? true : flag(env.SAI_EVENTS_API),
27357
+ localView: env.SAI_LOCAL_VIEW === void 0 ? true : flag(env.SAI_LOCAL_VIEW),
27344
27358
  ...env.SAI_API_VERSION_TAG?.trim() && { versionTag: env.SAI_API_VERSION_TAG.trim() }
27345
27359
  };
27346
27360
  }
@@ -27579,6 +27593,7 @@ var StreamCollector = class {
27579
27593
  const remaining = deadline - Date.now();
27580
27594
  if (remaining <= 0) break;
27581
27595
  await tracked.notifier.wait(remaining);
27596
+ if (opts.returnOnEvents) break;
27582
27597
  }
27583
27598
  return snapshot(tracked.state, opts.cursor);
27584
27599
  }
@@ -27642,7 +27657,7 @@ var EventsPoller = class {
27642
27657
  let since = opts.cursor;
27643
27658
  let status = "running";
27644
27659
  let text = "";
27645
- let first = true;
27660
+ let first = !(opts.returnOnEvents && opts.cursor);
27646
27661
  for (; ; ) {
27647
27662
  const remainingS = Math.ceil((deadline - Date.now()) / 1e3);
27648
27663
  const waitS = first ? 0 : Math.max(0, Math.min(EVENTS_MAX_WAIT_S, remainingS));
@@ -27659,7 +27674,7 @@ var EventsPoller = class {
27659
27674
  polled.state.approval = page.approval;
27660
27675
  polled.state.status = "needs_approval";
27661
27676
  }
27662
- if (!shouldKeepWaiting(status, polled.state.approval?.approvalId, waitWhile, knownApprovalId) || Date.now() >= deadline) {
27677
+ if (!shouldKeepWaiting(status, polled.state.approval?.approvalId, waitWhile, knownApprovalId) || Date.now() >= deadline || opts.returnOnEvents && collected.length > 0) {
27663
27678
  break;
27664
27679
  }
27665
27680
  if (status !== "running") {
@@ -27737,7 +27752,7 @@ var NO_SCREENSHOT = "There is no screenshot or screen-reading tool in this serve
27737
27752
  var toolDescriptions = {
27738
27753
  sai_machines: "List the Sai machines (cloud computers or the user's own desktops) this account can drive. Returns { machines: [{ machineId, name, kind?, online? }], default? } \u2014 `default` is set when there is exactly one machine, in which case sai_task_start may omit machine_id. Call this once per conversation, not before every task. An empty list means the account has no computer yet: this server cannot create one (it costs money and needs the signed-in user), so send the user to https://platform.simular.ai/playground to create it in one click.",
27739
27754
  sai_models: 'List the models Sai can run a task with: { models: [{ id, name, provider, tier, costTier, allowed }], default }. `allowed` is per plan (free accounts have an allowlist). Pass an id as sai_task_start.model to switch the session\'s model; "auto" lets Sai pick. Only call this when the user asks for a specific model or a task needs a stronger/faster one.',
27740
- sai_task_start: 'Send a task to Sai, an autonomous computer-use agent that operates a real Windows/macOS desktop (browser, apps, files) on the chosen machine. Returns { session_id, machine_id, queued, push } immediately; the task keeps running on the machine. Then call sai_task_wait with session_id until status is not "running" \u2014 UNLESS push is true, in which case do NOT call sai_task_wait: the result arrives as a channel notification and you should continue with other work. One conversation per machine persists on the "api" channel across calls (Sai remembers earlier tasks and their results); pass new_session: true to start clean. queued: true means Sai was busy and the message was parked as a steer for the running task. ' + NO_SCREENSHOT,
27755
+ sai_task_start: 'Send a task to Sai, an autonomous computer-use agent that operates a real Windows/macOS desktop (browser, apps, files) on the chosen machine. Returns { session_id, machine_id, queued, push, watch_url? } immediately; the task keeps running on the machine. watch_url is a local page where the user can watch the computer live and approve steps: always give it to the user as a clickable link (some apps can only show the screen there). Then call sai_task_wait with session_id until status is not "running" \u2014 UNLESS push is true, in which case do NOT call sai_task_wait: the result arrives as a channel notification and you should continue with other work. One conversation per machine persists on the "api" channel across calls (Sai remembers earlier tasks and their results); pass new_session: true to start clean. queued: true means Sai was busy and the message was parked as a steer for the running task. ' + NO_SCREENSHOT,
27741
27756
  sai_task_wait: 'Block up to timeout_s (default 90, max 110) for the task to leave the "running" state. Returns { status, events, text, approval?, cursor, error? }. status: "running" = timed out, call again passing the returned cursor; "needs_approval" = Sai is paused on `approval` \u2014 if approval.isLinkOnly is false decide with sai_task_approve, if true give approval.approvalUrl to the human and call sai_task_wait again; "idle" = done, `text` is Sai\'s answer; "error" = failed, see `error`. `text` is the assistant text of the whole turn so far; `events` are only the frames since `cursor`. ' + NO_SCREENSHOT,
27742
27757
  sai_task_approve: 'Resolve a pending approval from a needs_approval result. decision: approve | approve_for_task | deny. For approvalType "choice" pass `selections` with the chosen option values. Returns { ok: true } or, when the approval can only be completed by a human in a browser (OAuth consent, credentials, phone verification), { ok: false, reason: "link_only", approval_url } \u2014 hand approval_url to the human, then keep waiting. Only approve actions the user has actually asked for; when in doubt, ask the human.',
27743
27758
  sai_task_abort: "Stop the task running in this session. Returns { ok, aborted, reason? }; aborted: false with a reason means nothing was running. Safe to call twice.",
@@ -27840,10 +27855,11 @@ var SaiTools = class {
27840
27855
  push: this.ctx.push
27841
27856
  };
27842
27857
  }
27843
- async wait(args) {
27858
+ async wait(args, opts = {}) {
27844
27859
  const result = await this.ctx.source.wait(args.session_id, {
27845
27860
  timeoutMs: clampTimeoutS(args.timeout_s) * 1e3,
27846
- ...args.cursor !== void 0 && { cursor: args.cursor }
27861
+ ...args.cursor !== void 0 && { cursor: args.cursor },
27862
+ ...opts.returnOnEvents && { returnOnEvents: true }
27847
27863
  });
27848
27864
  return this.decorate(result);
27849
27865
  }
@@ -27975,6 +27991,273 @@ function watchSession(sessionId, deps) {
27975
27991
  })();
27976
27992
  }
27977
27993
 
27994
+ // src/app-view.ts
27995
+ var TASK_VIEW_URI = "ui://sai/task-view";
27996
+ var MCP_APP_MIME = "text/html;profile=mcp-app";
27997
+ var TASK_VIEW_TOOL_META = {
27998
+ ui: { resourceUri: TASK_VIEW_URI },
27999
+ "ui/resourceUri": TASK_VIEW_URI
28000
+ };
28001
+ function taskViewHtml() {
28002
+ return true ? '<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1" />\n <title>Sai task</title>\n <style>\n :root {\n --bg: #fff;\n --fg: #1b1b1b;\n --muted: #6b6b6b;\n --line: #e5e5e5;\n --card: #f6f6f4;\n --accent: #1b1b1b;\n --accent-fg: #fff;\n --ok: #2f9e44;\n --warn: #e8a317;\n --err: #d64545;\n }\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\'light\']) {\n --bg: #161616;\n --fg: #eee;\n --muted: #9a9a9a;\n --line: #2c2c2c;\n --card: #1f1f1f;\n --accent: #eee;\n --accent-fg: #111;\n }\n }\n :root[data-theme=\'dark\'] {\n --bg: #161616;\n --fg: #eee;\n --muted: #9a9a9a;\n --line: #2c2c2c;\n --card: #1f1f1f;\n --accent: #eee;\n --accent-fg: #111;\n }\n * {\n box-sizing: border-box;\n }\n body {\n margin: 0;\n padding: 14px 16px;\n font:\n 13.5px/1.5 system-ui,\n -apple-system,\n sans-serif;\n color: var(--fg);\n background: var(--bg);\n }\n header {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .brand {\n font-weight: 600;\n }\n .modes {\n margin-left: auto;\n display: flex;\n gap: 6px;\n }\n button.mode {\n padding: 2px 8px;\n font-size: 12px;\n }\n /* Picture-in-picture: just the screen, the status and any approval. */\n [data-mode=\'pip\'] #steps,\n [data-mode=\'pip\'] #steps-more,\n [data-mode=\'pip\'] #answer,\n [data-mode=\'pip\'] #live {\n display: none;\n }\n [data-mode=\'pip\'] body {\n padding: 8px 10px;\n }\n #headline {\n color: var(--muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n }\n .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--muted);\n flex: none;\n }\n .dot.running,\n .dot.starting {\n background: var(--ok);\n animation: pulse 1.2s ease-in-out infinite;\n }\n .dot.needs_approval {\n background: var(--warn);\n }\n .dot.error {\n background: var(--err);\n }\n @keyframes pulse {\n 50% {\n opacity: 0.35;\n }\n }\n ol {\n margin: 10px 0 0;\n padding-left: 20px;\n color: var(--muted);\n }\n ol li:last-child {\n color: var(--fg);\n }\n #steps-more {\n color: var(--muted);\n font-size: 12px;\n margin-top: 4px;\n }\n .card {\n margin-top: 12px;\n padding: 10px 12px;\n border: 1px solid var(--line);\n border-radius: 10px;\n background: var(--card);\n }\n #approval-text,\n #answer,\n #error {\n white-space: pre-wrap;\n }\n #error {\n border-color: var(--err);\n }\n .row {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 8px;\n }\n button {\n font: inherit;\n padding: 5px 10px;\n border-radius: 7px;\n border: 1px solid var(--line);\n background: var(--bg);\n color: var(--fg);\n cursor: pointer;\n }\n button.primary {\n background: var(--accent);\n color: var(--accent-fg);\n border-color: var(--accent);\n }\n #live {\n margin-top: 12px;\n }\n /* Opened in a browser tab rather than inside a host\'s chat. */\n .standalone body {\n max-width: 960px;\n margin: 24px auto;\n }\n #answer code {\n font:\n 12px ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--line);\n }\n #steps .detail {\n display: block;\n color: var(--muted);\n font-size: 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #screen-wrap {\n margin-top: 10px;\n }\n /* Guacamole sizes its own layers; the box keeps 16:9 before the first frame. */\n #screen {\n width: 100%;\n aspect-ratio: 16 / 9;\n background: #0b0d12;\n border-radius: 8px;\n overflow: hidden;\n }\n #screen img {\n width: 100%;\n display: block;\n }\n #screen-note {\n margin-top: 4px;\n color: var(--muted);\n font-size: 12px;\n }\n </style>\n </head>\n <body>\n <header>\n <span id="dot" class="dot starting"></span><span class="brand">Sai</span\n ><span id="headline">Starting Sai\u2026</span>\n <span class="modes">\n <button id="pin" class="mode" hidden title="Keep this view on screen while you scroll">\n Keep visible\n </button>\n <button id="expand" class="mode" hidden title="Open full screen">Expand</button>\n <button\n id="popout"\n class="mode"\n hidden\n title="Watch in its own window while you keep chatting"\n >\n Open in a window \u2197\n </button>\n <button id="restore" class="mode" hidden title="Back into the conversation">\n Back to chat\n </button>\n </span>\n </header>\n <div id="screen-wrap" hidden>\n <div id="screen"></div>\n <div id="screen-note" hidden></div>\n <button id="watch-here" hidden title="Take the live screen back to this view">\n Watch here\n </button>\n </div>\n <ol id="steps"></ol>\n <div id="steps-more"></div>\n <div id="approval" class="card" hidden>\n <strong id="approval-title"></strong>\n <div id="approval-text"></div>\n <div class="row">\n <button id="approve" class="primary">Approve</button>\n <button id="approve-task">Approve for this task</button>\n <button id="deny">Deny</button>\n <button id="open-approval" class="primary" hidden>Open to finish</button>\n </div>\n </div>\n <div id="error" class="card" hidden></div>\n <div id="answer" class="card" hidden></div>\n <button id="live" hidden>Open in the Playground \u2197</button>\n <script>\n "use strict";(()=>{function Ee(r){try{return["http:","https:"].includes(new URL(r).protocol)}catch{return!1}}var ae=class{constructor(e=window.parent){this.host=e;window.addEventListener("message",t=>this.receive(t))}host;nextId=1;pending=new Map;onToolInput;onToolResult;onHostContext;receive(e){if(e.source!==this.host)return;let t=e.data;if(!t||t.jsonrpc!=="2.0")return;if(typeof t.id=="number"&&("result"in t||"error"in t)){let s=this.pending.get(t.id);if(!s)return;this.pending.delete(t.id);let o=t.error;o?s.reject(new Error(o.message??"Host error")):s.resolve(t.result);return}let a=t.params??{};t.method==="ui/notifications/tool-input"?this.onToolInput?.(a.arguments??{}):t.method==="ui/notifications/tool-result"?this.onToolResult?.(a):t.method==="ui/notifications/host-context-changed"&&this.onHostContext?.(a),t.id!==void 0&&typeof t.method=="string"&&this.host.postMessage({jsonrpc:"2.0",id:t.id,result:{}},"*")}request(e,t){let a=this.nextId++;return new Promise((s,o)=>{this.pending.set(a,{resolve:s,reject:o}),this.host.postMessage({jsonrpc:"2.0",id:a,method:e,params:t},"*")})}notify(e,t){this.host.postMessage({jsonrpc:"2.0",method:e,params:t},"*")}async connect(e){let t=await this.request("ui/initialize",{protocolVersion:"2026-01-26",appInfo:{name:"Sai task",version:e},appCapabilities:{availableDisplayModes:["inline","pip","fullscreen"]}});return this.notify("ui/notifications/initialized",{}),t}async callTool(e,t){return await this.request("tools/call",{name:e,arguments:t})}openLink(e){Ee(e)&&this.request("ui/open-link",{url:e}).catch(()=>{})}async requestDisplayMode(e){return(await this.request("ui/request-display-mode",{mode:e})).mode??e}sizeChanged(e){this.notify("ui/notifications/size-changed",{width:document.body.scrollWidth,height:e})}},oe=class{onToolInput;onToolResult;onHostContext;params=new URLSearchParams(location.search);token=this.params.get("t")??"";async connect(e){let t=this.params.get("s"),a=this.params.get("m"),s=this.params.get("k");return t&&queueMicrotask(()=>this.onToolResult?.({structuredContent:{session_id:t,...a&&{machine_id:a},...s&&{task_id:s}}})),{}}async callTool(e,t){let a=await fetch("/tool",{method:"POST",headers:{"Content-Type":"application/json","x-sai-token":this.token},body:JSON.stringify({name:e,arguments:t})});return a.ok?await a.json():{isError:!0,content:[{type:"text",text:`HTTP ${a.status}`}]}}openLink(e){Ee(e)&&window.open(e,"_blank","noopener,noreferrer")}async requestDisplayMode(e){return"inline"}sizeChanged(e){}};var n=n||{};n.ArrayBufferReader=function(r){var e=this;r.onblob=function(t){var a,s;if(Uint8Array.fromBase64)s=Uint8Array.fromBase64(t),a=s.buffer;else{var o=window.atob(t);a=new ArrayBuffer(o.length),s=new Uint8Array(a);for(var d=0;d<o.length;d++)s[d]=o.charCodeAt(d)}e.ondata&&e.ondata(a)},r.onend=function(){e.onend&&e.onend()},this.ondata=null,this.onend=null};var n=n||{};n.ArrayBufferWriter=function(r){var e=this;r.onack=function(a){e.onack&&e.onack(a)};function t(a){for(var s="",o=0;o<a.byteLength;o++)s+=String.fromCharCode(a[o]);r.sendBlob(window.btoa(s))}this.blobLength=n.ArrayBufferWriter.DEFAULT_BLOB_LENGTH,this.sendData=function(a){var s=new Uint8Array(a);if(s.length<=e.blobLength)t(s);else for(var o=0;o<s.length;o+=e.blobLength)t(s.subarray(o,o+e.blobLength))},this.sendEnd=function(){r.sendEnd()},this.onack=null};n.ArrayBufferWriter.DEFAULT_BLOB_LENGTH=6048;var n=n||{};n.AudioContextFactory={singleton:null,getAudioContext:function(){var e=window.AudioContext||window.webkitAudioContext;if(e)try{return n.AudioContextFactory.singleton||(n.AudioContextFactory.singleton=new e),n.AudioContextFactory.singleton}catch{}return null}};var n=n||{};n.AudioPlayer=function(){this.sync=function(){}};n.AudioPlayer.isSupportedType=function(e){return n.RawAudioPlayer.isSupportedType(e)};n.AudioPlayer.getSupportedTypes=function(){return n.RawAudioPlayer.getSupportedTypes()};n.AudioPlayer.getInstance=function(e,t){return n.RawAudioPlayer.isSupportedType(t)?new n.RawAudioPlayer(e,t):null};n.RawAudioPlayer=function(e,t){var a=n.RawAudioFormat.parse(t),s=n.AudioContextFactory.getAudioContext(),o=s.currentTime,d=new n.ArrayBufferReader(e),c=.02,x=.3,u=a.bytesPerSample===1?window.Int8Array:window.Int16Array,y=a.bytesPerSample===1?128:32768,w=[],T=function(v){if(v.length<=1)return v[0];var g=0;v.forEach(function(R){g+=R.length});var p=0,m=new u(g);return v.forEach(function(R){m.set(R,p),p+=R.length}),m},k=function(v){for(var g=Number.MAX_VALUE,p=v.length,m=Math.floor(v.length/a.channels),S=Math.floor(a.rate*c),R=Math.max(a.channels*S,a.channels*(m-S)),M=R;M<v.length;M+=a.channels){for(var O=0,F=0;F<a.channels;F++)O+=Math.abs(v[M+F]);O<=g&&(p=M+a.channels,g=O)}return p===v.length?[v]:[new u(v.buffer.slice(0,p*a.bytesPerSample)),new u(v.buffer.slice(p*a.bytesPerSample))]},_=function(v){w.push(new u(v))},P=function(){var v=T(w);return v?(w=k(v),v=w.shift(),v):null},b=function(v){var g=v.length/a.channels,p=s.currentTime;o<p&&(o=p);for(var m=s.createBuffer(a.channels,g,a.rate),S=0;S<a.channels;S++)for(var R=m.getChannelData(S),M=S,O=0;O<g;O++)R[O]=v[M]/y,M+=a.channels;return m};d.ondata=function(v){_(new u(v));var g=P();if(g){var p=s.currentTime;o<p&&(o=p);var m=s.createBufferSource();m.connect(s.destination),m.start||(m.start=m.noteOn),m.buffer=b(g),m.start(o),o+=g.length/a.channels/a.rate}},this.sync=function(){var v=s.currentTime;o=Math.min(o,v+x)}};n.RawAudioPlayer.prototype=new n.AudioPlayer;n.RawAudioPlayer.isSupportedType=function(e){return n.AudioContextFactory.getAudioContext()?n.RawAudioFormat.parse(e)!==null:!1};n.RawAudioPlayer.getSupportedTypes=function(){return n.AudioContextFactory.getAudioContext()?["audio/L8","audio/L16"]:[]};var n=n||{};n.AudioRecorder=function(){this.onclose=null,this.onerror=null};n.AudioRecorder.isSupportedType=function(e){return n.RawAudioRecorder.isSupportedType(e)};n.AudioRecorder.getSupportedTypes=function(){return n.RawAudioRecorder.getSupportedTypes()};n.AudioRecorder.getInstance=function(e,t){return n.RawAudioRecorder.isSupportedType(t)?new n.RawAudioRecorder(e,t):null};n.RawAudioRecorder=function(e,t){var a=this,s=2048,o=3,d=n.RawAudioFormat.parse(t),c=n.AudioContextFactory.getAudioContext();navigator.mediaDevices||(navigator.mediaDevices={}),navigator.mediaDevices.getUserMedia||(navigator.mediaDevices.getUserMedia=(navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia).bind(navigator));var x=new n.ArrayBufferWriter(e),u=d.bytesPerSample===1?window.Int8Array:window.Int16Array,y=d.bytesPerSample===1?128:32768,w=0,T=0,k=null,_=null,P=null,b=function(O){if(O===0)return 1;var F=Math.PI*O;return Math.sin(F)/F},l=function(O,F){return-F<O&&O<F?b(O)*b(O/F):0},v=function(O,F){for(var H=(O.length-1)*F,i=Math.floor(H)-o+1,f=Math.floor(H)+o,h=0,C=i;C<=f;C++)h+=(O[C]||0)*l(H-C,o);return h},g=function(O){var F=O.length;w+=F;var H=Math.round(w*d.rate/O.sampleRate),i=H-T;T+=i;for(var f=new u(i*d.channels),h=0;h<d.channels;h++)for(var C=O.getChannelData(h),A=h,L=0;L<i;L++)f[A]=v(C,L/(i-1))*y,A+=d.channels;return f},p=function(O){P=c.createScriptProcessor(s,d.channels,d.channels),P.connect(c.destination),P.onaudioprocess=function(H){x.sendData(g(H.inputBuffer).buffer)},_=c.createMediaStreamSource(O),_.connect(P),c.state==="suspended"&&c.resume(),k=O},m=function(){x.sendEnd(),a.onerror&&a.onerror()},S=function(){var O=navigator.mediaDevices.getUserMedia({audio:!0},p,m);O&&O.then&&O.then(p,m)},R=function(){if(_&&_.disconnect(),P&&P.disconnect(),k)for(var O=k.getTracks(),F=0;F<O.length;F++)O[F].stop();P=null,_=null,k=null,x.sendEnd()};x.onack=function(O){O.code===n.Status.Code.SUCCESS&&!k?S():(R(),x.onack=null,O.code===n.Status.Code.RESOURCE_CLOSED?a.onclose&&a.onclose():a.onerror&&a.onerror())}};n.RawAudioRecorder.prototype=new n.AudioRecorder;n.RawAudioRecorder.isSupportedType=function(e){return n.AudioContextFactory.getAudioContext()?n.RawAudioFormat.parse(e)!==null:!1};n.RawAudioRecorder.getSupportedTypes=function(){return n.AudioContextFactory.getAudioContext()?["audio/L8","audio/L16"]:[]};var n=n||{};n.BlobReader=function(r,e){var t=this,a=0,s;window.BlobBuilder?s=new BlobBuilder:window.WebKitBlobBuilder?s=new WebKitBlobBuilder:window.MozBlobBuilder?s=new MozBlobBuilder:s=new(function(){var o=[];this.append=function(d){o.push(new Blob([d],{type:e}))},this.getBlob=function(){return new Blob(o,{type:e})}}),r.onblob=function(o){for(var d=window.atob(o),c=new ArrayBuffer(d.length),x=new Uint8Array(c),u=0;u<d.length;u++)x[u]=d.charCodeAt(u);s.append(c),a+=c.byteLength,t.onprogress&&t.onprogress(c.byteLength),r.sendAck("OK",0)},r.onend=function(){t.onend&&t.onend()},this.getLength=function(){return a},this.getBlob=function(){return s.getBlob()},this.onprogress=null,this.onend=null};var n=n||{};n.BlobWriter=function(e){var t=this,a=new n.ArrayBufferWriter(e);a.onack=function(o){t.onack&&t.onack(o)};var s=function(d,c,x){var u=(d.slice||d.webkitSlice||d.mozSlice).bind(d),y=x-c;if(y!==x){var w=u(c,y);if(w.size===y)return w}return u(c,x)};this.sendBlob=function(d){var c=0,x=function u(){if(c>=d.size){t.oncomplete&&t.oncomplete(d);return}var y=s(d,c,c+a.blobLength);c+=a.blobLength;var w=new FileReader;w.onload=function(){a.sendData(w.result),a.onack=function(_){t.onack&&t.onack(_),!_.isError()&&(t.onprogress&&t.onprogress(d,c-a.blobLength),u())}},w.onerror=function(){t.onerror&&t.onerror(d,c,w.error)},w.readAsArrayBuffer(y)};x()},this.sendEnd=function(){a.sendEnd()},this.onack=null,this.onerror=null,this.onprogress=null,this.oncomplete=null};var n=n||{};n.Client=function(r){var e=this,t=n.Client.State.IDLE,a=0,s=5e3,o=null,d=0,c={0:"butt",1:"round",2:"square"},x={0:"bevel",1:"miter",2:"round"},u=new n.Display,y={},w={},T={},k=[],_=[],P=[],b=new n.IntegerPool,l=[];function v(i){i!=t&&(t=i,e.onstatechange&&e.onstatechange(t))}function g(){return t==n.Client.State.CONNECTED||t==n.Client.State.WAITING}this.exportState=function(f){var h={currentState:t,currentTimestamp:a,layers:{}},C={};for(var A in y)C[A]=y[A];u.flush(function(){for(var I in C){var E=parseInt(I),D=C[I],B={width:D.width,height:D.height};if(D.width&&D.height){var Y=D.toCanvas();B.url=Y.toDataURL("image/png")}E>0&&(B.x=D.x,B.y=D.y,B.z=D.z,B.alpha=D.alpha,B.matrix=D.matrix,B.parent=m(D.parent)),h.layers[I]=B}f(h)})},this.importState=function(f,h){var C,A;t=f.currentState,a=f.currentTimestamp,u.cancel();for(C in y)A=parseInt(C),A>0&&y[C].dispose();y={};for(C in f.layers){A=parseInt(C);var L=f.layers[C],I=p(A);if(u.resize(I,L.width,L.height),L.url&&(u.setChannelMask(I,n.Layer.SRC),u.draw(I,0,0,L.url)),A>0&&L.parent>=0){var E=p(L.parent);u.move(I,E,L.x,L.y,L.z),u.shade(I,L.alpha);var D=L.matrix;u.distort(I,D[0],D[1],D[2],D[3],D[4],D[5])}}u.flush(h)},this.getDisplay=function(){return u},this.sendSize=function(i,f){g()&&r.sendMessage("size",i,f)},this.sendKeyEvent=function(i,f){g()&&r.sendMessage("key",f,i)},this.sendMouseState=function(f,h){if(g()){var C=f.x,A=f.y;h&&(C/=u.getScale(),A/=u.getScale()),u.moveCursor(Math.floor(C),Math.floor(A));var L=0;f.left&&(L|=1),f.middle&&(L|=2),f.right&&(L|=4),f.up&&(L|=8),f.down&&(L|=16),r.sendMessage("mouse",Math.floor(C),Math.floor(A),L)}},this.sendTouchState=function(f,h){if(g()){var C=f.x,A=f.y;h&&(C/=u.getScale(),A/=u.getScale()),r.sendMessage("touch",f.id,Math.floor(C),Math.floor(A),Math.floor(f.radiusX),Math.floor(f.radiusY),f.angle,f.force)}},this.createOutputStream=function(){var f=b.next(),h=l[f]=new n.OutputStream(e,f);return h},this.createAudioStream=function(i){var f=e.createOutputStream();return r.sendMessage("audio",f.index,i),f},this.createFileStream=function(i,f){var h=e.createOutputStream();return r.sendMessage("file",h.index,i,f),h},this.createPipeStream=function(i,f){var h=e.createOutputStream();return r.sendMessage("pipe",h.index,i,f),h},this.createClipboardStream=function(i){var f=e.createOutputStream();return r.sendMessage("clipboard",f.index,i),f},this.createArgumentValueStream=function(f,h){var C=e.createOutputStream();return r.sendMessage("argv",C.index,f,h),C},this.createObjectOutputStream=function(f,h,C){var A=e.createOutputStream();return r.sendMessage("put",f,A.index,h,C),A},this.requestObjectInputStream=function(f,h){g()&&r.sendMessage("get",f,h)},this.sendAck=function(i,f,h){g()&&r.sendMessage("ack",i,f,h)},this.sendBlob=function(i,f){g()&&r.sendMessage("blob",i,f)},this.endStream=function(i){g()&&(r.sendMessage("end",i),l[i]&&(b.free(i),delete l[i]))},this.onstatechange=null,this.onname=null,this.onerror=null,this.onmsg=null,this.onjoin=null,this.onleave=null,this.onaudio=null,this.onvideo=null,this.onmultitouch=null,this.onargv=null,this.onclipboard=null,this.onfile=null,this.onfilesystem=null,this.onpipe=null,this.onrequired=null,this.onsync=null;var p=function(f){var h=y[f];return h||(f===0?h=u.getDefaultLayer():f>0?h=u.createLayer():h=u.createBuffer(),y[f]=h),h},m=function(f){if(!f)return null;for(var h in y)if(f===y[h])return parseInt(h);return null};function S(i){var f=k[i];return f==null&&(f=k[i]=new n.Parser,f.oninstruction=r.oninstruction),f}var R={"miter-limit":function(i,f){u.setMiterLimit(i,parseFloat(f))},"multi-touch":function(f,h){e.onmultitouch&&f instanceof n.Display.VisibleLayer&&e.onmultitouch(f,parseInt(h))}},M={ack:function(i){var f=parseInt(i[0]),h=i[1],C=parseInt(i[2]),A=l[f];A&&(A.onack&&A.onack(new n.Status(C,h)),C>=256&&l[f]===A&&(b.free(f),delete l[f]))},arc:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseFloat(i[4]),I=parseFloat(i[5]),E=parseInt(i[6]);u.arc(f,h,C,A,L,I,E!=0)},argv:function(i){var f=parseInt(i[0]),h=i[1],C=i[2];if(e.onargv){var A=_[f]=new n.InputStream(e,f);e.onargv(A,h,C)}else e.sendAck(f,"Receiving argument values unsupported",256)},audio:function(i){var f=parseInt(i[0]),h=i[1],C=_[f]=new n.InputStream(e,f),A=null;e.onaudio&&(A=e.onaudio(C,h)),A||(A=n.AudioPlayer.getInstance(C,h)),A?(w[f]=A,e.sendAck(f,"OK",0)):e.sendAck(f,"BAD TYPE",783)},blob:function(i){var f=parseInt(i[0]),h=i[1],C=_[f];C&&C.onblob&&C.onblob(h)},body:function(f){var h=parseInt(f[0]),C=P[h],A=parseInt(f[1]),L=f[2],I=f[3];if(C&&C.onbody){var E=_[A]=new n.InputStream(e,A);C.onbody(E,L,I)}else e.sendAck(A,"Receipt of body unsupported",256)},cfill:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]),I=parseInt(i[5]);u.setChannelMask(h,f),u.fillColor(h,C,A,L,I)},clip:function(i){var f=p(parseInt(i[0]));u.clip(f)},clipboard:function(i){var f=parseInt(i[0]),h=i[1];if(e.onclipboard){var C=_[f]=new n.InputStream(e,f);e.onclipboard(C,h)}else e.sendAck(f,"Clipboard unsupported",256)},close:function(i){var f=p(parseInt(i[0]));u.close(f)},copy:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]),I=parseInt(i[5]),E=p(parseInt(i[6])),D=parseInt(i[7]),B=parseInt(i[8]);u.setChannelMask(E,I),u.copy(f,h,C,A,L,E,D,B)},cstroke:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=c[parseInt(i[2])],A=x[parseInt(i[3])],L=parseInt(i[4]),I=parseInt(i[5]),E=parseInt(i[6]),D=parseInt(i[7]),B=parseInt(i[8]);u.setChannelMask(h,f),u.strokeColor(h,C,A,L,I,E,D,B)},cursor:function(i){var f=parseInt(i[0]),h=parseInt(i[1]),C=p(parseInt(i[2])),A=parseInt(i[3]),L=parseInt(i[4]),I=parseInt(i[5]),E=parseInt(i[6]);u.setCursor(f,h,C,A,L,I,E)},curve:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]),I=parseInt(i[5]),E=parseInt(i[6]);u.curveTo(f,h,C,A,L,I,E)},disconnect:function(f){e.disconnect()},dispose:function(i){var f=parseInt(i[0]);if(f>0){var h=p(f);u.dispose(h),delete y[f]}else f<0&&delete y[f]},distort:function(i){var f=parseInt(i[0]),h=parseFloat(i[1]),C=parseFloat(i[2]),A=parseFloat(i[3]),L=parseFloat(i[4]),I=parseFloat(i[5]),E=parseFloat(i[6]);if(f>=0){var D=p(f);u.distort(D,h,C,A,L,I,E)}},error:function(i){var f=i[0],h=parseInt(i[1]);e.onerror&&e.onerror(new n.Status(h,f)),e.disconnect()},end:function(i){var f=parseInt(i[0]),h=_[f];h&&(h.onend&&h.onend(),delete _[f])},file:function(i){var f=parseInt(i[0]),h=i[1],C=i[2];if(e.onfile){var A=_[f]=new n.InputStream(e,f);e.onfile(A,h,C)}else e.sendAck(f,"File transfer unsupported",256)},filesystem:function(f){var h=parseInt(f[0]),C=f[1];if(e.onfilesystem){var A=P[h]=new n.Object(e,h);e.onfilesystem(A,C)}},identity:function(i){var f=p(parseInt(i[0]));u.setTransform(f,1,0,0,1,0,0)},img:function(i){var f=parseInt(i[0]),h=parseInt(i[1]),C=p(parseInt(i[2])),A=i[3],L=parseInt(i[4]),I=parseInt(i[5]),E=_[f]=new n.InputStream(e,f);u.setChannelMask(C,h),u.drawStream(C,L,I,E,A)},jpeg:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=parseInt(i[2]),A=parseInt(i[3]),L=i[4];u.setChannelMask(h,f),u.draw(h,C,A,"data:image/jpeg;base64,"+L)},lfill:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=p(parseInt(i[2]));u.setChannelMask(h,f),u.fillLayer(h,C)},line:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]);u.lineTo(f,h,C)},lstroke:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=p(parseInt(i[2]));u.setChannelMask(h,f),u.strokeLayer(h,C)},mouse:function(f){var h=parseInt(f[0]),C=parseInt(f[1]);u.showCursor(!0),u.moveCursor(h,C)},move:function(i){var f=parseInt(i[0]),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]);if(f>0&&h>=0){var I=p(f),E=p(h);u.move(I,E,C,A,L)}},msg:function(i){var f,h,C=!0,A=parseInt(i[0]);if(e.onmsg&&(C=e.onmsg(A,i.slice(1)),C===void 0&&(C=!0)),C)switch(A){case n.Client.Message.USER_JOINED:f=i[1],h=i[2],e.onjoin&&e.onjoin(f,h);break;case n.Client.Message.USER_LEFT:f=i[1],h=i[2],e.onleave&&e.onleave(f,h);break}},name:function(i){e.onname&&e.onname(i[0])},nest:function(i){var f=S(parseInt(i[0]));f.receive(i[1])},pipe:function(i){var f=parseInt(i[0]),h=i[1],C=i[2];if(e.onpipe){var A=_[f]=new n.InputStream(e,f);e.onpipe(A,h,C)}else e.sendAck(f,"Named pipes unsupported",256)},png:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=parseInt(i[2]),A=parseInt(i[3]),L=i[4];u.setChannelMask(h,f),u.draw(h,C,A,"data:image/png;base64,"+L)},pop:function(i){var f=p(parseInt(i[0]));u.pop(f)},push:function(i){var f=p(parseInt(i[0]));u.push(f)},rect:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]);u.rect(f,h,C,A,L)},required:function(f){e.onrequired&&e.onrequired(f)},reset:function(i){var f=p(parseInt(i[0]));u.reset(f)},set:function(i){var f=p(parseInt(i[0])),h=i[1],C=i[2],A=R[h];A&&A(f,C)},shade:function(i){var f=parseInt(i[0]),h=parseInt(i[1]);if(f>=0){var C=p(f);u.shade(C,h)}},size:function(i){var f=parseInt(i[0]),h=p(f),C=parseInt(i[1]),A=parseInt(i[2]);u.resize(h,C,A)},start:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]);u.moveTo(f,h,C)},sync:function(i){var f=parseInt(i[0]),h=i[1]?parseInt(i[1]):0;u.flush(function(){for(var A in w){var L=w[A];L&&L.sync()}f!==a&&(r.sendMessage("sync",f),a=f)},f,h),t===n.Client.State.WAITING&&v(n.Client.State.CONNECTED),e.onsync&&e.onsync(f,h)},transfer:function(i){var f=p(parseInt(i[0])),h=parseInt(i[1]),C=parseInt(i[2]),A=parseInt(i[3]),L=parseInt(i[4]),I=parseInt(i[5]),E=p(parseInt(i[6])),D=parseInt(i[7]),B=parseInt(i[8]);I===3?u.put(f,h,C,A,L,E,D,B):I!==5&&u.transfer(f,h,C,A,L,E,D,B,n.Client.DefaultTransferFunction[I])},transform:function(i){var f=p(parseInt(i[0])),h=parseFloat(i[1]),C=parseFloat(i[2]),A=parseFloat(i[3]),L=parseFloat(i[4]),I=parseFloat(i[5]),E=parseFloat(i[6]);u.transform(f,h,C,A,L,I,E)},undefine:function(f){var h=parseInt(f[0]),C=P[h];C&&C.onundefine&&C.onundefine()},video:function(i){var f=parseInt(i[0]),h=p(parseInt(i[1])),C=i[2],A=_[f]=new n.InputStream(e,f),L=null;e.onvideo&&(L=e.onvideo(A,h,C)),L||(L=n.VideoPlayer.getInstance(A,h,C)),L?(T[f]=L,e.sendAck(f,"OK",0)):e.sendAck(f,"BAD TYPE",783)}},O=function(){r.sendMessage("nop"),d=new Date().getTime()},F=function(){window.clearTimeout(o);var f=new Date().getTime(),h=Math.max(d+s-f,0);h>0?o=window.setTimeout(O,h):O()},H=function(){window.clearTimeout(o)};r.oninstruction=function(i,f){var h=M[i];h&&h(f),F()},this.disconnect=function(){t!=n.Client.State.DISCONNECTED&&t!=n.Client.State.DISCONNECTING&&(v(n.Client.State.DISCONNECTING),H(),r.sendMessage("disconnect"),r.disconnect(),v(n.Client.State.DISCONNECTED))},this.connect=function(i){v(n.Client.State.CONNECTING);try{r.connect(i)}catch(f){throw v(n.Client.State.IDLE),f}F(),v(n.Client.State.WAITING)}};n.Client.State={IDLE:0,CONNECTING:1,WAITING:2,CONNECTED:3,DISCONNECTING:4,DISCONNECTED:5};n.Client.DefaultTransferFunction={0:function(r,e){e.red=e.green=e.blue=0},15:function(r,e){e.red=e.green=e.blue=255},3:function(r,e){e.red=r.red,e.green=r.green,e.blue=r.blue,e.alpha=r.alpha},5:function(r,e){},12:function(r,e){e.red=255&~r.red,e.green=255&~r.green,e.blue=255&~r.blue,e.alpha=r.alpha},10:function(r,e){e.red=255&~e.red,e.green=255&~e.green,e.blue=255&~e.blue},1:function(r,e){e.red=r.red&e.red,e.green=r.green&e.green,e.blue=r.blue&e.blue},14:function(r,e){e.red=255&~(r.red&e.red),e.green=255&~(r.green&e.green),e.blue=255&~(r.blue&e.blue)},7:function(r,e){e.red=r.red|e.red,e.green=r.green|e.green,e.blue=r.blue|e.blue},8:function(r,e){e.red=255&~(r.red|e.red),e.green=255&~(r.green|e.green),e.blue=255&~(r.blue|e.blue)},6:function(r,e){e.red=r.red^e.red,e.green=r.green^e.green,e.blue=r.blue^e.blue},9:function(r,e){e.red=255&~(r.red^e.red),e.green=255&~(r.green^e.green),e.blue=255&~(r.blue^e.blue)},4:function(r,e){e.red=255&(~r.red&e.red),e.green=255&(~r.green&e.green),e.blue=255&(~r.blue&e.blue)},13:function(r,e){e.red=255&(~r.red|e.red),e.green=255&(~r.green|e.green),e.blue=255&(~r.blue|e.blue)},2:function(r,e){e.red=255&(r.red&~e.red),e.green=255&(r.green&~e.green),e.blue=255&(r.blue&~e.blue)},11:function(r,e){e.red=255&(r.red|~e.red),e.green=255&(r.green|~e.green),e.blue=255&(r.blue|~e.blue)}};n.Client.Message={USER_JOINED:1,USER_LEFT:2};var n=n||{};n.DataURIReader=function(r,e){var t=this,a="data:"+e+";base64,";r.onblob=function(o){a+=o},r.onend=function(){t.onend&&t.onend()},this.getURI=function(){return a},this.onend=null};var n=n||{};n.Display=function(){var r=this,e=0,t=0,a=1,s=document.createElement("div");s.style.position="relative",s.style.width=e+"px",s.style.height=t+"px",s.style.transformOrigin=s.style.webkitTransformOrigin=s.style.MozTransformOrigin=s.style.OTransformOrigin=s.style.msTransformOrigin="0 0";var o=new n.Display.VisibleLayer(e,t),d=new n.Display.VisibleLayer(0,0);d.setChannelMask(n.Layer.SRC),s.appendChild(o.getElement()),s.appendChild(d.getElement());var c=document.createElement("div");c.style.position="relative",c.style.width=e*a+"px",c.style.height=t*a+"px",c.appendChild(s),this.cursorHotspotX=0,this.cursorHotspotY=0,this.cursorX=0,this.cursorY=0,this.statisticWindow=0,this.onresize=null,this.oncursor=null,this.onstatistics=null;var x=[],u=[],y=function(){for(var v=0,g=0,p=0,m=0;m<u.length;){var S=u[m];if(!S.isReady())break;S.flush(),v=S.localTimestamp,g=S.remoteTimestamp,p+=S.logicalFrames,m++}u.splice(0,m),m&&T(v,g,p)},w=[],T=function(v,g,p){if(r.statisticWindow){for(var m=new Date().getTime(),S=0;S<w.length&&!(m-w[S].timestamp<=r.statisticWindow);S++);w.splice(0,S-1),w.push({localTimestamp:v,remoteTimestamp:g,timestamp:m,frames:p});var R=(w[w.length-1].timestamp-w[0].timestamp)/1e3,M=(w[w.length-1].remoteTimestamp-w[0].remoteTimestamp)/1e3,O=w.length,F=w.reduce(function(h,C){return h+C.frames},0),H=w.reduce(function(h,C){return h+Math.max(0,C.frames-1)},0),i=new n.Display.Statistics({processingLag:m-v,desktopFps:M&&F?F/M:null,clientFps:R?O/R:null,serverFps:M?O/M:null,dropRate:M?H/M:null});r.onstatistics&&r.onstatistics(i)}};function k(){y()}var _=function(v,g,p,m){this.localTimestamp=new Date().getTime(),this.remoteTimestamp=p||this.localTimestamp,this.logicalFrames=m||0,this.cancel=function(){v=null,g.forEach(function(M){M.cancel()}),g=[]},this.isReady=function(){for(var S=0;S<g.length;S++)if(g[S].blocked)return!1;return!0},this.flush=function(){for(var S=0;S<g.length;S++)g[S].execute();v&&v()}};function P(l,v){var g=this;this.blocked=v,this.cancel=function(){g.blocked=!1,l=null},this.unblock=function(){g.blocked&&(g.blocked=!1,u.length&&k())},this.execute=function(){l&&l()}}function b(l,v){var g=new P(l,v);return x.push(g),g}this.getElement=function(){return c},this.getWidth=function(){return e},this.getHeight=function(){return t},this.getDefaultLayer=function(){return o},this.getCursorLayer=function(){return d},this.createLayer=function(){var l=new n.Display.VisibleLayer(e,t);return l.move(o,0,0,0),l},this.createBuffer=function(){var l=new n.Layer(0,0);return l.autosize=1,l},this.flush=function(l,v,g){u.push(new _(l,x,v,g)),x=[],k()},this.cancel=function(){u.forEach(function(g){g.cancel()}),u=[],x.forEach(function(g){g.cancel()}),x=[]},this.setCursor=function(l,v,g,p,m,S,R){b(function(){r.cursorHotspotX=l,r.cursorHotspotY=v,d.resize(S,R),d.copy(g,p,m,S,R,0,0),r.moveCursor(r.cursorX,r.cursorY),r.oncursor&&r.oncursor(d.toCanvas(),l,v)})},this.showCursor=function(l){var v=d.getElement(),g=v.parentNode;l===!1?g&&g.removeChild(v):g!==s&&s.appendChild(v)},this.moveCursor=function(l,v){d.translate(l-r.cursorHotspotX,v-r.cursorHotspotY),r.cursorX=l,r.cursorY=v},this.resize=function(l,v,g){b(function(){l.resize(v,g),l===o&&(e=v,t=g,s.style.width=e+"px",s.style.height=t+"px",c.style.width=e*a+"px",c.style.height=t*a+"px",r.onresize&&r.onresize(v,g))})},this.drawImage=function(l,v,g,p){b(function(){l.drawImage(v,g,p)})},this.drawBlob=function(l,v,g,p){var m;if(window.createImageBitmap){var S;m=b(function(){l.drawImage(v,g,S)},!0),window.createImageBitmap(p).then(function(F){S=F,m.unblock()})}else{var R=URL.createObjectURL(p);m=b(function(){M.width&&M.height&&l.drawImage(v,g,M),URL.revokeObjectURL(R)},!0);var M=new Image;M.onload=m.unblock,M.onerror=m.unblock,M.src=R}},this.drawStream=function(v,g,p,m,S){if(window.ImageDecoder&&window.ReadableStream){var R=new ImageDecoder({type:S,data:m.toReadableStream()}),M=null,O=b(function(){v.drawImage(g,p,M)},!0);R.decode({completeFramesOnly:!0}).then(function(i){M=i.image,O.unblock()})}else{var F=new n.DataURIReader(m,S);F.onend=function(){r.draw(v,g,p,F.getURI())}}},this.draw=function(l,v,g,p){var m=b(function(){S.width&&S.height&&l.drawImage(v,g,S)},!0),S=new Image;S.onload=m.unblock,S.onerror=m.unblock,S.src=p},this.play=function(l,v,g,p){var m=document.createElement("video");m.type=v,m.src=p,m.addEventListener("play",function(){function S(){l.drawImage(0,0,m),m.ended||window.setTimeout(S,20)}S()},!1),b(m.play)},this.transfer=function(l,v,g,p,m,S,R,M,O){b(function(){S.transfer(l,v,g,p,m,R,M,O)})},this.put=function(l,v,g,p,m,S,R,M){b(function(){S.put(l,v,g,p,m,R,M)})},this.copy=function(l,v,g,p,m,S,R,M){b(function(){S.copy(l,v,g,p,m,R,M)})},this.moveTo=function(l,v,g){b(function(){l.moveTo(v,g)})},this.lineTo=function(l,v,g){b(function(){l.lineTo(v,g)})},this.arc=function(l,v,g,p,m,S,R){b(function(){l.arc(v,g,p,m,S,R)})},this.curveTo=function(l,v,g,p,m,S,R){b(function(){l.curveTo(v,g,p,m,S,R)})},this.close=function(l){b(function(){l.close()})},this.rect=function(l,v,g,p,m){b(function(){l.rect(v,g,p,m)})},this.clip=function(l){b(function(){l.clip()})},this.strokeColor=function(l,v,g,p,m,S,R,M){b(function(){l.strokeColor(v,g,p,m,S,R,M)})},this.fillColor=function(l,v,g,p,m){b(function(){l.fillColor(v,g,p,m)})},this.strokeLayer=function(l,v,g,p,m){b(function(){l.strokeLayer(v,g,p,m)})},this.fillLayer=function(l,v){b(function(){l.fillLayer(v)})},this.push=function(l){b(function(){l.push()})},this.pop=function(l){b(function(){l.pop()})},this.reset=function(l){b(function(){l.reset()})},this.setTransform=function(l,v,g,p,m,S,R){b(function(){l.setTransform(v,g,p,m,S,R)})},this.transform=function(l,v,g,p,m,S,R){b(function(){l.transform(v,g,p,m,S,R)})},this.setChannelMask=function(l,v){b(function(){l.setChannelMask(v)})},this.setMiterLimit=function(l,v){b(function(){l.setMiterLimit(v)})},this.dispose=function(v){b(function(){v.dispose()})},this.distort=function(v,g,p,m,S,R,M){b(function(){v.distort(g,p,m,S,R,M)})},this.move=function(v,g,p,m,S){b(function(){v.move(g,p,m,S)})},this.shade=function(v,g){b(function(){v.shade(g)})},this.scale=function(l){s.style.transform=s.style.WebkitTransform=s.style.MozTransform=s.style.OTransform=s.style.msTransform="scale("+l+","+l+")",a=l,c.style.width=e*a+"px",c.style.height=t*a+"px"},this.getScale=function(){return a},this.flatten=function(){var l=document.createElement("canvas");l.width=o.width,l.height=o.height;var v=l.getContext("2d");function g(m){var S=[];for(var R in m.children)S.push(m.children[R]);return S.sort(function(O,F){var H=O.z-F.z;if(H!==0)return H;var i=O.getElement(),f=F.getElement(),h=f.compareDocumentPosition(i);return h&Node.DOCUMENT_POSITION_PRECEDING?-1:h&Node.DOCUMENT_POSITION_FOLLOWING?1:0}),S}function p(m,S,R){if(m.width>0&&m.height>0){var M=v.globalAlpha;v.globalAlpha*=m.alpha/255,v.drawImage(m.getCanvas(),S,R);for(var O=g(m),F=0;F<O.length;F++){var H=O[F];p(H,S+H.x,R+H.y)}v.globalAlpha=M}}return p(o,0,0),l}};n.Display.VisibleLayer=function(r,e){n.Layer.apply(this,[r,e]);var t=this;this.__unique_id=n.Display.VisibleLayer.__next_id++,this.alpha=255,this.x=0,this.y=0,this.z=0,this.matrix=[1,0,0,1,0,0],this.parent=null,this.children={};var a=t.getCanvas();a.style.position="absolute",a.style.left="0px",a.style.top="0px";var s=document.createElement("div");s.appendChild(a),s.style.width=r+"px",s.style.height=e+"px",s.style.position="absolute",s.style.left="0px",s.style.top="0px",s.style.overflow="hidden";var o=this.resize;this.resize=function(x,u){s.style.width=x+"px",s.style.height=u+"px",o(x,u)},this.getElement=function(){return s};var d="translate(0px, 0px)",c="matrix(1, 0, 0, 1, 0, 0)";this.translate=function(x,u){t.x=x,t.y=u,d="translate("+x+"px,"+u+"px)",s.style.transform=s.style.WebkitTransform=s.style.MozTransform=s.style.OTransform=s.style.msTransform=d+" "+c},this.move=function(x,u,y,w){if(t.parent!==x){t.parent&&delete t.parent.children[t.__unique_id],t.parent=x,x.children[t.__unique_id]=t;var T=x.getElement();T.appendChild(s)}t.translate(u,y),t.z=w,s.style.zIndex=w},this.shade=function(x){t.alpha=x,s.style.opacity=x/255},this.dispose=function(){t.parent&&(delete t.parent.children[t.__unique_id],t.parent=null),s.parentNode&&s.parentNode.removeChild(s)},this.distort=function(x,u,y,w,T,k){t.matrix=[x,u,y,w,T,k],c="matrix("+x+","+u+","+y+","+w+","+T+","+k+")",s.style.transform=s.style.WebkitTransform=s.style.MozTransform=s.style.OTransform=s.style.msTransform=d+" "+c}};n.Display.VisibleLayer.__next_id=0;n.Display.Statistics=function(e){e=e||{},this.processingLag=e.processingLag,this.desktopFps=e.desktopFps,this.serverFps=e.serverFps,this.clientFps=e.clientFps,this.dropRate=e.dropRate};var n=n||{};n.Event=function(e){this.type=e,this.timestamp=new Date().getTime(),this.getAge=function(){return new Date().getTime()-this.timestamp},this.invokeLegacyHandler=function(a){}};n.Event.DOMEvent=function(e,t){n.Event.call(this,e),t=t||[],Array.isArray(t)||(t=[t]),this.preventDefault=function(){t.forEach(function(o){o.preventDefault&&o.preventDefault(),o.returnValue=!1})},this.stopPropagation=function(){t.forEach(function(o){o.stopPropagation()})}};n.Event.DOMEvent.cancelEvent=function(e){e.stopPropagation(),e.preventDefault&&e.preventDefault(),e.returnValue=!1};n.Event.Target=function(){var e={};this.on=function(a,s){var o=e[a];o||(e[a]=o=[]),o.push(s)},this.onEach=function(a,s){a.forEach(function(d){this.on(d,s)},this)},this.dispatch=function(a){a.invokeLegacyHandler(this);var s=e[a.type];if(s)for(var o=0;o<s.length;o++)s[o](a,this)},this.off=function(a,s){var o=e[a];if(!o)return!1;for(var d=0;d<o.length;d++)if(o[d]===s)return o.splice(d,1),!0;return!1},this.offEach=function(a,s){var o=!1;return a.forEach(function(c){o|=this.off(c,s)},this),o}};var n=n||{};n.InputSink=function(){var e=this,t=document.createElement("textarea");t.style.position="fixed",t.style.outline="none",t.style.border="none",t.style.margin="0",t.style.padding="0",t.style.height="0",t.style.width="0",t.style.left="0",t.style.bottom="0",t.style.resize="none",t.style.background="transparent",t.style.color="transparent",t.addEventListener("keypress",function(s){t.value=""},!1),t.addEventListener("compositionend",function(s){s.data&&(t.value="")},!1),t.addEventListener("input",function(s){s.data&&!s.isComposing&&(t.value="")},!1),t.addEventListener("focus",function(){window.setTimeout(function(){t.click(),t.select()},0)},!0),this.focus=function(){window.setTimeout(function(){t.focus()},0)},this.getElement=function(){return t},document.addEventListener("keydown",function(s){var o=document.activeElement;if(o&&o!==document.body){var d=o.getBoundingClientRect();if(d.left+d.width>0&&d.top+d.height>0)return}e.focus()},!0)};var n=n||{};n.InputStream=function(r,e){var t=this;this.index=e,this.onblob=null,this.onend=null,this.sendAck=function(a,s){r.sendAck(t.index,a,s)},this.toReadableStream=function(){return new ReadableStream({type:"bytes",start:function(o){var d=new n.ArrayBufferReader(t);d.ondata=function(x){if(o.byobRequest){var u=o.byobRequest.view,y=Math.min(u.byteLength,x.byteLength),w=new Uint8Array(x,0,y);u.buffer.set(w),o.byobRequest.respond(y),y<x.byteLength&&o.enqueue(x.slice(y))}else o.enqueue(new Uint8Array(x))},d.onend=function(){o.close()}}})}};var n=n||{};n.IntegerPool=function(){var r=this,e=[];this.next_int=0,this.next=function(){return e.length>0?e.shift():r.next_int++},this.free=function(t){e.push(t)}};var n=n||{};n.JSONReader=function(e){var t=this,a=new n.StringReader(e),s="";this.getLength=function(){return s.length},this.getJSON=function(){return JSON.parse(s)},a.ontext=function(d){s+=d,t.onprogress&&t.onprogress(d.length)},a.onend=function(){t.onend&&t.onend()},this.onprogress=null,this.onend=null};var n=n||{};n.Keyboard=function(e){var t=this,a=n.Keyboard._nextID++,s="_GUAC_KEYBOARD_HANDLED_BY_"+a;this.onkeydown=null,this.onkeyup=null;var o={keyupUnreliable:!1,altIsTypableOnly:!1,capsLockKeyEventUnreliable:!1};navigator&&navigator.platform&&(navigator.platform.match(/ipad|iphone|ipod/i)?o.keyupUnreliable=!0:navigator.platform.match(/^mac/i)&&(o.altIsTypableOnly=!0,o.capsLockKeyEventUnreliable=!0));var d=function(E){var D=this;this.keyCode=E?E.which||E.keyCode:0,this.keyIdentifier=E&&E.keyIdentifier,this.key=E&&E.key,this.location=E?A(E):0,this.modifiers=E?n.Keyboard.ModifierState.fromKeyboardEvent(E):new n.Keyboard.ModifierState,this.timestamp=new Date().getTime(),this.defaultPrevented=!1,this.keysym=null,this.reliable=!1,this.getAge=function(){return new Date().getTime()-D.timestamp}},c=function(E){d.call(this,E),this.keysym=m(this.key,this.location)||M(this.keyCode,this.location),this.keyupReliable=!o.keyupUnreliable,this.keysym&&!p(this.keysym)&&(this.reliable=!0),!this.keysym&&O(this.keyCode,this.keyIdentifier)&&(this.keysym=m(this.keyIdentifier,this.location,this.modifiers.shift)),this.modifiers.meta&&this.keysym!==65511&&this.keysym!==65512?this.keyupReliable=!1:this.keysym===65509&&o.capsLockKeyEventUnreliable&&(this.keyupReliable=!1);var D=!this.modifiers.ctrl&&!o.altIsTypableOnly;o.altIsTypableOnly&&(this.keysym===65513||this.keysym===65514)&&(this.keysym=65027);var B=!this.modifiers.alt;(B&&this.modifiers.ctrl||D&&this.modifiers.alt||this.modifiers.meta||this.modifiers.hyper)&&(this.reliable=!0),b[this.keyCode]=this.keysym};c.prototype=new d;var x=function(E){d.call(this,E),this.keysym=R(this.keyCode),this.reliable=!0};x.prototype=new d;var u=function(E){if(d.call(this,E),this.keyCode==20&&o.capsLockKeyEventUnreliable){y.push(new c(this));return}this.keysym=M(this.keyCode,this.location)||m(this.key,this.location),t.pressed[this.keysym]||(this.keysym=b[this.keyCode]||this.keysym),this.reliable=!0};u.prototype=new d;var y=[],w={8:[65288],9:[65289],12:[65291,65291,65291,65461],13:[65293],16:[65505,65505,65506],17:[65507,65507,65508],18:[65513,65513,65514],19:[65299],20:[65509],27:[65307],32:[32],33:[65365,65365,65365,65465],34:[65366,65366,65366,65459],35:[65367,65367,65367,65457],36:[65360,65360,65360,65463],37:[65361,65361,65361,65460],38:[65362,65362,65362,65464],39:[65363,65363,65363,65462],40:[65364,65364,65364,65458],45:[65379,65379,65379,65456],46:[65535,65535,65535,65454],91:[65511],92:[65512],93:[65383],96:[65456],97:[65457],98:[65458],99:[65459],100:[65460],101:[65461],102:[65462],103:[65463],104:[65464],105:[65465],106:[65450],107:[65451],109:[65453],110:[65454],111:[65455],112:[65470],113:[65471],114:[65472],115:[65473],116:[65474],117:[65475],118:[65476],119:[65477],120:[65478],121:[65479],122:[65480],123:[65481],144:[65407],145:[65300],225:[65027]},T={Again:[65382],AllCandidates:[65341],Alphanumeric:[65328],Alt:[65513,65513,65514],Attn:[64782],AltGraph:[65027],ArrowDown:[65364],ArrowLeft:[65361],ArrowRight:[65363],ArrowUp:[65362],Backspace:[65288],CapsLock:[65509],Cancel:[65385],Clear:[65291],Convert:[65315],Copy:[64789],Crsel:[64796],CrSel:[64796],CodeInput:[65335],Compose:[65312],Control:[65507,65507,65508],ContextMenu:[65383],Delete:[65535],Down:[65364],End:[65367],Enter:[65293],EraseEof:[64774],Escape:[65307],Execute:[65378],Exsel:[64797],ExSel:[64797],F1:[65470],F2:[65471],F3:[65472],F4:[65473],F5:[65474],F6:[65475],F7:[65476],F8:[65477],F9:[65478],F10:[65479],F11:[65480],F12:[65481],F13:[65482],F14:[65483],F15:[65484],F16:[65485],F17:[65486],F18:[65487],F19:[65488],F20:[65489],F21:[65490],F22:[65491],F23:[65492],F24:[65493],Find:[65384],GroupFirst:[65036],GroupLast:[65038],GroupNext:[65032],GroupPrevious:[65034],FullWidth:null,HalfWidth:null,HangulMode:[65329],Hankaku:[65321],HanjaMode:[65332],Help:[65386],Hiragana:[65317],HiraganaKatakana:[65319],Home:[65360],Hyper:[65517,65517,65518],Insert:[65379],JapaneseHiragana:[65317],JapaneseKatakana:[65318],JapaneseRomaji:[65316],JunjaMode:[65336],KanaMode:[65325],KanjiMode:[65313],Katakana:[65318],Left:[65361],Meta:[65511,65511,65512],ModeChange:[65406],NonConvert:[65314],NumLock:[65407],PageDown:[65366],PageUp:[65365],Pause:[65299],Play:[64790],PreviousCandidate:[65342],PrintScreen:[65377],Redo:[65382],Right:[65363],Romaji:[65316],RomanCharacters:null,Scroll:[65300],Select:[65376],Separator:[65452],Shift:[65505,65505,65506],SingleCandidate:[65340],Super:[65515,65515,65516],Tab:[65289],UIKeyInputDownArrow:[65364],UIKeyInputEscape:[65307],UIKeyInputLeftArrow:[65361],UIKeyInputRightArrow:[65363],UIKeyInputUpArrow:[65362],Up:[65362],Undo:[65381],Win:[65511,65511,65512],Zenkaku:[65320],ZenkakuHankaku:[65322]},k={65027:!0,65505:!0,65506:!0,65507:!0,65508:!0,65509:!0,65511:!0,65512:!0,65513:!0,65514:!0,65515:!0,65516:!0};this.modifiers=new n.Keyboard.ModifierState,this.pressed={};var _={},P={},b={},l=null,v=null,g=function(E,D){return E?E[D]||E[0]:null},p=function(E){return E>=0&&E<=255||(E&4294901760)===16777216};function m(I,E,D){if(!I)return null;var B,Y=I.indexOf("U+");if(Y>=0){var U=I.substring(Y+2);B=String.fromCharCode(parseInt(U,16))}else if(I.length===1&&E!==3)B=I;else return g(T[I],E);D===!0?B=B.toUpperCase():D===!1&&(B=B.toLowerCase());var N=B.charCodeAt(0);return R(N)}function S(I){return I<=31||I>=127&&I<=159}function R(I){return S(I)?65280|I:I>=0&&I<=255?I:I>=256&&I<=1114111?16777216|I:null}function M(I,E){return g(w[I],E)}var O=function(E,D){if(!D)return!1;var B=D.indexOf("U+");if(B===-1)return!0;var Y=parseInt(D.substring(B+2),16);return E!==Y||E>=65&&E<=90||E>=48&&E<=57};this.press=function(I){if(I!==null){if(!t.pressed[I]&&(t.pressed[I]=!0,t.onkeydown)){var E=t.onkeydown(I);return P[I]=E,window.clearTimeout(l),window.clearInterval(v),k[I]||(l=window.setTimeout(function(){v=window.setInterval(function(){t.onkeyup(I),t.onkeydown(I)},50)},500)),E}return P[I]||!1}},this.release=function(I){t.pressed[I]&&(delete t.pressed[I],delete _[I],window.clearTimeout(l),window.clearInterval(v),I!==null&&t.onkeyup&&t.onkeyup(I))},this.type=function(E){for(var D=0;D<E.length;D++){var B=E.codePointAt?E.codePointAt(D):E.charCodeAt(D),Y=R(B);t.press(Y),t.release(Y)}},this.reset=function(){for(var I in t.pressed)t.release(parseInt(I));y=[]};var F=function(E,D,B){var Y=B.modifiers[E],U=t.modifiers[E],N;if(D.indexOf(B.keysym)===-1){if(U&&Y===!1)for(N=0;N<D.length;N++)t.release(D[N]);else if(!U&&Y){for(N=0;N<D.length;N++)if(t.pressed[D[N]])return;var W=D[0];B.keysym&&(_[W]=!0),t.press(W)}}},H=function(E){F("alt",[65513,65514,65027],E),F("shift",[65505,65506],E),F("ctrl",[65507,65508],E),F("meta",[65511,65512],E),F("hyper",[65515,65516],E),t.modifiers=E.modifiers},i=function(){for(var E in t.pressed)if(!_[E])return!1;return!0};function f(){var I=C();if(!I)return!1;var E;do E=I,I=C();while(I!==null);return i()&&t.reset(),E.defaultPrevented}var h=function(E){!t.modifiers.ctrl||!t.modifiers.alt||E>=65&&E<=90||E>=97&&E<=122||(E<=255||(E&4278190080)===16777216)&&(t.release(65507),t.release(65508),t.release(65513),t.release(65514))},C=function(){var E=y[0];if(!E)return null;if(E instanceof c){var D=null,B=[];if(E.keysym===65511||E.keysym===65512){if(y.length===1)return null;if(y[1].keysym!==E.keysym){if(!y[1].modifiers.meta)return y.shift()}else if(y[1]instanceof c)return y.shift()}if(E.reliable?(D=E.keysym,B=y.splice(0,1)):y[1]instanceof x?(D=y[1].keysym,B=y.splice(0,2)):y[1]&&(D=E.keysym,B=y.splice(0,1)),B.length>0){if(H(E),D){h(D);var Y=!t.press(D);b[E.keyCode]=D,E.keyupReliable||t.release(D);for(var U=0;U<B.length;U++)B[U].defaultPrevented=Y}return E}}else if(E instanceof u&&!o.keyupUnreliable){var D=E.keysym;if(D)t.release(D),delete b[E.keyCode],E.defaultPrevented=!0;else return t.reset(),E;return H(E),y.shift()}else return y.shift();return null},A=function(E){return"location"in E?E.location:"keyLocation"in E?E.keyLocation:0},L=function(E){return E[s]?!1:(E[s]=!0,!0)};this.listenTo=function(E){E.addEventListener("keydown",function(U){if(t.onkeydown&&L(U)){var N=new c(U);U.isComposing||N.keyCode===229||(y.push(N),f()&&U.preventDefault())}},!0),E.addEventListener("keypress",function(U){!t.onkeydown&&!t.onkeyup||L(U)&&(y.push(new x(U)),f()&&U.preventDefault())},!0),E.addEventListener("keyup",function(U){t.onkeyup&&L(U)&&(U.preventDefault(),y.push(new u(U)),f())},!0);var D=function(N){!t.onkeydown&&!t.onkeyup||L(N)&&N.data&&!N.isComposing&&t.type(N.data)},B=function(N){E.removeEventListener("input",D,!1)},Y=function(N){!t.onkeydown&&!t.onkeyup||L(N)&&N.data&&t.type(N.data)};E.addEventListener("input",D,!1),E.addEventListener("compositionend",Y,!1),E.addEventListener("compositionstart",B,!1)},e&&t.listenTo(e)};n.Keyboard._nextID=0;n.Keyboard.ModifierState=function(){this.shift=!1,this.ctrl=!1,this.alt=!1,this.meta=!1,this.hyper=!1};n.Keyboard.ModifierState.fromKeyboardEvent=function(r){var e=new n.Keyboard.ModifierState;return e.shift=r.shiftKey,e.ctrl=r.ctrlKey,e.alt=r.altKey,e.meta=r.metaKey,r.getModifierState&&(e.hyper=r.getModifierState("OS")||r.getModifierState("Super")||r.getModifierState("Hyper")||r.getModifierState("Win")),e};var n=n||{};n.KeyEventInterpreter=function(e){e==null&&(e=0);var t=[{keysym:65027,name:"AltGr"},{keysym:65288,name:"Backspace"},{keysym:65289,name:"Tab"},{keysym:65291,name:"Clear"},{keysym:65293,name:"Return",value:`\n`},{keysym:65299,name:"Pause"},{keysym:65300,name:"Scroll"},{keysym:65301,name:"SysReq"},{keysym:65307,name:"Escape"},{keysym:65360,name:"Home"},{keysym:65361,name:"Left"},{keysym:65362,name:"Up"},{keysym:65363,name:"Right"},{keysym:65364,name:"Down"},{keysym:65365,name:"Page Up"},{keysym:65366,name:"Page Down"},{keysym:65367,name:"End"},{keysym:65379,name:"Insert"},{keysym:65381,name:"Undo"},{keysym:65386,name:"Help"},{keysym:65407,name:"Num"},{keysym:65408,name:"Space",value:" "},{keysym:65421,name:"Enter",value:`\n`},{keysym:65429,name:"Home"},{keysym:65430,name:"Left"},{keysym:65431,name:"Up"},{keysym:65432,name:"Right"},{keysym:65433,name:"Down"},{keysym:65434,name:"Page Up"},{keysym:65435,name:"Page Down"},{keysym:65436,name:"End"},{keysym:65438,name:"Insert"},{keysym:65450,name:"*",value:"*"},{keysym:65451,name:"+",value:"+"},{keysym:65453,name:"-",value:"-"},{keysym:65454,name:".",value:"."},{keysym:65455,name:"/",value:"/"},{keysym:65456,name:"0",value:"0"},{keysym:65457,name:"1",value:"1"},{keysym:65458,name:"2",value:"2"},{keysym:65459,name:"3",value:"3"},{keysym:65460,name:"4",value:"4"},{keysym:65461,name:"5",value:"5"},{keysym:65462,name:"6",value:"6"},{keysym:65463,name:"7",value:"7"},{keysym:65464,name:"8",value:"8"},{keysym:65465,name:"9",value:"9"},{keysym:65470,name:"F1"},{keysym:65471,name:"F2"},{keysym:65472,name:"F3"},{keysym:65473,name:"F4"},{keysym:65474,name:"F5"},{keysym:65475,name:"F6"},{keysym:65476,name:"F7"},{keysym:65477,name:"F8"},{keysym:65478,name:"F9"},{keysym:65479,name:"F10"},{keysym:65480,name:"F11"},{keysym:65481,name:"F12"},{keysym:65482,name:"F13"},{keysym:65483,name:"F14"},{keysym:65484,name:"F15"},{keysym:65485,name:"F16"},{keysym:65486,name:"F17"},{keysym:65487,name:"F18"},{keysym:65488,name:"F19"},{keysym:65489,name:"F20"},{keysym:65490,name:"F21"},{keysym:65491,name:"F22"},{keysym:65492,name:"F23"},{keysym:65493,name:"F24"},{keysym:65505,name:"Shift"},{keysym:65506,name:"Shift"},{keysym:65507,name:"Ctrl"},{keysym:65508,name:"Ctrl"},{keysym:65509,name:"Caps"},{keysym:65511,name:"Meta"},{keysym:65512,name:"Meta"},{keysym:65513,name:"Alt"},{keysym:65514,name:"Alt"},{keysym:65515,name:"Super"},{keysym:65516,name:"Super"},{keysym:65517,name:"Hyper"},{keysym:65518,name:"Hyper"},{keysym:65535,name:"Delete"}],a={};t.forEach(function(x){a[x.keysym]=new n.KeyEventInterpreter.KeyDefinition(x)});var s=[];function o(c){if(c<0||c>255&&(c|65535)!=16842751)return null;var x=c&65535,u=String.fromCharCode(x);return new n.KeyEventInterpreter.KeyDefinition({keysym:c,name:u,value:u})}function d(c){if(c in a)return a[c];var x=o(c);return x??new n.KeyEventInterpreter.KeyDefinition({keysym:c,name:"0x"+String(c.toString(16))})}this.handleKeyEvent=function(x){var u=parseInt(x[0]),y=parseInt(x[1]),w=parseInt(x[2]),T=w-e,k=d(u);s.push(new n.KeyEventInterpreter.KeyEvent({definition:k,pressed:y,timestamp:T}))},this.getEvents=function(){return s}};n.KeyEventInterpreter.KeyDefinition=function(e){e=e||{},this.keysym=parseInt(e.keysym),this.name=e.name,this.value=e.value};n.KeyEventInterpreter.KeyEvent=function(e){e=e||{},this.definition=e.definition,this.pressed=!!e.pressed,this.timestamp=e.timestamp};var n=n||{};n.Layer=function(r,e){var t=this,a=64,s=document.createElement("canvas"),o=s.getContext("2d");o.save();var d=!0,c=!0,x=0,u={1:"destination-in",2:"destination-out",4:"source-in",6:"source-atop",8:"source-out",9:"destination-atop",10:"xor",11:"destination-over",12:"copy",14:"source-over",15:"lighter"},y=function(k,_){k=k||0,_=_||0;var P=Math.ceil(k/a)*a,b=Math.ceil(_/a)*a;if(s.width!==P||s.height!==b){var l=null;if(!d&&s.width!==0&&s.height!==0){l=document.createElement("canvas"),l.width=Math.min(t.width,k),l.height=Math.min(t.height,_);var v=l.getContext("2d");v.drawImage(s,0,0,l.width,l.height,0,0,l.width,l.height)}var g=o.globalCompositeOperation;s.width=P,s.height=b,l&&o.drawImage(l,0,0,l.width,l.height,0,0,l.width,l.height),o.globalCompositeOperation=g,x=0,o.save()}else t.reset();t.width=k,t.height=_};function w(T,k,_,P){var b=_+T,l=P+k,v;b>t.width?v=b:v=t.width;var g;l>t.height?g=l:g=t.height,t.resize(v,g)}this.autosize=!1,this.width=r,this.height=e,this.getCanvas=function(){return s},this.toCanvas=function(){var k=document.createElement("canvas");k.width=t.width,k.height=t.height;var _=k.getContext("2d");return _.drawImage(t.getCanvas(),0,0),k},this.resize=function(T,k){(T!==t.width||k!==t.height)&&y(T,k)},this.drawImage=function(T,k,_){t.autosize&&w(T,k,_.width,_.height),o.drawImage(_,T,k),d=!1},this.transfer=function(T,k,_,P,b,l,v,g){var p=T.getCanvas();if(!(k>=p.width||_>=p.height)&&(k+P>p.width&&(P=p.width-k),_+b>p.height&&(b=p.height-_),!(P===0||b===0))){t.autosize&&w(l,v,P,b);for(var m=T.getCanvas().getContext("2d").getImageData(k,_,P,b),S=o.getImageData(l,v,P,b),R=0;R<P*b*4;R+=4){var M=new n.Layer.Pixel(m.data[R],m.data[R+1],m.data[R+2],m.data[R+3]),O=new n.Layer.Pixel(S.data[R],S.data[R+1],S.data[R+2],S.data[R+3]);g(M,O),S.data[R]=O.red,S.data[R+1]=O.green,S.data[R+2]=O.blue,S.data[R+3]=O.alpha}o.putImageData(S,l,v),d=!1}},this.put=function(T,k,_,P,b,l,v){var g=T.getCanvas();if(!(k>=g.width||_>=g.height)&&(k+P>g.width&&(P=g.width-k),_+b>g.height&&(b=g.height-_),!(P===0||b===0))){t.autosize&&w(l,v,P,b);var p=T.getCanvas().getContext("2d").getImageData(k,_,P,b);o.putImageData(p,l,v),d=!1}},this.copy=function(T,k,_,P,b,l,v){var g=T.getCanvas();k>=g.width||_>=g.height||(k+P>g.width&&(P=g.width-k),_+b>g.height&&(b=g.height-_),!(P===0||b===0)&&(t.autosize&&w(l,v,P,b),o.drawImage(g,k,_,P,b,l,v,P,b),d=!1))},this.moveTo=function(T,k){c&&(o.beginPath(),c=!1),t.autosize&&w(T,k,0,0),o.moveTo(T,k)},this.lineTo=function(T,k){c&&(o.beginPath(),c=!1),t.autosize&&w(T,k,0,0),o.lineTo(T,k)},this.arc=function(T,k,_,P,b,l){c&&(o.beginPath(),c=!1),t.autosize&&w(T,k,0,0),o.arc(T,k,_,P,b,l)},this.curveTo=function(T,k,_,P,b,l){c&&(o.beginPath(),c=!1),t.autosize&&w(b,l,0,0),o.bezierCurveTo(T,k,_,P,b,l)},this.close=function(){o.closePath(),c=!0},this.rect=function(T,k,_,P){c&&(o.beginPath(),c=!1),t.autosize&&w(T,k,_,P),o.rect(T,k,_,P)},this.clip=function(){o.clip(),c=!0},this.strokeColor=function(T,k,_,P,b,l,v){o.lineCap=T,o.lineJoin=k,o.lineWidth=_,o.strokeStyle="rgba("+P+","+b+","+l+","+v/255+")",o.stroke(),d=!1,c=!0},this.fillColor=function(T,k,_,P){o.fillStyle="rgba("+T+","+k+","+_+","+P/255+")",o.fill(),d=!1,c=!0},this.strokeLayer=function(T,k,_,P){o.lineCap=T,o.lineJoin=k,o.lineWidth=_,o.strokeStyle=o.createPattern(P.getCanvas(),"repeat"),o.stroke(),d=!1,c=!0},this.fillLayer=function(T){o.fillStyle=o.createPattern(T.getCanvas(),"repeat"),o.fill(),d=!1,c=!0},this.push=function(){o.save(),x++},this.pop=function(){x>0&&(o.restore(),x--)},this.reset=function(){for(;x>0;)o.restore(),x--;o.restore(),o.save(),o.beginPath(),c=!1},this.setTransform=function(T,k,_,P,b,l){o.setTransform(T,k,_,P,b,l)},this.transform=function(T,k,_,P,b,l){o.transform(T,k,_,P,b,l)},this.setChannelMask=function(T){o.globalCompositeOperation=u[T]},this.setMiterLimit=function(T){o.miterLimit=T},y(r,e),s.style.zIndex=-1};n.Layer.ROUT=2;n.Layer.ATOP=6;n.Layer.XOR=10;n.Layer.ROVER=11;n.Layer.OVER=14;n.Layer.PLUS=15;n.Layer.RIN=1;n.Layer.IN=4;n.Layer.OUT=8;n.Layer.RATOP=9;n.Layer.SRC=12;n.Layer.Pixel=function(r,e,t,a){this.red=r,this.green=e,this.blue=t,this.alpha=a};var n=n||{};n.Mouse=function(e){n.Mouse.Event.Target.call(this);var t=this;this.touchMouseThreshold=3,this.scrollThreshold=53,this.PIXELS_PER_LINE=18,this.PIXELS_PER_PAGE=this.PIXELS_PER_LINE*16;var a=[n.Mouse.State.Buttons.LEFT,n.Mouse.State.Buttons.MIDDLE,n.Mouse.State.Buttons.RIGHT],s=0,o=0;e.addEventListener("contextmenu",function(u){n.Event.DOMEvent.cancelEvent(u)},!1),e.addEventListener("mousemove",function(u){if(s){n.Event.DOMEvent.cancelEvent(u),s--;return}t.move(n.Position.fromClientPosition(e,u.clientX,u.clientY),u)},!1),e.addEventListener("mousedown",function(u){if(s){n.Event.DOMEvent.cancelEvent(u);return}var y=a[u.button];y&&t.press(y,u)},!1),e.addEventListener("mouseup",function(u){if(s){n.Event.DOMEvent.cancelEvent(u);return}var y=a[u.button];y&&t.release(y,u)},!1),e.addEventListener("mouseout",function(u){u||(u=window.event);for(var y=u.relatedTarget||u.toElement;y;){if(y===e)return;y=y.parentNode}t.reset(u),t.out(u)},!1),e.addEventListener("selectstart",function(u){n.Event.DOMEvent.cancelEvent(u)},!1);function d(){s=t.touchMouseThreshold}e.addEventListener("touchmove",d,!1),e.addEventListener("touchstart",d,!1),e.addEventListener("touchend",d,!1);function c(u){var y=u.deltaY||-u.wheelDeltaY||-u.wheelDelta;if(y?u.deltaMode===1?y=u.deltaY*t.PIXELS_PER_LINE:u.deltaMode===2&&(y=u.deltaY*t.PIXELS_PER_PAGE):y=u.detail*t.PIXELS_PER_LINE,o+=y,o<=-t.scrollThreshold){do t.click(n.Mouse.State.Buttons.UP),o+=t.scrollThreshold;while(o<=-t.scrollThreshold);o=0}if(o>=t.scrollThreshold){do t.click(n.Mouse.State.Buttons.DOWN),o-=t.scrollThreshold;while(o>=t.scrollThreshold);o=0}n.Event.DOMEvent.cancelEvent(u)}window.WheelEvent?e.addEventListener("wheel",c,!1):(e.addEventListener("DOMMouseScroll",c,!1),e.addEventListener("mousewheel",c,!1));var x=(function(){var u=document.createElement("div");if(!("cursor"in u.style))return!1;try{u.style.cursor="url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX///+nxBvIAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==) 0 0, auto"}catch{return!1}return/\\burl\\([^()]*\\)\\s+0\\s+0\\b/.test(u.style.cursor||"")})();this.setCursor=function(u,y,w){if(x){var T=u.toDataURL("image/png");return e.style.cursor="url("+T+") "+y+" "+w+", auto",!0}return!1}};n.Mouse.State=function(e){var t=function(s,o,d,c,x,u,y){return{x:s,y:o,left:d,middle:c,right:x,up:u,down:y}};arguments.length>1?e=t.apply(this,arguments):e=e||{},n.Position.call(this,e),this.left=e.left||!1,this.middle=e.middle||!1,this.right=e.right||!1,this.up=e.up||!1,this.down=e.down||!1};n.Mouse.State.Buttons={LEFT:"left",MIDDLE:"middle",RIGHT:"right",UP:"up",DOWN:"down"};n.Mouse.Event=function(e,t,a){n.Event.DOMEvent.call(this,e,a);var s="on"+this.type;this.state=t,this.invokeLegacyHandler=function(d){d[s]&&(this.preventDefault(),this.stopPropagation(),d[s](this.state))}};n.Mouse.Event.Target=function(){n.Event.Target.call(this),this.currentState=new n.Mouse.State,this.press=function(t,a){this.currentState[t]||(this.currentState[t]=!0,this.dispatch(new n.Mouse.Event("mousedown",this.currentState,a)))},this.release=function(t,a){this.currentState[t]&&(this.currentState[t]=!1,this.dispatch(new n.Mouse.Event("mouseup",this.currentState,a)))},this.click=function(t,a){this.press(t,a),this.release(t,a)},this.move=function(t,a){(this.currentState.x!==t.x||this.currentState.y!==t.y)&&(this.currentState.x=t.x,this.currentState.y=t.y,this.dispatch(new n.Mouse.Event("mousemove",this.currentState,a)))},this.out=function(t){this.dispatch(new n.Mouse.Event("mouseout",this.currentState,t))},this.reset=function(t){for(var a in n.Mouse.State.Buttons)this.release(n.Mouse.State.Buttons[a],t)}};n.Mouse.Touchpad=function(e){n.Mouse.Event.Target.call(this);var t=this;this.scrollThreshold=20*(window.devicePixelRatio||1),this.clickTimingThreshold=250,this.clickMoveThreshold=10*(window.devicePixelRatio||1),this.currentState=new n.Mouse.State;var a=0,s=0,o=0,d=0,c=0,x={1:"left",2:"right",3:"middle"},u=!1,y=null;e.addEventListener("touchend",function(w){if(w.preventDefault(),u&&w.touches.length===0){var T=new Date().getTime(),k=x[a];t.currentState[k]&&(t.release(k,w),y&&(window.clearTimeout(y),y=null)),T-d<=t.clickTimingThreshold&&c<t.clickMoveThreshold&&(t.press(k,w),y=window.setTimeout(function(){t.release(k,w),u=!1},t.clickTimingThreshold)),y||(u=!1)}},!1),e.addEventListener("touchstart",function(w){if(w.preventDefault(),a=Math.min(w.touches.length,3),y&&(window.clearTimeout(y),y=null),!u){u=!0;var T=w.touches[0];s=T.clientX,o=T.clientY,d=new Date().getTime(),c=0}},!1),e.addEventListener("touchmove",function(w){w.preventDefault();var T=w.touches[0],k=T.clientX-s,_=T.clientY-o;if(c+=Math.abs(k)+Math.abs(_),a===1){var P=c/(new Date().getTime()-d),b=1+P,l=new n.Position(t.currentState);l.x+=k*b,l.y+=_*b,l.x=Math.min(Math.max(0,l.x),e.offsetWidth-1),l.y=Math.min(Math.max(0,l.y),e.offsetHeight-1),t.move(l,w),s=T.clientX,o=T.clientY}else if(a===2&&Math.abs(_)>=t.scrollThreshold){var v;_>0?v="down":v="up",t.click(v,w),s=T.clientX,o=T.clientY}},!1)};n.Mouse.Touchscreen=function(e){n.Mouse.Event.Target.call(this);var t=this,a=!1,s=null,o=null,d=null,c=null;this.scrollThreshold=20*(window.devicePixelRatio||1),this.clickTimingThreshold=250,this.clickMoveThreshold=16*(window.devicePixelRatio||1),this.longPressThreshold=500;function x(w){var T=w.touches[0]||w.changedTouches[0],k=T.clientX-s,_=T.clientY-o;return Math.sqrt(k*k+_*_)>=t.clickMoveThreshold}function u(w){var T=w.touches[0];a=!0,s=T.clientX,o=T.clientY}function y(){window.clearTimeout(d),window.clearTimeout(c),a=!1}e.addEventListener("touchend",function(w){if(a){if(w.touches.length!==0||w.changedTouches.length!==1){y();return}if(window.clearTimeout(c),t.release(n.Mouse.State.Buttons.LEFT,w),!x(w)&&(w.preventDefault(),!t.currentState.left)){var T=w.changedTouches[0];t.move(n.Position.fromClientPosition(e,T.clientX,T.clientY)),t.press(n.Mouse.State.Buttons.LEFT,w),d=window.setTimeout(function(){t.release(n.Mouse.State.Buttons.LEFT,w),y()},t.clickTimingThreshold)}}},!1),e.addEventListener("touchstart",function(w){if(w.touches.length!==1){y();return}w.preventDefault(),u(w),window.clearTimeout(d),c=window.setTimeout(function(){var T=w.touches[0];t.move(n.Position.fromClientPosition(e,T.clientX,T.clientY)),t.click(n.Mouse.State.Buttons.RIGHT,w),y()},t.longPressThreshold)},!1),e.addEventListener("touchmove",function(w){if(a){if(x(w)&&window.clearTimeout(c),w.touches.length!==1){y();return}if(t.currentState.left){w.preventDefault();var T=w.touches[0];t.move(n.Position.fromClientPosition(e,T.clientX,T.clientY),w)}}},!1)};var n=n||{},n=n||{};n.Object=function(e,t){var a=this,s={},o=function(x){var u=s[x];if(!u)return null;var y=u.shift();return u.length===0&&delete s[x],y},d=function(x,u){var y=s[x];y||(y=[],s[x]=y),y.push(u)};this.index=t,this.onbody=function(x,u,y){var w=o(y);w&&w(x,u)},this.onundefine=null,this.requestInputStream=function(x,u){u&&d(x,u),e.requestObjectInputStream(a.index,x)},this.createOutputStream=function(x,u){return e.createObjectOutputStream(a.index,x,u)}};n.Object.ROOT_STREAM="/";n.Object.STREAM_INDEX_MIMETYPE="application/vnd.glyptodon.guacamole.stream-index+json";var n=n||{};n.OnScreenKeyboard=function(r){var e=this,t={},a={},s=[],o=function(p,m){p.classList?p.classList.add(m):p.className+=" "+m},d=function(p,m){p.classList?p.classList.remove(m):p.className=p.className.replace(/([^ ]+)[ ]*/g,function(R,M){return M===m?"":R})},c=0,x=function(){c=e.touchMouseThreshold},u=function(p,m,S,R){this.width=m,this.height=S,this.scale=function(M){p.style.width=m*M+"px",p.style.height=S*M+"px",R&&(p.style.lineHeight=S*M+"px",p.style.fontSize=M+"px")}},y=function(p){for(var m=0;m<p.length;m++){var S=p[m];if(!(S in t))return!1}return!0},w=function(p){var m=e.keys[p];if(!m)return null;for(var S=m.length-1;S>=0;S--){var R=m[S];if(y(R.requires))return R}return null},T=function(p,m){if(!a[p]){o(m,"guac-keyboard-pressed");var S=w(p);if(S.modifier){var R="guac-keyboard-modifier-"+l(S.modifier),M=t[S.modifier];M===void 0?(o(_,R),t[S.modifier]=S.keysym,S.keysym&&e.onkeydown&&e.onkeydown(S.keysym)):(d(_,R),delete t[S.modifier],M&&e.onkeyup&&e.onkeyup(M))}else e.onkeydown&&e.onkeydown(S.keysym);a[p]=!0}},k=function(p,m){if(a[p]){d(m,"guac-keyboard-pressed");var S=w(p);!S.modifier&&e.onkeyup&&e.onkeyup(S.keysym),a[p]=!1}},_=document.createElement("div");_.className="guac-keyboard",_.onselectstart=_.onmousemove=_.onmouseup=_.onmousedown=function(p){return c&&c--,p.stopPropagation(),!1},this.touchMouseThreshold=3,this.onkeydown=null,this.onkeyup=null,this.layout=new n.OnScreenKeyboard.Layout(r),this.getElement=function(){return _},this.resize=function(g){for(var p=Math.floor(g*10/e.layout.width)/10,m=0;m<s.length;m++){var S=s[m];S.scale(p)}};var P=function(p,m){if(m instanceof Array){for(var S=[],R=0;R<m.length;R++)S.push(new n.OnScreenKeyboard.Key(m[R],p));return S}return typeof m=="number"?[new n.OnScreenKeyboard.Key({name:p,keysym:m})]:typeof m=="string"?[new n.OnScreenKeyboard.Key({name:p,title:m})]:[new n.OnScreenKeyboard.Key(m,p)]},b=function(p){var m={};for(var S in r.keys)m[S]=P(S,p[S]);return m};this.keys=b(r.keys);var l=function(p){var m=p.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[^A-Za-z0-9]+/g,"-").toLowerCase();return m},v=function g(p,m,S){var R,M=document.createElement("div");if(S&&o(M,"guac-keyboard-"+l(S)),m instanceof Array)for(o(M,"guac-keyboard-group"),R=0;R<m.length;R++)g(M,m[R]);else if(m instanceof Object){o(M,"guac-keyboard-group");var O=Object.keys(m).sort();for(R=0;R<O.length;R++){var S=O[R];g(M,m[S],S)}}else if(typeof m=="number")o(M,"guac-keyboard-gap"),s.push(new u(M,m,m));else if(typeof m=="string"){var F=m;F.length===1&&(F="0x"+F.charCodeAt(0).toString(16)),o(M,"guac-keyboard-key-container");var H=document.createElement("div");H.className="guac-keyboard-key guac-keyboard-key-"+l(F);var i=e.keys[m];if(i)for(R=0;R<i.length;R++){var f=i[R],h=document.createElement("div");h.className="guac-keyboard-cap",h.textContent=f.title;for(var C=0;C<f.requires.length;C++){var A=f.requires[C];o(h,"guac-keyboard-requires-"+l(A)),o(H,"guac-keyboard-uses-"+l(A))}H.appendChild(h)}M.appendChild(H),s.push(new u(M,e.layout.keyWidths[m]||1,1,!0));var L=function(Y){Y.preventDefault(),c=e.touchMouseThreshold,T(m,H)},I=function(Y){Y.preventDefault(),c=e.touchMouseThreshold,k(m,H)},E=function(Y){Y.preventDefault(),c===0&&T(m,H)},D=function(Y){Y.preventDefault(),c===0&&k(m,H)};H.addEventListener("touchstart",L,!0),H.addEventListener("touchend",I,!0),H.addEventListener("mousedown",E,!0),H.addEventListener("mouseup",D,!0),H.addEventListener("mouseout",D,!0)}p.appendChild(M)};v(_,r.layout)};n.OnScreenKeyboard.Layout=function(r){this.language=r.language,this.type=r.type,this.keys=r.keys,this.layout=r.layout,this.width=r.width,this.keyWidths=r.keyWidths||{}};n.OnScreenKeyboard.Key=function(r,e){this.name=e||r.name,this.title=r.title||this.name,this.keysym=r.keysym||(function(a){if(!a||a.length!==1)return null;var s=a.charCodeAt(0);return s>=0&&s<=255?s:s>=256&&s<=1114111?16777216|s:null})(this.title),this.modifier=r.modifier,this.requires=r.requires||[]};var n=n||{};n.OutputStream=function(r,e){var t=this;this.index=e,this.onack=null,this.sendBlob=function(a){r.sendBlob(t.index,a)},this.sendEnd=function(){r.endStream(t.index)}};var n=n||{};n.Parser=function(){var e=this,t="",a=[],s=-1,o=0,d=0,c=4096,x=65536;this.receive=function(y,w){for(w?t=y:(o>c&&s>=o&&(t=t.substring(o),s-=o,o=0),t.length?t+=y:t=y);s<t.length;){if(s>=o){var T=n.Parser.codePointCount(t,o,s);if(T<d){s+=d-T;continue}else if(d&&t.codePointAt(s-1)>=x){s++;continue}var k=t.substring(o,s),_=t.substring(s,s+1);if(a.push(k),_===";"){var P=a.shift();e.oninstruction!==null&&e.oninstruction(P,a),a=[],!w&&s+1===t.length&&(s=-1,t="")}else if(_!==",")throw new Error(\'Element terminator of instruction was not ";" nor ",".\');o=s+1}var b=t.indexOf(".",o);if(b!==-1){if(d=parseInt(t.substring(s+1,b)),isNaN(d))throw new Error("Non-numeric character in element length.");o=b+1,s=o+d}else{o=t.length;break}}},this.oninstruction=null};n.Parser.codePointCount=function(e,t,a){e=e.substring(t||0,a);var s=e.match(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g);return e.length-(s?s.length:0)};n.Parser.toInstruction=function(e){for(var t=function(d){var c=""+d;return n.Parser.codePointCount(c)+"."+c},a=t(e[0]),s=1;s<e.length;s++)a+=","+t(e[s]);return a+";"};var n=n||{};n.Position=function(e){e=e||{},this.x=e.x||0,this.y=e.y||0,this.fromClientPosition=function(a,s,o){this.x=s-a.offsetLeft,this.y=o-a.offsetTop;for(var d=a.offsetParent;d&&d!==document.body;)this.x-=d.offsetLeft-d.scrollLeft,this.y-=d.offsetTop-d.scrollTop,d=d.offsetParent;if(d){var c=document.body.scrollLeft||document.documentElement.scrollLeft,x=document.body.scrollTop||document.documentElement.scrollTop;this.x-=d.offsetLeft-c,this.y-=d.offsetTop-x}}};n.Position.fromClientPosition=function(e,t,a){var s=new n.Position;return s.fromClientPosition(e,t,a),s};var n=n||{};n.RawAudioFormat=function(e){this.bytesPerSample=e.bytesPerSample,this.channels=e.channels,this.rate=e.rate};n.RawAudioFormat.parse=function(e){var t,a=null,s=1;if(e.substring(0,9)==="audio/L8;")e=e.substring(9),t=1;else if(e.substring(0,10)==="audio/L16;")e=e.substring(10),t=2;else return null;for(var o=e.split(","),d=0;d<o.length;d++){var c=o[d],x=c.indexOf("=");if(x===-1)return null;var u=c.substring(0,x),y=c.substring(x+1);switch(u){case"channels":s=parseInt(y);break;case"rate":a=parseInt(y);break;default:return null}}return a===null?null:new n.RawAudioFormat({bytesPerSample:t,channels:s,rate:a})};var n=n||{};n.SessionRecording=function(e,t){t===void 0&&(t=1e3);var a=this,s,o=null,d=262144,c=16384,x=5e3,u=[],y=0,w=new n.SessionRecording._PlaybackTunnel,T=new n.Client(w),k=-1,_=null,P=null,b=0,l=null,v=0,g=0,p=!1,m=null,S=null,R=null,M=function(N,W,G){if(!(p&&N===s)){var X=new n.Parser;X.oninstruction=W;var V=0,J=new FileReader,ie=function(){if(!(p&&N===s)){if(J.readyState===2)try{X.receive(J.result)}catch(ne){a.onerror&&a.onerror(ne.message);return}if(V>=N.size)G&&G();else{var te=N.slice(V,V+d);V+=te.size,J.readAsText(te)}}};J.onload=ie,ie()}},O=function(U){for(var N=U.length,W=U.length-1;W>=0;W--){var G=U.charCodeAt(W);G>127&&G<=2047?N++:G>2047&&G<=65535&&(N+=2),G>=56320&&G<=57343&&W--}return N},F=function(N){for(var W=O(N)+3,G=n.Parser.codePointCount(N);G>=10;)W++,G=Math.floor(G/10);return W};T.connect(),T.getDisplay().showCursor(!1);var H=null;function i(U){H=new n.KeyEventInterpreter(U)}var f=function(N,W){g+=F(N);for(var G=0;G<W.length;G++)g+=F(W[G]);if(N==="sync"){var X=parseInt(W[0]),V=new n.SessionRecording._Frame(X,v,g);u.push(V),v=g,u.length===1&&i(X),(u.length===1||g-u[y].start>=c&&X-u[y].timestamp>=x)&&(V.keyframe=!0,y=u.length-1),a.onprogress&&a.onprogress(a.getDuration(),g)}else N==="key"&&H.handleKeyEvent(W)},h=function(){a.onload&&a.onload()};if(e instanceof Blob)s=e,M(s,f,h);else{o=e,s=new Blob;var C=!1,A="";o.oninstruction=function(N,W){A+=N.length+"."+N,W.forEach(function(X){A+=","+X.length+"."+X}),A+=";",A.length>=d&&(s=new Blob([s,A]),A=""),f(N,W)},o.onerror=function(N){C=!0,a.onerror&&a.onerror(N.message)},o.onstatechange=function(N){N===n.Tunnel.State.CLOSED&&(A.length&&(s=new Blob([s,A]),A=""),a.onkeyevents&&a.onkeyevents(H.getEvents()),C||h())}}var L=function(N){return u.length===0?0:N-u[0].timestamp},I=function U(N,W,G){if(N===W){if(N===0)return N;if(L(u[N].timestamp)>G)return N-1}var X=Math.floor((N+W)/2),V=L(u[X].timestamp);return G<V&&X>N?U(N,X-1,G):G>V&&X<W?U(X+1,W,G):X},E=function(N,W){var G=u[N];M(s.slice(G.start,G.end),function(V,J){w.receiveInstruction(V,J)},function(){G.keyframe&&!G.clientState&&T.exportState(function(J){G.clientState=new Blob([JSON.stringify(J)])}),k=N,W&&W()})},D=function(N,W,G){B();for(var X=l={aborted:!1},V=N,J=function te(){a.onseek&&k>V&&(b=L(u[k].timestamp),a.onseek(b,k-V,N-V)),!X.aborted&&(k<N?E(k+1,te):W())},ie=function(){var ne=G?Math.max(G-new Date().getTime(),0):0;ne?(S&&clearTimeout(S),S=window.setTimeout(function(){S=null,J()},ne)):J()};V>=0;V--){var he=u[V];if(V===k)break;if(he.clientState){he.clientState.text().then(function(ne){T.importState(JSON.parse(ne)),k=V,ie()});return}}ie()},B=function(){l&&(l.aborted=!0,l=null)},Y=function U(){if(a.isPlaying())if(k+1<u.length){var N=u[k+1],W=Date.now()-P,G=L(N.timestamp)-_,X=G-W,V=t>=0?t*Math.floor((b+t)/t)-b:X;X<=V?D(k+1,function(){R=Date.now(),U()},Date.now()+X):(S&&window.clearTimeout(S),S=window.setTimeout(function(){S=null,b+=V,a.onseek&&a.onseek(b),R=Date.now(),U()},V))}else a.pause()};this.onload=null,this.onerror=null,this.onabort=null,this.onprogress=null,this.onplay=null,this.onpause=null,this.onkeyevents=null,this.onseek=null,this.connect=function(N){o&&o.connect(N)},this.disconnect=function(){o&&o.disconnect()},this.abort=function(){p||(p=!0,a.onabort&&a.onabort(),o&&o.disconnect())},this.getDisplay=function(){return T.getDisplay()},this.isPlaying=function(){return!!P},this.getPosition=function(){return b},this.getDuration=function(){return u.length===0?0:L(u[u.length-1].timestamp)},this.play=function(){!a.isPlaying()&&k+1<u.length&&(a.onplay&&a.onplay(),_=b,P=Date.now(),R=Date.now(),Y())},this.seek=function(N,W){if(u.length!==0){a.cancel();var G=a.isPlaying();a.pause(),m=function(){m=null,G&&(a.play(),G=null),W&&W()};var X=I(0,u.length-1,N);D(X,function(){b=N,a.onseek&&a.onseek(N),m()})}},this.cancel=function(){m&&(B(),m())},this.pause=function(){B(),S&&clearTimeout(S),b+=Date.now()-R,a.isPlaying()&&(a.onpause&&a.onpause(),R=null,_=null,P=null)}};n.SessionRecording._Frame=function(e,t,a){this.keyframe=!1,this.timestamp=e,this.start=t,this.end=a,this.clientState=null};n.SessionRecording._PlaybackTunnel=function(){var e=this;this.connect=function(a){},this.sendMessage=function(a){},this.disconnect=function(){},this.receiveInstruction=function(a,s){e.oninstruction&&e.oninstruction(a,s)}};var n=n||{};n.Status=function(r,e){var t=this;this.code=r,this.message=e,this.isError=function(){return t.code<0||t.code>255}};n.Status.Code={SUCCESS:0,UNSUPPORTED:256,SERVER_ERROR:512,SERVER_BUSY:513,UPSTREAM_TIMEOUT:514,UPSTREAM_ERROR:515,RESOURCE_NOT_FOUND:516,RESOURCE_CONFLICT:517,RESOURCE_CLOSED:518,UPSTREAM_NOT_FOUND:519,UPSTREAM_UNAVAILABLE:520,SESSION_CONFLICT:521,SESSION_TIMEOUT:522,SESSION_CLOSED:523,CLIENT_BAD_REQUEST:768,CLIENT_UNAUTHORIZED:769,CLIENT_FORBIDDEN:771,CLIENT_TIMEOUT:776,CLIENT_OVERRUN:781,CLIENT_BAD_TYPE:783,CLIENT_TOO_MANY:797};n.Status.Code.fromHTTPCode=function(e){switch(e){case 400:return n.Status.Code.CLIENT_BAD_REQUEST;case 403:return n.Status.Code.CLIENT_FORBIDDEN;case 404:return n.Status.Code.RESOURCE_NOT_FOUND;case 429:return n.Status.Code.CLIENT_TOO_MANY;case 503:return n.Status.Code.SERVER_BUSY}return n.Status.Code.SERVER_ERROR};n.Status.Code.fromWebSocketCode=function(e){switch(e){case 1e3:return n.Status.Code.SUCCESS;case 1006:case 1015:return n.Status.Code.UPSTREAM_NOT_FOUND;case 1001:case 1012:case 1013:case 1014:return n.Status.Code.UPSTREAM_UNAVAILABLE}return n.Status.Code.SERVER_ERROR};var n=n||{};n.StringReader=function(r){var e=this,t=new n.UTF8Parser,a=new n.ArrayBufferReader(r);a.ondata=function(s){var o=t.decode(s);e.ontext&&e.ontext(o)},a.onend=function(){e.onend&&e.onend()},this.ontext=null,this.onend=null};var n=n||{};n.StringWriter=function(r){var e=this,t=new n.ArrayBufferWriter(r),a=new Uint8Array(8192),s=0;t.onack=function(x){e.onack&&e.onack(x)};function o(x){if(s+x>=a.length){var u=new Uint8Array((s+x)*2);u.set(a),a=u}s+=x}function d(x){var u,y;if(x<=127)u=0,y=1;else if(x<=2047)u=192,y=2;else if(x<=65535)u=224,y=3;else if(x<=2097151)u=240,y=4;else{d(65533);return}o(y);for(var w=s-1,T=1;T<y;T++)a[w--]=128|x&63,x>>=6;a[w]=u|x}function c(x){for(var u=0;u<x.length;u++){var y=x.charCodeAt(u);d(y)}if(s>0){var w=a.subarray(0,s);return s=0,w}}this.sendText=function(x){x.length&&t.sendData(c(x))},this.sendEnd=function(){t.sendEnd()},this.onack=null};var n=n||{};n.Touch=function(e){n.Event.Target.call(this);var t=this,a=new n.IntegerPool,s={},o=Math.floor(16*window.devicePixelRatio);this.touches={},this.activeTouches=0;var d=function(u){return u in s?s[u]:s[u]=a.next()},c=function(u){var y=d(u);return delete s[u],a.free(y),y};e.addEventListener("touchstart",function(u){for(var y=0;y<u.changedTouches.length;y++){var w=u.changedTouches[y],T=d(w.identifier);if(!t.touches[T]){var k=t.touches[T]=new n.Touch.State({id:T,radiusX:w.radiusX||o,radiusY:w.radiusY||o,angle:w.angle||0,force:w.force||1});t.activeTouches++,k.fromClientPosition(e,w.clientX,w.clientY),t.dispatch(new n.Touch.Event("touchmove",u,k))}}},!1),e.addEventListener("touchmove",function(u){for(var y=0;y<u.changedTouches.length;y++){var w=u.changedTouches[y],T=d(w.identifier),k=t.touches[T];k&&(w.force&&(k.force=w.force),k.angle=w.angle||0,k.radiusX=w.radiusX||o,k.radiusY=w.radiusY||o,k.fromClientPosition(e,w.clientX,w.clientY),t.dispatch(new n.Touch.Event("touchmove",u,k)))}},!1),e.addEventListener("touchend",function(u){for(var y=0;y<u.changedTouches.length;y++){var w=u.changedTouches[y],T=c(w.identifier),k=t.touches[T];k&&(delete t.touches[T],t.activeTouches--,k.force=0,k.fromClientPosition(e,w.clientX,w.clientY),t.dispatch(new n.Touch.Event("touchend",u,k)))}},!1)};n.Touch.State=function(e){e=e||{},n.Position.call(this,e),this.id=e.id||0,this.radiusX=e.radiusX||0,this.radiusY=e.radiusY||0,this.angle=e.angle||0,this.force=e.force||1};n.Touch.Event=function(e,t,a){n.Event.DOMEvent.call(this,e,[t]),this.state=a};var n=n||{};n.Tunnel=function(){this.connect=function(r){},this.disconnect=function(){},this.sendMessage=function(r){},this.setState=function(r){r!==this.state&&(this.state=r,this.onstatechange&&this.onstatechange(r))},this.setUUID=function(e){this.uuid=e,this.onuuid&&this.onuuid(e)},this.isConnected=function(){return this.state===n.Tunnel.State.OPEN||this.state===n.Tunnel.State.UNSTABLE},this.state=n.Tunnel.State.CLOSED,this.receiveTimeout=15e3,this.unstableThreshold=1500,this.uuid=null,this.onuuid=null,this.onerror=null,this.onstatechange=null,this.oninstruction=null};n.Tunnel.INTERNAL_DATA_OPCODE="";n.Tunnel.State={CONNECTING:0,OPEN:1,CLOSED:2,UNSTABLE:3};n.HTTPTunnel=function(r,e,t){var a=this,s=r+"?connect",o=r+"?read:",d=r+"?write:",c=1,x=0,u=c,y=!1,w="",T=!!e,k=null,_=null,P=null,b=500,l=t||{},v="Guacamole-Tunnel-Token",g=null;function p(i,f){for(var h in f)i.setRequestHeader(h,f[h])}var m=function(){window.clearTimeout(k),window.clearTimeout(_),a.state===n.Tunnel.State.UNSTABLE&&a.setState(n.Tunnel.State.OPEN),k=window.setTimeout(function(){S(new n.Status(n.Status.Code.UPSTREAM_TIMEOUT,"Server timeout."))},a.receiveTimeout),_=window.setTimeout(function(){a.setState(n.Tunnel.State.UNSTABLE)},a.unstableThreshold)};function S(i){window.clearTimeout(k),window.clearTimeout(_),window.clearInterval(P),a.state!==n.Tunnel.State.CLOSED&&(i.code!==n.Status.Code.SUCCESS&&a.onerror&&(a.state===n.Tunnel.State.CONNECTING||i.code!==n.Status.Code.RESOURCE_NOT_FOUND)&&a.onerror(i),y=!1,a.setState(n.Tunnel.State.CLOSED))}this.sendMessage=function(){a.isConnected()&&arguments.length&&(w+=n.Parser.toInstruction(arguments),y||R())};function R(){if(a.isConnected())if(w.length>0){y=!0;var i=new XMLHttpRequest;i.open("POST",d+a.uuid),i.withCredentials=T,p(i,l),i.setRequestHeader("Content-type","application/octet-stream"),i.setRequestHeader(v,g),i.onreadystatechange=function(){i.readyState===4&&(m(),i.status!==200?M(i):R())},i.send(w),w=""}else y=!1}function M(i){var f=parseInt(i.getResponseHeader("Guacamole-Status-Code"));if(f){var h=i.getResponseHeader("Guacamole-Error-Message");S(new n.Status(f,h))}else i.status?S(new n.Status(n.Status.Code.fromHTTPCode(i.status),i.statusText)):S(new n.Status(n.Status.Code.UPSTREAM_NOT_FOUND))}function O(i){var f=null,h=null,C=0,A=new n.Parser;A.oninstruction=function I(E,D){E===n.Tunnel.INTERNAL_DATA_OPCODE&&D.length===0?(A=new n.Parser,A.oninstruction=I,f&&clearInterval(f),i.onreadystatechange=null,i.abort(),h&&O(h)):E!==n.Tunnel.INTERNAL_DATA_OPCODE&&a.oninstruction&&a.oninstruction(E,D)};function L(){if(!a.isConnected()){f!==null&&clearInterval(f);return}if(!(i.readyState<2)){var I;try{I=i.status}catch{I=200}if(!h&&I===200&&(h=H()),i.readyState===3||i.readyState===4){if(m(),u===c&&(i.readyState===3&&!f?f=setInterval(L,30):i.readyState===4&&f&&clearInterval(f)),i.status===0){a.disconnect();return}else if(i.status!==200){M(i);return}var E;try{E=i.responseText}catch{return}try{A.receive(E,!0)}catch(D){S(new n.Status(n.Status.Code.SERVER_ERROR,D.message));return}}}}u===c?i.onreadystatechange=function(){i.readyState===3&&(C++,C>=2&&(u=x,i.onreadystatechange=L)),L()}:i.onreadystatechange=L,L()}var F=0;function H(){var i=new XMLHttpRequest;return i.open("GET",o+a.uuid+":"+F++),i.setRequestHeader(v,g),i.withCredentials=T,p(i,l),i.send(null),i}this.connect=function(i){m(),a.setState(n.Tunnel.State.CONNECTING);var f=new XMLHttpRequest;f.onreadystatechange=function(){if(f.readyState===4){if(f.status!==200){M(f);return}if(m(),a.setUUID(f.responseText),g=f.getResponseHeader(v),!g){S(new n.Status(n.Status.Code.UPSTREAM_NOT_FOUND));return}a.setState(n.Tunnel.State.OPEN),P=setInterval(function(){a.sendMessage("nop")},b),O(H())}},f.open("POST",s,!0),f.withCredentials=T,p(f,l),f.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8"),f.send(i)},this.disconnect=function(){S(new n.Status(n.Status.Code.SUCCESS,"Manually closed."))}};n.HTTPTunnel.prototype=new n.Tunnel;n.WebSocketTunnel=function(r){var e=this,t=null,a=null,s=null,o=null,d=null,c={"http:":"ws:","https:":"wss:"},x=500,u=0;if(r.substring(0,3)!=="ws:"&&r.substring(0,4)!=="wss:"){var y=c[window.location.protocol];if(r.substring(0,1)==="/")r=y+"//"+window.location.host+r;else{var w=window.location.pathname.lastIndexOf("/"),T=window.location.pathname.substring(0,w+1);r=y+"//"+window.location.host+T+r}}var k=function(){var l=new Date().getTime();e.sendMessage(n.Tunnel.INTERNAL_DATA_OPCODE,"ping",l),u=l},_=function(){window.clearTimeout(s),window.clearTimeout(o),window.clearTimeout(d),e.state===n.Tunnel.State.UNSTABLE&&e.setState(n.Tunnel.State.OPEN),s=window.setTimeout(function(){P(new n.Status(n.Status.Code.UPSTREAM_TIMEOUT,"Server timeout."))},e.receiveTimeout),o=window.setTimeout(function(){e.setState(n.Tunnel.State.UNSTABLE)},e.unstableThreshold);var l=new Date().getTime(),v=Math.max(u+x-l,0);v>0?d=window.setTimeout(k,v):k()};function P(b){window.clearTimeout(s),window.clearTimeout(o),window.clearTimeout(d),e.state!==n.Tunnel.State.CLOSED&&(b.code!==n.Status.Code.SUCCESS&&e.onerror&&e.onerror(b),e.setState(n.Tunnel.State.CLOSED),a.close())}this.sendMessage=function(b){e.isConnected()&&arguments.length&&a.send(n.Parser.toInstruction(arguments))},this.connect=function(b){_(),e.setState(n.Tunnel.State.CONNECTING),t=new n.Parser,t.oninstruction=function(v,g){e.uuid===null&&(v===n.Tunnel.INTERNAL_DATA_OPCODE&&g.length===1&&e.setUUID(g[0]),e.setState(n.Tunnel.State.OPEN)),v!==n.Tunnel.INTERNAL_DATA_OPCODE&&e.oninstruction&&e.oninstruction(v,g)},a=new WebSocket(r+"?"+b,"guacamole"),a.onopen=function(l){_()},a.onclose=function(l){l.reason?P(new n.Status(parseInt(l.reason),l.reason)):l.code?P(new n.Status(n.Status.Code.fromWebSocketCode(l.code))):P(new n.Status(n.Status.Code.UPSTREAM_NOT_FOUND))},a.onmessage=function(l){_();try{t.receive(l.data)}catch(v){P(new n.Status(n.Status.Code.SERVER_ERROR,v.message))}}},this.disconnect=function(){P(new n.Status(n.Status.Code.SUCCESS,"Manually closed."))}};n.WebSocketTunnel.prototype=new n.Tunnel;n.ChainedTunnel=function(r){for(var e=this,t,a=[],s=null,o=0;o<arguments.length;o++)a.push(arguments[o]);function d(c){e.disconnect=c.disconnect,e.sendMessage=c.sendMessage;var x=function(w){if(w&&w.code===n.Status.Code.UPSTREAM_TIMEOUT)return a=[],null;var T=a.shift();return T&&(c.onerror=null,c.oninstruction=null,c.onstatechange=null,d(T)),T};function u(){c.onstatechange=e.onstatechange,c.oninstruction=e.oninstruction,c.onerror=e.onerror,c.uuid&&e.setUUID(c.uuid),c.onuuid=function(w){e.setUUID(w)},s=c}c.onstatechange=function(y){switch(y){case n.Tunnel.State.OPEN:u(),e.onstatechange&&e.onstatechange(y);break;case n.Tunnel.State.CLOSED:!x()&&e.onstatechange&&e.onstatechange(y);break}},c.oninstruction=function(y,w){u(),e.oninstruction&&e.oninstruction(y,w)},c.onerror=function(y){!x(y)&&e.onerror&&e.onerror(y)},c.connect(t)}this.connect=function(c){t=c;var x=s||a.shift();x?d(x):e.onerror&&e.onerror(n.Status.Code.SERVER_ERROR,"No tunnels to try.")}};n.ChainedTunnel.prototype=new n.Tunnel;n.StaticHTTPTunnel=function(e,t,a){var s=this,o=null,d=a||{};this.size=null,this.sendMessage=function(x){},this.connect=function(x){s.disconnect(),s.setState(n.Tunnel.State.CONNECTING);var u=new n.Parser,y=new n.UTF8Parser;u.oninstruction=function(T,k){s.oninstruction&&s.oninstruction(T,k)},o=new AbortController,fetch(e,{headers:d,credentials:t?"include":"same-origin",signal:o.signal}).then(function(T){if(!T.ok){s.onerror&&s.onerror(new n.Status(n.Status.Code.fromHTTPCode(T.status),T.statusText)),s.disconnect();return}s.size=T.headers.get("Content-Length"),s.setState(n.Tunnel.State.OPEN);var k=T.body.getReader(),_=function P(b){if(b.done){s.disconnect();return}u.receive(y.decode(b.value)),k.read().then(P)};k.read().then(_)})},this.disconnect=function(){o&&(o.abort(),o=null),s.setState(n.Tunnel.State.CLOSED)}};n.StaticHTTPTunnel.prototype=new n.Tunnel;var n=n||{};n.UTF8Parser=function(){var e=0,t=0;this.decode=function(s){for(var o="",d=new Uint8Array(s),c=0;c<d.length;c++){var x=d[c];e===0?(x|127)===127?o+=String.fromCharCode(x):(x|31)===223?(t=x&31,e=1):(x|15)===239?(t=x&15,e=2):(x|7)===247?(t=x&7,e=3):o+="\\uFFFD":(x|63)===191?(t=t<<6|x&63,e--,e===0&&(o+=String.fromCharCode(t))):(e=0,o+="\\uFFFD")}return o}};var n=n||{};n.API_VERSION="1.6.0";var n=n||{};n.VideoPlayer=function(){this.sync=function(){}};n.VideoPlayer.isSupportedType=function(e){return!1};n.VideoPlayer.getSupportedTypes=function(){return[]};n.VideoPlayer.getInstance=function(e,t,a){return null};typeof window<"u"&&(window.Guacamole=n);function Ue(r){return r===517||r===521?"conflict":r===519||r===520?"unreachable":"other"}function ke(r,e,t){let a=window.Guacamole,[s,o=""]=r.split("?",2),d=new a.WebSocketTunnel(s),c=new a.Client(d),x=c.getDisplay(),u=x.getElement();e.replaceChildren(u);let y=()=>{let b=x.getWidth();b>0&&x.scale(e.clientWidth/b)};x.onresize=y;let w=new ResizeObserver(y);w.observe(e);let T=!1,k=!1,_=(b,l)=>{T||(T=!0,w.disconnect(),t.lost(b,k?Ue(l):"blocked"))};d.onerror=b=>_(b.message||`Tunnel error ${b.code}`,b.code),c.onerror=b=>_(b.message||`Client error ${b.code}`,b.code),c.onstatechange=b=>{b===3?t.connected():b===5&&_("Disconnected")};let P=window.WebSocket;window.WebSocket=class extends P{constructor(b,l){super(b,l),this.addEventListener("open",()=>k=!0)}};try{c.connect(o)}finally{window.WebSocket=P}return{close:(b=!1)=>{let l;if(b&&x.getWidth()>0&&x.getHeight()>0)try{l=x.flatten().toDataURL("image/jpeg",.7),pe(e,l)}catch{l=void 0}l||e.replaceChildren(),T=!0,w.disconnect(),c.onstatechange=null,c.onerror=null,d.onerror=null;try{c.disconnect()}catch{}return l}}}function pe(r,e){let t=new Image;t.src=e,t.alt="The last frame of the computer screen",r.replaceChildren(t)}var ye={sessionId:null,status:"starting",steps:[],text:""},Fe=200;function be(r){let e=r.replace(/^mcp__[^_]+__/,"").replace(/[_-]+/g," ").trim();return e?e.charAt(0).toUpperCase()+e.slice(1):r}var Be=/^\\[[A-Za-z ]+\\]/,Ce=160;function Ke(r){if(!/^[[{]/.test(r))return!1;try{return typeof JSON.parse(r)=="object"}catch{return/^(\\[\\s*[{"\\]]|\\{\\s*")/.test(r)}}function He(r){let e=r.trim();return!e||Be.test(e)||Ke(e)?null:e.length>Ce?`${e.slice(0,Ce-1)}\\u2026`:e}function Ge(r){let e=r?.title;return typeof e=="string"&&e.trim()?e.trim():void 0}function ze(r,e){let t=[...r],a=()=>t.at(-1),s=o=>t.find(d=>d.callId===o);for(let o of e)if(o.type==="tool-input-start")o.toolName!=="execute"&&!s(o.toolCallId)&&t.push({title:be(o.toolName),callId:o.toolCallId});else if(o.type==="tool-input-available"){let d=Ge(o.input)??(o.toolName==="execute"?void 0:be(o.toolName));if(!d)continue;let c=s(o.toolCallId);c?c.title=d:t.push({title:d,callId:o.toolCallId})}else if((o.type==="data-progress"||o.type==="data-status")&&o.data?.text){let d=He(o.data.text),c=a();if(!d||d===c?.title||d===c?.detail)continue;c?.callId?t[t.length-1]={...c,detail:d}:t.push({title:d})}return t}function _e(r){let e=s=>s.replace(/[&<>"\']/g,o=>({"&":"&amp;","<":"&lt;",">":"&gt;",\'"\':"&quot;","\'":"&#39;"})[o]),t=[],a=r.replace(/\\uE000/g,"").replace(/`([^`\\n]+)`/g,(s,o)=>(t.push(`<code>${e(o)}</code>`),`\\uE000${t.length-1}\\uE000`));return e(a).replace(/\\*\\*([^\\n]+?)\\*\\*/g,"<strong>$1</strong>").replace(/\\uE000(\\d+)\\uE000/g,(s,o)=>t[Number(o)])}function Ie(r,e){let t=ze(r.steps,e.events??[]);return{...r,status:e.status,steps:t.slice(-Fe),text:e.text??r.text,approval:e.status==="needs_approval"?e.approval:void 0,error:e.error,playgroundUrl:e.playground_url??r.playgroundUrl}}function j(r){return r.status==="idle"||r.status==="error"}function Ae(r){return r.status==="starting"?"Starting Sai\\u2026":r.status==="needs_approval"?"Sai needs your OK to continue":r.status==="error"?"Sai stopped with an error":r.status==="idle"?"Done":r.steps.at(-1)?.title??"Sai is working\\u2026"}var q=window.parent===window?new oe:new ae;q instanceof oe&&document.documentElement.classList.add("standalone");var K=ye,ce,ge=!1,ve=null,ue=null,le=null,$,se=null,me=!1,ee="inline",de=[],We=new Set(["Claude"]),Pe="",re=r=>new Promise(e=>setTimeout(e,r)),z=r=>document.getElementById(r),Re=r=>r.replace(/[&<>"\']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",\'"\':"&quot;","\'":"&#39;"})[e]);function Z(){z("headline").textContent=Ae(K),z("dot").className=`dot ${K.status}`;let r=K.steps.slice(-12);z("steps").innerHTML=r.map(t=>`<li>${Re(t.title)}${t.detail?`<span class="detail">${Re(t.detail)}</span>`:""}</li>`).join(""),z("steps-more").textContent=K.steps.length>r.length?`${K.steps.length-r.length} earlier steps`:"",z("answer").hidden=!(j(K)&&K.text),z("answer").innerHTML=_e(K.text),z("error").hidden=!K.error,z("error").textContent=K.error??"";let e=K.approval;z("approval").hidden=!e,e&&(z("approval-title").textContent=e.title,z("approval-text").textContent=e.command?`${e.description}\n${e.command}`:e.description,z("approve").hidden=z("approve-task").hidden=z("deny").hidden=e.isLinkOnly,z("open-approval").hidden=!e.isLinkOnly||!e.approvalUrl),z("live").hidden=!K.playgroundUrl,z("screen-wrap").hidden=!ve||j(K)&&!$&&!se,z("screen").hidden=xe&&!$,document.documentElement.dataset.mode=ee,z("pin").hidden=ee!=="inline"||!de.includes("pip"),z("expand").hidden=ee!=="inline"||!de.includes("fullscreen"),z("restore").hidden=ee==="inline",z("watch-here").hidden=!fe||j(K),z("popout").hidden=!(q instanceof ae)||!le||de.includes("pip")||!We.has(Pe),q.sizeChanged(document.documentElement.scrollHeight)}function we(r){if(r.structuredContent)return r.structuredContent;let e=r.content?.find(t=>t.type==="text")?.text;try{return e?JSON.parse(e):void 0}catch{return}}function Me(r,e){r?(K={...K,status:r.status,steps:r.steps,text:r.text,error:r.error},r.frame&&($=r.frame,pe(z("screen"),r.frame))):K={...K,status:"idle",text:e??""},Z()}function Le(){if(!ue)return;let r={status:K.status,steps:K.steps.map(({title:e,detail:t})=>({title:e,...t&&{detail:t}})),text:K.text,...K.error&&{error:K.error},...$&&{frame:$}};q.callTool("sai_view_save",{task_id:ue,snapshot:r}).catch(()=>{})}async function De(r){if(ge||!K.sessionId)return;ge=!0;let e=!0,t=a=>{e&&r(a),e=!1};try{for(;K.sessionId&&!j(K);){let a=Date.now(),s=await q.callTool("sai_view_poll",{session_id:K.sessionId,...ce&&{cursor:ce},...ue&&{task_id:ue}}),o=we(s);if(s.isError||!o){await re(5e3);continue}if(o.archived||o.expired){K={...K,playgroundUrl:o.playground_url??K.playgroundUrl},Me(o.saved,o.archived?"Sai has started another task on this computer since; this one has ended.":"This task\'s details are no longer available (Sai was restarted)."),t(!1);return}ce=o.cursor,K=Ie(K,o),e&&j(K)&&o.saved&&Me({...o.saved,status:K.status}),Z(),t(!j(K)),j(K)||await re(Math.max(0,4e3-(Date.now()-a)))}Le()}finally{ge=!1}}function Q(r){z("screen-note").textContent=r,z("screen-note").hidden=!r}async function Ve(){for(;!j(K);)await re(1e3);return"done"}var fe=!1,xe=!1;async function Oe(){if(me||!ve)return;me=!0,fe=!1,Z();let r=3e3,e=0;try{for(;!j(K)&&!fe;){let t=await q.callTool("sai_view_live",{machine_id:ve}).catch(()=>null),a=t&&!t.isError?we(t):void 0,s=typeof a?.websocket_url=="string"?a.websocket_url:null;if(!s){Q("Waiting for the computer to start\\u2026"),await re(8e3);continue}Q("Connecting to the computer\\u2026");let o,d=new Promise(x=>{o=ke(s,z("screen"),{connected:()=>{r=3e3,Q("")},lost:(u,y)=>x(y)})});se=o;let c=await Promise.race([d,Ve()]);if(j(K))break;if($=o.close(!0)??$,se=null,c==="conflict"){fe=!0;break}if(c==="blocked"&&++e>=2){Q(le?"This app keeps the card offline, so the screen can\'t show here. Open the live view link in the chat, or the Playground.":"This app keeps the card offline, so the screen can\'t show here. Watch it in the Playground."),xe=!0;break}if(c==="unreachable"){Q("Waiting for the computer to wake up\\u2026"),await re(8e3);continue}Q("Reconnecting\\u2026"),await re(r),r=Math.min(r*2,3e4)}}finally{$=se?.close(!0)??$,se=null,me=!1,xe||Q(fe&&!j(K)?"Being watched somewhere else.":""),$&&j(K)&&Le(),Z()}}async function Se(r){let e=K.approval;e&&(K={...K,approval:void 0,status:"running"},Z(),await q.callTool("sai_task_approve",{approval_id:e.approvalId,decision:r}).catch(()=>{}),De(()=>{}))}q.onToolResult=r=>{let e=we(r),t=typeof e?.session_id=="string"?e.session_id:null;!t||t===K.sessionId||(K={...ye,sessionId:t,status:"running"},ce=void 0,ve=typeof e?.machine_id=="string"?e.machine_id:null,ue=typeof e?.task_id=="string"?e.task_id:null,le=typeof e?.watch_url=="string"?e.watch_url:null,Z(),De(a=>{a&&Oe()}))};function Ne(r){r&&(r.theme&&(document.documentElement.dataset.theme=r.theme),r.availableDisplayModes&&(de=r.availableDisplayModes),r.displayMode&&(ee=r.displayMode),Z())}q.onHostContext=Ne;async function Te(r){ee=await q.requestDisplayMode(r).catch(()=>ee),Z()}z("pin").onclick=()=>{Te("pip")};z("watch-here").onclick=()=>{Oe()};z("popout").onclick=()=>le&&q.openLink(le);z("expand").onclick=()=>{Te("fullscreen")};z("restore").onclick=()=>{Te("inline")};z("approve").onclick=()=>{Se("approve")};z("approve-task").onclick=()=>{Se("approve_for_task")};z("deny").onclick=()=>{Se("deny")};z("open-approval").onclick=()=>K.approval?.approvalUrl&&q.openLink(K.approval.approvalUrl);z("live").onclick=()=>K.playgroundUrl&&q.openLink(K.playgroundUrl);q.connect("0.2.1").then(r=>{Pe=r.hostInfo?.name??"",Ne(r.hostContext),Z()}).catch(()=>Z());})();\n\n </script>\n </body>\n</html>\n' : "<!doctype html><html><body><p>Sai task view (build with npm run build)</p></body></html>";
28003
+ }
28004
+ function playgroundUrl(apiUrl) {
28005
+ return /staging/.test(apiUrl) ? "https://staging.platform.simular.cloud/playground" : "https://platform.simular.ai/playground";
28006
+ }
28007
+ var LIVE_ORIGINS = [
28008
+ "wss://proxy.vm.api.simular.cloud",
28009
+ "https://proxy.vm.api.simular.cloud",
28010
+ "wss://staging.proxy.vm.api.simular.cloud",
28011
+ "https://staging.proxy.vm.api.simular.cloud"
28012
+ ];
28013
+ var VIEW_POLL_TIMEOUT_S = 20;
28014
+ var pollArgs = {
28015
+ session_id: external_exports.string().min(1),
28016
+ cursor: external_exports.string().optional(),
28017
+ task_id: external_exports.string().optional()
28018
+ };
28019
+ var saveArgs = {
28020
+ task_id: external_exports.string().min(1),
28021
+ snapshot: external_exports.object({
28022
+ status: external_exports.string().max(32),
28023
+ steps: external_exports.array(external_exports.object({ title: external_exports.string().max(400), detail: external_exports.string().max(400).optional() })).max(200),
28024
+ text: external_exports.string().max(5e4),
28025
+ error: external_exports.string().max(4e3).optional(),
28026
+ frame: external_exports.string().max(15e5).regex(/^data:image\/jpeg;base64,[A-Za-z0-9+/=]+$/).optional()
28027
+ })
28028
+ };
28029
+ var liveArgs = { machine_id: external_exports.string().min(1) };
28030
+ function taskViewTools(deps) {
28031
+ return {
28032
+ sai_view_poll: (raw) => deps.run(async () => {
28033
+ const { task_id, ...args } = external_exports.object(pollArgs).parse(raw);
28034
+ const playground_url = playgroundUrl(deps.apiUrl);
28035
+ const state = task_id ? deps.tasks.state(task_id, args.session_id) : "live";
28036
+ const saved = task_id ? deps.tasks.saved(task_id) : void 0;
28037
+ if (state !== "live") return { [state]: true, ...saved && { saved }, playground_url };
28038
+ return {
28039
+ ...await deps.wait({ ...args, timeout_s: VIEW_POLL_TIMEOUT_S }),
28040
+ ...saved && { saved },
28041
+ playground_url
28042
+ };
28043
+ }),
28044
+ sai_view_save: (raw) => deps.run(async () => {
28045
+ const args = external_exports.object(saveArgs).parse(raw);
28046
+ return { ok: deps.tasks.save(args.task_id, args.snapshot) };
28047
+ }),
28048
+ sai_view_live: (raw) => deps.run(async () => {
28049
+ const args = external_exports.object(liveArgs).parse(raw);
28050
+ const live = await deps.liveScreen(args.machine_id);
28051
+ return { websocket_url: live.websocketUrl, width: live.width, height: live.height };
28052
+ })
28053
+ };
28054
+ }
28055
+ function registerTaskView(mcp, deps) {
28056
+ const tools = taskViewTools(deps);
28057
+ mcp.registerResource(
28058
+ "sai-task-view",
28059
+ TASK_VIEW_URI,
28060
+ { title: "Sai task", description: "Live progress of a Sai task", mimeType: MCP_APP_MIME },
28061
+ async () => ({
28062
+ contents: [
28063
+ {
28064
+ uri: TASK_VIEW_URI,
28065
+ mimeType: MCP_APP_MIME,
28066
+ text: taskViewHtml(),
28067
+ // Only the Guacamole proxies, for the live screen's WebSocket tunnel;
28068
+ // everything else comes through the host.
28069
+ _meta: {
28070
+ ui: { csp: { connectDomains: LIVE_ORIGINS, resourceDomains: [] }, prefersBorder: true }
28071
+ }
28072
+ }
28073
+ ]
28074
+ })
28075
+ );
28076
+ mcp.registerTool(
28077
+ "sai_view_poll",
28078
+ {
28079
+ description: "Internal to the Sai task view (MCP Apps). Agents: do not call this; use sai_task_wait.",
28080
+ inputSchema: pollArgs,
28081
+ _meta: { ui: { resourceUri: TASK_VIEW_URI, visibility: ["app"] } }
28082
+ },
28083
+ (args) => tools.sai_view_poll(args)
28084
+ );
28085
+ mcp.registerTool(
28086
+ "sai_view_save",
28087
+ {
28088
+ description: "Internal to the Sai task view (MCP Apps): keeps an ended task's view for when it is reopened. Agents: do not call this.",
28089
+ inputSchema: saveArgs,
28090
+ _meta: { ui: { resourceUri: TASK_VIEW_URI, visibility: ["app"] } }
28091
+ },
28092
+ (args) => tools.sai_view_save(args)
28093
+ );
28094
+ mcp.registerTool(
28095
+ "sai_view_live",
28096
+ {
28097
+ description: "Internal to the Sai task view (MCP Apps): a view-only live screen tunnel. Agents: do not call this.",
28098
+ inputSchema: liveArgs,
28099
+ _meta: { ui: { resourceUri: TASK_VIEW_URI, visibility: ["app"] } }
28100
+ },
28101
+ (args) => tools.sai_view_live(args)
28102
+ );
28103
+ }
28104
+
28105
+ // src/local-view.ts
28106
+ import { randomBytes, timingSafeEqual } from "node:crypto";
28107
+ import { createServer } from "node:http";
28108
+ var LOCAL_VIEW_TOOLS = [
28109
+ "sai_view_poll",
28110
+ "sai_view_live",
28111
+ "sai_view_save",
28112
+ "sai_task_approve"
28113
+ ];
28114
+ var MAX_BODY = 2 * 1024 * 1024;
28115
+ var CSP = [
28116
+ "default-src 'none'",
28117
+ "script-src 'unsafe-inline'",
28118
+ "style-src 'unsafe-inline'",
28119
+ "img-src data: blob:",
28120
+ `connect-src 'self' ${LIVE_ORIGINS.join(" ")}`,
28121
+ "base-uri 'none'",
28122
+ "form-action 'none'",
28123
+ "frame-ancestors 'none'"
28124
+ ].join("; ");
28125
+ var LocalView = class {
28126
+ constructor(deps) {
28127
+ this.deps = deps;
28128
+ }
28129
+ deps;
28130
+ token = randomBytes(24).toString("hex");
28131
+ listening;
28132
+ /** The page for one task; starts the listener on first use. */
28133
+ async urlFor(sessionId, machineId, taskId) {
28134
+ const port = await this.port();
28135
+ const q = new URLSearchParams({ s: sessionId, m: machineId, k: taskId, t: this.token });
28136
+ return `http://127.0.0.1:${port}/task?${q}`;
28137
+ }
28138
+ port() {
28139
+ this.listening ??= new Promise((resolve3, reject) => {
28140
+ const server = createServer((req, res) => {
28141
+ this.handle(req, res, server.address().port).catch((err) => {
28142
+ this.deps.log(`local view: ${err instanceof Error ? err.message : String(err)}`);
28143
+ if (!res.headersSent) send(res, 500, "text/plain", "Internal error");
28144
+ });
28145
+ });
28146
+ server.once("error", (err) => {
28147
+ this.listening = void 0;
28148
+ reject(err);
28149
+ });
28150
+ server.listen(0, "127.0.0.1", () => {
28151
+ server.unref();
28152
+ resolve3(server.address().port);
28153
+ });
28154
+ });
28155
+ return this.listening;
28156
+ }
28157
+ tokenOk(given) {
28158
+ if (!given) return false;
28159
+ const a = Buffer.from(given);
28160
+ const b = Buffer.from(this.token);
28161
+ return a.length === b.length && timingSafeEqual(a, b);
28162
+ }
28163
+ async handle(req, res, port) {
28164
+ const origin = `http://127.0.0.1:${port}`;
28165
+ if (req.headers.host !== `127.0.0.1:${port}`) return send(res, 421, "text/plain", "Wrong host");
28166
+ const url = new URL(req.url ?? "/", origin);
28167
+ if (req.method === "GET" && url.pathname === "/task") {
28168
+ if (!this.tokenOk(url.searchParams.get("t"))) return send(res, 403, "text/plain", "Forbidden");
28169
+ return send(res, 200, "text/html; charset=utf-8", this.deps.html(), {
28170
+ "Content-Security-Policy": CSP
28171
+ });
28172
+ }
28173
+ if (req.method === "POST" && url.pathname === "/tool") {
28174
+ const token = req.headers["x-sai-token"];
28175
+ if (req.headers.origin !== origin || !this.tokenOk(typeof token === "string" ? token : null))
28176
+ return send(res, 403, "text/plain", "Forbidden");
28177
+ if (!String(req.headers["content-type"]).startsWith("application/json"))
28178
+ return send(res, 415, "text/plain", "JSON only");
28179
+ const body = await readBody(req);
28180
+ if (body === null) return send(res, 413, "text/plain", "Too large");
28181
+ let parsed;
28182
+ try {
28183
+ parsed = JSON.parse(body);
28184
+ } catch {
28185
+ return send(res, 400, "text/plain", "Bad JSON");
28186
+ }
28187
+ const name = parsed.name;
28188
+ if (typeof name !== "string" || !LOCAL_VIEW_TOOLS.includes(name))
28189
+ return send(res, 404, "text/plain", "Unknown tool");
28190
+ const args = parsed.arguments && typeof parsed.arguments === "object" ? parsed.arguments : {};
28191
+ const result = await this.deps.call(name, args);
28192
+ return send(res, 200, "application/json", JSON.stringify(result));
28193
+ }
28194
+ send(res, 404, "text/plain", "Not found");
28195
+ }
28196
+ };
28197
+ function send(res, status, type, body, extra = {}) {
28198
+ res.writeHead(status, {
28199
+ "Content-Type": type,
28200
+ "Cache-Control": "no-store",
28201
+ // The page URL carries the token: never leak it in a Referer.
28202
+ "Referrer-Policy": "no-referrer",
28203
+ "X-Content-Type-Options": "nosniff",
28204
+ "X-Frame-Options": "DENY",
28205
+ ...extra
28206
+ });
28207
+ res.end(body);
28208
+ }
28209
+ function readBody(req) {
28210
+ return new Promise((resolve3, reject) => {
28211
+ let size = 0;
28212
+ const chunks = [];
28213
+ req.on("data", (c) => {
28214
+ size += c.length;
28215
+ if (size > MAX_BODY) {
28216
+ resolve3(null);
28217
+ req.destroy();
28218
+ return;
28219
+ }
28220
+ chunks.push(c);
28221
+ });
28222
+ req.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
28223
+ req.on("error", reject);
28224
+ });
28225
+ }
28226
+
28227
+ // src/task-log.ts
28228
+ import { randomBytes as randomBytes2 } from "node:crypto";
28229
+ var MAX_TASKS = 50;
28230
+ var TaskLog = class {
28231
+ tasks = /* @__PURE__ */ new Map();
28232
+ latest = /* @__PURE__ */ new Map();
28233
+ /** A new task on `sessionId`: it becomes that session's live one. */
28234
+ start(sessionId) {
28235
+ const taskId = randomBytes2(9).toString("base64url");
28236
+ this.tasks.set(taskId, { sessionId });
28237
+ this.latest.set(sessionId, taskId);
28238
+ while (this.tasks.size > MAX_TASKS) {
28239
+ const oldest = this.tasks.keys().next().value;
28240
+ this.tasks.delete(oldest);
28241
+ }
28242
+ return taskId;
28243
+ }
28244
+ /** `live`: still its session's newest task; `archived`: a newer one started; `expired`: unknown. */
28245
+ state(taskId, sessionId) {
28246
+ const task = this.tasks.get(taskId);
28247
+ if (!task || task.sessionId !== sessionId) return "expired";
28248
+ return this.latest.get(sessionId) === taskId ? "live" : "archived";
28249
+ }
28250
+ saved(taskId) {
28251
+ return this.tasks.get(taskId)?.saved;
28252
+ }
28253
+ save(taskId, snapshot2) {
28254
+ const task = this.tasks.get(taskId);
28255
+ if (!task) return false;
28256
+ task.saved = snapshot2;
28257
+ return true;
28258
+ }
28259
+ };
28260
+
27978
28261
  // src/init-claude.ts
27979
28262
  import { createHash } from "node:crypto";
27980
28263
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -28064,7 +28347,7 @@ function runInitClaude(options, env) {
28064
28347
 
28065
28348
  // src/server.ts
28066
28349
  var SERVER_NAME = "sai";
28067
- var SERVER_VERSION = "0.1.2";
28350
+ var SERVER_VERSION = "0.2.1";
28068
28351
  var log = (line) => {
28069
28352
  process.stderr.write(`[sai-mcp] ${line}
28070
28353
  `);
@@ -28086,7 +28369,7 @@ async function run(fn) {
28086
28369
  return fail(err);
28087
28370
  }
28088
28371
  }
28089
- function createServer() {
28372
+ function createServer2() {
28090
28373
  const config2 = readConfig();
28091
28374
  const api = new SaiApi(config2.apiUrl, config2.apiKey, void 0, config2.versionTag);
28092
28375
  const source = config2.eventsApi ? new EventsPoller(api) : new StreamCollector(api, log);
@@ -28137,6 +28420,21 @@ function createServer() {
28137
28420
  });
28138
28421
  }
28139
28422
  });
28423
+ const taskLog = new TaskLog();
28424
+ const viewDeps = {
28425
+ tasks: taskLog,
28426
+ // The view wants each step as it happens, not just the end of the turn.
28427
+ wait: (args) => tools.wait(args, { returnOnEvents: true }),
28428
+ apiUrl: config2.apiUrl,
28429
+ run,
28430
+ liveScreen: (machineId) => api.liveScreen(machineId)
28431
+ };
28432
+ const viewTools = taskViewTools(viewDeps);
28433
+ const localView = config2.localView ? new LocalView({
28434
+ html: taskViewHtml,
28435
+ call: (name, args) => name === "sai_task_approve" ? run(() => tools.approve(external_exports.object(toolSchemas.sai_task_approve).parse(args))) : viewTools[name](args),
28436
+ log
28437
+ }) : void 0;
28140
28438
  mcp.registerTool(
28141
28439
  "sai_machines",
28142
28440
  { description: toolDescriptions.sai_machines, inputSchema: toolSchemas.sai_machines },
@@ -28149,8 +28447,21 @@ function createServer() {
28149
28447
  );
28150
28448
  mcp.registerTool(
28151
28449
  "sai_task_start",
28152
- { description: toolDescriptions.sai_task_start, inputSchema: toolSchemas.sai_task_start },
28153
- (args) => run(() => tools.start(args))
28450
+ {
28451
+ description: toolDescriptions.sai_task_start,
28452
+ inputSchema: toolSchemas.sai_task_start,
28453
+ // MCP Apps hosts render the live task view under this call.
28454
+ _meta: TASK_VIEW_TOOL_META
28455
+ },
28456
+ (args) => run(async () => {
28457
+ const started = await tools.start(args);
28458
+ const taskId = taskLog.start(started.session_id);
28459
+ const watchUrl = await localView?.urlFor(started.session_id, started.machine_id, taskId).catch((err) => {
28460
+ log(`local view unavailable: ${err instanceof Error ? err.message : String(err)}`);
28461
+ return void 0;
28462
+ });
28463
+ return { ...started, task_id: taskId, ...watchUrl && { watch_url: watchUrl } };
28464
+ })
28154
28465
  );
28155
28466
  mcp.registerTool(
28156
28467
  "sai_task_wait",
@@ -28172,6 +28483,7 @@ function createServer() {
28172
28483
  { description: toolDescriptions.sai_upload, inputSchema: toolSchemas.sai_upload },
28173
28484
  (args) => run(() => tools.upload(args))
28174
28485
  );
28486
+ registerTaskView(mcp, viewDeps);
28175
28487
  return mcp;
28176
28488
  }
28177
28489
  async function main() {
@@ -28190,7 +28502,7 @@ async function main() {
28190
28502
  }
28191
28503
  process.exit(runInitClaude(parsed.options, { version: SERVER_VERSION }));
28192
28504
  }
28193
- const mcp = createServer();
28505
+ const mcp = createServer2();
28194
28506
  await mcp.connect(new StdioServerTransport());
28195
28507
  }
28196
28508
  main().catch((err) => {
@@ -28200,6 +28512,6 @@ main().catch((err) => {
28200
28512
  export {
28201
28513
  SERVER_NAME,
28202
28514
  SERVER_VERSION,
28203
- createServer
28515
+ createServer2 as createServer
28204
28516
  };
28205
28517
  //# sourceMappingURL=server.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simular-ai/sai-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Sai MCP server — drive your Sai computer-use agent from Claude Code, Codex, or Cursor.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  "dev": "tsx src/server.ts",
29
29
  "build": "node build.mjs",
30
30
  "lint": "eslint . --max-warnings 0",
31
- "typecheck": "tsc --noEmit",
31
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.ui.json",
32
32
  "test": "vitest run",
33
33
  "prepublishOnly": "npm run build"
34
34
  },