@highstate/common 0.7.11 → 0.8.0

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,6 +1,9 @@
1
1
  // src/shared/server.ts
2
2
  import { remote } from "@pulumi/command";
3
- import { output } from "@highstate/pulumi";
3
+ import {
4
+ interpolate,
5
+ output
6
+ } from "@highstate/pulumi";
4
7
  import "@highstate/library";
5
8
  function getServerConnection(server) {
6
9
  return output(server).apply((server2) => ({
@@ -32,7 +35,20 @@ var Server = class {
32
35
  connection: this.connection,
33
36
  create: options.create,
34
37
  update: options.update,
35
- delete: options.delete
38
+ delete: options.delete,
39
+ logging: options.logging
40
+ },
41
+ { dependsOn: options.dependsOn }
42
+ );
43
+ }
44
+ file(options) {
45
+ const escapedContent = output(options.content).apply((content) => content.replace(/"/g, '\\"'));
46
+ return new remote.Command(
47
+ options.id,
48
+ {
49
+ connection: this.connection,
50
+ create: interpolate`mkdir -p $(dirname ${options.path}) && echo "${escapedContent}" > ${options.path}`,
51
+ delete: interpolate`rm -rf ${options.path}`
36
52
  },
37
53
  { dependsOn: options.dependsOn }
38
54
  );
@@ -66,7 +82,7 @@ function n(...t) {
66
82
  var e = (t) => `${t[0]?.toUpperCase() ?? ""}${t.slice(1)}`;
67
83
 
68
84
  // src/shared/dns.ts
69
- var DnsRecord = class extends ComponentResource {
85
+ var DnsRecord = class _DnsRecord extends ComponentResource {
70
86
  /**
71
87
  * The underlying dns record resource.
72
88
  */
@@ -76,7 +92,6 @@ var DnsRecord = class extends ComponentResource {
76
92
  this.dnsRecord = output2(args).apply((args2) => {
77
93
  return output2(this.create(name, args2, { ...opts, parent: this }));
78
94
  });
79
- this.registerOutputs({ dnsRecord: this.dnsRecord });
80
95
  }
81
96
  static create(name, args, opts) {
82
97
  return output2(args).apply(async (args2) => {
@@ -87,6 +102,20 @@ var DnsRecord = class extends ComponentResource {
87
102
  return new implClass(name, args2, opts);
88
103
  });
89
104
  }
105
+ static createSet(name, args, opts) {
106
+ return output2(args).apply((args2) => {
107
+ const recordName = args2.name ?? name;
108
+ return output2(
109
+ args2.providers.filter((provider) => recordName.endsWith(provider.domain)).map(
110
+ (provider) => _DnsRecord.create(
111
+ `${name}.${provider.type}`,
112
+ { name: recordName, ...args2, provider },
113
+ opts
114
+ )
115
+ )
116
+ );
117
+ });
118
+ }
90
119
  };
91
120
 
92
121
  // src/shared/passwords.ts
@@ -165,4 +194,4 @@ export {
165
194
  getKeysFromString,
166
195
  getOrCreateSshKeyPair
167
196
  };
168
- //# sourceMappingURL=chunk-ITZRY7W5.js.map
197
+ //# sourceMappingURL=chunk-PP7IKBFF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/server.ts","../src/shared/dns.ts","../../../node_modules/remeda/dist/chunk-D6FCK2GA.js","../../../node_modules/remeda/dist/chunk-WIMGWYZL.js","../../../node_modules/remeda/dist/chunk-VG2NVNXT.js","../src/shared/passwords.ts","../src/shared/ssh.ts"],"sourcesContent":["import { remote, type types } from \"@pulumi/command\"\nimport {\n interpolate,\n output,\n Resource,\n type Input,\n type InputOrArray,\n type Output,\n} from \"@highstate/pulumi\"\nimport { common } from \"@highstate/library\"\n\nexport function getServerConnection(\n server: Input<common.Server>,\n): Output<types.input.remote.ConnectionArgs> {\n return output(server).apply(server => ({\n host: server.endpoint,\n port: server.sshCredentials?.port ?? 22,\n user: server.sshCredentials?.user ?? \"root\",\n password: server.sshCredentials?.password,\n privateKey: server.sshCredentials?.privateKey,\n dialErrorLimit: 3,\n }))\n}\n\nexport interface CommandOptions {\n id: string\n create: Input<string>\n update?: Input<string>\n delete?: Input<string>\n logging?: Input<remote.Logging>\n dependsOn?: InputOrArray<Resource>\n}\n\nexport interface FileOptions {\n id: string\n path: Input<string>\n content: Input<string>\n dependsOn?: InputOrArray<Resource>\n}\n\nexport class Server {\n public readonly server: Output<common.Server>\n public readonly connection: Output<types.input.remote.ConnectionArgs>\n\n public get endpoint(): Output<string> {\n return this.server.endpoint\n }\n\n public get hostname(): Output<string> {\n return this.server.hostname\n }\n\n constructor(server: Input<common.Server>) {\n this.server = output(server)\n this.connection = getServerConnection(this.server)\n }\n\n public command(options: CommandOptions): remote.Command {\n return new remote.Command(\n options.id,\n {\n connection: this.connection,\n create: options.create,\n update: options.update,\n delete: options.delete,\n logging: options.logging,\n },\n { dependsOn: options.dependsOn },\n )\n }\n\n public file(options: FileOptions): remote.Command {\n const escapedContent = output(options.content).apply(content => content.replace(/\"/g, '\\\\\"'))\n\n return new remote.Command(\n options.id,\n {\n connection: this.connection,\n create: interpolate`mkdir -p $(dirname ${options.path}) && echo \"${escapedContent}\" > ${options.path}`,\n delete: interpolate`rm -rf ${options.path}`,\n },\n { dependsOn: options.dependsOn },\n )\n }\n}\n","import type { dns } from \"@highstate/library\"\nimport {\n ComponentResource,\n output,\n Output,\n Resource,\n type Input,\n type ResourceOptions,\n type Unwrap,\n} from \"@highstate/pulumi\"\nimport { capitalize } from \"remeda\"\n\nexport type DnsRecordArgs = {\n /**\n * The DNS provider to use.\n */\n provider: Input<dns.Provider>\n\n /**\n * The name of the DNS record.\n * If not provided, the name of the resource will be used.\n */\n name?: Input<string>\n\n /**\n * The type of the DNS record.\n */\n type: Input<string>\n\n /**\n * The value of the DNS record.\n */\n value: Input<string>\n\n /**\n * Whether the DNS record is proxied (e.g. to provide DDoS protection).\n *\n * Available only for public IPs and some DNS providers like Cloudflare.\n * If not supported, the DNS provider will ignore this value.\n */\n proxied?: Input<boolean>\n}\n\nexport type DnsRecordSetArgs = Omit<DnsRecordArgs, \"provider\"> & {\n /**\n * The DNS providers to use to create the DNS records.\n *\n * If multiple providers matched the specified domain, multiple DNS records will be created.\n */\n providers: Input<dns.Provider[]>\n}\n\nexport abstract class DnsRecord extends ComponentResource {\n /**\n * The underlying dns record resource.\n */\n public readonly dnsRecord: Output<Resource>\n\n constructor(name: string, args: DnsRecordArgs, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecord\", name, args, opts)\n\n this.dnsRecord = output(args).apply(args => {\n return output(this.create(name, args, { ...opts, parent: this }))\n })\n }\n\n protected abstract create(\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ): Input<Resource>\n\n static create(name: string, args: DnsRecordArgs, opts?: ResourceOptions): Output<DnsRecord> {\n return output(args).apply(async args => {\n const providerType = args.provider.type\n const implName = `${capitalize(providerType)}DnsRecord`\n const implModule = (await import(`@highstate/${providerType}`)) as Record<string, unknown>\n\n const implClass = implModule[implName] as new (\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ) => DnsRecord\n\n return new implClass(name, args, opts)\n })\n }\n\n static createSet(\n name: string,\n args: DnsRecordSetArgs,\n opts?: ResourceOptions,\n ): Output<DnsRecord[]> {\n return output(args).apply(args => {\n const recordName = args.name ?? name\n\n return output(\n args.providers\n .filter(provider => recordName.endsWith(provider.domain))\n .map(provider =>\n DnsRecord.create(\n `${name}.${provider.type}`,\n { name: recordName, ...args, provider },\n opts,\n ),\n ),\n )\n })\n }\n}\n","function u(o,n,a){let t=r=>o(r,...n);return a===void 0?t:Object.assign(t,{lazy:a,lazyArgs:n})}export{u as a};\n","import{a as t}from\"./chunk-D6FCK2GA.js\";function u(r,n,o){let a=r.length-n.length;if(a===0)return r(...n);if(a===1)return t(r,n,o);throw new Error(\"Wrong number of arguments\")}export{u as a};\n","import{a as i}from\"./chunk-WIMGWYZL.js\";function n(...t){return i(e,t)}var e=t=>`${t[0]?.toUpperCase()??\"\"}${t.slice(1)}`;export{n as a};\n","import { randomBytes } from \"@noble/hashes/utils\"\nimport { secureMask } from \"micro-key-producer/password.js\"\n\nexport function generatePassword() {\n return secureMask.apply(randomBytes(32)).password\n}\n","import type { common, ssh } from \"@highstate/library\"\nimport {\n getOrCreateSecret,\n getUnitInstanceName,\n output,\n Output,\n type Input,\n type InstanceTerminal,\n} from \"@highstate/pulumi\"\nimport getKeys, { PrivateExport } from \"micro-key-producer/ssh.js\"\nimport { randomBytes } from \"micro-key-producer/utils.js\"\n\nexport function createSshTerminal(server: common.Server): InstanceTerminal {\n const command = [\"ssh\", \"-tt\", \"-o\", \"StrictHostKeyChecking=no\"]\n\n if (server.sshCredentials?.port) {\n command.push(\"-p\", server.sshCredentials.port.toString())\n }\n\n if (server.sshCredentials?.privateKey) {\n command.push(\"-i\", \"/private-key\")\n }\n\n const endpoint = server.sshCredentials?.endpoint ?? server.endpoint\n const user = server.sshCredentials?.user ?? \"root\"\n\n command.push(`${user}@${endpoint}`)\n\n if (server.sshCredentials?.password) {\n command.unshift(\"sshpass\", \"-f\", \"/password\")\n }\n\n return {\n name: \"ssh\",\n title: `SSH: ${getUnitInstanceName()}`,\n description: \"Connect to the server via SSH\",\n image: \"ghcr.io/exeteres/highstate/terminal-ssh\",\n command,\n\n files: {\n \"/password\": server.sshCredentials?.password,\n\n \"/private-key\": {\n content: server.sshCredentials?.privateKey,\n mode: 0o600,\n },\n },\n }\n}\n\nexport function generatePrivateKey(): string {\n const seed = randomBytes(32)\n\n return getKeys(seed).privateKey\n}\n\nexport function getKeysFromString(privateKey: string) {\n const privateKeyStruct = PrivateExport.decode(privateKey)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array\n\n return getKeys(privKey.slice(0, 32))\n}\n\nexport type SshKeyPairInputs = {\n sshKeyPair?: Input<ssh.KeyPair>\n}\n\nexport type SshKeyPairSecrets = {\n sshPrivateKey?: Input<string>\n}\n\nexport function getOrCreateSshKeyPair(\n inputs: SshKeyPairInputs,\n secrets: Output<SshKeyPairSecrets>,\n): Output<ssh.KeyPair> {\n if (inputs.sshKeyPair) {\n return output(inputs.sshKeyPair)\n }\n\n const privateKey = getOrCreateSecret(secrets, \"sshPrivateKey\", generatePrivateKey)\n const keys = privateKey.apply(getKeysFromString)\n\n return output({\n type: \"ed25519\",\n privateKey,\n publicKey: keys.publicKey,\n })\n}\n"],"mappings":"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","names":["server","output","n","u","n","u","output","args","output","randomBytes"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createSshTerminal
3
- } from "../chunk-ITZRY7W5.js";
3
+ } from "../chunk-PP7IKBFF.js";
4
4
 
5
5
  // src/existing-server/index.ts
6
6
  import { common } from "@highstate/library";
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "sourceHashes": {
3
- "./dist/index.js": "7431e121a60c2ff72e7952ca4b0e4202c67511508f7feaaffa53147d6bad33a2",
3
+ "./dist/index.js": "995a7cf7e90a6b56f2699a875b30ef0f67d98502e9ae029bfa677090918bb693",
4
4
  "./dist/dns/record/index.js": "e4f4558eaa29c589553e0c99b2f12e29e55e3db434977685b4f0e033e5666ac3",
5
- "./dist/existing-server/index.js": "4f8954bc6fdcf0a4ba735dc1ebb554baba9888b747f39174033067d40e7a15ab",
6
- "./dist/ssh/key-pair/index.js": "22240fd3e4e2f1b89e21a67b4159ad434362541f7eaa8a1b671fc7b6ad7d0c38",
7
- "./dist/script/index.js": "9769c699a1a6febb34617c6b4f180ac643aefab7aa1f1d7c391f7a4ec80c517a"
5
+ "./dist/existing-server/index.js": "79e982230b45807e159378accf67b7b4623586aa2481eea2857dfc77fe2d1ca0",
6
+ "./dist/ssh/key-pair/index.js": "ebb4001477598f682f7d732baf2fb0494b97d26b1d7d294ffa3e73c7f2778ed9",
7
+ "./dist/script/index.js": "7d03209b0ff1778f1be33a86dba8d1e0e59f4cbb44c82fedb4efb62f60e97652"
8
8
  }
9
9
  }
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getKeysFromString,
8
8
  getOrCreateSshKeyPair,
9
9
  getServerConnection
10
- } from "./chunk-ITZRY7W5.js";
10
+ } from "./chunk-PP7IKBFF.js";
11
11
  export {
12
12
  DnsRecord,
13
13
  Server,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Server
3
- } from "../chunk-ITZRY7W5.js";
3
+ } from "../chunk-PP7IKBFF.js";
4
4
 
5
5
  // src/script/index.ts
6
6
  import { common } from "@highstate/library";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  generatePrivateKey,
3
3
  getKeysFromString
4
- } from "../../chunk-ITZRY7W5.js";
4
+ } from "../../chunk-PP7IKBFF.js";
5
5
 
