@highstate/common 0.9.20 → 0.9.21

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.
@@ -1,13 +1,10 @@
1
- import { ComponentResource, Resource, toPromise, output, interpolate, normalizeInputsAndMap, fileFromString, secret, asset, normalizeInputs } from '@highstate/pulumi';
1
+ import { ComponentResource, Resource, toPromise, output, interpolate, normalizeInputsAndMap, normalizeInputs, asset, fileFromString, secret } from '@highstate/pulumi';
2
2
  import { uniqueBy, flat, groupBy } from 'remeda';
3
3
  import { homedir, tmpdir } from 'node:os';
4
- import { local, remote } from '@pulumi/command';
5
4
  import { sha256 } from '@noble/hashes/sha2';
6
- import { z, getOrCreate, stripNullish, HighstateSignature } from '@highstate/contract';
7
- import { randomBytes, bytesToHex } from '@noble/hashes/utils';
8
- import { secureMask } from 'micro-key-producer/password.js';
9
- import getKeys, { PrivateExport } from 'micro-key-producer/ssh.js';
10
- import { randomBytes as randomBytes$1 } from 'micro-key-producer/utils.js';
5
+ import { local, remote } from '@pulumi/command';
6
+ import { z, getOrCreate, HighstateSignature, stripNullish } from '@highstate/contract';
7
+ import { network } from '@highstate/library';
11
8
  import { createHash } from 'node:crypto';
12
9
  import { createReadStream } from 'node:fs';
13
10
  import { mkdtemp, writeFile, cp, rm, stat, rename, mkdir } from 'node:fs/promises';
@@ -17,7 +14,10 @@ import { pipeline } from 'node:stream/promises';
17
14
  import { minimatch } from 'minimatch';
18
15
  import * as tar from 'tar';
19
16
  import unzipper from 'unzipper';
20
- import { network } from '@highstate/library';
17
+ import { randomBytes, bytesToHex } from '@noble/hashes/utils';
18
+ import { secureMask } from 'micro-key-producer/password.js';
19
+ import getKeys, { PrivateExport } from 'micro-key-producer/ssh.js';
20
+ import { randomBytes as randomBytes$1 } from 'micro-key-producer/utils.js';
21
21
 
22
22
  // src/shared/network.ts
23
23
  function l3EndpointToString(l3Endpoint) {
@@ -641,182 +641,185 @@ async function updateEndpointsWithFqdn(endpoints, fqdn, fqdnEndpointFilter, patc
641
641
  dnsRecordSet
642
642
  };
643
643
  }
