@meshxdata/fops 0.1.52 → 0.1.54

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 (86) hide show
  1. package/CHANGELOG.md +559 -0
  2. package/package.json +2 -6
  3. package/src/agent/agent.js +6 -0
  4. package/src/commands/setup.js +34 -0
  5. package/src/fleet-registry.js +38 -2
  6. package/src/plugins/__test-fixtures__/fake-plugin.js +2 -0
  7. package/src/plugins/__test-fixtures__/no-register-plugin.js +2 -0
  8. package/src/plugins/__test-fixtures__/with-register/index.js +2 -0
  9. package/src/plugins/__test-fixtures__/without-register/index.js +2 -0
  10. package/src/plugins/api.js +4 -0
  11. package/src/plugins/builtins/docker-compose.js +65 -0
  12. package/src/plugins/bundled/fops-plugin-azure/index.js +4 -0
  13. package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-core.js +44 -53
  14. package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks-storage.js +2 -2
  15. package/src/plugins/bundled/fops-plugin-azure/lib/azure-cost.js +52 -22
  16. package/src/plugins/bundled/fops-plugin-azure/lib/azure-helpers.js +6 -2
  17. package/src/plugins/bundled/fops-plugin-azure/lib/azure-ops.js +113 -7
  18. package/src/plugins/bundled/fops-plugin-azure/lib/azure-provision-init.js +13 -4
  19. package/src/plugins/bundled/fops-plugin-azure/lib/azure-provision.js +91 -14
  20. package/src/plugins/bundled/fops-plugin-azure/lib/azure-service.js +507 -0
  21. package/src/plugins/bundled/fops-plugin-azure/lib/azure-sync.js +146 -7
  22. package/src/plugins/bundled/fops-plugin-azure/lib/azure.js +1 -1
  23. package/src/plugins/bundled/fops-plugin-azure/lib/commands/vm-cmds.js +61 -0
  24. package/src/plugins/bundled/fops-plugin-cloud/api.js +712 -0
  25. package/src/plugins/bundled/fops-plugin-cloud/fops.plugin.json +6 -0
  26. package/src/plugins/bundled/fops-plugin-cloud/index.js +208 -0
  27. package/src/plugins/bundled/fops-plugin-cloud/lib/azure-provider.js +81 -0
  28. package/src/plugins/bundled/fops-plugin-cloud/lib/provider.js +50 -0
  29. package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/favicon-C49brna2.svg +15 -0
  30. package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/index-CVqQ_kKW.js +65 -0
  31. package/src/plugins/bundled/fops-plugin-cloud/ui/dist/assets/index-DZetahP3.css +1 -0
  32. package/src/plugins/bundled/fops-plugin-cloud/ui/dist/index.html +28 -0
  33. package/src/plugins/bundled/fops-plugin-cloud/ui/index.html +27 -0
  34. package/src/plugins/bundled/fops-plugin-cloud/ui/package-lock.json +2634 -0
  35. package/src/plugins/bundled/fops-plugin-cloud/ui/package.json +29 -0
  36. package/src/plugins/bundled/fops-plugin-cloud/ui/postcss.config.cjs +5 -0
  37. package/src/plugins/bundled/fops-plugin-cloud/ui/src/App.jsx +32 -0
  38. package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/client.js +114 -0
  39. package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/queries.js +111 -0
  40. package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/LogPanel.jsx +162 -0
  41. package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/ThemeToggle.jsx +46 -0
  42. package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/additional-styles/utility-patterns.css +147 -0
  43. package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/style.css +138 -0
  44. package/src/plugins/bundled/fops-plugin-cloud/ui/src/favicon.svg +15 -0
  45. package/src/plugins/bundled/fops-plugin-cloud/ui/src/lib/utils.ts +19 -0
  46. package/src/plugins/bundled/fops-plugin-cloud/ui/src/main.jsx +25 -0
  47. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Audit.jsx +164 -0
  48. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Costs.jsx +305 -0
  49. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/CreateResource.jsx +285 -0
  50. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Fleet.jsx +307 -0
  51. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Resources.jsx +229 -0
  52. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Header.jsx +132 -0
  53. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Sidebar.jsx +174 -0
  54. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/SidebarLinkGroup.jsx +21 -0
  55. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/AuthContext.jsx +170 -0
  56. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Info.jsx +49 -0
  57. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/ThemeContext.jsx +37 -0
  58. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Transition.jsx +116 -0
  59. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Utils.js +63 -0
  60. package/src/plugins/bundled/fops-plugin-cloud/ui/vite.config.js +23 -0
  61. package/src/plugins/bundled/fops-plugin-foundation/test-helpers.js +65 -0
  62. package/src/plugins/loader.js +34 -1
  63. package/src/plugins/registry.js +15 -0
  64. package/src/plugins/schemas.js +17 -0
  65. package/src/project.js +1 -1
  66. package/src/serve.js +196 -2
  67. package/src/shell.js +21 -1
  68. package/src/web/admin.html.js +236 -0
  69. package/src/web/api.js +73 -0
  70. package/src/web/dist/assets/index-BphVaAUd.css +1 -0
  71. package/src/web/dist/assets/index-CSckLzuG.js +129 -0
  72. package/src/web/dist/index.html +2 -2
  73. package/src/web/frontend/index.html +16 -0
  74. package/src/web/frontend/src/App.jsx +445 -0
  75. package/src/web/frontend/src/components/ChatView.jsx +910 -0
  76. package/src/web/frontend/src/components/InputBox.jsx +523 -0
  77. package/src/web/frontend/src/components/Sidebar.jsx +410 -0
  78. package/src/web/frontend/src/components/StatusBar.jsx +37 -0
  79. package/src/web/frontend/src/components/TabBar.jsx +87 -0
  80. package/src/web/frontend/src/hooks/useWebSocket.js +412 -0
  81. package/src/web/frontend/src/index.css +78 -0
  82. package/src/web/frontend/src/main.jsx +6 -0
  83. package/src/web/frontend/vite.config.js +21 -0
  84. package/src/web/server.js +64 -1
  85. package/src/web/dist/assets/index-NXC8Hvnp.css +0 -1
  86. package/src/web/dist/assets/index-QH1N4ejK.js +0 -112
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "fops-plugin-cloud",
3
+ "name": "meshXcloud",
4
+ "version": "0.1.0",
5
+ "description": "Web-based cloud management panel for Azure deployments"
6
+ }
@@ -0,0 +1,208 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+
7
+ export async function register(api) {
8
+ const distDir = path.join(__dirname, "ui", "dist");
9
+
10
+ // ── Web Panel ──────────────────────────────────────────
11
+ const { createCloudApi } = await import("./api.js");
12
+
13
+ api.registerWebPanel({
14
+ name: "cloud",
15
+ title: "meshXcloud",
16
+ prefix: "/cloud",
17
+ distDir,
18
+ apiFactory: (registry) => createCloudApi(registry),
19
+ });
20
+
21
+ // ── Embeddings: index cost, fleet, and audit data ─────
22
+ api.registerIndexSource({
23
+ name: "cloud",
24
+ fn: async (_root, log) => {
25
+ const chunks = [];
26
+ const FOPS_URL = process.env.FOPS_API_URL || "http://127.0.0.1:4100";
27
+ const safeFetch = async (path) => {
28
+ try {
29
+ const res = await fetch(`${FOPS_URL}${path}`, { signal: AbortSignal.timeout(30_000) });
30
+ if (!res.ok) return null;
31
+ return res.json();
32
+ } catch { return null; }
33
+ };
34
+
35
+ // Fleet state
36
+ const fleet = await safeFetch("/api/fleet");
37
+ if (fleet?.vms) {
38
+ for (const vm of fleet.vms) {
39
+ const svcs = vm.services ? Object.entries(vm.services).map(([k, v]) => `${k}: ${v}`).join(", ") : "";
40
+ chunks.push({
41
+ title: `Fleet: ${vm.vm}`,
42
+ content: `VM ${vm.vm} (${vm.ip || ""}) — status: ${vm.status}, containers: ${vm.containers}, disk: ${vm.disk || "?"}, memory: ${vm.memory || "?"}, load: ${vm.load || "?"}, branch: ${vm.branch || "?"}, commit: ${vm.commit || "?"}, fops: ${vm.fopsVersion || "?"}, services: ${svcs}`,
43
+ metadata: { source: "cloud-fleet", vm: vm.vm },
44
+ });
45
+ }
46
+ if (log) log(` cloud: indexed ${fleet.vms.length} VMs`);
47
+ }
48
+
49
+ // Cost data
50
+ const costs = await safeFetch("/api/tools/azure_cost_all_subscriptions");
51
+ const costData = costs?.result || costs;
52
+ if (costData?.subscriptions) {
53
+ for (const sub of costData.subscriptions) {
54
+ const services = (sub.services || []).map((s) => `${s.name || s.service}: $${s.cost}`).join(", ");
55
+ chunks.push({
56
+ title: `Costs: ${sub.name || sub.subscriptionId}`,
57
+ content: `Subscription ${sub.name || sub.subscriptionId} — total: $${sub.cost || sub.totalCost || 0}, currency: ${sub.currency || "USD"}, services: ${services}`,
58
+ metadata: { source: "cloud-costs", subscription: sub.name || sub.subscriptionId },
59
+ });
60
+ }
61
+ if (log) log(` cloud: indexed ${costData.subscriptions.length} subscription costs`);
62
+ }
63
+
64
+ // Budgets
65
+ const budgetsRaw = await safeFetch("/api/tools/azure_budgets");
66
+ const budgetList = Array.isArray(budgetsRaw?.result || budgetsRaw) ? (budgetsRaw?.result || budgetsRaw) : [];
67
+ for (const b of budgetList) {
68
+ chunks.push({
69
+ title: `Budget: ${b.name}`,
70
+ content: `Budget ${b.name} — limit: $${b.limit || b.amount || 0}, spent: $${b.spent || b.currentSpend || 0}, usage: ${b.limit ? (((b.spent || b.currentSpend || 0) / b.limit) * 100).toFixed(0) : "?"}%`,
71
+ metadata: { source: "cloud-budgets" },
72
+ });
73
+ }
74
+ if (budgetList.length && log) log(` cloud: indexed ${budgetList.length} budgets`);
75
+
76
+ // Audit findings
77
+ const audit = await safeFetch("/api/tools/security_audit_all");
78
+ const auditData = audit?.result || audit;
79
+ const allFindings = [
80
+ ...(auditData?.vms?.findings || []),
81
+ ...(auditData?.aks?.findings || []),
82
+ ...(auditData?.storage?.findings || []),
83
+ ];
84
+ if (allFindings.length) {
85
+ const warns = allFindings.filter((f) => f.severity === "warn");
86
+ chunks.push({
87
+ title: "Security Audit Summary",
88
+ content: `${allFindings.length} findings (${warns.length} warnings). ${warns.map((f) => `${f.check}: ${f.message}`).join("; ")}`,
89
+ metadata: { source: "cloud-audit" },
90
+ });
91
+ if (log) log(` cloud: indexed ${allFindings.length} audit findings`);
92
+ }
93
+
94
+ return chunks;
95
+ },
96
+ });
97
+
98
+ // ── CLI Command ────────────────────────────────────────
99
+ api.registerCommand((program, registry) => {
100
+ program
101
+ .command("cloud")
102
+ .description("Start meshXcloud web UI with fleet scraping")
103
+ .option("-p, --port <port>", "Web server port", "3099")
104
+ .option("--no-open", "Don't open browser automatically")
105
+ .option("--no-scrape", "Disable fleet scraping")
106
+ .action(async (opts) => {
107
+ const chalk = (await import("chalk")).default;
108
+ const port = parseInt(opts.port);
109
+ const url = `http://localhost:${port}/cloud`;
110
+ const root = program._fopsRoot || process.cwd();
111
+
112
+ // Check if server is already running
113
+ try {
114
+ const res = await fetch(`http://localhost:${port}/api/health`, { signal: AbortSignal.timeout(1000) });
115
+ if (res.ok) {
116
+ console.log(` Server already running on port ${port}`);
117
+ if (opts.open !== false) {
118
+ try { const open = (await import("open")).default; await open(url); } catch {}
119
+ }
120
+ console.log(` ${url}`);
121
+ await new Promise((resolve) => { process.on("SIGINT", resolve); });
122
+ return;
123
+ }
124
+ } catch {}
125
+
126
+ console.log(chalk.cyan(` Starting meshXcloud on port ${port}...`));
127
+
128
+ // Resolve core module paths via api.getCliPath (works from ~/.fops/plugins/ or source)
129
+ const serverPath = api.getCliPath?.("src", "web", "server.js");
130
+ const fleetPath = api.getCliPath?.("src", "fleet-registry.js");
131
+ const servePath = api.getCliPath?.("src", "serve.js");
132
+ const syncPath = api.getCliPath?.("src", "plugins", "bundled", "fops-plugin-azure", "lib", "azure-sync.js");
133
+
134
+ if (!serverPath) {
135
+ console.error(chalk.red(" Cannot resolve core modules. Are you running from the CLI?"));
136
+ process.exitCode = 1;
137
+ return;
138
+ }
139
+
140
+ // Start fleet scraper (SSH → VMs, same as fops serve --scrape)
141
+ if (opts.scrape !== false && fleetPath && servePath && syncPath) {
142
+ try {
143
+ const { azureSync, startBackgroundSync } = await import(syncPath);
144
+ try { await azureSync({ quiet: true }); } catch {}
145
+ startBackgroundSync(120);
146
+
147
+ const { FleetRegistry } = await import(fleetPath);
148
+ const { setFleetRegistry } = await import(servePath);
149
+ const fleet = new FleetRegistry({
150
+ interval: 60,
151
+ retention: 10,
152
+ onScrape: (snapshots) => {
153
+ const ok = snapshots.filter(s => s.status === "healthy").length;
154
+ console.log(chalk.dim(` [scrape] ${ok}/${snapshots.length} healthy ${new Date().toLocaleTimeString()}`));
155
+ },
156
+ });
157
+ setFleetRegistry(fleet);
158
+ fleet.start();
159
+ console.log(chalk.dim(" Fleet scraping enabled"));
160
+ } catch (e) {
161
+ console.log(chalk.yellow(` Fleet scraping unavailable: ${e.message}`));
162
+ }
163
+ }
164
+
165
+ // Start fops serve API on port 4100 (fleet, tools, grafana, exec endpoints)
166
+ try {
167
+ const { startServer } = await import(servePath);
168
+ const servePort = parseInt(process.env.FOPS_API_PORT || "4100");
169
+ await startServer(registry, { port: servePort, host: "127.0.0.1", skipEmbedIndex: true });
170
+ console.log(chalk.dim(` API server on port ${servePort}`));
171
+ } catch (e) {
172
+ console.log(chalk.yellow(` API server unavailable: ${e.message}`));
173
+ }
174
+
175
+ // Start web server (same as fops agent --web, mounts cloud panel + API)
176
+ try {
177
+ const { startWebServer } = await import(serverPath);
178
+ await startWebServer(root, registry, { port });
179
+ console.log(chalk.green(` meshXcloud: ${url}`));
180
+ if (opts.open !== false) {
181
+ try { const open = (await import("open")).default; await open(url); } catch {}
182
+ }
183
+ // Keep alive
184
+ await new Promise(() => {});
185
+ } catch (err) {
186
+ if (err.code === "EADDRINUSE") {
187
+ console.error(chalk.red(` Port ${port} in use. Try: fops cloud --port ${port + 1}`));
188
+ process.exitCode = 1;
189
+ } else {
190
+ throw err;
191
+ }
192
+ }
193
+ });
194
+ });
195
+
196
+ // ── Doctor Check ───────────────────────────────────────
197
+ api.registerDoctorCheck({
198
+ name: "cloud-ui-built",
199
+ fn: async ({ ok, warn }) => {
200
+ const indexPath = path.join(distDir, "index.html");
201
+ if (fs.existsSync(indexPath)) {
202
+ ok("meshXcloud UI built");
203
+ } else {
204
+ warn("meshXcloud UI not built — run: cd platform-fops/src/plugins/bundled/fops-plugin-cloud/ui && npm run build");
205
+ }
206
+ },
207
+ });
208
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * AzureProvider — wraps the azure plugin service to implement CloudProvider.
3
+ */
4
+
5
+ import { CloudProvider } from "./provider.js";
6
+
7
+ export class AzureProvider extends CloudProvider {
8
+ constructor(azureService) {
9
+ super("azure");
10
+ this.svc = azureService;
11
+ }
12
+
13
+ async listResources() {
14
+ const { vms } = this.svc.listVms();
15
+ const { clusters } = this.svc.listClusters();
16
+ return {
17
+ vms: vms.map((v) => ({ ...v, provider: "azure", type: "vm" })),
18
+ clusters: clusters.map((c) => ({ ...c, provider: "azure", type: "cluster" })),
19
+ };
20
+ }
21
+
22
+ async getResource(type, name) {
23
+ if (type === "vm") {
24
+ const detail = this.svc.getVmDetail(name);
25
+ if (!detail) return null;
26
+ const live = await this.svc.getVmLiveStatus(name);
27
+ return { ...detail, live, provider: "azure", type: "vm" };
28
+ }
29
+ if (type === "cluster") {
30
+ const detail = this.svc.getClusterDetail(name);
31
+ return detail ? { ...detail, provider: "azure", type: "cluster" } : null;
32
+ }
33
+ return null;
34
+ }
35
+
36
+ async createResource(type, opts) {
37
+ if (type === "vm") return this.svc.provisionVm(opts);
38
+ if (type === "cluster") return this.svc.provisionCluster(opts);
39
+ throw new Error(`Unknown resource type: ${type}`);
40
+ }
41
+
42
+ async performAction(type, name, action, opts = {}) {
43
+ if (type === "vm") {
44
+ switch (action) {
45
+ case "start": return this.svc.startVm(name);
46
+ case "stop": return this.svc.stopVm(name);
47
+ case "resize": return this.svc.resizeVm(name, opts);
48
+ case "grant-admin": return this.svc.grantAdmin(name, opts);
49
+ default: throw new Error(`Unknown VM action: ${action}`);
50
+ }
51
+ }
52
+ throw new Error(`Actions not supported for type: ${type}`);
53
+ }
54
+
55
+ async deleteResource(type, name) {
56
+ if (type === "vm") return this.svc.deleteVm(name);
57
+ if (type === "cluster") return this.svc.deleteCluster(name);
58
+ throw new Error(`Unknown resource type: ${type}`);
59
+ }
60
+
61
+ async getFleet() {
62
+ return this.svc.getFleet();
63
+ }
64
+
65
+ async syncResources() {
66
+ return this.svc.syncResources({ quiet: true });
67
+ }
68
+
69
+ async getCosts(opts = {}) {
70
+ return this.svc.getCosts(opts);
71
+ }
72
+
73
+ async healthCheck() {
74
+ try {
75
+ const { vms } = this.svc.listVms();
76
+ return { available: true, vmCount: vms.length };
77
+ } catch (e) {
78
+ return { available: false, error: e.message };
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * CloudProvider — base interface for cloud providers.
3
+ * Each provider (Azure, AWS, GCP) implements these methods.
4
+ */
5
+
6
+ export class CloudProvider {
7
+ constructor(name) {
8
+ this.name = name;
9
+ }
10
+
11
+ /** @returns {{ vms: object[], clusters: object[] }} */
12
+ async listResources() {
13
+ throw new Error("listResources() not implemented");
14
+ }
15
+
16
+ /** @returns {object|null} */
17
+ async getResource(type, name) {
18
+ throw new Error("getResource() not implemented");
19
+ }
20
+
21
+ /** @returns {object} */
22
+ async createResource(type, opts) {
23
+ throw new Error("createResource() not implemented");
24
+ }
25
+
26
+ /** @returns {object} */
27
+ async performAction(type, name, action, opts) {
28
+ throw new Error("performAction() not implemented");
29
+ }
30
+
31
+ /** @returns {object} */
32
+ async deleteResource(type, name) {
33
+ throw new Error("deleteResource() not implemented");
34
+ }
35
+
36
+ /** @returns {object} */
37
+ async getFleet() {
38
+ throw new Error("getFleet() not implemented");
39
+ }
40
+
41
+ /** @returns {object} */
42
+ async syncResources() {
43
+ throw new Error("syncResources() not implemented");
44
+ }
45
+
46
+ /** @returns {{ available: boolean, error?: string }} */
47
+ async healthCheck() {
48
+ return { available: false, error: "not implemented" };
49
+ }
50
+ }
@@ -0,0 +1,15 @@
1
+ <svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
3
+ <path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
4
+ <defs>
5
+ <linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
6
+ <stop stop-color="#41D1FF"/>
7
+ <stop offset="1" stop-color="#BD34FE"/>
8
+ </linearGradient>
9
+ <linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
10
+ <stop stop-color="#FFEA83"/>
11
+ <stop offset="0.0833333" stop-color="#FFDD35"/>
12
+ <stop offset="1" stop-color="#FFA800"/>
13
+ </linearGradient>
14
+ </defs>
15
+ </svg>