6
6
  // src/ssh/key-pair/index.ts
7
7
  import { ssh } from "@highstate/library";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highstate/common",
3
- "version": "0.7.11",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,7 +23,7 @@
23
23
  "build": "highstate build"
24
24
  },
25
25
  "dependencies": {
26
- "@highstate/pulumi": "^0.7.11",
26
+ "@highstate/pulumi": "^0.8.0",
27
27
  "@noble/hashes": "^1.7.1",
28
28
  "@pulumi/command": "^1.0.2",
29
29
  "micro-key-producer": "^0.7.3"
@@ -32,7 +32,7 @@
32
32
  "@highstate/library": "workspace:^0.4.4"
33
33
  },
34
34
  "devDependencies": {
35
- "@highstate/cli": "^0.7.11"
35
+ "@highstate/cli": "^0.8.0"
36
36
  },
37
- "gitHead": "f425f2be2d5800fdee3512c848129adab1b0186e"
37
+ "gitHead": "8590eea089a016c9b4b797299fc94ddc9afe10ba"
38
38
  }
package/src/shared/dns.ts CHANGED
@@ -41,6 +41,15 @@ export type DnsRecordArgs = {
41
41
  proxied?: Input<boolean>
42
42
  }
43
43
 
44
+ export type DnsRecordSetArgs = Omit<DnsRecordArgs, "provider"> & {
45
+ /**
46
+ * The DNS providers to use to create the DNS records.
47
+ *
48
+ * If multiple providers matched the specified domain, multiple DNS records will be created.
49
+ */
50
+ providers: Input<dns.Provider[]>
51
+ }
52
+
44
53
  export abstract class DnsRecord extends ComponentResource {
45
54
  /**
46
55
  * The underlying dns record resource.
@@ -53,8 +62,6 @@ export abstract class DnsRecord extends ComponentResource {
53
62
  this.dnsRecord = output(args).apply(args => {
54
63
  return output(this.create(name, args, { ...opts, parent: this }))
55
64
  })
56
-
57
- this.registerOutputs({ dnsRecord: this.dnsRecord })
58
65
  }
59
66
 
60
67
  protected abstract create(
@@ -78,4 +85,26 @@ export abstract class DnsRecord extends ComponentResource {
78
85
  return new implClass(name, args, opts)
79
86
  })
80
87
  }
88
+
89
+ static createSet(
90
+ name: string,
91
+ args: DnsRecordSetArgs,
92
+ opts?: ResourceOptions,
93
+ ): Output<DnsRecord[]> {
94
+ return output(args).apply(args => {
95
+ const recordName = args.name ?? name
96
+
97
+ return output(
98
+ args.providers
99
+ .filter(provider => recordName.endsWith(provider.domain))
100
+ .map(provider =>
101
+ DnsRecord.create(
102
+ `${name}.${provider.type}`,
103
+ { name: recordName, ...args, provider },
104
+ opts,
105
+ ),
106
+ ),
107
+ )
108
+ })
109
+ }
81
110
  }
@@ -1,5 +1,12 @@
1
1
  import { remote, type types } from "@pulumi/command"
2
- import { output, Resource, type Input, type InputOrArray, type Output } from "@highstate/pulumi"
2
+ import {
3
+ interpolate,
4
+ output,
5
+ Resource,
6
+ type Input,
7
+ type InputOrArray,
8
+ type Output,
9
+ } from "@highstate/pulumi"
3
10
  import { common } from "@highstate/library"
4
11
 
5
12
  export function getServerConnection(
@@ -20,6 +27,14 @@ export interface CommandOptions {
20
27
  create: Input<string>
21
28
  update?: Input<string>
22
29
  delete?: Input<string>
30
+ logging?: Input<remote.Logging>
31
+ dependsOn?: InputOrArray<Resource>
32
+ }
33
+
34
+ export interface FileOptions {
35
+ id: string
36
+ path: Input<string>
37
+ content: Input<string>
23
38
  dependsOn?: InputOrArray<Resource>
24
39
  }
25
40
 
@@ -48,6 +63,21 @@ export class Server {
48
63
  create: options.create,
49
64
  update: options.update,
50
65
  delete: options.delete,
66
+ logging: options.logging,
67
+ },
68
+ { dependsOn: options.dependsOn },
69
+ )
70
+ }
71
+
72
+ public file(options: FileOptions): remote.Command {
73
+ const escapedContent = output(options.content).apply(content => content.replace(/"/g, '\\"'))
74
+
75
+ return new remote.Command(
76
+ options.id,
77
+ {
78
+ connection: this.connection,
79
+ create: interpolate`mkdir -p $(dirname ${options.path}) && echo "${escapedContent}" > ${options.path}`,
80
+ delete: interpolate`rm -rf ${options.path}`,
51
81
  },
52
82
  { dependsOn: options.dependsOn },
53
83
  )
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/server.ts","../src/shared/dns.ts","../../../node_modules/remeda/dist/chunk-D6FCK2GA.js","../../../node_modules/remeda/dist/chunk-WIMGWYZL.js","../../../node_modules/remeda/dist/chunk-VG2NVNXT.js","../src/shared/passwords.ts","../src/shared/ssh.ts"],"sourcesContent":["import { remote, type types } from \"@pulumi/command\"\nimport { output, Resource, type Input, type InputOrArray, type Output } from \"@highstate/pulumi\"\nimport { common } from \"@highstate/library\"\n\nexport function getServerConnection(\n server: Input<common.Server>,\n): Output<types.input.remote.ConnectionArgs> {\n return output(server).apply(server => ({\n host: server.endpoint,\n port: server.sshCredentials?.port ?? 22,\n user: server.sshCredentials?.user ?? \"root\",\n password: server.sshCredentials?.password,\n privateKey: server.sshCredentials?.privateKey,\n dialErrorLimit: 3,\n }))\n}\n\nexport interface CommandOptions {\n id: string\n create: Input<string>\n update?: Input<string>\n delete?: Input<string>\n dependsOn?: InputOrArray<Resource>\n}\n\nexport class Server {\n public readonly server: Output<common.Server>\n public readonly connection: Output<types.input.remote.ConnectionArgs>\n\n public get endpoint(): Output<string> {\n return this.server.endpoint\n }\n\n public get hostname(): Output<string> {\n return this.server.hostname\n }\n\n constructor(server: Input<common.Server>) {\n this.server = output(server)\n this.connection = getServerConnection(this.server)\n }\n\n public command(options: CommandOptions): remote.Command {\n return new remote.Command(\n options.id,\n {\n connection: this.connection,\n create: options.create,\n update: options.update,\n delete: options.delete,\n },\n { dependsOn: options.dependsOn },\n )\n }\n}\n","import type { dns } from \"@highstate/library\"\nimport {\n ComponentResource,\n output,\n Output,\n Resource,\n type Input,\n type ResourceOptions,\n type Unwrap,\n} from \"@highstate/pulumi\"\nimport { capitalize } from \"remeda\"\n\nexport type DnsRecordArgs = {\n /**\n * The DNS provider to use.\n */\n provider: Input<dns.Provider>\n\n /**\n * The name of the DNS record.\n * If not provided, the name of the resource will be used.\n */\n name?: Input<string>\n\n /**\n * The type of the DNS record.\n */\n type: Input<string>\n\n /**\n * The value of the DNS record.\n */\n value: Input<string>\n\n /**\n * Whether the DNS record is proxied (e.g. to provide DDoS protection).\n *\n * Available only for public IPs and some DNS providers like Cloudflare.\n * If not supported, the DNS provider will ignore this value.\n */\n proxied?: Input<boolean>\n}\n\nexport abstract class DnsRecord extends ComponentResource {\n /**\n * The underlying dns record resource.\n */\n public readonly dnsRecord: Output<Resource>\n\n constructor(name: string, args: DnsRecordArgs, opts?: ResourceOptions) {\n super(\"highstate:common:DnsRecord\", name, args, opts)\n\n this.dnsRecord = output(args).apply(args => {\n return output(this.create(name, args, { ...opts, parent: this }))\n })\n\n this.registerOutputs({ dnsRecord: this.dnsRecord })\n }\n\n protected abstract create(\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ): Input<Resource>\n\n static create(name: string, args: DnsRecordArgs, opts?: ResourceOptions): Output<DnsRecord> {\n return output(args).apply(async args => {\n const providerType = args.provider.type\n const implName = `${capitalize(providerType)}DnsRecord`\n const implModule = (await import(`@highstate/${providerType}`)) as Record<string, unknown>\n\n const implClass = implModule[implName] as new (\n name: string,\n args: Unwrap<DnsRecordArgs>,\n opts?: ResourceOptions,\n ) => DnsRecord\n\n return new implClass(name, args, opts)\n })\n }\n}\n","function u(o,n,a){let t=r=>o(r,...n);return a===void 0?t:Object.assign(t,{lazy:a,lazyArgs:n})}export{u as a};\n","import{a as t}from\"./chunk-D6FCK2GA.js\";function u(r,n,o){let a=r.length-n.length;if(a===0)return r(...n);if(a===1)return t(r,n,o);throw new Error(\"Wrong number of arguments\")}export{u as a};\n","import{a as i}from\"./chunk-WIMGWYZL.js\";function n(...t){return i(e,t)}var e=t=>`${t[0]?.toUpperCase()??\"\"}${t.slice(1)}`;export{n as a};\n","import { randomBytes } from \"@noble/hashes/utils\"\nimport { secureMask } from \"micro-key-producer/password.js\"\n\nexport function generatePassword() {\n return secureMask.apply(randomBytes(32)).password\n}\n","import type { common, ssh } from \"@highstate/library\"\nimport {\n getOrCreateSecret,\n getUnitInstanceName,\n output,\n Output,\n type Input,\n type InstanceTerminal,\n} from \"@highstate/pulumi\"\nimport getKeys, { PrivateExport } from \"micro-key-producer/ssh.js\"\nimport { randomBytes } from \"micro-key-producer/utils.js\"\n\nexport function createSshTerminal(server: common.Server): InstanceTerminal {\n const command = [\"ssh\", \"-tt\", \"-o\", \"StrictHostKeyChecking=no\"]\n\n if (server.sshCredentials?.port) {\n command.push(\"-p\", server.sshCredentials.port.toString())\n }\n\n if (server.sshCredentials?.privateKey) {\n command.push(\"-i\", \"/private-key\")\n }\n\n const endpoint = server.sshCredentials?.endpoint ?? server.endpoint\n const user = server.sshCredentials?.user ?? \"root\"\n\n command.push(`${user}@${endpoint}`)\n\n if (server.sshCredentials?.password) {\n command.unshift(\"sshpass\", \"-f\", \"/password\")\n }\n\n return {\n name: \"ssh\",\n title: `SSH: ${getUnitInstanceName()}`,\n description: \"Connect to the server via SSH\",\n image: \"ghcr.io/exeteres/highstate/terminal-ssh\",\n command,\n\n files: {\n \"/password\": server.sshCredentials?.password,\n\n \"/private-key\": {\n content: server.sshCredentials?.privateKey,\n mode: 0o600,\n },\n },\n }\n}\n\nexport function generatePrivateKey(): string {\n const seed = randomBytes(32)\n\n return getKeys(seed).privateKey\n}\n\nexport function getKeysFromString(privateKey: string) {\n const privateKeyStruct = PrivateExport.decode(privateKey)\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array\n\n return getKeys(privKey.slice(0, 32))\n}\n\nexport type SshKeyPairInputs = {\n sshKeyPair?: Input<ssh.KeyPair>\n}\n\nexport type SshKeyPairSecrets = {\n sshPrivateKey?: Input<string>\n}\n\nexport function getOrCreateSshKeyPair(\n inputs: SshKeyPairInputs,\n secrets: Output<SshKeyPairSecrets>,\n): Output<ssh.KeyPair> {\n if (inputs.sshKeyPair) {\n return output(inputs.sshKeyPair)\n }\n\n const privateKey = getOrCreateSecret(secrets, \"sshPrivateKey\", generatePrivateKey)\n const keys = privateKey.apply(getKeysFromString)\n\n return output({\n type: \"ed25519\",\n privateKey,\n publicKey: keys.publicKey,\n })\n}\n"],"mappings":"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","names":["server","output","n","u","n","u","output","args","output","randomBytes"]}