sliftutils 1.7.133 → 1.7.134

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.
Files changed (58) hide show
  1. package/bin/addmachine.js +1 -5
  2. package/bin/derivekey.js +2 -7
  3. package/bin/portsecuredaemon.js +1 -8
  4. package/bin/securessh.js +2 -7
  5. package/bin/setupnotify.js +2 -6
  6. package/bin/signfiles.js +2 -7
  7. package/bin/testnotify.js +2 -6
  8. package/bin/unrevoke.js +1 -5
  9. package/misc/dist/ownIPs.ts.cache +2 -2
  10. package/misc/https/dist/certs.ts.cache +12 -9
  11. package/misc/https/dist/persistentLocalStorage.ts.cache +10 -4
  12. package/package.json +1 -1
  13. package/security/authorizedKeys/daemon/daemon.ts +142 -7
  14. package/security/authorizedKeys/daemon/dist/changes.ts.cache +2 -2
  15. package/security/authorizedKeys/daemon/dist/git.ts.cache +26 -8
  16. package/security/authorizedKeys/daemon/dist/notify.ts.cache +2 -2
  17. package/security/authorizedKeys/daemon/dist/paths.ts.cache +2 -2
  18. package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +4 -6
  19. package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
  20. package/security/authorizedKeys/daemon/dist/sessions.ts.cache +2 -2
  21. package/security/authorizedKeys/daemon/dist/state.ts.cache +2 -2
  22. package/security/authorizedKeys/daemon/dist/trust.ts.cache +85 -265
  23. package/security/authorizedKeys/daemon/git.d.ts +8 -0
  24. package/security/authorizedKeys/daemon/git.ts +22 -4
  25. package/security/authorizedKeys/daemon/repoFiles.ts +2 -4
  26. package/security/authorizedKeys/daemon/trust.d.ts +25 -19
  27. package/security/authorizedKeys/daemon/trust.ts +91 -278
  28. package/security/authorizedKeys/dist/authorizedKeys.ts.cache +2 -2
  29. package/security/authorizedKeys/dist/keysRepo.ts.cache +2 -2
  30. package/security/authorizedKeys/dist/revokeSource.ts.cache +2 -2
  31. package/security/authorizedKeys/dist/sources.ts.cache +2 -2
  32. package/security/authorizedKeys/secureSSH.ts +6 -1
  33. package/security/authorizedKeys/unrevoke.ts +2 -198
  34. package/security/authorizedKeys/unrevokeCli.ts +204 -0
  35. package/security/helpers/dist/paths.ts.cache +2 -2
  36. package/security/helpers/dist/remoteSSH.ts.cache +2 -2
  37. package/security/helpers/dist/spawn.ts.cache +2 -2
  38. package/security/helpers/remoteSSH.d.ts +35 -0
  39. package/security/keys/deriveKey.d.ts +0 -1
  40. package/security/keys/deriveKey.ts +1 -56
  41. package/security/keys/deriveKeyCli.ts +62 -0
  42. package/security/keys/dist/deriveKey.ts.cache +4 -51
  43. package/security/keys/dist/sshKeyFile.ts.cache +5 -3
  44. package/security/machines/addMachine.ts +31 -61
  45. package/security/machines/dist/addMachine.ts.cache +72 -57
  46. package/security/machines/dist/machines.ts.cache +134 -55
  47. package/security/machines/machines.d.ts +11 -0
  48. package/security/machines/machines.ts +134 -57
  49. package/security/notifications/dist/discord.ts.cache +2 -2
  50. package/security/notifications/setupNotify.ts +6 -1
  51. package/security/notifications/testNotify.ts +6 -1
  52. package/security/signedFiles/dist/manifest.ts.cache +2 -2
  53. package/security/signedFiles/dist/sshsig.ts.cache +138 -0
  54. package/security/signedFiles/signFiles.ts +11 -52
  55. package/security/signedFiles/signFilesCli.ts +54 -0
  56. package/security/signedFiles/sshsig.d.ts +13 -0
  57. package/security/signedFiles/sshsig.ts +141 -0
  58. package/storage/dist/backblaze.ts.cache +10 -2
@@ -1,29 +1,13 @@
1
1
  import fs from "fs/promises";
2
2
  import os from "os";
3
3
  import path from "path";
4
- import { runPromise } from "socket-function/src/runPromise";
5
- import { allowAddresses, keyFingerprint, normalizeKeys, summarizeKey } from "./authorizedKeys";
4
+ import { keyFingerprint, normalizeKeys } from "./authorizedKeys";
6
5
  import { deriveRevokeKey, revokeRepoURL } from "./revokeSource";
7
6
  import { findSourceKey } from "./sources";
8
- import { signRepo } from "../signedFiles/signFiles";
9
- import { readRepoKeys } from "./authorizedKeys";
10
- import { expandHome } from "../helpers/paths";
11
7
  import { spawnPromise } from "../helpers/spawn";
12
- import { getMachines, setMachines } from "../machines/machines";
13
- import { resolveKeysRepo } from "./keysRepo";
14
8
 
15
- const UNREVOKES_DIR = "unrevoked";
9
+ export const UNREVOKES_DIR = "unrevoked";
16
10
  const REVOCATIONS_DIR = "revocations";
17
- const GIT_KEYWORD = "git";
18
- const COMMIT_MESSAGE = "unrevoke keys";
19
- const USAGE = `Usage: yarn unrevoke [keys-repo] [${GIT_KEYWORD}]
20
-
21
- Run this in a keys repo, or name one. It reads that repo's revoke repo and writes one unrevoke file
22
- naming every revocation in it, so the keys are accepted again as each machine picks it up. It signs
23
- the result, and with ${GIT_KEYWORD} it commits and pushes it too.
24
-
25
- Keys that were revoked should normally be deleted from the repo instead. Unrevoking only matters
26
- for a key you still want.`;
27
11
 
