@meshxdata/fops 0.1.51 → 0.1.53
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 +207 -21
- package/package.json +2 -6
- package/src/agent/agent.js +6 -0
- package/src/commands/setup.js +34 -0
- package/src/doctor.js +11 -8
- package/src/fleet-registry.js +38 -2
- package/src/plugins/__test-fixtures__/fake-plugin.js +2 -0
- package/src/plugins/__test-fixtures__/no-register-plugin.js +2 -0
- package/src/plugins/__test-fixtures__/with-register/index.js +2 -0
- package/src/plugins/__test-fixtures__/without-register/index.js +2 -0
- package/src/plugins/api.js +4 -0
- package/src/plugins/builtins/docker-compose.js +59 -0
- package/src/plugins/bundled/fops-plugin-azure/index.js +4 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-core.js +53 -53
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-secrets.js +151 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-storage.js +2 -2
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-cost.js +52 -22
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-fleet.js +12 -4
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-helpers.js +6 -2
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-ops.js +113 -7
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-provision-init.js +13 -4
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-provision.js +91 -14
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-service.js +507 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-sync.js +146 -7
- package/src/plugins/bundled/fops-plugin-azure/lib/azure.js +1 -1
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/test-cmds.js +28 -0
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/vm-cmds.js +61 -0
- package/src/plugins/bundled/fops-plugin-cloud/api.js +712 -0
- package/src/plugins/bundled/fops-plugin-cloud/fops.plugin.json +6 -0
- package/src/plugins/bundled/fops-plugin-cloud/index.js +208 -0
- package/src/plugins/bundled/fops-plugin-cloud/lib/azure-provider.js +81 -0
- package/src/plugins/bundled/fops-plugin-cloud/lib/provider.js +50 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/favicon-C49brna2.svg +15 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/index-CVqQ_kKW.js +65 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/index-DZetahP3.css +1 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/dist/index.html +28 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/index.html +27 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/package-lock.json +2634 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/package.json +29 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/postcss.config.cjs +5 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/App.jsx +32 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/client.js +114 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/queries.js +111 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/LogPanel.jsx +162 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/ThemeToggle.jsx +46 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/additional-styles/utility-patterns.css +147 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/style.css +138 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/favicon.svg +15 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/lib/utils.ts +19 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/main.jsx +25 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Audit.jsx +164 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Costs.jsx +305 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/CreateResource.jsx +285 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Fleet.jsx +307 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Resources.jsx +229 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Header.jsx +132 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Sidebar.jsx +174 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/SidebarLinkGroup.jsx +21 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/AuthContext.jsx +170 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Info.jsx +49 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/ThemeContext.jsx +37 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Transition.jsx +116 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Utils.js +63 -0
- package/src/plugins/bundled/fops-plugin-cloud/ui/vite.config.js +23 -0
- package/src/plugins/bundled/fops-plugin-foundation/test-helpers.js +65 -0
- package/src/plugins/loader.js +34 -1
- package/src/plugins/registry.js +15 -0
- package/src/plugins/schemas.js +17 -0
- package/src/project.js +1 -1
- package/src/serve.js +196 -2
- package/src/shell.js +21 -1
- package/src/web/admin.html.js +236 -0
- package/src/web/api.js +73 -0
- package/src/web/dist/assets/index-BphVaAUd.css +1 -0
- package/src/web/dist/assets/index-CSckLzuG.js +129 -0
- package/src/web/dist/index.html +2 -2
- package/src/web/frontend/index.html +16 -0
- package/src/web/frontend/src/App.jsx +445 -0
- package/src/web/frontend/src/components/ChatView.jsx +910 -0
- package/src/web/frontend/src/components/InputBox.jsx +523 -0
- package/src/web/frontend/src/components/Sidebar.jsx +410 -0
- package/src/web/frontend/src/components/StatusBar.jsx +37 -0
- package/src/web/frontend/src/components/TabBar.jsx +87 -0
- package/src/web/frontend/src/hooks/useWebSocket.js +412 -0
- package/src/web/frontend/src/index.css +78 -0
- package/src/web/frontend/src/main.jsx +6 -0
- package/src/web/frontend/vite.config.js +21 -0
- package/src/web/server.js +64 -1
- package/src/web/dist/assets/index-NXC8Hvnp.css +0 -1
- package/src/web/dist/assets/index-QH1N4ejK.js +0 -112
|
@@ -44,19 +44,26 @@ export async function provisionVm(execa, ip, adminUser, { githubToken, branch =
|
|
|
44
44
|
"apt-get install -y -qq apt-transport-https ca-certificates curl gnupg lsb-release jq git make unzip zsh software-properties-common python3-venv python3-pip",
|
|
45
45
|
].join("\n"), 300000);
|
|
46
46
|
|
|
47
|
-
await runScript("Installing Docker", [
|
|
47
|
+
const dockerExit = await runScript("Installing Docker", [
|
|
48
48
|
waitAptLock,
|
|
49
49
|
"export DEBIAN_FRONTEND=noninteractive",
|
|
50
50
|
"install -m 0755 -d /etc/apt/keyrings",
|
|
51
|
-
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg",
|
|
51
|
+
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg",
|
|
52
52
|
"chmod a+r /etc/apt/keyrings/docker.gpg",
|
|
53
53
|
`echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list`,
|
|
54
|
-
"
|
|
55
|
-
"apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin",
|
|
54
|
+
"set +e",
|
|
55
|
+
"for _ in 1 2 3 4 5; do if apt-get update -qq && apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then break; fi; echo 'Retrying Docker install in 10s…'; sleep 10; done",
|
|
56
|
+
"set -e",
|
|
57
|
+
"command -v docker >/dev/null 2>&1 || (echo 'Docker not found after install attempts' && exit 1)",
|
|
56
58
|
"systemctl enable docker && systemctl start docker",
|
|
57
59
|
`usermod -aG docker ${adminUser}`,
|
|
58
60
|
].join("\n"), 300000);
|
|
59
61
|
|
|
62
|
+
if (dockerExit !== 0) {
|
|
63
|
+
console.log(WARN(" ✗ Docker installation failed — cannot continue provisioning"));
|
|
64
|
+
throw new Error("Docker installation failed");
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
await runScript("Configuring br_netfilter for k3s DNS", [
|
|
61
68
|
"modprobe br_netfilter",
|
|
62
69
|
"echo br_netfilter > /etc/modules-load.d/br_netfilter.conf",
|
|
@@ -178,6 +185,8 @@ export async function provisionVm(execa, ip, adminUser, { githubToken, branch =
|
|
|
178
185
|
Project dir: /opt/foundation-compose
|
|
179
186
|
|
|
180
187
|
MOTD`,
|
|
188
|
+
`grep -q 'cd /opt/foundation-compose' /home/${adminUser}/.bashrc 2>/dev/null || echo 'cd /opt/foundation-compose' >> /home/${adminUser}/.bashrc`,
|
|
189
|
+
`grep -q 'cd /opt/foundation-compose' /home/${adminUser}/.zshrc 2>/dev/null || echo 'cd /opt/foundation-compose' >> /home/${adminUser}/.zshrc`,
|
|
181
190
|
].join("\n"));
|
|
182
191
|
|
|
183
192
|
await ssh("sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/*", 30000);
|
|
@@ -44,7 +44,7 @@ async function ensureGhcrOnVm(ssh, user, githubToken, { timeout = 60000 } = {})
|
|
|
44
44
|
// ── Configure a fresh or restarted VM ───────────────────────────────────────
|
|
45
45
|
|
|
46
46
|
export async function configureVm(execa, ip, user, publicUrl, { githubToken, k3s, traefik, dai, deferStartToReconcile, quiet } = {}) {
|
|
47
|
-
const ssh = (cmd) => sshCmd(execa, ip, user, cmd);
|
|
47
|
+
const ssh = (cmd, timeout) => sshCmd(execa, ip, user, cmd, timeout);
|
|
48
48
|
|
|
49
49
|
if (!quiet) console.log(chalk.dim(" Configuring VM..."));
|
|
50
50
|
|
|
@@ -72,6 +72,68 @@ export async function configureVm(execa, ip, user, publicUrl, { githubToken, k3s
|
|
|
72
72
|
].join("\n");
|
|
73
73
|
await ssh(setupBatch);
|
|
74
74
|
|
|
75
|
+
// Verify Docker is installed — if missing, install it before anything else
|
|
76
|
+
const { exitCode: dockerCheck } = await ssh("sudo docker info >/dev/null 2>&1");
|
|
77
|
+
if (dockerCheck !== 0) {
|
|
78
|
+
if (!quiet) console.log(chalk.yellow(" ⚠ Docker not found — installing..."));
|
|
79
|
+
// Repo setup is idempotent (tolerates partial prior attempts); install+start uses &&
|
|
80
|
+
const repoSetup = [
|
|
81
|
+
"export DEBIAN_FRONTEND=noninteractive",
|
|
82
|
+
"while fuser /var/lib/dpkg/lock-frontend /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do sleep 3; done",
|
|
83
|
+
"sudo install -m 0755 -d /etc/apt/keyrings",
|
|
84
|
+
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg",
|
|
85
|
+
"sudo chmod a+r /etc/apt/keyrings/docker.gpg",
|
|
86
|
+
`echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null`,
|
|
87
|
+
].join("; ");
|
|
88
|
+
const installAndStart = [
|
|
89
|
+
"sudo apt-get update -qq",
|
|
90
|
+
"sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin",
|
|
91
|
+
"sudo systemctl enable docker && sudo systemctl start docker",
|
|
92
|
+
`sudo usermod -aG docker ${user}`,
|
|
93
|
+
].join(" && ");
|
|
94
|
+
const { exitCode: installExit } = await ssh(`${repoSetup}; ${installAndStart}`, 300000);
|
|
95
|
+
if (installExit === 0) {
|
|
96
|
+
if (!quiet) console.log(chalk.green(" ✓ Docker installed"));
|
|
97
|
+
} else {
|
|
98
|
+
console.log(chalk.red(" ✗ Docker installation failed — container operations will not work"));
|
|
99
|
+
console.log(chalk.dim(` SSH in and check: ssh ${user}@${ip} "sudo apt-get install -y docker-ce"`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Verify Node.js + fops CLI are installed — if missing, install them
|
|
104
|
+
const { stdout: fopsWhich } = await ssh("command -v fops 2>/dev/null || echo MISSING");
|
|
105
|
+
if (!fopsWhich?.trim() || fopsWhich.includes("MISSING")) {
|
|
106
|
+
if (!quiet) console.log(chalk.yellow(" ⚠ fops CLI not found — installing Node.js + fops..."));
|
|
107
|
+
const installNode = [
|
|
108
|
+
"export DEBIAN_FRONTEND=noninteractive",
|
|
109
|
+
"if ! command -v node >/dev/null 2>&1; then curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - && sudo apt-get install -y -qq nodejs; fi",
|
|
110
|
+
].join("; ");
|
|
111
|
+
await ssh(installNode, 120000);
|
|
112
|
+
// Install fops globally, retry with sudo if needed
|
|
113
|
+
let fopsInstalled = false;
|
|
114
|
+
const { exitCode: userInstall } = await ssh("npm install -g @meshxdata/fops@latest 2>&1", 300000);
|
|
115
|
+
if (userInstall === 0) {
|
|
116
|
+
fopsInstalled = true;
|
|
117
|
+
} else {
|
|
118
|
+
const { exitCode: sudoInstall } = await ssh(
|
|
119
|
+
"sudo bash -c 'D=\"$(npm root -g)/@meshxdata\"; rm -rf \"$D\" 2>/dev/null; npm install -g @meshxdata/fops@latest' 2>&1",
|
|
120
|
+
300000,
|
|
121
|
+
);
|
|
122
|
+
fopsInstalled = sudoInstall === 0;
|
|
123
|
+
}
|
|
124
|
+
// Ensure fops is on PATH
|
|
125
|
+
await ssh(
|
|
126
|
+
'MJS="$(npm root -g 2>/dev/null)/@meshxdata/fops/fops.mjs"; [ -f "$MJS" ] || MJS="$(sudo npm root -g 2>/dev/null)/@meshxdata/fops/fops.mjs"; [ -f "$MJS" ] && sudo ln -sf "$MJS" /usr/local/bin/fops; true',
|
|
127
|
+
15000,
|
|
128
|
+
);
|
|
129
|
+
if (fopsInstalled) {
|
|
130
|
+
if (!quiet) console.log(chalk.green(" ✓ fops CLI installed"));
|
|
131
|
+
} else {
|
|
132
|
+
console.log(chalk.red(" ✗ fops CLI installation failed"));
|
|
133
|
+
console.log(chalk.dim(` SSH in and check: ssh ${user}@${ip} "sudo npm install -g @meshxdata/fops@latest"`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
75
137
|
let ghcrOk = false;
|
|
76
138
|
if (githubToken) {
|
|
77
139
|
if (!quiet) console.log(chalk.dim(" Configuring GitHub/GHCR credentials..."));
|
|
@@ -458,9 +520,12 @@ async function vmReconcileNetworking(ctx) {
|
|
|
458
520
|
console.log(chalk.yellow(" ⚠ No NSG attached to NIC"));
|
|
459
521
|
}
|
|
460
522
|
|
|
523
|
+
// Accelerated networking — only supported on D/E/F/M series (2+ vCPU), not B-series
|
|
524
|
+
const vmSize = (iv.hardwareProfile?.vmSize || "").toLowerCase();
|
|
525
|
+
const supportsAccelNet = !vmSize.startsWith("standard_b") && !vmSize.startsWith("standard_a");
|
|
461
526
|
if (nic.enableAcceleratedNetworking) {
|
|
462
527
|
reconcileOk("Accelerated networking", "enabled");
|
|
463
|
-
} else {
|
|
528
|
+
} else if (supportsAccelNet) {
|
|
464
529
|
console.log(chalk.yellow(" ↻ Accelerated networking not enabled — enabling…"));
|
|
465
530
|
const { exitCode: anCode } = await execa("az", [
|
|
466
531
|
"network", "nic", "update", "-g", rg, "-n", ctx.nicName,
|
|
@@ -469,8 +534,9 @@ async function vmReconcileNetworking(ctx) {
|
|
|
469
534
|
], { reject: false, timeout: 30000 });
|
|
470
535
|
console.log(anCode === 0
|
|
471
536
|
? chalk.green(` ✓ ${"Accelerated networking".padEnd(RECONCILE_LABEL_WIDTH)} — enabled`)
|
|
472
|
-
: chalk.yellow(" ⚠ Could not enable accelerated networking
|
|
537
|
+
: chalk.yellow(" ⚠ Could not enable accelerated networking"));
|
|
473
538
|
}
|
|
539
|
+
// B-series and A-series VMs don't support accelerated networking — skip silently
|
|
474
540
|
}
|
|
475
541
|
|
|
476
542
|
if (!ctx.ip) ctx.ip = await resolvePublicIp(execa, rg, vmName);
|
|
@@ -832,10 +898,16 @@ async function vmReconcileSecurity(ctx) {
|
|
|
832
898
|
reconcileOk("Boot diagnostics", "enabled");
|
|
833
899
|
} else {
|
|
834
900
|
console.log(chalk.yellow(" ↻ Boot diagnostics not enabled — enabling..."));
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
901
|
+
let bdCode = 1;
|
|
902
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
903
|
+
const res = await execa("az", [
|
|
904
|
+
"vm", "boot-diagnostics", "enable", "-g", rg, "-n", vmName, "--output", "none",
|
|
905
|
+
...subArgs(sub),
|
|
906
|
+
], { reject: false, timeout: 30000 });
|
|
907
|
+
bdCode = res.exitCode;
|
|
908
|
+
if (bdCode === 0) break;
|
|
909
|
+
if (attempt < 2) await new Promise((r) => setTimeout(r, 5000));
|
|
910
|
+
}
|
|
839
911
|
if (bdCode === 0) {
|
|
840
912
|
await new Promise((r) => setTimeout(r, 2000));
|
|
841
913
|
const { stdout: bdJson } = await execa("az", [
|
|
@@ -880,20 +952,25 @@ async function vmReconcileSecurity(ctx) {
|
|
|
880
952
|
console.log(amCode === 0
|
|
881
953
|
? chalk.green(` ✓ ${"Antimalware extension".padEnd(RECONCILE_LABEL_WIDTH)} — installed`)
|
|
882
954
|
: chalk.yellow(" ⚠ Could not install antimalware extension"));
|
|
883
|
-
} else {
|
|
884
|
-
console.log(chalk.dim(` Antimalware extension — Windows only (skipped on Linux)`));
|
|
885
955
|
}
|
|
956
|
+
// Linux VMs don't need antimalware — skip silently
|
|
886
957
|
|
|
887
958
|
if (isTrustedLaunch) {
|
|
888
959
|
if (hasGuestAttestation) {
|
|
889
960
|
reconcileOk("Guest Attestation extension", "installed");
|
|
890
961
|
} else {
|
|
891
962
|
console.log(chalk.yellow(" ↻ Guest Attestation missing — installing…"));
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
963
|
+
let gaCode = 1;
|
|
964
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
965
|
+
const res = await execa("az", [
|
|
966
|
+
"vm", "extension", "set", "-g", rg, "--vm-name", vmName,
|
|
967
|
+
"-n", "GuestAttestation", "--publisher", "Microsoft.Azure.Security.LinuxAttestation",
|
|
968
|
+
"--output", "none", ...subArgs(sub),
|
|
969
|
+
], { reject: false, timeout: 120000 });
|
|
970
|
+
gaCode = res.exitCode;
|
|
971
|
+
if (gaCode === 0) break;
|
|
972
|
+
if (attempt < 2) await new Promise((r) => setTimeout(r, 10000));
|
|
973
|
+
}
|
|
897
974
|
console.log(gaCode === 0
|
|
898
975
|
? chalk.green(` ✓ ${"Guest Attestation extension".padEnd(RECONCILE_LABEL_WIDTH)} — installed`)
|
|
899
976
|
: chalk.yellow(" ⚠ Could not install Guest Attestation extension"));
|