@freestyle-sh/with-ttyd 0.0.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.
- package/dist/index.d.ts +65 -0
- package/dist/index.js +143 -0
- package/examples/basic.ts +72 -0
- package/package.json +32 -0
- package/src/index.ts +228 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as freestyle_sandboxes from 'freestyle-sandboxes';
|
|
2
|
+
import { VmWith, VmWithInstance, CreateVmOptions } from 'freestyle-sandboxes';
|
|
3
|
+
|
|
4
|
+
type TtydConfig = {
|
|
5
|
+
/** Port to run ttyd on (default: auto-assigned starting at 7682) */
|
|
6
|
+
port?: number;
|
|
7
|
+
/** Shell or command to run (default: /bin/bash) */
|
|
8
|
+
command?: string;
|
|
9
|
+
/** User to run terminal as (default: current user) */
|
|
10
|
+
user?: string;
|
|
11
|
+
/** Working directory (default: user home) */
|
|
12
|
+
cwd?: string;
|
|
13
|
+
/** Enable basic auth */
|
|
14
|
+
credential?: {
|
|
15
|
+
username: string;
|
|
16
|
+
password: string;
|
|
17
|
+
};
|
|
18
|
+
/** Terminal title shown in browser tab */
|
|
19
|
+
title?: string;
|
|
20
|
+
/** Read-only terminal (no input allowed) */
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
};
|
|
23
|
+
type ResolvedTerminalConfig = {
|
|
24
|
+
port: number;
|
|
25
|
+
command: string;
|
|
26
|
+
user: string;
|
|
27
|
+
cwd: string;
|
|
28
|
+
credential?: {
|
|
29
|
+
username: string;
|
|
30
|
+
password: string;
|
|
31
|
+
};
|
|
32
|
+
title: string;
|
|
33
|
+
readOnly: boolean;
|
|
34
|
+
};
|
|
35
|
+
declare class VmWebTerminal<T extends TtydConfig[] = TtydConfig[]> extends VmWith<VmWebTerminalInstance<T>> {
|
|
36
|
+
private resolvedTerminals;
|
|
37
|
+
constructor(terminals: T | TtydConfig);
|
|
38
|
+
configure(existingConfig: CreateVmOptions): CreateVmOptions | Promise<CreateVmOptions>;
|
|
39
|
+
createInstance(): VmWebTerminalInstance<T>;
|
|
40
|
+
installServiceName(): string;
|
|
41
|
+
}
|
|
42
|
+
declare class WebTerminal {
|
|
43
|
+
readonly port: number;
|
|
44
|
+
readonly command: string;
|
|
45
|
+
private instance;
|
|
46
|
+
constructor({ port, command, instance, }: {
|
|
47
|
+
port: number;
|
|
48
|
+
command: string;
|
|
49
|
+
instance: VmWebTerminalInstance<any>;
|
|
50
|
+
});
|
|
51
|
+
/** Expose this terminal publicly via Freestyle routing */
|
|
52
|
+
route({ domain }: {
|
|
53
|
+
domain: string;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
declare class VmWebTerminalInstance<T extends TtydConfig[]> extends VmWithInstance {
|
|
57
|
+
builder: VmWebTerminal<T>;
|
|
58
|
+
readonly terminals: WebTerminal[];
|
|
59
|
+
constructor(builder: VmWebTerminal<T>, resolvedTerminals: ResolvedTerminalConfig[]);
|
|
60
|
+
/** @internal */
|
|
61
|
+
get _vm(): freestyle_sandboxes.Vm;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { VmWebTerminal, VmWebTerminalInstance, WebTerminal };
|
|
65
|
+
export type { ResolvedTerminalConfig, TtydConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { VmWith, VmTemplate, VmWithInstance } from 'freestyle-sandboxes';
|
|
2
|
+
|
|
3
|
+
class VmWebTerminal extends VmWith {
|
|
4
|
+
resolvedTerminals;
|
|
5
|
+
constructor(terminals) {
|
|
6
|
+
super();
|
|
7
|
+
const terminalList = Array.isArray(terminals) ? terminals : [terminals];
|
|
8
|
+
let nextPort = 7682;
|
|
9
|
+
this.resolvedTerminals = terminalList.map((config) => {
|
|
10
|
+
const port = config.port ?? nextPort++;
|
|
11
|
+
return {
|
|
12
|
+
port,
|
|
13
|
+
command: config.command ?? "bash -l",
|
|
14
|
+
user: config.user ?? "root",
|
|
15
|
+
cwd: config.cwd ?? "/root",
|
|
16
|
+
credential: config.credential,
|
|
17
|
+
title: config.title ?? `terminal-${port}`,
|
|
18
|
+
readOnly: config.readOnly ?? false
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
configure(existingConfig) {
|
|
23
|
+
const installScript = `#!/bin/bash
|
|
24
|
+
set -e
|
|
25
|
+
|
|
26
|
+
TTYD_VERSION="1.7.7"
|
|
27
|
+
curl -fsSL -o /usr/local/bin/ttyd "https://github.com/tsl0922/ttyd/releases/download/\${TTYD_VERSION}/ttyd.x86_64"
|
|
28
|
+
chmod +x /usr/local/bin/ttyd
|
|
29
|
+
/usr/local/bin/ttyd --version
|
|
30
|
+
`;
|
|
31
|
+
const services = this.resolvedTerminals.map((t) => {
|
|
32
|
+
const args = [`/usr/local/bin/ttyd`, `-p ${t.port}`];
|
|
33
|
+
if (t.credential) {
|
|
34
|
+
if (t.credential.username.length === 0 || t.credential.password.length === 0) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Invalid credential for terminal on port ${t.port}: username and password cannot be empty`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (t.credential.username.includes(":") || t.credential.password.includes(":")) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Invalid credential for terminal on port ${t.port}: username and password cannot contain colon (:) character`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
args.push(
|
|
45
|
+
`--credential ${t.credential.username}:${t.credential.password}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (t.readOnly) {
|
|
49
|
+
args.push(`--readonly`);
|
|
50
|
+
} else {
|
|
51
|
+
args.push(`--writable`);
|
|
52
|
+
}
|
|
53
|
+
args.push(t.command);
|
|
54
|
+
return {
|
|
55
|
+
name: `web-terminal-${t.port}`,
|
|
56
|
+
mode: "service",
|
|
57
|
+
exec: [args.join(" ")],
|
|
58
|
+
user: t.user,
|
|
59
|
+
cwd: t.cwd,
|
|
60
|
+
restart: "always",
|
|
61
|
+
restartSec: 2,
|
|
62
|
+
after: ["install-ttyd.service", "systemd-sysusers.service"],
|
|
63
|
+
requires: ["systemd-sysusers.service"]
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
const config = {
|
|
67
|
+
template: new VmTemplate({
|
|
68
|
+
additionalFiles: {
|
|
69
|
+
"/opt/install-ttyd.sh": { content: installScript }
|
|
70
|
+
},
|
|
71
|
+
systemd: {
|
|
72
|
+
services: [
|
|
73
|
+
{
|
|
74
|
+
name: "install-ttyd",
|
|
75
|
+
mode: "oneshot",
|
|
76
|
+
deleteAfterSuccess: true,
|
|
77
|
+
exec: ["bash /opt/install-ttyd.sh"],
|
|
78
|
+
timeoutSec: 120
|
|
79
|
+
},
|
|
80
|
+
...services
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
};
|
|
85
|
+
return this.compose(existingConfig, config);
|
|
86
|
+
}
|
|
87
|
+
createInstance() {
|
|
88
|
+
return new VmWebTerminalInstance(this, this.resolvedTerminals);
|
|
89
|
+
}
|
|
90
|
+
installServiceName() {
|
|
91
|
+
return "install-ttyd.service";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
class WebTerminal {
|
|
95
|
+
port;
|
|
96
|
+
command;
|
|
97
|
+
instance;
|
|
98
|
+
constructor({
|
|
99
|
+
port,
|
|
100
|
+
command,
|
|
101
|
+
instance
|
|
102
|
+
}) {
|
|
103
|
+
this.port = port;
|
|
104
|
+
this.command = command;
|
|
105
|
+
this.instance = instance;
|
|
106
|
+
}
|
|
107
|
+
/** Expose this terminal publicly via Freestyle routing */
|
|
108
|
+
async route({ domain }) {
|
|
109
|
+
const vm = this.instance._vm;
|
|
110
|
+
const freestyle = vm._freestyle;
|
|
111
|
+
console.log(
|
|
112
|
+
`Routing terminal on vm ${vm.vmId} at port ${this.port} to domain ${domain}`
|
|
113
|
+
);
|
|
114
|
+
await freestyle.domains.mappings.create({
|
|
115
|
+
domain,
|
|
116
|
+
vmId: vm.vmId,
|
|
117
|
+
vmPort: this.port
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
class VmWebTerminalInstance extends VmWithInstance {
|
|
122
|
+
builder;
|
|
123
|
+
terminals;
|
|
124
|
+
constructor(builder, resolvedTerminals) {
|
|
125
|
+
super();
|
|
126
|
+
this.builder = builder;
|
|
127
|
+
this.terminals = [];
|
|
128
|
+
for (const config of resolvedTerminals) {
|
|
129
|
+
const terminal = new WebTerminal({
|
|
130
|
+
port: config.port,
|
|
131
|
+
command: config.command,
|
|
132
|
+
instance: this
|
|
133
|
+
});
|
|
134
|
+
this.terminals.push(terminal);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** @internal */
|
|
138
|
+
get _vm() {
|
|
139
|
+
return this.vm;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { VmWebTerminal, VmWebTerminalInstance, WebTerminal };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { freestyle, VmSpec } from "freestyle-sandboxes";
|
|
3
|
+
import { VmWebTerminal as VmTtyd } from "../src/index.ts";
|
|
4
|
+
import { VmDevServer } from "../../with-dev-server/src/index.ts";
|
|
5
|
+
|
|
6
|
+
const devLogs = new VmTtyd({
|
|
7
|
+
port: 3010,
|
|
8
|
+
command: "tmux attach -t dev-server",
|
|
9
|
+
readOnly: true,
|
|
10
|
+
cwd: "/root/repo",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const otherTerminals = new VmTtyd({
|
|
14
|
+
port: 3011,
|
|
15
|
+
cwd: "/root/repo",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const domain = `${crypto.randomUUID()}.style.dev`;
|
|
19
|
+
|
|
20
|
+
const innerDeps = new VmSpec({
|
|
21
|
+
discriminator: "inner-deps",
|
|
22
|
+
with: {
|
|
23
|
+
devLogs: devLogs,
|
|
24
|
+
otherTerminals: otherTerminals,
|
|
25
|
+
},
|
|
26
|
+
aptDeps: ["tmux"],
|
|
27
|
+
// systemd: {
|
|
28
|
+
// services: [
|
|
29
|
+
// {
|
|
30
|
+
// name: "install-tmux",
|
|
31
|
+
// mode: "oneshot",
|
|
32
|
+
// bash: "apt-get update && apt-get install -y tmux",
|
|
33
|
+
// timeoutSec: 120,
|
|
34
|
+
// },
|
|
35
|
+
// ],
|
|
36
|
+
// },
|
|
37
|
+
additionalFiles: {
|
|
38
|
+
"/root/.tmux.conf": {
|
|
39
|
+
content: `set -g mouse on`,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const snapshot = new VmSpec({
|
|
45
|
+
snapshot: innerDeps,
|
|
46
|
+
with: {
|
|
47
|
+
devServer: new VmDevServer({
|
|
48
|
+
templateRepo: "https://github.com/freestyle-sh/freestyle-next",
|
|
49
|
+
workdir: "/root/repo",
|
|
50
|
+
devCommand: "tmux new -s dev-server npm run dev",
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const { vm, vmId } = await freestyle.vms.create({
|
|
56
|
+
snapshot: snapshot,
|
|
57
|
+
domains: [
|
|
58
|
+
{
|
|
59
|
+
domain: "dev-logs-" + domain,
|
|
60
|
+
vmPort: 3010,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
domain: "other-terminals-" + domain,
|
|
64
|
+
vmPort: 3011,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
console.log("npx freestyle-sandboxes vm ssh " + vmId);
|
|
70
|
+
|
|
71
|
+
console.log(`Terminal available at: https://dev-logs-${domain}`);
|
|
72
|
+
console.log(`Other terminals available at: https://other-terminals-${domain}`);
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@freestyle-sh/with-ttyd",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Web terminal for freestyle sandboxes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"source": "./src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ttyd",
|
|
17
|
+
"terminal",
|
|
18
|
+
"web-terminal",
|
|
19
|
+
"freestyle"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"pkgroll": "^2.11.2",
|
|
24
|
+
"typescript": "^5.8.3"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"freestyle-sandboxes": "^0.1.27"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "pkgroll"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VmTemplate,
|
|
3
|
+
type CreateVmOptions,
|
|
4
|
+
VmWith,
|
|
5
|
+
VmWithInstance,
|
|
6
|
+
Freestyle,
|
|
7
|
+
} from "freestyle-sandboxes";
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Configuration Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export type TtydConfig = {
|
|
14
|
+
/** Port to run ttyd on (default: auto-assigned starting at 7682) */
|
|
15
|
+
port?: number;
|
|
16
|
+
/** Shell or command to run (default: /bin/bash) */
|
|
17
|
+
command?: string;
|
|
18
|
+
/** User to run terminal as (default: current user) */
|
|
19
|
+
user?: string;
|
|
20
|
+
/** Working directory (default: user home) */
|
|
21
|
+
cwd?: string;
|
|
22
|
+
/** Enable basic auth */
|
|
23
|
+
credential?: { username: string; password: string };
|
|
24
|
+
/** Terminal title shown in browser tab */
|
|
25
|
+
title?: string;
|
|
26
|
+
/** Read-only terminal (no input allowed) */
|
|
27
|
+
readOnly?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ResolvedTerminalConfig = {
|
|
31
|
+
port: number;
|
|
32
|
+
command: string;
|
|
33
|
+
user: string;
|
|
34
|
+
cwd: string;
|
|
35
|
+
credential?: { username: string; password: string };
|
|
36
|
+
title: string;
|
|
37
|
+
readOnly: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Builder Class
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
export class VmWebTerminal<
|
|
45
|
+
T extends TtydConfig[] = TtydConfig[],
|
|
46
|
+
> extends VmWith<VmWebTerminalInstance<T>> {
|
|
47
|
+
private resolvedTerminals: ResolvedTerminalConfig[];
|
|
48
|
+
|
|
49
|
+
constructor(terminals: T | TtydConfig) {
|
|
50
|
+
super();
|
|
51
|
+
const terminalList = Array.isArray(terminals) ? terminals : [terminals];
|
|
52
|
+
// Resolve config once with defaults
|
|
53
|
+
let nextPort = 7682;
|
|
54
|
+
this.resolvedTerminals = terminalList.map((config) => {
|
|
55
|
+
const port = config.port ?? nextPort++;
|
|
56
|
+
return {
|
|
57
|
+
port,
|
|
58
|
+
command: config.command ?? "bash -l",
|
|
59
|
+
user: config.user ?? "root",
|
|
60
|
+
cwd: config.cwd ?? "/root",
|
|
61
|
+
credential: config.credential,
|
|
62
|
+
title: config.title ?? `terminal-${port}`,
|
|
63
|
+
readOnly: config.readOnly ?? false,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override configure(
|
|
69
|
+
existingConfig: CreateVmOptions,
|
|
70
|
+
): CreateVmOptions | Promise<CreateVmOptions> {
|
|
71
|
+
// Generate install script
|
|
72
|
+
const installScript = `#!/bin/bash
|
|
73
|
+
set -e
|
|
74
|
+
|
|
75
|
+
TTYD_VERSION="1.7.7"
|
|
76
|
+
curl -fsSL -o /usr/local/bin/ttyd "https://github.com/tsl0922/ttyd/releases/download/\${TTYD_VERSION}/ttyd.x86_64"
|
|
77
|
+
chmod +x /usr/local/bin/ttyd
|
|
78
|
+
/usr/local/bin/ttyd --version
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
// Generate systemd service for each terminal
|
|
82
|
+
const services = this.resolvedTerminals.map((t) => {
|
|
83
|
+
const args: string[] = [`/usr/local/bin/ttyd`, `-p ${t.port}`];
|
|
84
|
+
|
|
85
|
+
if (t.credential) {
|
|
86
|
+
if (
|
|
87
|
+
t.credential.username.length === 0 ||
|
|
88
|
+
t.credential.password.length === 0
|
|
89
|
+
) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Invalid credential for terminal on port ${t.port}: username and password cannot be empty`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
t.credential.username.includes(":") ||
|
|
96
|
+
t.credential.password.includes(":")
|
|
97
|
+
) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Invalid credential for terminal on port ${t.port}: username and password cannot contain colon (:) character`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
args.push(
|
|
103
|
+
`--credential ${t.credential.username}:${t.credential.password}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (t.readOnly) {
|
|
107
|
+
args.push(`--readonly`);
|
|
108
|
+
} else {
|
|
109
|
+
args.push(`--writable`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Shell command at the end
|
|
113
|
+
args.push(t.command);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
name: `web-terminal-${t.port}`,
|
|
117
|
+
mode: "service" as const,
|
|
118
|
+
exec: [args.join(" ")],
|
|
119
|
+
user: t.user,
|
|
120
|
+
cwd: t.cwd,
|
|
121
|
+
restart: "always" as const,
|
|
122
|
+
restartSec: 2,
|
|
123
|
+
after: ["install-ttyd.service", "systemd-sysusers.service"],
|
|
124
|
+
requires: ["systemd-sysusers.service"],
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const config: CreateVmOptions = {
|
|
129
|
+
template: new VmTemplate({
|
|
130
|
+
additionalFiles: {
|
|
131
|
+
"/opt/install-ttyd.sh": { content: installScript },
|
|
132
|
+
},
|
|
133
|
+
systemd: {
|
|
134
|
+
services: [
|
|
135
|
+
{
|
|
136
|
+
name: "install-ttyd",
|
|
137
|
+
mode: "oneshot",
|
|
138
|
+
deleteAfterSuccess: true,
|
|
139
|
+
exec: ["bash /opt/install-ttyd.sh"],
|
|
140
|
+
timeoutSec: 120,
|
|
141
|
+
},
|
|
142
|
+
...services,
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return this.compose(existingConfig, config);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
createInstance(): VmWebTerminalInstance<T> {
|
|
152
|
+
return new VmWebTerminalInstance(this, this.resolvedTerminals);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
installServiceName(): string {
|
|
156
|
+
return "install-ttyd.service";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Instance Class (runtime access)
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
export class WebTerminal {
|
|
165
|
+
readonly port: number;
|
|
166
|
+
readonly command: string;
|
|
167
|
+
private instance: VmWebTerminalInstance<any>;
|
|
168
|
+
|
|
169
|
+
constructor({
|
|
170
|
+
port,
|
|
171
|
+
command,
|
|
172
|
+
instance,
|
|
173
|
+
}: {
|
|
174
|
+
port: number;
|
|
175
|
+
command: string;
|
|
176
|
+
instance: VmWebTerminalInstance<any>;
|
|
177
|
+
}) {
|
|
178
|
+
this.port = port;
|
|
179
|
+
this.command = command;
|
|
180
|
+
this.instance = instance;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Expose this terminal publicly via Freestyle routing */
|
|
184
|
+
async route({ domain }: { domain: string }): Promise<void> {
|
|
185
|
+
const vm = this.instance._vm;
|
|
186
|
+
// @ts-expect-error using internal thing
|
|
187
|
+
const freestyle: Freestyle = vm._freestyle;
|
|
188
|
+
console.log(
|
|
189
|
+
`Routing terminal on vm ${vm.vmId} at port ${this.port} to domain ${domain}`,
|
|
190
|
+
);
|
|
191
|
+
await freestyle.domains.mappings.create({
|
|
192
|
+
domain: domain,
|
|
193
|
+
vmId: vm.vmId,
|
|
194
|
+
vmPort: this.port,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export class VmWebTerminalInstance<
|
|
200
|
+
T extends TtydConfig[],
|
|
201
|
+
> extends VmWithInstance {
|
|
202
|
+
builder: VmWebTerminal<T>;
|
|
203
|
+
readonly terminals: WebTerminal[];
|
|
204
|
+
|
|
205
|
+
constructor(
|
|
206
|
+
builder: VmWebTerminal<T>,
|
|
207
|
+
resolvedTerminals: ResolvedTerminalConfig[],
|
|
208
|
+
) {
|
|
209
|
+
super();
|
|
210
|
+
this.builder = builder;
|
|
211
|
+
this.terminals = [];
|
|
212
|
+
|
|
213
|
+
// Create terminals in order and expose as an array
|
|
214
|
+
for (const config of resolvedTerminals) {
|
|
215
|
+
const terminal = new WebTerminal({
|
|
216
|
+
port: config.port,
|
|
217
|
+
command: config.command,
|
|
218
|
+
instance: this,
|
|
219
|
+
});
|
|
220
|
+
this.terminals.push(terminal);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** @internal */
|
|
225
|
+
get _vm() {
|
|
226
|
+
return this.vm;
|
|
227
|
+
}
|
|
228
|
+
}
|