28
12
  /** One revocation event. Either an ssh key used from an address it is not allowed from, or a
29
13
  machine that talked to us from one. Both are the same shape - an identity and an address - and
@@ -126,183 +110,3 @@ export async function revokedKeysInRepo(config: { repoPath: string; sourceURL: s
126
110
  .filter(revocation => keys.some(key => keyFingerprint(key) === revocation.fingerprint))
127
111
  .map(revocation => ({ revocation, key: keys.find(key => keyFingerprint(key) === revocation.fingerprint) || "" }));
128
112
  }
129
-
130
- /** Writes the addresses into the from= list of each key in the repo's authorized_keys, and says
131
- which ones were actually new. Only the lines being changed are touched, so comments, ordering
132
- and any key not involved come out of this byte for byte the same. */
133
- async function allowAddressesInRepo(config: {
134
- repoPath: string;
135
- addressesByFingerprint: Map<string, string[]>;
136
- }) {
137
- let { repoPath, addressesByFingerprint } = config;
138
- let added = new Map<string, string[]>();
139
- let filePath = path.join(repoPath, "authorized_keys");
140
- let contents;
141
- try {
142
- contents = await fs.readFile(filePath, "utf8");
143
- } catch (e) {
144
- // A repo holding its keys as separate .pub files. Nothing to edit in one place, so the
145
- // caller's listing is all whoever ran this gets.
146
- console.log(`No authorized_keys in ${repoPath}, so the addresses were not added to any key.`);
147
- return added;
148
- }
149
- let lines = contents.split("\n");
150
- for (let index = 0; index < lines.length; index++) {
151
- let fingerprint = keyFingerprint(lines[index]);
152
- let addresses = fingerprint && addressesByFingerprint.get(fingerprint);
153
- if (!addresses || !addresses.length) {
154
- continue;
155
- }
156
- let result = allowAddresses(lines[index], addresses);
157
- lines[index] = result.keyLine;
158
- added.set(fingerprint, [...(added.get(fingerprint) || []), ...result.added]);
159
- }
160
- if ([...added.values()].some(list => list.length)) {
161
- await fs.writeFile(filePath, lines.join("\n"));
162
- }
163
- return added;
164
- }
165
-
166
- /** Puts each machine's addresses back into its own file, which is the machine equivalent of adding
167
- an address to a key's from= list: without it the machine is allowed again in principle and
168
- still rejected on sight, which revokes it once more. */
169
- async function allowAddressesForMachines(config: { repoPath: string; revocations: Revocation[] }) {
170
- let { repoPath, revocations } = config;
171
- let added = new Map<string, string[]>();
172
- // Read the whole list, change what these revocations are about, write it back. Setting is by
173
- // the whole set, so writing one machine at a time would delete every other machine.
174
- let machines = await getMachines(repoPath);
175
- for (let revocation of revocations) {
176
- let machineId = revocation.machineId;
177
- let ip = revocationIP(revocation);
178
- if (!machineId || !ip) {
179
- continue;
180
- }
181
- let existing = machines.find(machine => machine.machineId === machineId);
182
- if (!existing) {
183
- // Removed from the repo since it was revoked, so there is nothing to allow. Deleting a
184
- // machine is the stronger statement and it stands.
185
- console.log(`machines/${machineId}.json is not in this repo, so ${ip} was not added to it`);
186
- continue;
187
- }
188
- if (existing.ips.includes(ip)) {
189
- continue;
190
- }
191
- existing.ips = [...existing.ips, ip];
192
- added.set(machineId, [...(added.get(machineId) || []), ip]);
193
- }
194
- if (added.size) {
195
- await setMachines({ repoPath, machines });
196
- }
197
- return added;
198
- }
199
-
200
-
201
- export async function main() {
202
- let argv = process.argv.slice(2);
203
- let pushToGit = argv.includes(GIT_KEYWORD);
204
- let positional = argv.filter(arg => arg !== GIT_KEYWORD);
205
- if (positional.length > 1) {
206
- throw new Error(`Expected at most a keys repo, was ${positional.length} argument(s)\n${USAGE}`);
207
- }
208
- let { repoPath, sourceURL: originURL } = await resolveKeysRepo(positional[0]);
209
-
210
- let revocations = await readRemoteRevocations({ sourceURL: originURL });
211
- if (!revocations.length) {
212
- console.log(`${revokeRepoURL(originURL)} lists no revocations, so there is nothing to undo.`);
213
- return;
214
- }
215
-
216
- let directory = path.join(repoPath, UNREVOKES_DIR);
217
- await fs.mkdir(directory, { recursive: true });
218
- let stamp = new Date().toISOString().replace(/[:.]/g, "-");
219
- let unrevokeId = `${stamp}-unrevoke`;
220
- await fs.writeFile(path.join(directory, `${unrevokeId}.json`), JSON.stringify({
221
- unrevokeId,
222
- createdAt: new Date().toISOString(),
223
- // What this file says: each of these keys is allowed from this address. Pairs, not keys,
224
- // and not a blanket "allow everything again" - a key forgiven here for one address is
225
- // still revoked the moment it is used from another.
226
- allowed: revocations.map(revocation => ({
227
- fingerprint: revocation.fingerprint,
228
- machineId: revocation.machineId,
229
- ip: revocationIP(revocation),
230
- })),
231
- revocations: revocations.map(revocation => ({
232
- revocationId: revocation.revocationId,
233
- fingerprint: revocation.fingerprint,
234
- machineId: revocation.machineId,
235
- ip: revocationIP(revocation),
236
- revokedAt: revocation.revokedAt,
237
- revokedBy: revocation.revokedBy,
238
- attempt: revocation.attempt,
239
- })),
240
- }, undefined, 4) + "\n");
241
-
242
- // Named the way the notifications name them. A fingerprint is not in authorized_keys and
243
- // cannot be worked out by eye, so on its own it tells whoever is reading this nothing about
244
- // which of their keys is being talked about.
245
- let keysByFingerprint = new Map<string, string>();
246
- for (let key of await readRepoKeys(repoPath).catch(() => [] as string[])) {
247
- keysByFingerprint.set(keyFingerprint(key), key);
248
- }
249
- let describeKey = (revocation: Revocation) => {
250
- let key = keysByFingerprint.get(revocation.fingerprint || "") || revocation.key || "";
251
- return key && summarizeKey(key) || `a key no longer in this repo (${revocation.fingerprint})`;
252
- };
253
-
254
- let addressesByIdentity = new Map<string, string[]>();
255
- for (let revocation of revocations) {
256
- let ip = revocationIP(revocation);
257
- let identity = revocationIdentity(revocation);
258
- let existing = addressesByIdentity.get(identity) || [];
259
- if (ip && !existing.includes(ip)) {
260
- existing.push(ip);
261
- }
262
- addressesByIdentity.set(identity, existing);
263
- }
264
-
265
- // Cancelling the revocation on its own would leave sshd refusing that address, and the machines
266
- // revoking all over again, so the addresses go into the key's from= list and into the machine's
267
- // own file here. We are in the repo that owns both, so there is nothing to hand back to whoever
268
- // ran this.
269
- let added = await allowAddressesInRepo({ repoPath, addressesByFingerprint: addressesByIdentity });
270
- let addedForMachines = await allowAddressesForMachines({ repoPath, revocations });
271
-
272
- console.log("");
273
- for (let [identity, addresses] of addressesByIdentity) {
274
- let entries = revocations.filter(entry => revocationIdentity(entry) === identity);
275
- let machineId = entries[0].machineId;
276
- console.log(`Unfreezing ${machineId && `machine ${machineId}` || describeKey(entries[0])}`);
277
- console.log(`Allowing IP ${addresses.join(", ") || "(no address recorded)"}`);
278
- let addedHere = machineId && addedForMachines.get(machineId) || added.get(identity) || [];
279
- if (addedHere.length) {
280
- let where = machineId && `machines/${machineId}.json` || `that key's from= list in authorized_keys`;
281
- console.log(` added to ${where}: ${addedHere.join(", ")}`);
282
- } else {
283
- console.log(` already allowed from ${addresses.join(", ") || "everywhere"}`);
284
- }
285
- for (let entry of entries) {
286
- console.log(
287
- ` frozen by ${entry.revokedBy || "?"} ${entry.revokedAt || "at an unrecorded time"}`
288
- );
289
- }
290
- console.log("");
291
- }
292
- console.log(`Every other address stays frozen, and any of these used from anywhere else is`);
293
- console.log(`frozen again.`);
294
-
295
- // Signed here rather than left as a step to remember. An unrevoke nobody signed does nothing at
296
- // all, and the repo would sit there looking done while every machine ignored it.
297
- await signRepo({ repoPath });
298
-
299
- if (!pushToGit) {
300
- console.log(`\nCommit and push it, and those keys work again as each machine picks it up:`);
301
- console.log(`\`\`\`\ngit add -A\ngit commit -m "${COMMIT_MESSAGE}"\ngit push\n\`\`\``);
302
- return;
303
- }
304
- await runPromise(`git add -A`, { cwd: repoPath });
305
- await runPromise(`git commit -m "${COMMIT_MESSAGE}"`, { cwd: repoPath });
306
- await runPromise(`git push`, { cwd: repoPath });
307
- console.log(`\nCommitted and pushed. Those keys work again as each machine picks it up.`);
308
- }
@@ -0,0 +1,204 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import { runPromise } from "socket-function/src/runPromise";
4
+ import { allowAddresses, keyFingerprint, readRepoKeys, summarizeKey } from "./authorizedKeys";
5
+ import { revokeRepoURL } from "./revokeSource";
6
+ import { signRepo } from "../signedFiles/signFiles";
7
+ import { getMachines, setMachines } from "../machines/machines";
8
+ import { resolveKeysRepo } from "./keysRepo";
9
+ import { readRemoteRevocations, Revocation, revocationIdentity, revocationIP, UNREVOKES_DIR } from "./unrevoke";
10
+
11
+ const GIT_KEYWORD = "git";
12
+ const COMMIT_MESSAGE = "unrevoke keys";
13
+ const USAGE = `Usage: yarn unrevoke [keys-repo] [${GIT_KEYWORD}]
14
+
15
+ Run this in a keys repo, or name one. It reads that repo's revoke repo and writes one unrevoke file
16
+ naming every revocation in it, so the keys are accepted again as each machine picks it up. It signs
17
+ the result, and with ${GIT_KEYWORD} it commits and pushes it too.
18
+
19
+ Keys that were revoked should normally be deleted from the repo instead. Unrevoking only matters
20
+ for a key you still want.`;
21
+
22
+ /** Writes the addresses into the from= list of each key in the repo's authorized_keys, and says
23
+ which ones were actually new. Only the lines being changed are touched, so comments, ordering
24
+ and any key not involved come out of this byte for byte the same. */
25
+ async function allowAddressesInRepo(config: {
26
+ repoPath: string;
27
+ addressesByFingerprint: Map<string, string[]>;
28
+ }) {
29
+ let { repoPath, addressesByFingerprint } = config;
30
+ let added = new Map<string, string[]>();
31
+ let filePath = path.join(repoPath, "authorized_keys");
32
+ let contents;
33
+ try {
34
+ contents = await fs.readFile(filePath, "utf8");
35
+ } catch (e) {
36
+ // A repo holding its keys as separate .pub files. Nothing to edit in one place, so the
37
+ // caller's listing is all whoever ran this gets.
38
+ console.log(`No authorized_keys in ${repoPath}, so the addresses were not added to any key.`);
39
+ return added;
40
+ }
41
+ let lines = contents.split("\n");
42
+ for (let index = 0; index < lines.length; index++) {
43
+ let fingerprint = keyFingerprint(lines[index]);
44
+ let addresses = fingerprint && addressesByFingerprint.get(fingerprint);
45
+ if (!addresses || !addresses.length) {
46
+ continue;
47
+ }
48
+ let result = allowAddresses(lines[index], addresses);
49
+ lines[index] = result.keyLine;
50
+ added.set(fingerprint, [...(added.get(fingerprint) || []), ...result.added]);
51
+ }
52
+ if ([...added.values()].some(list => list.length)) {
53
+ await fs.writeFile(filePath, lines.join("\n"));
54
+ }
55
+ return added;
56
+ }
57
+
58
+ /** Puts each machine's addresses back into its own file, which is the machine equivalent of adding
59
+ an address to a key's from= list: without it the machine is allowed again in principle and
60
+ still rejected on sight, which revokes it once more. */
61
+ async function allowAddressesForMachines(config: { repoPath: string; revocations: Revocation[] }) {
62
+ let { repoPath, revocations } = config;
63
+ let added = new Map<string, string[]>();
64
+ // Read the whole list, change what these revocations are about, write it back. Setting is by
65
+ // the whole set, so writing one machine at a time would delete every other machine.
66
+ let machines = await getMachines(repoPath);
67
+ for (let revocation of revocations) {
68
+ let machineId = revocation.machineId;
69
+ let ip = revocationIP(revocation);
70
+ if (!machineId || !ip) {
71
+ continue;
72
+ }
73
+ let existing = machines.find(machine => machine.machineId === machineId);
74
+ if (!existing) {
75
+ // Removed from the repo since it was revoked, so there is nothing to allow. Deleting a
76
+ // machine is the stronger statement and it stands.
77
+ console.log(`machines/${machineId}.json is not in this repo, so ${ip} was not added to it`);
78
+ continue;
79
+ }
80
+ if (existing.ips.includes(ip)) {
81
+ continue;
82
+ }
83
+ existing.ips = [...existing.ips, ip];
84
+ added.set(machineId, [...(added.get(machineId) || []), ip]);
85
+ }
86
+ if (added.size) {
87
+ await setMachines({ repoPath, machines });
88
+ }
89
+ return added;
90
+ }
91
+
92
+ async function main() {
93
+ let argv = process.argv.slice(2);
94
+ let pushToGit = argv.includes(GIT_KEYWORD);
95
+ let positional = argv.filter(arg => arg !== GIT_KEYWORD);
96
+ if (positional.length > 1) {
97
+ throw new Error(`Expected at most a keys repo, was ${positional.length} argument(s)\n${USAGE}`);
98
+ }
99
+ let { repoPath, sourceURL: originURL } = await resolveKeysRepo(positional[0]);
100
+
101
+ let revocations = await readRemoteRevocations({ sourceURL: originURL });
102
+ if (!revocations.length) {
103
+ console.log(`${revokeRepoURL(originURL)} lists no revocations, so there is nothing to undo.`);
104
+ return;
105
+ }
106
+
107
+ let directory = path.join(repoPath, UNREVOKES_DIR);
108
+ await fs.mkdir(directory, { recursive: true });
109
+ let stamp = new Date().toISOString().replace(/[:.]/g, "-");
110
+ let unrevokeId = `${stamp}-unrevoke`;
111
+ await fs.writeFile(path.join(directory, `${unrevokeId}.json`), JSON.stringify({
112
+ unrevokeId,
113
+ createdAt: new Date().toISOString(),
114
+ // What this file says: each of these keys is allowed from this address. Pairs, not keys,
115
+ // and not a blanket "allow everything again" - a key forgiven here for one address is
116
+ // still revoked the moment it is used from another.
117
+ allowed: revocations.map(revocation => ({
118
+ fingerprint: revocation.fingerprint,
119
+ machineId: revocation.machineId,
120
+ ip: revocationIP(revocation),
121
+ })),
122
+ revocations: revocations.map(revocation => ({
123
+ revocationId: revocation.revocationId,
124
+ fingerprint: revocation.fingerprint,
125
+ machineId: revocation.machineId,
126
+ ip: revocationIP(revocation),
127
+ revokedAt: revocation.revokedAt,
128
+ revokedBy: revocation.revokedBy,
129
+ attempt: revocation.attempt,
130
+ })),
131
+ }, undefined, 4) + "\n");
132
+
133
+ // Named the way the notifications name them. A fingerprint is not in authorized_keys and
134
+ // cannot be worked out by eye, so on its own it tells whoever is reading this nothing about
135
+ // which of their keys is being talked about.
136
+ let keysByFingerprint = new Map<string, string>();
137
+ for (let key of await readRepoKeys(repoPath).catch(() => [] as string[])) {
138
+ keysByFingerprint.set(keyFingerprint(key), key);
139
+ }
140
+ let describeKey = (revocation: Revocation) => {
141
+ let key = keysByFingerprint.get(revocation.fingerprint || "") || revocation.key || "";
142
+ return key && summarizeKey(key) || `a key no longer in this repo (${revocation.fingerprint})`;
143
+ };
144
+
145
+ let addressesByIdentity = new Map<string, string[]>();
146
+ for (let revocation of revocations) {
147
+ let ip = revocationIP(revocation);
148
+ let identity = revocationIdentity(revocation);
149
+ let existing = addressesByIdentity.get(identity) || [];
150
+ if (ip && !existing.includes(ip)) {
151
+ existing.push(ip);
152
+ }
153
+ addressesByIdentity.set(identity, existing);
154
+ }
155
+
156
+ // Cancelling the revocation on its own would leave sshd refusing that address, and the machines
157
+ // revoking all over again, so the addresses go into the key's from= list and into the machine's
158
+ // own file here. We are in the repo that owns both, so there is nothing to hand back to whoever
159
+ // ran this.
160
+ let added = await allowAddressesInRepo({ repoPath, addressesByFingerprint: addressesByIdentity });
161
+ let addedForMachines = await allowAddressesForMachines({ repoPath, revocations });
162
+
163
+ console.log("");
164
+ for (let [identity, addresses] of addressesByIdentity) {
165
+ let entries = revocations.filter(entry => revocationIdentity(entry) === identity);
166
+ let machineId = entries[0].machineId;
167
+ console.log(`Unfreezing ${machineId && `machine ${machineId}` || describeKey(entries[0])}`);
168
+ console.log(`Allowing IP ${addresses.join(", ") || "(no address recorded)"}`);
169
+ let addedHere = machineId && addedForMachines.get(machineId) || added.get(identity) || [];
170
+ if (addedHere.length) {
171
+ let where = machineId && `machines/${machineId}.json` || `that key's from= list in authorized_keys`;
172
+ console.log(` added to ${where}: ${addedHere.join(", ")}`);
173
+ } else {
174
+ console.log(` already allowed from ${addresses.join(", ") || "everywhere"}`);
175
+ }
176
+ for (let entry of entries) {
177
+ console.log(
178
+ ` frozen by ${entry.revokedBy || "?"} ${entry.revokedAt || "at an unrecorded time"}`
179
+ );
180
+ }
181
+ console.log("");
182
+ }
183
+ console.log(`Every other address stays frozen, and any of these used from anywhere else is`);
184
+ console.log(`frozen again.`);
185
+
186
+ // Signed here rather than left as a step to remember. An unrevoke nobody signed does nothing at
187
+ // all, and the repo would sit there looking done while every machine ignored it.
188
+ await signRepo({ repoPath });
189
+
190
+ if (!pushToGit) {
191
+ console.log(`\nCommit and push it, and those keys work again as each machine picks it up:`);
192
+ console.log(`\`\`\`\ngit add -A\ngit commit -m "${COMMIT_MESSAGE}"\ngit push\n\`\`\``);
193
+ return;
194
+ }
195
+ await runPromise(`git add -A`, { cwd: repoPath });
196
+ await runPromise(`git commit -m "${COMMIT_MESSAGE}"`, { cwd: repoPath });
197
+ await runPromise(`git push`, { cwd: repoPath });
198
+ console.log(`\nCommitted and pushed. Those keys work again as each machine picks it up.`);
199
+ }
200
+
201
+ main().catch(e => {
202
+ console.error(`${e}`);
203
+ process.exitCode = 1;
204
+ }).finally(() => process.exit());
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
4
4
  };
