@ai-coders/installer 0.3.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.
- package/CHANGELOG.md +25 -0
- package/LICENSE +18 -0
- package/README.md +64 -0
- package/dist/callback-page.d.ts +11 -0
- package/dist/callback-page.js +230 -0
- package/dist/callback-page.js.map +1 -0
- package/dist/clerk-auth.d.ts +18 -0
- package/dist/clerk-auth.js +296 -0
- package/dist/clerk-auth.js.map +1 -0
- package/dist/cli-command.d.ts +10 -0
- package/dist/cli-command.js +17 -0
- package/dist/cli-command.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +1 -0
- package/dist/i18n.d.ts +52 -0
- package/dist/i18n.js +163 -0
- package/dist/i18n.js.map +1 -0
- package/dist/product-manifest.d.ts +43 -0
- package/dist/product-manifest.js +140 -0
- package/dist/product-manifest.js.map +1 -0
- package/dist/products/second-brain/apply.d.ts +86 -0
- package/dist/products/second-brain/apply.js +277 -0
- package/dist/products/second-brain/apply.js.map +1 -0
- package/dist/products/second-brain/bearer.d.ts +29 -0
- package/dist/products/second-brain/bearer.js +78 -0
- package/dist/products/second-brain/bearer.js.map +1 -0
- package/dist/products/second-brain/bootstrap.d.ts +33 -0
- package/dist/products/second-brain/bootstrap.js +39 -0
- package/dist/products/second-brain/bootstrap.js.map +1 -0
- package/dist/products/second-brain/envfile.d.ts +35 -0
- package/dist/products/second-brain/envfile.js +136 -0
- package/dist/products/second-brain/envfile.js.map +1 -0
- package/dist/products/second-brain/github.d.ts +77 -0
- package/dist/products/second-brain/github.js +195 -0
- package/dist/products/second-brain/github.js.map +1 -0
- package/dist/products/second-brain/hostinger.d.ts +140 -0
- package/dist/products/second-brain/hostinger.js +607 -0
- package/dist/products/second-brain/hostinger.js.map +1 -0
- package/dist/products/second-brain/i18n.d.ts +887 -0
- package/dist/products/second-brain/i18n.js +587 -0
- package/dist/products/second-brain/i18n.js.map +1 -0
- package/dist/products/second-brain/keys.d.ts +5 -0
- package/dist/products/second-brain/keys.js +16 -0
- package/dist/products/second-brain/keys.js.map +1 -0
- package/dist/products/second-brain/main.d.ts +3 -0
- package/dist/products/second-brain/main.js +78 -0
- package/dist/products/second-brain/main.js.map +1 -0
- package/dist/products/second-brain/ports.d.ts +34 -0
- package/dist/products/second-brain/ports.js +77 -0
- package/dist/products/second-brain/ports.js.map +1 -0
- package/dist/products/second-brain/recovery.d.ts +50 -0
- package/dist/products/second-brain/recovery.js +96 -0
- package/dist/products/second-brain/recovery.js.map +1 -0
- package/dist/products/second-brain/sealed-box.d.ts +1 -0
- package/dist/products/second-brain/sealed-box.js +11 -0
- package/dist/products/second-brain/sealed-box.js.map +1 -0
- package/dist/products/second-brain/server.d.ts +143 -0
- package/dist/products/second-brain/server.js +280 -0
- package/dist/products/second-brain/server.js.map +1 -0
- package/dist/products/second-brain/service.d.ts +26 -0
- package/dist/products/second-brain/service.js +806 -0
- package/dist/products/second-brain/service.js.map +1 -0
- package/dist/products/second-brain/ssh.d.ts +35 -0
- package/dist/products/second-brain/ssh.js +140 -0
- package/dist/products/second-brain/ssh.js.map +1 -0
- package/dist/products/second-brain/state.d.ts +27 -0
- package/dist/products/second-brain/state.js +60 -0
- package/dist/products/second-brain/state.js.map +1 -0
- package/dist/products/second-brain/ui/assets/index-BoXpakUt.css +2 -0
- package/dist/products/second-brain/ui/assets/index-MgfOpW_R.js +66 -0
- package/dist/products/second-brain/ui/index.html +15 -0
- package/dist/products/second-brain/verify.d.ts +23 -0
- package/dist/products/second-brain/verify.js +59 -0
- package/dist/products/second-brain/verify.js.map +1 -0
- package/dist/products/severino/apply.d.ts +87 -0
- package/dist/products/severino/apply.js +250 -0
- package/dist/products/severino/apply.js.map +1 -0
- package/dist/products/severino/domain.d.ts +6 -0
- package/dist/products/severino/domain.js +20 -0
- package/dist/products/severino/domain.js.map +1 -0
- package/dist/products/severino/envfile.d.ts +20 -0
- package/dist/products/severino/envfile.js +89 -0
- package/dist/products/severino/envfile.js.map +1 -0
- package/dist/products/severino/github.d.ts +77 -0
- package/dist/products/severino/github.js +195 -0
- package/dist/products/severino/github.js.map +1 -0
- package/dist/products/severino/hostinger.d.ts +140 -0
- package/dist/products/severino/hostinger.js +607 -0
- package/dist/products/severino/hostinger.js.map +1 -0
- package/dist/products/severino/i18n.d.ts +887 -0
- package/dist/products/severino/i18n.js +587 -0
- package/dist/products/severino/i18n.js.map +1 -0
- package/dist/products/severino/keys.d.ts +5 -0
- package/dist/products/severino/keys.js +16 -0
- package/dist/products/severino/keys.js.map +1 -0
- package/dist/products/severino/main.d.ts +3 -0
- package/dist/products/severino/main.js +78 -0
- package/dist/products/severino/main.js.map +1 -0
- package/dist/products/severino/recovery.d.ts +50 -0
- package/dist/products/severino/recovery.js +96 -0
- package/dist/products/severino/recovery.js.map +1 -0
- package/dist/products/severino/sealed-box.d.ts +1 -0
- package/dist/products/severino/sealed-box.js +11 -0
- package/dist/products/severino/sealed-box.js.map +1 -0
- package/dist/products/severino/server.d.ts +123 -0
- package/dist/products/severino/server.js +274 -0
- package/dist/products/severino/server.js.map +1 -0
- package/dist/products/severino/service.d.ts +28 -0
- package/dist/products/severino/service.js +752 -0
- package/dist/products/severino/service.js.map +1 -0
- package/dist/products/severino/slack-manifest.d.ts +3 -0
- package/dist/products/severino/slack-manifest.js +45 -0
- package/dist/products/severino/slack-manifest.js.map +1 -0
- package/dist/products/severino/ssh.d.ts +35 -0
- package/dist/products/severino/ssh.js +140 -0
- package/dist/products/severino/ssh.js.map +1 -0
- package/dist/products/severino/state.d.ts +26 -0
- package/dist/products/severino/state.js +61 -0
- package/dist/products/severino/state.js.map +1 -0
- package/dist/products/severino/ui/assets/index-7S8Hl4jq.js +96 -0
- package/dist/products/severino/ui/assets/index-D5kql1Bh.css +2 -0
- package/dist/products/severino/ui/index.html +15 -0
- package/dist/products/severino/verify.d.ts +22 -0
- package/dist/products/severino/verify.js +149 -0
- package/dist/products/severino/verify.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy keypair generation. ssh2 emits OpenSSH-format ed25519 keys on every
|
|
3
|
+
* platform, which is what the installer appends to ~/.ssh/authorized_keys.
|
|
4
|
+
*/
|
|
5
|
+
import ssh2 from "ssh2";
|
|
6
|
+
export function generateDeployKey() {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
ssh2.utils.generateKeyPair("ed25519", { comment: "severino-deploy" }, (error, keys) => {
|
|
9
|
+
if (error)
|
|
10
|
+
reject(error);
|
|
11
|
+
else
|
|
12
|
+
resolve({ publicKey: keys.public, privateKey: keys.private });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../packages/severino/src/keys.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC,KAAK,CAAC,eAAe,CACxB,SAAS,EACT,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAC9B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YACd,IAAI,KAAK;gBAAE,MAAM,CAAC,KAAK,CAAC,CAAC;;gBACpB,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Local web-UI installer composition root for the Severino product. */
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { parseRepositoryFromRemote } from "./github.js";
|
|
7
|
+
import { defaultLanguage, installerMessages } from "./i18n.js";
|
|
8
|
+
import { startInstallerServer } from "./server.js";
|
|
9
|
+
import { createInstallerService, loadInitialState } from "./service.js";
|
|
10
|
+
import { stateFileName } from "./state.js";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
const messages = installerMessages[defaultLanguage];
|
|
13
|
+
async function detectRepository(repoRoot) {
|
|
14
|
+
try {
|
|
15
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], { cwd: repoRoot });
|
|
16
|
+
return parseRepositoryFromRemote(stdout.trim());
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function openBrowser(url) {
|
|
23
|
+
const command = process.platform === "darwin"
|
|
24
|
+
? { file: "open", args: [url] }
|
|
25
|
+
: process.platform === "win32"
|
|
26
|
+
? { file: "cmd", args: ["/c", "start", "", url] }
|
|
27
|
+
: { file: "xdg-open", args: [url] };
|
|
28
|
+
try {
|
|
29
|
+
const child = spawn(command.file, command.args, {
|
|
30
|
+
detached: true,
|
|
31
|
+
stdio: "ignore",
|
|
32
|
+
});
|
|
33
|
+
child.on("error", () => { });
|
|
34
|
+
child.unref();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// The printed URL is the contract; opening the browser is best effort.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function runSeverinoInstaller(options) {
|
|
41
|
+
const repoRoot = path.resolve(options.projectRoot);
|
|
42
|
+
const statePath = path.join(repoRoot, stateFileName);
|
|
43
|
+
let server;
|
|
44
|
+
try {
|
|
45
|
+
const [initialState, detectedRepository] = await Promise.all([
|
|
46
|
+
loadInitialState(statePath),
|
|
47
|
+
detectRepository(repoRoot),
|
|
48
|
+
]);
|
|
49
|
+
const service = createInstallerService({
|
|
50
|
+
statePath,
|
|
51
|
+
repoRoot,
|
|
52
|
+
initialState,
|
|
53
|
+
detectedRepository,
|
|
54
|
+
});
|
|
55
|
+
server = await startInstallerServer(service);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error(messages.terminalStartFailed);
|
|
59
|
+
}
|
|
60
|
+
process.stdout.write(`${messages.terminalRunningAt}\n│ ${server.url}\n│\n`);
|
|
61
|
+
process.stdout.write(`${messages.terminalPressCtrlC}\n`);
|
|
62
|
+
openBrowser(server.url);
|
|
63
|
+
let shutdownPromise = null;
|
|
64
|
+
const shutdown = () => {
|
|
65
|
+
if (shutdownPromise !== null)
|
|
66
|
+
return;
|
|
67
|
+
shutdownPromise = server
|
|
68
|
+
.close()
|
|
69
|
+
.then(() => process.exit(0))
|
|
70
|
+
.catch(() => {
|
|
71
|
+
process.stderr.write(`${messages.terminalStopFailed}\n`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
process.on("SIGINT", shutdown);
|
|
76
|
+
process.on("SIGTERM", shutdown);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../packages/severino/src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconnects a new computer to an existing VPS installation. Recovery proves
|
|
3
|
+
* server ownership over SSH, reads only the public hostname, installs a fresh
|
|
4
|
+
* deploy key for this computer, and persists a new private local state file.
|
|
5
|
+
*/
|
|
6
|
+
import type { DeployKeyPair } from "./keys.js";
|
|
7
|
+
import type { InstallerLanguage } from "./i18n.js";
|
|
8
|
+
import type { InstallerState } from "./state.js";
|
|
9
|
+
import type { SshLike } from "./apply.js";
|
|
10
|
+
type RecoverySshAuth = {
|
|
11
|
+
readonly kind: "password";
|
|
12
|
+
readonly password: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: "key";
|
|
15
|
+
readonly privateKey: string;
|
|
16
|
+
};
|
|
17
|
+
export interface RecoveryInput {
|
|
18
|
+
readonly language: InstallerLanguage;
|
|
19
|
+
readonly repository?: {
|
|
20
|
+
readonly owner: string;
|
|
21
|
+
readonly repo: string;
|
|
22
|
+
} | undefined;
|
|
23
|
+
readonly vps: {
|
|
24
|
+
readonly host: string;
|
|
25
|
+
readonly port: number;
|
|
26
|
+
readonly username: string;
|
|
27
|
+
};
|
|
28
|
+
readonly vpsAuth: RecoverySshAuth;
|
|
29
|
+
}
|
|
30
|
+
export interface RecoveryDependencies {
|
|
31
|
+
readonly generateKey: () => Promise<DeployKeyPair>;
|
|
32
|
+
readonly connectSsh: (credentials: {
|
|
33
|
+
readonly host: string;
|
|
34
|
+
readonly port: number;
|
|
35
|
+
readonly username: string;
|
|
36
|
+
readonly auth: RecoverySshAuth;
|
|
37
|
+
}) => Promise<SshLike>;
|
|
38
|
+
readonly saveState: (state: InstallerState) => Promise<void>;
|
|
39
|
+
readonly now: () => Date;
|
|
40
|
+
}
|
|
41
|
+
export type RecoveryOutcome = {
|
|
42
|
+
readonly ok: true;
|
|
43
|
+
readonly domain: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly ok: false;
|
|
46
|
+
readonly error: string;
|
|
47
|
+
};
|
|
48
|
+
export declare const readRecoveryMetadataCommand: string;
|
|
49
|
+
export declare function recoverExistingInstallation(input: RecoveryInput, deps: RecoveryDependencies): Promise<RecoveryOutcome>;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { normalizeDomain } from "./envfile.js";
|
|
2
|
+
import { buildAuthorizedKeysCommand } from "./ssh.js";
|
|
3
|
+
export const readRecoveryMetadataCommand = [
|
|
4
|
+
"test -f /opt/severino/.env || exit 44",
|
|
5
|
+
"test -f /opt/severino/compose.yaml || exit 44",
|
|
6
|
+
"domain=$(awk -F= '$1 == \"SEVERINO_PUBLIC_HOST\" { sub(/^[^=]*=/, \"\"); print; exit }' /opt/severino/.env)",
|
|
7
|
+
"test -n \"$domain\" || exit 45",
|
|
8
|
+
"printf '%s\\n' \"$domain\"",
|
|
9
|
+
].join(" && ");
|
|
10
|
+
export async function recoverExistingInstallation(input, deps) {
|
|
11
|
+
let keys;
|
|
12
|
+
try {
|
|
13
|
+
keys = await deps.generateKey();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
error: "A new local deploy key could not be created.",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
let ssh;
|
|
22
|
+
try {
|
|
23
|
+
ssh = await deps.connectSsh({ ...input.vps, auth: input.vpsAuth });
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
error: error instanceof Error ? error.message : String(error),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const metadata = await ssh.exec(readRecoveryMetadataCommand);
|
|
33
|
+
if (metadata.code !== 0 || metadata.stdout.trim() === "") {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
error: "No existing Severino installation was found on this server.",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
let domain;
|
|
40
|
+
try {
|
|
41
|
+
domain = normalizeDomain(metadata.stdout.trim());
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: "The existing installation has an invalid public hostname.",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const authorized = await ssh.exec(buildAuthorizedKeysCommand(keys.publicKey));
|
|
50
|
+
if (authorized.code !== 0) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
error: "The new local deploy key could not be installed on the server.",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const keySession = await deps.connectSsh({
|
|
58
|
+
...input.vps,
|
|
59
|
+
auth: { kind: "key", privateKey: keys.privateKey },
|
|
60
|
+
});
|
|
61
|
+
keySession.close();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
error: "The new local deploy key could not connect to the server.",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const timestamp = deps.now().toISOString();
|
|
70
|
+
try {
|
|
71
|
+
await deps.saveState({
|
|
72
|
+
version: 1,
|
|
73
|
+
language: input.language,
|
|
74
|
+
...(input.repository === undefined
|
|
75
|
+
? {}
|
|
76
|
+
: { repository: input.repository }),
|
|
77
|
+
vps: input.vps,
|
|
78
|
+
deployKey: keys,
|
|
79
|
+
domain,
|
|
80
|
+
installedAt: timestamp,
|
|
81
|
+
updatedAt: timestamp,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
error: "The recovered settings could not be saved on this computer.",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, domain };
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
ssh.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../packages/severino/src/recovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sealForGitHubSecret(publicKeyBase64: string, value: string): Promise<string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions secrets require libsodium sealed-box encryption with the
|
|
3
|
+
* repository public key; node:crypto has no crypto_box_seal equivalent.
|
|
4
|
+
*/
|
|
5
|
+
import sodium from "libsodium-wrappers";
|
|
6
|
+
export async function sealForGitHubSecret(publicKeyBase64, value) {
|
|
7
|
+
await sodium.ready;
|
|
8
|
+
const sealed = sodium.crypto_box_seal(sodium.from_string(value), sodium.from_base64(publicKeyBase64, sodium.base64_variants.ORIGINAL));
|
|
9
|
+
return sodium.to_base64(sealed, sodium.base64_variants.ORIGINAL);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=sealed-box.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sealed-box.js","sourceRoot":"","sources":["../../../packages/severino/src/sealed-box.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,eAAuB,EACvB,KAAa;IAEb,MAAM,MAAM,CAAC,KAAK,CAAC;IACnB,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,CACnC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EACzB,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CACrE,CAAC;IACF,OAAO,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { InstallerLanguage } from "./i18n.js";
|
|
2
|
+
import type { InstallStepReport } from "./apply.js";
|
|
3
|
+
import type { DnsCheckResult, VerificationResult } from "./verify.js";
|
|
4
|
+
import type { HostingerVirtualMachine } from "./hostinger.js";
|
|
5
|
+
import type { WorkflowRun } from "./github.js";
|
|
6
|
+
export interface InstallerContext {
|
|
7
|
+
readonly mode: "wizard" | "dashboard";
|
|
8
|
+
readonly language: InstallerLanguage;
|
|
9
|
+
readonly repository: {
|
|
10
|
+
readonly owner: string;
|
|
11
|
+
readonly repo: string;
|
|
12
|
+
} | null;
|
|
13
|
+
readonly domain: string | null;
|
|
14
|
+
readonly vpsHost: string | null;
|
|
15
|
+
readonly githubConnected: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface InstallProgress {
|
|
18
|
+
readonly running: boolean;
|
|
19
|
+
readonly finished: boolean;
|
|
20
|
+
readonly ok: boolean | null;
|
|
21
|
+
readonly steps: readonly InstallStepReport[];
|
|
22
|
+
}
|
|
23
|
+
interface HostingerOperationFailure {
|
|
24
|
+
readonly ok: false;
|
|
25
|
+
readonly errorCode: string;
|
|
26
|
+
readonly correlationId?: string;
|
|
27
|
+
readonly retryAfterMs?: number;
|
|
28
|
+
}
|
|
29
|
+
type HostingerVirtualMachinesResult = {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
readonly virtualMachines: readonly HostingerVirtualMachine[];
|
|
32
|
+
} | HostingerOperationFailure;
|
|
33
|
+
type HostingerPasswordPreparationResult = {
|
|
34
|
+
readonly ok: true;
|
|
35
|
+
readonly confirmationToken: string;
|
|
36
|
+
readonly target: {
|
|
37
|
+
readonly virtualMachineId: number;
|
|
38
|
+
readonly hostname: string;
|
|
39
|
+
readonly ipv4Id: number;
|
|
40
|
+
readonly ipv4: string;
|
|
41
|
+
};
|
|
42
|
+
} | HostingerOperationFailure;
|
|
43
|
+
type HostingerPasswordResetResult = {
|
|
44
|
+
readonly ok: true;
|
|
45
|
+
readonly actionId: number;
|
|
46
|
+
} | HostingerOperationFailure;
|
|
47
|
+
export interface InstallerService {
|
|
48
|
+
context(): InstallerContext;
|
|
49
|
+
connectHostinger(input: {
|
|
50
|
+
token: string;
|
|
51
|
+
}): Promise<HostingerVirtualMachinesResult>;
|
|
52
|
+
disconnectHostinger(): Promise<{
|
|
53
|
+
readonly ok: true;
|
|
54
|
+
} | HostingerOperationFailure>;
|
|
55
|
+
refreshHostinger(): Promise<HostingerVirtualMachinesResult>;
|
|
56
|
+
prepareHostingerRootPasswordReset(input: {
|
|
57
|
+
virtualMachineId: number;
|
|
58
|
+
}): Promise<HostingerPasswordPreparationResult>;
|
|
59
|
+
resetHostingerRootPassword(input: {
|
|
60
|
+
confirmationToken: string;
|
|
61
|
+
confirmationText: string;
|
|
62
|
+
password: string;
|
|
63
|
+
}): Promise<HostingerPasswordResetResult>;
|
|
64
|
+
validateGitHub(input: {
|
|
65
|
+
token: string;
|
|
66
|
+
owner: string;
|
|
67
|
+
repo: string;
|
|
68
|
+
}): Promise<unknown>;
|
|
69
|
+
connectGitHub(input: {
|
|
70
|
+
token: string;
|
|
71
|
+
owner?: string;
|
|
72
|
+
repo?: string;
|
|
73
|
+
}): Promise<unknown>;
|
|
74
|
+
deploy(input: {
|
|
75
|
+
imageTag?: string;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
ok: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
listRuns(): Promise<WorkflowRun[]>;
|
|
80
|
+
testSsh(input: {
|
|
81
|
+
host: string;
|
|
82
|
+
port: number;
|
|
83
|
+
username: string;
|
|
84
|
+
password: string;
|
|
85
|
+
}): Promise<{
|
|
86
|
+
ok: boolean;
|
|
87
|
+
message?: string;
|
|
88
|
+
proof?: string;
|
|
89
|
+
}>;
|
|
90
|
+
recoverExisting(input: Record<string, unknown>): Promise<{
|
|
91
|
+
ok: boolean;
|
|
92
|
+
domain?: string;
|
|
93
|
+
error?: string;
|
|
94
|
+
}>;
|
|
95
|
+
checkDns(input: {
|
|
96
|
+
domain: string;
|
|
97
|
+
host: string;
|
|
98
|
+
}): Promise<DnsCheckResult>;
|
|
99
|
+
startInstall(input: Record<string, unknown>): {
|
|
100
|
+
started: boolean;
|
|
101
|
+
error?: string;
|
|
102
|
+
};
|
|
103
|
+
deployLocal(): {
|
|
104
|
+
started: boolean;
|
|
105
|
+
};
|
|
106
|
+
progress(): InstallProgress;
|
|
107
|
+
verify(): Promise<VerificationResult>;
|
|
108
|
+
reset(): Promise<{
|
|
109
|
+
ok: boolean;
|
|
110
|
+
errorCode?: string;
|
|
111
|
+
}>;
|
|
112
|
+
shutdown?(): void | Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
export interface InstallerServer {
|
|
115
|
+
readonly url: string;
|
|
116
|
+
close(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
export interface InstallerServerOptions {
|
|
119
|
+
/** Directory holding the built UI (index.html + assets/). Test override. */
|
|
120
|
+
readonly uiDir?: string;
|
|
121
|
+
}
|
|
122
|
+
export declare function startInstallerServer(service: InstallerService, options?: InstallerServerOptions): Promise<InstallerServer>;
|
|
123
|
+
export {};
|