@meshxdata/fops 0.1.37 → 0.1.38

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.

Potentially problematic release.


This version of @meshxdata/fops might be problematic. Click here for more details.

Files changed (46) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/package.json +1 -1
  3. package/src/agent/llm.js +2 -0
  4. package/src/auth/azure.js +92 -0
  5. package/src/auth/cloudflare.js +125 -0
  6. package/src/auth/index.js +2 -0
  7. package/src/commands/index.js +8 -4
  8. package/src/commands/lifecycle.js +31 -10
  9. package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks.js +130 -2
  10. package/src/plugins/bundled/fops-plugin-azure/lib/azure-auth.js +47 -4
  11. package/src/plugins/bundled/fops-plugin-azure/lib/azure-ops.js +24 -25
  12. package/src/plugins/bundled/fops-plugin-azure/lib/azure-provision.js +66 -26
  13. package/src/plugins/bundled/fops-plugin-azure/lib/azure-shared-cache.js +1 -1
  14. package/src/plugins/bundled/fops-plugin-azure/lib/azure-sync.js +4 -4
  15. package/src/plugins/bundled/fops-plugin-azure/lib/commands/infra-cmds.js +4 -0
  16. package/src/plugins/bundled/fops-plugin-azure/lib/commands/vm-cmds.js +4 -3
  17. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/dai-backend.yaml +13 -0
  18. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/dai-frontend.yaml +13 -0
  19. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-backend.yaml +13 -0
  20. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-frontend.yaml +13 -0
  21. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-hive.yaml +13 -0
  22. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-kafka.yaml +13 -0
  23. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-meltano.yaml +13 -0
  24. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-mlflow.yaml +13 -0
  25. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-opa.yaml +13 -0
  26. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-processor.yaml +13 -0
  27. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-scheduler.yaml +13 -0
  28. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-storage-engine.yaml +13 -0
  29. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-trino.yaml +13 -0
  30. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-watcher.yaml +13 -0
  31. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/config/repository.yaml +66 -0
  32. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/kustomization.yaml +30 -0
  33. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/acr-webhook-controller.yaml +63 -0
  34. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/externalsecrets.yaml +15 -0
  35. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/istio.yaml +42 -0
  36. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kafka.yaml +15 -0
  37. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kube-reflector.yaml +33 -0
  38. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kubecost.yaml +12 -0
  39. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/nats-server.yaml +15 -0
  40. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/prometheus-agent.yaml +34 -0
  41. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/reloader.yaml +12 -0
  42. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/spark.yaml +112 -0
  43. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/tailscale.yaml +67 -0
  44. package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/vertical-pod-autoscaler.yaml +15 -0
  45. package/src/plugins/bundled/fops-plugin-foundation/index.js +26 -6
  46. package/src/plugins/loader.js +23 -6