@@ -25,4 +25,4 @@ function expandHome(filePath) {
25
25
  }
26
26
  exports.expandHome = expandHome;
27
27
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJwYXRocy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSw0Q0FBb0I7QUFDcEIsZ0RBQXdCO0FBRXhCOztnQ0FFZ0M7QUFDaEMsU0FBZ0IsVUFBVSxDQUFDLFFBQWdCO0lBQ3ZDLElBQUksUUFBUSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ25CLE9BQU8sWUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFDRCxJQUFJLFFBQVEsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksUUFBUSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzFELE9BQU8sY0FBSSxDQUFDLElBQUksQ0FBQyxZQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3RELENBQUM7SUFDRCxJQUFJLFFBQVEsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUMzQix5RkFBeUY7UUFDekYsa0RBQWtEO1FBQ2xELE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLFFBQVEsaUVBQWlFLENBQUMsQ0FBQztJQUN0SSxDQUFDO0lBQ0QsT0FBTyxjQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ2xDLENBQUM7QUFiRCxnQ0FhQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCI7XG5cbi8qKiBXaW5kb3dzIHNoZWxscyBkbyBub3QgZXhwYW5kIH4gdGhlbXNlbHZlcywgYW5kIGh1bnRpbmcgZG93biB5b3VyIGhvbWUgZm9sZGVyIGJ5IGhhbmQgaXNcbiAgICBhbm5veWluZywgc28gd2UgZXhwYW5kIGl0IG9uIGV2ZXJ5IHBsYXRmb3JtLiBCb3RoIHNlcGFyYXRvcnMgYXJlIGFjY2VwdGVkLCBiZWNhdXNlIGEgV2luZG93c1xuICAgIHVzZXIgbWF5IHR5cGUgZWl0aGVyIG9uZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHBhbmRIb21lKGZpbGVQYXRoOiBzdHJpbmcpIHtcbiAgICBpZiAoZmlsZVBhdGggPT09IFwiflwiKSB7XG4gICAgICAgIHJldHVybiBvcy5ob21lZGlyKCk7XG4gICAgfVxuICAgIGlmIChmaWxlUGF0aC5zdGFydHNXaXRoKFwifi9cIikgfHwgZmlsZVBhdGguc3RhcnRzV2l0aChcIn5cXFxcXCIpKSB7XG4gICAgICAgIHJldHVybiBwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCBmaWxlUGF0aC5zbGljZSgyKSk7XG4gICAgfVxuICAgIGlmIChmaWxlUGF0aC5zdGFydHNXaXRoKFwiflwiKSkge1xuICAgICAgICAvLyB+b3RoZXJ1c2VyIG5lZWRzIGFuIGFjY291bnQgZGF0YWJhc2Ugd2UgY2Fubm90IHJlYWQgcG9ydGFibHksIGFuZCBxdWlldGx5IHJlc29sdmluZyBpdFxuICAgICAgICAvLyB0byB0aGUgd3JvbmcgaG9tZSB3b3VsZCBiZSB3b3JzZSB0aGFuIHJlZnVzaW5nLlxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIH4gb3IgYW4gb3JkaW5hcnkgcGF0aCwgd2FzICR7ZmlsZVBhdGh9LiBSZWZlcnJpbmcgdG8gYW5vdGhlciB1c2VyJ3MgaG9tZSB3aXRoIH5uYW1lIGlzIG5vdCBzdXBwb3J0ZWQuYCk7XG4gICAgfVxuICAgIHJldHVybiBwYXRoLnJlc29sdmUoZmlsZVBhdGgpO1xufVxuIl19
28
- /* _JS_SOURCE_HASH = "aa5698729d5def416d177e211ac514eed7d8c8fcb796c68dcc520ddf0d83526b"; */
28
+ /* _JS_SOURCE_HASH = "493654cc4781cc00e46bb1f87e8f539ef3884ca93d7ce6173bbd46a2bd648be8"; */
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
3
  //exports.remoteCommandExists = exports.writeRemoteFile = exports.readRemoteFile = exports.runOverSSH = exports.describeHost = exports.THIS_MACHINE = exports.SUDO_PREAMBLE = void 0;
