@highstate/common 0.9.4 → 0.9.5

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 (48) hide show
  1. package/dist/chunk-USV6SHAU.js +530 -0
  2. package/dist/chunk-USV6SHAU.js.map +1 -0
  3. package/dist/highstate.manifest.json +9 -5
  4. package/dist/index.js +33 -7
  5. package/dist/units/dns/record-set/index.js +18 -0
  6. package/dist/units/dns/record-set/index.js.map +1 -0
  7. package/dist/units/existing-server/index.js +34 -0
  8. package/dist/units/existing-server/index.js.map +1 -0
  9. package/dist/units/network/l3-endpoint/index.js +15 -0
  10. package/dist/units/network/l3-endpoint/index.js.map +1 -0
  11. package/dist/units/network/l4-endpoint/index.js +15 -0
  12. package/dist/units/network/l4-endpoint/index.js.map +1 -0
  13. package/dist/{script → units/script}/index.js +5 -6
  14. package/dist/units/script/index.js.map +1 -0
  15. package/dist/units/server-dns/index.js +30 -0
  16. package/dist/units/server-dns/index.js.map +1 -0
  17. package/dist/units/server-patch/index.js +29 -0
  18. package/dist/units/server-patch/index.js.map +1 -0
  19. package/dist/units/ssh/key-pair/index.js +22 -0
  20. package/dist/units/ssh/key-pair/index.js.map +1 -0
  21. package/package.json +15 -10
  22. package/src/shared/command.ts +132 -0
  23. package/src/shared/dns.ts +209 -21
  24. package/src/shared/index.ts +2 -2
  25. package/src/shared/network.ts +311 -0
  26. package/src/shared/ssh.ts +111 -38
  27. package/src/units/dns/record-set/index.ts +16 -0
  28. package/src/units/existing-server/index.ts +34 -0
  29. package/src/units/network/l3-endpoint/index.ts +9 -0
  30. package/src/units/network/l4-endpoint/index.ts +9 -0
  31. package/src/{script → units/script}/index.ts +3 -5
  32. package/src/units/server-dns/index.ts +26 -0
  33. package/src/units/server-patch/index.ts +25 -0
  34. package/src/units/ssh/key-pair/index.ts +16 -0
  35. package/dist/chunk-ZA27FN5N.js +0 -214
  36. package/dist/chunk-ZA27FN5N.js.map +0 -1
  37. package/dist/dns/record/index.js +0 -1
  38. package/dist/dns/record/index.js.map +0 -1
  39. package/dist/existing-server/index.js +0 -48
  40. package/dist/existing-server/index.js.map +0 -1
  41. package/dist/script/index.js.map +0 -1
  42. package/dist/ssh/key-pair/index.js +0 -30
  43. package/dist/ssh/key-pair/index.js.map +0 -1
  44. package/src/dns/record/index.ts +0 -0
  45. package/src/existing-server/index.ts +0 -46
  46. package/src/shared/server.ts +0 -85
  47. package/src/shared/utils.ts +0 -18
  48. package/src/ssh/key-pair/index.ts +0 -24
package/src/shared/ssh.ts CHANGED
@@ -1,51 +1,65 @@
1
- import type { common, ssh } from "@highstate/library"
1
+ import type { common, network, ssh } from "@highstate/library"
2
2
  import {
3
3
  getOrCreateSecret,
4
4
  getUnitInstanceName,
5
5
  output,
6
6
  Output,
7
+ secret,
7
8
  type Input,
8
9
  type InstanceTerminal,
9
10
  } from "@highstate/pulumi"
10
11
  import getKeys, { PrivateExport } from "micro-key-producer/ssh.js"
11
12
  import { randomBytes } from "micro-key-producer/utils.js"
13
+ import { local, remote } from "@pulumi/command"
14
+ import { l3EndpointToString, l3ToL4Endpoint, l4EndpointToString } from "./network"
12
15
 
