create-wpt-iot 0.1.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.
Files changed (62) hide show
  1. package/README.md +71 -0
  2. package/dist/artifact.d.ts +13 -0
  3. package/dist/artifact.d.ts.map +1 -0
  4. package/dist/artifact.js +37 -0
  5. package/dist/artifact.js.map +1 -0
  6. package/dist/bin.d.ts +3 -0
  7. package/dist/bin.d.ts.map +1 -0
  8. package/dist/bin.js +5 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/cli.d.ts +33 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +175 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/config-file.d.ts +12 -0
  15. package/dist/config-file.d.ts.map +1 -0
  16. package/dist/config-file.js +35 -0
  17. package/dist/config-file.js.map +1 -0
  18. package/dist/i18n.d.ts +7 -0
  19. package/dist/i18n.d.ts.map +1 -0
  20. package/dist/i18n.js +10 -0
  21. package/dist/i18n.js.map +1 -0
  22. package/dist/installer-manifest.d.ts +3 -0
  23. package/dist/installer-manifest.d.ts.map +1 -0
  24. package/dist/installer-manifest.js +7 -0
  25. package/dist/installer-manifest.js.map +1 -0
  26. package/dist/local.d.ts +6 -0
  27. package/dist/local.d.ts.map +1 -0
  28. package/dist/local.js +42 -0
  29. package/dist/local.js.map +1 -0
  30. package/dist/messages/en.d.ts +48 -0
  31. package/dist/messages/en.d.ts.map +1 -0
  32. package/dist/messages/en.js +48 -0
  33. package/dist/messages/en.js.map +1 -0
  34. package/dist/messages/it.d.ts +3 -0
  35. package/dist/messages/it.d.ts.map +1 -0
  36. package/dist/messages/it.js +48 -0
  37. package/dist/messages/it.js.map +1 -0
  38. package/dist/preflight.d.ts +5 -0
  39. package/dist/preflight.d.ts.map +1 -0
  40. package/dist/preflight.js +25 -0
  41. package/dist/preflight.js.map +1 -0
  42. package/dist/process.d.ts +39 -0
  43. package/dist/process.d.ts.map +1 -0
  44. package/dist/process.js +115 -0
  45. package/dist/process.js.map +1 -0
  46. package/dist/prompts.d.ts +36 -0
  47. package/dist/prompts.d.ts.map +1 -0
  48. package/dist/prompts.js +65 -0
  49. package/dist/prompts.js.map +1 -0
  50. package/dist/remote.d.ts +8 -0
  51. package/dist/remote.d.ts.map +1 -0
  52. package/dist/remote.js +211 -0
  53. package/dist/remote.js.map +1 -0
  54. package/dist/types.d.ts +24 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/validation.d.ts +12 -0
  59. package/dist/validation.d.ts.map +1 -0
  60. package/dist/validation.js +69 -0
  61. package/dist/validation.js.map +1 -0
  62. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # create-wpt-iot