@@ -0,0 +1,63 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: acr-webhook-controller
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/acr-webhook-controller
12
+ prune: false
13
+ patches:
14
+ - target:
15
+ kind: Secret
16
+ name: acr-pull-secret
17
+ namespace: acr-cache-system
18
+ patch: |-
19
+ - op: replace
20
+ path: /stringData/.dockerconfigjson
21
+ value: |
22
+ {
23
+ "auths": {
24
+ "meshxregistry.azurecr.io": {
25
+ "username": "{{ACR_USERNAME}}",
26
+ "password": "{{ACR_PASSWORD}}"
27
+ }
28
+ }
29
+ }
30
+ - target:
31
+ kind: Secret
32
+ name: meshxregistry-helm-secret
33
+ namespace: acr-cache-system
34
+ patch: |-
35
+ - op: replace
36
+ path: /stringData/username
37
+ value: "{{ACR_USERNAME}}"
38
+ - op: replace
39
+ path: /stringData/password
40
+ value: "{{ACR_PASSWORD}}"
41
+ - target:
42
+ kind: Deployment
43
+ name: acr-cache-webhook
44
+ namespace: acr-cache-system
45
+ patch: |
46
+ apiVersion: apps/v1
47
+ kind: Deployment
48
+ metadata:
49
+ name: acr-cache-webhook
50
+ spec:
51
+ replicas: 3
52
+ template:
53
+ spec:
54
+ affinity:
55
+ podAntiAffinity:
56
+ requiredDuringSchedulingIgnoredDuringExecution:
57
+ - labelSelector:
58
+ matchExpressions:
59
+ - key: app
60
+ operator: In
61
+ values:
62
+ - acr-cache-webhook
63
+ topologyKey: kubernetes.io/hostname
@@ -0,0 +1,15 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: external-secrets
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1h
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/external-secrets/manifests
12
+ prune: false
13
+ dependsOn:
14
+ - name: acr-webhook-controller
15
+ namespace: flux-system
@@ -0,0 +1,42 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: istio-operator
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1h
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/istio-operator
12
+ prune: false
13
+ ---
14
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
15
+ kind: Kustomization
16
+ metadata:
17
+ name: istio-controlplane
18
+ namespace: flux-system
19
+ spec:
20
+ dependsOn:
21
+ - name: istio-operator
22
+ interval: 10s
23
+ sourceRef:
24
+ kind: GitRepository
25
+ name: flux-system
26
+ path: ./infrastructure/istio-tenant/demo/azure/uaenorth
27
+ prune: false
28
+ ---
29
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
30
+ kind: Kustomization
31
+ metadata:
32
+ name: jaeger
33
+ namespace: flux-system
34
+ spec:
35
+ dependsOn:
36
+ - name: istio-operator
37
+ interval: 10m0s
38
+ sourceRef:
39
+ kind: GitRepository
40
+ name: flux-system
41
+ path: ./infrastructure/jaeger
42
+ prune: false
@@ -0,0 +1,15 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: kafka-operator
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/kafka-operator/velora
12
+ prune: false
13
+ dependsOn:
14
+ - name: acr-webhook-controller
15
+ namespace: flux-system
@@ -0,0 +1,33 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: kube-reflector
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/reflector
12
+ prune: false
13
+ targetNamespace: reflector
14
+ dependsOn:
15
+ - name: acr-webhook-controller
16
+ namespace: flux-system
17
+ patches:
18
+ - target:
19
+ kind: Secret
20
+ name: acr-pull-secret
21
+ namespace: reflector
22
+ patch: |-
23
+ - op: replace
24
+ path: /stringData/.dockerconfigjson
25
+ value: |
26
+ {
27
+ "auths": {
28
+ "meshxregistry.azurecr.io": {
29
+ "username": "{{ACR_USERNAME}}",
30
+ "password": "{{ACR_PASSWORD}}"
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,12 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: kubecost
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/kubecost/{{CLUSTER_NAME}}
12
+ prune: false
@@ -0,0 +1,15 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: nats-server
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/nats-server/velora
12
+ prune: false
13
+ dependsOn:
14
+ - name: acr-webhook-controller
15
+ namespace: flux-system
@@ -0,0 +1,34 @@
1
+ ---
2
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
3
+ kind: Kustomization
4
+ metadata:
5
+ name: prometheus-agent
6
+ namespace: flux-system
7
+ spec:
8
+ interval: 1m
9
+ sourceRef:
10
+ kind: GitRepository
11
+ name: flux-system
12
+ path: ./infrastructure/prometheus-agent
13
+ prune: false
14
+ patches:
15
+ - target:
16
+ kind: ConfigMap
17
+ name: prometheus-agent-server
18
+ patch: |
19
+ apiVersion: v1
20
+ kind: ConfigMap
21
+ metadata:
22
+ name: prometheus-agent-server
23
+ namespace: monitoring
24
+ data:
25
+ prometheus.yml: |
26
+ global:
27
+ scrape_interval: 15s
28
+ evaluation_interval: 30s
29
+ scrape_configs:
30
+ - job_name: "{{CLUSTER_NAME}}-nats-exporter"
31
+ static_configs:
32
+ - targets: ["nats.nats.svc.cluster.local:7777"]
33
+ remote_write:
34
+ - url: "http://central-live-prometheus.privatelink.uaenorth.azmk8s.io/api/v1/write"
@@ -0,0 +1,12 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: reloader
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/reloader
12
+ prune: false
@@ -0,0 +1,112 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: spark
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/spark-operator/launchpad
12
+ prune: false
13
+ dependsOn:
14
+ - name: velora
15
+ namespace: flux-system
16
+ - name: acr-webhook-controller
17
+ namespace: flux-system
18
+ patches:
19
+ - target:
20
+ kind: HelmRelease
21
+ name: spark-operator
22
+ namespace: flux-system
23
+ patch: |-
24
+ apiVersion: helm.toolkit.fluxcd.io/v2
25
+ kind: HelmRelease
26
+ metadata:
27
+ name: spark-operator
28
+ namespace: flux-system
29
+ spec:
30
+ values:
31
+ image:
32
+ pullSecrets:
33
+ - name: ecr-credentials
34
+ spark:
35
+ jobNamespaces:
36
+ - velora
37
+ ---
38
+ apiVersion: rbac.authorization.k8s.io/v1
39
+ kind: ClusterRole
40
+ metadata:
41
+ name: job-creator
42
+ namespace: velora
43
+ rules:
44
+ - apiGroups: [""]
45
+ resources: ["configmaps"]
46
+ verbs: ["create", "delete", "get"]
47
+ - apiGroups: [""]
48
+ resources: ["pods"]
49
+ verbs: ["get"]
50
+ - apiGroups: [""]
51
+ resources: ["pods/log"]
52
+ verbs: ["get"]
53
+ - apiGroups: [""]
54
+ resources: ["pods/status"]
55
+ verbs: ["get", "list", "watch"]
56
+ - apiGroups: ["sparkoperator.k8s.io"]
57
+ resources: ["sparkapplications"]
58
+ verbs: ["create", "delete", "get"]
59
+ - apiGroups: ["sparkoperator.k8s.io"]
60
+ resources: ["scheduledsparkapplications"]
61
+ verbs: ["create", "delete", "get", "patch"]
62
+ - apiGroups: [""]
63
+ resources: ["secrets"]
64
+ verbs: ["create", "delete", "get", "patch", "update"]
65
+ ---
66
+ apiVersion: rbac.authorization.k8s.io/v1
67
+ kind: RoleBinding
68
+ metadata:
69
+ name: create-jobs
70
+ namespace: velora
71
+ subjects:
72
+ - kind: Group
73
+ name: system:serviceaccounts:foundation
74
+ apiGroup: rbac.authorization.k8s.io
75
+ roleRef:
76
+ kind: ClusterRole
77
+ name: job-creator
78
+ apiGroup: rbac.authorization.k8s.io
79
+ ---
80
+ apiVersion: rbac.authorization.k8s.io/v1
81
+ kind: RoleBinding
82
+ metadata:
83
+ name: spark
84
+ namespace: velora
85
+ subjects:
86
+ - kind: Group
87
+ name: system:serviceaccounts:foundation
88
+ roleRef:
89
+ kind: ClusterRole
90
+ name: job-creator
91
+ apiGroup: rbac.authorization.k8s.io
92
+ ---
93
+ apiVersion: rbac.authorization.k8s.io/v1
94
+ kind: RoleBinding
95
+ metadata:
96
+ name: allow-velora-sas-in-spark-jobs
97
+ namespace: spark-jobs
98
+ subjects:
99
+ - kind: Group
100
+ name: system:serviceaccounts:velora
101
+ apiGroup: rbac.authorization.k8s.io
102
+ roleRef:
103
+ kind: ClusterRole
104
+ name: job-creator
105
+ apiGroup: rbac.authorization.k8s.io
106
+ ---
107
+ apiVersion: v1
108
+ kind: Namespace
109
+ metadata:
110
+ name: spark-jobs
111
+ labels:
112
+ name: spark-jobs
@@ -0,0 +1,67 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: tailscale-operator
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/tailscale/operator
12
+ prune: false
13
+ dependsOn:
14
+ - name: acr-webhook-controller
15
+ namespace: flux-system
16
+ patches:
17
+ - target:
18
+ kind: HelmRelease
19
+ name: tailscale-operator
20
+ namespace: flux-system
21
+ patch: |-
22
+ apiVersion: helm.toolkit.fluxcd.io/v2
23
+ kind: HelmRelease
24
+ metadata:
25
+ name: tailscale-operator
26
+ namespace: flux-system
27
+ spec:
28
+ values:
29
+ oauth:
30
+ clientId: "{{TAILSCALE_CLIENT_ID}}"
31
+ clientSecret: "{{TAILSCALE_CLIENT_SECRET}}"
32
+ operatorConfig:
33
+ hostname: "{{CLUSTER_NAME}}"
34
+ ---
35
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
36
+ kind: Kustomization
37
+ metadata:
38
+ name: tailscale-connector
39
+ namespace: flux-system
40
+ spec:
41
+ interval: 1m0s
42
+ sourceRef:
43
+ kind: GitRepository
44
+ name: flux-system
45
+ path: ./infrastructure/tailscale/connector
46
+ prune: false
47
+ dependsOn:
48
+ - name: tailscale-operator
49
+ patches:
50
+ - target:
51
+ kind: Connector
52
+ name: default-connector
53
+ namespace: tailscale
54
+ patch: |-
55
+ - op: replace
56
+ path: /metadata/name
57
+ value: "{{CLUSTER_NAME}}"
58
+ - op: replace
59
+ path: /spec/hostname
60
+ value: "{{CLUSTER_NAME}}"
61
+ - op: replace
62
+ path: /spec/exitNode
63
+ value: true
64
+ - op: replace
65
+ path: /spec/subnetRouter/advertiseRoutes
66
+ value:
67
+ - 10.50.0.0/16
@@ -0,0 +1,15 @@
1
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
2
+ kind: Kustomization
3
+ metadata:
4
+ name: vertical-pod-autoscaler
5
+ namespace: flux-system
6
+ spec:
7
+ interval: 1m0s
8
+ sourceRef:
9
+ kind: GitRepository
10
+ name: flux-system
11
+ path: ./infrastructure/vertical-pod-autoscaler/overlays/{{OVERLAY}}
12
+ prune: false
13
+ dependsOn:
14
+ - name: acr-webhook-controller
15
+ namespace: flux-system
@@ -995,14 +995,22 @@ app.run()
995
995
 
996
996
  let apiUrl = opts.url || process.env.FOPS_API_URL || "http://127.0.0.1:9001";
997
997
 
998
- // Current fops version
998
+ // Current fops version — resolve from the running binary's location
999
999
  let fopsVersion = "0.0.0";
1000
1000
  try {
1001
- const pluginsNodeModules2 = join(homedir(), ".fops", "plugins", "node_modules");
1002
- const fopsRoot2 = dirname(realpathSync(pluginsNodeModules2));
1001
+ // process.argv[1] is the path to fops.mjs; package.json sits next to it
1002
+ const fopsRoot2 = dirname(realpathSync(process.argv[1]));
1003
1003
  const pkgJson = JSON.parse(readFileSync(join(fopsRoot2, "package.json"), "utf8"));
1004
1004
  fopsVersion = pkgJson.version || "0.0.0";
1005
- } catch { /* fallback */ }
1005
+ } catch {
1006
+ // Fallback: try the plugins node_modules path
1007
+ try {
1008
+ const pluginsNodeModules2 = join(homedir(), ".fops", "plugins", "node_modules");
1009
+ const fopsRoot2 = dirname(realpathSync(pluginsNodeModules2));
1010
+ const pkgJson = JSON.parse(readFileSync(join(fopsRoot2, "package.json"), "utf8"));
1011
+ fopsVersion = pkgJson.version || "0.0.0";
1012
+ } catch { /* stay at 0.0.0 */ }
1013
+ }
1006
1014
 
1007
1015
  // Resolve icon
1008
1016
  let iconPath = "";
@@ -1449,6 +1457,8 @@ menu.addItem(NSMenuItem.separator())
1449
1457
  let updateItem = NSMenuItem(title: "", action: #selector(AppDelegate.runUpdate), keyEquivalent: "")
1450
1458
  updateItem.isHidden = true
1451
1459
  menu.addItem(updateItem)
1460
+ let checkUpdateItem = NSMenuItem(title: "Check for updates", action: #selector(AppDelegate.checkForUpdateManual), keyEquivalent: "")
1461
+ menu.addItem(checkUpdateItem)
1452
1462
 
1453
1463
  menu.addItem(NSMenuItem.separator())
1454
1464
  menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
@@ -1467,9 +1477,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
1467
1477
  refresh()
1468
1478
  let pollTimer = Timer(timeInterval: 8, repeats: true) { _ in self.refresh() }
1469
1479
  RunLoop.main.add(pollTimer, forMode: .common)
1470
- // Check for updates on launch, then every hour
1480
+ // Check for updates on launch, then every 15 minutes
1471
1481
  checkForUpdate()
1472
- let updateTimer = Timer(timeInterval: 3600, repeats: true) { _ in self.checkForUpdate() }
1482
+ let updateTimer = Timer(timeInterval: 900, repeats: true) { _ in self.checkForUpdate() }
1473
1483
  RunLoop.main.add(updateTimer, forMode: .common)
1474
1484
  }
1475
1485
 
@@ -1526,6 +1536,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
1526
1536
  RunLoop.main.add(pulse, forMode: .common)
1527
1537
  }
1528
1538
 
1539
+ @objc func checkForUpdateManual() {
1540
+ checkUpdateItem.title = "Checking…"
1541
+ checkUpdateItem.isEnabled = false
1542
+ checkForUpdate()
1543
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
1544
+ checkUpdateItem.title = "Check for updates"
1545
+ checkUpdateItem.isEnabled = true
1546
+ }
1547
+ }
1548
+
1529
1549
  @objc func runUpdate() {
1530
1550
  updateItem.title = "⬆ Updating…"
1531
1551
  updateItem.isEnabled = false
@@ -156,21 +156,38 @@ function parseFrontmatter(content) {
156
156
  // Alias for backward compatibility
157
157
  const parseSkillFrontmatter = parseFrontmatter;
158
158
 
159
+ /**
160
+ * Plugins that are disabled by default and must be explicitly enabled via
161
+ * `fops plugin enable <id>` or by setting `enabled: true` in ~/.fops.json.
162
+ */
163
+ const DEFAULT_DISABLED = new Set([
164
+ "fops-plugin-teleport",
165
+ "coda",
166
+ "fops-plugin-github-roles",
167
+ "fops-plugin-ecr",
168
+ "fops-plugin-vm-users",
169
+ "fops-plugin-aws",
170
+ "fops-plugin-dai-ttyd",
171
+ ]);
172
+
159
173
  /**
160
174
  * Check if a plugin is enabled in ~/.fops.json.
161
- * Default: enabled unless explicitly set to false.
175
+ * Plugins in DEFAULT_DISABLED are off unless explicitly enabled.
176
+ * All other plugins are on unless explicitly disabled.
162
177
  */
163
178
  function isPluginEnabled(pluginId) {
164
179
  try {
165
180
  const fopsConfig = path.join(os.homedir(), ".fops.json");
166
- if (!fs.existsSync(fopsConfig)) return true;
167
- const raw = JSON.parse(fs.readFileSync(fopsConfig, "utf8"));
168
- const entry = raw?.plugins?.entries?.[pluginId];
169
- if (entry && entry.enabled === false) return false;
181
+ if (fs.existsSync(fopsConfig)) {
182
+ const raw = JSON.parse(fs.readFileSync(fopsConfig, "utf8"));
183
+ const entry = raw?.plugins?.entries?.[pluginId];
184
+ if (entry?.enabled === false) return false;
185
+ if (entry?.enabled === true) return true;
186
+ }
170
187
  } catch {
171
188
  // ignore parse errors
172
189
  }
173
- return true;
190
+ return !DEFAULT_DISABLED.has(pluginId);
174
191
  }
175
192
 
176
193
  /**