4
4
  const spawn_1 = require("./spawn");
@@ -97,4 +97,4 @@ async function remoteCommandExists(config) {
97
97
  }
98
98
  exports.remoteCommandExists = remoteCommandExists;
99
99
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3RlU1NILmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmVtb3RlU1NILnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUF1QztBQUV2QyxNQUFNLG1CQUFtQixHQUFHLEVBQUUsQ0FBQztBQUMvQixNQUFNLGFBQWEsR0FBRyx1QkFBdUIsQ0FBQztBQUM5QyxNQUFNLGNBQWMsR0FBRyx3QkFBd0IsQ0FBQztBQUNoRCxNQUFNLHFCQUFxQixHQUFHLEdBQUcsQ0FBQztBQUVsQyw4RUFBOEU7QUFDakUsUUFBQSxhQUFhLEdBQUcsMkRBQTJELENBQUM7QUFFekY7OzZCQUU2QjtBQUNoQixRQUFBLFlBQVksR0FBRyxFQUFFLENBQUM7QUFFL0IsU0FBZ0IsWUFBWSxDQUFDLElBQVk7SUFDckMsT0FBTyxJQUFJLElBQUksY0FBYyxDQUFDO0FBQ2xDLENBQUM7QUFGRCxvQ0FFQztBQUVEO3dGQUN3RjtBQUNqRixLQUFLLFVBQVUsVUFBVSxDQUFDLE1BQWdGO0lBQzdHLElBQUksRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsR0FBRyxNQUFNLENBQUM7SUFDbkQsSUFBSSxNQUFNLEdBQUcsTUFBTSxJQUFBLG9CQUFZLEVBQUM7UUFDNUIsT0FBTyxFQUFFLElBQUksSUFBSSxLQUFLLElBQUksSUFBSTtRQUM5QixJQUFJLEVBQUUsSUFBSSxJQUFJO1lBQ1YsSUFBSSxFQUFFLGVBQWU7WUFDckIsSUFBSSxFQUFFLGtCQUFrQixtQkFBbUIsRUFBRTtZQUM3QyxJQUFJO1lBQ0osTUFBTTtTQUNULElBQUksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDO1FBQ25CLEtBQUs7UUFDTCxhQUFhLEVBQUUsQ0FBQyxZQUFZO0tBQy9CLENBQUMsQ0FBQztJQUNILElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsWUFBWSxDQUFDLElBQUksQ0FBQyxpQkFBaUIs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LEdBQUcsTUFBTSxVQUFVLENBQUM7UUFDMUIsSUFBSTtRQUNKLE1BQU0sRUFBRSxlQUFlLE9BQU8sMkNBQTJDO1FBQ3pFLFlBQVksRUFBRSxJQUFJO0tBQ3JCLENBQUMsQ0FBQztJQUNILE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsS0FBSyxLQUFLLENBQUM7QUFDMUMsQ0FBQztBQVJELGtEQVFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc3Bhd25Qcm9taXNlIH0gZnJvbSBcIi4vc3Bhd25cIjtcblxuY29uc3QgU1NIX0NPTk5FQ1RfVElNRU9VVCA9IDEwO1xuY29uc3QgRVhJU1RTX01BUktFUiA9IFwiX19QT1JUU0VDVVJFX0VYSVNUU19fXCI7XG5jb25zdCBNSVNTSU5HX01BUktFUiA9IFwiX19QT1JUU0VDVVJFX01JU1NJTkdfX1wiO1xuY29uc3QgTUFYX0VSUk9SX0JPRFlfTEVOR1RIID0gNTAwO1xuXG4vLyAvZXRjIGlzIHJvb3Qgb3duZWQsIHNvIGZhbGwgYmFjayB0byBzdWRvIHdoZW5ldmVyIHRoZSBTU0ggdXNlciBpcyBub3Qgcm9vdC5cbmV4cG9ydCBjb25zdCBTVURPX1BSRUFNQkxFID0gYFNVRE89XCJcIjsgaWYgWyBcIiQoaWQgLXUpXCIgLW5lIDAgXTsgdGhlbiBTVURPPVwic3VkbyAtblwiOyBmaWA7XG5cbi8qKiBObyBob3N0IG1lYW5zIHRoaXMgbWFjaGluZSwgc28gdGhlIHZlcnkgc2FtZSBzY3JpcHQgcnVucyB3aXRoIG5vdGhpbmcgaW4gZnJvbnQgb2YgaXQuIExvY2FsIGFuZFxuICAgIHJlbW90ZSB0aGVuIGRvIGV4YWN0bHkgdGhlIHNhbWUgdGhpbmcsIHJhdGhlciB0aGFuIGJlaW5nIHR3byBwaWVjZXMgb2YgY29kZSB0aGF0IGhhdmUgdG8gYmUga2VwdFxuICAgIHNheWluZyB0aGUgc2FtZSB0aGluZy4gKi9cbmV4cG9ydCBjb25zdCBUSElTX01BQ0hJTkUgPSBcIlwiO1xuXG5leHBvcnQgZnVuY3Rpb24gZGVzY3JpYmVIb3N0KGhvc3Q6IHN0cmluZykge1xuICAgIHJldHVybiBob3N0IHx8IFwidGhpcyBtYWNoaW5lXCI7XG59XG5cbi8qKiBUaGUgaG9zdCBzdHJpbmcgaXMgaGFuZGVkIHRvIHNzaCB1bnRvdWNoZWQuIFVzZXJzLCBrZXlzIGFuZCBwb3J0cyBiZWxvbmcgaW4gdGhlIGNhbGxlcidzIHNzaFxuICAgIGNvbmZpZywgc28gQmF0Y2hNb2RlIG1ha2VzIGEgbWlzc2luZyBzZXR1cCBmYWlsIGltbWVkaWF0ZWx5IGluc3RlYWQgb2YgcHJvbXB0aW5nLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJ1bk92ZXJTU0goY29uZmlnOiB7IGhvc3Q6IHN0cmluZzsgc2NyaXB0OiBzdHJpbmc7IGlucHV0Pzogc3RyaW5nOyBhbGxvd0ZhaWx1cmU/OiBib29sZWFuIH0pIHtcbiAgICBsZXQgeyBob3N0LCBzY3JpcHQsIGlucHV0LCBhbGxvd0ZhaWx1cmUgfSA9IGNvbmZpZztcbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgc3Bhd25Qcm9taXNlKHtcbiAgICAgICAgY29tbWFuZDogaG9zdCAmJiBcInNzaFwiIHx8IFwic2hcIixcbiAgICAgICAgYXJnczogaG9zdCAmJiBbXG4gICAgICAgICAgICBcIi1vXCIsIFwiQmF0Y2hNb2RlPXllc1wiLFxuICAgICAgICAgICAgXCItb1wiLCBgQ29ubmVjdFRpbWVvdXQ9JHtTU0hfQ09OTkVDVF9USU1FT1VUfWAsXG4gICAgICAgICAgICBob3N0LFxuICAgICAgICAgICAgc2NyaXB0LFxuICAgICAgICBdIHx8IFtcIi1jXCIsIHNjcmlwdF0sXG4gICAgICAgIGlucHV0LFxuICAgICAgICBpbmhlcml0U3RkZXJyOiAhYWxsb3dGYWlsdXJlLFxuICAgIH0pO1xuICAgIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCB0byBydW4gYWdhaW5zdCAke2Rlc2NyaWJlSG9zdChob3N0KX0sIGZhaWxlZCB3aXRoICR7cmVzdWx0LmVycm9yLm1lc3NhZ2V9YCk7XG4gICAgfVxuICAgIGlmIChyZXN1bHQuc3RhdHVzICE9PSAwICYmICFhbGxvd0ZhaWx1cmUpIHtcbiAgICAgICAgbGV0IGFkdmljZSA9IGhvc3RcbiAgICAgICAgICAgICYmIGAgTm9uLWludGVyYWN0aXZlIHNzaCBhY2Nlc3MgdG8gJHtob3N0fSBoYXMgdG8gd29yayBvbiBpdHMgb3duIC0gZml4IGl0IGluIHlvdXIgc3NoIGNvbmZpZy5gXG4gICAgICAgICAgICB8fCBcIlwiO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgRXhwZWN0ZWQgdGhlIHNjcmlwdCBvbiAke2Rlc2NyaWJlSG9zdChob3N0KX0gdG8gZXhpdCAwLCB3YXMgJHtyZXN1bHQuc3RhdHVzfS4ke2FkdmljZX1cXG5gXG4gICAgICAgICAgICArIGAkeyhyZXN1bHQuc3Rkb3V0ICsgcmVzdWx0LnN0ZGVycikudHJpbSgpLnNsaWNlKDAsIE1BWF9FUlJPUl9CT0RZX0xFTkdUSCl9YFxuICAgICAgICApO1xuICAgIH1cbiAgICByZXR1cm4geyBzdGRvdXQ6IHJlc3VsdC5zdGRvdXQgfHwgXCJcIiwgc3RkZXJyOiByZXN1bHQuc3RkZXJyIHx8IFwiXCIsIHN0YXR1czogcmVzdWx0LnN0YXR1cyB9O1xufVxuXG4vKiogUmV0dXJucyB1bmRlZmluZWQgd2hlbiB0aGUgZmlsZSBkb2VzIG5vdCBleGlzdCwgc28gYSBtaXNzaW5nIGZpbGUgcmVhZHMgZGlmZmVyZW50bHkgZnJvbSBhblxuICAgIGVtcHR5IG9uZS4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkUmVtb3RlRmlsZShjb25maWc6IHsgaG9zdDogc3RyaW5nOyBmaWxlUGF0aDogc3RyaW5nIH0pIHtcbiAgICBsZXQgeyBob3N0LCBmaWxlUGF0aCB9ID0gY29uZmlnO1xuICAgIGxldCBvdXRwdXQgPSAoYXdhaXQgcnVuT3ZlclNTSCh7XG4gICAgICAgIGhvc3QsXG4gICAgICAgIHNjcmlwdDogYCR7U1VET19QUkVBTUJMRX1cbmlmICRTVURPIHRlc3QgLWYgXCIke2ZpbGVQYXRofVwiOyB0aGVuXG4gICAgZWNobyBcIiR7RVhJU1RTX01BUktFUn1cIlxuICAgICRTVURPIGNhdCBcIiR7ZmlsZVBhdGh9XCJcbmVsc2VcbiAgICBlY2hvIFwiJHtNSVNTSU5HX01BUktFUn1cIlxuZmlgLFxuICAgIH0pKS5zdGRvdXQ7XG4gICAgbGV0IG5ld2xpbmVJbmRleCA9IG91dHB1dC5pbmRleE9mKFwiXFxuXCIpO1xuICAgIGxldCBtYXJrZXIgPSBvdXRwdXQuc2xpY2UoMCwgbmV3bGluZUluZGV4KS50cmltKCk7XG4gICAgaWYgKG1hcmtlciA9PT0gTUlTU0lOR19NQVJLRVIpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgaWYgKG1hcmtlciAhPT0gRVhJU1RTX01BUktFUikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkICR7RVhJU1RTX01BUktFUn0gb3IgJHtNSVNTSU5HX01BUktFUn0gZnJvbSAke2hvc3R9LCB3YXMgJHtvdXRwdXQuc2xpY2UoMCwgTUFYX0VSUk9SX0JPRFlfTEVOR1RIKX1gKTtcbiAgICB9XG4gICAgcmV0dXJuIG91dHB1dC5zbGljZShuZXdsaW5lSW5kZXggKyAxKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdyaXRlUmVtb3RlRmlsZShjb25maWc6IHtcbiAgICBob3N0OiBzdHJpbmc7XG4gICAgZmlsZVBhdGg6IHN0cmluZztcbiAgICBjb250ZW50czogc3RyaW5nO1xuICAgIGZpbGVNb2RlOiBzdHJpbmc7XG4gICAgZGlyZWN0b3J5TW9kZTogc3RyaW5nO1xufSkge1xuICAgIGxldCB7IGhvc3QsIGZpbGVQYXRoLCBjb250ZW50cywgZmlsZU1vZGUsIGRpcmVjdG9yeU1vZGUgfSA9IGNvbmZpZztcbiAgICBsZXQgZGlyZWN0b3J5ID0gZmlsZVBhdGguc2xpY2UoMCwgZmlsZVBhdGgubGFzdEluZGV4T2YoXCIvXCIpKTtcbiAgICBhd2FpdCBydW5PdmVyU1NIKHtcbiAgICAgICAgaG9zdCxcbiAgICAgICAgLy8gRGVwbG95aW5nIGZyb20gV2luZG93cyBtdXN0IG5vdCBjYXJyeSBDUkxGIG9udG8gdGhlIHRhcmdldCwgd2hlcmUgaXQgYnJlYWtzIHN5c3RlbWRcbiAgICAgICAgLy8gdW5pdHMgYW5kIG1ha2VzIHByaXZhdGUga2V5cyB1bnJlYWRhYmxlIHRvIHNzaC5cbiAgICAgICAgaW5wdXQ6IGNvbnRlbnRzLnJlcGxhY2UoL1xcclxcbi9nLCBcIlxcblwiKSxcbiAgICAgICAgc2NyaXB0OiBgJHtTVURPX1BSRUFNQkxFfVxuc2V0IC1lXG4kU1VETyBta2RpciAtcCBcIiR7ZGlyZWN0b3J5fVwiXG4kU1VETyBjaG1vZCAke2RpcmVjdG9yeU1vZGV9IFwiJHtkaXJlY3Rvcnl9XCJcbiRTVURPIHRlZSBcIiR7ZmlsZVBhdGh9XCIgPiAvZGV2L251bGxcbiRTVURPIGNobW9kICR7ZmlsZU1vZGV9IFwiJHtmaWxlUGF0aH1cImAsXG4gICAgfSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW1vdGVDb21tYW5kRXhpc3RzKGNvbmZpZzogeyBob3N0OiBzdHJpbmc7IGNvbW1hbmQ6IHN0cmluZyB9KSB7XG4gICAgbGV0IHsgaG9zdCwgY29tbWFuZCB9ID0gY29uZmlnO1xuICAgIGxldCByZXN1bHQgPSBhd2FpdCBydW5PdmVyU1NIKHtcbiAgICAgICAgaG9zdCxcbiAgICAgICAgc2NyaXB0OiBgY29tbWFuZCAtdiBcIiR7Y29tbWFuZH1cIiA+IC9kZXYvbnVsbCAyPiYxICYmIGVjaG8geWVzIHx8IGVjaG8gbm9gLFxuICAgICAgICBhbGxvd0ZhaWx1cmU6IHRydWUsXG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdC5zdGRvdXQudHJpbSgpID09PSBcInllc1wiO1xufVxuIl19
100
- /* _JS_SOURCE_HASH = "50b1a08f2aa4b96f04f676f20c516bc6c2897a47ad7c12729c0f651735c71057"; */
100
+ /* _JS_SOURCE_HASH = "2bb084f8935f8ec03fde1d65be1a14ffcc51f8bbfefc04b9363fea0639a2a721"; */
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
3
  //exports.spawnPromise = void 0;