13
- export function createSshTerminal(server: common.Server): InstanceTerminal {
14
- const command = ["ssh", "-tt", "-o", "StrictHostKeyChecking=no"]
16
+ export function createSshTerminal(
17
+ credentials: Input<ssh.Credentials | undefined>,
18
+ ): Output<InstanceTerminal | undefined> {
19
+ return output(credentials).apply(credentials => {
20
+ if (!credentials) {
21
+ return undefined
22
+ }
15
23
 
16
- if (server.sshCredentials?.port) {
17
- command.push("-p", server.sshCredentials.port.toString())
18
- }
24
+ const command = ["ssh", "-tt", "-o", "UserKnownHostsFile=/known_hosts"]
19
25
 
20
- if (server.sshCredentials?.privateKey) {
21
- command.push("-i", "/private-key")
22
- }
26
+ // TODO: select best endpoint based on the environment
27
+ const endpoint = credentials.endpoints[0]
23
28
 
24
- const endpoint = server.sshCredentials?.endpoint ?? server.endpoint
25
- const user = server.sshCredentials?.user ?? "root"
29
+ command.push("-p", endpoint.port.toString())
26
30
 
27
- command.push(`${user}@${endpoint}`)
31
+ if (credentials.keyPair) {
32
+ command.push("-i", "/private_key")
33
+ }
28
34
 
29
- if (server.sshCredentials?.password) {
30
- command.unshift("sshpass", "-f", "/password")
31
- }
35
+ command.push(`${credentials.user}@${l3EndpointToString(endpoint)}`)
36
+
37
+ if (credentials.password) {
38
+ command.unshift("sshpass", "-f", "/password")
39
+ }
32
40
 
33
- return {
34
- name: "ssh",
35
- title: `SSH: ${getUnitInstanceName()}`,
36
- description: "Connect to the server via SSH",
37
- image: "ghcr.io/exeteres/highstate/terminal-ssh",
38
- command,
41
+ return {
42
+ name: "ssh",
43
+ title: `SSH: ${getUnitInstanceName()}`,
44
+ description: "Connect to the server via SSH",
45
+ image: "ghcr.io/exeteres/highstate/terminal-ssh",
46
+ command,
39
47
 
40
- files: {
41
- "/password": server.sshCredentials?.password,
48
+ files: {
49
+ "/password": credentials.password,
42
50
 
43
- "/private-key": {
44
- content: server.sshCredentials?.privateKey,
45
- mode: 0o600,
51
+ "/private_key": {
52
+ content: credentials.keyPair?.privateKey,
53
+ mode: 0o600,
54
+ },
55
+
56
+ "/known_hosts": {
57
+ content: `${l4EndpointToString(endpoint)} ${credentials.hostKey}`,
58
+ mode: 0o644,
59
+ },
46
60
  },
47
- },
48
- }
61
+ }
62
+ })
49
63
  }
50
64
 
51
65
  export function generatePrivateKey(): string {
@@ -54,13 +68,22 @@ export function generatePrivateKey(): string {
54
68
  return getKeys(seed).privateKey
55
69
  }
56
70
 
57
- export function getKeysFromString(privateKey: string) {
58
- const privateKeyStruct = PrivateExport.decode(privateKey)
71
+ export function privateKeyToKeyPair(privateKeyString: Input<string>): Output<ssh.KeyPair> {
72
+ return output(privateKeyString).apply(privateKeyString => {
73
+ const privateKeyStruct = PrivateExport.decode(privateKeyString)
74
+
75
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
76
+ const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array
59
77
 
60
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
61
- const privKey = privateKeyStruct.keys[0].privKey.privKey as Uint8Array
78
+ const { fingerprint, publicKey, privateKey } = getKeys(privKey.slice(0, 32))
62
79
 
63
- return getKeys(privKey.slice(0, 32))
80
+ return output({
81
+ type: "ed25519" as const,
82
+ fingerprint,
83
+ publicKey,
84
+ privateKey: secret(privateKey),
85
+ })
86
+ })
64
87
  }
65
88
 
66
89
  export type SshKeyPairInputs = {
@@ -80,11 +103,61 @@ export function getOrCreateSshKeyPair(
80
103
  }
81
104
 
82
105
  const privateKey = getOrCreateSecret(secrets, "sshPrivateKey", generatePrivateKey)
83
- const keys = privateKey.apply(getKeysFromString)
84
106
 
85
- return output({
86
- type: "ed25519",
87
- privateKey,
88
- publicKey: keys.publicKey,
107
+ return privateKey.apply(privateKeyToKeyPair)
108
+ }
109
+
110
+ export function createServerEntity(
111
+ fallbackHostname: string,
112
+ endpoint: network.L3Endpoint,
113
+ sshPort = 22,
114
+ sshUser = "root",
115
+ sshPassword?: Input<string | undefined>,
116
+ sshPrivateKey?: Input<string>,
117
+ ): Output<common.Server> {
118
+ const connection = output({
119
+ host: l3EndpointToString(endpoint),
120
+ port: sshPort,
121
+ user: sshUser,
122
+ password: sshPassword,
123
+ privateKey: sshPrivateKey,
124
+ dialErrorLimit: 3,
125
+ })
126
+
127
+ const command = new local.Command("check-ssh", {
128
+ create: `nc -zv ${l3EndpointToString(endpoint)} ${sshPort} && echo "up" || echo "down"`,
129
+ })
130
+
131
+ return command.stdout.apply(result => {
132
+ if (result === "down") {
133
+ return output({
134
+ hostname: fallbackHostname,
135
+ endpoints: [endpoint],
136
+ })
137
+ }
138
+
139
+ const hostnameResult = new remote.Command("hostname", {
140
+ connection,
141
+ create: "hostname",
142
+ triggers: [Date.now()],
143
+ })
144
+
145
+ const hostKeyResult = new remote.Command("host-key", {
146
+ connection,
147
+ create: "cat /etc/ssh/ssh_host_ed25519_key.pub",
148
+ triggers: [Date.now()],
149
+ })
150
+
151
+ return output({
152
+ endpoints: [endpoint],
153
+ hostname: hostnameResult.stdout.apply(x => x.trim()),
154
+ ssh: {
155
+ endpoints: [l3ToL4Endpoint(endpoint, sshPort)],
156
+ user: sshUser,
157
+ hostKey: hostKeyResult.stdout.apply(x => x.trim()),
158
+ password: sshPassword,
159
+ keyPair: sshPrivateKey ? privateKeyToKeyPair(sshPrivateKey) : undefined,
160
+ },
161
+ })
89
162
  })
90
163
  }
@@ -0,0 +1,16 @@
1
+ import { dns } from "@highstate/library"
2
+ import { forUnit, toPromise } from "@highstate/pulumi"
3
+ import { DnsRecordSet } from "../../../shared"
4
+
5
+ const { name, args, inputs } = forUnit(dns.recordSet)
6
+
7
+ const endpoints = await toPromise(inputs.endpoints)
8
+
9
+ DnsRecordSet.create(name, {
10
+ type: args.type,
11
+ providers: inputs.dnsProviders,
12
+ values: [...args.values, ...endpoints],
13
+ ttl: args.ttl,
14
+ priority: args.priority,
15
+ proxied: args.proxied,
16
+ })
@@ -0,0 +1,34 @@
1
+ import { common } from "@highstate/library"
2
+ import { forUnit, toPromise } from "@highstate/pulumi"
3
+ import {
4
+ createServerEntity,
5
+ createSshTerminal,
6
+ l3EndpointToString,
7
+ requireInputL3Endpoint,
8
+ } from "../../shared"
9
+
10
+ const { name, args, inputs, secrets, outputs } = forUnit(common.existingServer)
11
+
12
+ const endpoint = await requireInputL3Endpoint(args.endpoint, inputs.endpoint)
13
+ const privateKey = await toPromise(inputs.sshKeyPair?.privateKey ?? secrets.sshPrivateKey)
14
+
15
+ const server = createServerEntity(
16
+ name,
17
+ endpoint,
18
+ args.sshPort,
19
+ args.sshUser,
20
+ secrets.sshPassword,
21
+ privateKey,
22
+ )
23
+
24
+ export default outputs({
25
+ server,
26
+ endpoints: server.endpoints,
27
+
28
+ $status: {
29
+ hostname: server.hostname,
30
+ endpoints: server.endpoints.apply(endpoints => endpoints.map(l3EndpointToString)),
31
+ },
32
+
33
+ $terminals: [createSshTerminal(server.ssh)],
34
+ })
@@ -0,0 +1,9 @@
1
+ import { network } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+ import { parseL3Endpoint } from "../../../shared"
4
+
5
+ const { args, outputs } = forUnit(network.l3Endpoint)
6
+
7
+ export default outputs({
8
+ endpoint: parseL3Endpoint(args.endpoint),
9
+ })
@@ -0,0 +1,9 @@
1
+ import { network } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+ import { parseL4Endpoint } from "../../../shared"
4
+
5
+ const { args, outputs } = forUnit(network.l4Endpoint)
6
+
7
+ export default outputs({
8
+ endpoint: parseL4Endpoint(args.endpoint),
9
+ })
@@ -1,13 +1,11 @@
1
1
  import { common } from "@highstate/library"
2
2
  import { forUnit } from "@highstate/pulumi"
3
- import { Server } from "../shared"
3
+ import { Command } from "../../shared"
4
4
 
5
5
  const { name, args, inputs, outputs } = forUnit(common.script)
6
6
 
7
- const server = new Server(inputs.server)
8
-
9
- server.command({
10
- id: name,
7
+ new Command(name, {
8
+ host: inputs.server,
11
9
  create: args.script,
12
10
  update: args.updateScript,
13
11
  delete: args.deleteScript,
@@ -0,0 +1,26 @@
1
+ import { common } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+ import { l3EndpointToString, updateEndpointsWithFqdn } from "../../shared"
4
+
5
+ const { args, inputs, outputs } = forUnit(common.serverDns)
6
+
7
+ const { endpoints } = await updateEndpointsWithFqdn(
8
+ inputs.server.endpoints,
9
+ args.fqdn,
10
+ args.endpointFilter,
11
+ args.patchMode,
12
+ inputs.dnsProviders,
13
+ )
14
+
15
+ export default outputs({
16
+ server: inputs.server.apply(server => ({
17
+ ...server,
18
+ endpoints,
19
+ })),
20
+
21
+ endpoints,
22
+
23
+ $status: {
24
+ endpoints: endpoints.map(l3EndpointToString),
25
+ },
26
+ })
@@ -0,0 +1,25 @@
1
+ import { common } from "@highstate/library"
2
+ import { forUnit } from "@highstate/pulumi"
3
+ import { l3EndpointToString, updateEndpoints } from "../../shared"
4
+
5
+ const { args, inputs, outputs } = forUnit(common.serverPatch)
6
+
7
+ const endpoints = await updateEndpoints(
8
+ inputs.server.endpoints,
9
+ args.endpoints,
10
+ inputs.endpoints,
11
+ args.endpointsPatchMode,
12
+ )
13
+
14
+ export default outputs({
15
+ server: inputs.server.apply(server => ({
16
+ ...server,
17
+ endpoints,
18
+ })),
19
+
20
+ endpoints,
21
+
22
+ $status: {
23
+ endpoints: endpoints.map(l3EndpointToString),
24
+ },
25
+ })
@@ -0,0 +1,16 @@
1
+ import { ssh } from "@highstate/library"
2
+ import { forUnit, getOrCreateSecret } from "@highstate/pulumi"
3
+ import { generatePrivateKey, privateKeyToKeyPair } from "../../../shared"
4
+
5
+ const { secrets, outputs } = forUnit(ssh.keyPair)
6
+
7
+ const privateKey = getOrCreateSecret(secrets, "privateKey", generatePrivateKey)
8
+ const keyPair = privateKeyToKeyPair(privateKey)
9
+
10
+ export default outputs({
11
+ keyPair: privateKeyToKeyPair(privateKey),
12
+ $status: {
13
+ fingerprint: keyPair.fingerprint,
14
+ publicKey: keyPair.publicKey,
15
+ },
16
+ })
@@ -1,214 +0,0 @@
1
- // src/shared/server.ts
2
- import { remote } from "@pulumi/command";
3
- import {
4
- interpolate,
5
- output
6
- } from "@highstate/pulumi";
7
- import "@highstate/library";
8
- function getServerConnection(server) {
9
- return output(server).apply((server2) => ({
10
- host: server2.endpoint,
11
- port: server2.sshCredentials?.port ?? 22,
12
- user: server2.sshCredentials?.user ?? "root",
13
- password: server2.sshCredentials?.password,
14
- privateKey: server2.sshCredentials?.privateKey,
15
- dialErrorLimit: 3
16
- }));
17
- }
18
- var Server = class {
19
- server;
20
- connection;
21
- get endpoint() {
22
- return this.server.endpoint;
23
- }
24
- get hostname() {
25
- return this.server.hostname;
26
- }
27
- constructor(server) {
28
- this.server = output(server);
29
- this.connection = getServerConnection(this.server);
30
- }
31
- command(options) {
32
- return new remote.Command(
33
- options.id,
34
- {
35
- connection: this.connection,
36
- create: options.create,
37
- update: options.update,
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}`
52
- },
53
- { dependsOn: options.dependsOn }
54
- );
55
- }
56
- };
57
-
58
- // src/shared/dns.ts
59
- import {
60
- ComponentResource,
61
- output as output2
62
- } from "@highstate/pulumi";
63
-
64
- // ../../node_modules/remeda/dist/chunk-D6FCK2GA.js
65
- function u(o, n2, a) {
66
- let t = (r) => o(r, ...n2);
67
- return a === void 0 ? t : Object.assign(t, { lazy: a, lazyArgs: n2 });
68
- }
69
-
70
- // ../../node_modules/remeda/dist/chunk-WIMGWYZL.js
71
- function u2(r, n2, o) {
72
- let a = r.length - n2.length;
73
- if (a === 0) return r(...n2);
74
- if (a === 1) return u(r, n2, o);
75
- throw new Error("Wrong number of arguments");
76
- }
77
-
78
- // ../../node_modules/remeda/dist/chunk-VG2NVNXT.js
79
- function n(...t) {
80
- return u2(e, t);
81
- }
82
- var e = (t) => `${t[0]?.toUpperCase() ?? ""}${t.slice(1)}`;
83
-
84
- // src/shared/dns.ts
85
- var DnsRecord = class _DnsRecord extends ComponentResource {
86
- /**
87
- * The underlying dns record resource.
88
- */
89
- dnsRecord;
90
- constructor(name, args, opts) {
91
- super("highstate:common:DnsRecord", name, args, opts);
92
- this.dnsRecord = output2(args).apply((args2) => {
93
- return output2(this.create(name, args2, { ...opts, parent: this }));
94
- });
95
- }
96
- static create(name, args, opts) {
97
- return output2(args).apply(async (args2) => {
98
- const providerType = args2.provider.type;
99
- const implName = `${n(providerType)}DnsRecord`;
100
- const implModule = await import(`@highstate/${providerType}`);
101
- const implClass = implModule[implName];
102
- return new implClass(name, args2, opts);
103
- });
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
- }
119
- };
120
-
121
- // src/shared/passwords.ts
122
- import { randomBytes } from "@noble/hashes/utils";
123
- import { secureMask } from "micro-key-producer/password.js";
124
- function generatePassword() {
125
- return secureMask.apply(randomBytes(32)).password;
126
- }
127
-
128
- // src/shared/ssh.ts
129
- import {
130
- getOrCreateSecret,
131
- getUnitInstanceName,
132
- output as output3
133
- } from "@highstate/pulumi";
134
- import getKeys, { PrivateExport } from "micro-key-producer/ssh.js";
135
- import { randomBytes as randomBytes2 } from "micro-key-producer/utils.js";
136
- function createSshTerminal(server) {
137
- const command = ["ssh", "-tt", "-o", "StrictHostKeyChecking=no"];
138
- if (server.sshCredentials?.port) {
139
- command.push("-p", server.sshCredentials.port.toString());
140
- }
141
- if (server.sshCredentials?.privateKey) {
142
- command.push("-i", "/private-key");
143
- }
144
- const endpoint = server.sshCredentials?.endpoint ?? server.endpoint;
145
- const user = server.sshCredentials?.user ?? "root";
146
- command.push(`${user}@${endpoint}`);
147
- if (server.sshCredentials?.password) {
148
- command.unshift("sshpass", "-f", "/password");
149
- }
150
- return {
151
- name: "ssh",
152
- title: `SSH: ${getUnitInstanceName()}`,
153
- description: "Connect to the server via SSH",
154
- image: "ghcr.io/exeteres/highstate/terminal-ssh",
155
- command,
156
- files: {
157
- "/password": server.sshCredentials?.password,
158
- "/private-key": {
159
- content: server.sshCredentials?.privateKey,
160
- mode: 384
161
- }
162
- }
163
- };
164
- }
165
- function generatePrivateKey() {
166
- const seed = randomBytes2(32);
167
- return getKeys(seed).privateKey;
168
- }
169
- function getKeysFromString(privateKey) {
170
- const privateKeyStruct = PrivateExport.decode(privateKey);
171
- const privKey = privateKeyStruct.keys[0].privKey.privKey;
172
- return getKeys(privKey.slice(0, 32));
173
- }
174
- function getOrCreateSshKeyPair(inputs, secrets) {
175
- if (inputs.sshKeyPair) {
176
- return output3(inputs.sshKeyPair);
177
- }
178
- const privateKey = getOrCreateSecret(secrets, "sshPrivateKey", generatePrivateKey);
179
- const keys = privateKey.apply(getKeysFromString);
180
- return output3({
181
- type: "ed25519",
182
- privateKey,
183
- publicKey: keys.publicKey
184
- });
185
- }
186
-
187
- // src/shared/utils.ts
188
- function l4EndpointToString(l4Endpoint) {
189
- return `${l4Endpoint.endpoint}:${l4Endpoint.port}`;
190
- }
191
- function parseL4Endpoint(l4Endpoint) {
192
- const [endpoint, port] = l4Endpoint.split(":");
193
- if (!port) {
194
- throw new Error(`Invalid L4 endpoint: ${l4Endpoint}`);
195
- }
196
- return {
197
- endpoint,
198
- port: parseInt(port, 10)
199
- };
200
- }
201
-
202
- export {
203
- getServerConnection,
204
- Server,
205
- DnsRecord,
206
- generatePassword,
207
- createSshTerminal,
208
- generatePrivateKey,
209
- getKeysFromString,
210
- getOrCreateSshKeyPair,
211
- l4EndpointToString,
212
- parseL4Endpoint
213
- };
214
- //# sourceMappingURL=chunk-ZA27FN5N.js.map
@@ -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","../src/shared/utils.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 /**\n * The TTL of the DNS record.\n *\n * If not provided, the DNS provider will use its default value.\n */\n ttl?: Input<number>\n\n /**\n * The priority of the DNS record.\n *\n * Only used for some DNS record types (e.g. MX).\n */\n priority?: Input<number>\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","import type { common } from \"@highstate/library\"\n\nexport function l4EndpointToString(l4Endpoint: common.L4Endpoint): string {\n return `${l4Endpoint.endpoint}:${l4Endpoint.port}`\n}\n\nexport function parseL4Endpoint(l4Endpoint: string): common.L4Endpoint {\n const [endpoint, port] = l4Endpoint.split(\":\")\n\n if (!port) {\n throw new Error(`Invalid L4 endpoint: ${l4Endpoint}`)\n }\n\n return {\n endpoint,\n port: parseInt(port, 10),\n }\n}\n"],"mappings":"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","names":["server","output","n","u","n","u","output","args","output","randomBytes"]}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,48 +0,0 @@
1
- import {
2
- createSshTerminal
3
- } from "../chunk-ZA27FN5N.js";
4
-
5
- // src/existing-server/index.ts
6
- import { common } from "@highstate/library";
7
- import { forUnit, output } from "@highstate/pulumi";
8
- import { remote } from "@pulumi/command";
9
- var { args, inputs, secrets, outputs } = forUnit(common.existingServer);
10
- var privateKey = inputs.sshKeyPair?.privateKey ?? secrets.sshPrivateKey;
11
- var hostnameResult = new remote.Command("hostname", {
12
- connection: output({
13
- host: args.endpoint,
14
- port: args.sshPort,
15
- user: args.sshUser,
16
- password: secrets.sshPassword,
17
- privateKey,
18
- dialErrorLimit: 3
19
- }),
20
- create: "hostname",
21
- triggers: [Date.now()]
22
- });
23
- var hostname = hostnameResult.stdout.apply((x) => x.trim());
24
- var server = output({
25
- endpoint: args.endpoint,
26
- hostname,
27
- sshCredentials: {
28
- user: args.sshUser,
29
- port: args.sshPort,
30
- password: secrets.sshPassword,
31
- privateKey
32
- }
33
- });
34
- var existing_server_default = outputs({
35
- server,
36
- $status: {
37
- hostname: {
38
- value: hostname
39
- }
40
- },
41
- $terminals: {
42
- ssh: server.apply(createSshTerminal)
43
- }
44
- });
45
- export {
46
- existing_server_default as default
47
- };
48
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/existing-server/index.ts"],"sourcesContent":["import { common } from \"@highstate/library\"\nimport { forUnit, Output, output } from \"@highstate/pulumi\"\nimport { remote } from \"@pulumi/command\"\nimport { createSshTerminal } from \"../shared\"\n\nconst { args, inputs, secrets, outputs } = forUnit(common.existingServer)\n\nconst privateKey = inputs.sshKeyPair?.privateKey ?? secrets.sshPrivateKey\n\nconst hostnameResult = new remote.Command(\"hostname\", {\n connection: output({\n host: args.endpoint,\n port: args.sshPort,\n user: args.sshUser,\n password: secrets.sshPassword,\n privateKey,\n dialErrorLimit: 3,\n }),\n create: \"hostname\",\n triggers: [Date.now()],\n})\n\nconst hostname = hostnameResult.stdout.apply(x => x.trim())\n\nconst server: Output<common.Server> = output({\n endpoint: args.endpoint,\n hostname,\n sshCredentials: {\n user: args.sshUser,\n port: args.sshPort,\n password: secrets.sshPassword,\n privateKey,\n },\n})\n\nexport default outputs({\n server,\n $status: {\n hostname: {\n value: hostname,\n },\n },\n $terminals: {\n ssh: server.apply(createSshTerminal),\n },\n})\n"],"mappings":";;;;;AAAA,SAAS,cAAc;AACvB,SAAS,SAAiB,cAAc;AACxC,SAAS,cAAc;AAGvB,IAAM,EAAE,MAAM,QAAQ,SAAS,QAAQ,IAAI,QAAQ,OAAO,cAAc;AAExE,IAAM,aAAa,OAAO,YAAY,cAAc,QAAQ;AAE5D,IAAM,iBAAiB,IAAI,OAAO,QAAQ,YAAY;AAAA,EACpD,YAAY,OAAO;AAAA,IACjB,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,gBAAgB;AAAA,EAClB,CAAC;AAAA,EACD,QAAQ;AAAA,EACR,UAAU,CAAC,KAAK,IAAI,CAAC;AACvB,CAAC;AAED,IAAM,WAAW,eAAe,OAAO,MAAM,OAAK,EAAE,KAAK,CAAC;AAE1D,IAAM,SAAgC,OAAO;AAAA,EAC3C,UAAU,KAAK;AAAA,EACf;AAAA,EACA,gBAAgB;AAAA,IACd,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX,UAAU,QAAQ;AAAA,IAClB;AAAA,EACF;AACF,CAAC;AAED,IAAO,0BAAQ,QAAQ;AAAA,EACrB;AAAA,EACA,SAAS;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,YAAY;AAAA,IACV,KAAK,OAAO,MAAM,iBAAiB;AAAA,EACrC;AACF,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/script/index.ts"],"sourcesContent":["import { common } from \"@highstate/library\"\nimport { forUnit } from \"@highstate/pulumi\"\nimport { Server } from \"../shared\"\n\nconst { name, args, inputs, outputs } = forUnit(common.script)\n\nconst server = new Server(inputs.server)\n\nserver.command({\n id: name,\n create: args.script,\n update: args.updateScript,\n delete: args.deleteScript,\n})\n\nexport default outputs({\n server: inputs.server,\n})\n"],"mappings":";;;;;AAAA,SAAS,cAAc;AACvB,SAAS,eAAe;AAGxB,IAAM,EAAE,MAAM,MAAM,QAAQ,QAAQ,IAAI,QAAQ,OAAO,MAAM;AAE7D,IAAM,SAAS,IAAI,OAAO,OAAO,MAAM;AAEvC,OAAO,QAAQ;AAAA,EACb,IAAI;AAAA,EACJ,QAAQ,KAAK;AAAA,EACb,QAAQ,KAAK;AAAA,EACb,QAAQ,KAAK;AACf,CAAC;AAED,IAAO,iBAAQ,QAAQ;AAAA,EACrB,QAAQ,OAAO;AACjB,CAAC;","names":[]}