2
+
3
+ Interactive installer for one WPT IoT edge device. It supports a local Linux installation or a remote Linux installation from a Windows/Linux workstation.
4
+
5
+ ## Usage
6
+
7
+ ```console
8
+ npx create-wpt-iot
9
+ npx create-wpt-iot --mode local
10
+ npx create-wpt-iot --mode remote
11
+ npx create-wpt-iot --help
12
+ ```
13
+
14
+ The wizard is interactive by default and configures one device per run. `--mode` skips only the first local/remote question.
15
+
16
+ ## Requirements
17
+
18
+ The workstation running `npx` needs Node.js 22.13 or newer and Internet access.
19
+
20
+ Local mode requires:
21
+
22
+ - Linux on `arm64` or `amd64`;
23
+ - `apt-get`, `systemd`, `sudo`, `curl`, and at least 12 GiB free;
24
+ - outbound HTTPS access to Docker, GHCR, and GitHub.
25
+
26
+ Remote mode supports a Windows or Linux workstation and requires native `ssh` and `scp`. The target must be Linux on `arm64` or `amd64`, have `apt-get`, `systemd`, `sudo`, `curl`, at least 12 GiB free, and outbound Internet access. Node.js is not required on the target.
27
+
28
+ Local installation from Windows is not supported. Use remote mode to install a Raspberry/IndustrialPI from Windows.
29
+
30
+ ## Password prompts
31
+
32
+ For a new installation, the wizard asks for an initial WPT administrator password twice using hidden input. It must contain at least 12 Unicode characters.
33
+
34
+ SSH, SCP, and remote `sudo` use their native terminal prompts. With username/password SSH authentication, the target password may be requested more than once. The CLI never captures, stores, or passes the SSH password in command arguments.
35
+
36
+ On the first connection, verify the SSH host fingerprint before accepting it. OpenSSH then records it in the normal `known_hosts` file.
37
+
38
+ ## Security and reinstall behavior
39
+
40
+ - The immutable installer script is downloaded from the package-pinned Git commit and verified with SHA-256.
41
+ - The administrator password is transported only in a randomly named temporary config file, protected with restrictive permissions where supported.
42
+ - Secrets are never placed in subprocess arguments or printed in summaries and logs.
43
+ - Local and remote temporary files are removed on success and failure; a later run removes abandoned remote files older than one day.
44
+ - Reinstalling preserves `.env`, the administrator credential, PostgreSQL password, session secret, `SECRETS_ENCRYPTION_KEY`, database, uploads, and Docker volumes.
45
+ - An existing installation does not ask for a replacement administrator password. Rotate credentials from the frontend.
46
+ - PostgreSQL remains in UTC. Application timezone conversion is configured in the frontend.
47
+
48
+ ## Automatic updates
49
+
50
+ Backend and frontend image updates are enabled by default every five minutes. Database, Mosquitto, nginx, certificates, uploads, and volumes are not replaced by the image updater.
51
+
52
+ On the target:
53
+
54
+ ```console
55
+ sudo systemctl status wpt-image-update.timer
56
+ sudo systemctl start wpt-image-update.service
57
+ sudo systemctl disable --now wpt-image-update.timer
58
+ sudo systemctl enable --now wpt-image-update.timer
59
+ ```
60
+
61
+ The wizard can explicitly disable automatic updates during installation.
62
+
63
+ ## After installation
64
+
65
+ Open `https://wpt.local`, download and trust the generated local CA, and log in as `admin` with the password entered in the wizard.
66
+
67
+ PLC address/byte order, MQTT/Sparkplug, application timezone, energy settings, and users remain deliberately untouched by the installer and are configured after login in the frontend.
68
+
69
+ ## Maintainers
70
+
71
+ Publishing uses npm trusted publishing with provenance. Do not publish until the disposable Raspberry acceptance checklist in the repository has passed.
@@ -0,0 +1,13 @@
1
+ import type { TemporaryFile } from './config-file.js';
2
+ export interface InstallerManifest {
3
+ owner: string;
4
+ repository: string;
5
+ ref: string;
6
+ sha256: string;
7
+ }
8
+ export interface ArtifactDependencies {
9
+ fetch: typeof globalThis.fetch;
10
+ tempRoot?: string;
11
+ }
12
+ export declare function downloadVerifiedInstaller(manifest: InstallerManifest, dependencies?: ArtifactDependencies): Promise<TemporaryFile>;
13
+ //# sourceMappingURL=artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,iBAAiB,EAC3B,YAAY,GAAE,oBAAkD,GAC/D,OAAO,CAAC,aAAa,CAAC,CAmCxB"}
@@ -0,0 +1,37 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { chmod, mkdir, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ export async function downloadVerifiedInstaller(manifest, dependencies = { fetch: globalThis.fetch }) {
6
+ const directory = join(dependencies.tempRoot ?? tmpdir(), `create-wpt-iot-installer-${randomUUID()}`);
7
+ const path = join(directory, 'install-enduser.sh');
8
+ await mkdir(directory, { mode: 0o700 });
9
+ await chmod(directory, 0o700);
10
+ try {
11
+ const url = [
12
+ 'https://raw.githubusercontent.com',
13
+ manifest.owner,
14
+ manifest.repository,
15
+ manifest.ref,
16
+ 'scripts/install-enduser.sh',
17
+ ].join('/');
18
+ const response = await dependencies.fetch(url, { redirect: 'error' });
19
+ if (!response.ok)
20
+ throw new Error(`downloadFailed:${response.status}`);
21
+ const content = Buffer.from(await response.arrayBuffer());
22
+ const digest = createHash('sha256').update(content).digest('hex');
23
+ if (digest !== manifest.sha256.toLowerCase())
24
+ throw new Error('checksumFailed');
25
+ await writeFile(path, content, { mode: 0o700, flag: 'wx' });
26
+ await chmod(path, 0o700);
27
+ return {
28
+ path,
29
+ cleanup: async () => rm(directory, { recursive: true, force: true }),
30
+ };
31
+ }
32
+ catch (error) {
33
+ await rm(directory, { recursive: true, force: true });
34
+ throw error;
35
+ }
36
+ }
37
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.js","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBjC,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,QAA2B,EAC3B,eAAqC,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE;IAEhE,MAAM,SAAS,GAAG,IAAI,CACpB,YAAY,CAAC,QAAQ,IAAI,MAAM,EAAE,EACjC,4BAA4B,UAAU,EAAE,EAAE,CAC3C,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG;YACV,mCAAmC;YACnC,QAAQ,CAAC,KAAK;YACd,QAAQ,CAAC,UAAU;YACnB,QAAQ,CAAC,GAAG;YACZ,4BAA4B;SAC7B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAEvE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClE,IAAI,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAEhF,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAEzB,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;SACrE,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './cli.js';
3
+ const exitCode = await runCli(process.argv.slice(2));
4
+ process.exitCode = exitCode;
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { TemporaryFile } from './config-file.js';
2
+ import { createTranslator } from './i18n.js';
3
+ import type { PromptPort, TargetSelection } from './prompts.js';
4
+ import type { InputCommandRunner } from './process.js';
5
+ import type { InstallMode, InstallSettings, PreflightResult, RemoteTarget } from './types.js';
6
+ type Writer = (message: string) => void;
7
+ type TargetCollector = (prompt: PromptPort, t: ReturnType<typeof createTranslator>, requestedMode?: InstallMode) => Promise<TargetSelection>;
8
+ type SettingsCollector = (prompt: PromptPort, t: ReturnType<typeof createTranslator>, preflight: PreflightResult, installDir: string) => Promise<InstallSettings | null>;
9
+ type LocalPreflight = (runner: InputCommandRunner, installDir: string) => Promise<PreflightResult>;
10
+ type RemotePreflight = (runner: InputCommandRunner, target: RemoteTarget, installDir: string) => Promise<PreflightResult>;
11
+ type ArtifactDownloader = () => Promise<TemporaryFile>;
12
+ type ConfigCreator = (settings: InstallSettings) => Promise<TemporaryFile>;
13
+ type LocalInstaller = (runner: InputCommandRunner, artifact: TemporaryFile, config: TemporaryFile, settings: InstallSettings) => Promise<void>;
14
+ type RemoteInstaller = (runner: InputCommandRunner, target: RemoteTarget, artifact: TemporaryFile, config: TemporaryFile, settings: InstallSettings) => Promise<void>;
15
+ export interface CliDependencies {
16
+ locale?: string;
17
+ version?: string;
18
+ prompt?: PromptPort;
19
+ runner?: InputCommandRunner;
20
+ collectTarget?: TargetCollector;
21
+ collectSettings?: SettingsCollector;
22
+ preflightLocal?: LocalPreflight;
23
+ preflightRemote?: RemotePreflight;
24
+ downloadArtifact?: ArtifactDownloader;
25
+ createConfig?: ConfigCreator;
26
+ installLocal?: LocalInstaller;
27
+ installRemote?: RemoteInstaller;
28
+ write?: Writer;
29
+ writeError?: Writer;
30
+ }
31
+ export declare function runCli(argv: readonly string[], dependencies?: CliDependencies): Promise<number>;
32
+ export {};
33
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAgB,MAAM,WAAW,CAAC;AAK3D,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,eAAe,EACf,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AACxC,KAAK,eAAe,GAAG,CACrB,MAAM,EAAE,UAAU,EAClB,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,EACtC,aAAa,CAAC,EAAE,WAAW,KACxB,OAAO,CAAC,eAAe,CAAC,CAAC;AAC9B,KAAK,iBAAiB,GAAG,CACvB,MAAM,EAAE,UAAU,EAClB,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,EACtC,SAAS,EAAE,eAAe,EAC1B,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;AACrC,KAAK,cAAc,GAAG,CACpB,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,eAAe,CAAC,CAAC;AAC9B,KAAK,eAAe,GAAG,CACrB,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,eAAe,CAAC,CAAC;AAC9B,KAAK,kBAAkB,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;AACvD,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAC3E,KAAK,cAAc,GAAG,CACpB,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,eAAe,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;AACnB,KAAK,eAAe,GAAG,CACrB,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,eAAe,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,eAAe,CAAC,EAAE,iBAAiB,CAAC;IACpC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+DD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,CAyHjB"}
package/dist/cli.js ADDED
@@ -0,0 +1,175 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { downloadVerifiedInstaller } from './artifact.js';
3
+ import { createTemporaryInstallConfig } from './config-file.js';
4
+ import { createTranslator, detectLocale } from './i18n.js';
5
+ import { installerManifest } from './installer-manifest.js';
6
+ import { installLocal, preflightLocal } from './local.js';
7
+ import { collectSettings, collectTarget, inquirerPrompt } from './prompts.js';
8
+ import { ProcessRunner } from './process.js';
9
+ import { installRemote, preflightRemote } from './remote.js';
10
+ import { ValidationError } from './validation.js';
11
+ const TRANSLATED_ERROR_CODES = {
12
+ invalidHost: 'invalidHost',
13
+ invalidPort: 'invalidPort',
14
+ invalidUsername: 'invalidUsername',
15
+ invalidInstallDir: 'invalidInstallDir',
16
+ invalidSerial: 'invalidSerial',
17
+ weakPassword: 'weakPassword',
18
+ passwordMismatch: 'passwordMismatch',
19
+ unsupportedLocalPlatform: 'unsupportedLocalPlatform',
20
+ checksumFailed: 'checksumFailed',
21
+ insufficientDiskSpace: 'insufficientDiskSpace',
22
+ cleanupFailed: 'cleanupFailed',
23
+ invalidRemotePreflightOutput: 'invalidRemotePreflightOutput',
24
+ unsupportedRemoteClientPlatform: 'unsupportedRemoteClientPlatform',
25
+ };
26
+ function parseArguments(argv) {
27
+ if (argv.length === 0)
28
+ return { action: 'install' };
29
+ if (argv.length === 1 && argv[0] === '--help')
30
+ return { action: 'help' };
31
+ if (argv.length === 1 && argv[0] === '--version')
32
+ return { action: 'version' };
33
+ if (argv.length === 2
34
+ && argv[0] === '--mode'
35
+ && (argv[1] === 'local' || argv[1] === 'remote')) {
36
+ return { action: 'install', mode: argv[1] };
37
+ }
38
+ return { action: 'invalid' };
39
+ }
40
+ function packageVersion() {
41
+ const contents = readFileSync(new URL('../package.json', import.meta.url), 'utf8');
42
+ const parsed = JSON.parse(contents);
43
+ if (typeof parsed !== 'object'
44
+ || parsed === null
45
+ || !('version' in parsed)
46
+ || typeof parsed.version !== 'string') {
47
+ throw new Error('invalidPackageVersion');
48
+ }
49
+ return parsed.version;
50
+ }
51
+ function errorMessage(error, t) {
52
+ if (!(error instanceof Error))
53
+ return String(error);
54
+ const rawCode = error instanceof ValidationError ? error.code : error.message;
55
+ const code = rawCode.split(':', 1)[0] ?? rawCode;
56
+ const key = TRANSLATED_ERROR_CODES[code];
57
+ return key ? t(key) : error.message;
58
+ }
59
+ function isPromptCancellation(error) {
60
+ return error instanceof Error && error.name === 'ExitPromptError';
61
+ }
62
+ export async function runCli(argv, dependencies = {}) {
63
+ const locale = detectLocale(dependencies.locale ?? Intl.DateTimeFormat().resolvedOptions().locale);
64
+ const t = createTranslator(locale);
65
+ const write = dependencies.write ?? ((message) => process.stdout.write(`${message}\n`));
66
+ const writeError = dependencies.writeError ?? ((message) => process.stderr.write(`${message}\n`));
67
+ const parsed = parseArguments(argv);
68
+ if (parsed.action === 'invalid') {
69
+ writeError(t('usageError'));
70
+ return 2;
71
+ }
72
+ if (parsed.action === 'help') {
73
+ write([
74
+ t('cliDescription'),
75
+ '',
76
+ t('helpUsage'),
77
+ t('helpMode'),
78
+ t('helpHelp'),
79
+ t('helpVersion'),
80
+ ].join('\n'));
81
+ return 0;
82
+ }
83
+ if (parsed.action === 'version') {
84
+ write(dependencies.version ?? packageVersion());
85
+ return 0;
86
+ }
87
+ const prompt = dependencies.prompt ?? inquirerPrompt;
88
+ const runner = dependencies.runner ?? new ProcessRunner();
89
+ const targetCollector = dependencies.collectTarget ?? collectTarget;
90
+ const settingsCollector = dependencies.collectSettings ?? collectSettings;
91
+ const localPreflight = dependencies.preflightLocal ?? preflightLocal;
92
+ const remotePreflight = dependencies.preflightRemote ?? preflightRemote;
93
+ const artifactDownloader = dependencies.downloadArtifact
94
+ ?? (() => downloadVerifiedInstaller(installerManifest));
95
+ const configCreator = dependencies.createConfig ?? createTemporaryInstallConfig;
96
+ const localInstaller = dependencies.installLocal ?? installLocal;
97
+ let artifact;
98
+ let config;
99
+ let operationError;
100
+ let installed = false;
101
+ try {
102
+ const target = await targetCollector(prompt, t, parsed.mode);
103
+ write(t('phasePreflight'));
104
+ let preflight;
105
+ if (target.mode === 'remote') {
106
+ if (!target.remote)
107
+ throw new Error('invalidRemoteTarget');
108
+ preflight = await remotePreflight(runner, target.remote, target.installDir);
109
+ write(t('remoteTargetSummary', {
110
+ user: target.remote.username,
111
+ host: target.remote.host,
112
+ port: target.remote.port,
113
+ }));
114
+ }
115
+ else {
116
+ preflight = await localPreflight(runner, target.installDir);
117
+ write(t('localTargetSummary'));
118
+ }
119
+ if (preflight.existingInstall)
120
+ write(t('existingInstall'));
121
+ const settings = await settingsCollector(prompt, t, preflight, target.installDir);
122
+ if (settings === null) {
123
+ write(t('cancelled'));
124
+ return 0;
125
+ }
126
+ write(t('phaseDownload'));
127
+ artifact = await artifactDownloader();
128
+ config = await configCreator(settings);
129
+ if (target.mode === 'remote') {
130
+ if (!target.remote)
131
+ throw new Error('invalidRemoteTarget');
132
+ write(t('phaseTransfer'));
133
+ write(t('phaseInstall'));
134
+ if (dependencies.installRemote) {
135
+ await dependencies.installRemote(runner, target.remote, artifact, config, settings);
136
+ }
137
+ else {
138
+ await installRemote(runner, target.remote, artifact, config, settings, undefined, () => writeError(t('cleanupWarning')));
139
+ }
140
+ }
141
+ else {
142
+ write(t('phaseInstall'));
143
+ await localInstaller(runner, artifact, config, settings);
144
+ }
145
+ installed = true;
146
+ }
147
+ catch (error) {
148
+ if (isPromptCancellation(error) && !artifact && !config) {
149
+ write(t('cancelled'));
150
+ return 0;
151
+ }
152
+ operationError = error;
153
+ }
154
+ if (config || artifact)
155
+ write(t('phaseCleanup'));
156
+ for (const temporary of [config, artifact]) {
157
+ if (!temporary)
158
+ continue;
159
+ try {
160
+ await temporary.cleanup();
161
+ }
162
+ catch {
163
+ writeError(t('cleanupWarning'));
164
+ operationError ??= new Error('cleanupFailed');
165
+ }
166
+ }
167
+ if (operationError !== undefined) {
168
+ writeError(errorMessage(operationError, t));
169
+ return 1;
170
+ }
171
+ if (installed)
172
+ write(t('installComplete'));
173
+ return 0;
174
+ }
175
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { InstallSettings } from './types.js';
2
+ export interface TemporaryFile {
3
+ path: string;
4
+ cleanup: () => Promise<void>;
5
+ }
6
+ export interface TemporaryInstallConfig extends TemporaryFile {
7
+ path: string;
8
+ directory: string;
9
+ }
10
+ export declare function serializeInstallConfig(settings: InstallSettings): string;
11
+ export declare function createTemporaryInstallConfig(settings: InstallSettings, tempRoot?: string): Promise<TemporaryInstallConfig>;
12
+ //# sourceMappingURL=config-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../src/config-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CASxE;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,eAAe,EACzB,QAAQ,SAAW,GAClB,OAAO,CAAC,sBAAsB,CAAC,CAkBjC"}
@@ -0,0 +1,35 @@
1
+ import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ function encode(value) {
5
+ return Buffer.from(value, 'utf8').toString('base64');
6
+ }
7
+ export function serializeInstallConfig(settings) {
8
+ return [
9
+ 'format=1',
10
+ `install_dir_base64=${encode(settings.installDir)}`,
11
+ `device_serial_base64=${encode(settings.deviceSerial)}`,
12
+ `admin_password_base64=${encode(settings.adminPassword ?? '')}`,
13
+ `enable_auto_update=${settings.enableAutoUpdate ? 'true' : 'false'}`,
14
+ '',
15
+ ].join('\n');
16
+ }
17
+ export async function createTemporaryInstallConfig(settings, tempRoot = tmpdir()) {
18
+ const directory = await mkdtemp(join(tempRoot, 'create-wpt-iot-'));
19
+ const path = join(directory, 'install.conf');
20
+ try {
21
+ await chmod(directory, 0o700);
22
+ await writeFile(path, serializeInstallConfig(settings), { encoding: 'utf8', mode: 0o600 });
23
+ await chmod(path, 0o600);
24
+ }
25
+ catch (error) {
26
+ await rm(directory, { recursive: true, force: true });
27
+ throw error;
28
+ }
29
+ return {
30
+ path,
31
+ directory,
32
+ cleanup: async () => rm(directory, { recursive: true, force: true }),
33
+ };
34
+ }
35
+ //# sourceMappingURL=config-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../src/config-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAcjC,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAyB;IAC9D,OAAO;QACL,UAAU;QACV,sBAAsB,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;QACnD,wBAAwB,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;QACvD,yBAAyB,MAAM,CAAC,QAAQ,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE;QAC/D,sBAAsB,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;QACpE,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,QAAyB,EACzB,QAAQ,GAAG,MAAM,EAAE;IAEnB,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,SAAS,CAAC,IAAI,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3F,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI;QACJ,SAAS;QACT,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC"}
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { en } from './messages/en.js';
2
+ export type Locale = 'en' | 'it';
3
+ export type MessageKey = keyof typeof en;
4
+ export type Translator = (key: MessageKey, values?: Readonly<Record<string, string | number>>) => string;
5
+ export declare function detectLocale(locale?: string): Locale;
6
+ export declare function createTranslator(locale: Locale): Translator;
7
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAGtC,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACjC,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,EAAE,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,CACvB,GAAG,EAAE,UAAU,EACf,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,KAC/C,MAAM,CAAC;AAIZ,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAK3D"}
package/dist/i18n.js ADDED
@@ -0,0 +1,10 @@
1
+ import { en } from './messages/en.js';
2
+ import { it } from './messages/it.js';
3
+ const catalogs = { en, it };
4
+ export function detectLocale(locale) {
5
+ return locale?.replace('_', '-').toLowerCase().startsWith('it') ? 'it' : 'en';
6
+ }
7
+ export function createTranslator(locale) {
8
+ return (key, values = {}) => catalogs[locale][key].replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (token, name) => name in values ? String(values[name]) : token);
9
+ }
10
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAStC,MAAM,QAAQ,GAA+C,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAExE,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CACxD,6BAA6B,EAC7B,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CACvE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { InstallerManifest } from './artifact.js';
2
+ export declare const installerManifest: InstallerManifest;
3
+ //# sourceMappingURL=installer-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer-manifest.d.ts","sourceRoot":"","sources":["../src/installer-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const installerManifest = {
2
+ owner: 'chetto1983',
3
+ repository: 'wpt-iot',
4
+ ref: 'b58bb4ac23807bb628d82027a5e6f97487329711',
5
+ sha256: 'f3c36f3879934e98d1c73bffcf570cf72ddc894c510d5290e7dea07c2472ff77',
6
+ };
7
+ //# sourceMappingURL=installer-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer-manifest.js","sourceRoot":"","sources":["../src/installer-manifest.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,KAAK,EAAE,YAAY;IACnB,UAAU,EAAE,SAAS;IACrB,GAAG,EAAE,uBAAuB;IAC5B,MAAM,EAAE,0BAA0B;CACnC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { TemporaryFile } from './config-file.js';
2
+ import type { CommandRunner } from './process.js';
3
+ import type { InstallSettings, PreflightResult } from './types.js';
4
+ export declare function preflightLocal(runner: CommandRunner, installDir: string, platform?: NodeJS.Platform): Promise<PreflightResult>;
5
+ export declare function installLocal(runner: CommandRunner, artifact: TemporaryFile, config: TemporaryFile, settings: InstallSettings): Promise<void>;
6
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AASnE,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,eAAe,CAAC,CAiC1B;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC,CAMf"}
package/dist/local.js ADDED
@@ -0,0 +1,42 @@
1
+ import { assertSufficientDiskSpace, normalizeArchitecture } from './preflight.js';
2
+ const REQUIRED_COMMANDS = ['apt-get', 'systemctl', 'sudo', 'curl'];
3
+ const REQUIRED_HOSTS = [
4
+ 'https://ghcr.io',
5
+ 'https://raw.githubusercontent.com',
6
+ 'https://get.docker.com',
7
+ ];
8
+ export async function preflightLocal(runner, installDir, platform = process.platform) {
9
+ if (platform !== 'linux')
10
+ throw new Error('unsupportedLocalPlatform');
11
+ for (const command of REQUIRED_COMMANDS) {
12
+ await runner.run('sh', [
13
+ '-c',
14
+ 'command -v "$1" >/dev/null 2>&1',
15
+ 'create-wpt-iot',
16
+ command,
17
+ ]);
18
+ }
19
+ const architecture = normalizeArchitecture((await runner.run('uname', ['-m'])).stdout);
20
+ const existingInstall = (await runner.run('sh', [
21
+ '-c',
22
+ 'if test -f "$1/docker-compose.yml"; then printf "true\\n"; else printf "false\\n"; fi',
23
+ 'create-wpt-iot',
24
+ installDir,
25
+ ])).stdout.trim() === 'true';
26
+ const detectedSerial = (await runner.run('sh', [
27
+ '-c',
28
+ 'if test -r /etc/wpt/serial; then cat /etc/wpt/serial; fi',
29
+ ])).stdout.trim();
30
+ assertSufficientDiskSpace((await runner.run('sh', [
31
+ '-c',
32
+ "df -Pk / | awk 'NR == 2 { print $4 }'",
33
+ ])).stdout);
34
+ for (const host of REQUIRED_HOSTS) {
35
+ await runner.run('curl', ['-fsS', '-o', '/dev/null', '-m', '10', host]);
36
+ }
37
+ return { architecture, existingInstall, detectedSerial };
38
+ }
39
+ export async function installLocal(runner, artifact, config, settings) {
40
+ await runner.run('sudo', ['bash', artifact.path, '--config', config.path], { redactions: [settings.adminPassword] });
41
+ }
42
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../src/local.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAIlF,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAU,CAAC;AAC5E,MAAM,cAAc,GAAG;IACrB,iBAAiB;IACjB,mCAAmC;IACnC,wBAAwB;CAChB,CAAC;AAEX,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqB,EACrB,UAAkB,EAClB,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,QAAQ,KAAK,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAEtE,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;QACxC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;YACrB,IAAI;YACJ,iCAAiC;YACjC,gBAAgB;YAChB,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IAED,MAAM,YAAY,GAAG,qBAAqB,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACvF,MAAM,eAAe,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;QAC9C,IAAI;QACJ,uFAAuF;QACvF,gBAAgB;QAChB,UAAU;KACX,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;IAC7B,MAAM,cAAc,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;QAC7C,IAAI;QACJ,0DAA0D;KAC3D,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAClB,yBAAyB,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;QAChD,IAAI;QACJ,uCAAuC;KACxC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,QAAuB,EACvB,MAAqB,EACrB,QAAyB;IAEzB,MAAM,MAAM,CAAC,GAAG,CACd,MAAM,EACN,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAChD,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CACzC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ export declare const en: {
2
+ readonly cliDescription: "Install or update one WPT IoT edge device";
3
+ readonly helpUsage: "Usage: create-wpt-iot [--mode local|remote] [--help] [--version]";
4
+ readonly helpMode: " --mode local|remote Skip only the installation mode question";
5
+ readonly helpHelp: " --help Show this help";
6
+ readonly helpVersion: " --version Show the package version";
7
+ readonly usageError: "Invalid arguments. Use --help to see the supported options.";
8
+ readonly modeQuestion: "Where do you want to install WPT IoT?";
9
+ readonly modeLocal: "This Linux device";
10
+ readonly modeRemote: "A remote Linux device over SSH";
11
+ readonly remoteHost: "Target hostname or IP address";
12
+ readonly remotePort: "SSH port";
13
+ readonly remoteUsername: "SSH username";
14
+ readonly installDir: "Installation directory";
15
+ readonly deviceSerial: "Device serial used by wpt-<serial>.local";
16
+ readonly adminPassword: "Initial admin password";
17
+ readonly adminPasswordConfirm: "Confirm the admin password";
18
+ readonly autoUpdate: "Enable automatic backend/frontend updates every 5 minutes?";
19
+ readonly confirmInstall: "Apply this installation?";
20
+ readonly existingInstall: "Existing installation detected; credentials and data will be preserved.";
21
+ readonly remoteTargetSummary: "Target: {user}@{host}:{port}";
22
+ readonly localTargetSummary: "Target: this Linux device";
23
+ readonly phasePreflight: "Preflight checks";
24
+ readonly phaseDownload: "Download verified installer";
25
+ readonly phaseTransfer: "Transfer secure configuration";
26
+ readonly phaseInstall: "Install WPT IoT";
27
+ readonly phaseCleanup: "Remove temporary files";
28
+ readonly installComplete: "WPT IoT is healthy at https://wpt.local";
29
+ readonly cancelled: "Installation cancelled before making changes.";
30
+ readonly unsupportedLocalPlatform: "Local mode requires Linux with apt and systemd. Use remote mode from Windows.";
31
+ readonly invalidHost: "Enter a valid hostname or IP address.";
32
+ readonly invalidPort: "Enter a port between 1 and 65535.";
33
+ readonly invalidUsername: "Enter a valid Linux username.";
34
+ readonly invalidInstallDir: "Enter an absolute Linux path other than /.";
35
+ readonly invalidSerial: "Use 1-32 lowercase letters, digits, or hyphens.";
36
+ readonly weakPassword: "Use at least 12 characters.";
37
+ readonly passwordMismatch: "The passwords do not match.";
38
+ readonly missingCommand: "Required command not found: {command}";
39
+ readonly preflightFailed: "Preflight failed: {reason}";
40
+ readonly checksumFailed: "Installer integrity verification failed.";
41
+ readonly insufficientDiskSpace: "At least 12 GiB of free target disk space is required.";
42
+ readonly cleanupWarning: "Warning: a temporary file could not be removed.";
43
+ readonly cleanupFailed: "Temporary-file cleanup failed.";
44
+ readonly invalidRemotePreflightOutput: "The remote preflight returned an invalid response.";
45
+ readonly unsupportedRemoteClientPlatform: "Remote mode requires Windows or Linux.";
46
+ readonly installFailed: "Installation failed during {phase}. It is safe to run the wizard again.";
47
+ };
48
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CL,CAAC"}