@serverkgg/bridge 0.0.7 → 0.0.9
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/package.json +4 -3
- package/src/cli/validate.ts +1 -0
- package/src/context/bridgeContext.ts +10 -0
- package/src/context/index.ts +1 -0
- package/src/core/bridgeDownloadError.ts +10 -0
- package/src/core/bridgeHold.ts +12 -0
- package/src/core/bridgeKind.ts +2 -0
- package/src/core/bridgeUserError.test.ts +23 -0
- package/src/core/bridgeUserError.ts +12 -0
- package/src/core/index.ts +3 -0
- package/src/manifest/index.ts +1 -0
- package/src/manifest/manifest.test.ts +85 -0
- package/src/manifest/manifest.ts +21 -0
- package/src/manifest/presence.ts +48 -0
- package/src/manifest/primitives.ts +6 -0
- package/src/modules/bridge.ts +45 -2
- package/src/modules/bridgeDriver.ts +2 -0
- package/src/modules/bridgeModule.ts +25 -3
- package/src/panel/bridgeDetail.ts +49 -0
- package/src/panel/bridgeLayout.ts +1 -0
- package/src/panel/bridgePanel.ts +30 -0
- package/src/panel/bridgeUploadMode.ts +4 -0
- package/src/panel/index.ts +2 -0
- package/src/protocol/bridgeEgress.ts +8 -1
- package/src/protocol/bridgeEvents.ts +4 -0
- package/src/protocol/bridgeFrame.ts +28 -1
- package/src/protocol/bridgeLimits.ts +10 -0
- package/src/runtime/bridgeActions.ts +7 -0
- package/src/runtime/bridgeDriver.test.ts +477 -2
- package/src/runtime/bridgeDriver.ts +116 -3
- package/src/runtime/bridgeEgress.ts +3 -0
- package/src/runtime/bridgeEvents.ts +7 -5
- package/src/runtime/bridgeFiles.test.ts +625 -15
- package/src/runtime/bridgeFiles.ts +152 -45
- package/src/runtime/bridgeInstall.test.ts +118 -0
- package/src/runtime/bridgeInstall.ts +55 -0
- package/src/runtime/bridgeNet.ts +32 -3
- package/src/runtime/bridgePending.test.ts +148 -0
- package/src/runtime/bridgePending.ts +43 -0
- package/src/runtime/bridgePrivilege.test.ts +77 -0
- package/src/runtime/bridgePrivilege.ts +5 -3
- package/src/runtime/bridgeRegistry.test.ts +134 -0
- package/src/runtime/bridgeRegistry.ts +26 -4
- package/src/runtime/bridgeServer.test.ts +338 -0
- package/src/runtime/bridgeServer.ts +25 -3
- package/src/runtime/bridgeSupervisor.ts +18 -0
- package/src/runtime/bridgeZip.ts +179 -58
- package/src/runtime/serve.ts +64 -4
- package/src/wire/index.ts +2 -0
- package/src/wire/panel.test.ts +451 -0
- package/src/wire/panel.ts +295 -0
- package/src/wire/zEnvKey.ts +7 -0
package/src/runtime/bridgeZip.ts
CHANGED
|
@@ -6,12 +6,26 @@ const CENTRAL_SIGNATURE = 0x02_01_4b_50;
|
|
|
6
6
|
|
|
7
7
|
const LOCAL_SIGNATURE = 0x04_03_4b_50;
|
|
8
8
|
|
|
9
|
+
const ZIP64_LOCATOR_SIGNATURE = 0x07_06_4b_50;
|
|
10
|
+
|
|
11
|
+
const ZIP64_EOCD_SIGNATURE = 0x06_06_4b_50;
|
|
12
|
+
|
|
9
13
|
const EOCD_MIN_LENGTH = 22;
|
|
10
14
|
|
|
11
15
|
const EOCD_SEARCH_LIMIT = 65_557;
|
|
12
16
|
|
|
17
|
+
const ZIP64_LOCATOR_LENGTH = 20;
|
|
18
|
+
|
|
19
|
+
const ZIP64_EOCD_MIN_LENGTH = 56;
|
|
20
|
+
|
|
21
|
+
const ZIP64_EXTRA_ID = 0x00_01;
|
|
22
|
+
|
|
23
|
+
const CENTRAL_HEADER_LENGTH = 46;
|
|
24
|
+
|
|
13
25
|
const LOCAL_HEADER_LENGTH = 30;
|
|
14
26
|
|
|
27
|
+
const SIZE_SENTINEL = 0xff_ff_ff_ff;
|
|
28
|
+
|
|
15
29
|
const STORED = 0;
|
|
16
30
|
|
|
17
31
|
const DEFLATED = 8;
|
|
@@ -20,7 +34,7 @@ const ENCRYPTED_FLAG = 0x1;
|
|
|
20
34
|
|
|
21
35
|
export const ZIP_ENTRY_LIMIT = 2048;
|
|
22
36
|
|
|
23
|
-
export const ZIP_TREE_ENTRY_LIMIT =
|
|
37
|
+
export const ZIP_TREE_ENTRY_LIMIT = 16_384;
|
|
24
38
|
|
|
25
39
|
const MAX_ENTRY_BYTES = 128 * 1024 * 1024;
|
|
26
40
|
|
|
@@ -30,14 +44,34 @@ const UNIX_MODE_SYMLINK = 0xa0_00;
|
|
|
30
44
|
|
|
31
45
|
export interface ZipEntry {
|
|
32
46
|
name: string;
|
|
33
|
-
data: Uint8Array;
|
|
34
47
|
symlink: boolean;
|
|
48
|
+
sizeBytes: number;
|
|
49
|
+
compressedSize: number;
|
|
50
|
+
method: number;
|
|
51
|
+
localOffset: number;
|
|
35
52
|
}
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
export interface ZipArchive {
|
|
55
|
+
entries: ZipEntry[];
|
|
56
|
+
read(entry: ZipEntry): Promise<Uint8Array>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const readRange = async (path: string, start: number, end: number) => {
|
|
60
|
+
return Buffer.from(await Bun.file(path).slice(start, end).arrayBuffer());
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const readOffset = (buffer: Buffer, offset: number) => {
|
|
64
|
+
const value = buffer.readBigUInt64LE(offset);
|
|
65
|
+
|
|
66
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
67
|
+
throw new Error("archive declares an offset this runtime cannot address");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return Number(value);
|
|
71
|
+
};
|
|
39
72
|
|
|
40
|
-
|
|
73
|
+
const findEndOfCentralDirectory = (buffer: Buffer) => {
|
|
74
|
+
for (let offset = buffer.length - EOCD_MIN_LENGTH; offset >= 0; offset--) {
|
|
41
75
|
if (buffer.readUInt32LE(offset) === EOCD_SIGNATURE) {
|
|
42
76
|
return offset;
|
|
43
77
|
}
|
|
@@ -46,88 +80,175 @@ const findEndOfCentralDirectory = (buffer: Buffer) => {
|
|
|
46
80
|
return -1;
|
|
47
81
|
};
|
|
48
82
|
|
|
49
|
-
|
|
50
|
-
|
|
83
|
+
interface CentralDirectory {
|
|
84
|
+
total: number;
|
|
85
|
+
offset: number;
|
|
86
|
+
sizeBytes: number;
|
|
87
|
+
}
|
|
51
88
|
|
|
52
|
-
|
|
53
|
-
|
|
89
|
+
const readZip64Directory = async (path: string, tail: Buffer, eocd: number): Promise<CentralDirectory | null> => {
|
|
90
|
+
const locator = eocd - ZIP64_LOCATOR_LENGTH;
|
|
91
|
+
|
|
92
|
+
if (locator < 0 || tail.readUInt32LE(locator) !== ZIP64_LOCATOR_SIGNATURE) {
|
|
93
|
+
return null;
|
|
54
94
|
}
|
|
55
95
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
96
|
+
const record = readOffset(tail, locator + 8);
|
|
97
|
+
const buffer = await readRange(path, record, record + ZIP64_EOCD_MIN_LENGTH);
|
|
58
98
|
|
|
59
|
-
if (
|
|
60
|
-
throw new Error(
|
|
99
|
+
if (buffer.length < ZIP64_EOCD_MIN_LENGTH || buffer.readUInt32LE(0) !== ZIP64_EOCD_SIGNATURE) {
|
|
100
|
+
throw new Error("archive has a corrupt zip64 central directory record");
|
|
61
101
|
}
|
|
62
102
|
|
|
63
|
-
|
|
103
|
+
return {
|
|
104
|
+
total: readOffset(buffer, 32),
|
|
105
|
+
sizeBytes: readOffset(buffer, 40),
|
|
106
|
+
offset: readOffset(buffer, 48),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
64
109
|
|
|
65
|
-
|
|
110
|
+
interface Zip64Extended {
|
|
111
|
+
sizeBytes: number | null;
|
|
112
|
+
compressedSize: number | null;
|
|
113
|
+
localOffset: number | null;
|
|
114
|
+
}
|
|
66
115
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
const readZip64Extra = (extra: Buffer, sizeBytes: number, compressedSize: number, localOffset: number) => {
|
|
117
|
+
const extended: Zip64Extended = {
|
|
118
|
+
sizeBytes: null,
|
|
119
|
+
compressedSize: null,
|
|
120
|
+
localOffset: null,
|
|
121
|
+
};
|
|
71
122
|
|
|
72
|
-
|
|
73
|
-
const method = buffer.readUInt16LE(cursor + 10);
|
|
74
|
-
const compressedSize = buffer.readUInt32LE(cursor + 20);
|
|
75
|
-
const uncompressedSize = buffer.readUInt32LE(cursor + 24);
|
|
76
|
-
const nameLength = buffer.readUInt16LE(cursor + 28);
|
|
77
|
-
const extraLength = buffer.readUInt16LE(cursor + 30);
|
|
78
|
-
const commentLength = buffer.readUInt16LE(cursor + 32);
|
|
79
|
-
const externalAttributes = buffer.readUInt32LE(cursor + 38);
|
|
80
|
-
const localOffset = buffer.readUInt32LE(cursor + 42);
|
|
81
|
-
const name = buffer.subarray(cursor + 46, cursor + 46 + nameLength).toString("utf8");
|
|
82
|
-
const symlink = ((externalAttributes >>> 16) & UNIX_MODE_FORMAT_MASK) === UNIX_MODE_SYMLINK;
|
|
123
|
+
let cursor = 0;
|
|
83
124
|
|
|
84
|
-
|
|
125
|
+
while (cursor + 4 <= extra.length) {
|
|
126
|
+
const id = extra.readUInt16LE(cursor);
|
|
127
|
+
const length = extra.readUInt16LE(cursor + 2);
|
|
128
|
+
const body = extra.subarray(cursor + 4, cursor + 4 + length);
|
|
85
129
|
|
|
86
|
-
|
|
130
|
+
cursor += 4 + length;
|
|
131
|
+
|
|
132
|
+
if (id !== ZIP64_EXTRA_ID) {
|
|
87
133
|
continue;
|
|
88
134
|
}
|
|
89
135
|
|
|
90
|
-
|
|
91
|
-
|
|
136
|
+
let field = 0;
|
|
137
|
+
|
|
138
|
+
if (sizeBytes === SIZE_SENTINEL && field + 8 <= body.length) {
|
|
139
|
+
extended.sizeBytes = readOffset(body, field);
|
|
140
|
+
field += 8;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (compressedSize === SIZE_SENTINEL && field + 8 <= body.length) {
|
|
144
|
+
extended.compressedSize = readOffset(body, field);
|
|
145
|
+
field += 8;
|
|
92
146
|
}
|
|
93
147
|
|
|
94
|
-
if (localOffset
|
|
95
|
-
|
|
148
|
+
if (localOffset === SIZE_SENTINEL && field + 8 <= body.length) {
|
|
149
|
+
extended.localOffset = readOffset(body, field);
|
|
96
150
|
}
|
|
97
151
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const dataOffset = localOffset + LOCAL_HEADER_LENGTH + localNameLength + localExtraLength;
|
|
101
|
-
const compressed = buffer.subarray(dataOffset, dataOffset + compressedSize);
|
|
152
|
+
return extended;
|
|
153
|
+
}
|
|
102
154
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
name,
|
|
106
|
-
data: compressed,
|
|
107
|
-
symlink,
|
|
108
|
-
});
|
|
155
|
+
return extended;
|
|
156
|
+
};
|
|
109
157
|
|
|
110
|
-
|
|
111
|
-
|
|
158
|
+
export const openZip = async (path: string, maxEntries: number = ZIP_ENTRY_LIMIT): Promise<ZipArchive> => {
|
|
159
|
+
const size = Bun.file(path).size;
|
|
160
|
+
const tailStart = Math.max(0, size - (EOCD_SEARCH_LIMIT + ZIP64_LOCATOR_LENGTH));
|
|
161
|
+
const tail = await readRange(path, tailStart, size);
|
|
162
|
+
const eocd = tail.length < EOCD_MIN_LENGTH ? -1 : findEndOfCentralDirectory(tail);
|
|
163
|
+
|
|
164
|
+
if (eocd === -1) {
|
|
165
|
+
throw new Error("archive has no zip central directory");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const directory = (await readZip64Directory(path, tail, eocd)) ?? {
|
|
169
|
+
total: tail.readUInt16LE(eocd + 10),
|
|
170
|
+
sizeBytes: tail.readUInt32LE(eocd + 12),
|
|
171
|
+
offset: tail.readUInt32LE(eocd + 16),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (directory.total > maxEntries) {
|
|
175
|
+
throw new Error(`archive has too many entries (${directory.total})`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const central = await readRange(path, directory.offset, directory.offset + directory.sizeBytes);
|
|
179
|
+
const entries: ZipEntry[] = [];
|
|
112
180
|
|
|
113
|
-
|
|
114
|
-
|
|
181
|
+
let cursor = 0;
|
|
182
|
+
|
|
183
|
+
for (let index = 0; index < directory.total; index++) {
|
|
184
|
+
if (cursor + CENTRAL_HEADER_LENGTH > central.length || central.readUInt32LE(cursor) !== CENTRAL_SIGNATURE) {
|
|
185
|
+
throw new Error("archive central directory is corrupt");
|
|
115
186
|
}
|
|
116
187
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
188
|
+
const flags = central.readUInt16LE(cursor + 8);
|
|
189
|
+
const method = central.readUInt16LE(cursor + 10);
|
|
190
|
+
const compressedSize = central.readUInt32LE(cursor + 20);
|
|
191
|
+
const sizeBytes = central.readUInt32LE(cursor + 24);
|
|
192
|
+
const nameLength = central.readUInt16LE(cursor + 28);
|
|
193
|
+
const extraLength = central.readUInt16LE(cursor + 30);
|
|
194
|
+
const commentLength = central.readUInt16LE(cursor + 32);
|
|
195
|
+
const externalAttributes = central.readUInt32LE(cursor + 38);
|
|
196
|
+
const localOffset = central.readUInt32LE(cursor + 42);
|
|
197
|
+
const nameStart = cursor + CENTRAL_HEADER_LENGTH;
|
|
198
|
+
const name = central.subarray(nameStart, nameStart + nameLength).toString("utf8");
|
|
199
|
+
const extra = central.subarray(nameStart + nameLength, nameStart + nameLength + extraLength);
|
|
200
|
+
const extended = readZip64Extra(extra, sizeBytes, compressedSize, localOffset);
|
|
201
|
+
|
|
202
|
+
cursor = nameStart + nameLength + extraLength + commentLength;
|
|
120
203
|
|
|
121
|
-
if (
|
|
122
|
-
|
|
204
|
+
if (name.endsWith("/") || (flags & ENCRYPTED_FLAG) !== 0) {
|
|
205
|
+
continue;
|
|
123
206
|
}
|
|
124
207
|
|
|
125
208
|
entries.push({
|
|
126
209
|
name,
|
|
127
|
-
|
|
128
|
-
|
|
210
|
+
symlink: ((externalAttributes >>> 16) & UNIX_MODE_FORMAT_MASK) === UNIX_MODE_SYMLINK,
|
|
211
|
+
sizeBytes: extended.sizeBytes ?? sizeBytes,
|
|
212
|
+
compressedSize: extended.compressedSize ?? compressedSize,
|
|
213
|
+
method,
|
|
214
|
+
localOffset: extended.localOffset ?? localOffset,
|
|
129
215
|
});
|
|
130
216
|
}
|
|
131
217
|
|
|
132
|
-
return
|
|
218
|
+
return {
|
|
219
|
+
entries,
|
|
220
|
+
|
|
221
|
+
async read(entry) {
|
|
222
|
+
if (entry.sizeBytes > MAX_ENTRY_BYTES || entry.compressedSize > MAX_ENTRY_BYTES) {
|
|
223
|
+
throw new Error(`archive entry "${entry.name}" is too large`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const header = await readRange(path, entry.localOffset, entry.localOffset + LOCAL_HEADER_LENGTH);
|
|
227
|
+
|
|
228
|
+
if (header.length < LOCAL_HEADER_LENGTH || header.readUInt32LE(0) !== LOCAL_SIGNATURE) {
|
|
229
|
+
throw new Error(`archive entry "${entry.name}" has a corrupt local header`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const dataOffset = entry.localOffset + LOCAL_HEADER_LENGTH + header.readUInt16LE(26) + header.readUInt16LE(28);
|
|
233
|
+
const compressed = await readRange(path, dataOffset, dataOffset + entry.compressedSize);
|
|
234
|
+
|
|
235
|
+
if (entry.method === STORED) {
|
|
236
|
+
return compressed;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (entry.method !== DEFLATED) {
|
|
240
|
+
throw new Error(`archive entry "${entry.name}" uses unsupported compression ${entry.method}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const data = inflateRawSync(compressed, {
|
|
244
|
+
maxOutputLength: MAX_ENTRY_BYTES,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (data.length !== entry.sizeBytes) {
|
|
248
|
+
throw new Error(`archive entry "${entry.name}" did not inflate to its declared size`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return data;
|
|
252
|
+
},
|
|
253
|
+
};
|
|
133
254
|
};
|
package/src/runtime/serve.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { chmod, chown, mkdir, rm } from "node:fs/promises";
|
|
2
|
-
import { BridgeDesired, BridgeRuntime } from "../core";
|
|
2
|
+
import { BridgeDesired, type BridgeInstallHold, BridgeRuntime, BridgeUserError } from "../core";
|
|
3
3
|
import type { BridgeDriver } from "../modules";
|
|
4
4
|
import {
|
|
5
5
|
BRIDGE_INSTALL_ERROR_LIMIT,
|
|
6
6
|
BRIDGE_VERSION,
|
|
7
7
|
type BridgeConfig,
|
|
8
|
+
type BridgePendingResult,
|
|
8
9
|
type BridgeSampleResult,
|
|
9
10
|
type BridgeStatus,
|
|
10
11
|
} from "../protocol";
|
|
@@ -12,7 +13,9 @@ import { configPath, readConfig, socketPath, statePath } from "./bridgeConfigRea
|
|
|
12
13
|
import { createContext } from "./bridgeContext";
|
|
13
14
|
import { validateDriver } from "./bridgeDriver";
|
|
14
15
|
import { createEventRing } from "./bridgeEvents";
|
|
16
|
+
import { decideInstall, recountHold } from "./bridgeInstall";
|
|
15
17
|
import { errorText, log } from "./bridgeLog";
|
|
18
|
+
import { pendingResult, rejectedPending, unsupportedPending } from "./bridgePending";
|
|
16
19
|
import { createRegistry } from "./bridgeRegistry";
|
|
17
20
|
import { createServer } from "./bridgeServer";
|
|
18
21
|
import { readDesired, writeDesired } from "./bridgeState";
|
|
@@ -64,6 +67,8 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
64
67
|
|
|
65
68
|
let installError: string | null = null;
|
|
66
69
|
|
|
70
|
+
let installHold: BridgeInstallHold | null = null;
|
|
71
|
+
|
|
67
72
|
const supervisor = createSupervisor({
|
|
68
73
|
cwd: config.cwd,
|
|
69
74
|
uid: config.gameUid,
|
|
@@ -105,8 +110,10 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
105
110
|
runtime: supervisor.runtime,
|
|
106
111
|
desired: current,
|
|
107
112
|
exitCode: supervisor.exitCode,
|
|
113
|
+
outOfMemory: supervisor.outOfMemory,
|
|
108
114
|
restarts: supervisor.restarts,
|
|
109
115
|
installError,
|
|
116
|
+
installHold,
|
|
110
117
|
};
|
|
111
118
|
};
|
|
112
119
|
|
|
@@ -152,6 +159,20 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
152
159
|
return value;
|
|
153
160
|
};
|
|
154
161
|
|
|
162
|
+
const pending = async (): Promise<BridgePendingResult> => {
|
|
163
|
+
const module = driver.pending;
|
|
164
|
+
|
|
165
|
+
if (!module) {
|
|
166
|
+
return unsupportedPending();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const files = await module.list(context);
|
|
170
|
+
|
|
171
|
+
installHold = recountHold(installHold, files.length);
|
|
172
|
+
|
|
173
|
+
return pendingResult(module.staging, module.extensions, files, log);
|
|
174
|
+
};
|
|
175
|
+
|
|
155
176
|
const server = createServer({
|
|
156
177
|
socket: socketPath(config),
|
|
157
178
|
token: config.token,
|
|
@@ -260,6 +281,32 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
260
281
|
delivered: true,
|
|
261
282
|
};
|
|
262
283
|
},
|
|
284
|
+
async onPendingList() {
|
|
285
|
+
return await pending();
|
|
286
|
+
},
|
|
287
|
+
async onPendingAdd(input) {
|
|
288
|
+
const module = driver.pending;
|
|
289
|
+
|
|
290
|
+
if (!module) {
|
|
291
|
+
return unsupportedPending();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
await module.add(context, input);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (!(error instanceof BridgeUserError)) {
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
log.warn("pending upload rejected", {
|
|
302
|
+
error: error.message,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return rejectedPending(await pending());
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return await pending();
|
|
309
|
+
},
|
|
263
310
|
});
|
|
264
311
|
|
|
265
312
|
await chmod(socketPath(config), SOCKET_MODE);
|
|
@@ -303,8 +350,10 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
303
350
|
|
|
304
351
|
log("installing");
|
|
305
352
|
|
|
353
|
+
let decision = decideInstall(undefined, current);
|
|
354
|
+
|
|
306
355
|
try {
|
|
307
|
-
await driver.install.run(context);
|
|
356
|
+
decision = decideInstall(await driver.install.run(context), current);
|
|
308
357
|
|
|
309
358
|
log("install complete");
|
|
310
359
|
} catch (error) {
|
|
@@ -319,9 +368,20 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
319
368
|
return;
|
|
320
369
|
}
|
|
321
370
|
|
|
322
|
-
|
|
371
|
+
installHold = decision.hold;
|
|
372
|
+
|
|
373
|
+
supervisor.setRuntime(decision.runtime);
|
|
374
|
+
|
|
375
|
+
if (installHold) {
|
|
376
|
+
log("install held", {
|
|
377
|
+
reason: installHold.reason,
|
|
378
|
+
count: installHold.count,
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
323
383
|
|
|
324
|
-
if (
|
|
384
|
+
if (decision.start) {
|
|
325
385
|
try {
|
|
326
386
|
await supervisor.start();
|
|
327
387
|
} catch (error) {
|