644
- function generatePassword() {
645
- return secureMask.apply(randomBytes(32)).password;
646
- }
647
- function generateKey(format = "hex") {
648
- const bytes = randomBytes(32);
649
- if (format === "raw") {
650
- return bytes;
651
- }
652
- if (format === "base64") {
653
- return Buffer.from(bytes).toString("base64");
644
+ var gatewayRouteMediator = new ImplementationMediator(
645
+ "gateway-route",
646
+ z.object({
647
+ name: z.string(),
648
+ spec: z.custom(),
649
+ opts: z.custom().optional()
650
+ }),
651
+ z.object({
652
+ resource: z.instanceof(Resource),
653
+ endpoints: network.l3EndpointEntity.schema.array()
654
+ })
655
+ );
656
+ var GatewayRoute = class extends ComponentResource {
657
+ /**
658
+ * The underlying resource created by the implementation.
659
+ */
660
+ resource;
661
+ /**
662
+ * The endpoints of the gateway which serve this route.
663
+ *
664
+ * In most cases, this will be a single endpoint of the gateway shared for all routes.
665
+ */
666
+ endpoints;
667
+ constructor(name, args, opts) {
668
+ super("highstate:common:GatewayRoute", name, args, opts);
669
+ const { resource, endpoints } = gatewayRouteMediator.callOutput(output(args.gateway).implRef, {
670
+ name,
671
+ spec: args,
672
+ opts: { ...opts, parent: this }
673
+ });
674
+ this.resource = resource;
675
+ this.endpoints = endpoints;
654
676
  }
655
- return bytesToHex(bytes);
656
- }
657
-
658
- // assets/images.json
659
- var terminal_ssh = {
660
- image: "ghcr.io/exeteres/highstate/terminal-ssh:latest@sha256:99380e0405522afa0058eedce124c1970a87408663365b2dbce737801a7cd5d1"
661
677
  };
662
-
663
- // src/shared/ssh.ts
664
- async function createSshTerminal(credentials) {
665
- const resolvedCredentials = await toPromise(credentials);
666
- const command = ["ssh", "-tt", "-o", "UserKnownHostsFile=/known_hosts"];
667
- const endpoint = resolvedCredentials.endpoints[0];
668
- command.push("-p", endpoint.port.toString());
669
- if (resolvedCredentials.keyPair) {
670
- command.push("-i", "/private_key");
678
+ var tlsCertificateMediator = new ImplementationMediator(
679
+ "tls-certificate",
680
+ z.object({
681
+ name: z.string(),
682
+ spec: z.custom(),
683
+ opts: z.custom().optional()
684
+ }),
685
+ z.instanceof(Resource)
686
+ );
687
+ var TlsCertificate = class _TlsCertificate extends ComponentResource {
688
+ /**
689
+ * The underlying resource created by the implementation.
690
+ */
691
+ resource;
692
+ constructor(name, args, opts) {
693
+ super("highstate:common:TlsCertificate", name, args, opts);
694
+ const issuers = normalizeInputs(args.issuer, args.issuers);
695
+ this.resource = output({
696
+ issuers,
697
+ commonName: args.commonName,
698
+ dnsNames: args.dnsNames
699
+ }).apply(async ({ issuers: issuers2, commonName, dnsNames }) => {
700
+ const matchedIssuer = issuers2.find((issuer) => {
701
+ if (commonName && !commonName.endsWith(issuer.domain)) {
702
+ return false;
703
+ }
704
+ if (dnsNames && !dnsNames.every((name2) => name2.endsWith(issuer.domain))) {
705
+ return false;
706
+ }
707
+ return true;
708
+ });
709
+ if (!matchedIssuer) {
710
+ throw new Error(
711
+ `No TLS issuer matched the common name "${commonName}" and DNS names "${dnsNames?.join(", ") ?? ""}"`
712
+ );
713
+ }
714
+ return await tlsCertificateMediator.call(matchedIssuer.implRef, {
715
+ name,
716
+ spec: args
717
+ });
718
+ });
671
719
  }
672
- command.push(`${resolvedCredentials.user}@${l3EndpointToString(endpoint)}`);
673
- if (resolvedCredentials.password) {
674
- command.unshift("sshpass", "-f", "/password");
720
+ static tlsCertificateCache = /* @__PURE__ */ new Map();
721
+ /**
722
+ * Creates a TLS certificate for the specified common name and DNS names.
723
+ *
724
+ * If a TLS certificate with the same name already exists, it will be reused.
725
+ *
726
+ * @param name The name of the TLS certificate.
727
+ * @param args The arguments for the TLS certificate.
728
+ * @param opts The options for the resource.
729
+ */
730
+ static createOnce(name, args, opts) {
731
+ return getOrCreate(
732
+ _TlsCertificate.tlsCertificateCache,
733
+ name,
734
+ () => new _TlsCertificate(name, args, opts)
735
+ );
675
736
  }
676
- return output({
677
- name: "ssh",
678
- meta: {
679
- title: "Shell",
680
- description: "Connect to the server via SSH.",
681
- icon: "gg:remote"
682
- },
683
- spec: {
684
- image: terminal_ssh.image,
685
- command,
686
- files: stripNullish({
687
- "/password": resolvedCredentials.password ? fileFromString("password", resolvedCredentials.password, { isSecret: true }) : void 0,
688
- "/private_key": resolvedCredentials.keyPair?.privateKey ? fileFromString("private_key", resolvedCredentials.keyPair.privateKey, {
689
- isSecret: true,
690
- mode: 384
691
- }) : void 0,
692
- "/known_hosts": fileFromString(
693
- "known_hosts",
694
- `${l3EndpointToString(endpoint)} ${resolvedCredentials.hostKey}`,
695
- { mode: 420 }
696
- )
697
- })
737
+ };
738
+ var AccessPointRoute = class extends ComponentResource {
739
+ /**
740
+ * The created gateway route.
741
+ */
742
+ route;
743
+ /**
744
+ * The DNS record set created for the route.
745
+ *
746
+ * May be shared between multiple routes with the same FQDN.
747
+ */
748
+ dnsRecordSet;
749
+ /**
750
+ * The TLS certificate created for the route.
751
+ *
752
+ * May be shared between multiple routes with the same FQDN.
753
+ */
754
+ tlsCertificate;
755
+ constructor(name, args, opts) {
756
+ super("highstate:common:AccessPointRoute", name, args, opts);
757
+ if (args.fqdn && args.type === "http" && !args.insecure) {
758
+ this.tlsCertificate = output(args.accessPoint).apply((accessPoint) => {
759
+ if (accessPoint.tlsIssuers.length === 0) {
760
+ return void 0;
761
+ }
762
+ return TlsCertificate.createOnce(
763
+ name,
764
+ {
765
+ issuers: accessPoint.tlsIssuers,
766
+ dnsNames: args.fqdn ? [args.fqdn] : [],
767
+ nativeData: args.tlsCertificateNativeData
768
+ },
769
+ { ...opts, parent: this }
770
+ );
771
+ });
698
772
  }
699
- });
700
- }
701
- function generateSshPrivateKey() {
702
- const seed = randomBytes$1(32);
703
- return secret(getKeys(seed).privateKey);
704
- }
705
- function sshPrivateKeyToKeyPair(privateKeyString) {
706
- return output(privateKeyString).apply((privateKeyString2) => {
707
- const privateKeyStruct = PrivateExport.decode(privateKeyString2);
708
- const privKey = privateKeyStruct.keys[0].privKey.privKey;
709
- const { fingerprint, publicKey } = getKeys(privKey.slice(0, 32));
710
- return output({
711
- type: "ed25519",
712
- fingerprint,
713
- publicKey,
714
- privateKey: secret(privateKeyString2)
715
- });
716
- });
717
- }
718
- async function createServerBundle(options) {
719
- const server = await createServerEntity(options);
720
- const ssh = await toPromise(server.ssh);
721
- return {
722
- server,
723
- terminal: ssh ? await createSshTerminal(ssh) : void 0
724
- };
725
- }
726
- async function createServerEntity({
727
- name,
728
- fallbackHostname,
729
- endpoints,
730
- sshArgs = { enabled: true, port: 22, user: "root" },
731
- sshPassword,
732
- sshPrivateKey,
733
- sshKeyPair,
734
- pingInterval,
735
- pingTimeout,
736
- waitForPing,
737
- waitForSsh,
738
- sshCheckInterval,
739
- sshCheckTimeout
740
- }) {
741
- if (endpoints.length === 0) {
742
- throw new Error("At least one L3 endpoint is required to create a server entity");
743
- }
744
- fallbackHostname ??= name;
745
- waitForSsh ??= sshArgs.enabled;
746
- waitForPing ??= !waitForSsh;
747
- if (waitForPing) {
748
- await Command.waitFor(`${name}.ping`, {
749
- host: "local",
750
- create: `ping -c 1 ${l3EndpointToString(endpoints[0])}`,
751
- timeout: pingTimeout ?? 300,
752
- interval: pingInterval ?? 5,
753
- triggers: [Date.now()]
754
- }).wait();
755
- }
756
- if (!sshArgs.enabled) {
757
- return output({
758
- hostname: name,
759
- endpoints
760
- });
761
- }
762
- const sshHost = sshArgs?.host ?? l3EndpointToString(endpoints[0]);
763
- if (waitForSsh) {
764
- await Command.waitFor(`${name}.ssh`, {
765
- host: "local",
766
- create: `nc -zv ${sshHost} ${sshArgs.port}`,
767
- timeout: sshCheckTimeout ?? 300,
768
- interval: sshCheckInterval ?? 5,
769
- triggers: [Date.now()]
770
- }).wait();
771
- }
772
- const connection = output({
773
- host: sshHost,
774
- port: sshArgs.port,
775
- user: sshArgs.user,
776
- password: sshPassword,
777
- privateKey: sshKeyPair ? output(sshKeyPair).privateKey : sshPrivateKey,
778
- dialErrorLimit: 3
779
- });
780
- const hostnameResult = new remote.Command("hostname", {
781
- connection,
782
- create: "hostname",
783
- triggers: [Date.now()]
784
- });
785
- const hostKeyResult = new remote.Command("host-key", {
786
- connection,
787
- create: "cat /etc/ssh/ssh_host_ed25519_key.pub",
788
- triggers: [Date.now()]
789
- });
790
- return output({
791
- endpoints,
792
- hostname: hostnameResult.stdout.apply((x) => x.trim()),
793
- ssh: {
794
- endpoints: [l3EndpointToL4(sshHost, sshArgs.port ?? 22)],
795
- user: sshArgs.user ?? "root",
796
- hostKey: hostKeyResult.stdout.apply((x) => x.trim()),
797
- password: connection.password,
798
- keyPair: sshKeyPair ? sshKeyPair : sshPrivateKey ? sshPrivateKeyToKeyPair(sshPrivateKey) : void 0
799
- }
800
- });
801
- }
802
- function assetFromFile(file) {
803
- if (file.content.type === "remote") {
804
- return new asset.RemoteAsset(l7EndpointToString(file.content.endpoint));
805
- }
806
- if (file.content.type === "local") {
807
- return new asset.FileAsset(file.content.path);
808
- }
809
- if (file.content.type === "artifact") {
810
- throw new Error(
811
- "Artifact-based files cannot be converted to Pulumi assets directly. Use MaterializedFile instead."
812
- );
813
- }
814
- if (file.content.isBinary) {
815
- throw new Error(
816
- "Cannot create asset from inline binary file content. Please open an issue if you need this feature."
817
- );
818
- }
819
- return new asset.StringAsset(file.content.value);
773
+ this.route = new GatewayRoute(
774
+ name,
775
+ {
776
+ ...args,
777
+ gateway: output(args.accessPoint).gateway,
778
+ tlsCertificate: this.tlsCertificate,
779
+ nativeData: args.gatewayNativeData
780
+ },
781
+ { ...opts, parent: this }
782
+ );
783
+ if (args.fqdn) {
784
+ this.dnsRecordSet = output(args.accessPoint).apply(async (accessPoint) => {
785
+ if (accessPoint.dnsProviders.length === 0) {
786
+ return void 0;
787
+ }
788
+ const fqdn = await toPromise(args.fqdn);
789
+ if (!fqdn) {
790
+ return void 0;
791
+ }
792
+ return DnsRecordSet.createOnce(
793
+ fqdn,
794
+ {
795
+ providers: output(args.accessPoint).dnsProviders,
796
+ values: this.route.endpoints,
797
+ waitAt: "local"
798
+ },
799
+ { ...opts, parent: this }
800
+ );
801
+ });
802
+ }
803
+ }
804
+ };
805
+ function assetFromFile(file) {
806
+ if (file.content.type === "remote") {
807
+ return new asset.RemoteAsset(l7EndpointToString(file.content.endpoint));
808
+ }
809
+ if (file.content.type === "local") {
810
+ return new asset.FileAsset(file.content.path);
811
+ }
812
+ if (file.content.type === "artifact") {
813
+ throw new Error(
814
+ "Artifact-based files cannot be converted to Pulumi assets directly. Use MaterializedFile instead."
815
+ );
816
+ }
817
+ if (file.content.isBinary) {
818
+ throw new Error(
819
+ "Cannot create asset from inline binary file content. Please open an issue if you need this feature."
820
+ );
821
+ }
822
+ return new asset.StringAsset(file.content.value);
820
823
  }
821
824
  function archiveFromFolder(folder) {
822
825
  if (folder.content.type === "remote") {
@@ -1282,168 +1285,165 @@ function getNameByEndpoint(endpoint) {
1282
1285
  const parsedEndpoint = parseL7Endpoint(endpoint);
1283
1286
  return parsedEndpoint.resource ? basename(parsedEndpoint.resource) : "";
1284
1287
  }
1285
- var gatewayRouteMediator = new ImplementationMediator(
1286
- "gateway-route",
1287
- z.object({
1288
- name: z.string(),
1289
- spec: z.custom(),
1290
- opts: z.custom().optional()
1291
- }),
1292
- z.object({
1293
- resource: z.instanceof(Resource),
1294
- endpoints: network.l3EndpointEntity.schema.array()
1295
- })
1296
- );
1297
- var GatewayRoute = class extends ComponentResource {
1298
- /**
1299
- * The underlying resource created by the implementation.
1300
- */
1301
- resource;
1302
- /**
1303
- * The endpoints of the gateway which serve this route.
1304
- *
1305
- * In most cases, this will be a single endpoint of the gateway shared for all routes.
1306
- */
1307
- endpoints;
1308
- constructor(name, args, opts) {
1309
- super("highstate:common:GatewayRoute", name, args, opts);
1310
- const { resource, endpoints } = gatewayRouteMediator.callOutput(output(args.gateway).implRef, {
1311
- name,
1312
- spec: args,
1313
- opts: { ...opts, parent: this }
1314
- });
1315
- this.resource = resource;
1316
- this.endpoints = endpoints;
1288
+ function generatePassword() {
1289
+ return secureMask.apply(randomBytes(32)).password;
1290
+ }
1291
+ function generateKey(format = "hex") {
1292
+ const bytes = randomBytes(32);
1293
+ if (format === "raw") {
1294
+ return bytes;
1317
1295
  }
1296
+ if (format === "base64") {
1297
+ return Buffer.from(bytes).toString("base64");
1298
+ }
1299
+ return bytesToHex(bytes);
1300
+ }
1301
+
1302
+ // assets/images.json
1303
+ var terminal_ssh = {
1304
+ image: "ghcr.io/exeteres/highstate/terminal-ssh:latest@sha256:99380e0405522afa0058eedce124c1970a87408663365b2dbce737801a7cd5d1"
1318
1305
  };
1319
- var tlsCertificateMediator = new ImplementationMediator(
1320
- "tls-certificate",
1321
- z.object({
1322
- name: z.string(),
1323
- spec: z.custom(),
1324
- opts: z.custom().optional()
1325
- }),
1326
- z.instanceof(Resource)
1327
- );
1328
- var TlsCertificate = class _TlsCertificate extends ComponentResource {
1329
- /**
1330
- * The underlying resource created by the implementation.
1331
- */
1332
- resource;
1333
- constructor(name, args, opts) {
1334
- super("highstate:common:TlsCertificate", name, args, opts);
1335
- const issuers = normalizeInputs(args.issuer, args.issuers);
1336
- this.resource = output({
1337
- issuers,
1338
- commonName: args.commonName,
1339
- dnsNames: args.dnsNames
1340
- }).apply(async ({ issuers: issuers2, commonName, dnsNames }) => {
1341
- const matchedIssuer = issuers2.find((issuer) => {
1342
- if (commonName && !commonName.endsWith(issuer.domain)) {
1343
- return false;
1344
- }
1345
- if (dnsNames && !dnsNames.every((name2) => name2.endsWith(issuer.domain))) {
1346
- return false;
1347
- }
1348
- return true;
1349
- });
1350
- if (!matchedIssuer) {
1351
- throw new Error(
1352
- `No TLS issuer matched the common name "${commonName}" and DNS names "${dnsNames?.join(", ") ?? ""}"`
1353
- );
1354
- }
1355
- return await tlsCertificateMediator.call(matchedIssuer.implRef, {
1356
- name,
1357
- spec: args
1358
- });
1359
- });
1306
+
1307
+ // src/shared/ssh.ts
1308
+ async function createSshTerminal(credentials) {
1309
+ const resolvedCredentials = await toPromise(credentials);
1310
+ const command = ["ssh", "-tt", "-o", "UserKnownHostsFile=/known_hosts"];
1311
+ const endpoint = resolvedCredentials.endpoints[0];
1312
+ command.push("-p", endpoint.port.toString());
1313
+ if (resolvedCredentials.keyPair) {
1314
+ command.push("-i", "/private_key");
1360
1315
  }
1361
- static tlsCertificateCache = /* @__PURE__ */ new Map();
1362
- /**
1363
- * Creates a TLS certificate for the specified common name and DNS names.
1364
- *
1365
- * If a TLS certificate with the same name already exists, it will be reused.
1366
- *
1367
- * @param name The name of the TLS certificate.
1368
- * @param args The arguments for the TLS certificate.
1369
- * @param opts The options for the resource.
1370
- */
1371
- static createOnce(name, args, opts) {
1372
- return getOrCreate(
1373
- _TlsCertificate.tlsCertificateCache,
1374
- name,
1375
- () => new _TlsCertificate(name, args, opts)
1376
- );
1316
+ command.push(`${resolvedCredentials.user}@${l3EndpointToString(endpoint)}`);
1317
+ if (resolvedCredentials.password) {
1318
+ command.unshift("sshpass", "-f", "/password");
1377
1319
  }
1378
- };
1379
- var AccessPointRoute = class extends ComponentResource {
1380
- /**
1381
- * The created gateway route.
1382
- */
1383
- route;
1384
- /**
1385
- * The DNS record set created for the route.
1386
- *
1387
- * May be shared between multiple routes with the same FQDN.
1388
- */
1389
- dnsRecordSet;
1390
- /**
1391
- * The TLS certificate created for the route.
1392
- *
1393
- * May be shared between multiple routes with the same FQDN.
1394
- */
1395
- tlsCertificate;
1396
- constructor(name, args, opts) {
1397
- super("highstate:common:AccessPointRoute", name, args, opts);
1398
- if (args.fqdn && args.type === "http" && !args.insecure) {
1399
- this.tlsCertificate = output(args.accessPoint).apply((accessPoint) => {
1400
- if (accessPoint.tlsIssuers.length === 0) {
1401
- return void 0;
1402
- }
1403
- return TlsCertificate.createOnce(
1404
- name,
1405
- {
1406
- issuers: accessPoint.tlsIssuers,
1407
- dnsNames: args.fqdn ? [args.fqdn] : [],
1408
- nativeData: args.tlsCertificateNativeData
1409
- },
1410
- { ...opts, parent: this }
1411
- );
1412
- });
1413
- }
1414
- this.route = new GatewayRoute(
1415
- name,
1416
- {
1417
- ...args,
1418
- gateway: output(args.accessPoint).gateway,
1419
- tlsCertificate: this.tlsCertificate,
1420
- nativeData: args.gatewayNativeData
1421
- },
1422
- { ...opts, parent: this }
1423
- );
1424
- if (args.fqdn) {
1425
- this.dnsRecordSet = output(args.accessPoint).apply(async (accessPoint) => {
1426
- if (accessPoint.dnsProviders.length === 0) {
1427
- return void 0;
1428
- }
1429
- const fqdn = await toPromise(args.fqdn);
1430
- if (!fqdn) {
1431
- return void 0;
1432
- }
1433
- return DnsRecordSet.createOnce(
1434
- fqdn,
1435
- {
1436
- providers: output(args.accessPoint).dnsProviders,
1437
- values: this.route.endpoints,
1438
- waitAt: "local"
1439
- },
1440
- { ...opts, parent: this }
1441
- );
1442
- });
1320
+ return output({
1321
+ name: "ssh",
1322
+ meta: {
1323
+ title: "Shell",
1324
+ description: "Connect to the server via SSH.",
1325
+ icon: "gg:remote"
1326
+ },
1327
+ spec: {
1328
+ image: terminal_ssh.image,
1329
+ command,
1330
+ files: stripNullish({
1331
+ "/password": resolvedCredentials.password ? fileFromString("password", resolvedCredentials.password, { isSecret: true }) : void 0,
1332
+ "/private_key": resolvedCredentials.keyPair?.privateKey ? fileFromString("private_key", resolvedCredentials.keyPair.privateKey, {
1333
+ isSecret: true,
1334
+ mode: 384
1335
+ }) : void 0,
1336
+ "/known_hosts": fileFromString(
1337
+ "known_hosts",
1338
+ `${l3EndpointToString(endpoint)} ${resolvedCredentials.hostKey}`,
1339
+ { mode: 420 }
1340
+ )
1341
+ })
1443
1342
  }
1343
+ });
1344
+ }
1345
+ function generateSshPrivateKey() {
1346
+ const seed = randomBytes$1(32);
1347
+ return secret(getKeys(seed).privateKey);
1348
+ }
1349
+ function sshPrivateKeyToKeyPair(privateKeyString) {
1350
+ return output(privateKeyString).apply((privateKeyString2) => {
1351
+ const privateKeyStruct = PrivateExport.decode(privateKeyString2);
1352
+ const privKey = privateKeyStruct.keys[0].privKey.privKey;
1353
+ const { fingerprint, publicKey } = getKeys(privKey.slice(0, 32));
1354
+ return output({
1355
+ type: "ed25519",
1356
+ fingerprint,
1357
+ publicKey,
1358
+ privateKey: secret(privateKeyString2)
1359
+ });
1360
+ });
1361
+ }
1362
+ async function createServerBundle(options) {
1363
+ const server = await createServerEntity(options);
1364
+ const ssh = await toPromise(server.ssh);
1365
+ return {
1366
+ server,
1367
+ terminal: ssh ? await createSshTerminal(ssh) : void 0
1368
+ };
1369
+ }
1370
+ async function createServerEntity({
1371
+ name,
1372
+ fallbackHostname,
1373
+ endpoints,
1374
+ sshArgs = { enabled: true, port: 22, user: "root" },
1375
+ sshPassword,
1376
+ sshPrivateKey,
1377
+ sshKeyPair,
1378
+ pingInterval,
1379
+ pingTimeout,
1380
+ waitForPing,
1381
+ waitForSsh,
1382
+ sshCheckInterval,
1383
+ sshCheckTimeout
1384
+ }) {
1385
+ if (endpoints.length === 0) {
1386
+ throw new Error("At least one L3 endpoint is required to create a server entity");
1444
1387
  }
1445
- };
1388
+ fallbackHostname ??= name;
1389
+ waitForSsh ??= sshArgs.enabled;
1390
+ waitForPing ??= !waitForSsh;
1391
+ if (waitForPing) {
1392
+ await Command.waitFor(`${name}.ping`, {
1393
+ host: "local",
1394
+ create: `ping -c 1 ${l3EndpointToString(endpoints[0])}`,
1395
+ timeout: pingTimeout ?? 300,
1396
+ interval: pingInterval ?? 5,
1397
+ triggers: [Date.now()]
1398
+ }).wait();
1399
+ }
1400
+ if (!sshArgs.enabled) {
1401
+ return output({
1402
+ hostname: name,
1403
+ endpoints
1404
+ });
1405
+ }
1406
+ const sshHost = sshArgs?.host ?? l3EndpointToString(endpoints[0]);
1407
+ if (waitForSsh) {
1408
+ await Command.waitFor(`${name}.ssh`, {
1409
+ host: "local",
1410
+ create: `nc -zv ${sshHost} ${sshArgs.port}`,
1411
+ timeout: sshCheckTimeout ?? 300,
1412
+ interval: sshCheckInterval ?? 5,
1413
+ triggers: [Date.now()]
1414
+ }).wait();
1415
+ }
1416
+ const connection = output({
1417
+ host: sshHost,
1418
+ port: sshArgs.port,
1419
+ user: sshArgs.user,
1420
+ password: sshPassword,
1421
+ privateKey: sshKeyPair ? output(sshKeyPair).privateKey : sshPrivateKey,
1422
+ dialErrorLimit: 3
1423
+ });
1424
+ const hostnameResult = new remote.Command("hostname", {
1425
+ connection,
1426
+ create: "hostname",
1427
+ triggers: [Date.now()]
1428
+ });
1429
+ const hostKeyResult = new remote.Command("host-key", {
1430
+ connection,
1431
+ create: "cat /etc/ssh/ssh_host_ed25519_key.pub",
1432
+ triggers: [Date.now()]
1433
+ });
1434
+ return output({
1435
+ endpoints,
1436
+ hostname: hostnameResult.stdout.apply((x) => x.trim()),
1437
+ ssh: {
1438
+ endpoints: [l3EndpointToL4(sshHost, sshArgs.port ?? 22)],
1439
+ user: sshArgs.user ?? "root",
1440
+ hostKey: hostKeyResult.stdout.apply((x) => x.trim()),
1441
+ password: connection.password,
1442
+ keyPair: sshKeyPair ? sshKeyPair : sshPrivateKey ? sshPrivateKeyToKeyPair(sshPrivateKey) : void 0
1443
+ }
1444
+ });
1445
+ }
1446
1446
 
1447
1447
  export { AccessPointRoute, Command, DnsRecord, DnsRecordSet, GatewayRoute, ImplementationMediator, MaterializedFile, MaterializedFolder, TlsCertificate, archiveFromFolder, assetFromFile, createServerBundle, createServerEntity, createSshTerminal, dnsRecordMediator, fetchFileSize, filterEndpoints, gatewayRouteMediator, generateKey, generatePassword, generateSshPrivateKey, getNameByEndpoint, getServerConnection, l34EndpointToString, l3EndpointToCidr, l3EndpointToL4, l3EndpointToString, l4EndpointToString, l4EndpointWithProtocolToString, l7EndpointToString, parseEndpoints, parseL34Endpoint, parseL3Endpoint, parseL4Endpoint, parseL7Endpoint, requireInputL3Endpoint, requireInputL4Endpoint, sshPrivateKeyToKeyPair, tlsCertificateMediator, updateEndpoints, updateEndpointsWithFqdn };
1448
- //# sourceMappingURL=chunk-WDYIUWYZ.js.map
1449
- //# sourceMappingURL=chunk-WDYIUWYZ.js.map
1448
+ //# sourceMappingURL=chunk-FHSPC2ZL.js.map
1449
+ //# sourceMappingURL=chunk-FHSPC2ZL.js.map