@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.
- package/LICENSE +202 -0
- package/README.md +48 -2
- package/index.ts +18 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +15 -0
- package/lib/src/_ssh.d.ts +152 -0
- package/lib/src/_ssh.js +489 -0
- package/lib/src/_ssh_openssh.d.ts +18 -0
- package/lib/src/_ssh_openssh.js +382 -0
- package/lib/src/_ssh_ssh2.d.ts +16 -0
- package/lib/src/_ssh_ssh2.js +225 -0
- package/lib/src/key-store.d.ts +39 -0
- package/lib/src/key-store.js +145 -0
- package/lib/src/lifecycle.d.ts +18 -0
- package/lib/src/lifecycle.js +75 -0
- package/lib/src/rest.d.ts +81 -0
- package/lib/src/rest.js +199 -0
- package/lib/src/session.d.ts +89 -0
- package/lib/src/session.js +309 -0
- package/lib/src/transport.d.ts +15 -0
- package/lib/src/transport.js +16 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +117 -4
- package/src/_ssh.ts +610 -0
- package/src/_ssh_openssh.ts +471 -0
- package/src/_ssh_ssh2.ts +273 -0
- package/src/key-store.ts +177 -0
- package/src/lifecycle.ts +88 -0
- package/src/rest.ts +264 -0
- package/src/session.ts +371 -0
- package/src/transport.ts +25 -0
|
@@ -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==
|