@ours.network/fleet 0.13.3 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -8
- package/dist/cli.js +60 -7
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +31 -5
- package/dist/monitor.js +17 -6
- package/dist/owner-channel/channel.d.ts +27 -0
- package/dist/owner-channel/channel.js +95 -19
- package/dist/owner-channel/commands.d.ts +79 -0
- package/dist/owner-channel/commands.js +183 -0
- package/dist/owner-channel/notices.d.ts +5 -0
- package/dist/owner-channel/notices.js +17 -0
- package/dist/runner.js +2 -0
- package/dist/web/access.d.ts +19 -0
- package/dist/web/access.js +70 -0
- package/dist/web/auth.d.ts +13 -2
- package/dist/web/auth.js +36 -7
- package/dist/web/runtime.d.ts +4 -0
- package/dist/web/runtime.js +29 -12
- package/dist/web/server.js +38 -8
- package/dist/web/service.d.ts +15 -4
- package/dist/web/service.js +16 -10
- package/dist/web-app/assets/{TerminalView-BvcIkuIF.js → TerminalView-DMoT8udI.js} +1 -1
- package/dist/web-app/assets/index-B6T8JLSd.js +9 -0
- package/dist/web-app/index.html +1 -1
- package/package.json +1 -1
- package/dist/web-app/assets/index-CUN7ksTw.js +0 -9
package/dist/web/auth.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import { FleetError } from '../application/errors.js';
|
|
3
3
|
import { TrustedDeviceStore } from './device-store.js';
|
|
4
|
+
import { verifyPassword } from './access.js';
|
|
4
5
|
const token = (bytes = 32) => randomBytes(bytes).toString('base64url');
|
|
5
6
|
const same = (a, b) => {
|
|
6
7
|
const left = Buffer.from(a);
|
|
@@ -12,6 +13,7 @@ export class WebAuth {
|
|
|
12
13
|
_host;
|
|
13
14
|
now;
|
|
14
15
|
devices;
|
|
16
|
+
access;
|
|
15
17
|
_bootstrapSecret = token(32);
|
|
16
18
|
bootstrapExpiresAt = Date.now() + 5 * 60_000;
|
|
17
19
|
bootstrapUsed = false;
|
|
@@ -20,18 +22,25 @@ export class WebAuth {
|
|
|
20
22
|
tickets = new Map();
|
|
21
23
|
rates = new Map();
|
|
22
24
|
sockets = new Map();
|
|
23
|
-
constructor(_origin, _host, now = Date.now, devices = new TrustedDeviceStore()) {
|
|
25
|
+
constructor(_origin, _host, now = Date.now, devices = new TrustedDeviceStore(), access = { version: 1, mode: 'pairing' }) {
|
|
24
26
|
this._origin = _origin;
|
|
25
27
|
this._host = _host;
|
|
26
28
|
this.now = now;
|
|
27
29
|
this.devices = devices;
|
|
30
|
+
this.access = access;
|
|
28
31
|
}
|
|
29
32
|
get bootstrapSecret() { return this._bootstrapSecret; }
|
|
30
33
|
get origin() { return this._origin; }
|
|
31
34
|
get host() { return this._host; }
|
|
32
|
-
|
|
35
|
+
get mode() { return this.access.mode; }
|
|
36
|
+
get secureCookies() { return this._origin.startsWith('https:'); }
|
|
37
|
+
allowedOrigins = new Set();
|
|
38
|
+
allowedHosts = new Set();
|
|
39
|
+
setBoundary(origin, host, aliases = {}) {
|
|
33
40
|
this._origin = origin;
|
|
34
41
|
this._host = host;
|
|
42
|
+
this.allowedOrigins = new Set([origin, ...(aliases.origins ?? [])]);
|
|
43
|
+
this.allowedHosts = new Set([host, ...(aliases.hosts ?? [])]);
|
|
35
44
|
}
|
|
36
45
|
/** Mint a replacement for an operator-triggered reauthentication ceremony. */
|
|
37
46
|
mintBootstrap() {
|
|
@@ -42,16 +51,20 @@ export class WebAuth {
|
|
|
42
51
|
}
|
|
43
52
|
validateBoundary(request, requireOrigin) {
|
|
44
53
|
const host = request.headers.host;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
throw new FleetError('forbidden',
|
|
54
|
+
const hosts = this.allowedHosts.size ? this.allowedHosts : new Set([this.host]);
|
|
55
|
+
const origins = this.allowedOrigins.size ? this.allowedOrigins : new Set([this.origin]);
|
|
56
|
+
if (!host || !hosts.has(host))
|
|
57
|
+
throw new FleetError('forbidden', `This address is not configured for the fleet console. Open ${this.origin} or set --public-origin.`);
|
|
58
|
+
if (requireOrigin && (!request.headers.origin || !origins.has(request.headers.origin)))
|
|
59
|
+
throw new FleetError('forbidden', 'request Origin does not match the configured control-panel origin');
|
|
49
60
|
const fetchSite = request.headers['sec-fetch-site'];
|
|
50
61
|
if (fetchSite && !['same-origin', 'none'].includes(String(fetchSite)))
|
|
51
62
|
throw new FleetError('forbidden', 'cross-site request rejected');
|
|
52
63
|
}
|
|
53
64
|
exchange(request) {
|
|
54
65
|
this.validateBoundary(request, true);
|
|
66
|
+
if (this.access.mode !== 'pairing')
|
|
67
|
+
throw new FleetError('forbidden', 'one-time pairing is disabled for this access mode');
|
|
55
68
|
this.consumeRate('bootstrap', 10, 60_000);
|
|
56
69
|
const authorization = request.headers.authorization ?? '';
|
|
57
70
|
const supplied = authorization.startsWith('Bootstrap ') ? authorization.slice(10) : '';
|
|
@@ -61,8 +74,24 @@ export class WebAuth {
|
|
|
61
74
|
const device = this.devices.issue();
|
|
62
75
|
return { session: this.createSession(device.id), device };
|
|
63
76
|
}
|
|
77
|
+
login(request, password) {
|
|
78
|
+
this.validateBoundary(request, true);
|
|
79
|
+
this.consumeRate('password-login', 10, 60_000);
|
|
80
|
+
if (!verifyPassword(this.access, password))
|
|
81
|
+
throw new FleetError('unauthorized', 'invalid control-panel password');
|
|
82
|
+
const device = this.devices.issue();
|
|
83
|
+
return { session: this.createSession(device.id), device };
|
|
84
|
+
}
|
|
85
|
+
anonymous(request) {
|
|
86
|
+
this.validateBoundary(request, true);
|
|
87
|
+
if (this.access.mode !== 'none')
|
|
88
|
+
throw new FleetError('forbidden', 'unprotected access is disabled');
|
|
89
|
+
return this.createSession('unprotected');
|
|
90
|
+
}
|
|
64
91
|
resume(request) {
|
|
65
92
|
this.validateBoundary(request, true);
|
|
93
|
+
if (this.access.mode === 'none')
|
|
94
|
+
throw new FleetError('forbidden', 'trusted devices are disabled');
|
|
66
95
|
this.consumeRate('device-resume', 30, 60_000);
|
|
67
96
|
const current = parseCookies(request.headers.cookie ?? '').ofs_device;
|
|
68
97
|
const device = current ? this.devices.rotate(current) : undefined;
|
|
@@ -92,7 +121,7 @@ export class WebAuth {
|
|
|
92
121
|
logout(request) {
|
|
93
122
|
const session = this.authenticate(request, true);
|
|
94
123
|
const deviceId = this.sessionDevices.get(session.id);
|
|
95
|
-
if (deviceId)
|
|
124
|
+
if (deviceId && deviceId !== 'unprotected')
|
|
96
125
|
this.devices.revokeId(deviceId);
|
|
97
126
|
this.removeSession(session.id);
|
|
98
127
|
}
|
package/dist/web/runtime.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { type WebServer } from './server.js';
|
|
2
|
+
import { type WebAccessConfig } from './access.js';
|
|
2
3
|
export interface StartWebOptions {
|
|
3
4
|
configPath?: string;
|
|
4
5
|
port?: number;
|
|
5
6
|
open?: boolean;
|
|
6
7
|
binPath: string;
|
|
7
8
|
log?(line: string): void;
|
|
9
|
+
bind?: string;
|
|
10
|
+
publicOrigin?: string;
|
|
11
|
+
access?: WebAccessConfig;
|
|
8
12
|
}
|
|
9
13
|
export interface RunningWebConsole extends WebServer {
|
|
10
14
|
address: string;
|
package/dist/web/runtime.js
CHANGED
|
@@ -22,6 +22,7 @@ import { acquireWebServerLock } from './lock.js';
|
|
|
22
22
|
import { TrustedDeviceStore } from './device-store.js';
|
|
23
23
|
import { WebAuth } from './auth.js';
|
|
24
24
|
import { startWebControlServer } from './control.js';
|
|
25
|
+
import { WebAccessStore, validatePublicOrigin } from './access.js';
|
|
25
26
|
import { buildWatchdogFindings, cachedWatchdogFindingsProvider, WatchdogQueryService } from '../watchdog/query.js';
|
|
26
27
|
import { latestReport } from '../watchdog/store.js';
|
|
27
28
|
const CONFIG_CACHE_TTL_MS = 5_000;
|
|
@@ -46,7 +47,14 @@ export async function startWebConsole(options) {
|
|
|
46
47
|
throw new FleetError('invalid_request', 'port must be between 0 and 65535');
|
|
47
48
|
const lock = acquireWebServerLock();
|
|
48
49
|
const webDir = resolve(stateRoot(), 'web');
|
|
49
|
-
const
|
|
50
|
+
const bind = options.bind ?? '127.0.0.1';
|
|
51
|
+
const publicOrigin = options.publicOrigin ? validatePublicOrigin(options.publicOrigin) : undefined;
|
|
52
|
+
if (!isLoopback(bind) && !publicOrigin) {
|
|
53
|
+
lock.release();
|
|
54
|
+
throw new FleetError('forbidden', 'a non-loopback bind requires an explicit --public-origin');
|
|
55
|
+
}
|
|
56
|
+
const access = options.access ?? new WebAccessStore(webDir).read();
|
|
57
|
+
const auth = new WebAuth(publicOrigin?.origin ?? `http://127.0.0.1:${requestedPort}`, publicOrigin?.host ?? `127.0.0.1:${requestedPort}`, Date.now, new TrustedDeviceStore(webDir), access);
|
|
50
58
|
const tmux = new Tmux();
|
|
51
59
|
const backend = pickBackend();
|
|
52
60
|
const repository = new RoleRepository({
|
|
@@ -157,7 +165,7 @@ export async function startWebConsole(options) {
|
|
|
157
165
|
}
|
|
158
166
|
let address;
|
|
159
167
|
try {
|
|
160
|
-
address = await server.app.listen({ host:
|
|
168
|
+
address = await server.app.listen({ host: bind, port: requestedPort });
|
|
161
169
|
}
|
|
162
170
|
catch (error) {
|
|
163
171
|
await server.close();
|
|
@@ -165,19 +173,24 @@ export async function startWebConsole(options) {
|
|
|
165
173
|
throw error;
|
|
166
174
|
}
|
|
167
175
|
const actual = new URL(address);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
const localHost = `127.0.0.1:${actual.port}`;
|
|
177
|
+
const browserOrigin = publicOrigin?.origin ?? `http://${localHost}`;
|
|
178
|
+
const browserHost = publicOrigin?.host ?? localHost;
|
|
179
|
+
server.auth.setBoundary(browserOrigin, browserHost, publicOrigin ? {
|
|
180
|
+
// nginx's safe default uses the loopback upstream as Host. The declared
|
|
181
|
+
// browser Origin remains mandatory for auth/mutations and WebSocket hello,
|
|
182
|
+
// so operators do not need a fragile Host-rewrite incantation.
|
|
183
|
+
hosts: [localHost, `localhost:${actual.port}`],
|
|
184
|
+
} : {
|
|
185
|
+
hosts: [`localhost:${actual.port}`], origins: [`http://localhost:${actual.port}`],
|
|
186
|
+
});
|
|
175
187
|
let control;
|
|
176
188
|
try {
|
|
177
189
|
control = await startWebControlServer({
|
|
178
190
|
dir: webDir,
|
|
179
191
|
onOpen() {
|
|
180
|
-
const url =
|
|
192
|
+
const url = access.mode === 'pairing'
|
|
193
|
+
? `${browserOrigin}/#bootstrap=${server.auth.mintBootstrap()}` : `${browserOrigin}/`;
|
|
181
194
|
openBrowser(url);
|
|
182
195
|
},
|
|
183
196
|
onRevokeAll() { server.auth.revokeAllTrustedDevices(); },
|
|
@@ -189,9 +202,10 @@ export async function startWebConsole(options) {
|
|
|
189
202
|
throw error;
|
|
190
203
|
}
|
|
191
204
|
if (options.open !== false)
|
|
192
|
-
openBrowser(
|
|
205
|
+
openBrowser(access.mode === 'pairing'
|
|
206
|
+
? `${browserOrigin}/#bootstrap=${server.auth.bootstrapSecret}` : `${browserOrigin}/`);
|
|
193
207
|
return {
|
|
194
|
-
...server, address:
|
|
208
|
+
...server, address: browserOrigin,
|
|
195
209
|
async close() {
|
|
196
210
|
try {
|
|
197
211
|
await control.close();
|
|
@@ -204,6 +218,9 @@ export async function startWebConsole(options) {
|
|
|
204
218
|
},
|
|
205
219
|
};
|
|
206
220
|
}
|
|
221
|
+
function isLoopback(host) {
|
|
222
|
+
return ['127.0.0.1', 'localhost', '::1'].includes(host);
|
|
223
|
+
}
|
|
207
224
|
function openBrowser(url) {
|
|
208
225
|
const command = process.platform === 'darwin' ? 'open'
|
|
209
226
|
: process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
package/dist/web/server.js
CHANGED
|
@@ -33,12 +33,19 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
33
33
|
reply.header('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=()');
|
|
34
34
|
reply.header('X-Frame-Options', 'DENY');
|
|
35
35
|
reply.header('Content-Security-Policy', `default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; ` +
|
|
36
|
-
`connect-src 'self' ws://${auth.host}; object-src 'none'; base-uri 'none'; ` +
|
|
36
|
+
`connect-src 'self' ${auth.secureCookies ? 'wss' : 'ws'}://${auth.host}; object-src 'none'; base-uri 'none'; ` +
|
|
37
37
|
`frame-ancestors 'none'; form-action 'self'; manifest-src 'self'; worker-src 'self'`);
|
|
38
38
|
if (request.url.startsWith('/api/'))
|
|
39
39
|
reply.header('Cache-Control', 'no-store');
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
try {
|
|
41
|
+
auth.validateBoundary(request, false);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (request.url.startsWith('/api/'))
|
|
45
|
+
throw error;
|
|
46
|
+
const message = normalizeError(error).message;
|
|
47
|
+
return reply.code(421).type('text/html').send(`<!doctype html><html><head><title>Fleet console address</title></head><body><main><h1>This fleet-console address is not configured</h1><p>${escapeHtml(message)}</p><p>For nginx or VPS access, configure an explicit public origin. The console does not guess proxy hosts.</p></main></body></html>`);
|
|
48
|
+
}
|
|
42
49
|
});
|
|
43
50
|
app.setErrorHandler(async (error, request, reply) => {
|
|
44
51
|
const fleetError = normalizeError(error, request.id);
|
|
@@ -50,13 +57,29 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
50
57
|
});
|
|
51
58
|
app.post('/api/v1/auth/exchange', async (request, reply) => {
|
|
52
59
|
const { session, device } = auth.exchange(request);
|
|
53
|
-
setAuthCookies(reply, session.id, device.token);
|
|
60
|
+
setAuthCookies(reply, session.id, device.token, auth.secureCookies);
|
|
54
61
|
await audit.record({ requestId: request.id, browser: session.id, action: 'auth.exchange', result: 'succeeded' });
|
|
55
62
|
return { csrfToken: session.csrf, expiresAt: new Date(session.absoluteExpiresAt).toISOString() };
|
|
56
63
|
});
|
|
64
|
+
app.get('/api/v1/auth/mode', async () => ({
|
|
65
|
+
mode: auth.mode,
|
|
66
|
+
warning: auth.mode === 'none'
|
|
67
|
+
? 'Unprotected mode: anyone who can reach this address can control the fleet.' : undefined,
|
|
68
|
+
}));
|
|
69
|
+
app.post('/api/v1/auth/login', async (request, reply) => {
|
|
70
|
+
const { session, device } = auth.login(request, String(request.body?.password ?? ''));
|
|
71
|
+
setAuthCookies(reply, session.id, device.token, auth.secureCookies);
|
|
72
|
+
await audit.record({ requestId: request.id, browser: session.id, action: 'auth.password', result: 'succeeded' });
|
|
73
|
+
return { csrfToken: session.csrf, expiresAt: new Date(session.absoluteExpiresAt).toISOString() };
|
|
74
|
+
});
|
|
75
|
+
app.post('/api/v1/auth/anonymous', async (request, reply) => {
|
|
76
|
+
const session = auth.anonymous(request);
|
|
77
|
+
setSessionCookie(reply, session.id, auth.secureCookies);
|
|
78
|
+
return { csrfToken: session.csrf, expiresAt: new Date(session.absoluteExpiresAt).toISOString() };
|
|
79
|
+
});
|
|
57
80
|
app.post('/api/v1/auth/resume', async (request, reply) => {
|
|
58
81
|
const { session, device } = auth.resume(request);
|
|
59
|
-
setAuthCookies(reply, session.id, device.token);
|
|
82
|
+
setAuthCookies(reply, session.id, device.token, auth.secureCookies);
|
|
60
83
|
await audit.record({ requestId: request.id, browser: session.id, action: 'auth.resume', result: 'succeeded' });
|
|
61
84
|
return { csrfToken: session.csrf, expiresAt: new Date(session.absoluteExpiresAt).toISOString() };
|
|
62
85
|
});
|
|
@@ -243,12 +266,16 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
243
266
|
},
|
|
244
267
|
};
|
|
245
268
|
}
|
|
246
|
-
function setAuthCookies(reply, session, device) {
|
|
269
|
+
function setAuthCookies(reply, session, device, secure = false) {
|
|
270
|
+
const suffix = secure ? '; Secure' : '';
|
|
247
271
|
reply.header('Set-Cookie', [
|
|
248
|
-
`ofs_session=${encodeURIComponent(session)}; HttpOnly; SameSite=Strict; Path=/api; Max-Age=28800`,
|
|
249
|
-
`ofs_device=${encodeURIComponent(device)}; HttpOnly; SameSite=Strict; Path=/api; Max-Age=2592000`,
|
|
272
|
+
`ofs_session=${encodeURIComponent(session)}; HttpOnly; SameSite=Strict; Path=/api; Max-Age=28800${suffix}`,
|
|
273
|
+
`ofs_device=${encodeURIComponent(device)}; HttpOnly; SameSite=Strict; Path=/api; Max-Age=2592000${suffix}`,
|
|
250
274
|
]);
|
|
251
275
|
}
|
|
276
|
+
function setSessionCookie(reply, session, secure = false) {
|
|
277
|
+
reply.header('Set-Cookie', `ofs_session=${encodeURIComponent(session)}; HttpOnly; SameSite=Strict; Path=/api; Max-Age=28800${secure ? '; Secure' : ''}`);
|
|
278
|
+
}
|
|
252
279
|
function clearAuthCookies(reply) {
|
|
253
280
|
reply.header('Set-Cookie', [
|
|
254
281
|
'ofs_session=; HttpOnly; SameSite=Strict; Path=/api; Max-Age=0',
|
|
@@ -256,6 +283,9 @@ function clearAuthCookies(reply) {
|
|
|
256
283
|
]);
|
|
257
284
|
}
|
|
258
285
|
function cryptoRandomId() { return randomBytes(12).toString('hex'); }
|
|
286
|
+
function escapeHtml(value) {
|
|
287
|
+
return value.replace(/[&<>"']/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]));
|
|
288
|
+
}
|
|
259
289
|
function requireSubprotocol(request, expected) {
|
|
260
290
|
const protocols = String(request.headers['sec-websocket-protocol'] ?? '')
|
|
261
291
|
.split(',').map(value => value.trim());
|
package/dist/web/service.d.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { type Exec } from '../exec.js';
|
|
|
2
2
|
export declare const WEB_SYSTEMD_UNIT = "ours-fleet-web.service";
|
|
3
3
|
export declare const WEB_LAUNCHD_LABEL = "network.ours.fleet.web";
|
|
4
4
|
interface ServiceMetadata {
|
|
5
|
-
version:
|
|
5
|
+
version: 3;
|
|
6
6
|
platform: 'linux' | 'darwin';
|
|
7
7
|
runtime: string;
|
|
8
8
|
script: string;
|
|
9
9
|
port: number;
|
|
10
10
|
configuration?: string;
|
|
11
|
+
bind?: string;
|
|
12
|
+
publicOrigin?: string;
|
|
11
13
|
}
|
|
12
14
|
export interface WebServiceOptions {
|
|
13
15
|
platform?: NodeJS.Platform;
|
|
@@ -27,7 +29,10 @@ export declare class WebServiceManager {
|
|
|
27
29
|
constructor(options?: WebServiceOptions);
|
|
28
30
|
get metadataPath(): string;
|
|
29
31
|
get definitionPath(): string;
|
|
30
|
-
install(script: string, port?: number, configuration?: string
|
|
32
|
+
install(script: string, port?: number, configuration?: string, web?: {
|
|
33
|
+
bind?: string;
|
|
34
|
+
publicOrigin?: string;
|
|
35
|
+
}): Promise<string[]>;
|
|
31
36
|
start(): Promise<void>;
|
|
32
37
|
stop(): Promise<void>;
|
|
33
38
|
restart(): Promise<void>;
|
|
@@ -37,6 +42,12 @@ export declare class WebServiceManager {
|
|
|
37
42
|
private requireInstalled;
|
|
38
43
|
private must;
|
|
39
44
|
}
|
|
40
|
-
export declare function systemdUnit(runtime: string, script: string, port: number, configuration?: string
|
|
41
|
-
|
|
45
|
+
export declare function systemdUnit(runtime: string, script: string, port: number, configuration?: string, web?: {
|
|
46
|
+
bind?: string;
|
|
47
|
+
publicOrigin?: string;
|
|
48
|
+
}): string;
|
|
49
|
+
export declare function launchdPlist(runtime: string, script: string, port: number, configuration?: string, web?: {
|
|
50
|
+
bind?: string;
|
|
51
|
+
publicOrigin?: string;
|
|
52
|
+
}): string;
|
|
42
53
|
export {};
|
package/dist/web/service.js
CHANGED
|
@@ -5,7 +5,7 @@ import { replaceFileAtomically } from '../atomic-file.js';
|
|
|
5
5
|
import { FleetError } from '../application/errors.js';
|
|
6
6
|
import { realExec } from '../exec.js';
|
|
7
7
|
import { home, stateRoot } from '../paths.js';
|
|
8
|
-
const VERSION =
|
|
8
|
+
const VERSION = 3;
|
|
9
9
|
export const WEB_SYSTEMD_UNIT = 'ours-fleet-web.service';
|
|
10
10
|
export const WEB_LAUNCHD_LABEL = 'network.ours.fleet.web';
|
|
11
11
|
export class WebServiceManager {
|
|
@@ -32,7 +32,7 @@ export class WebServiceManager {
|
|
|
32
32
|
? join(this.homeDir, '.config', 'systemd', 'user', WEB_SYSTEMD_UNIT)
|
|
33
33
|
: join(this.homeDir, 'Library', 'LaunchAgents', `${WEB_LAUNCHD_LABEL}.plist`);
|
|
34
34
|
}
|
|
35
|
-
async install(script, port = 49_271, configuration) {
|
|
35
|
+
async install(script, port = 49_271, configuration, web = {}) {
|
|
36
36
|
validatePort(port);
|
|
37
37
|
const resolvedScript = resolveExecutable(script, 'web CLI script');
|
|
38
38
|
const runtime = resolveExecutable(this.runtimeExecutable, 'Node runtime');
|
|
@@ -43,10 +43,12 @@ export class WebServiceManager {
|
|
|
43
43
|
const metadata = {
|
|
44
44
|
version: VERSION, platform: this.platform, runtime, script: resolvedScript, port,
|
|
45
45
|
...(config ? { configuration: config } : {}),
|
|
46
|
+
...(web.bind ? { bind: web.bind } : {}),
|
|
47
|
+
...(web.publicOrigin ? { publicOrigin: web.publicOrigin } : {}),
|
|
46
48
|
};
|
|
47
49
|
replaceFileAtomically(this.definitionPath, this.platform === 'linux'
|
|
48
|
-
? systemdUnit(runtime, resolvedScript, port, config)
|
|
49
|
-
: launchdPlist(runtime, resolvedScript, port, config), 0o600);
|
|
50
|
+
? systemdUnit(runtime, resolvedScript, port, config, web)
|
|
51
|
+
: launchdPlist(runtime, resolvedScript, port, config, web), 0o600);
|
|
50
52
|
replaceFileAtomically(this.metadataPath, JSON.stringify(metadata, null, 2) + '\n', 0o600);
|
|
51
53
|
if (this.platform === 'linux') {
|
|
52
54
|
await this.must('systemctl', ['--user', 'daemon-reload']);
|
|
@@ -123,9 +125,9 @@ export class WebServiceManager {
|
|
|
123
125
|
readMetadata() {
|
|
124
126
|
try {
|
|
125
127
|
const parsed = JSON.parse(readFileSync(this.metadataPath, 'utf8'));
|
|
126
|
-
return parsed.version
|
|
128
|
+
return [2, VERSION].includes(parsed.version) && parsed.platform === this.platform
|
|
127
129
|
&& typeof parsed.runtime === 'string' && typeof parsed.script === 'string'
|
|
128
|
-
&& Number.isInteger(parsed.port) ? parsed : undefined;
|
|
130
|
+
&& Number.isInteger(parsed.port) ? { ...parsed, version: VERSION } : undefined;
|
|
129
131
|
}
|
|
130
132
|
catch {
|
|
131
133
|
return undefined;
|
|
@@ -157,24 +159,28 @@ function validatePort(port) {
|
|
|
157
159
|
function systemdQuote(value) {
|
|
158
160
|
return `"${value.replace(/[%\\"]/g, char => char === '%' ? '%%' : `\\${char}`)}"`;
|
|
159
161
|
}
|
|
160
|
-
export function systemdUnit(runtime, script, port, configuration) {
|
|
162
|
+
export function systemdUnit(runtime, script, port, configuration, web = {}) {
|
|
161
163
|
const config = configuration ? ` --configuration ${systemdQuote(configuration)}` : '';
|
|
164
|
+
const access = `${web.bind ? ` --bind ${systemdQuote(web.bind)}` : ''}`
|
|
165
|
+
+ `${web.publicOrigin ? ` --public-origin ${systemdQuote(web.publicOrigin)}` : ''}`;
|
|
162
166
|
return `[Unit]\nDescription=ours-fleet localhost web console\nAfter=default.target\n\n`
|
|
163
|
-
+ `[Service]\nType=simple\nExecStart=${systemdQuote(runtime)} ${systemdQuote(script)} web serve --port ${port} --no-open${config}\n`
|
|
167
|
+
+ `[Service]\nType=simple\nExecStart=${systemdQuote(runtime)} ${systemdQuote(script)} web serve --port ${port} --no-open${config}${access}\n`
|
|
164
168
|
+ `Restart=on-failure\nRestartSec=5\nTimeoutStopSec=15\n\n`
|
|
165
169
|
+ `[Install]\nWantedBy=default.target\n`;
|
|
166
170
|
}
|
|
167
171
|
const xml = (value) => value.replace(/[&<>"']/g, char => ({
|
|
168
172
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
169
173
|
}[char]));
|
|
170
|
-
export function launchdPlist(runtime, script, port, configuration) {
|
|
174
|
+
export function launchdPlist(runtime, script, port, configuration, web = {}) {
|
|
171
175
|
const config = configuration
|
|
172
176
|
? `<string>--configuration</string><string>${xml(configuration)}</string>` : '';
|
|
177
|
+
const access = `${web.bind ? `<string>--bind</string><string>${xml(web.bind)}</string>` : ''}`
|
|
178
|
+
+ `${web.publicOrigin ? `<string>--public-origin</string><string>${xml(web.publicOrigin)}</string>` : ''}`;
|
|
173
179
|
return `<?xml version="1.0" encoding="UTF-8"?>\n`
|
|
174
180
|
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n`
|
|
175
181
|
+ `<plist version="1.0"><dict>\n<key>Label</key><string>${WEB_LAUNCHD_LABEL}</string>\n`
|
|
176
182
|
+ `<key>ProgramArguments</key><array><string>${xml(runtime)}</string><string>${xml(script)}</string><string>web</string>`
|
|
177
|
-
+ `<string>serve</string><string>--port</string><string>${port}</string><string>--no-open</string>${config}</array>\n`
|
|
183
|
+
+ `<string>serve</string><string>--port</string><string>${port}</string><string>--no-open</string>${config}${access}</array>\n`
|
|
178
184
|
+ `<key>RunAtLoad</key><true/><key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>\n`
|
|
179
185
|
+ `<key>ProcessType</key><string>Background</string>\n</dict></plist>\n`;
|
|
180
186
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as le,a as Ee,j as re}from"./index-
|
|
1
|
+
import{r as le,a as Ee,j as re}from"./index-B6T8JLSd.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
|
|
2
2
|
`)))),this.register(this._terminal.onA11yTab((f=>this._handleTab(f)))),this.register(this._terminal.onKey((f=>this._handleKey(f.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,t.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,u.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let i=0;i<e;i++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===`
|
|
3
3
|
`&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(e)||this._charsToConsume.push(e)}_refreshRows(e,i){this._liveRegionDebouncer.refresh(e,i,this._terminal.rows)}_renderRows(e,i){const a=this._terminal.buffer,v=a.lines.length.toString();for(let f=e;f<=i;f++){const g=a.lines.get(a.ydisp+f),c=[],m=g?.translateToString(!0,void 0,void 0,c)||"",E=(a.ydisp+f+1).toString(),k=this._rowElements[f];k&&(m.length===0?(k.innerText=" ",this._rowColumns.set(k,[0,1])):(k.textContent=m,this._rowColumns.set(k,c)),k.setAttribute("aria-posinset",E),k.setAttribute("aria-setsize",v))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,i){const a=e.target,v=this._rowElements[i===0?1:this._rowElements.length-2];if(a.getAttribute("aria-posinset")===(i===0?"1":`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==v)return;let f,g;if(i===0?(f=a,g=this._rowElements.pop(),this._rowContainer.removeChild(g)):(f=this._rowElements.shift(),g=a,this._rowContainer.removeChild(f)),f.removeEventListener("focus",this._topBoundaryFocusListener),g.removeEventListener("focus",this._bottomBoundaryFocusListener),i===0){const c=this._createAccessibilityTreeNode();this._rowElements.unshift(c),this._rowContainer.insertAdjacentElement("afterbegin",c)}else{const c=this._createAccessibilityTreeNode();this._rowElements.push(c),this._rowContainer.appendChild(c)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(i===0?-1:1),this._rowElements[i===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;const e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let i={node:e.anchorNode,offset:e.anchorOffset},a={node:e.focusNode,offset:e.focusOffset};if((i.node.compareDocumentPosition(a.node)&Node.DOCUMENT_POSITION_PRECEDING||i.node===a.node&&i.offset>a.offset)&&([i,a]=[a,i]),i.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(i={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(i.node))return;const v=this._rowElements.slice(-1)[0];if(a.node.compareDocumentPosition(v)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(a={node:v,offset:v.textContent?.length??0}),!this._rowContainer.contains(a.node))return;const f=({node:m,offset:E})=>{const k=m instanceof Text?m.parentNode:m;let D=parseInt(k?.getAttribute("aria-posinset"),10)-1;if(isNaN(D))return console.warn("row is invalid. Race condition?"),null;const b=this._rowColumns.get(k);if(!b)return console.warn("columns is null. Race condition?"),null;let x=E<b.length?b[E]:b.slice(-1)[0]+1;return x>=this._terminal.cols&&(++D,x=0),{row:D,column:x}},g=f(i),c=f(a);if(g&&c){if(g.row>c.row||g.row===c.row&&g.column>=c.column)throw new Error("invalid range");this._terminal.select(g.column,g.row,(c.row-g.row)*this._terminal.cols-g.column+c.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let i=this._rowContainer.children.length;i<this._terminal.rows;i++)this._rowElements[i]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[i]);for(;this._rowElements.length>e;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e<this._terminal.rows;e++)this._refreshRowDimensions(this._rowElements[e])}}_refreshRowDimensions(e){e.style.height=`${this._renderService.dimensions.css.cell.height}px`}};r.AccessibilityManager=s=l([_(1,h.IInstantiationService),_(2,p.ICoreBrowserService),_(3,p.IRenderService)],s)},3614:(B,r)=>{function o(d){return d.replace(/\r?\n/g,"\r")}function l(d,u){return u?"\x1B[200~"+d+"\x1B[201~":d}function _(d,u,p,h){d=l(d=o(d),p.decPrivateModes.bracketedPasteMode&&h.rawOptions.ignoreBracketedPasteMode!==!0),p.triggerDataEvent(d,!0),u.value=""}function n(d,u,p){const h=p.getBoundingClientRect(),t=d.clientX-h.left-10,s=d.clientY-h.top-10;u.style.width="20px",u.style.height="20px",u.style.left=`${t}px`,u.style.top=`${s}px`,u.style.zIndex="1000",u.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=o,r.bracketTextForPaste=l,r.copyHandler=function(d,u){d.clipboardData&&d.clipboardData.setData("text/plain",u.selectionText),d.preventDefault()},r.handlePasteEvent=function(d,u,p,h){d.stopPropagation(),d.clipboardData&&_(d.clipboardData.getData("text/plain"),u,p,h)},r.paste=_,r.moveTextAreaUnderMouseCursor=n,r.rightClickHandler=function(d,u,p,h,t){n(d,u,p),t&&h.rightClickSelect(d),u.value=h.selectionText,u.select()}},7239:(B,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;const l=o(1505);r.ColorContrastCache=class{constructor(){this._color=new l.TwoKeyMap,this._css=new l.TwoKeyMap}setCss(_,n,d){this._css.set(_,n,d)}getCss(_,n){return this._css.get(_,n)}setColor(_,n,d){this._color.set(_,n,d)}getColor(_,n){return this._color.get(_,n)}clear(){this._color.clear(),this._css.clear()}}},3656:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(o,l,_,n){o.addEventListener(l,_,n);let d=!1;return{dispose:()=>{d||(d=!0,o.removeEventListener(l,_,n))}}}},3551:function(B,r,o){var l=this&&this.__decorate||function(s,e,i,a){var v,f=arguments.length,g=f<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,i):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,i,a);else for(var c=s.length-1;c>=0;c--)(v=s[c])&&(g=(f<3?v(g):f>3?v(e,i,g):v(e,i))||g);return f>3&&g&&Object.defineProperty(e,i,g),g},_=this&&this.__param||function(s,e){return function(i,a){e(i,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;const n=o(3656),d=o(8460),u=o(844),p=o(2585),h=o(4725);let t=r.Linkifier=class extends u.Disposable{get currentLink(){return this._currentLink}constructor(s,e,i,a,v){super(),this._element=s,this._mouseService=e,this._renderService=i,this._bufferService=a,this._linkProviderService=v,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new d.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new d.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,u.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,u.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(s){this._lastMouseEvent=s;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);if(!e)return;this._isMouseOut=!1;const i=s.composedPath();for(let a=0;a<i.length;a++){const v=i[a];if(v.classList.contains("xterm"))break;if(v.classList.contains("xterm-hover"))return}this._lastBufferCell&&e.x===this._lastBufferCell.x&&e.y===this._lastBufferCell.y||(this._handleHover(e),this._lastBufferCell=e)}_handleHover(s){if(this._activeLine!==s.y||this._wasResized)return this._clearCurrentLink(),this._askForLink(s,!1),void(this._wasResized=!1);this._currentLink&&this._linkAtPosition(this._currentLink.link,s)||(this._clearCurrentLink(),this._askForLink(s,!0))}_askForLink(s,e){this._activeProviderReplies&&e||(this._activeProviderReplies?.forEach((a=>{a?.forEach((v=>{v.link.dispose&&v.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=s.y);let i=!1;for(const[a,v]of this._linkProviderService.linkProviders.entries())e?this._activeProviderReplies?.get(a)&&(i=this._checkLinkProviderResult(a,s,i)):v.provideLinks(s.y,(f=>{if(this._isMouseOut)return;const g=f?.map((c=>({link:c})));this._activeProviderReplies?.set(a,g),i=this._checkLinkProviderResult(a,s,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(s.y,this._activeProviderReplies)}))}_removeIntersectingLinks(s,e){const i=new Set;for(let a=0;a<e.size;a++){const v=e.get(a);if(v)for(let f=0;f<v.length;f++){const g=v[f],c=g.link.range.start.y<s?0:g.link.range.start.x,m=g.link.range.end.y>s?this._bufferService.cols:g.link.range.end.x;for(let E=c;E<=m;E++){if(i.has(E)){v.splice(f--,1);break}i.add(E)}}}}_checkLinkProviderResult(s,e,i){if(!this._activeProviderReplies)return i;const a=this._activeProviderReplies.get(s);let v=!1;for(let f=0;f<s;f++)this._activeProviderReplies.has(f)&&!this._activeProviderReplies.get(f)||(v=!0);if(!v&&a){const f=a.find((g=>this._linkAtPosition(g.link,e)));f&&(i=!0,this._handleNewLink(f))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let f=0;f<this._activeProviderReplies.size;f++){const g=this._activeProviderReplies.get(f)?.find((c=>this._linkAtPosition(c.link,e)));if(g){i=!0,this._handleNewLink(g);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(s){if(!this._currentLink)return;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);e&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,e)&&this._currentLink.link.activate(s,this._currentLink.link.text)}_clearCurrentLink(s,e){this._currentLink&&this._lastMouseEvent&&(!s||!e||this._currentLink.link.range.start.y>=s&&this._currentLink.link.range.end.y<=e)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,u.disposeArray)(this._linkCacheDisposables))}_handleNewLink(s){if(!this._lastMouseEvent)return;const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._linkAtPosition(s.link,e)&&(this._currentLink=s,this._currentLink.state={decorations:{underline:s.link.decorations===void 0||s.link.decorations.underline,pointerCursor:s.link.decorations===void 0||s.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,s.link,this._lastMouseEvent),s.link.decorations={},Object.defineProperties(s.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:i=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==i&&(this._currentLink.state.decorations.pointerCursor=i,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",i))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:i=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==i&&(this._currentLink.state.decorations.underline=i,this._currentLink.state.isHovered&&this._fireUnderlineEvent(s.link,i))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((i=>{if(!this._currentLink)return;const a=i.start===0?0:i.start+1+this._bufferService.buffer.ydisp,v=this._bufferService.buffer.ydisp+1+i.end;if(this._currentLink.link.range.start.y>=a&&this._currentLink.link.range.end.y<=v&&(this._clearCurrentLink(a,v),this._lastMouseEvent)){const f=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);f&&this._askForLink(f,!1)}}))))}_linkHover(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!0),this._currentLink.state.decorations.pointerCursor&&s.classList.add("xterm-cursor-pointer")),e.hover&&e.hover(i,e.text)}_fireUnderlineEvent(s,e){const i=s.range,a=this._bufferService.buffer.ydisp,v=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-a-1,i.end.x,i.end.y-a-1,void 0);(e?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(v)}_linkLeave(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!1),this._currentLink.state.decorations.pointerCursor&&s.classList.remove("xterm-cursor-pointer")),e.leave&&e.leave(i,e.text)}_linkAtPosition(s,e){const i=s.range.start.y*this._bufferService.cols+s.range.start.x,a=s.range.end.y*this._bufferService.cols+s.range.end.x,v=e.y*this._bufferService.cols+e.x;return i<=v&&v<=a}_positionFromMouseEvent(s,e,i){const a=i.getCoords(s,e,this._bufferService.cols,this._bufferService.rows);if(a)return{x:a[0],y:a[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(s,e,i,a,v){return{x1:s,y1:e,x2:i,y2:a,cols:this._bufferService.cols,fg:v}}};r.Linkifier=t=l([_(1,h.IMouseService),_(2,h.IRenderService),_(3,p.IBufferService),_(4,h.ILinkProviderService)],t)},9042:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(B,r,o){var l=this&&this.__decorate||function(h,t,s,e){var i,a=arguments.length,v=a<3?t:e===null?e=Object.getOwnPropertyDescriptor(t,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")v=Reflect.decorate(h,t,s,e);else for(var f=h.length-1;f>=0;f--)(i=h[f])&&(v=(a<3?i(v):a>3?i(t,s,v):i(t,s))||v);return a>3&&v&&Object.defineProperty(t,s,v),v},_=this&&this.__param||function(h,t){return function(s,e){t(s,e,h)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;const n=o(511),d=o(2585);let u=r.OscLinkProvider=class{constructor(h,t,s){this._bufferService=h,this._optionsService=t,this._oscLinkService=s}provideLinks(h,t){const s=this._bufferService.buffer.lines.get(h-1);if(!s)return void t(void 0);const e=[],i=this._optionsService.rawOptions.linkHandler,a=new n.CellData,v=s.getTrimmedLength();let f=-1,g=-1,c=!1;for(let m=0;m<v;m++)if(g!==-1||s.hasContent(m)){if(s.loadCell(m,a),a.hasExtendedAttrs()&&a.extended.urlId){if(g===-1){g=m,f=a.extended.urlId;continue}c=a.extended.urlId!==f}else g!==-1&&(c=!0);if(c||g!==-1&&m===v-1){const E=this._oscLinkService.getLinkData(f)?.uri;if(E){const k={start:{x:g+1,y:h},end:{x:m+(c||m!==v-1?0:1),y:h}};let D=!1;if(!i?.allowNonHttpProtocols)try{const b=new URL(E);["http:","https:"].includes(b.protocol)||(D=!0)}catch{D=!0}D||e.push({text:E,range:k,activate:(b,x)=>i?i.activate(b,x,k):p(0,x),hover:(b,x)=>i?.hover?.(b,x,k),leave:(b,x)=>i?.leave?.(b,x,k)})}c=!1,a.hasExtendedAttrs()&&a.extended.urlId?(g=m,f=a.extended.urlId):(g=-1,f=-1)}}t(e)}};function p(h,t){if(confirm(`Do you want to navigate to ${t}?
|
|
4
4
|
|