@dbx-tools/lakebox 0.0.0-stage → 0.9.131

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.
@@ -0,0 +1,225 @@
1
+ import { log } from "@dbx-tools/shared-core";
2
+ import { PooledSshTransport, } from "./_ssh.js";
3
+ const logger = log.logger("lakebox:ssh2");
4
+ const patchedSsh2Protocols = new WeakSet();
5
+ /** Exclusive ssh2 connection pool for commands and direct TCP forwarding. */
6
+ export class Ssh2Transport extends PooledSshTransport {
7
+ kind = "ssh2";
8
+ connectTimeoutMs;
9
+ constructor(options, poolOptions = {}) {
10
+ super(ssh2Lifecycle(options), {
11
+ acquireTimeoutMillis: options.connectTimeoutMs,
12
+ ...poolOptions,
13
+ });
14
+ this.connectTimeoutMs = options.connectTimeoutMs;
15
+ }
16
+ runWithResource(resource, script, execution) {
17
+ return executeCommand(resource, script, execution);
18
+ }
19
+ connectWithResource(resource, remoteHost, remotePort) {
20
+ return new Promise((resolve, reject) => {
21
+ let settled = false;
22
+ let timeout;
23
+ const cleanup = () => {
24
+ clearTimeout(timeout);
25
+ resource.client.removeListener("close", clientClosed);
26
+ resource.client.removeListener("error", clientFailed);
27
+ };
28
+ const fail = (error) => {
29
+ if (settled)
30
+ return;
31
+ settled = true;
32
+ cleanup();
33
+ reject(error);
34
+ };
35
+ const clientClosed = () => {
36
+ fail(new Error("ssh2 connection closed while opening forwarding"));
37
+ };
38
+ const clientFailed = (error) => {
39
+ fail(error);
40
+ };
41
+ timeout = setTimeout(() => fail(new Error(`ssh2 forwarding did not open within ${this.connectTimeoutMs}ms`)), this.connectTimeoutMs);
42
+ timeout.unref?.();
43
+ resource.client.once("close", clientClosed);
44
+ resource.client.once("error", clientFailed);
45
+ resource.client.forwardOut("127.0.0.1", 0, remoteHost, remotePort, (error, channel) => {
46
+ if (settled) {
47
+ channel?.destroy();
48
+ return;
49
+ }
50
+ settled = true;
51
+ cleanup();
52
+ if (error)
53
+ reject(error);
54
+ else
55
+ resolve(channel);
56
+ });
57
+ });
58
+ }
59
+ }
60
+ function ssh2Lifecycle(options) {
61
+ const patchMode = options.ssh2Patch ?? "auto";
62
+ const requirePatch = patchMode === "true";
63
+ const patchCommands = requirePatch || (patchMode === "auto" && isNodeDevelopment());
64
+ let initialClient = options.ssh2Client;
65
+ const initialPatched = patchCommands ? patchSsh2ExecWithoutReply(options.ssh2Client) : false;
66
+ if (requirePatch && !initialPatched) {
67
+ options.ssh2Client.destroy();
68
+ throw new Error("Installed ssh2 internals do not support the required exec no-reply patch");
69
+ }
70
+ let logged = false;
71
+ return {
72
+ create: async () => {
73
+ const seeded = initialClient !== undefined;
74
+ const client = initialClient ?? (await options.createSsh2CommandClient());
75
+ initialClient = undefined;
76
+ const resource = trackClient(client);
77
+ const patched = seeded
78
+ ? initialPatched
79
+ : patchCommands
80
+ ? patchSsh2ExecWithoutReply(client)
81
+ : false;
82
+ if (requirePatch && !patched) {
83
+ client.destroy();
84
+ throw new Error("Installed ssh2 internals do not support the required exec no-reply patch");
85
+ }
86
+ if (!logged) {
87
+ logged = true;
88
+ logger.warn("using pooled ssh2 Lakebox transport", {
89
+ patchMode,
90
+ patched,
91
+ nodeEnv: process.env.NODE_ENV,
92
+ transportMode: options.transport ?? "auto",
93
+ });
94
+ }
95
+ return resource;
96
+ },
97
+ destroy: (resource) => closeClient(resource, options.connectTimeoutMs),
98
+ // A remote no-op would consume the connection's only permitted operation.
99
+ test: async (resource) => resource.alive,
100
+ };
101
+ }
102
+ function trackClient(client) {
103
+ const resource = { client, alive: true };
104
+ const markClosed = () => {
105
+ resource.alive = false;
106
+ };
107
+ client.once("close", markClosed);
108
+ client.once("error", markClosed);
109
+ return resource;
110
+ }
111
+ async function closeClient(resource, timeoutMs) {
112
+ if (!resource.alive) {
113
+ resource.client.destroy();
114
+ return;
115
+ }
116
+ await new Promise((resolve) => {
117
+ let settled = false;
118
+ const finish = () => {
119
+ if (settled)
120
+ return;
121
+ settled = true;
122
+ clearTimeout(timeout);
123
+ resource.alive = false;
124
+ resolve();
125
+ };
126
+ const timeout = setTimeout(() => {
127
+ resource.client.destroy();
128
+ finish();
129
+ }, Math.min(timeoutMs, 1_000));
130
+ timeout.unref?.();
131
+ resource.client.once("close", finish);
132
+ resource.client.once("error", finish);
133
+ resource.client.end();
134
+ });
135
+ }
136
+ function executeCommand(resource, script, execution) {
137
+ return new Promise((resolve, reject) => {
138
+ let channel;
139
+ let exitCode;
140
+ let settled = false;
141
+ const cleanup = () => {
142
+ resource.client.removeListener("close", clientClosed);
143
+ resource.client.removeListener("error", clientClosed);
144
+ };
145
+ const finish = (code) => {
146
+ if (settled)
147
+ return;
148
+ settled = true;
149
+ cleanup();
150
+ resolve(code);
151
+ };
152
+ const fail = (error) => {
153
+ if (settled)
154
+ return;
155
+ settled = true;
156
+ cleanup();
157
+ reject(error);
158
+ };
159
+ const clientClosed = () => fail(new Error("ssh2 connection closed during command execution"));
160
+ resource.client.once("close", clientClosed);
161
+ resource.client.once("error", clientClosed);
162
+ execution.setCancel(() => {
163
+ if (settled)
164
+ return;
165
+ try {
166
+ channel?.signal("KILL");
167
+ }
168
+ catch {
169
+ // The remote may close the channel before the signal is written.
170
+ }
171
+ finish(130);
172
+ channel?.close();
173
+ });
174
+ if (execution.signal.aborted) {
175
+ finish(130);
176
+ return;
177
+ }
178
+ resource.client.exec(script, (error, openedChannel) => {
179
+ if (error) {
180
+ fail(error);
181
+ return;
182
+ }
183
+ if (settled) {
184
+ openedChannel.close();
185
+ return;
186
+ }
187
+ channel = openedChannel;
188
+ channel.on("data", execution.stdout);
189
+ channel.stderr.on("data", execution.stderr);
190
+ channel.on("exit", (code) => {
191
+ exitCode = code ?? 1;
192
+ });
193
+ channel.once("error", fail);
194
+ channel.once("close", () => finish(exitCode ?? 1));
195
+ });
196
+ });
197
+ }
198
+ /** Apply the Databricks gateway exec-request workaround when internals permit it. */
199
+ function patchSsh2ExecWithoutReply(client) {
200
+ const internals = client;
201
+ const protocol = internals._protocol;
202
+ const channelManager = internals._chanMgr;
203
+ if (!protocol || typeof protocol.exec !== "function" || !channelManager) {
204
+ return false;
205
+ }
206
+ if (patchedSsh2Protocols.has(protocol))
207
+ return true;
208
+ const originalExec = protocol.exec.bind(protocol);
209
+ protocol.exec = (channelId, command) => {
210
+ originalExec(channelId, command, false);
211
+ const channel = Object.values(channelManager._channels).find((candidate) => candidate?.outgoing.id === channelId);
212
+ const callback = channel?._callbacks.shift();
213
+ if (!callback) {
214
+ throw new Error("ssh2 exec callback queue was not available for the no-reply workaround");
215
+ }
216
+ queueMicrotask(() => callback(false));
217
+ };
218
+ patchedSsh2Protocols.add(protocol);
219
+ return true;
220
+ }
221
+ function isNodeDevelopment() {
222
+ const nodeEnv = process.env.NODE_ENV?.trim().toLowerCase();
223
+ return nodeEnv === "development" || nodeEnv === "dev";
224
+ }
225
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX3NzaF9zc2gyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL19zc2hfc3NoMi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFN0MsT0FBTyxFQUNMLGtCQUFrQixHQUtuQixNQUFNLFdBQVcsQ0FBQztBQUVuQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBQzFDLE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxPQUFPLEVBQWdCLENBQUM7QUFPekQsNkVBQTZFO0FBQzdFLE1BQU0sT0FBTyxhQUFjLFNBQVEsa0JBQWdDO0lBQ3hELElBQUksR0FBRyxNQUFlLENBQUM7SUFDZixnQkFBZ0IsQ0FBUztJQUUxQyxZQUFZLE9BQTRCLEVBQUUsY0FBOEIsRUFBRTtRQUN4RSxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQzVCLG9CQUFvQixFQUFFLE9BQU8sQ0FBQyxnQkFBZ0I7WUFDOUMsR0FBRyxXQUFXO1NBQ2YsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztJQUNuRCxDQUFDO0lBRVMsZUFBZSxDQUN2QixRQUFzQixFQUN0QixNQUFjLEVBQ2QsU0FBOEI7UUFFOUIsT0FBTyxjQUFjLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNyRCxDQUFDO0lBRVMsbUJBQW1CLENBQzNCLFFBQXNCLEVBQ3RCLFVBQWtCLEVBQ2xCLFVBQWtCO1FBRWxCLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDckMsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFDO1lBQ3BCLElBQUksT0FBc0MsQ0FBQztZQUMzQyxNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUU7Z0JBQ25CLFlBQVksQ0FB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VUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzdDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0VBQXdFLENBQUMsQ0FBQztRQUM1RixDQUFDO1FBQ0QsY0FBYyxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBQ3hDLENBQUMsQ0FBQztJQUNGLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNuQyxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxTQUFTLGlCQUFpQjtJQUN4QixNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUMzRCxPQUFPLE9BQU8sS0FBSyxhQUFhLElBQUksT0FBTyxLQUFLLEtBQUssQ0FBQztBQUN4RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBEdXBsZXggfSBmcm9tIFwibm9kZTpzdHJlYW1cIjtcbmltcG9ydCB7IGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgdHlwZSB7IENsaWVudCBhcyBTc2gyQ2xpZW50LCBDbGllbnRDaGFubmVsIH0gZnJvbSBcInNzaDJcIjtcbmltcG9ydCB7XG4gIFBvb2xlZFNzaFRyYW5zcG9ydCxcbiAgdHlwZSBTc2hDb21tYW5kRXhlY3V0aW9uLFxuICB0eXBlIFNzaFBvb2xPcHRpb25zLFxuICB0eXBlIFNzaFJlc291cmNlTGlmZWN5Y2xlLFxuICB0eXBlIFNzaFRyYW5zcG9ydE9wdGlvbnMsXG59IGZyb20gXCIuL19zc2gudHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImxha2Vib3g6c3NoMlwiKTtcbmNvbnN0IHBhdGNoZWRTc2gyUHJvdG9jb2xzID0gbmV3IFdlYWtTZXQ8U3NoMlByb3RvY29sPigpO1xuXG5pbnRlcmZhY2UgU3NoMlJlc291cmNlIHtcbiAgcmVhZG9ubHkgY2xpZW50OiBTc2gyQ2xpZW50O1xuICBhbGl2ZTogYm9vbGVhbjtcbn1cblxuLyoqIEV4Y2x1c2l2ZSBzc2gyIGNvbm5lY3Rpb24gcG9vbCBmb3IgY29tbWFuZHMgYW5kIGRpcmVjdCBUQ1AgZm9yd2FyZGluZy4gKi9cbmV4cG9ydCBjbGFzcyBTc2gyVHJhbnNwb3J0IGV4dGVuZHMgUG9vbGVkU3NoVHJhbnNwb3J0PFNzaDJSZXNvdXJjZT4ge1xuICByZWFkb25seSBraW5kID0gXCJzc2gyXCIgYXMgY29uc3Q7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29ubmVjdFRpbWVvdXRNczogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IFNzaFRyYW5zcG9ydE9wdGlvbnMsIHBvb2xPcHRpb25zOiBTc2hQb29sT3B0aW9ucyA9IHt9KSB7XG4gICAgc3VwZXIoc3NoMkxpZmVjeWNsZShvcHRpb25zKSwge1xuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IG9wdGlvbnMuY29ubmVjdFRpbWVvdXRNcyxcbiAgICAgIC4uLnBvb2xPcHRpb25zLFxuICAgIH0pO1xuICAgIHRoaXMuY29ubmVjdFRpbWVvdXRNcyA9IG9wdGlvbnMuY29ubmVjdFRpbWVvdXRNcztcbiAgfVxuXG4gIHByb3RlY3RlZCBydW5XaXRoUmVzb3VyY2UoXG4gICAgcmVzb3VyY2U6IFNzaDJSZXNvdXJjZSxcbiAgICBzY3JpcHQ6IHN0cmluZyxcbiAgICBleGVjdXRpb246IFNzaENvbW1hbmRFeGVjdXRpb24sXG4gICk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgcmV0dXJuIGV4ZWN1dGVDb21tYW5kKHJlc291cmNlLCBzY3JpcHQsIGV4ZWN1dGlvbik7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29ubmVjdFdpdGhSZXNvdXJjZShcbiAgICByZXNvdXJjZTogU3NoMlJlc291cmNlLFxuICAgIHJlbW90ZUhvc3Q6IHN0cmluZyxcbiAgICByZW1vdGVQb3J0OiBudW1iZXIsXG4gICk6IFByb21pc2U8RHVwbGV4PiB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGxldCBzZXR0bGVkID0gZmFsc2U7XG4gICAgICBsZXQgdGltZW91dDogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD47XG4gICAgICBjb25zdCBjbGVhbnVwID0gKCkgPT4ge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgICAgIHJlc291cmNlLmNsaWVudC5yZW1vdmVMaXN0ZW5lcihcImNsb3NlXCIsIGNsaWVudENsb3NlZCk7XG4gICAgICAgIHJlc291cmNlLmNsaWVudC5yZW1vdmVMaXN0ZW5lcihcImVycm9yXCIsIGNsaWVudEZhaWxlZCk7XG4gICAgICB9O1xuICAgICAgY29uc3QgZmFpbCA9IChlcnJvcjogRXJyb3IpID0+IHtcbiAgICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICAgIGNsZWFudXAoKTtcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgIH07XG4gICAgICBjb25zdCBjbGllbnRDbG9zZWQgPSAoKSA9PiB7XG4gICAgICAgIGZhaWwobmV3IEVycm9yKFwic3NoMiBjb25uZWN0aW9uIGNsb3NlZCB3aGlsZSBvcGVuaW5nIGZvcndhcmRpbmdcIikpO1xuICAgICAgfTtcbiAgICAgIGNvbnN0IGNsaWVudEZhaWxlZCA9IChlcnJvcjogRXJyb3IpID0+IHtcbiAgICAgICAgZmFpbChlcnJvcik7XG4gICAgICB9O1xuICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQoXG4gICAgICAgICgpID0+IGZhaWwobmV3IEVycm9yKGBzc2gyIGZvcndhcmRpbmcgZGlkIG5vdCBvcGVuIHdpdGhpbiAke3RoaXMuY29ubmVjdFRpbWVvdXRNc31tc2ApKSxcbiAgICAgICAgdGhpcy5jb25uZWN0VGltZW91dE1zLFxuICAgICAgKTtcbiAgICAgIHRpbWVvdXQudW5yZWY/LigpO1xuICAgICAgcmVzb3VyY2UuY2xpZW50Lm9uY2UoXCJjbG9zZVwiLCBjbGllbnRDbG9zZWQpO1xuICAgICAgcmVzb3VyY2UuY2xpZW50Lm9uY2UoXCJlcnJvclwiLCBjbGllbnRGYWlsZWQpO1xuICAgICAgcmVzb3VyY2UuY2xpZW50LmZvcndhcmRPdXQoXCIxMjcuMC4wLjFcIiwgMCwgcmVtb3RlSG9zdCwgcmVtb3RlUG9ydCwgKGVycm9yLCBjaGFubmVsKSA9PiB7XG4gICAgICAgIGlmIChzZXR0bGVkKSB7XG4gICAgICAgICAgY2hhbm5lbD8uZGVzdHJveSgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgICAgY2xlYW51cCgpO1xuICAgICAgICBpZiAoZXJyb3IpIHJlamVjdChlcnJvcik7XG4gICAgICAgIGVsc2UgcmVzb2x2ZShjaGFubmVsKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG59XG5cbmZ1bmN0aW9uIHNzaDJMaWZlY3ljbGUob3B0aW9uczogU3NoVHJhbnNwb3J0T3B0aW9ucyk6IFNzaFJlc291cmNlTGlmZWN5Y2xlPFNzaDJSZXNvdXJjZT4ge1xuICBjb25zdCBwYXRjaE1vZGUgPSBvcHRpb25zLnNzaDJQYXRjaCA/PyBcImF1dG9cIjtcbiAgY29uc3QgcmVxdWlyZVBhdGNoID0gcGF0Y2hNb2RlID09PSBcInRydWVcIjtcbiAgY29uc3QgcGF0Y2hDb21tYW5kcyA9IHJlcXVpcmVQYXRjaCB8fCAocGF0Y2hNb2RlID09PSBcImF1dG9cIiAmJiBpc05vZGVEZXZlbG9wbWVudCgpKTtcbiAgbGV0IGluaXRpYWxDbGllbnQ6IFNzaDJDbGllbnQgfCB1bmRlZmluZWQgPSBvcHRpb25zLnNzaDJDbGllbnQ7XG4gIGNvbnN0IGluaXRpYWxQYXRjaGVkID0gcGF0Y2hDb21tYW5kcyA/IHBhdGNoU3NoMkV4ZWNXaXRob3V0UmVwbHkob3B0aW9ucy5zc2gyQ2xpZW50KSA6IGZhbHNlO1xuICBpZiAocmVxdWlyZVBhdGNoICYmICFpbml0aWFsUGF0Y2hlZCkge1xuICAgIG9wdGlvbnMuc3NoMkNsaWVudC5kZXN0cm95KCk7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiSW5zdGFsbGVkIHNzaDIgaW50ZXJuYWxzIGRvIG5vdCBzdXBwb3J0IHRoZSByZXF1aXJlZCBleGVjIG5vLXJlcGx5IHBhdGNoXCIpO1xuICB9XG4gIGxldCBsb2dnZWQgPSBmYWxzZTtcbiAgcmV0dXJuIHtcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHNlZWRlZCA9IGluaXRpYWxDbGllbnQgIT09IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IGNsaWVudCA9IGluaXRpYWxDbGllbnQgPz8gKGF3YWl0IG9wdGlvbnMuY3JlYXRlU3NoMkNvbW1hbmRDbGllbnQoKSk7XG4gICAgICBpbml0aWFsQ2xpZW50ID0gdW5kZWZpbmVkO1xuICAgICAgY29uc3QgcmVzb3VyY2UgPSB0cmFja0NsaWVudChjbGllbnQpO1xuICAgICAgY29uc3QgcGF0Y2hlZCA9IHNlZWRlZFxuICAgICAgICA/IGluaXRpYWxQYXRjaGVkXG4gICAgICAgIDogcGF0Y2hDb21tYW5kc1xuICAgICAgICAgID8gcGF0Y2hTc2gyRXhlY1dpdGhvdXRSZXBseShjbGllbnQpXG4gICAgICAgICAgOiBmYWxzZTtcbiAgICAgIGlmIChyZXF1aXJlUGF0Y2ggJiYgIXBhdGNoZWQpIHtcbiAgICAgICAgY2xpZW50LmRlc3Ryb3koKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiSW5zdGFsbGVkIHNzaDIgaW50ZXJuYWxzIGRvIG5vdCBzdXBwb3J0IHRoZSByZXF1aXJlZCBleGVjIG5vLXJlcGx5IHBhdGNoXCIpO1xuICAgICAgfVxuICAgICAgaWYgKCFsb2dnZWQpIHtcbiAgICAgICAgbG9nZ2VkID0gdHJ1ZTtcbiAgICAgICAgbG9nZ2VyLndhcm4oXCJ1c2luZyBwb29sZWQgc3NoMiBMYWtlYm94IHRyYW5zcG9ydFwiLCB7XG4gICAgICAgICAgcGF0Y2hNb2RlLFxuICAgICAgICAgIHBhdGNoZWQsXG4gICAgICAgICAgbm9kZUVudjogcHJvY2Vzcy5lbnYuTk9ERV9FTlYsXG4gICAgICAgICAgdHJhbnNwb3J0TW9kZTogb3B0aW9ucy50cmFuc3BvcnQgPz8gXCJhdXRvXCIsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc291cmNlO1xuICAgIH0sXG4gICAgZGVzdHJveTogKHJlc291cmNlKSA9PiBjbG9zZUNsaWVudChyZXNvdXJjZSwgb3B0aW9ucy5jb25uZWN0VGltZW91dE1zKSxcbiAgICAvLyBBIHJlbW90ZSBuby1vcCB3b3VsZCBjb25zdW1lIHRoZSBjb25uZWN0aW9uJ3Mgb25seSBwZXJtaXR0ZWQgb3BlcmF0aW9uLlxuICAgIHRlc3Q6IGFzeW5jIChyZXNvdXJjZSkgPT4gcmVzb3VyY2UuYWxpdmUsXG4gIH07XG59XG5cbmZ1bmN0aW9uIHRyYWNrQ2xpZW50KGNsaWVudDogU3NoMkNsaWVudCk6IFNzaDJSZXNvdXJjZSB7XG4gIGNvbnN0IHJlc291cmNlOiBTc2gyUmVzb3VyY2UgPSB7IGNsaWVudCwgYWxpdmU6IHRydWUgfTtcbiAgY29uc3QgbWFya0Nsb3NlZCA9ICgpID0+IHtcbiAgICByZXNvdXJjZS5hbGl2ZSA9IGZhbHNlO1xuICB9O1xuICBjbGllbnQub25jZShcImNsb3NlXCIsIG1hcmtDbG9zZWQpO1xuICBjbGllbnQub25jZShcImVycm9yXCIsIG1hcmtDbG9zZWQpO1xuICByZXR1cm4gcmVzb3VyY2U7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNsb3NlQ2xpZW50KHJlc291cmNlOiBTc2gyUmVzb3VyY2UsIHRpbWVvdXRNczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIGlmICghcmVzb3VyY2UuYWxpdmUpIHtcbiAgICByZXNvdXJjZS5jbGllbnQuZGVzdHJveSgpO1xuICAgIHJldHVybjtcbiAgfVxuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgIGxldCBzZXR0bGVkID0gZmFsc2U7XG4gICAgY29uc3QgZmluaXNoID0gKCkgPT4ge1xuICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgIHNldHRsZWQgPSB0cnVlO1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgcmVzb3VyY2UuYWxpdmUgPSBmYWxzZTtcbiAgICAgIHJlc29sdmUoKTtcbiAgICB9O1xuICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KFxuICAgICAgKCkgPT4ge1xuICAgICAgICByZXNvdXJjZS5jbGllbnQuZGVzdHJveSgpO1xuICAgICAgICBmaW5pc2goKTtcbiAgICAgIH0sXG4gICAgICBNYXRoLm1pbih0aW1lb3V0TXMsIDFfMDAwKSxcbiAgICApO1xuICAgIHRpbWVvdXQudW5yZWY/LigpO1xuICAgIHJlc291cmNlLmNsaWVudC5vbmNlKFwiY2xvc2VcIiwgZmluaXNoKTtcbiAgICByZXNvdXJjZS5jbGllbnQub25jZShcImVycm9yXCIsIGZpbmlzaCk7XG4gICAgcmVzb3VyY2UuY2xpZW50LmVuZCgpO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gZXhlY3V0ZUNvbW1hbmQoXG4gIHJlc291cmNlOiBTc2gyUmVzb3VyY2UsXG4gIHNjcmlwdDogc3RyaW5nLFxuICBleGVjdXRpb246IFNzaENvbW1hbmRFeGVjdXRpb24sXG4pOiBQcm9taXNlPG51bWJlcj4ge1xuICByZXR1cm4gbmV3IFByb21pc2U8bnVtYmVyPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgbGV0IGNoYW5uZWw6IENsaWVudENoYW5uZWwgfCB1bmRlZmluZWQ7XG4gICAgbGV0IGV4aXRDb2RlOiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gICAgbGV0IHNldHRsZWQgPSBmYWxzZTtcbiAgICBjb25zdCBjbGVhbnVwID0gKCkgPT4ge1xuICAgICAgcmVzb3VyY2UuY2xpZW50LnJlbW92ZUxpc3RlbmVyKFwiY2xvc2VcIiwgY2xpZW50Q2xvc2VkKTtcbiAgICAgIHJlc291cmNlLmNsaWVudC5yZW1vdmVMaXN0ZW5lcihcImVycm9yXCIsIGNsaWVudENsb3NlZCk7XG4gICAgfTtcbiAgICBjb25zdCBmaW5pc2ggPSAoY29kZTogbnVtYmVyKSA9PiB7XG4gICAgICBpZiAoc2V0dGxlZCkgcmV0dXJuO1xuICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgICBjbGVhbnVwKCk7XG4gICAgICByZXNvbHZlKGNvZGUpO1xuICAgIH07XG4gICAgY29uc3QgZmFpbCA9IChlcnJvcjogRXJyb3IpID0+IHtcbiAgICAgIGlmIChzZXR0bGVkKSByZXR1cm47XG4gICAgICBzZXR0bGVkID0gdHJ1ZTtcbiAgICAgIGNsZWFudXAoKTtcbiAgICAgIHJlamVjdChlcnJvcik7XG4gICAgfTtcbiAgICBjb25zdCBjbGllbnRDbG9zZWQgPSAoKSA9PiBmYWlsKG5ldyBFcnJvcihcInNzaDIgY29ubmVjdGlvbiBjbG9zZWQgZHVyaW5nIGNvbW1hbmQgZXhlY3V0aW9uXCIpKTtcbiAgICByZXNvdXJjZS5jbGllbnQub25jZShcImNsb3NlXCIsIGNsaWVudENsb3NlZCk7XG4gICAgcmVzb3VyY2UuY2xpZW50Lm9uY2UoXCJlcnJvclwiLCBjbGllbnRDbG9zZWQpO1xuICAgIGV4ZWN1dGlvbi5zZXRDYW5jZWwoKCkgPT4ge1xuICAgICAgaWYgKHNldHRsZWQpIHJldHVybjtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNoYW5uZWw/LnNpZ25hbChcIktJTExcIik7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gVGhlIHJlbW90ZSBtYXkgY2xvc2UgdGhlIGNoYW5uZWwgYmVmb3JlIHRoZSBzaWduYWwgaXMgd3JpdHRlbi5cbiAgICAgIH1cbiAgICAgIGZpbmlzaCgxMzApO1xuICAgICAgY2hhbm5lbD8uY2xvc2UoKTtcbiAgICB9KTtcbiAgICBpZiAoZXhlY3V0aW9uLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICBmaW5pc2goMTMwKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmVzb3VyY2UuY2xpZW50LmV4ZWMoc2NyaXB0LCAoZXJyb3IsIG9wZW5lZENoYW5uZWwpID0+IHtcbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICBmYWlsKGVycm9yKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKHNldHRsZWQpIHtcbiAgICAgICAgb3BlbmVkQ2hhbm5lbC5jbG9zZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjaGFubmVsID0gb3BlbmVkQ2hhbm5lbDtcbiAgICAgIGNoYW5uZWwub24oXCJkYXRhXCIsIGV4ZWN1dGlvbi5zdGRvdXQpO1xuICAgICAgY2hhbm5lbC5zdGRlcnIub24oXCJkYXRhXCIsIGV4ZWN1dGlvbi5zdGRlcnIpO1xuICAgICAgY2hhbm5lbC5vbihcImV4aXRcIiwgKGNvZGU6IG51bWJlciB8IG51bGwpID0+IHtcbiAgICAgICAgZXhpdENvZGUgPSBjb2RlID8/IDE7XG4gICAgICB9KTtcbiAgICAgIGNoYW5uZWwub25jZShcImVycm9yXCIsIGZhaWwpO1xuICAgICAgY2hhbm5lbC5vbmNlKFwiY2xvc2VcIiwgKCkgPT4gZmluaXNoKGV4aXRDb2RlID8/IDEpKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbi8qKiBBcHBseSB0aGUgRGF0YWJyaWNrcyBnYXRld2F5IGV4ZWMtcmVxdWVzdCB3b3JrYXJvdW5kIHdoZW4gaW50ZXJuYWxzIHBlcm1pdCBpdC4gKi9cbmZ1bmN0aW9uIHBhdGNoU3NoMkV4ZWNXaXRob3V0UmVwbHkoY2xpZW50OiBTc2gyQ2xpZW50KTogYm9vbGVhbiB7XG4gIGNvbnN0IGludGVybmFscyA9IGNsaWVudCBhcyB1bmtub3duIGFzIFNzaDJDbGllbnRJbnRlcm5hbHM7XG4gIGNvbnN0IHByb3RvY29sID0gaW50ZXJuYWxzLl9wcm90b2NvbDtcbiAgY29uc3QgY2hhbm5lbE1hbmFnZXIgPSBpbnRlcm5hbHMuX2NoYW5NZ3I7XG4gIGlmICghcHJvdG9jb2wgfHwgdHlwZW9mIHByb3RvY29sLmV4ZWMgIT09IFwiZnVuY3Rpb25cIiB8fCAhY2hhbm5lbE1hbmFnZXIpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgaWYgKHBhdGNoZWRTc2gyUHJvdG9jb2xzLmhhcyhwcm90b2NvbCkpIHJldHVybiB0cnVlO1xuICBjb25zdCBvcmlnaW5hbEV4ZWMgPSBwcm90b2NvbC5leGVjLmJpbmQocHJvdG9jb2wpO1xuICBwcm90b2NvbC5leGVjID0gKGNoYW5uZWxJZCwgY29tbWFuZCkgPT4ge1xuICAgIG9yaWdpbmFsRXhlYyhjaGFubmVsSWQsIGNvbW1hbmQsIGZhbHNlKTtcbiAgICBjb25zdCBjaGFubmVsID0gT2JqZWN0LnZhbHVlcyhjaGFubmVsTWFuYWdlci5fY2hhbm5lbHMpLmZpbmQoXG4gICAgICAoY2FuZGlkYXRlKSA9PiBjYW5kaWRhdGU/Lm91dGdvaW5nLmlkID09PSBjaGFubmVsSWQsXG4gICAgKTtcbiAgICBjb25zdCBjYWxsYmFjayA9IGNoYW5uZWw/Ll9jYWxsYmFja3Muc2hpZnQoKTtcbiAgICBpZiAoIWNhbGxiYWNrKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJzc2gyIGV4ZWMgY2FsbGJhY2sgcXVldWUgd2FzIG5vdCBhdmFpbGFibGUgZm9yIHRoZSBuby1yZXBseSB3b3JrYXJvdW5kXCIpO1xuICAgIH1cbiAgICBxdWV1ZU1pY3JvdGFzaygoKSA9PiBjYWxsYmFjayhmYWxzZSkpO1xuICB9O1xuICBwYXRjaGVkU3NoMlByb3RvY29scy5hZGQocHJvdG9jb2wpO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gaXNOb2RlRGV2ZWxvcG1lbnQoKTogYm9vbGVhbiB7XG4gIGNvbnN0IG5vZGVFbnYgPSBwcm9jZXNzLmVudi5OT0RFX0VOVj8udHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gIHJldHVybiBub2RlRW52ID09PSBcImRldmVsb3BtZW50XCIgfHwgbm9kZUVudiA9PT0gXCJkZXZcIjtcbn1cblxuaW50ZXJmYWNlIFNzaDJFeGVjQ2hhbm5lbCB7XG4gIG91dGdvaW5nOiB7IGlkOiBudW1iZXIgfTtcbiAgX2NhbGxiYWNrczogQXJyYXk8KGhhZEVycm9yOiBib29sZWFuIHwgRXJyb3IpID0+IHZvaWQ+O1xufVxuXG5pbnRlcmZhY2UgU3NoMlByb3RvY29sIHtcbiAgZXhlYyhjaGFubmVsSWQ6IG51bWJlciwgY29tbWFuZDogc3RyaW5nIHwgQnVmZmVyLCB3YW50UmVwbHk/OiBib29sZWFuKTogdm9pZDtcbn1cblxuaW50ZXJmYWNlIFNzaDJDbGllbnRJbnRlcm5hbHMge1xuICBfcHJvdG9jb2w/OiBTc2gyUHJvdG9jb2w7XG4gIF9jaGFuTWdyPzogeyBfY2hhbm5lbHM6IFJlY29yZDxudW1iZXIsIFNzaDJFeGVjQ2hhbm5lbCB8IHVuZGVmaW5lZD4gfTtcbn1cbiJdfQ==
@@ -0,0 +1,39 @@
1
+ import type { LakeboxClient } from "./rest.ts";
2
+ interface StoredKey {
3
+ privateKey: string;
4
+ privateKeyPath: string;
5
+ publicKey: string;
6
+ keyHash: string;
7
+ }
8
+ /**
9
+ * Stores caller-scoped SSH keys and trusted Databricks gateway host keys.
10
+ *
11
+ * Each workspace and caller identity receives a separate Ed25519 key pair. The
12
+ * store writes private keys, gateway fingerprints, and OpenSSH known-host files
13
+ * with owner-only permissions beneath the configured root directory.
14
+ */
15
+ export declare class SshKeyStore {
16
+ private readonly root;
17
+ /** Creates a key store rooted at the default dbx-tools application-data path. */
18
+ constructor(root?: string);
19
+ /**
20
+ * Loads or creates the caller key pair and registers its public key when the
21
+ * workspace does not already contain the matching key hash.
22
+ *
23
+ * @returns The local key material and the gateway host returned by Databricks,
24
+ * when available.
25
+ */
26
+ ensureRegistered(api: LakeboxClient, owner: string, signal?: AbortSignal): Promise<StoredKey & {
27
+ gatewayHost?: string;
28
+ }>;
29
+ /** Returns the stored SHA-256 gateway fingerprint, or `undefined` when untrusted. */
30
+ knownHost(scope: string, owner: string, host: string, port: number): Promise<string | undefined>;
31
+ /** Returns the OpenSSH known-host file path for one workspace, user, and gateway. */
32
+ knownHostsPath(scope: string, owner: string, host: string, port: number): string;
33
+ /** Stores the accepted gateway fingerprint and OpenSSH host-key entry. */
34
+ rememberHost(scope: string, owner: string, host: string, port: number, fingerprint: string, key: Buffer): Promise<void>;
35
+ private loadOrCreate;
36
+ private hostFingerprintPath;
37
+ private directory;
38
+ }
39
+ export {};
@@ -0,0 +1,145 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { fileLock } from "@dbx-tools/core";
6
+ import ssh2 from "ssh2";
7
+ const { utils } = ssh2;
8
+ /**
9
+ * Stores caller-scoped SSH keys and trusted Databricks gateway host keys.
10
+ *
11
+ * Each workspace and caller identity receives a separate Ed25519 key pair. The
12
+ * store writes private keys, gateway fingerprints, and OpenSSH known-host files
13
+ * with owner-only permissions beneath the configured root directory.
14
+ */
15
+ export class SshKeyStore {
16
+ root;
17
+ /** Creates a key store rooted at the default dbx-tools application-data path. */
18
+ constructor(root = join(homedir(), ".dbx-tools", "lakebox")) {
19
+ this.root = root;
20
+ }
21
+ /**
22
+ * Loads or creates the caller key pair and registers its public key when the
23
+ * workspace does not already contain the matching key hash.
24
+ *
25
+ * @returns The local key material and the gateway host returned by Databricks,
26
+ * when available.
27
+ */
28
+ async ensureRegistered(api, owner, signal) {
29
+ const key = await this.loadOrCreate(await api.workspaceScope(), owner);
30
+ const keys = await api.listKeys(signal);
31
+ const existing = keys.find((entry) => entry.keyHash === key.keyHash);
32
+ if (existing) {
33
+ return { ...key, ...(existing.gatewayHost ? { gatewayHost: existing.gatewayHost } : {}) };
34
+ }
35
+ const registered = await api.registerKey(key.publicKey, `dbx-tools-lakebox:${key.keyHash}`, signal);
36
+ return {
37
+ ...key,
38
+ ...(registered.gatewayHost ? { gatewayHost: registered.gatewayHost } : {}),
39
+ };
40
+ }
41
+ /** Returns the stored SHA-256 gateway fingerprint, or `undefined` when untrusted. */
42
+ async knownHost(scope, owner, host, port) {
43
+ try {
44
+ const value = await readFile(this.hostFingerprintPath(scope, owner, host, port), "utf8");
45
+ return value.trim() || undefined;
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ }
51
+ /** Returns the OpenSSH known-host file path for one workspace, user, and gateway. */
52
+ knownHostsPath(scope, owner, host, port) {
53
+ return join(this.directory(scope, owner), `host-${digest(`${host}\0${port}`)}.known_hosts`);
54
+ }
55
+ /** Stores the accepted gateway fingerprint and OpenSSH host-key entry. */
56
+ async rememberHost(scope, owner, host, port, fingerprint, key) {
57
+ const directory = this.directory(scope, owner);
58
+ await mkdir(directory, { recursive: true, mode: 0o700 });
59
+ const fingerprintPath = this.hostFingerprintPath(scope, owner, host, port);
60
+ const knownHostsPath = this.knownHostsPath(scope, owner, host, port);
61
+ const knownHost = `[${host}]:${port} ${sshKeyAlgorithm(key)} ${key.toString("base64")}\n`;
62
+ await Promise.all([
63
+ writeFile(fingerprintPath, `${fingerprint}\n`, { mode: 0o600 }),
64
+ writeFile(knownHostsPath, knownHost, { mode: 0o600 }),
65
+ ]);
66
+ await Promise.all([chmod(fingerprintPath, 0o600), chmod(knownHostsPath, 0o600)]);
67
+ }
68
+ async loadOrCreate(scope, owner) {
69
+ const directory = this.directory(scope, owner);
70
+ const privatePath = join(directory, "id_ed25519");
71
+ const publicPath = `${privatePath}.pub`;
72
+ const stored = await readStoredKey(privatePath, publicPath);
73
+ if (stored)
74
+ return stored;
75
+ await mkdir(directory, { recursive: true, mode: 0o700 });
76
+ return fileLock.withFileLock(["lakebox-ssh-key", privatePath], async () => {
77
+ const rechecked = await readStoredKey(privatePath, publicPath);
78
+ if (rechecked)
79
+ return rechecked;
80
+ const generated = utils.generateKeyPairSync("ed25519");
81
+ const privateKey = generated.private;
82
+ const publicKey = generated.public;
83
+ const suffix = `${process.pid}-${randomBytes(8).toString("hex")}`;
84
+ const stagedPrivate = `${privatePath}.${suffix}`;
85
+ const stagedPublic = `${publicPath}.${suffix}`;
86
+ try {
87
+ await writeFile(stagedPrivate, privateKey, { mode: 0o600 });
88
+ await writeFile(stagedPublic, `${publicKey.trim()}\n`, { mode: 0o600 });
89
+ await rename(stagedPrivate, privatePath);
90
+ await rename(stagedPublic, publicPath);
91
+ await Promise.all([chmod(privatePath, 0o600), chmod(publicPath, 0o600)]);
92
+ const created = await readStoredKey(privatePath, publicPath);
93
+ if (!created)
94
+ throw new Error("SSH key files were not published atomically");
95
+ return created;
96
+ }
97
+ finally {
98
+ await Promise.all([rm(stagedPrivate, { force: true }), rm(stagedPublic, { force: true })]);
99
+ }
100
+ });
101
+ }
102
+ hostFingerprintPath(scope, owner, host, port) {
103
+ return join(this.directory(scope, owner), `host-${digest(`${host}\0${port}`)}.sha256`);
104
+ }
105
+ directory(scope, owner) {
106
+ return join(this.root, digest(`${scope}\0${owner}`));
107
+ }
108
+ }
109
+ async function readStoredKey(privatePath, publicPath) {
110
+ try {
111
+ const [privateKey, publicKey] = await Promise.all([
112
+ readFile(privatePath, "utf8"),
113
+ readFile(publicPath, "utf8"),
114
+ ]);
115
+ return {
116
+ privateKey,
117
+ privateKeyPath: privatePath,
118
+ publicKey,
119
+ keyHash: publicKeyHash(publicKey),
120
+ };
121
+ }
122
+ catch {
123
+ return undefined;
124
+ }
125
+ }
126
+ function publicKeyHash(publicKey) {
127
+ const normalized = publicKey.trim().split(/\s+/).slice(0, 2).join(" ");
128
+ return createHash("sha256").update(normalized).digest().subarray(0, 16).toString("hex");
129
+ }
130
+ function digest(value) {
131
+ return createHash("sha256").update(value).digest("hex").slice(0, 32);
132
+ }
133
+ function sshKeyAlgorithm(key) {
134
+ if (key.length < 5)
135
+ throw new Error("SSH gateway returned an invalid host key");
136
+ const length = key.readUInt32BE(0);
137
+ const end = 4 + length;
138
+ if (length === 0 || end > key.length)
139
+ throw new Error("SSH gateway returned an invalid host key");
140
+ const algorithm = key.subarray(4, end).toString("ascii");
141
+ if (/\s/.test(algorithm))
142
+ throw new Error("SSH gateway returned an invalid host key algorithm");
143
+ return algorithm;
144
+ }
145
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5LXN0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2tleS1zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN0RCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNqRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDakMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzNDLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUd4QixNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBU3ZCOzs7Ozs7R0FNRztBQUNILE1BQU0sT0FBTyxXQUFXO0lBRU87SUFEN0IsaUZBQWlGO0lBQ2pGLFlBQTZCLE9BQU8sSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFLFlBQVksRUFBRSxTQUFTLENBQUM7UUFBL0MsU0FBSSxHQUFKLElBQUksQ0FBMkM7SUFBRyxDQUFDO0lBRWhGOzs7Ozs7T0FNRztJQUNILEtBQUssQ0FBQyxnQkFBZ0IsQ0FDcEIsR0FBa0IsRUFDbEIsS0FBYSxFQUNiLE1BQW9CO1FBRXBCLE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxjQUFjLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN2RSxNQUFNLElBQUksR0FBRyxNQUFNLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDeEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxD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QUFDLEtBQUssQ0FBQyxDQUFDO0FBQzFGLENBQUM7QUFFRCxTQUFTLE1BQU0sQ0FBQyxLQUFhO0lBQzNCLE9BQU8sVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQUMsR0FBVztJQUNsQyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLENBQUMsQ0FBQztJQUNoRixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ25DLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUM7SUFDdkIsSUFBSSxNQUFNLEtBQUssQ0FBQyxJQUFJLEdBQUcsR0FBRyxHQUFHLENBQUMsTUFBTTtRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLENBQUMsQ0FBQztJQUNsRyxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDekQsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsb0RBQW9ELENBQUMsQ0FBQztJQUNoRyxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlSGFzaCwgcmFuZG9tQnl0ZXMgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IGNobW9kLCBta2RpciwgcmVhZEZpbGUsIHJlbmFtZSwgcm0sIHdyaXRlRmlsZSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyBob21lZGlyIH0gZnJvbSBcIm5vZGU6b3NcIjtcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBmaWxlTG9jayB9IGZyb20gXCJAZGJ4LXRvb2xzL2NvcmVcIjtcbmltcG9ydCBzc2gyIGZyb20gXCJzc2gyXCI7XG5pbXBvcnQgdHlwZSB7IExha2Vib3hDbGllbnQgfSBmcm9tIFwiLi9yZXN0LnRzXCI7XG5cbmNvbnN0IHsgdXRpbHMgfSA9IHNzaDI7XG5cbmludGVyZmFjZSBTdG9yZWRLZXkge1xuICBwcml2YXRlS2V5OiBzdHJpbmc7XG4gIHByaXZhdGVLZXlQYXRoOiBzdHJpbmc7XG4gIHB1YmxpY0tleTogc3RyaW5nO1xuICBrZXlIYXNoOiBzdHJpbmc7XG59XG5cbi8qKlxuICogU3RvcmVzIGNhbGxlci1zY29wZWQgU1NIIGtleXMgYW5kIHRydXN0ZWQgRGF0YWJyaWNrcyBnYXRld2F5IGhvc3Qga2V5cy5cbiAqXG4gKiBFYWNoIHdvcmtzcGFjZSBhbmQgY2FsbGVyIGlkZW50aXR5IHJlY2VpdmVzIGEgc2VwYXJhdGUgRWQyNTUxOSBrZXkgcGFpci4gVGhlXG4gKiBzdG9yZSB3cml0ZXMgcHJpdmF0ZSBrZXlzLCBnYXRld2F5IGZpbmdlcnByaW50cywgYW5kIE9wZW5TU0gga25vd24taG9zdCBmaWxlc1xuICogd2l0aCBvd25lci1vbmx5IHBlcm1pc3Npb25zIGJlbmVhdGggdGhlIGNvbmZpZ3VyZWQgcm9vdCBkaXJlY3RvcnkuXG4gKi9cbmV4cG9ydCBjbGFzcyBTc2hLZXlTdG9yZSB7XG4gIC8qKiBDcmVhdGVzIGEga2V5IHN0b3JlIHJvb3RlZCBhdCB0aGUgZGVmYXVsdCBkYngtdG9vbHMgYXBwbGljYXRpb24tZGF0YSBwYXRoLiAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHJvb3QgPSBqb2luKGhvbWVkaXIoKSwgXCIuZGJ4LXRvb2xzXCIsIFwibGFrZWJveFwiKSkge31cblxuICAvKipcbiAgICogTG9hZHMgb3IgY3JlYXRlcyB0aGUgY2FsbGVyIGtleSBwYWlyIGFuZCByZWdpc3RlcnMgaXRzIHB1YmxpYyBrZXkgd2hlbiB0aGVcbiAgICogd29ya3NwYWNlIGRvZXMgbm90IGFscmVhZHkgY29udGFpbiB0aGUgbWF0Y2hpbmcga2V5IGhhc2guXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBsb2NhbCBrZXkgbWF0ZXJpYWwgYW5kIHRoZSBnYXRld2F5IGhvc3QgcmV0dXJuZWQgYnkgRGF0YWJyaWNrcyxcbiAgICogd2hlbiBhdmFpbGFibGUuXG4gICAqL1xuICBhc3luYyBlbnN1cmVSZWdpc3RlcmVkKFxuICAgIGFwaTogTGFrZWJveENsaWVudCxcbiAgICBvd25lcjogc3RyaW5nLFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPFN0b3JlZEtleSAmIHsgZ2F0ZXdheUhvc3Q/OiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IGtleSA9IGF3YWl0IHRoaXMubG9hZE9yQ3JlYXRlKGF3YWl0IGFwaS53b3Jrc3BhY2VTY29wZSgpLCBvd25lcik7XG4gICAgY29uc3Qga2V5cyA9IGF3YWl0IGFwaS5saXN0S2V5cyhzaWduYWwpO1xuICAgIGNvbnN0IGV4aXN0aW5nID0ga2V5cy5maW5kKChlbnRyeSkgPT4gZW50cnkua2V5SGFzaCA9PT0ga2V5LmtleUhhc2gpO1xuICAgIGlmIChleGlzdGluZykge1xuICAgICAgcmV0dXJuIHsgLi4ua2V5LCAuLi4oZXhpc3RpbmcuZ2F0ZXdheUhvc3QgPyB7IGdhdGV3YXlIb3N0OiBleGlzdGluZy5nYXRld2F5SG9zdCB9IDoge30pIH07XG4gICAgfVxuICAgIGNvbnN0IHJlZ2lzdGVyZWQgPSBhd2FpdCBhcGkucmVnaXN0ZXJLZXkoXG4gICAgICBrZXkucHVibGljS2V5LFxuICAgICAgYGRieC10b29scy1sYWtlYm94OiR7a2V5LmtleUhhc2h9YCxcbiAgICAgIHNpZ25hbCxcbiAgICApO1xuICAgIHJldHVybiB7XG4gICAgICAuLi5rZXksXG4gICAgICAuLi4ocmVnaXN0ZXJlZC5nYXRld2F5SG9zdCA/IHsgZ2F0ZXdheUhvc3Q6IHJlZ2lzdGVyZWQuZ2F0ZXdheUhvc3QgfSA6IHt9KSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIFJldHVybnMgdGhlIHN0b3JlZCBTSEEtMjU2IGdhdGV3YXkgZmluZ2VycHJpbnQsIG9yIGB1bmRlZmluZWRgIHdoZW4gdW50cnVzdGVkLiAqL1xuICBhc3luYyBrbm93bkhvc3QoXG4gICAgc2NvcGU6IHN0cmluZyxcbiAgICBvd25lcjogc3RyaW5nLFxuICAgIGhvc3Q6IHN0cmluZyxcbiAgICBwb3J0OiBudW1iZXIsXG4gICk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHZhbHVlID0gYXdhaXQgcmVhZEZpbGUodGhpcy5ob3N0RmluZ2VycHJpbnRQYXRoKHNjb3BlLCBvd25lciwgaG9zdCwgcG9ydCksIFwidXRmOFwiKTtcbiAgICAgIHJldHVybiB2YWx1ZS50cmltKCkgfHwgdW5kZWZpbmVkO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICAvKiogUmV0dXJucyB0aGUgT3BlblNTSCBrbm93bi1ob3N0IGZpbGUgcGF0aCBmb3Igb25lIHdvcmtzcGFjZSwgdXNlciwgYW5kIGdhdGV3YXkuICovXG4gIGtub3duSG9zdHNQYXRoKHNjb3BlOiBzdHJpbmcsIG93bmVyOiBzdHJpbmcsIGhvc3Q6IHN0cmluZywgcG9ydDogbnVtYmVyKTogc3RyaW5nIHtcbiAgICByZXR1cm4gam9pbih0aGlzLmRpcmVjdG9yeShzY29wZSwgb3duZXIpLCBgaG9zdC0ke2RpZ2VzdChgJHtob3N0fVxcMCR7cG9ydH1gKX0ua25vd25faG9zdHNgKTtcbiAgfVxuXG4gIC8qKiBTdG9yZXMgdGhlIGFjY2VwdGVkIGdhdGV3YXkgZmluZ2VycHJpbnQgYW5kIE9wZW5TU0ggaG9zdC1rZXkgZW50cnkuICovXG4gIGFzeW5jIHJlbWVtYmVySG9zdChcbiAgICBzY29wZTogc3RyaW5nLFxuICAgIG93bmVyOiBzdHJpbmcsXG4gICAgaG9zdDogc3RyaW5nLFxuICAgIHBvcnQ6IG51bWJlcixcbiAgICBmaW5nZXJwcmludDogc3RyaW5nLFxuICAgIGtleTogQnVmZmVyLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBkaXJlY3RvcnkgPSB0aGlzLmRpcmVjdG9yeShzY29wZSwgb3duZXIpO1xuICAgIGF3YWl0IG1rZGlyKGRpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIG1vZGU6IDBvNzAwIH0pO1xuICAgIGNvbnN0IGZpbmdlcnByaW50UGF0aCA9IHRoaXMuaG9zdEZpbmdlcnByaW50UGF0aChzY29wZSwgb3duZXIsIGhvc3QsIHBvcnQpO1xuICAgIGNvbnN0IGtub3duSG9zdHNQYXRoID0gdGhpcy5rbm93bkhvc3RzUGF0aChzY29wZSwgb3duZXIsIGhvc3QsIHBvcnQpO1xuICAgIGNvbnN0IGtub3duSG9zdCA9IGBbJHtob3N0fV06JHtwb3J0fSAke3NzaEtleUFsZ29yaXRobShrZXkpfSAke2tleS50b1N0cmluZyhcImJhc2U2NFwiKX1cXG5gO1xuICAgIGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIHdyaXRlRmlsZShmaW5nZXJwcmludFBhdGgsIGAke2ZpbmdlcnByaW50fVxcbmAsIHsgbW9kZTogMG82MDAgfSksXG4gICAgICB3cml0ZUZpbGUoa25vd25Ib3N0c1BhdGgsIGtub3duSG9zdCwgeyBtb2RlOiAwbzYwMCB9KSxcbiAgICBdKTtcbiAgICBhd2FpdCBQcm9taXNlLmFsbChbY2htb2QoZmluZ2VycHJpbnRQYXRoLCAwbzYwMCksIGNobW9kKGtub3duSG9zdHNQYXRoLCAwbzYwMCldKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZE9yQ3JlYXRlKHNjb3BlOiBzdHJpbmcsIG93bmVyOiBzdHJpbmcpOiBQcm9taXNlPFN0b3JlZEtleT4ge1xuICAgIGNvbnN0IGRpcmVjdG9yeSA9IHRoaXMuZGlyZWN0b3J5KHNjb3BlLCBvd25lcik7XG4gICAgY29uc3QgcHJpdmF0ZVBhdGggPSBqb2luKGRpcmVjdG9yeSwgXCJpZF9lZDI1NTE5XCIpO1xuICAgIGNvbnN0IHB1YmxpY1BhdGggPSBgJHtwcml2YXRlUGF0aH0ucHViYDtcbiAgICBjb25zdCBzdG9yZWQgPSBhd2FpdCByZWFkU3RvcmVkS2V5KHByaXZhdGVQYXRoLCBwdWJsaWNQYXRoKTtcbiAgICBpZiAoc3RvcmVkKSByZXR1cm4gc3RvcmVkO1xuICAgIGF3YWl0IG1rZGlyKGRpcmVjdG9yeSwgeyByZWN1cnNpdmU6IHRydWUsIG1vZGU6IDBvNzAwIH0pO1xuICAgIHJldHVybiBmaWxlTG9jay53aXRoRmlsZUxvY2soW1wibGFrZWJveC1zc2gta2V5XCIsIHByaXZhdGVQYXRoXSwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgcmVjaGVja2VkID0gYXdhaXQgcmVhZFN0b3JlZEtleShwcml2YXRlUGF0aCwgcHVibGljUGF0aCk7XG4gICAgICBpZiAocmVjaGVja2VkKSByZXR1cm4gcmVjaGVja2VkO1xuXG4gICAgICBjb25zdCBnZW5lcmF0ZWQgPSB1dGlscy5nZW5lcmF0ZUtleVBhaXJTeW5jKFwiZWQyNTUxOVwiKTtcbiAgICAgIGNvbnN0IHByaXZhdGVLZXkgPSBnZW5lcmF0ZWQucHJpdmF0ZTtcbiAgICAgIGNvbnN0IHB1YmxpY0tleSA9IGdlbmVyYXRlZC5wdWJsaWM7XG4gICAgICBjb25zdCBzdWZmaXggPSBgJHtwcm9jZXNzLnBpZH0tJHtyYW5kb21CeXRlcyg4KS50b1N0cmluZyhcImhleFwiKX1gO1xuICAgICAgY29uc3Qgc3RhZ2VkUHJpdmF0ZSA9IGAke3ByaXZhdGVQYXRofS4ke3N1ZmZpeH1gO1xuICAgICAgY29uc3Qgc3RhZ2VkUHVibGljID0gYCR7cHVibGljUGF0aH0uJHtzdWZmaXh9YDtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHdyaXRlRmlsZShzdGFnZWRQcml2YXRlLCBwcml2YXRlS2V5LCB7IG1vZGU6IDBvNjAwIH0pO1xuICAgICAgICBhd2FpdCB3cml0ZUZpbGUoc3RhZ2VkUHVibGljLCBgJHtwdWJsaWNLZXkudHJpbSgpfVxcbmAsIHsgbW9kZTogMG82MDAgfSk7XG4gICAgICAgIGF3YWl0IHJlbmFtZShzdGFnZWRQcml2YXRlLCBwcml2YXRlUGF0aCk7XG4gICAgICAgIGF3YWl0IHJlbmFtZShzdGFnZWRQdWJsaWMsIHB1YmxpY1BhdGgpO1xuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbY2htb2QocHJpdmF0ZVBhdGgsIDBvNjAwKSwgY2htb2QocHVibGljUGF0aCwgMG82MDApXSk7XG4gICAgICAgIGNvbnN0IGNyZWF0ZWQgPSBhd2FpdCByZWFkU3RvcmVkS2V5KHByaXZhdGVQYXRoLCBwdWJsaWNQYXRoKTtcbiAgICAgICAgaWYgKCFjcmVhdGVkKSB0aHJvdyBuZXcgRXJyb3IoXCJTU0gga2V5IGZpbGVzIHdlcmUgbm90IHB1Ymxpc2hlZCBhdG9taWNhbGx5XCIpO1xuICAgICAgICByZXR1cm4gY3JlYXRlZDtcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtybShzdGFnZWRQcml2YXRlLCB7IGZvcmNlOiB0cnVlIH0pLCBybShzdGFnZWRQdWJsaWMsIHsgZm9yY2U6IHRydWUgfSldKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgaG9zdEZpbmdlcnByaW50UGF0aChzY29wZTogc3RyaW5nLCBvd25lcjogc3RyaW5nLCBob3N0OiBzdHJpbmcsIHBvcnQ6IG51bWJlcik6IHN0cmluZyB7XG4gICAgcmV0dXJuIGpvaW4odGhpcy5kaXJlY3Rvcnkoc2NvcGUsIG93bmVyKSwgYGhvc3QtJHtkaWdlc3QoYCR7aG9zdH1cXDAke3BvcnR9YCl9LnNoYTI1NmApO1xuICB9XG5cbiAgcHJpdmF0ZSBkaXJlY3Rvcnkoc2NvcGU6IHN0cmluZywgb3duZXI6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGpvaW4odGhpcy5yb290LCBkaWdlc3QoYCR7c2NvcGV9XFwwJHtvd25lcn1gKSk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVhZFN0b3JlZEtleShcbiAgcHJpdmF0ZVBhdGg6IHN0cmluZyxcbiAgcHVibGljUGF0aDogc3RyaW5nLFxuKTogUHJvbWlzZTxTdG9yZWRLZXkgfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBbcHJpdmF0ZUtleSwgcHVibGljS2V5XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIHJlYWRGaWxlKHByaXZhdGVQYXRoLCBcInV0ZjhcIiksXG4gICAgICByZWFkRmlsZShwdWJsaWNQYXRoLCBcInV0ZjhcIiksXG4gICAgXSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHByaXZhdGVLZXksXG4gICAgICBwcml2YXRlS2V5UGF0aDogcHJpdmF0ZVBhdGgsXG4gICAgICBwdWJsaWNLZXksXG4gICAgICBrZXlIYXNoOiBwdWJsaWNLZXlIYXNoKHB1YmxpY0tleSksXG4gICAgfTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufVxuXG5mdW5jdGlvbiBwdWJsaWNLZXlIYXNoKHB1YmxpY0tleTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3Qgbm9ybWFsaXplZCA9IHB1YmxpY0tleS50cmltKCkuc3BsaXQoL1xccysvKS5zbGljZSgwLCAyKS5qb2luKFwiIFwiKTtcbiAgcmV0dXJuIGNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKG5vcm1hbGl6ZWQpLmRpZ2VzdCgpLnN1YmFycmF5KDAsIDE2KS50b1N0cmluZyhcImhleFwiKTtcbn1cblxuZnVuY3Rpb24gZGlnZXN0KHZhbHVlOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUodmFsdWUpLmRpZ2VzdChcImhleFwiKS5zbGljZSgwLCAzMik7XG59XG5cbmZ1bmN0aW9uIHNzaEtleUFsZ29yaXRobShrZXk6IEJ1ZmZlcik6IHN0cmluZyB7XG4gIGlmIChrZXkubGVuZ3RoIDwgNSkgdGhyb3cgbmV3IEVycm9yKFwiU1NIIGdhdGV3YXkgcmV0dXJuZWQgYW4gaW52YWxpZCBob3N0IGtleVwiKTtcbiAgY29uc3QgbGVuZ3RoID0ga2V5LnJlYWRVSW50MzJCRSgwKTtcbiAgY29uc3QgZW5kID0gNCArIGxlbmd0aDtcbiAgaWYgKGxlbmd0aCA9PT0gMCB8fCBlbmQgPiBrZXkubGVuZ3RoKSB0aHJvdyBuZXcgRXJyb3IoXCJTU0ggZ2F0ZXdheSByZXR1cm5lZCBhbiBpbnZhbGlkIGhvc3Qga2V5XCIpO1xuICBjb25zdCBhbGdvcml0aG0gPSBrZXkuc3ViYXJyYXkoNCwgZW5kKS50b1N0cmluZyhcImFzY2lpXCIpO1xuICBpZiAoL1xccy8udGVzdChhbGdvcml0aG0pKSB0aHJvdyBuZXcgRXJyb3IoXCJTU0ggZ2F0ZXdheSByZXR1cm5lZCBhbiBpbnZhbGlkIGhvc3Qga2V5IGFsZ29yaXRobVwiKTtcbiAgcmV0dXJuIGFsZ29yaXRobTtcbn1cbiJdfQ==
@@ -0,0 +1,18 @@
1
+ import type { LakeboxStatus } from "@dbx-tools/shared-lakebox";
2
+ import type { LakeboxClient, LakeboxEntry } from "./rest.ts";
3
+ /** Maps an upstream Lakebox status to the shared lifecycle state. */
4
+ export declare function normalizeLakeboxStatus(value: string): Exclude<LakeboxStatus, "missing">;
5
+ /** Parses an upstream duration such as `600s` into seconds. */
6
+ export declare function lakeboxIdleTimeoutSeconds(value: string | undefined): number | undefined;
7
+ /**
8
+ * Starts a Lakebox when needed and waits until it is running.
9
+ *
10
+ * @throws When the Lakebox does not reach running within five minutes.
11
+ */
12
+ export declare function ensureLakeboxRunning(api: LakeboxClient, current: LakeboxEntry, signal?: AbortSignal): Promise<LakeboxEntry>;
13
+ /**
14
+ * Stops a Lakebox when needed and waits until it is stopped.
15
+ *
16
+ * @throws When the Lakebox does not reach stopped within five minutes.
17
+ */
18
+ export declare function ensureLakeboxStopped(api: LakeboxClient, current: LakeboxEntry, signal?: AbortSignal): Promise<LakeboxEntry>;
@@ -0,0 +1,75 @@
1
+ import { asyncUtils } from "@dbx-tools/shared-core";
2
+ const POLL_INTERVAL_MS = 1_000;
3
+ const LIFECYCLE_TIMEOUT_MS = 300_000;
4
+ /** Maps an upstream Lakebox status to the shared lifecycle state. */
5
+ export function normalizeLakeboxStatus(value) {
6
+ switch (value.trim().toLowerCase()) {
7
+ case "starting":
8
+ case "provisioning":
9
+ case "pending":
10
+ return "starting";
11
+ case "running":
12
+ return "running";
13
+ case "stopping":
14
+ return "stopping";
15
+ case "stopped":
16
+ return "stopped";
17
+ case "failed":
18
+ case "error":
19
+ return "error";
20
+ default:
21
+ return "unknown";
22
+ }
23
+ }
24
+ /** Parses an upstream duration such as `600s` into seconds. */
25
+ export function lakeboxIdleTimeoutSeconds(value) {
26
+ if (!value?.endsWith("s"))
27
+ return undefined;
28
+ const seconds = Number(value.slice(0, -1));
29
+ return Number.isFinite(seconds) && seconds > 0 ? seconds : undefined;
30
+ }
31
+ /**
32
+ * Starts a Lakebox when needed and waits until it is running.
33
+ *
34
+ * @throws When the Lakebox does not reach running within five minutes.
35
+ */
36
+ export async function ensureLakeboxRunning(api, current, signal) {
37
+ let entry = current;
38
+ const status = normalizeLakeboxStatus(entry.status);
39
+ if (status === "running")
40
+ return entry;
41
+ if (status === "starting")
42
+ return waitForLakeboxStatus(api, entry, "running", signal);
43
+ if (status === "stopping")
44
+ entry = await waitForLakeboxStatus(api, entry, "stopped", signal);
45
+ return waitForLakeboxStatus(api, await api.start(entry.sandboxId, signal), "running", signal);
46
+ }
47
+ /**
48
+ * Stops a Lakebox when needed and waits until it is stopped.
49
+ *
50
+ * @throws When the Lakebox does not reach stopped within five minutes.
51
+ */
52
+ export async function ensureLakeboxStopped(api, current, signal) {
53
+ let entry = current;
54
+ const status = normalizeLakeboxStatus(entry.status);
55
+ if (status === "stopped")
56
+ return entry;
57
+ if (status === "stopping")
58
+ return waitForLakeboxStatus(api, entry, "stopped", signal);
59
+ if (status === "starting")
60
+ entry = await waitForLakeboxStatus(api, entry, "running", signal);
61
+ return waitForLakeboxStatus(api, await api.stop(entry.sandboxId, signal), "stopped", signal);
62
+ }
63
+ async function waitForLakeboxStatus(api, current, expected, signal) {
64
+ const deadline = Date.now() + LIFECYCLE_TIMEOUT_MS;
65
+ let entry = current;
66
+ while (normalizeLakeboxStatus(entry.status) !== expected) {
67
+ if (Date.now() >= deadline) {
68
+ throw new Error(`Timed out waiting for Lakebox ${entry.sandboxId} to become ${expected}`);
69
+ }
70
+ await asyncUtils.sleep(POLL_INTERVAL_MS, signal);
71
+ entry = await api.get(entry.sandboxId, signal);
72
+ }
73
+ return entry;
74
+ }
75
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlmZWN5Y2xlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpZmVjeWNsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFJcEQsTUFBTSxnQkFBZ0IsR0FBRyxLQUFLLENBQUM7QUFDL0IsTUFBTSxvQkFBb0IsR0FBRyxPQUFPLENBQUM7QUFFckMscUVBQXFFO0FBQ3JFLE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxLQUFhO0lBQ2xELFFBQVEsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7UUFDbkMsS0FBSyxVQUFVLENBQUM7UUFDaEIsS0FBSyxjQUFjLENBQUM7UUFDcEIsS0FBSyxTQUFTO1lBQ1osT0FBTyxVQUFVLENBQUM7UUFDcEIsS0FBSyxTQUFTO1lBQ1osT0FBTyxTQUFTLENBQUM7UUFDbkIsS0FBSyxVQUFVO1lBQ2IsT0FBTyxVQUFVLENBQUM7UUFDcEIsS0FBSyxTQUFTO1lBQ1osT0FBTyxTQUFTLENBQUM7UUFDbkIsS0FBSyxRQUFRLENBQUM7UUFDZCxLQUFLLE9BQU87WUFDVixPQUFPLE9BQU8sQ0FBQztRQUNqQjtZQUNFLE9BQU8sU0FBUyxDQUFDO0lBQ3JCLENBQUM7QUFDSCxDQUFDO0FBRUQsK0RBQStEO0FBQy9ELE1BQU0sVUFBVSx5QkFBeUIsQ0FBQyxLQUF5QjtJQUNqRSxJQUFJLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxHQUFHLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1QyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNDLE9BQU8sTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUN2RSxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsb0JBQW9CLENBQ3hDLEdBQWtCLEVBQ2xCLE9BQXFCLEVBQ3JCLE1BQW9CO0lBRXBCLElBQUksS0FBSyxHQUFHLE9BQU8sQ0FBQztJQUNwQixNQUFNLE1BQU0sR0FBRyxzQkFBc0IsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDcEQsSUFBSSxNQUFNLEtBQUssU0FBUztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLElBQUksTUFBTSxLQUFLLFVBQVU7UUFBRSxPQUFPLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3RGLElBQUksTUFBTSxLQUFLLFVBQVU7UUFBRSxLQUFLLEdBQUcsTUFBTSxvQkFBb0IsQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM3RixPQUFPLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDaEcsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxHQUFrQixFQUNsQixPQUFxQixFQUNyQixNQUFvQjtJQUVwQixJQUFJLEtBQUssR0FBRyxPQUFPLENBQUM7SUFDcEIsTUFBTSxNQUFNLEdBQUcsc0JBQXNCLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3BELElBQUksTUFBTSxLQUFLLFNBQVM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUN2QyxJQUFJLE1BQU0sS0FBSyxVQUFVO1FBQUUsT0FBTyxvQkFBb0IsQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUN0RixJQUFJLE1BQU0sS0FBSyxVQUFVO1FBQUUsS0FBSyxHQUFHLE1BQU0sb0JBQW9CLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDN0YsT0FBTyxvQkFBb0IsQ0FBQyxHQUFHLEVBQUUsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQy9GLENBQUM7QUFFRCxLQUFLLFVBQVUsb0JBQW9CLENBQ2pDLEdBQWtCLEVBQ2xCLE9BQXFCLEVBQ3JCLFFBQStCLEVBQy9CLE1BQW9CO0lBRXBCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxvQkFBb0IsQ0FBQztJQUNuRCxJQUFJLEtBQUssR0FBRyxPQUFPLENBQUM7SUFDcEIsT0FBTyxzQkFBc0IsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDekQsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksUUFBUSxFQUFFLENBQUM7WUFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsS0FBSyxDQUFDLFNBQVMsY0FBYyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzVGLENBQUM7UUFDRCxNQUFNLFVBQVUsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDakQsS0FBSyxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3luY1V0aWxzIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB0eXBlIHsgTGFrZWJveFN0YXR1cyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1sYWtlYm94XCI7XG5pbXBvcnQgdHlwZSB7IExha2Vib3hDbGllbnQsIExha2Vib3hFbnRyeSB9IGZyb20gXCIuL3Jlc3QudHNcIjtcblxuY29uc3QgUE9MTF9JTlRFUlZBTF9NUyA9IDFfMDAwO1xuY29uc3QgTElGRUNZQ0xFX1RJTUVPVVRfTVMgPSAzMDBfMDAwO1xuXG4vKiogTWFwcyBhbiB1cHN0cmVhbSBMYWtlYm94IHN0YXR1cyB0byB0aGUgc2hhcmVkIGxpZmVjeWNsZSBzdGF0ZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVMYWtlYm94U3RhdHVzKHZhbHVlOiBzdHJpbmcpOiBFeGNsdWRlPExha2Vib3hTdGF0dXMsIFwibWlzc2luZ1wiPiB7XG4gIHN3aXRjaCAodmFsdWUudHJpbSgpLnRvTG93ZXJDYXNlKCkpIHtcbiAgICBjYXNlIFwic3RhcnRpbmdcIjpcbiAgICBjYXNlIFwicHJvdmlzaW9uaW5nXCI6XG4gICAgY2FzZSBcInBlbmRpbmdcIjpcbiAgICAgIHJldHVybiBcInN0YXJ0aW5nXCI7XG4gICAgY2FzZSBcInJ1bm5pbmdcIjpcbiAgICAgIHJldHVybiBcInJ1bm5pbmdcIjtcbiAgICBjYXNlIFwic3RvcHBpbmdcIjpcbiAgICAgIHJldHVybiBcInN0b3BwaW5nXCI7XG4gICAgY2FzZSBcInN0b3BwZWRcIjpcbiAgICAgIHJldHVybiBcInN0b3BwZWRcIjtcbiAgICBjYXNlIFwiZmFpbGVkXCI6XG4gICAgY2FzZSBcImVycm9yXCI6XG4gICAgICByZXR1cm4gXCJlcnJvclwiO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gXCJ1bmtub3duXCI7XG4gIH1cbn1cblxuLyoqIFBhcnNlcyBhbiB1cHN0cmVhbSBkdXJhdGlvbiBzdWNoIGFzIGA2MDBzYCBpbnRvIHNlY29uZHMuICovXG5leHBvcnQgZnVuY3Rpb24gbGFrZWJveElkbGVUaW1lb3V0U2Vjb25kcyh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCF2YWx1ZT8uZW5kc1dpdGgoXCJzXCIpKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBzZWNvbmRzID0gTnVtYmVyKHZhbHVlLnNsaWNlKDAsIC0xKSk7XG4gIHJldHVybiBOdW1iZXIuaXNGaW5pdGUoc2Vjb25kcykgJiYgc2Vjb25kcyA+IDAgPyBzZWNvbmRzIDogdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFN0YXJ0cyBhIExha2Vib3ggd2hlbiBuZWVkZWQgYW5kIHdhaXRzIHVudGlsIGl0IGlzIHJ1bm5pbmcuXG4gKlxuICogQHRocm93cyBXaGVuIHRoZSBMYWtlYm94IGRvZXMgbm90IHJlYWNoIHJ1bm5pbmcgd2l0aGluIGZpdmUgbWludXRlcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGVuc3VyZUxha2Vib3hSdW5uaW5nKFxuICBhcGk6IExha2Vib3hDbGllbnQsXG4gIGN1cnJlbnQ6IExha2Vib3hFbnRyeSxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPExha2Vib3hFbnRyeT4ge1xuICBsZXQgZW50cnkgPSBjdXJyZW50O1xuICBjb25zdCBzdGF0dXMgPSBub3JtYWxpemVMYWtlYm94U3RhdHVzKGVudHJ5LnN0YXR1cyk7XG4gIGlmIChzdGF0dXMgPT09IFwicnVubmluZ1wiKSByZXR1cm4gZW50cnk7XG4gIGlmIChzdGF0dXMgPT09IFwic3RhcnRpbmdcIikgcmV0dXJuIHdhaXRGb3JMYWtlYm94U3RhdHVzKGFwaSwgZW50cnksIFwicnVubmluZ1wiLCBzaWduYWwpO1xuICBpZiAoc3RhdHVzID09PSBcInN0b3BwaW5nXCIpIGVudHJ5ID0gYXdhaXQgd2FpdEZvckxha2Vib3hTdGF0dXMoYXBpLCBlbnRyeSwgXCJzdG9wcGVkXCIsIHNpZ25hbCk7XG4gIHJldHVybiB3YWl0Rm9yTGFrZWJveFN0YXR1cyhhcGksIGF3YWl0IGFwaS5zdGFydChlbnRyeS5zYW5kYm94SWQsIHNpZ25hbCksIFwicnVubmluZ1wiLCBzaWduYWwpO1xufVxuXG4vKipcbiAqIFN0b3BzIGEgTGFrZWJveCB3aGVuIG5lZWRlZCBhbmQgd2FpdHMgdW50aWwgaXQgaXMgc3RvcHBlZC5cbiAqXG4gKiBAdGhyb3dzIFdoZW4gdGhlIExha2Vib3ggZG9lcyBub3QgcmVhY2ggc3RvcHBlZCB3aXRoaW4gZml2ZSBtaW51dGVzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZW5zdXJlTGFrZWJveFN0b3BwZWQoXG4gIGFwaTogTGFrZWJveENsaWVudCxcbiAgY3VycmVudDogTGFrZWJveEVudHJ5LFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8TGFrZWJveEVudHJ5PiB7XG4gIGxldCBlbnRyeSA9IGN1cnJlbnQ7XG4gIGNvbnN0IHN0YXR1cyA9IG5vcm1hbGl6ZUxha2Vib3hTdGF0dXMoZW50cnkuc3RhdHVzKTtcbiAgaWYgKHN0YXR1cyA9PT0gXCJzdG9wcGVkXCIpIHJldHVybiBlbnRyeTtcbiAgaWYgKHN0YXR1cyA9PT0gXCJzdG9wcGluZ1wiKSByZXR1cm4gd2FpdEZvckxha2Vib3hTdGF0dXMoYXBpLCBlbnRyeSwgXCJzdG9wcGVkXCIsIHNpZ25hbCk7XG4gIGlmIChzdGF0dXMgPT09IFwic3RhcnRpbmdcIikgZW50cnkgPSBhd2FpdCB3YWl0Rm9yTGFrZWJveFN0YXR1cyhhcGksIGVudHJ5LCBcInJ1bm5pbmdcIiwgc2lnbmFsKTtcbiAgcmV0dXJuIHdhaXRGb3JMYWtlYm94U3RhdHVzKGFwaSwgYXdhaXQgYXBpLnN0b3AoZW50cnkuc2FuZGJveElkLCBzaWduYWwpLCBcInN0b3BwZWRcIiwgc2lnbmFsKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gd2FpdEZvckxha2Vib3hTdGF0dXMoXG4gIGFwaTogTGFrZWJveENsaWVudCxcbiAgY3VycmVudDogTGFrZWJveEVudHJ5LFxuICBleHBlY3RlZDogXCJydW5uaW5nXCIgfCBcInN0b3BwZWRcIixcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPExha2Vib3hFbnRyeT4ge1xuICBjb25zdCBkZWFkbGluZSA9IERhdGUubm93KCkgKyBMSUZFQ1lDTEVfVElNRU9VVF9NUztcbiAgbGV0IGVudHJ5ID0gY3VycmVudDtcbiAgd2hpbGUgKG5vcm1hbGl6ZUxha2Vib3hTdGF0dXMoZW50cnkuc3RhdHVzKSAhPT0gZXhwZWN0ZWQpIHtcbiAgICBpZiAoRGF0ZS5ub3coKSA+PSBkZWFkbGluZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUaW1lZCBvdXQgd2FpdGluZyBmb3IgTGFrZWJveCAke2VudHJ5LnNhbmRib3hJZH0gdG8gYmVjb21lICR7ZXhwZWN0ZWR9YCk7XG4gICAgfVxuICAgIGF3YWl0IGFzeW5jVXRpbHMuc2xlZXAoUE9MTF9JTlRFUlZBTF9NUywgc2lnbmFsKTtcbiAgICBlbnRyeSA9IGF3YWl0IGFwaS5nZXQoZW50cnkuc2FuZGJveElkLCBzaWduYWwpO1xuICB9XG4gIHJldHVybiBlbnRyeTtcbn1cbiJdfQ==