4
4
  const child_process_1 = require("child_process");
@@ -31,4 +31,4 @@ function spawnPromise(config) {
31
31
  }
32
32
  exports.spawnPromise = spawnPromise;
33
33
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3Bhd24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzcGF3bi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxpREFBc0M7QUFFdEMsMEhBQTBIO0FBQzFILGtGQUFrRjtBQUVsRjs7MEZBRTBGO0FBQzFGLFNBQWdCLFlBQVksQ0FBQyxNQU81QjtJQUNHLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxHQUFHLE1BQU0sQ0FBQztJQUN2RSxPQUFPLElBQUksT0FBTyxDQUEyRixPQUFPLENBQUMsRUFBRTs7UUFDbkgsSUFBSSxLQUFLLEdBQUcsSUFBQSxxQkFBSyxFQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUU7WUFDN0IsR0FBRztZQUNILEtBQUssRUFBRSxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsYUFBYSxJQUFJLFNBQVMsSUFBSSxNQUFNLENBQUM7U0FDaEUsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBQ2hCLElBQUksTUFBTSxHQUFHLEVBQUUsQ0FBQztRQUNoQiw2RUFBNkU7UUFDN0UsSUFBSSxLQUFLLEdBQUcsV0FBVyxJQUFJLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxJQUFJLFNBQVMsQ0FBQztRQUM3RixLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxLQUFLLElBQUksWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDdEQsTUFBQSxLQUFLLENBQUMsTUFBTSwwQ0FBRSxFQUFFLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxDQUFDO1FBQ25ELE1BQUEsS0FBSyxDQUFDLE1BQU0sMENBQUUsRUFBRSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLE1BQU0sSUFBSSxLQUFLLENBQUMsQ0FBQztRQUNuRCxLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDbEYsS0FBSyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQU4sTUFBTSxjQUFOLE1BQU0sR0FBSSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN4Ryw0RUFBNEU7UUFDNUUsTUFBQSxLQUFLLENBQUMsS0FBSywwQ0FBRSxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzFDLE1BQUEsS0FBSyxDQUFDLEtBQUssMENBQUUsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNsQyxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUM7QUEzQkQsb0NBMkJDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc3Bhd24gfSBmcm9tIFwiY2hpbGRfcHJvY2Vzc1wiO1xuXG4vLyBQT1JURUQgQ09ERTogc2VjdXJpdHkvYXV0aG9yaXplZEtleXMvZGFlbW9uL3BvcnRzZWN1cmVEYWVtb24uanMgY29udGFpbnMgYSBwbGFpbiBKUyBwb3J0IG9mIHNwYXduUHJvbWlzZSwgc28gaXQgY2FuIHJ1blxuLy8gd2l0aCBubyBkZXBlbmRlbmNpZXMuIElmIHlvdSBjaGFuZ2Ugb25lLCBtYWtlIHRoZSBtYXRjaGluZyBjaGFuZ2UgaW4gdGhlIG90aGVyLlxuXG4vKiogQW4gYXJndW1lbnQgbGlzdCByYXRoZXIgdGhhbiBhIHNoZWxsIHN0cmluZywgc28gaG9zdG5hbWVzIGFuZCBwYXRocyBjYW4gbmV2ZXIgYmUgcGFyc2VkIGFzXG4gICAgc2hlbGwgc3ludGF4LiBSZXNvbHZlcyB3aXRoIHRoZSBleGl0IGNvZGUgaW5zdGVhZCBvZiB0aHJvd2luZywgY2FsbGVycyBkZWNpZGUgd2hhdCBhIGZhaWx1cmVcbiAgICBtZWFucy4gYGluaGVyaXRTdGRlcnJgIGxldHMgYSBjaGlsZCdzIG93biBlcnJvcnMgcmVhY2ggdGhlIHRlcm1pbmFsIGFzIHRoZXkgaGFwcGVuLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNwYXduUHJvbWlzZShjb25maWc6IHtcbiAgICBjb21tYW5kOiBzdHJpbmc7XG4gICAgYXJnczogc3RyaW5nW107XG4gICAgY3dkPzogc3RyaW5nO1xuICAgIGlucHV0Pzogc3RyaW5nO1xuICAgIGluaGVyaXRTdGRlcnI/OiBib29sZWFuO1xuICAgIHRpbWVvdXRUaW1lPzogbnVtYmVyO1xufSkge1xuICAgIGxldCB7IGNvbW1hbmQsIGFyZ3MsIGN3ZCwgaW5wdXQsIGluaGVyaXRTdGRlcnIsIHRpbWVvdXRUaW1lIH0gPSBjb25maWc7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHsgc3Rkb3V0OiBzdHJpbmc7IHN0ZGVycjogc3RyaW5nOyBzdGF0dXM6IG51bWJlciB8IHVuZGVmaW5lZDsgZXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkIH0+KHJlc29sdmUgPT4ge1xuICAgICAgICBsZXQgY2hpbGQgPSBzcGF3bihjb21tYW5kLCBhcmdzLCB7XG4gICAgICAgICAgICBjd2QsXG4gICAgICAgICAgICBzdGRpbzogW1wicGlwZVwiLCBcInBpcGVcIiwgaW5oZXJpdFN0ZGVyciAmJiBcImluaGVyaXRcIiB8fCBcInBpcGVcIl0sXG4gICAgICAgIH0pO1xuICAgICAgICBsZXQgc3Rkb3V0ID0gXCJcIjtcbiAgICAgICAgbGV0IHN0ZGVyciA9IFwiXCI7XG4gICAgICAgIC8vIEEgY29tbWFuZCB0aGF0IG5ldmVyIHJldHVybnMgd291bGQgb3RoZXJ3aXNlIGhvbGQgdXAgZXZlcnl0aGluZyBiZWhpbmQgaXQuXG4gICAgICAgIGxldCB0aW1lciA9IHRpbWVvdXRUaW1lICYmIHNldFRpbWVvdXQoKCkgPT4gY2hpbGQua2lsbChcIlNJR0tJTExcIiksIHRpbWVvdXRUaW1lKSB8fCB1bmRlZmluZWQ7XG4gICAgICAgIGNoaWxkLm9uKFwiY2xvc2VcIiwgKCkgPT4gdGltZXIgJiYgY2xlYXJUaW1lb3V0KHRpbWVyKSk7XG4gICAgICAgIGNoaWxkLnN0ZG91dD8ub24oXCJkYXRhXCIsIGNodW5rID0+IHN0ZG91dCArPSBjaHVuayk7XG4gICAgICAgIGNoaWxkLnN0ZGVycj8ub24oXCJkYXRhXCIsIGNodW5rID0+IHN0ZGVyciArPSBjaHVuayk7XG4gICAgICAgIGNoaWxkLm9uKFwiZXJyb3JcIiwgZXJyb3IgPT4gcmVzb2x2ZSh7IHN0ZG91dCwgc3RkZXJyLCBzdGF0dXM6IHVuZGVmaW5lZCwgZXJyb3IgfSkpO1xuICAgICAgICBjaGlsZC5vbihcImNsb3NlXCIsIHN0YXR1cyA9PiByZXNvbHZlKHsgc3Rkb3V0LCBzdGRlcnIsIHN0YXR1czogc3RhdHVzID8/IHVuZGVmaW5lZCwgZXJyb3I6IHVuZGVmaW5lZCB9KSk7XG4gICAgICAgIC8vIEEgY2hpbGQgdGhhdCBleGl0cyBiZWZvcmUgcmVhZGluZyBldmVyeXRoaW5nIHdvdWxkIG90aGVyd2lzZSByYWlzZSBFUElQRS5cbiAgICAgICAgY2hpbGQuc3RkaW4/Lm9uKFwiZXJyb3JcIiwgKCkgPT4gdW5kZWZpbmVkKTtcbiAgICAgICAgY2hpbGQuc3RkaW4/LmVuZChpbnB1dCB8fCBcIlwiKTtcbiAgICB9KTtcbn1cbiJdfQ==
34
- /* _JS_SOURCE_HASH = "148887f3afb42a330df49eb955396403fab2e558c30741383b1a17b19a134288"; */
34
+ /* _JS_SOURCE_HASH = "d2f47a3b98d8645feac1558871c6670685fd9583f40bccb7d60d8f0ced0051ac"; */
@@ -0,0 +1,35 @@
1
+ export declare const SUDO_PREAMBLE = "SUDO=\"\"; if [ \"$(id -u)\" -ne 0 ]; then SUDO=\"sudo -n\"; fi";
2
+ /** No host means this machine, so the very same script runs with nothing in front of it. Local and
3
+ remote then do exactly the same thing, rather than being two pieces of code that have to be kept
4
+ saying the same thing. */
5
+ export declare const THIS_MACHINE = "";
6
+ export declare function describeHost(host: string): string;
7
+ /** The host string is handed to ssh untouched. Users, keys and ports belong in the caller's ssh
8
+ config, so BatchMode makes a missing setup fail immediately instead of prompting. */
9
+ export declare function runOverSSH(config: {
10
+ host: string;
11
+ script: string;
12
+ input?: string;
13
+ allowFailure?: boolean;
14
+ }): Promise<{
15
+ stdout: string;
16
+ stderr: string;
17
+ status: number | undefined;
18
+ }>;
19
+ /** Returns undefined when the file does not exist, so a missing file reads differently from an
20
+ empty one. */
21
+ export declare function readRemoteFile(config: {
22
+ host: string;
23
+ filePath: string;
24
+ }): Promise<string | undefined>;
25
+ export declare function writeRemoteFile(config: {
26
+ host: string;
27
+ filePath: string;
28
+ contents: string;
29
+ fileMode: string;
30
+ directoryMode: string;
31
+ }): Promise<void>;
32
+ export declare function remoteCommandExists(config: {
33
+ host: string;
34
+ command: string;
35
+ }): Promise<boolean>;
@@ -13,4 +13,3 @@ export declare function deriveEd25519Key(config: {
13
13
  seed: Buffer;
14
14
  publicKey: Buffer;
15
15
  };
16
- export declare function main(): Promise<void>;
@@ -1,14 +1,5 @@
1
1
  import crypto from "crypto";
2
- import fs from "fs/promises";
3
- import { expandHome } from "../helpers/paths";
4
- import { formatOpenSSHPrivateKey, formatPublicKeyLine, parseOpenSSHPrivateKey, publicKeyFromSeed } from "./sshKeyFile";
5
-
6
- const USAGE = `Usage: yarn derivekey <label> <source-key> <derived-key>
7
-
8
- Derives a second ed25519 key from an existing one. The same label and source key always give the
9
- same derived key, so it can be worked out again rather than backed up.
10
-
11
- yarn derivekey revokegithubkey ~/authorized_keys_access/id_ed25519 ~/authorized_keys_access/id_ed25519_revoke`;
2
+ import { publicKeyFromSeed } from "./sshKeyFile";
12
3
 
13
4
  /** A label mixed into a key's secret to get another key, so one key can stand behind several
14
5
  identities without any of them being stored.
@@ -24,49 +15,3 @@ export function deriveEd25519Key(config: { seed: Buffer; label: string }) {
24
15
  let derivedSeed = crypto.createHmac("sha256", seed).update(label, "utf8").digest();
25
16
  return { seed: derivedSeed, publicKey: publicKeyFromSeed(derivedSeed) };
26
17
  }
27
-
28
- async function pathExists(filePath: string) {
29
- try {
30
- await fs.access(filePath);
31
- return true;
32
- } catch (e) {
33
- return false;
34
- }
35
- }
36
-
37
- function parseArgs(argv: string[]) {
38
- if (argv.length !== 3) {
39
- throw new Error(`Expected a label, a source key and a derived key, was ${argv.length} argument(s)\n${USAGE}`);
40
- }
41
- let [label, sourcePath, derivedPath] = argv;
42
- return { label, sourcePath: expandHome(sourcePath), derivedPath: expandHome(derivedPath) };
43
- }
44
-
45
- export async function main() {
46
- let { label, sourcePath, derivedPath } = parseArgs(process.argv.slice(2));
47
-
48
- if (!await pathExists(sourcePath)) {
49
- throw new Error(`Expected a source key at ${sourcePath}, no such file exists`);
50
- }
51
- // Writing over a key would strand whatever it already gives access to, and the derived key can
52
- // always be worked out again, so there is never a reason to overwrite.
53
- if (await pathExists(derivedPath)) {
54
- throw new Error(
55
- `Expected ${derivedPath} to not exist, it does. Deriving the same label again gives the`
56
- + ` same key, so delete it first if you want it rewritten.`
57
- );
58
- }
59
-
60
- let source = parseOpenSSHPrivateKey(await fs.readFile(sourcePath, "utf8"));
61
- let derived = deriveEd25519Key({ seed: source.seed, label });
62
- let comment = `${source.comment || sourcePath} ${label}`.trim();
63
-
64
- await fs.writeFile(derivedPath, formatOpenSSHPrivateKey({ seed: derived.seed, comment }), { mode: 0o600 });
65
- let publicLine = formatPublicKeyLine({ publicKey: derived.publicKey, comment });
66
- await fs.writeFile(`${derivedPath}.pub`, publicLine, { mode: 0o644 });
67
-
68
- console.log(`Derived "${label}" from ${sourcePath}`);
69
- console.log(` private ${derivedPath}`);
70
- console.log(` public ${derivedPath}.pub`);
71
- console.log(publicLine.trim());
72
- }