@simular-ai/sai-mcp 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/server.js +323 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
99
|
-
|
|
|
100
|
-
| `SAI_API_KEY`
|
|
101
|
-
| `SAI_PUSH`
|
|
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/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 =
|
|
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=>({"&":"&","<":"<",">":">",\'"\':""","\'":"'"})[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=>({"&":"&","<":"<",">":">",\'"\':""","\'":"'"})[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.0").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.
|
|
28350
|
+
var SERVER_VERSION = "0.2.0";
|
|
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
|
|
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
|
-
{
|
|
28153
|
-
|
|
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 =
|
|
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.
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
},
|