gestament 0.4.0 → 0.5.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 +20 -5
- package/dist/displaySession.d.ts +2 -2
- package/dist/displaySession.d.ts.map +1 -1
- package/dist/element.d.ts +2 -2
- package/dist/errors.d.ts +2 -2
- package/dist/generated/packageMetadata.d.ts +4 -4
- package/dist/gestament-config.d.ts +2 -2
- package/dist/gestament-launcher-driver.cjs +91 -5
- package/dist/gestament-launcher-driver.cjs.map +1 -1
- package/dist/gestament-launcher-driver.d.ts +2 -2
- package/dist/gestament-launcher-driver.mjs +91 -5
- package/dist/gestament-launcher-driver.mjs.map +1 -1
- package/dist/gestament-tray-host.cjs +9 -1
- package/dist/gestament-tray-host.cjs.map +1 -1
- package/dist/gestament-tray-host.d.ts +2 -2
- package/dist/gestament-tray-host.mjs +9 -1
- package/dist/gestament-tray-host.mjs.map +1 -1
- package/dist/gestament-xvfb-pool-probe.cjs +1 -1
- package/dist/gestament-xvfb-pool-probe.d.ts +2 -2
- package/dist/gestament-xvfb-pool-probe.mjs +1 -1
- package/dist/gestament-xvfb-worker.d.ts +2 -2
- package/dist/gestament-xvfb.d.ts +2 -2
- package/dist/gestament.cjs +279 -0
- package/dist/gestament.cjs.map +1 -0
- package/dist/gestament.d.ts +29 -0
- package/dist/gestament.d.ts.map +1 -0
- package/dist/gestament.mjs +278 -0
- package/dist/gestament.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{launchGtkApp-BfELuV-H.cjs → launchGtkApp-CzYcrc9f.cjs} +693 -76
- package/dist/launchGtkApp-CzYcrc9f.cjs.map +1 -0
- package/dist/{launchGtkApp-Bst1BFbD.js → launchGtkApp-EI6OIpPI.js} +691 -74
- package/dist/launchGtkApp-EI6OIpPI.js.map +1 -0
- package/dist/launchGtkApp.d.ts +2 -2
- package/dist/launchGtkApp.d.ts.map +1 -1
- package/dist/launcherDriverProtocol.d.ts +26 -4
- package/dist/launcherDriverProtocol.d.ts.map +1 -1
- package/dist/{native-C6MsIBNF.js → native-DlCBBWlV.js} +2 -2
- package/dist/native-DlCBBWlV.js.map +1 -0
- package/dist/{native-BUWDWMBB.cjs → native-Kfm95Uxw.cjs} +6 -6
- package/dist/native-Kfm95Uxw.cjs.map +1 -0
- package/dist/native.d.ts +2 -2
- package/dist/output.d.ts +27 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/packageMetadata-CewXH0iF.cjs +4 -0
- package/dist/packageMetadata-CewXH0iF.cjs.map +1 -0
- package/dist/packageMetadata-DjxyJCJm.js +5 -0
- package/dist/packageMetadata-DjxyJCJm.js.map +1 -0
- package/dist/prerequisites.d.ts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/tray.d.ts +2 -2
- package/dist/types.d.ts +183 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/wait.d.ts +2 -2
- package/package.json +8 -7
- package/prebuilds/linux-arm/gtk3/node.napi.armv7.glibc.node +0 -0
- package/prebuilds/linux-arm/gtk4/node.napi.armv7.glibc.node +0 -0
- package/prebuilds/linux-arm64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-arm64/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-ia32/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-ia32/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-riscv64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-riscv64/gtk4/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/gtk3/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/gtk4/node.napi.glibc.node +0 -0
- package/dist/launchGtkApp-BfELuV-H.cjs.map +0 -1
- package/dist/launchGtkApp-Bst1BFbD.js.map +0 -1
- package/dist/native-BUWDWMBB.cjs.map +0 -1
- package/dist/native-C6MsIBNF.js.map +0 -1
|
@@ -7,7 +7,232 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { join, resolve, dirname } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { a as appendPrerequisiteInstallHint } from "./prerequisites-JB0SKPVd.js";
|
|
10
|
-
import { c as nativeElementInfo, d as nativeClick, e as nativeImageInfo, f as nativeCaptureBounds, g as nativeTableDeselectColumn, h as nativeTableSelectColumn, i as nativeTableDeselectRow, j as nativeTableSelectRow, k as nativeTableIsCellSelected, l as nativeTableIsColumnSelected, m as nativeTableIsRowSelected, o as nativeTableSelectedColumns, p as nativeTableSelectedRows, q as nativeTableColumnCount, r as nativeTableRowCount, s as nativeClearSelection, t as nativeSelectAllChildren, u as nativeDeselectChildAt, v as nativeSelectChildAt, w as nativeIsChildSelected, x as nativeSelectedChildAt, y as nativeSelectedChildCount, z as nativeChildCount, A as nativeChildAt, B as nativeValueInfo, C as nativeSetValue, D as nativeText, E as nativeSetText, F as nativeX11Info, G as nativeResizeHints, H as nativeBounds, I as nativeCapture, J as nativeTableCellAt, K as nativeFindAnyById, L as nativeTrayItems, M as nativeWindowCount, N as nativeWindowAt, a as nativeCaptureScreen, O as nativeFindById, P as nativeProcessAtspiReadiness } from "./native-
|
|
10
|
+
import { c as nativeElementInfo, d as nativeClick, e as nativeImageInfo, f as nativeCaptureBounds, g as nativeTableDeselectColumn, h as nativeTableSelectColumn, i as nativeTableDeselectRow, j as nativeTableSelectRow, k as nativeTableIsCellSelected, l as nativeTableIsColumnSelected, m as nativeTableIsRowSelected, o as nativeTableSelectedColumns, p as nativeTableSelectedRows, q as nativeTableColumnCount, r as nativeTableRowCount, s as nativeClearSelection, t as nativeSelectAllChildren, u as nativeDeselectChildAt, v as nativeSelectChildAt, w as nativeIsChildSelected, x as nativeSelectedChildAt, y as nativeSelectedChildCount, z as nativeChildCount, A as nativeChildAt, B as nativeValueInfo, C as nativeSetValue, D as nativeText, E as nativeSetText, F as nativeX11Info, G as nativeResizeHints, H as nativeBounds, I as nativeCapture, J as nativeTableCellAt, K as nativeFindAnyById, L as nativeTrayItems, M as nativeWindowCount, N as nativeWindowAt, a as nativeCaptureScreen, O as nativeFindById, P as nativeProcessAtspiReadiness } from "./native-DlCBBWlV.js";
|
|
11
|
+
const normalizeOutputBufferBytes = (value, optionName = "outputBufferBytes") => {
|
|
12
|
+
if (value === void 0) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
16
|
+
throw createGtkInvalidArgumentError(
|
|
17
|
+
`${optionName} must be a non-negative safe integer.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
const createStreamState = () => ({
|
|
23
|
+
byteLength: 0,
|
|
24
|
+
chunks: [],
|
|
25
|
+
decoder: new TextDecoder(),
|
|
26
|
+
flushed: false,
|
|
27
|
+
truncated: false
|
|
28
|
+
});
|
|
29
|
+
const appendBoundedChunk = (state, chunk, maxBytes) => {
|
|
30
|
+
if (chunk.length === 0) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (maxBytes === void 0) {
|
|
34
|
+
state.chunks.push(Buffer.from(chunk));
|
|
35
|
+
state.byteLength += chunk.length;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (maxBytes === 0) {
|
|
39
|
+
state.truncated = true;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (chunk.length >= maxBytes) {
|
|
43
|
+
state.chunks.splice(
|
|
44
|
+
0,
|
|
45
|
+
state.chunks.length,
|
|
46
|
+
Buffer.from(chunk.subarray(chunk.length - maxBytes))
|
|
47
|
+
);
|
|
48
|
+
state.byteLength = maxBytes;
|
|
49
|
+
state.truncated = true;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
state.chunks.push(Buffer.from(chunk));
|
|
53
|
+
state.byteLength += chunk.length;
|
|
54
|
+
while (state.byteLength > maxBytes) {
|
|
55
|
+
const first = state.chunks[0];
|
|
56
|
+
if (first === void 0) {
|
|
57
|
+
state.byteLength = 0;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const excess = state.byteLength - maxBytes;
|
|
61
|
+
if (first.length <= excess) {
|
|
62
|
+
state.chunks.shift();
|
|
63
|
+
state.byteLength -= first.length;
|
|
64
|
+
state.truncated = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
state.chunks[0] = Buffer.from(first.subarray(excess));
|
|
68
|
+
state.byteLength -= excess;
|
|
69
|
+
state.truncated = true;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const streamText = (state) => Buffer.concat(state.chunks, state.byteLength).toString("utf8");
|
|
73
|
+
const createOutputEvent = (nextSequence, create, text) => {
|
|
74
|
+
if (text.length === 0) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
return create(nextSequence());
|
|
78
|
+
};
|
|
79
|
+
const createGtkAppOutputRecorder = (outputBufferBytes) => {
|
|
80
|
+
const maxBytes = normalizeOutputBufferBytes(outputBufferBytes);
|
|
81
|
+
const stdout = createStreamState();
|
|
82
|
+
const stderr = createStreamState();
|
|
83
|
+
let nextSequence = 0;
|
|
84
|
+
const streamState = (stream) => stream === "stdout" ? stdout : stderr;
|
|
85
|
+
const takeSequence = () => {
|
|
86
|
+
const sequence = nextSequence;
|
|
87
|
+
nextSequence += 1;
|
|
88
|
+
return sequence;
|
|
89
|
+
};
|
|
90
|
+
const createEvent = (stream, text) => createOutputEvent(
|
|
91
|
+
takeSequence,
|
|
92
|
+
(sequence) => ({
|
|
93
|
+
sequence,
|
|
94
|
+
stream,
|
|
95
|
+
text,
|
|
96
|
+
timestampMs: Date.now()
|
|
97
|
+
}),
|
|
98
|
+
text
|
|
99
|
+
);
|
|
100
|
+
return {
|
|
101
|
+
append: (stream, chunk) => {
|
|
102
|
+
const state = streamState(stream);
|
|
103
|
+
appendBoundedChunk(state, chunk, maxBytes);
|
|
104
|
+
return createEvent(stream, state.decoder.decode(chunk, { stream: true }));
|
|
105
|
+
},
|
|
106
|
+
flush: (stream) => {
|
|
107
|
+
const state = streamState(stream);
|
|
108
|
+
if (state.flushed) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
state.flushed = true;
|
|
112
|
+
return createEvent(stream, state.decoder.decode());
|
|
113
|
+
},
|
|
114
|
+
snapshot: (exitCode, exitSignal) => ({
|
|
115
|
+
exitCode,
|
|
116
|
+
exitSignal,
|
|
117
|
+
stderr: streamText(stderr),
|
|
118
|
+
stderrTruncated: stderr.truncated,
|
|
119
|
+
stdout: streamText(stdout),
|
|
120
|
+
stdoutTruncated: stdout.truncated
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const systemOutputSources = [
|
|
125
|
+
"xvfb",
|
|
126
|
+
"launcher-driver",
|
|
127
|
+
"tray-host"
|
|
128
|
+
];
|
|
129
|
+
const createSourceState = () => ({
|
|
130
|
+
stderr: createStreamState(),
|
|
131
|
+
stdout: createStreamState()
|
|
132
|
+
});
|
|
133
|
+
const createGtkSystemOutputRecorder = (systemOutputBufferBytes) => {
|
|
134
|
+
const maxBytes = normalizeOutputBufferBytes(
|
|
135
|
+
systemOutputBufferBytes,
|
|
136
|
+
"systemOutputBufferBytes"
|
|
137
|
+
);
|
|
138
|
+
const sources = /* @__PURE__ */ new Map();
|
|
139
|
+
let nextSequence = 0;
|
|
140
|
+
const takeSequence = () => {
|
|
141
|
+
const sequence = nextSequence;
|
|
142
|
+
nextSequence += 1;
|
|
143
|
+
return sequence;
|
|
144
|
+
};
|
|
145
|
+
const sourceState = (source) => {
|
|
146
|
+
const existing = sources.get(source);
|
|
147
|
+
if (existing !== void 0) {
|
|
148
|
+
return existing;
|
|
149
|
+
}
|
|
150
|
+
const state = createSourceState();
|
|
151
|
+
sources.set(source, state);
|
|
152
|
+
return state;
|
|
153
|
+
};
|
|
154
|
+
const streamState = (source, stream) => {
|
|
155
|
+
const state = sourceState(source);
|
|
156
|
+
return stream === "stdout" ? state.stdout : state.stderr;
|
|
157
|
+
};
|
|
158
|
+
const existingStreamState = (source, stream) => {
|
|
159
|
+
const state = sources.get(source);
|
|
160
|
+
if (state === void 0) {
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
return stream === "stdout" ? state.stdout : state.stderr;
|
|
164
|
+
};
|
|
165
|
+
const createEvent = (source, stream, text) => createOutputEvent(
|
|
166
|
+
takeSequence,
|
|
167
|
+
(sequence) => ({
|
|
168
|
+
sequence,
|
|
169
|
+
source,
|
|
170
|
+
stream,
|
|
171
|
+
text,
|
|
172
|
+
timestampMs: Date.now()
|
|
173
|
+
}),
|
|
174
|
+
text
|
|
175
|
+
);
|
|
176
|
+
const sourceSnapshot = (source, state) => ({
|
|
177
|
+
source,
|
|
178
|
+
stderr: streamText(state.stderr),
|
|
179
|
+
stderrTruncated: state.stderr.truncated,
|
|
180
|
+
stdout: streamText(state.stdout),
|
|
181
|
+
stdoutTruncated: state.stdout.truncated
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
append: (source, stream, chunk) => {
|
|
185
|
+
const state = streamState(source, stream);
|
|
186
|
+
appendBoundedChunk(state, chunk, maxBytes);
|
|
187
|
+
return createEvent(
|
|
188
|
+
source,
|
|
189
|
+
stream,
|
|
190
|
+
state.decoder.decode(chunk, { stream: true })
|
|
191
|
+
);
|
|
192
|
+
},
|
|
193
|
+
flush: (source, stream) => {
|
|
194
|
+
const state = existingStreamState(source, stream);
|
|
195
|
+
if (state === void 0) {
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
if (state.flushed) {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
state.flushed = true;
|
|
202
|
+
return createEvent(source, stream, state.decoder.decode());
|
|
203
|
+
},
|
|
204
|
+
snapshot: () => ({
|
|
205
|
+
sources: systemOutputSources.flatMap((source) => {
|
|
206
|
+
const state = sources.get(source);
|
|
207
|
+
return state === void 0 ? [] : [sourceSnapshot(source, state)];
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const notifyGtkAppOutput = (callback, event) => {
|
|
213
|
+
if (callback === void 0 || event === void 0) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
callback(event);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
queueMicrotask(() => {
|
|
220
|
+
throw error;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const notifyGtkSystemOutput = (callback, event) => {
|
|
225
|
+
if (callback === void 0 || event === void 0) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
callback(event);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
queueMicrotask(() => {
|
|
232
|
+
throw error;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
};
|
|
11
236
|
const defaultDisplay = "xvfb";
|
|
12
237
|
const defaultGSettings = "memory";
|
|
13
238
|
const defaultTheme = "Adwaita";
|
|
@@ -20,6 +245,9 @@ const sessionStartupTimeoutMs = 3e4;
|
|
|
20
245
|
const sessionReleaseTimeoutMs = 5e3;
|
|
21
246
|
const xvfbStartupTimeoutMs = 1e4;
|
|
22
247
|
const xvfbPoolProbeTimeoutMs = 5e3;
|
|
248
|
+
const xvfbPoolProbeRetryIntervalMs = 50;
|
|
249
|
+
const xvfbPoolProbePrefix = "gestament-xvfb-pool-probe: ";
|
|
250
|
+
const x11DisplayOpenFailureMessage = "Failed to open the X11 display. Ensure DISPLAY points to an X11 display.";
|
|
23
251
|
const firstPooledDisplayNumber = 90;
|
|
24
252
|
const lastPooledDisplayNumber = 590;
|
|
25
253
|
const sessionOwnedEnvironmentKeys = [
|
|
@@ -33,6 +261,7 @@ const sessionOwnedEnvironmentKeys = [
|
|
|
33
261
|
"GESTAMENT_XVFB_ACTIVE",
|
|
34
262
|
"XDG_SESSION_TYPE"
|
|
35
263
|
];
|
|
264
|
+
let outputScopeCounter = 0;
|
|
36
265
|
let socketCounter = 0;
|
|
37
266
|
const leasedDisplayNumbers = /* @__PURE__ */ new Set();
|
|
38
267
|
const idleXvfbByKey = /* @__PURE__ */ new Map();
|
|
@@ -49,6 +278,12 @@ const appendOutput$1 = (lines, chunk) => {
|
|
|
49
278
|
lines.splice(0, lines.length - 40);
|
|
50
279
|
}
|
|
51
280
|
};
|
|
281
|
+
const appendSystemOutput = (sink, source, stream, chunk) => {
|
|
282
|
+
sink?.append(source, stream, chunk);
|
|
283
|
+
};
|
|
284
|
+
const flushSystemOutput = (sink, source, stream) => {
|
|
285
|
+
sink?.flush(source, stream);
|
|
286
|
+
};
|
|
52
287
|
const unrefHandle = (handle) => {
|
|
53
288
|
const maybeRefHandle = handle;
|
|
54
289
|
if (typeof maybeRefHandle.unref === "function") {
|
|
@@ -67,6 +302,41 @@ ${stdoutText}
|
|
|
67
302
|
stderr:
|
|
68
303
|
${stderrText}`;
|
|
69
304
|
};
|
|
305
|
+
const createXvfbProbeError = (message, retryable) => {
|
|
306
|
+
const error = createGtkOperationFailedError(message);
|
|
307
|
+
Object.defineProperty(error, "retryable", {
|
|
308
|
+
value: retryable
|
|
309
|
+
});
|
|
310
|
+
return error;
|
|
311
|
+
};
|
|
312
|
+
const parseXvfbProbeErrorPayload = (stderrText) => {
|
|
313
|
+
const lines = stderrText.trim().split("\n").reverse();
|
|
314
|
+
for (const line of lines) {
|
|
315
|
+
if (!line.startsWith(xvfbPoolProbePrefix)) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const value = JSON.parse(
|
|
320
|
+
line.slice(xvfbPoolProbePrefix.length)
|
|
321
|
+
);
|
|
322
|
+
if (!isRecord(value)) {
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
code: typeof value.code === "string" ? value.code : void 0,
|
|
327
|
+
message: typeof value.message === "string" ? value.message : void 0
|
|
328
|
+
};
|
|
329
|
+
} catch {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return void 0;
|
|
334
|
+
};
|
|
335
|
+
const isRetryableXvfbProbeExit = (stderrText) => {
|
|
336
|
+
const payload = parseXvfbProbeErrorPayload(stderrText);
|
|
337
|
+
return payload?.code === "OPERATION_FAILED" && payload.message === x11DisplayOpenFailureMessage;
|
|
338
|
+
};
|
|
339
|
+
const isRetryableXvfbProbeError = (error) => isRecord(error) && typeof error.retryable === "boolean" && error.retryable;
|
|
70
340
|
const getHostDisplayState = () => ({
|
|
71
341
|
display: process.env.DISPLAY,
|
|
72
342
|
waylandDisplay: process.env.WAYLAND_DISPLAY
|
|
@@ -92,6 +362,63 @@ const resolveDisplay = (display) => {
|
|
|
92
362
|
);
|
|
93
363
|
};
|
|
94
364
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
365
|
+
const createDriverEventKey = (channel, scopeId) => `${channel}
|
|
366
|
+
${scopeId}`;
|
|
367
|
+
const createOutputScopeId = () => {
|
|
368
|
+
const scopeId = `output-${process.pid}-${outputScopeCounter}`;
|
|
369
|
+
outputScopeCounter += 1;
|
|
370
|
+
return scopeId;
|
|
371
|
+
};
|
|
372
|
+
const isDriverEventMessage = (message) => isRecord(message) && message.type === "event" && typeof message.channel === "string" && typeof message.scopeId === "string";
|
|
373
|
+
const resolveOutputBufferBytes = (launcherValue, launchValue) => {
|
|
374
|
+
const value = launchValue ?? launcherValue;
|
|
375
|
+
if (value === void 0) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
379
|
+
throw createGtkInvalidArgumentError(
|
|
380
|
+
"outputBufferBytes must be a non-negative safe integer."
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
};
|
|
385
|
+
const createSystemOutputSink = (recorder, callback) => ({
|
|
386
|
+
append: (source, stream, chunk) => {
|
|
387
|
+
notifyGtkSystemOutput(callback, recorder.append(source, stream, chunk));
|
|
388
|
+
},
|
|
389
|
+
flush: (source, stream) => {
|
|
390
|
+
notifyGtkSystemOutput(callback, recorder.flush(source, stream));
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
const isGtkSystemOutputSource = (value) => value === "xvfb" || value === "launcher-driver" || value === "tray-host";
|
|
394
|
+
const isGtkAppOutputStream = (value) => value === "stdout" || value === "stderr";
|
|
395
|
+
const isWireGtkSystemOutput = (value) => {
|
|
396
|
+
if (!isRecord(value)) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
if (!isGtkSystemOutputSource(value.source) || !isGtkAppOutputStream(value.stream)) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
if (value.type === "flush") {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
return value.type === "chunk" && typeof value.chunkBase64 === "string";
|
|
406
|
+
};
|
|
407
|
+
const routeWireSystemOutput = (sink, value) => {
|
|
408
|
+
if (!isWireGtkSystemOutput(value)) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (value.type === "flush") {
|
|
412
|
+
flushSystemOutput(sink, value.source, value.stream);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
appendSystemOutput(
|
|
416
|
+
sink,
|
|
417
|
+
value.source,
|
|
418
|
+
value.stream,
|
|
419
|
+
Buffer.from(value.chunkBase64, "base64")
|
|
420
|
+
);
|
|
421
|
+
};
|
|
95
422
|
const resolvePoolLimit = (name, value, defaultValue) => {
|
|
96
423
|
if (value === void 0) {
|
|
97
424
|
return defaultValue;
|
|
@@ -239,6 +566,7 @@ const retainIdleXvfb = async (xvfb, limits) => {
|
|
|
239
566
|
await terminateXvfb(xvfb);
|
|
240
567
|
return;
|
|
241
568
|
}
|
|
569
|
+
xvfb.systemOutputSink = void 0;
|
|
242
570
|
xvfb.lastUsedAt = Date.now();
|
|
243
571
|
pushArrayEntry(idleXvfbByKey, xvfb.key, xvfb);
|
|
244
572
|
await trimIdleXvfbKey(xvfb.key, limits.maxIdlePerKey);
|
|
@@ -249,6 +577,8 @@ const retainIdleAllSession = async (session, limits) => {
|
|
|
249
577
|
await session.session.terminate();
|
|
250
578
|
return;
|
|
251
579
|
}
|
|
580
|
+
session.session.setSystemOutputSink(void 0);
|
|
581
|
+
session.xvfb.systemOutputSink = void 0;
|
|
252
582
|
session.lastUsedAt = Date.now();
|
|
253
583
|
session.xvfb.lastUsedAt = session.lastUsedAt;
|
|
254
584
|
pushArrayEntry(idleAllByKey, session.key, session);
|
|
@@ -461,7 +791,7 @@ const installPoolCleanup = () => {
|
|
|
461
791
|
}
|
|
462
792
|
});
|
|
463
793
|
};
|
|
464
|
-
const spawnDirectXvfb = async (screen) => {
|
|
794
|
+
const spawnDirectXvfb = async (screen, systemOutputSink) => {
|
|
465
795
|
installPoolCleanup();
|
|
466
796
|
for (let displayNumber = firstPooledDisplayNumber; displayNumber <= lastPooledDisplayNumber; displayNumber += 1) {
|
|
467
797
|
if (!isDisplayNumberAvailable(displayNumber)) {
|
|
@@ -478,11 +808,30 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
478
808
|
stdio: ["ignore", "pipe", "pipe"]
|
|
479
809
|
}
|
|
480
810
|
);
|
|
811
|
+
const xvfb = {
|
|
812
|
+
child,
|
|
813
|
+
display: `:${displayNumber}`,
|
|
814
|
+
displayNumber,
|
|
815
|
+
key: screen,
|
|
816
|
+
lastUsedAt: Date.now(),
|
|
817
|
+
screen,
|
|
818
|
+
stderr,
|
|
819
|
+
stdout,
|
|
820
|
+
systemOutputSink
|
|
821
|
+
};
|
|
481
822
|
child.stdout.on("data", (chunk) => {
|
|
482
823
|
appendOutput$1(stdout, chunk);
|
|
824
|
+
appendSystemOutput(xvfb.systemOutputSink, "xvfb", "stdout", chunk);
|
|
483
825
|
});
|
|
484
826
|
child.stderr.on("data", (chunk) => {
|
|
485
827
|
appendOutput$1(stderr, chunk);
|
|
828
|
+
appendSystemOutput(xvfb.systemOutputSink, "xvfb", "stderr", chunk);
|
|
829
|
+
});
|
|
830
|
+
child.stdout.once("end", () => {
|
|
831
|
+
flushSystemOutput(xvfb.systemOutputSink, "xvfb", "stdout");
|
|
832
|
+
});
|
|
833
|
+
child.stderr.once("end", () => {
|
|
834
|
+
flushSystemOutput(xvfb.systemOutputSink, "xvfb", "stderr");
|
|
486
835
|
});
|
|
487
836
|
child.unref();
|
|
488
837
|
unrefHandle(child.stdout);
|
|
@@ -505,22 +854,10 @@ const spawnDirectXvfb = async (screen) => {
|
|
|
505
854
|
});
|
|
506
855
|
})
|
|
507
856
|
]);
|
|
508
|
-
const xvfb = {
|
|
509
|
-
child,
|
|
510
|
-
display: `:${displayNumber}`,
|
|
511
|
-
displayNumber,
|
|
512
|
-
key: screen,
|
|
513
|
-
lastUsedAt: Date.now(),
|
|
514
|
-
screen,
|
|
515
|
-
stderr,
|
|
516
|
-
stdout
|
|
517
|
-
};
|
|
518
857
|
directXvfbs.add(xvfb);
|
|
519
858
|
return xvfb;
|
|
520
859
|
} catch (error) {
|
|
521
|
-
killXvfbNow(
|
|
522
|
-
child
|
|
523
|
-
});
|
|
860
|
+
killXvfbNow(xvfb);
|
|
524
861
|
leasedDisplayNumbers.delete(displayNumber);
|
|
525
862
|
const message = error instanceof Error ? error.message : String(error);
|
|
526
863
|
if (message.includes("ENOENT")) {
|
|
@@ -548,22 +885,24 @@ const terminateXvfb = async (xvfb) => {
|
|
|
548
885
|
await delay(25);
|
|
549
886
|
}
|
|
550
887
|
}
|
|
888
|
+
xvfb.systemOutputSink = void 0;
|
|
551
889
|
leasedDisplayNumbers.delete(xvfb.displayNumber);
|
|
552
890
|
};
|
|
553
|
-
const leaseXvfb = async (screen) => {
|
|
891
|
+
const leaseXvfb = async (screen, systemOutputSink) => {
|
|
554
892
|
for (; ; ) {
|
|
555
893
|
const idle = popArrayEntry(idleXvfbByKey, screen);
|
|
556
894
|
if (idle === void 0) {
|
|
557
|
-
return spawnDirectXvfb(screen);
|
|
895
|
+
return spawnDirectXvfb(screen, systemOutputSink);
|
|
558
896
|
}
|
|
559
897
|
if (idle.child.exitCode === null && idle.child.signalCode === null) {
|
|
560
898
|
idle.lastUsedAt = Date.now();
|
|
899
|
+
idle.systemOutputSink = systemOutputSink;
|
|
561
900
|
return idle;
|
|
562
901
|
}
|
|
563
902
|
await terminateXvfb(idle);
|
|
564
903
|
}
|
|
565
904
|
};
|
|
566
|
-
const
|
|
905
|
+
const runXvfbProbeOnce = (xvfb, timeoutMs) => new Promise((resolveProbe, rejectProbe) => {
|
|
567
906
|
const probePath = resolveXvfbPoolProbePath();
|
|
568
907
|
const stdout = [];
|
|
569
908
|
const stderr = [];
|
|
@@ -583,12 +922,32 @@ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
|
|
|
583
922
|
env,
|
|
584
923
|
stdio: ["ignore", "pipe", "pipe"]
|
|
585
924
|
});
|
|
586
|
-
|
|
925
|
+
let settled = false;
|
|
926
|
+
let timeout;
|
|
927
|
+
const rejectOnce = (error) => {
|
|
928
|
+
if (settled) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
settled = true;
|
|
932
|
+
if (timeout !== void 0) {
|
|
933
|
+
clearTimeout(timeout);
|
|
934
|
+
}
|
|
935
|
+
rejectProbe(error);
|
|
936
|
+
};
|
|
937
|
+
const resolveOnce = (result) => {
|
|
938
|
+
if (settled) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
settled = true;
|
|
942
|
+
if (timeout !== void 0) {
|
|
943
|
+
clearTimeout(timeout);
|
|
944
|
+
}
|
|
945
|
+
resolveProbe(result);
|
|
946
|
+
};
|
|
947
|
+
timeout = setTimeout(() => {
|
|
587
948
|
child.kill("SIGKILL");
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
);
|
|
591
|
-
}, xvfbPoolProbeTimeoutMs);
|
|
949
|
+
rejectOnce(createXvfbProbeError("Timed out probing Xvfb pool.", false));
|
|
950
|
+
}, timeoutMs);
|
|
592
951
|
child.stdout.on("data", (chunk) => {
|
|
593
952
|
appendOutput$1(stdout, chunk);
|
|
594
953
|
});
|
|
@@ -596,42 +955,71 @@ const runXvfbProbe = (xvfb) => new Promise((resolveProbe, rejectProbe) => {
|
|
|
596
955
|
appendOutput$1(stderr, chunk);
|
|
597
956
|
});
|
|
598
957
|
child.once("error", (error) => {
|
|
599
|
-
|
|
600
|
-
rejectProbe(error);
|
|
958
|
+
rejectOnce(error);
|
|
601
959
|
});
|
|
602
960
|
child.once("exit", (code, signal) => {
|
|
603
|
-
clearTimeout(timeout);
|
|
604
961
|
if (code !== 0) {
|
|
605
|
-
|
|
606
|
-
|
|
962
|
+
const stderrText = stderr.join("");
|
|
963
|
+
rejectOnce(
|
|
964
|
+
createXvfbProbeError(
|
|
607
965
|
`Xvfb pool probe failed: code=${String(code)}, signal=${String(
|
|
608
966
|
signal
|
|
609
|
-
)}` + formatOutputTail(stdout, stderr)
|
|
967
|
+
)}` + formatOutputTail(stdout, stderr),
|
|
968
|
+
isRetryableXvfbProbeExit(stderrText)
|
|
610
969
|
)
|
|
611
970
|
);
|
|
612
971
|
return;
|
|
613
972
|
}
|
|
614
973
|
const output = stdout.join("").trim().split("\n").at(-1);
|
|
615
974
|
if (output === void 0 || output.length === 0) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
"Xvfb pool probe did not return a result." + formatOutputTail(stdout, stderr)
|
|
975
|
+
rejectOnce(
|
|
976
|
+
createXvfbProbeError(
|
|
977
|
+
"Xvfb pool probe did not return a result." + formatOutputTail(stdout, stderr),
|
|
978
|
+
false
|
|
619
979
|
)
|
|
620
980
|
);
|
|
621
981
|
return;
|
|
622
982
|
}
|
|
623
983
|
try {
|
|
624
|
-
|
|
984
|
+
resolveOnce(JSON.parse(output));
|
|
625
985
|
} catch (error) {
|
|
626
986
|
const message = error instanceof Error ? error.message : String(error);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
`Xvfb pool probe returned invalid JSON: ${message}` + formatOutputTail(stdout, stderr)
|
|
987
|
+
rejectOnce(
|
|
988
|
+
createXvfbProbeError(
|
|
989
|
+
`Xvfb pool probe returned invalid JSON: ${message}` + formatOutputTail(stdout, stderr),
|
|
990
|
+
false
|
|
630
991
|
)
|
|
631
992
|
);
|
|
632
993
|
}
|
|
633
994
|
});
|
|
634
995
|
});
|
|
996
|
+
const runXvfbProbe = async (xvfb) => {
|
|
997
|
+
const startedAt = Date.now();
|
|
998
|
+
let lastError;
|
|
999
|
+
while (Date.now() - startedAt < xvfbPoolProbeTimeoutMs) {
|
|
1000
|
+
const remainingTimeoutMs = Math.max(
|
|
1001
|
+
1,
|
|
1002
|
+
xvfbPoolProbeTimeoutMs - (Date.now() - startedAt)
|
|
1003
|
+
);
|
|
1004
|
+
try {
|
|
1005
|
+
return await runXvfbProbeOnce(xvfb, remainingTimeoutMs);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
lastError = error;
|
|
1008
|
+
if (!isRetryableXvfbProbeError(error)) {
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const remainingDelayMs = xvfbPoolProbeTimeoutMs - (Date.now() - startedAt);
|
|
1013
|
+
if (remainingDelayMs <= 0) {
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
await delay(Math.min(xvfbPoolProbeRetryIntervalMs, remainingDelayMs));
|
|
1017
|
+
}
|
|
1018
|
+
if (lastError instanceof Error) {
|
|
1019
|
+
throw lastError;
|
|
1020
|
+
}
|
|
1021
|
+
throw createGtkOperationFailedError("Timed out probing Xvfb pool.");
|
|
1022
|
+
};
|
|
635
1023
|
const cleanCheckXvfb = async (xvfb, allowedMappedWindowCount) => {
|
|
636
1024
|
try {
|
|
637
1025
|
const probe = await runXvfbProbe(xvfb);
|
|
@@ -650,7 +1038,7 @@ const returnXvfbToPool = async (xvfb, limits) => {
|
|
|
650
1038
|
};
|
|
651
1039
|
const allPoolKey = (xvfb) => `${xvfb.screen}
|
|
652
1040
|
${xvfb.trayHost ? "tray" : "no-tray"}`;
|
|
653
|
-
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
1041
|
+
const spawnDriverProcess = (driverPath, socketPath, effective, xvfb, systemOutputSink) => {
|
|
654
1042
|
const driverArgs = [
|
|
655
1043
|
"--socket",
|
|
656
1044
|
socketPath,
|
|
@@ -683,14 +1071,46 @@ const spawnDriverProcess = (driverPath, socketPath, effective, xvfb) => {
|
|
|
683
1071
|
env,
|
|
684
1072
|
stdio: ["ignore", "pipe", "pipe"]
|
|
685
1073
|
});
|
|
1074
|
+
const processState = {
|
|
1075
|
+
child,
|
|
1076
|
+
commandLine: [command.bin, ...command.args].join(" "),
|
|
1077
|
+
stderr,
|
|
1078
|
+
stdout,
|
|
1079
|
+
systemOutputSink
|
|
1080
|
+
};
|
|
686
1081
|
child.stdout.on("data", (chunk) => {
|
|
687
1082
|
appendOutput$1(stdout, chunk);
|
|
1083
|
+
appendSystemOutput(
|
|
1084
|
+
processState.systemOutputSink,
|
|
1085
|
+
"launcher-driver",
|
|
1086
|
+
"stdout",
|
|
1087
|
+
chunk
|
|
1088
|
+
);
|
|
688
1089
|
});
|
|
689
1090
|
child.stderr.on("data", (chunk) => {
|
|
690
1091
|
appendOutput$1(stderr, chunk);
|
|
1092
|
+
appendSystemOutput(
|
|
1093
|
+
processState.systemOutputSink,
|
|
1094
|
+
"launcher-driver",
|
|
1095
|
+
"stderr",
|
|
1096
|
+
chunk
|
|
1097
|
+
);
|
|
1098
|
+
});
|
|
1099
|
+
child.stdout.once("end", () => {
|
|
1100
|
+
flushSystemOutput(
|
|
1101
|
+
processState.systemOutputSink,
|
|
1102
|
+
"launcher-driver",
|
|
1103
|
+
"stdout"
|
|
1104
|
+
);
|
|
1105
|
+
});
|
|
1106
|
+
child.stderr.once("end", () => {
|
|
1107
|
+
flushSystemOutput(
|
|
1108
|
+
processState.systemOutputSink,
|
|
1109
|
+
"launcher-driver",
|
|
1110
|
+
"stderr"
|
|
1111
|
+
);
|
|
691
1112
|
});
|
|
692
|
-
|
|
693
|
-
return { child, commandLine, stderr, stdout };
|
|
1113
|
+
return processState;
|
|
694
1114
|
};
|
|
695
1115
|
const listenOnSocket = (server, socketPath) => new Promise((resolveListen, rejectListen) => {
|
|
696
1116
|
const rejectFromError = (error) => {
|
|
@@ -769,7 +1189,13 @@ const waitForDriverReady = (server, processState) => new Promise((resolveReady,
|
|
|
769
1189
|
const line = input.slice(0, newlineIndex);
|
|
770
1190
|
input = input.slice(newlineIndex + 1);
|
|
771
1191
|
try {
|
|
772
|
-
|
|
1192
|
+
const message = JSON.parse(line);
|
|
1193
|
+
if (isDriverEventMessage(message)) {
|
|
1194
|
+
const event = message;
|
|
1195
|
+
if (event.channel === "system.output") {
|
|
1196
|
+
routeWireSystemOutput(processState.systemOutputSink, event.value);
|
|
1197
|
+
}
|
|
1198
|
+
} else if (isRecord(message) && message.type === "ready" && parseReadyMessage(line) !== void 0) {
|
|
773
1199
|
if (settled) {
|
|
774
1200
|
return;
|
|
775
1201
|
}
|
|
@@ -847,6 +1273,7 @@ const decodeCapture = (capture) => ({
|
|
|
847
1273
|
visibleBounds: capture.visibleBounds
|
|
848
1274
|
});
|
|
849
1275
|
const createDriverSession = (socket, bufferedInput, processState, tempDirectory, poolOptions) => {
|
|
1276
|
+
const eventHandlers = /* @__PURE__ */ new Map();
|
|
850
1277
|
const pending = /* @__PURE__ */ new Map();
|
|
851
1278
|
let input = bufferedInput;
|
|
852
1279
|
let nextRequestId = 1;
|
|
@@ -866,8 +1293,34 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
866
1293
|
closed = true;
|
|
867
1294
|
rejectPending(createGtkAppExitedError("Launcher driver has exited."));
|
|
868
1295
|
};
|
|
1296
|
+
const handleEvent = (event) => {
|
|
1297
|
+
if (event.channel === "system.output") {
|
|
1298
|
+
routeWireSystemOutput(processState.systemOutputSink, event.value);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const handlers = eventHandlers.get(
|
|
1302
|
+
createDriverEventKey(event.channel, event.scopeId)
|
|
1303
|
+
);
|
|
1304
|
+
if (handlers === void 0) {
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
for (const handler of handlers) {
|
|
1308
|
+
try {
|
|
1309
|
+
handler(event.value);
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
queueMicrotask(() => {
|
|
1312
|
+
throw error;
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
869
1317
|
const handleResponse = (line) => {
|
|
870
|
-
const
|
|
1318
|
+
const message = JSON.parse(line);
|
|
1319
|
+
if (isDriverEventMessage(message)) {
|
|
1320
|
+
handleEvent(message);
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
const response = message;
|
|
871
1324
|
const entry = pending.get(response.id);
|
|
872
1325
|
if (entry === void 0) {
|
|
873
1326
|
return;
|
|
@@ -942,6 +1395,28 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
942
1395
|
});
|
|
943
1396
|
});
|
|
944
1397
|
};
|
|
1398
|
+
const subscribe = (channel, scopeId, handler) => {
|
|
1399
|
+
const key = createDriverEventKey(channel, scopeId);
|
|
1400
|
+
const handlers = eventHandlers.get(key) ?? /* @__PURE__ */ new Set();
|
|
1401
|
+
handlers.add(handler);
|
|
1402
|
+
eventHandlers.set(key, handlers);
|
|
1403
|
+
let disposed = false;
|
|
1404
|
+
return {
|
|
1405
|
+
dispose: () => {
|
|
1406
|
+
if (disposed) {
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
disposed = true;
|
|
1410
|
+
handlers.delete(handler);
|
|
1411
|
+
if (handlers.size === 0) {
|
|
1412
|
+
eventHandlers.delete(key);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
};
|
|
1417
|
+
const setSystemOutputSink = (sink) => {
|
|
1418
|
+
processState.systemOutputSink = sink;
|
|
1419
|
+
};
|
|
945
1420
|
const waitForExit = async () => {
|
|
946
1421
|
const startedAt = Date.now();
|
|
947
1422
|
while (processState.child.exitCode === null && processState.child.signalCode === null) {
|
|
@@ -1026,10 +1501,10 @@ const createDriverSession = (socket, bufferedInput, processState, tempDirectory,
|
|
|
1026
1501
|
poolOptions.limits
|
|
1027
1502
|
);
|
|
1028
1503
|
};
|
|
1029
|
-
session = { release, request, terminate };
|
|
1504
|
+
session = { release, request, setSystemOutputSink, subscribe, terminate };
|
|
1030
1505
|
return session;
|
|
1031
1506
|
};
|
|
1032
|
-
const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
1507
|
+
const startFreshDriverSession = async (effective, xvfb, poolOptions, systemOutputSink) => {
|
|
1033
1508
|
const driverPath = resolveDriverPath();
|
|
1034
1509
|
const tempDirectory = mkdtempSync(
|
|
1035
1510
|
join(tmpdir(), `gestament-launcher-${process.pid}-${socketCounter}-`)
|
|
@@ -1041,7 +1516,13 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
1041
1516
|
try {
|
|
1042
1517
|
await listenOnSocket(server, socketPath);
|
|
1043
1518
|
server.unref();
|
|
1044
|
-
processState = spawnDriverProcess(
|
|
1519
|
+
processState = spawnDriverProcess(
|
|
1520
|
+
driverPath,
|
|
1521
|
+
socketPath,
|
|
1522
|
+
effective,
|
|
1523
|
+
xvfb,
|
|
1524
|
+
systemOutputSink
|
|
1525
|
+
);
|
|
1045
1526
|
const connection = await waitForDriverReady(server, processState);
|
|
1046
1527
|
server.close();
|
|
1047
1528
|
const resolvedPoolOptions = poolOptions.mode === "all" && xvfb !== void 0 ? {
|
|
@@ -1067,7 +1548,7 @@ const startFreshDriverSession = async (effective, xvfb, poolOptions) => {
|
|
|
1067
1548
|
throw error;
|
|
1068
1549
|
}
|
|
1069
1550
|
};
|
|
1070
|
-
const startDriverSession = async (options) => {
|
|
1551
|
+
const startDriverSession = async (options, systemOutputSink) => {
|
|
1071
1552
|
const display = resolveDisplay(options.display);
|
|
1072
1553
|
const xvfbOptions = resolveXvfbOptions(options);
|
|
1073
1554
|
const effective = resolveEffectiveDisplay(display, xvfbOptions);
|
|
@@ -1075,7 +1556,8 @@ const startDriverSession = async (options) => {
|
|
|
1075
1556
|
return startFreshDriverSession(
|
|
1076
1557
|
effective,
|
|
1077
1558
|
void 0,
|
|
1078
|
-
emptyDriverSessionPoolOptions()
|
|
1559
|
+
emptyDriverSessionPoolOptions(),
|
|
1560
|
+
systemOutputSink
|
|
1079
1561
|
);
|
|
1080
1562
|
}
|
|
1081
1563
|
const pool = effective.xvfb.pool;
|
|
@@ -1083,18 +1565,24 @@ const startDriverSession = async (options) => {
|
|
|
1083
1565
|
return startFreshDriverSession(
|
|
1084
1566
|
effective,
|
|
1085
1567
|
void 0,
|
|
1086
|
-
emptyDriverSessionPoolOptions()
|
|
1568
|
+
emptyDriverSessionPoolOptions(),
|
|
1569
|
+
systemOutputSink
|
|
1087
1570
|
);
|
|
1088
1571
|
}
|
|
1089
1572
|
if (pool.type === "xvfb") {
|
|
1090
|
-
const xvfb2 = await leaseXvfb(effective.xvfb.screen);
|
|
1091
|
-
return startFreshDriverSession(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1573
|
+
const xvfb2 = await leaseXvfb(effective.xvfb.screen, systemOutputSink);
|
|
1574
|
+
return startFreshDriverSession(
|
|
1575
|
+
effective,
|
|
1576
|
+
xvfb2,
|
|
1577
|
+
{
|
|
1578
|
+
allKey: void 0,
|
|
1579
|
+
allowedMappedWindowCount: 0,
|
|
1580
|
+
limits: poolLimits(pool),
|
|
1581
|
+
mode: "xvfb",
|
|
1582
|
+
xvfb: xvfb2
|
|
1583
|
+
},
|
|
1584
|
+
systemOutputSink
|
|
1585
|
+
);
|
|
1098
1586
|
}
|
|
1099
1587
|
const key = allPoolKey(effective.xvfb);
|
|
1100
1588
|
for (; ; ) {
|
|
@@ -1105,20 +1593,27 @@ const startDriverSession = async (options) => {
|
|
|
1105
1593
|
if (idle.xvfb.child.exitCode === null && idle.xvfb.child.signalCode === null) {
|
|
1106
1594
|
idle.lastUsedAt = Date.now();
|
|
1107
1595
|
idle.xvfb.lastUsedAt = idle.lastUsedAt;
|
|
1596
|
+
idle.xvfb.systemOutputSink = systemOutputSink;
|
|
1597
|
+
idle.session.setSystemOutputSink(systemOutputSink);
|
|
1108
1598
|
return idle.session;
|
|
1109
1599
|
}
|
|
1110
1600
|
await idle.session.terminate().catch(() => void 0);
|
|
1111
1601
|
}
|
|
1112
|
-
const xvfb = await leaseXvfb(effective.xvfb.screen);
|
|
1113
|
-
return startFreshDriverSession(
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1602
|
+
const xvfb = await leaseXvfb(effective.xvfb.screen, systemOutputSink);
|
|
1603
|
+
return startFreshDriverSession(
|
|
1604
|
+
effective,
|
|
1605
|
+
xvfb,
|
|
1606
|
+
{
|
|
1607
|
+
allKey: key,
|
|
1608
|
+
allowedMappedWindowCount: 0,
|
|
1609
|
+
limits: poolLimits(pool),
|
|
1610
|
+
mode: "all",
|
|
1611
|
+
xvfb
|
|
1612
|
+
},
|
|
1613
|
+
systemOutputSink
|
|
1614
|
+
);
|
|
1120
1615
|
};
|
|
1121
|
-
const createLaunchPayload = (options, args) => {
|
|
1616
|
+
const createLaunchPayload = (options, args, launchOptions, outputScopeId) => {
|
|
1122
1617
|
const display = resolveDisplay(options.display);
|
|
1123
1618
|
const xvfb = resolveXvfbOptions(options);
|
|
1124
1619
|
const effective = resolveEffectiveDisplay(display, xvfb);
|
|
@@ -1126,6 +1621,11 @@ const createLaunchPayload = (options, args) => {
|
|
|
1126
1621
|
appPath: options.appPath,
|
|
1127
1622
|
args: [...options.args ?? [], ...args],
|
|
1128
1623
|
env: resolveLauncherEnvironment(options, effective),
|
|
1624
|
+
outputBufferBytes: resolveOutputBufferBytes(
|
|
1625
|
+
options.outputBufferBytes,
|
|
1626
|
+
launchOptions?.outputBufferBytes
|
|
1627
|
+
),
|
|
1628
|
+
outputScopeId,
|
|
1129
1629
|
timeoutMs: options.timeoutMs ?? null
|
|
1130
1630
|
};
|
|
1131
1631
|
};
|
|
@@ -1139,7 +1639,7 @@ const createEnvironmentPayload = (options) => {
|
|
|
1139
1639
|
};
|
|
1140
1640
|
const elementRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkElement(session, ref);
|
|
1141
1641
|
const trayRefToProxy = (session, ref) => ref === null ? void 0 : createProxyGtkTrayItem(session, ref);
|
|
1142
|
-
const createProxyGtkApp = (session, ref) => {
|
|
1642
|
+
const createProxyGtkApp = (session, ref, outputSubscription) => {
|
|
1143
1643
|
let released = false;
|
|
1144
1644
|
const assertNotReleased = () => {
|
|
1145
1645
|
if (released) {
|
|
@@ -1158,6 +1658,7 @@ const createProxyGtkApp = (session, ref) => {
|
|
|
1158
1658
|
return;
|
|
1159
1659
|
}
|
|
1160
1660
|
released = true;
|
|
1661
|
+
outputSubscription?.dispose();
|
|
1161
1662
|
await session.request("app.release", { appId: ref.appId });
|
|
1162
1663
|
};
|
|
1163
1664
|
const app = {
|
|
@@ -1165,6 +1666,7 @@ const createProxyGtkApp = (session, ref) => {
|
|
|
1165
1666
|
environment: async () => wireEnvironmentToGtkAppEnvironment(
|
|
1166
1667
|
await appRequest("app.environment")
|
|
1167
1668
|
),
|
|
1669
|
+
output: () => appRequest("app.output"),
|
|
1168
1670
|
findById: async (id) => elementRefToProxy(
|
|
1169
1671
|
session,
|
|
1170
1672
|
await appRequest("app.findById", { id })
|
|
@@ -1392,19 +1894,72 @@ const createProxyGtkTrayItem = (session, ref) => {
|
|
|
1392
1894
|
};
|
|
1393
1895
|
};
|
|
1394
1896
|
const createDriverBackedGtkAppLauncher = (options) => {
|
|
1897
|
+
const outputSubscriptions = /* @__PURE__ */ new Set();
|
|
1898
|
+
let systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1899
|
+
normalizeOutputBufferBytes(
|
|
1900
|
+
options.systemOutputBufferBytes,
|
|
1901
|
+
"systemOutputBufferBytes"
|
|
1902
|
+
)
|
|
1903
|
+
);
|
|
1395
1904
|
let sessionPromise;
|
|
1396
1905
|
const ensureSession = () => {
|
|
1397
|
-
sessionPromise
|
|
1906
|
+
if (sessionPromise === void 0) {
|
|
1907
|
+
systemOutputRecorder = createGtkSystemOutputRecorder(
|
|
1908
|
+
normalizeOutputBufferBytes(
|
|
1909
|
+
options.systemOutputBufferBytes,
|
|
1910
|
+
"systemOutputBufferBytes"
|
|
1911
|
+
)
|
|
1912
|
+
);
|
|
1913
|
+
sessionPromise = startDriverSession(
|
|
1914
|
+
options,
|
|
1915
|
+
createSystemOutputSink(systemOutputRecorder, options.onSystemOutput)
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1398
1918
|
return sessionPromise;
|
|
1399
1919
|
};
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1920
|
+
const trackOutputSubscription = (subscription) => {
|
|
1921
|
+
let tracked;
|
|
1922
|
+
tracked = {
|
|
1923
|
+
dispose: () => {
|
|
1924
|
+
if (!outputSubscriptions.delete(tracked)) {
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
subscription.dispose();
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
outputSubscriptions.add(tracked);
|
|
1931
|
+
return tracked;
|
|
1932
|
+
};
|
|
1933
|
+
const disposeOutputSubscriptions = () => {
|
|
1934
|
+
for (const subscription of [...outputSubscriptions]) {
|
|
1935
|
+
subscription.dispose();
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
const launch = async (args, launchOptions) => {
|
|
1402
1939
|
const session = await ensureSession();
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1940
|
+
const onOutput = launchOptions?.onOutput;
|
|
1941
|
+
const outputScopeId = onOutput === void 0 ? null : createOutputScopeId();
|
|
1942
|
+
const outputSubscription = outputScopeId === null ? void 0 : trackOutputSubscription(
|
|
1943
|
+
session.subscribe("app.output", outputScopeId, (value) => {
|
|
1944
|
+
onOutput?.(value);
|
|
1945
|
+
})
|
|
1946
|
+
);
|
|
1947
|
+
const payload = createLaunchPayload(
|
|
1948
|
+
options,
|
|
1949
|
+
args ?? [],
|
|
1950
|
+
launchOptions,
|
|
1951
|
+
outputScopeId
|
|
1406
1952
|
);
|
|
1407
|
-
|
|
1953
|
+
try {
|
|
1954
|
+
const appRef = await session.request(
|
|
1955
|
+
"launcher.launch",
|
|
1956
|
+
payload
|
|
1957
|
+
);
|
|
1958
|
+
return createProxyGtkApp(session, appRef, outputSubscription);
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
outputSubscription?.dispose();
|
|
1961
|
+
throw error;
|
|
1962
|
+
}
|
|
1408
1963
|
};
|
|
1409
1964
|
const environment = async () => {
|
|
1410
1965
|
const payload = createEnvironmentPayload(options);
|
|
@@ -1416,6 +1971,7 @@ const createDriverBackedGtkAppLauncher = (options) => {
|
|
|
1416
1971
|
)
|
|
1417
1972
|
);
|
|
1418
1973
|
};
|
|
1974
|
+
const systemOutput = () => Promise.resolve(systemOutputRecorder.snapshot());
|
|
1419
1975
|
const release = async () => {
|
|
1420
1976
|
const releasingSession = sessionPromise;
|
|
1421
1977
|
sessionPromise = void 0;
|
|
@@ -1423,12 +1979,17 @@ const createDriverBackedGtkAppLauncher = (options) => {
|
|
|
1423
1979
|
return;
|
|
1424
1980
|
}
|
|
1425
1981
|
const session = await releasingSession;
|
|
1426
|
-
|
|
1982
|
+
try {
|
|
1983
|
+
await session.release();
|
|
1984
|
+
} finally {
|
|
1985
|
+
disposeOutputSubscriptions();
|
|
1986
|
+
}
|
|
1427
1987
|
};
|
|
1428
1988
|
return {
|
|
1429
1989
|
environment,
|
|
1430
1990
|
launch,
|
|
1431
1991
|
release,
|
|
1992
|
+
systemOutput,
|
|
1432
1993
|
[Symbol.asyncDispose]: release
|
|
1433
1994
|
};
|
|
1434
1995
|
};
|
|
@@ -2340,31 +2901,70 @@ const createGtkAppEnvironment = (baseEnv, overrides) => {
|
|
|
2340
2901
|
const launchGtkApp = (appPath, args, options) => {
|
|
2341
2902
|
const _args = args ?? [];
|
|
2342
2903
|
const _timeoutMs = options?.timeoutMs ?? 1e4;
|
|
2904
|
+
const outputBufferBytes = normalizeOutputBufferBytes(
|
|
2905
|
+
options?.outputBufferBytes
|
|
2906
|
+
);
|
|
2907
|
+
const outputRecorder = createGtkAppOutputRecorder(outputBufferBytes);
|
|
2343
2908
|
const appEnvironment = createGtkAppEnvironment(process.env, options?.env);
|
|
2344
2909
|
const child = spawn(appPath, [..._args], {
|
|
2345
2910
|
env: appEnvironment,
|
|
2346
2911
|
stdio: "pipe"
|
|
2347
2912
|
});
|
|
2913
|
+
let resolveClosed;
|
|
2914
|
+
const closedPromise = new Promise((resolve2) => {
|
|
2915
|
+
resolveClosed = resolve2;
|
|
2916
|
+
});
|
|
2348
2917
|
const state = {
|
|
2349
2918
|
atspiReadiness: "missing-bus-name",
|
|
2350
2919
|
atspiReady: false,
|
|
2920
|
+
closed: false,
|
|
2921
|
+
closedPromise,
|
|
2351
2922
|
exitCode: null,
|
|
2352
2923
|
exitSignal: null,
|
|
2353
2924
|
process: child,
|
|
2925
|
+
released: false,
|
|
2354
2926
|
stderr: [],
|
|
2355
2927
|
stdout: []
|
|
2356
2928
|
};
|
|
2929
|
+
const notifyOutput = (stream, chunk) => {
|
|
2930
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.append(stream, chunk));
|
|
2931
|
+
};
|
|
2932
|
+
const flushOutput = (stream) => {
|
|
2933
|
+
notifyGtkAppOutput(options?.onOutput, outputRecorder.flush(stream));
|
|
2934
|
+
};
|
|
2357
2935
|
child.stdout.on("data", (chunk) => {
|
|
2358
2936
|
appendOutput(state.stdout, chunk);
|
|
2937
|
+
notifyOutput("stdout", chunk);
|
|
2359
2938
|
});
|
|
2360
2939
|
child.stderr.on("data", (chunk) => {
|
|
2361
2940
|
appendOutput(state.stderr, chunk);
|
|
2941
|
+
notifyOutput("stderr", chunk);
|
|
2942
|
+
});
|
|
2943
|
+
child.stdout.on("end", () => {
|
|
2944
|
+
flushOutput("stdout");
|
|
2945
|
+
});
|
|
2946
|
+
child.stderr.on("end", () => {
|
|
2947
|
+
flushOutput("stderr");
|
|
2362
2948
|
});
|
|
2363
2949
|
child.on("exit", (code, signal) => {
|
|
2364
2950
|
state.exitCode = code;
|
|
2365
2951
|
state.exitSignal = signal;
|
|
2366
2952
|
});
|
|
2953
|
+
child.on("close", (code, signal) => {
|
|
2954
|
+
if (state.exitCode === null && state.exitSignal === null) {
|
|
2955
|
+
state.exitCode = code;
|
|
2956
|
+
state.exitSignal = signal;
|
|
2957
|
+
}
|
|
2958
|
+
flushOutput("stdout");
|
|
2959
|
+
flushOutput("stderr");
|
|
2960
|
+
state.closed = true;
|
|
2961
|
+
resolveClosed?.();
|
|
2962
|
+
});
|
|
2367
2963
|
const release = async () => {
|
|
2964
|
+
if (state.released) {
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
state.released = true;
|
|
2368
2968
|
if (state.exitCode !== null || state.exitSignal !== null) {
|
|
2369
2969
|
return;
|
|
2370
2970
|
}
|
|
@@ -2378,6 +2978,11 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2378
2978
|
await delay$1(25);
|
|
2379
2979
|
}
|
|
2380
2980
|
};
|
|
2981
|
+
const assertNotReleased = () => {
|
|
2982
|
+
if (state.released) {
|
|
2983
|
+
throw createGtkAppExitedError("GTK application has been released.");
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2381
2986
|
const findById = async (id) => {
|
|
2382
2987
|
const startedAt = Date.now();
|
|
2383
2988
|
const timeoutMs = effectiveWaitTimeoutMs(_timeoutMs);
|
|
@@ -2485,6 +3090,7 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2485
3090
|
};
|
|
2486
3091
|
const app = {
|
|
2487
3092
|
capture: async () => {
|
|
3093
|
+
assertNotReleased();
|
|
2488
3094
|
assertProcessRunning(state, appPath);
|
|
2489
3095
|
try {
|
|
2490
3096
|
return nativeCaptureScreen();
|
|
@@ -2493,9 +3099,20 @@ const launchGtkApp = (appPath, args, options) => {
|
|
|
2493
3099
|
}
|
|
2494
3100
|
},
|
|
2495
3101
|
environment: async () => {
|
|
3102
|
+
assertNotReleased();
|
|
2496
3103
|
assertProcessRunning(state, appPath);
|
|
2497
3104
|
return { ...appEnvironment };
|
|
2498
3105
|
},
|
|
3106
|
+
output: async () => {
|
|
3107
|
+
assertNotReleased();
|
|
3108
|
+
if ((state.exitCode !== null || state.exitSignal !== null) && !state.closed) {
|
|
3109
|
+
await state.closedPromise;
|
|
3110
|
+
}
|
|
3111
|
+
return outputRecorder.snapshot(
|
|
3112
|
+
state.exitCode,
|
|
3113
|
+
state.exitSignal === null ? null : state.exitSignal
|
|
3114
|
+
);
|
|
3115
|
+
},
|
|
2499
3116
|
findById,
|
|
2500
3117
|
findByPath,
|
|
2501
3118
|
getById,
|
|
@@ -2564,4 +3181,4 @@ export {
|
|
|
2564
3181
|
createGtkAppEnvironment as c,
|
|
2565
3182
|
launchGtkApp as l
|
|
2566
3183
|
};
|
|
2567
|
-
//# sourceMappingURL=launchGtkApp-
|
|
3184
|
+
//# sourceMappingURL=launchGtkApp-EI6OIpPI.js.map
|