ai-remote 0.5.1 → 0.6.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 +40 -0
- package/SKILL.md +123 -13
- package/dist/{cli-chunk-M5GGPPAC.mjs → cli-chunk-L6E2YYPQ.mjs} +19 -5
- package/dist/{cli-chunk-DGYNADUR.mjs → cli-chunk-P4645KKB.mjs} +3 -3
- package/dist/{cli-chunk-WBOYG4KJ.mjs → cli-chunk-TKKENOSJ.mjs} +1 -1
- package/dist/{cli-chunk-X2JMVOWL.mjs → cli-chunk-UD2YES3P.mjs} +33 -22
- package/dist/cli-chunk-WWORJ2NE.mjs +72 -0
- package/dist/{cli-copy-ZBTICP7N.mjs → cli-copy-2HQU2E7D.mjs} +18 -5
- package/dist/{cli-daemon-M3KM2YHW.mjs → cli-daemon-3L36TBIB.mjs} +636 -211
- package/dist/cli-known-hosts-UU427T4N.mjs +12 -0
- package/dist/{cli-secrets-26D3F6EN.mjs → cli-secrets-BMYFPDB2.mjs} +1 -1
- package/dist/cli-shell-JEC6BEME.mjs +11 -0
- package/dist/{cli-window-WLRFUKSW.mjs → cli-window-AWXZ5NJX.mjs} +2 -2
- package/dist/cli.mjs +201 -95
- package/dist/computer.d.ts +77 -0
- package/dist/computer.js +3 -0
- package/dist/index.d.ts +80 -1
- package/dist/index.js +1 -1
- package/dist/protocols.d.ts +108 -12
- package/dist/protocols.js +15 -15
- package/package.json +6 -1
- package/dist/cli-shell-22PZYNSJ.mjs +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -98,17 +98,72 @@ export declare function connectionId(input: Pick<ConnectionSettings, 'protocol'
|
|
|
98
98
|
/** A blank connection, ready for the dialog to fill in. */
|
|
99
99
|
export declare function emptyConnection(protocol?: RemoteProtocol): ConnectionSettings;
|
|
100
100
|
export declare function normalizeDisplaySettings(value: unknown): DisplaySettings;
|
|
101
|
+
/**
|
|
102
|
+
* Anything a caller might have.
|
|
103
|
+
*
|
|
104
|
+
* Every field is `unknown` on purpose. This is the function that a saved card,
|
|
105
|
+
* a URL, a host-list entry and a localStorage blob all arrive at, and none of
|
|
106
|
+
* those had their types checked by anyone. Declaring them as already the right
|
|
107
|
+
* shape would not make them so -- it would only move the guesswork from here,
|
|
108
|
+
* where each field is coerced deliberately, to the callers, where it is not.
|
|
109
|
+
*/
|
|
110
|
+
export type ConnectionInput = {
|
|
111
|
+
[K in keyof ConnectionSettings]?: unknown;
|
|
112
|
+
} & Record<string, unknown>;
|
|
101
113
|
/**
|
|
102
114
|
* Build a complete, valid connection out of whatever a caller happens to have.
|
|
103
115
|
* Partial input from a saved card, a host list entry, a URL or localStorage all
|
|
104
116
|
* arrive here, so the dialog never has to reason about missing fields.
|
|
105
117
|
*/
|
|
106
|
-
export declare function normalizeConnection(input:
|
|
118
|
+
export declare function normalizeConnection(input: ConnectionInput): ConnectionSettings;
|
|
107
119
|
/** What a connection is worth remembering by -- never the password. */
|
|
108
120
|
export type PersistedConnection = Omit<ConnectionSettings, 'password'>;
|
|
109
121
|
export declare function withoutPassword(settings: ConnectionSettings): PersistedConnection;
|
|
110
122
|
/** A one-line description for the dialog's summary row and the status bar. */
|
|
111
123
|
export declare function describeConnection(settings: ConnectionSettings): string;
|
|
124
|
+
/**
|
|
125
|
+
* Carrying VNC credentials on the WebSocket handshake instead of in its URL.
|
|
126
|
+
*
|
|
127
|
+
* VNC is the one protocol whose authentication the gateway performs itself:
|
|
128
|
+
* macOS Screen Sharing needs the Apple Diffie-Hellman (type 30) exchange that
|
|
129
|
+
* noVNC does not implement, so the password has to reach the Worker. Where it
|
|
130
|
+
* travels decides who else gets to read it, and a query string is the one place
|
|
131
|
+
* it must not go -- request URLs are recorded by the platform's own request
|
|
132
|
+
* logging, which no amount of care in this codebase can redact.
|
|
133
|
+
*
|
|
134
|
+
* A browser cannot set headers on a WebSocket, so `Sec-WebSocket-Protocol` is
|
|
135
|
+
* the only field it controls. That is exactly how Kubernetes passes a bearer
|
|
136
|
+
* token to `exec`, and headers are not part of the logged request line.
|
|
137
|
+
*
|
|
138
|
+
* This is confidentiality against logging, not against the gateway: the Worker
|
|
139
|
+
* still sees the password, because Apple DH cannot be computed without it. RDP
|
|
140
|
+
* and SSH authenticate in the browser and never send one here at all.
|
|
141
|
+
*/
|
|
142
|
+
/** Subprotocol the byte relay actually speaks, and the one it echoes back. */
|
|
143
|
+
export declare const WS_BINARY_SUBPROTOCOL = "binary";
|
|
144
|
+
export interface VncCredentials {
|
|
145
|
+
username: string;
|
|
146
|
+
password: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The subprotocols a VNC WebSocket should offer: the real one first, so a
|
|
150
|
+
* gateway that ignores credentials entirely still negotiates a working session.
|
|
151
|
+
*/
|
|
152
|
+
export declare function websocketSubprotocols(credentials: Partial<VncCredentials>): string[];
|
|
153
|
+
/**
|
|
154
|
+
* The credentials an offer list carries, or null when it carries none.
|
|
155
|
+
*
|
|
156
|
+
* A malformed offer is treated as absent rather than as an error: it is
|
|
157
|
+
* attacker-reachable input, and the handshake that follows will fail on its own
|
|
158
|
+
* terms with a message about authentication rather than about base64.
|
|
159
|
+
*/
|
|
160
|
+
export declare function readCredentialSubprotocol(header: string | null): VncCredentials | null;
|
|
161
|
+
/**
|
|
162
|
+
* The subprotocol to accept, which is never the credential offer: echoing that
|
|
163
|
+
* back would put the password in the response headers, and in whatever the
|
|
164
|
+
* browser hands to `WebSocket.protocol`.
|
|
165
|
+
*/
|
|
166
|
+
export declare function selectSubprotocol(header: string | null): string | null;
|
|
112
167
|
export interface SavedDevice {
|
|
113
168
|
id: string;
|
|
114
169
|
label: string;
|
|
@@ -262,3 +317,27 @@ export declare function websocketCloseReason(reason: string): string;
|
|
|
262
317
|
export declare function sendableCloseCode(code: number): number;
|
|
263
318
|
/** Normalize any WebSocket message payload to bytes. */
|
|
264
319
|
export declare function messageToBytes(data: unknown): Promise<Uint8Array | null>;
|
|
320
|
+
/**
|
|
321
|
+
* The part of a WebSocket the protocol engines actually use.
|
|
322
|
+
*
|
|
323
|
+
* The engines were written against a browser WebSocket pointed at the gateway's
|
|
324
|
+
* relay, and the CLI hands them a TCP socket wearing the same face (see
|
|
325
|
+
* `cli/transport.ts`). Naming that face is what lets both be passed without the
|
|
326
|
+
* engines being told which one they got -- and without the seam being an `any`,
|
|
327
|
+
* which is the same thing said less carefully.
|
|
328
|
+
*/
|
|
329
|
+
export interface ByteTransport extends EventTarget {
|
|
330
|
+
readonly readyState: number;
|
|
331
|
+
binaryType: string;
|
|
332
|
+
/** The full range a browser WebSocket takes, so a real one satisfies this. */
|
|
333
|
+
send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void;
|
|
334
|
+
close(code?: number, reason?: string): void;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* A `CustomEvent` whose detail this code knows the shape of.
|
|
338
|
+
*
|
|
339
|
+
* The engines communicate by dispatching events, so their consumers spend a lot
|
|
340
|
+
* of time reaching into `event.detail`. Saying which shape is expected at the
|
|
341
|
+
* point of listening is what keeps that from being unchecked.
|
|
342
|
+
*/
|
|
343
|
+
export type DetailListener<T> = (event: CustomEvent<T>) => void;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var A=["vnc","rdp"],k=["vnc","rdp","ssh"],P=["auto","nla","tls","rdp"],a={vnc:5900,rdp:3389,ssh:22},f=a.ssh,v=15e3,D=5*6e4;function C(t){return A.includes(t)}function g(t){return P.includes(t)}function h(t,e="vnc"){let n=String(t??"").toLowerCase();return C(n)?n:e}function M(t,e="auto"){let n=String(t??"").toLowerCase();return g(n)?n:e}function u(t,e){let n=typeof t=="number"?t:Number.parseInt(String(t??""),10);return Number.isInteger(n)&&n>0&&n<65536?n:e}var d={quality:6,compression:2,scaling:!0,viewOnly:!1,audio:!1};function E(t){let e=t.remoteApp?.program?.trim()??"",n=`${t.protocol||"vnc"}:${t.host.trim()}:${t.port||""}`;return(e?`${n}:${e}`:n).toLowerCase().slice(0,64)}function V(t="vnc"){let e=a[t];return{id:"",label:"",protocol:t,host:"",port:e,username:"",password:"",domain:"",security:"auto",remoteApp:null,display:{...d},ssh:{enabled:!1,port:a.ssh,username:""},openSshOnConnect:!1}}function x(t,e,n,r){let o=typeof t=="number"?t:Number.parseInt(String(t??""),10);return Number.isInteger(o)?Math.min(r,Math.max(n,o)):e}function _(t){let e=t??{};return{quality:x(e.quality,d.quality,1,9),compression:x(e.compression,d.compression,0,9),scaling:typeof e.scaling=="boolean"?e.scaling:d.scaling,viewOnly:!!e.viewOnly,audio:!!e.audio}}function T(t){if(typeof t=="string"){let i=t.trim();return i?{program:i}:null}if(!t||typeof t!="object")return null;let e=t,n=String(e.program??"").trim();if(!n)return null;let r=String(e.workingDir??"").trim(),o=String(e.arguments??"").trim();return{program:n,...r?{workingDir:r}:{},...o?{arguments:o}:{}}}function z(t){let e=h(t.protocol),n=String(t.host??"").trim(),r=u(t.port,a[e]),o=T(t.remoteApp??t.program),i=t.ssh??{},c={enabled:!!i.enabled,port:u(i.port,a.ssh),username:String(i.username??"").trim()},p={id:"",label:String(t.label??"").trim()||n,protocol:e,host:n,port:r,username:String(t.username??"").trim(),password:String(t.password??""),domain:String(t.domain??"").trim(),security:M(t.security),remoteApp:o,display:_(t.display),ssh:c,openSshOnConnect:!!t.openSshOnConnect};return p.id=String(t.id??"").trim()||E(p),p}function $(t){let{password:e,...n}=t;return n}function U(t){let e=`${t.host||"no address"}:${t.port}`,n=t.remoteApp?` \xB7 ${t.remoteApp.program}`:"";return`${t.protocol.toUpperCase()} \xB7 ${e}${n}`}function L(t){if(typeof t=="string"&&t.trim())return{program:t.trim()};if(!t||typeof t!="object")return;let e=t,n=String(e.program??"").trim();if(n)return{program:n,...e.workingDir?{workingDir:String(e.workingDir)}:{},...e.arguments?{arguments:String(e.arguments)}:{}}}function O(t,e){let n=typeof e=="string"&&e.trim()?e.trim():void 0;if(t===!0)return{port:f,...n?{username:n}:{}};if(typeof t=="number"||typeof t=="string"&&/^\d+$/.test(t.trim())){let i=u(t,0);return i?{port:i,...n?{username:n}:{}}:void 0}if(!t||typeof t!="object")return;let r=t;if(r.enabled===!1)return;let o=typeof r.username=="string"&&r.username.trim()?r.username.trim():n;return{port:u(r.port,f),...o?{username:o}:{}}}function R(t,e,n,r,o){if(!t)return[];let i;try{i=JSON.parse(t)}catch(c){return console.warn(`Failed to parse ${o.toUpperCase()}_HOSTS JSON, ignoring it:`,c),[]}return!Array.isArray(i)||i.length===0?[]:i.map((c,p)=>{let s=c??{},l=h(s.protocol,e),m=l==="rdp"?L(s.remoteApp):void 0,S=O(s.ssh??s.sshPort,s.sshUser??s.sshUsername),y=String(s.host??"")||n,w=String(s.name??"").trim()||y;return{id:String(s.id??"")||`${o}-${p+1}`,name:w,host:y,port:u(s.port??r,a[l]),protocol:l,...s.domain?{domain:String(s.domain)}:{},...g(s.security)?{security:s.security}:{},...m?{remoteApp:m}:{},...S?{ssh:S}:{}}})}function F(t){let e=t.defaultHost||"10.0.0.10",n=t.defaultPort||String(a.vnc),r=[...R(t.vncHostsJson,"vnc",e,n,"vnc-host"),...R(t.rdpHostsJson,"rdp",t.defaultRdpHost||e,t.defaultRdpPort||String(a.rdp),"rdp-host")];return r.length>0?r:[{id:"default-host",name:"Primary Host",host:e,port:u(n,a.vnc),protocol:"vnc"}]}function b(t){let e=String(t).trim().split(".");if(e.length!==4)return null;let n=0;for(let r of e){if(!/^\d{1,3}$/.test(r))return null;let o=Number.parseInt(r,10);if(o>255)return null;n=n<<8|o}return n>>>0}function H(t){if(!t)return[];let e=[];for(let n of t.split(",")){let r=n.trim();if(!r)continue;let o=r.split(".").findIndex(m=>m==="*"),[i,c]=o>=0?[r.replaceAll("*","0"),String(o*8)]:r.split("/"),p=b(i??"");if(p===null)continue;let s=c===void 0?32:Number.parseInt(c,10);if(!Number.isInteger(s)||s<0||s>32)continue;let l=s===0?0:4294967295<<32-s>>>0;e.push({base:(p&l)>>>0,mask:l})}return e}function G(t,e){let n=b(t);return n===null?!1:H(e).some(({base:r,mask:o})=>(n&o)>>>0===r)}var J={monitor:'<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>',link:'<path d="M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8"/>',power:'<path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.8 0"/>',maximize:'<path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3"/>',minimize:'<path d="M8 3v3a2 2 0 0 1-2 2H3M21 8h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3M16 21v-3a2 2 0 0 1 2-2h3"/>',keyboard:'<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/>',clipboard:'<rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>',check:'<path d="m5 13 4 4 10-10"/>',camera:'<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',eye:'<path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12z"/><circle cx="12" cy="12" r="3"/>',sliders:'<path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M2 14h4M10 8h4M18 16h4"/>',expand:'<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>',shield:'<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',chevron:'<path d="m6 9 6 6 6-6"/>',back:'<path d="m15 18-6-6 6-6"/>',zap:'<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>',cube:'<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="m3.3 7 8.7 5 8.7-5M12 22V12"/>',logout:'<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>',clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',activity:'<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>',transfer:'<path d="m3 16 4 4 4-4M7 20V4M21 8l-4-4-4 4M17 4v16"/>',screen:'<rect x="2" y="5" width="20" height="13" rx="2"/><path d="M7 21h10"/>',command:'<path d="M15 6a3 3 0 1 1 3 3h-3zm0 0v12m0 0a3 3 0 1 0 3-3h-3zM9 6a3 3 0 1 0-3 3h3zm0 0v12m0 0a3 3 0 1 1-3-3h3z"/>',apps:'<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>',terminal:'<path d="m4 17 6-6-6-6M12 19h8"/>',folder:'<path d="M3 7h6l2 2h10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M3 7V6a2 2 0 0 1 2-2h4l2 3"/>',close:'<path d="M18 6 6 18M6 6l12 12"/>',sparkles:'<path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3z"/><path d="m19 15 .8 2.2 2.2.8-2.2.8L19 21l-.8-2.2-2.2-.8 2.2-.8z"/>',broom:'<path d="M10 10V4a2 2 0 0 1 4 0v6"/><path d="M6 10h12a2 2 0 0 1 2 2v2H4v-2a2 2 0 0 1 2-2Z"/><path d="m5 14-2 7h18l-2-7M9 18l-1 3M15 18l1 3"/>',panel:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M14 4v16"/>',menu:'<path d="M4 7h16M4 12h16M4 17h16"/>',plus:'<path d="M12 5v14M5 12h14"/>',star:'<path d="m12 3 2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.9l1.1-6.5L2.6 9.8l6.5-.9L12 3z"/>',trash:'<path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>'},j="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f38020' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='3' width='20' height='14' rx='2'/%3E%3Cpath d='M8 21h8M12 17v4'/%3E%3C/svg%3E";var K="VNC_GATEWAY_READY_v1",X="VNC_GATEWAY_KEEPALIVE_v1",Z=25e3,Q=123;function tt(t){return!t.established||!t.close?!1:t.close.code===1006?!0:t.close.code===1011&&/tcp|socket|network|host closed/i.test(t.close.reason)}function et(t){let e=new TextEncoder;if(e.encode(t).byteLength<=123)return t;let n="";for(let r of t){if(e.encode(`${n}${r}\u2026`).byteLength>123)break;n+=r}return`${n}\u2026`}function nt(t){return t===1005||t===1006||t===1015?1011:t}async function rt(t){return typeof t=="string"?new TextEncoder().encode(t):t instanceof ArrayBuffer?new Uint8Array(t):t instanceof Blob?new Uint8Array(await t.arrayBuffer()):ArrayBuffer.isView(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):null}export{D as COMMAND_IDLE_MS,v as COMMAND_START_MS,d as DEFAULT_DISPLAY_SETTINGS,a as DEFAULT_PORTS,f as DEFAULT_SSH_PORT,j as FAVICON_DATA_URI,X as GATEWAY_KEEPALIVE_SIGNAL,K as GATEWAY_READY_SIGNAL,J as ICON_PATHS,Z as KEEPALIVE_INTERVAL_MS,Q as MAX_CLOSE_REASON_BYTES,P as RDP_SECURITY_MODES,A as REMOTE_PROTOCOLS,k as TRANSPORT_PROTOCOLS,E as connectionId,U as describeConnection,V as emptyConnection,G as hostInAllowedSubnets,b as ipv4ToInt,g as isRdpSecurity,C as isRemoteProtocol,tt as isRetryableDisconnect,rt as messageToBytes,z as normalizeConnection,_ as normalizeDisplaySettings,H as parseAllowedSubnets,F as parseConfiguredHosts,nt as sendableCloseCode,u as toPort,M as toRdpSecurity,h as toRemoteProtocol,et as websocketCloseReason,$ as withoutPassword};
|
|
1
|
+
var _=["vnc","rdp"],V=["vnc","rdp","ssh"],P=["auto","nla","tls","rdp"],a={vnc:5900,rdp:3389,ssh:22},g=a.ssh,z=15e3,U=5*6e4;function T(t){return _.includes(t)}function y(t){return P.includes(t)}function d(t,e="vnc"){let n=String(t??"").toLowerCase();return T(n)?n:e}function b(t,e="auto"){let n=String(t??"").toLowerCase();return y(n)?n:e}function u(t,e){let n=typeof t=="number"?t:Number.parseInt(String(t??""),10);return Number.isInteger(n)&&n>0&&n<65536?n:e}var h={quality:6,compression:2,scaling:!0,viewOnly:!1,audio:!1};function O(t){let e=t.remoteApp?.program?.trim()??"",n=`${t.protocol||"vnc"}:${t.host.trim()}:${t.port||""}`;return(e?`${n}:${e}`:n).toLowerCase().slice(0,64)}function Y(t="vnc"){let e=a[t];return{id:"",label:"",protocol:t,host:"",port:e,username:"",password:"",domain:"",security:"auto",remoteApp:null,display:{...h},ssh:{enabled:!1,port:a.ssh,username:""},openSshOnConnect:!1}}function M(t,e,n,r){let o=typeof t=="number"?t:Number.parseInt(String(t??""),10);return Number.isInteger(o)?Math.min(r,Math.max(n,o)):e}function L(t){let e=t??{};return{quality:M(e.quality,h.quality,1,9),compression:M(e.compression,h.compression,0,9),scaling:typeof e.scaling=="boolean"?e.scaling:h.scaling,viewOnly:!!e.viewOnly,audio:!!e.audio}}function k(t){if(typeof t=="string"){let i=t.trim();return i?{program:i}:null}if(!t||typeof t!="object")return null;let e=t,n=String(e.program??"").trim();if(!n)return null;let r=String(e.workingDir??"").trim(),o=String(e.arguments??"").trim();return{program:n,...r?{workingDir:r}:{},...o?{arguments:o}:{}}}function F(t){let e=d(t.protocol),n=String(t.host??"").trim(),r=u(t.port,a[e]),o=k(t.remoteApp??t.program),i=t.ssh??{},c={enabled:!!i.enabled,port:u(i.port,a.ssh),username:(i.username??"").trim()},p={id:"",label:String(t.label??"").trim()||n,protocol:e,host:n,port:r,username:String(t.username??"").trim(),password:String(t.password??""),domain:String(t.domain??"").trim(),security:b(t.security),remoteApp:o,display:L(t.display),ssh:c,openSshOnConnect:!!t.openSshOnConnect};return p.id=String(t.id??"").trim()||O(p),p}function G(t){let{password:e,...n}=t;return n}function J(t){let e=`${t.host||"no address"}:${t.port}`,n=t.remoteApp?` \xB7 ${t.remoteApp.program}`:"";return`${t.protocol.toUpperCase()} \xB7 ${e}${n}`}var w="binary",f="airemote-cred.";function H(t){let e=new TextEncoder().encode(t),n="";for(let r of e)n+=String.fromCharCode(r);return btoa(n).replaceAll("+","-").replaceAll("/","_").replaceAll("=","")}function v(t){let e=t.replaceAll("-","+").replaceAll("_","/").padEnd(Math.ceil(t.length/4)*4,"="),n=atob(e),r=new Uint8Array(n.length);for(let o=0;o<n.length;o++)r[o]=n.charCodeAt(o);return new TextDecoder().decode(r)}function j(t){let e=t.username||"",n=t.password||"";return!e&&!n?[w]:[w,f+H(JSON.stringify({u:e,p:n}))]}function R(t){return t?t.split(",").map(e=>e.trim()).filter(Boolean):[]}function q(t){for(let e of R(t))if(e.startsWith(f))try{let n=JSON.parse(v(e.slice(f.length)));if(!n||typeof n!="object")return null;let{u:r,p:o}=n;return{username:typeof r=="string"?r:"",password:typeof o=="string"?o:""}}catch{return null}return null}function K(t){return R(t).find(e=>!e.startsWith(f))??null}function I(t){if(typeof t=="string"&&t.trim())return{program:t.trim()};if(!t||typeof t!="object")return;let e=t,n=String(e.program??"").trim();if(n)return{program:n,...e.workingDir?{workingDir:String(e.workingDir)}:{},...e.arguments?{arguments:String(e.arguments)}:{}}}function D(t,e){let n=typeof e=="string"&&e.trim()?e.trim():void 0;if(t===!0)return{port:g,...n?{username:n}:{}};if(typeof t=="number"||typeof t=="string"&&/^\d+$/.test(t.trim())){let i=u(t,0);return i?{port:i,...n?{username:n}:{}}:void 0}if(!t||typeof t!="object")return;let r=t;if(r.enabled===!1)return;let o=typeof r.username=="string"&&r.username.trim()?r.username.trim():n;return{port:u(r.port,g),...o?{username:o}:{}}}function A(t,e,n,r,o){if(!t)return[];let i;try{i=JSON.parse(t)}catch(c){return console.warn(`Failed to parse ${o.toUpperCase()}_HOSTS JSON, ignoring it:`,c),[]}return!Array.isArray(i)||i.length===0?[]:i.map((c,p)=>{let s=c??{},l=d(s.protocol,e),m=l==="rdp"?I(s.remoteApp):void 0,S=D(s.ssh??s.sshPort,s.sshUser??s.sshUsername),x=String(s.host??"")||n,E=String(s.name??"").trim()||x;return{id:String(s.id??"")||`${o}-${p+1}`,name:E,host:x,port:u(s.port??r,a[l]),protocol:l,...s.domain?{domain:String(s.domain)}:{},...y(s.security)?{security:s.security}:{},...m?{remoteApp:m}:{},...S?{ssh:S}:{}}})}function Q(t){let e=t.defaultHost||"10.0.0.10",n=t.defaultPort||String(a.vnc),r=[...A(t.vncHostsJson,"vnc",e,n,"vnc-host"),...A(t.rdpHostsJson,"rdp",t.defaultRdpHost||e,t.defaultRdpPort||String(a.rdp),"rdp-host")];return r.length>0?r:[{id:"default-host",name:"Primary Host",host:e,port:u(n,a.vnc),protocol:"vnc"}]}function C(t){let e=t.trim().split(".");if(e.length!==4)return null;let n=0;for(let r of e){if(!/^\d{1,3}$/.test(r))return null;let o=Number.parseInt(r,10);if(o>255)return null;n=n<<8|o}return n>>>0}function N(t){if(!t)return[];let e=[];for(let n of t.split(",")){let r=n.trim();if(!r)continue;let o=r.split(".").findIndex(m=>m==="*"),[i,c]=o>=0?[r.replaceAll("*","0"),String(o*8)]:r.split("/"),p=C(i??"");if(p===null)continue;let s=c===void 0?32:Number.parseInt(c,10);if(!Number.isInteger(s)||s<0||s>32)continue;let l=s===0?0:4294967295<<32-s>>>0;e.push({base:(p&l)>>>0,mask:l})}return e}function tt(t,e){let n=C(t);return n===null?!1:N(e).some(({base:r,mask:o})=>(n&o)>>>0===r)}var nt={monitor:'<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>',link:'<path d="M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8"/>',power:'<path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.8 0"/>',maximize:'<path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3"/>',minimize:'<path d="M8 3v3a2 2 0 0 1-2 2H3M21 8h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3M16 21v-3a2 2 0 0 1 2-2h3"/>',keyboard:'<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/>',clipboard:'<rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>',check:'<path d="m5 13 4 4 10-10"/>',camera:'<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',eye:'<path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12z"/><circle cx="12" cy="12" r="3"/>',sliders:'<path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M2 14h4M10 8h4M18 16h4"/>',expand:'<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>',shield:'<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',chevron:'<path d="m6 9 6 6 6-6"/>',back:'<path d="m15 18-6-6 6-6"/>',zap:'<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>',cube:'<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="m3.3 7 8.7 5 8.7-5M12 22V12"/>',logout:'<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>',clock:'<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',activity:'<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>',transfer:'<path d="m3 16 4 4 4-4M7 20V4M21 8l-4-4-4 4M17 4v16"/>',screen:'<rect x="2" y="5" width="20" height="13" rx="2"/><path d="M7 21h10"/>',command:'<path d="M15 6a3 3 0 1 1 3 3h-3zm0 0v12m0 0a3 3 0 1 0 3-3h-3zM9 6a3 3 0 1 0-3 3h3zm0 0v12m0 0a3 3 0 1 1-3-3h3z"/>',apps:'<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>',terminal:'<path d="m4 17 6-6-6-6M12 19h8"/>',folder:'<path d="M3 7h6l2 2h10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M3 7V6a2 2 0 0 1 2-2h4l2 3"/>',close:'<path d="M18 6 6 18M6 6l12 12"/>',sparkles:'<path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3z"/><path d="m19 15 .8 2.2 2.2.8-2.2.8L19 21l-.8-2.2-2.2-.8 2.2-.8z"/>',broom:'<path d="M10 10V4a2 2 0 0 1 4 0v6"/><path d="M6 10h12a2 2 0 0 1 2 2v2H4v-2a2 2 0 0 1 2-2Z"/><path d="m5 14-2 7h18l-2-7M9 18l-1 3M15 18l1 3"/>',panel:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M14 4v16"/>',menu:'<path d="M4 7h16M4 12h16M4 17h16"/>',plus:'<path d="M12 5v14M5 12h14"/>',star:'<path d="m12 3 2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.9l1.1-6.5L2.6 9.8l6.5-.9L12 3z"/>',trash:'<path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>'},rt="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f38020' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='3' width='20' height='14' rx='2'/%3E%3Cpath d='M8 21h8M12 17v4'/%3E%3C/svg%3E";var st="VNC_GATEWAY_READY_v1",it="VNC_GATEWAY_KEEPALIVE_v1",at=25e3,ct=123;function pt(t){return!t.established||!t.close?!1:t.close.code===1006?!0:t.close.code===1011&&/tcp|socket|network|host closed/i.test(t.close.reason)}function ut(t){let e=new TextEncoder;if(e.encode(t).byteLength<=123)return t;let n="";for(let r of t){if(e.encode(`${n}${r}\u2026`).byteLength>123)break;n+=r}return`${n}\u2026`}function lt(t){return t===1005||t===1006||t===1015?1011:t}async function mt(t){return typeof t=="string"?new TextEncoder().encode(t):t instanceof ArrayBuffer?new Uint8Array(t):t instanceof Blob?new Uint8Array(await t.arrayBuffer()):ArrayBuffer.isView(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):null}export{U as COMMAND_IDLE_MS,z as COMMAND_START_MS,h as DEFAULT_DISPLAY_SETTINGS,a as DEFAULT_PORTS,g as DEFAULT_SSH_PORT,rt as FAVICON_DATA_URI,it as GATEWAY_KEEPALIVE_SIGNAL,st as GATEWAY_READY_SIGNAL,nt as ICON_PATHS,at as KEEPALIVE_INTERVAL_MS,ct as MAX_CLOSE_REASON_BYTES,P as RDP_SECURITY_MODES,_ as REMOTE_PROTOCOLS,V as TRANSPORT_PROTOCOLS,w as WS_BINARY_SUBPROTOCOL,O as connectionId,J as describeConnection,Y as emptyConnection,tt as hostInAllowedSubnets,C as ipv4ToInt,y as isRdpSecurity,T as isRemoteProtocol,pt as isRetryableDisconnect,mt as messageToBytes,F as normalizeConnection,L as normalizeDisplaySettings,N as parseAllowedSubnets,Q as parseConfiguredHosts,q as readCredentialSubprotocol,K as selectSubprotocol,lt as sendableCloseCode,u as toPort,b as toRdpSecurity,d as toRemoteProtocol,ut as websocketCloseReason,j as websocketSubprotocols,G as withoutPassword};
|
package/dist/protocols.d.ts
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The part of a WebSocket the protocol engines actually use.
|
|
3
|
+
*
|
|
4
|
+
* The engines were written against a browser WebSocket pointed at the gateway's
|
|
5
|
+
* relay, and the CLI hands them a TCP socket wearing the same face (see
|
|
6
|
+
* `cli/transport.ts`). Naming that face is what lets both be passed without the
|
|
7
|
+
* engines being told which one they got -- and without the seam being an `any`,
|
|
8
|
+
* which is the same thing said less carefully.
|
|
9
|
+
*/
|
|
10
|
+
interface ByteTransport extends EventTarget {
|
|
11
|
+
readonly readyState: number;
|
|
12
|
+
binaryType: string;
|
|
13
|
+
/** The full range a browser WebSocket takes, so a real one satisfies this. */
|
|
14
|
+
send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void;
|
|
15
|
+
close(code?: number, reason?: string): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A `CustomEvent` whose detail this code knows the shape of.
|
|
19
|
+
*
|
|
20
|
+
* The engines communicate by dispatching events, so their consumers spend a lot
|
|
21
|
+
* of time reaching into `event.detail`. Saying which shape is expected at the
|
|
22
|
+
* point of listening is what keeps that from being unchecked.
|
|
23
|
+
*/
|
|
24
|
+
type DetailListener<T> = (event: CustomEvent<T>) => void;
|
|
1
25
|
/**
|
|
2
26
|
* The vocabulary both halves of the gateway share.
|
|
3
27
|
*
|
|
@@ -110,12 +134,24 @@ declare function connectionId(input: Pick<ConnectionSettings, 'protocol' | 'host
|
|
|
110
134
|
/** A blank connection, ready for the dialog to fill in. */
|
|
111
135
|
declare function emptyConnection(protocol?: RemoteProtocol): ConnectionSettings;
|
|
112
136
|
declare function normalizeDisplaySettings(value: unknown): DisplaySettings;
|
|
137
|
+
/**
|
|
138
|
+
* Anything a caller might have.
|
|
139
|
+
*
|
|
140
|
+
* Every field is `unknown` on purpose. This is the function that a saved card,
|
|
141
|
+
* a URL, a host-list entry and a localStorage blob all arrive at, and none of
|
|
142
|
+
* those had their types checked by anyone. Declaring them as already the right
|
|
143
|
+
* shape would not make them so -- it would only move the guesswork from here,
|
|
144
|
+
* where each field is coerced deliberately, to the callers, where it is not.
|
|
145
|
+
*/
|
|
146
|
+
type ConnectionInput = {
|
|
147
|
+
[K in keyof ConnectionSettings]?: unknown;
|
|
148
|
+
} & Record<string, unknown>;
|
|
113
149
|
/**
|
|
114
150
|
* Build a complete, valid connection out of whatever a caller happens to have.
|
|
115
151
|
* Partial input from a saved card, a host list entry, a URL or localStorage all
|
|
116
152
|
* arrive here, so the dialog never has to reason about missing fields.
|
|
117
153
|
*/
|
|
118
|
-
declare function normalizeConnection(input:
|
|
154
|
+
declare function normalizeConnection(input: ConnectionInput): ConnectionSettings;
|
|
119
155
|
/** What a connection is worth remembering by -- never the password. */
|
|
120
156
|
type PersistedConnection = Omit<ConnectionSettings, 'password'>;
|
|
121
157
|
declare function withoutPassword(settings: ConnectionSettings): PersistedConnection;
|
|
@@ -143,10 +179,7 @@ interface ScrollDelta {
|
|
|
143
179
|
* What the metrics rail instruments. A real WebSocket in the browser; in the
|
|
144
180
|
* CLI, a shim with the same surface over a TCP socket straight at the host.
|
|
145
181
|
*/
|
|
146
|
-
|
|
147
|
-
readonly readyState: number;
|
|
148
|
-
addEventListener(type: string, listener: (event: any) => void): void;
|
|
149
|
-
}
|
|
182
|
+
type InstrumentableSocket = ByteTransport;
|
|
150
183
|
/**
|
|
151
184
|
* A live session. Both drivers expose this, so the AI pane's screen tool and
|
|
152
185
|
* the toolbar never have to ask which protocol they are driving.
|
|
@@ -330,12 +363,72 @@ export interface SshSessionOptions {
|
|
|
330
363
|
rows?: number;
|
|
331
364
|
hostLabel?: string;
|
|
332
365
|
log?: (step: string, detail?: unknown) => void;
|
|
333
|
-
verifyHost?: (info:
|
|
334
|
-
|
|
335
|
-
|
|
366
|
+
verifyHost?: (info: {
|
|
367
|
+
fingerprint: string;
|
|
368
|
+
keyType: string;
|
|
369
|
+
algorithm: string;
|
|
370
|
+
}) => Promise<boolean> | boolean;
|
|
371
|
+
openTransport?: ((url: string) => ByteTransport) | null;
|
|
372
|
+
requestInput?: (prompt: {
|
|
373
|
+
prompt?: string;
|
|
374
|
+
echo?: boolean;
|
|
375
|
+
}) => Promise<string>;
|
|
376
|
+
}
|
|
377
|
+
/** What each SshSession event carries in its `detail`. */
|
|
378
|
+
export interface SshSessionEventDetail {
|
|
379
|
+
data: {
|
|
380
|
+
bytes: Uint8Array;
|
|
381
|
+
display?: Uint8Array;
|
|
382
|
+
isStderr: boolean;
|
|
383
|
+
};
|
|
384
|
+
ready: {
|
|
385
|
+
fingerprint?: string;
|
|
386
|
+
shellFamily: string;
|
|
387
|
+
};
|
|
388
|
+
status: {
|
|
389
|
+
phase: string;
|
|
390
|
+
message: string;
|
|
391
|
+
};
|
|
392
|
+
banner: {
|
|
393
|
+
text: string;
|
|
394
|
+
};
|
|
395
|
+
secure: {
|
|
396
|
+
fingerprint: string;
|
|
397
|
+
kexAlgorithm: string;
|
|
398
|
+
hostKeyAlgorithm: string;
|
|
399
|
+
cipher: string;
|
|
400
|
+
firstTime: boolean;
|
|
401
|
+
};
|
|
402
|
+
error: {
|
|
403
|
+
message: string;
|
|
404
|
+
};
|
|
405
|
+
close: {
|
|
406
|
+
message: string;
|
|
407
|
+
exitStatus?: number | null;
|
|
408
|
+
clean?: boolean;
|
|
409
|
+
code?: number;
|
|
410
|
+
};
|
|
411
|
+
command: {
|
|
412
|
+
phase: 'start' | 'end';
|
|
413
|
+
command: string;
|
|
414
|
+
exitStatus?: number | null;
|
|
415
|
+
timedOut?: boolean;
|
|
416
|
+
started?: boolean;
|
|
417
|
+
/** Why it was given up on: never acknowledged, silent, or simply too long. */
|
|
418
|
+
reason?: string;
|
|
419
|
+
durationMs?: number;
|
|
420
|
+
};
|
|
336
421
|
}
|
|
337
422
|
export declare class SshSession extends EventTarget {
|
|
338
423
|
#private;
|
|
424
|
+
/**
|
|
425
|
+
* Listen for one of this session's own events, typed by what it carries.
|
|
426
|
+
* The shapes are enumerated in `SshSessionEventDetail` just above.
|
|
427
|
+
*/
|
|
428
|
+
addEventListener: {
|
|
429
|
+
<K extends keyof SshSessionEventDetail>(type: K, listener: (event: CustomEvent<SshSessionEventDetail[K]>) => void, options?: boolean | AddEventListenerOptions): void;
|
|
430
|
+
(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
|
|
431
|
+
};
|
|
339
432
|
private readonly options;
|
|
340
433
|
private readonly username;
|
|
341
434
|
private password;
|
|
@@ -359,16 +452,19 @@ export declare class SshSession extends EventTarget {
|
|
|
359
452
|
private remoteMaxPacket;
|
|
360
453
|
private localWindow;
|
|
361
454
|
private pendingWrites;
|
|
362
|
-
|
|
455
|
+
/** True while the interactive shell channel is usable. */
|
|
456
|
+
shellOpen: boolean;
|
|
363
457
|
private commandInFlight;
|
|
364
458
|
/** Set while a tool command runs, to keep its framing off the screen. */
|
|
365
459
|
displayFilter: DisplayFilter | null;
|
|
366
460
|
/** True only after the requested interactive shell is ready for commands. */
|
|
367
|
-
|
|
461
|
+
/** True once the interactive shell is actually usable, not merely authenticated. */
|
|
462
|
+
readySignaled: boolean;
|
|
368
463
|
private powerShellAttempted;
|
|
369
464
|
private powerShellTimer;
|
|
370
465
|
/** '', 'posix', 'cmd' or 'powershell'; learned from the prompt. */
|
|
371
|
-
|
|
466
|
+
/** 'powershell' when the host answered with one; '' until the shell is known. */
|
|
467
|
+
shellFamily: string;
|
|
372
468
|
private promptTail;
|
|
373
469
|
private exitStatus;
|
|
374
470
|
private exitSignal?;
|
|
@@ -421,7 +517,7 @@ export declare class SshSession extends EventTarget {
|
|
|
421
517
|
idleMs?: number;
|
|
422
518
|
maxMs?: number;
|
|
423
519
|
startMs?: number;
|
|
424
|
-
}): Promise<
|
|
520
|
+
}): Promise<CommandResult>;
|
|
425
521
|
/** Tell the shell its window changed, so full-screen programs redraw. */
|
|
426
522
|
resize(columns: number, rows: number): void;
|
|
427
523
|
}
|