@meshxdata/fops 0.1.40 → 0.1.42
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 +5 -190
- package/package.json +1 -1
- package/src/agent/llm.js +0 -2
- package/src/doctor.js +21 -93
- package/src/plugins/bundled/fops-plugin-1password/index.js +1 -13
- package/src/plugins/bundled/fops-plugin-azure/index.js +2 -4
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-aks.js +2 -130
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-auth.js +39 -71
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-helpers.js +2 -64
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-ops.js +28 -36
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-shared-cache.js +1 -1
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-sync.js +4 -4
- package/src/plugins/bundled/fops-plugin-azure/lib/azure-vm-lifecycle.js +10 -3
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/infra-cmds.js +0 -4
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/test-cmds.js +10 -31
- package/src/plugins/bundled/fops-plugin-azure/lib/commands/vm-cmds.js +30 -0
- package/src/plugins/bundled/fops-plugin-foundation/index.js +1 -18
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/dai-backend.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/dai-frontend.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-backend.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-frontend.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-hive.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-kafka.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-meltano.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-mlflow.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-opa.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-processor.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-scheduler.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-storage-engine.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-trino.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/apps/foundation-watcher.yaml +0 -13
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/config/repository.yaml +0 -66
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/kustomization.yaml +0 -30
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/acr-webhook-controller.yaml +0 -63
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/externalsecrets.yaml +0 -15
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/istio.yaml +0 -42
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kafka.yaml +0 -15
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kube-reflector.yaml +0 -33
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/kubecost.yaml +0 -12
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/nats-server.yaml +0 -15
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/prometheus-agent.yaml +0 -34
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/reloader.yaml +0 -12
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/spark.yaml +0 -112
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/tailscale.yaml +0 -67
- package/src/plugins/bundled/fops-plugin-azure/templates/cluster/operator/vertical-pod-autoscaler.yaml +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,194 +1,11 @@
|
|
|
1
|
-
## [0.1.40] - 2026-03-10
|
|
2
|
-
|
|
3
|
-
- callback url for localhost (821fb94)
|
|
4
|
-
- disable 4 scaffolding plugin by default. (bfb2b76)
|
|
5
|
-
- jaccard improvements (b7494a0)
|
|
6
|
-
- refactor azure plugin (68dfef4)
|
|
7
|
-
- refactor azure plugin (b24a008)
|
|
8
|
-
- fix trino catalog missing (4928a55)
|
|
9
|
-
- v36 bump and changelog generation on openai (37a0440)
|
|
10
|
-
- v36 bump and changelog generation on openai (a3b02d9)
|
|
11
|
-
- bump (a990058)
|
|
12
|
-
- status bar fix and new plugin for ttyd (27dde1e)
|
|
13
|
-
- file demo and tray (1a3e704)
|
|
14
|
-
- electron app (59ad0bb)
|
|
15
|
-
- compose and fops file plugin (1cf0e81)
|
|
16
|
-
- bump (346ffc1)
|
|
17
|
-
- localhost replaced by 127.0.0.1 (82b9f30)
|
|
18
|
-
- .29 (587b0e1)
|
|
19
|
-
- improve up down and bootstrap script (b79ebaf)
|
|
20
|
-
- checksum (22c8086)
|
|
21
|
-
- checksum (96b434f)
|
|
22
|
-
- checksum (15ed3c0)
|
|
23
|
-
- checksum (8a6543a)
|
|
24
|
-
- bump embed trino linksg (8440504)
|
|
25
|
-
- bump data (765ffd9)
|
|
26
|
-
- bump (cb8b232)
|
|
27
|
-
- broken tests (c532229)
|
|
28
|
-
- release 0.1.18, preflight checks (d902249)
|
|
29
|
-
- fix compute display bug (d10f5d9)
|
|
30
|
-
- cleanup packer files (6330f18)
|
|
31
|
-
- plan mode (cb36a8a)
|
|
32
|
-
- bump to 0.1.16 - agent ui (41ac1a2)
|
|
33
|
-
- bump to 0.1.15 - agent ui (4ebe2e1)
|
|
34
|
-
- bump to 0.1.14 (6c3a7fa)
|
|
35
|
-
- bump to 0.1.13 (8db570f)
|
|
36
|
-
- release 0.1.12 (c1c79e5)
|
|
37
|
-
- bump (11aa3b0)
|
|
38
|
-
- git keep and bump tui (be1678e)
|
|
39
|
-
- skills, index, rrf, compacted context (100k > 10k) (7b2fffd)
|
|
40
|
-
- cloudflare and token consumption, graphs indexing (0ad9eec)
|
|
41
|
-
- bump storage default (22c83ba)
|
|
42
|
-
- storage fix (68a22a0)
|
|
43
|
-
- skills update (7f56500)
|
|
44
|
-
- v9 bump (3864446)
|
|
45
|
-
- bump (c95eedc)
|
|
46
|
-
- rrf (dbf8c95)
|
|
47
|
-
- feat: warning when running predictions (95e8c52)
|
|
48
|
-
- feat: support for local predictions (45cf26b)
|
|
49
|
-
- feat: wip support for predictions + mlflow (3457052)
|
|
50
|
-
- add Reciprocal Rank Fusion (RRF) to knowledge and skill retrieval (61549bc)
|
|
51
|
-
- validate CSV headers in compute_run readiness check (a8c7a43)
|
|
52
|
-
- fix corrupted Iceberg metadata: probe tables + force cleanup on re-apply (50578af)
|
|
53
|
-
- enforce: never use foundation_apply to fix broken products (2e049bf)
|
|
54
|
-
- update SKILL.md with complete tool reference for knowledge retrieval (30b1924)
|
|
55
|
-
- add storage read, input DP table probe, and compute_run improvements (34e6c4c)
|
|
56
|
-
- skills update (1220385)
|
|
57
|
-
- skills update (bb66958)
|
|
58
|
-
- some tui improvement andd tools apply overwrite (e90c35c)
|
|
59
|
-
- skills update (e9227a1)
|
|
60
|
-
- skills update (669c4b3)
|
|
61
|
-
- fix plugin pre-flight checks (f741743)
|
|
62
|
-
- increase agent context (6479aaa)
|
|
63
|
-
- skills and init sql fixes (5fce35e)
|
|
64
|
-
- checksum (3518b56)
|
|
65
|
-
- penging job limit (a139861)
|
|
66
|
-
- checksum (575d28c)
|
|
67
|
-
- bump (92049ba)
|
|
68
|
-
- fix bug per tab status (0a33657)
|
|
69
|
-
- fix bug per tab status (50457c6)
|
|
70
|
-
- checksumming (0ad842e)
|
|
71
|
-
- shot af mardkwon overlapping (51f63b9)
|
|
72
|
-
- add spark dockerfile for multiarch builds (95abbd1)
|
|
73
|
-
- fix plugin initialization (16b9782)
|
|
74
|
-
- split index.js (50902a2)
|
|
75
|
-
- cloudflare cidr (cc4e021)
|
|
76
|
-
- cloduflare restrictions (2f6ba2d)
|
|
77
|
-
- sequential start (86b496e)
|
|
78
|
-
- sequential start (4930fe1)
|
|
79
|
-
- sequential start (353f014)
|
|
80
|
-
- qa tests (2dc6a1a)
|
|
81
|
-
- bump sha for .85 (dc2edfe)
|
|
82
|
-
- preserve env on sudo (7831227)
|
|
83
|
-
- bump sha for .84 (6c052f9)
|
|
84
|
-
- non interactive for azure vms (0aa8a2f)
|
|
85
|
-
- keep .env if present (d072450)
|
|
86
|
-
- bump (7a8e732)
|
|
87
|
-
- ensure opa is on compose if not set (f4a5228)
|
|
88
|
-
- checksum bump (a2ccc20)
|
|
89
|
-
- netrc defensive checks (a0b0ccc)
|
|
90
|
-
- netrc defensive checks (ae37403)
|
|
91
|
-
- checksum (ec45d11)
|
|
92
|
-
- update sync and fix up (7f9af72)
|
|
93
|
-
- expand test for azure and add new per app tag support (388a168)
|
|
94
|
-
- checksum on update (44005fc)
|
|
95
|
-
- cleanup for later (15e5313)
|
|
96
|
-
- cleanup for later (11c9597)
|
|
97
|
-
- switch branch feature (822fecc)
|
|
98
|
-
- add pull (d1c19ab)
|
|
99
|
-
- Bump hono from 4.11.9 to 4.12.0 in /operator-cli (ad25144)
|
|
100
|
-
- tests (f180a9a)
|
|
101
|
-
- cleanup (39c49a3)
|
|
102
|
-
- registry (7b7126a)
|
|
103
|
-
- reconcile kafka (832d0db)
|
|
104
|
-
- gh login bug (025886c)
|
|
105
|
-
- cleanup (bb96cab)
|
|
106
|
-
- strip envs from process (2421180)
|
|
107
|
-
- force use of gh creds not tokens in envs var (fff7787)
|
|
108
|
-
- resolve import between npm installs and npm link (79522e1)
|
|
109
|
-
- fix gh scope and azure states (afd846c)
|
|
110
|
-
- refactoring (da50352)
|
|
111
|
-
- split fops repo (d447638)
|
|
112
|
-
- aks (b791f8f)
|
|
113
|
-
- refactor azure (67d3bad)
|
|
114
|
-
- wildcard (391f023)
|
|
115
|
-
- azure plugin (c074074)
|
|
116
|
-
- zap (d7e6e7f)
|
|
117
|
-
- fix knock (cf89c05)
|
|
118
|
-
- azure (4adec98)
|
|
119
|
-
- Bump tar from 7.5.7 to 7.5.9 in /operator-cli (e41e98e)
|
|
120
|
-
- azure stack index.js split (de12272)
|
|
121
|
-
- Bump ajv from 8.17.1 to 8.18.0 in /operator-cli (76da21f)
|
|
122
|
-
- packer (9665fbc)
|
|
123
|
-
- remove stack api (db0fd4d)
|
|
124
|
-
- packer cleanup (fe1bf14)
|
|
125
|
-
- force refresh token (3a3d7e2)
|
|
126
|
-
- provision shell (2ad505f)
|
|
127
|
-
- azure vm management (91dcb31)
|
|
128
|
-
- azure specific (2b0cca8)
|
|
129
|
-
- azure packer (12175b8)
|
|
130
|
-
- init hashed pwd (db8523c)
|
|
131
|
-
- packer (5b5c7c4)
|
|
132
|
-
- doctor for azure vm (ed524fa)
|
|
133
|
-
- packer and 1pwd (c6d053e)
|
|
134
|
-
- split big index.js (dc85a1b)
|
|
135
|
-
- kafka volume update (21815ec)
|
|
136
|
-
- fix openai azure tools confirmation and flow (0118cd1)
|
|
137
|
-
- nighly fixx, test fix (5e0d04f)
|
|
138
|
-
- open ai training (cdc494a)
|
|
139
|
-
- openai integration in azure (1ca1475)
|
|
140
|
-
- ci (672cea9)
|
|
141
|
-
- refresh ghcr creds (4220c48)
|
|
142
|
-
- cleaned up version (1a0074f)
|
|
143
|
-
- traefik on ghcr and templates (8e31a05)
|
|
144
|
-
- apply fcl (e78911f)
|
|
145
|
-
- demo landscape (dd205fe)
|
|
146
|
-
- smarter login and schema (1af514f)
|
|
147
|
-
- no down before up unless something broke (56b1132)
|
|
148
|
-
- dai, reconcile failed containers (12907fa)
|
|
149
|
-
- reconcile dead container (7da75e4)
|
|
150
|
-
- defensive around storage buckets dir (b98871d)
|
|
151
|
-
- defensive around storage buckets dir (e86e132)
|
|
152
|
-
- gear in for multiarch (bf3fa3e)
|
|
153
|
-
- up autofix (99c7f89)
|
|
154
|
-
- autofix stale containers on up (43c7d0f)
|
|
155
|
-
- shared sessions fix (5de1359)
|
|
156
|
-
- share sessions between ui and tui (8321391)
|
|
157
|
-
- fix chat view display details (e263996)
|
|
158
|
-
- fix chat view display details (9babdda)
|
|
159
|
-
- tui up fixes (86e9f17)
|
|
160
|
-
- fix commands init (442538b)
|
|
161
|
-
- enable k3s profile (b2dcfc8)
|
|
162
|
-
- test up till job creation (656d388)
|
|
163
|
-
- tui fixes (0599779)
|
|
164
|
-
- cleanup (27731f0)
|
|
165
|
-
- train (90bf559)
|
|
166
|
-
- training (f809bf6)
|
|
167
|
-
- training (ba2b836)
|
|
168
|
-
- training (6fc5267)
|
|
169
|
-
- training (4af8ac9)
|
|
170
|
-
- fix build script (bd82836)
|
|
171
|
-
- infra test (5b79815)
|
|
172
|
-
- infra test (3a0ac05)
|
|
173
|
-
- infra test (e5c67b5)
|
|
174
|
-
- tests (ae7b621)
|
|
175
|
-
- tests (c09ae6a)
|
|
176
|
-
- update tui (4784153)
|
|
177
|
-
- training (0a5a330)
|
|
178
|
-
- tui (df4dd4a)
|
|
179
|
-
- pkg builds (4dc9993)
|
|
180
|
-
- also source env for creds (9a17d8f)
|
|
181
|
-
- fcl support (e8a5743)
|
|
182
|
-
- fcl support (8d6b6cd)
|
|
183
|
-
- fcl support (cb76a4a)
|
|
184
|
-
- bump package (df2ee85)
|
|
185
|
-
|
|
186
1
|
# Changelog
|
|
187
2
|
|
|
188
3
|
All notable changes to @meshxdata/fops (Foundation Operator CLI) are documented here.
|
|
189
4
|
|
|
190
|
-
## [0.1.
|
|
5
|
+
## [0.1.42] - 2026-03-11
|
|
191
6
|
|
|
7
|
+
- Mlflow and azure plugin fix (176881f)
|
|
8
|
+
- fix lifecycle (a2cb9e7)
|
|
192
9
|
- callback url for localhost (821fb94)
|
|
193
10
|
- disable 4 scaffolding plugin by default. (bfb2b76)
|
|
194
11
|
- jaccard improvements (b7494a0)
|
|
@@ -369,11 +186,10 @@ All notable changes to @meshxdata/fops (Foundation Operator CLI) are documented
|
|
|
369
186
|
- also source env for creds (9a17d8f)
|
|
370
187
|
- fcl support (e8a5743)
|
|
371
188
|
- fcl support (8d6b6cd)
|
|
372
|
-
- fcl support (cb76a4a)
|
|
373
|
-
- bump package (df2ee85)
|
|
374
189
|
|
|
375
|
-
## [0.1.
|
|
190
|
+
## [0.1.41] - 2026-03-11
|
|
376
191
|
|
|
192
|
+
- fix lifecycle (a2cb9e7)
|
|
377
193
|
- callback url for localhost (821fb94)
|
|
378
194
|
- disable 4 scaffolding plugin by default. (bfb2b76)
|
|
379
195
|
- jaccard improvements (b7494a0)
|
|
@@ -555,7 +371,6 @@ All notable changes to @meshxdata/fops (Foundation Operator CLI) are documented
|
|
|
555
371
|
- fcl support (e8a5743)
|
|
556
372
|
- fcl support (8d6b6cd)
|
|
557
373
|
- fcl support (cb76a4a)
|
|
558
|
-
- bump package (df2ee85)
|
|
559
374
|
|
|
560
375
|
# Changelog
|
|
561
376
|
|
package/package.json
CHANGED
package/src/agent/llm.js
CHANGED
|
@@ -309,8 +309,6 @@ let _responsesApiFallback = false;
|
|
|
309
309
|
function isResponsesApiFormatError(err) {
|
|
310
310
|
if (!err) return false;
|
|
311
311
|
const status = err.status ?? err.error?.status ?? err.code;
|
|
312
|
-
// 404 = Responses API route doesn't exist on this Azure endpoint/api-version
|
|
313
|
-
if (status === 404 || status === "404") return true;
|
|
314
312
|
if (status !== 400 && status !== "400") return false;
|
|
315
313
|
const msg = [err.message, err.error?.message, err.body?.error?.message].filter(Boolean).join(" ");
|
|
316
314
|
return /Responses API|moved to ['"]input['"]|unknown parameter|'input' is required|'instructions' is not/i.test(msg);
|
package/src/doctor.js
CHANGED
|
@@ -11,20 +11,6 @@ import { rootDir } from "./project.js";
|
|
|
11
11
|
import { wslExec, wslHomedir, wslFileExists, wslReadFile, wslCmdVersion } from "./wsl.js";
|
|
12
12
|
import { getInquirer } from "./lazy.js";
|
|
13
13
|
|
|
14
|
-
// Check if sudo is available without a password (cached credentials or NOPASSWD)
|
|
15
|
-
let _sudoOk = null;
|
|
16
|
-
async function canSudo() {
|
|
17
|
-
if (_sudoOk !== null) return _sudoOk;
|
|
18
|
-
if (process.platform === "win32") { _sudoOk = false; return false; }
|
|
19
|
-
try {
|
|
20
|
-
const { exitCode } = await execa("sudo", ["-n", "true"], { reject: false, timeout: 5000 });
|
|
21
|
-
_sudoOk = exitCode === 0;
|
|
22
|
-
} catch { _sudoOk = false; }
|
|
23
|
-
return _sudoOk;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
14
|
const KEY_PORTS = {
|
|
29
15
|
5432: "Postgres",
|
|
30
16
|
9092: "Kafka",
|
|
@@ -260,7 +246,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
260
246
|
console.log(chalk.green(` ✓ Removed ${b}`));
|
|
261
247
|
} catch (err) {
|
|
262
248
|
if (err.code === "EACCES") {
|
|
263
|
-
if (!(await canSudo())) throw new Error(`Permission denied removing ${b} — sudo not available`);
|
|
264
249
|
console.log(chalk.cyan(` ▶ sudo rm ${b}`));
|
|
265
250
|
await execa("sudo", ["rm", b], { stdio: "inherit", timeout: 10000 });
|
|
266
251
|
} else {
|
|
@@ -394,7 +379,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
394
379
|
console.log(chalk.cyan(' ▶ start "" "Docker Desktop"'));
|
|
395
380
|
await execa("cmd", ["/c", "start", "", "Docker Desktop"], { timeout: 10000 });
|
|
396
381
|
} else {
|
|
397
|
-
if (!(await canSudo())) throw new Error("sudo not available — start Docker manually: sudo systemctl start docker");
|
|
398
382
|
console.log(chalk.cyan(" ▶ sudo systemctl start docker"));
|
|
399
383
|
await execa("sudo", ["systemctl", "start", "docker"], { stdio: "inherit", timeout: 30000 });
|
|
400
384
|
return;
|
|
@@ -463,7 +447,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
463
447
|
console.log(chalk.cyan(' ▶ start "" "Docker Desktop"'));
|
|
464
448
|
await execa("cmd", ["/c", "start", "", "Docker Desktop"], { timeout: 10000 });
|
|
465
449
|
} else {
|
|
466
|
-
if (!(await canSudo())) throw new Error("sudo not available — install Docker manually: curl -fsSL https://get.docker.com | sudo sh");
|
|
467
450
|
console.log(chalk.cyan(" ▶ curl -fsSL https://get.docker.com | sudo sh"));
|
|
468
451
|
await execa("sh", ["-c", "curl -fsSL https://get.docker.com | sudo sh"], {
|
|
469
452
|
stdio: "inherit", timeout: 300_000,
|
|
@@ -514,7 +497,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
514
497
|
await execa("winget", ["install", "Git.Git", "--accept-source-agreements", "--accept-package-agreements"], { stdio: "inherit", timeout: 300_000 });
|
|
515
498
|
}
|
|
516
499
|
} else {
|
|
517
|
-
if (!(await canSudo())) throw new Error("sudo not available — install git manually");
|
|
518
500
|
console.log(chalk.cyan(" ▶ sudo apt-get install -y git"));
|
|
519
501
|
await execa("sudo", ["apt-get", "install", "-y", "git"], { stdio: "inherit", timeout: 300_000 });
|
|
520
502
|
}
|
|
@@ -558,7 +540,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
558
540
|
console.log(chalk.cyan(" ▶ brew install npm"));
|
|
559
541
|
await execa("brew", ["install", "npm"], { stdio: "inherit", timeout: 300_000 });
|
|
560
542
|
} else if (process.platform === "linux" || (process.platform === "win32" && useWsl)) {
|
|
561
|
-
if (!(await canSudo())) throw new Error("sudo not available — install npm manually");
|
|
562
543
|
console.log(chalk.cyan(" ▶ sudo apt-get install -y npm"));
|
|
563
544
|
await run("sudo", ["apt-get", "install", "-y", "npm"], { stdio: "inherit", timeout: 300_000 });
|
|
564
545
|
} else {
|
|
@@ -592,8 +573,9 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
592
573
|
await execa("brew", ["install", "--cask", "1password-cli"], { stdio: "inherit", timeout: 300_000 });
|
|
593
574
|
} else if (process.platform === "win32") {
|
|
594
575
|
if (useWsl) {
|
|
595
|
-
if (!(await canSudo())) throw new Error("sudo not available — install 1Password CLI manually");
|
|
596
576
|
console.log(chalk.cyan(" ▶ [WSL] Installing 1Password CLI via apt…"));
|
|
577
|
+
// Authenticate sudo upfront so the long install chain doesn't hang at a password prompt
|
|
578
|
+
await run("sudo", ["-v"], { stdio: "inherit", timeout: 30_000 });
|
|
597
579
|
await run("sh", ["-c", [
|
|
598
580
|
"curl -sS https://downloads.1password.com/linux/keys/1password.asc | sudo gpg --batch --yes --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg",
|
|
599
581
|
'&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | sudo tee /etc/apt/sources.list.d/1password.list',
|
|
@@ -609,8 +591,9 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
609
591
|
}
|
|
610
592
|
} else {
|
|
611
593
|
// Linux — install via 1Password apt repository
|
|
612
|
-
|
|
594
|
+
// Authenticate sudo upfront so the long install chain doesn't hang at a password prompt
|
|
613
595
|
console.log(chalk.cyan(" ▶ Installing 1Password CLI via apt…"));
|
|
596
|
+
await execa("sudo", ["-v"], { stdio: "inherit", timeout: 30_000 });
|
|
614
597
|
await execa("sh", ["-c", [
|
|
615
598
|
"curl -sS https://downloads.1password.com/linux/keys/1password.asc | sudo gpg --batch --yes --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg",
|
|
616
599
|
'&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | sudo tee /etc/apt/sources.list.d/1password.list',
|
|
@@ -624,7 +607,6 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
624
607
|
|
|
625
608
|
// GitHub CLI
|
|
626
609
|
const installGhLinux = async (runner = execa) => {
|
|
627
|
-
if (!(await canSudo())) throw new Error("sudo not available — install gh manually: https://cli.github.com");
|
|
628
610
|
console.log(chalk.cyan(" ▶ Installing gh via apt…"));
|
|
629
611
|
await runner("sh", ["-c", [
|
|
630
612
|
"type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)",
|
|
@@ -873,26 +855,7 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
873
855
|
console.log(chalk.green(" ✓ write:packages and repo scopes added"));
|
|
874
856
|
};
|
|
875
857
|
|
|
876
|
-
// Double-check: if hasWritePackages came from netrc fallback, verify the gh CLI token too
|
|
877
|
-
let ghCliHasPackages = hasWritePackages;
|
|
878
858
|
if (hasWritePackages) {
|
|
879
|
-
try {
|
|
880
|
-
const { stdout: tkOut, exitCode: tkExit } = await runGh(["auth", "token"], { timeout: 5000, reject: false });
|
|
881
|
-
if (tkExit === 0 && tkOut?.trim()) {
|
|
882
|
-
const { status, scopes } = await ghApiGetWithScopes(tkOut.trim());
|
|
883
|
-
if (status === 200 && scopes && !scopes.includes("write:packages") && !scopes.includes("read:packages")) {
|
|
884
|
-
ghCliHasPackages = false;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
} catch {}
|
|
888
|
-
}
|
|
889
|
-
if (hasWritePackages && !ghCliHasPackages) {
|
|
890
|
-
warn(
|
|
891
|
-
"gh CLI token missing packages scope",
|
|
892
|
-
"netrc token has it, but gh auth token does not — run: gh auth refresh -h github.com -s write:packages",
|
|
893
|
-
ghcrRefreshFn,
|
|
894
|
-
);
|
|
895
|
-
} else if (hasWritePackages) {
|
|
896
859
|
ok("gh token has write:packages scope");
|
|
897
860
|
} else {
|
|
898
861
|
fail(
|
|
@@ -936,63 +899,32 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
936
899
|
}
|
|
937
900
|
|
|
938
901
|
const ghcrFixFn = async () => {
|
|
939
|
-
//
|
|
940
|
-
|
|
902
|
+
// Ensure write:packages scope is present (implies read:packages)
|
|
903
|
+
if (!hasWritePackages) {
|
|
904
|
+
console.log(chalk.cyan(" ▶ gh auth refresh -h github.com -s write:packages -s repo"));
|
|
905
|
+
console.log(chalk.dim(" (gh CLI will ask for device code auth, then optionally Git credential setup)"));
|
|
906
|
+
await runGh(["auth", "refresh", "-h", "github.com", "-s", "write:packages", "-s", "repo"], {
|
|
907
|
+
stdio: "inherit", timeout: 120_000,
|
|
908
|
+
});
|
|
909
|
+
}
|
|
941
910
|
|
|
942
|
-
//
|
|
911
|
+
// Get fresh token and login to ghcr.io
|
|
912
|
+
let ghToken = null;
|
|
943
913
|
try {
|
|
944
914
|
const { stdout, exitCode } = await runGh(["auth", "token"], { timeout: 5000, reject: false });
|
|
945
|
-
if (exitCode === 0 && stdout?.trim())
|
|
946
|
-
const t = stdout.trim();
|
|
947
|
-
const { status, scopes } = await ghApiGetWithScopes(t);
|
|
948
|
-
if (status === 200 && scopes?.includes("write:packages")) {
|
|
949
|
-
ghToken = t;
|
|
950
|
-
} else if (status === 200) {
|
|
951
|
-
// gh token works but lacks packages scope — refresh it
|
|
952
|
-
console.log(chalk.yellow(" gh CLI token missing write:packages — refreshing…"));
|
|
953
|
-
console.log(chalk.cyan(" ▶ gh auth refresh -h github.com -s write:packages -s repo"));
|
|
954
|
-
const { exitCode: refExit } = await runGh(["auth", "refresh", "-h", "github.com", "-s", "write:packages", "-s", "repo"], {
|
|
955
|
-
stdio: "inherit", timeout: 120_000, reject: false,
|
|
956
|
-
});
|
|
957
|
-
if (refExit === 0) {
|
|
958
|
-
const { stdout: fresh } = await runGh(["auth", "token"], { timeout: 5000, reject: false });
|
|
959
|
-
if (fresh?.trim()) ghToken = fresh.trim();
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
915
|
+
if (exitCode === 0 && stdout?.trim()) ghToken = stdout.trim();
|
|
963
916
|
} catch {}
|
|
964
917
|
|
|
965
|
-
// 2) Fall back to netrc token if gh CLI token doesn't have the right scopes
|
|
966
|
-
if (!ghToken) {
|
|
967
|
-
try {
|
|
968
|
-
const content = await readFile(netrcPath);
|
|
969
|
-
const netrcToken = readNetrcToken(content, "github.com");
|
|
970
|
-
if (netrcToken) {
|
|
971
|
-
const { status, scopes } = await ghApiGetWithScopes(netrcToken);
|
|
972
|
-
if (status === 200 && (scopes?.includes("write:packages") || scopes?.includes("read:packages"))) {
|
|
973
|
-
console.log(chalk.cyan(" Using netrc token (has packages scope)"));
|
|
974
|
-
ghToken = netrcToken;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
} catch {}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
918
|
if (ghToken) {
|
|
981
|
-
console.log(chalk.cyan(" ▶ Logging into ghcr.io…"));
|
|
982
|
-
// Login for both current user and root (Docker daemon runs as root)
|
|
919
|
+
console.log(chalk.cyan(" ▶ Logging into ghcr.io using gh auth token…"));
|
|
983
920
|
await run("sh", ["-c", `echo ${ghToken} | docker login ghcr.io -u x-access-token --password-stdin`], {
|
|
984
921
|
stdio: "inherit", timeout: 15000,
|
|
985
922
|
});
|
|
986
|
-
try {
|
|
987
|
-
await execa("sh", ["-c", `echo ${ghToken} | sudo docker login ghcr.io -u x-access-token --password-stdin`], {
|
|
988
|
-
timeout: 15000, stdio: "pipe",
|
|
989
|
-
});
|
|
990
|
-
} catch { /* root login best-effort */ }
|
|
991
923
|
} else {
|
|
992
|
-
console.log(chalk.yellow("
|
|
993
|
-
console.log(chalk.dim("
|
|
994
|
-
console.log(chalk.dim("
|
|
995
|
-
throw new Error("
|
|
924
|
+
console.log(chalk.yellow(" gh CLI not authenticated — manual login required."));
|
|
925
|
+
console.log(chalk.dim(" Create a PAT at https://github.com/settings/tokens with write:packages scope, then run:"));
|
|
926
|
+
console.log(chalk.dim(" docker login ghcr.io -u <your-username>"));
|
|
927
|
+
throw new Error("Manual GHCR login required");
|
|
996
928
|
}
|
|
997
929
|
};
|
|
998
930
|
|
|
@@ -1016,11 +948,7 @@ export async function runDoctor(opts = {}, registry = null) {
|
|
|
1016
948
|
}
|
|
1017
949
|
}
|
|
1018
950
|
} else if (ghcrLoggedIn) {
|
|
1019
|
-
fail(
|
|
1020
|
-
"Docker logged into ghcr.io but pull access denied",
|
|
1021
|
-
"Docker has a stale token — fix: echo $(gh auth token) | docker login ghcr.io -u x-access-token --password-stdin",
|
|
1022
|
-
ghcrFixFn,
|
|
1023
|
-
);
|
|
951
|
+
fail("Docker logged into ghcr.io but pull access denied", "token may lack read:packages or write:packages scope", ghcrFixFn);
|
|
1024
952
|
} else {
|
|
1025
953
|
fail("Docker not logged into ghcr.io", "needed to pull/push private images", ghcrFixFn);
|
|
1026
954
|
}
|
|
@@ -99,10 +99,6 @@ export function register(api) {
|
|
|
99
99
|
} else if (synced > 0) {
|
|
100
100
|
const files = synced === 1 ? ".env" : `${synced} .env files`;
|
|
101
101
|
console.log(chalk.green(` ✓ ${totalSecrets} secret(s) synced → ${files}`));
|
|
102
|
-
// Update one-shot marker so auto-sync on next `fops up` is skipped
|
|
103
|
-
const markerDir = path.join(root, ".fops");
|
|
104
|
-
if (!fs.existsSync(markerDir)) fs.mkdirSync(markerDir, { recursive: true });
|
|
105
|
-
fs.writeFileSync(path.join(markerDir, ".1p-synced"), new Date().toISOString() + "\n");
|
|
106
102
|
} else {
|
|
107
103
|
console.log(chalk.dim(" Nothing to sync."));
|
|
108
104
|
}
|
|
@@ -185,18 +181,13 @@ export function register(api) {
|
|
|
185
181
|
},
|
|
186
182
|
});
|
|
187
183
|
|
|
188
|
-
// ── Hook: before:up —
|
|
184
|
+
// ── Hook: before:up — auto-sync secrets ────────────
|
|
189
185
|
api.registerHook("before:up", async () => {
|
|
190
186
|
if (!config.autoSync) return;
|
|
191
187
|
|
|
192
188
|
const root = findRoot();
|
|
193
189
|
if (!root) return;
|
|
194
190
|
|
|
195
|
-
// One-shot: skip if secrets were already synced once
|
|
196
|
-
const markerDir = path.join(root, ".fops");
|
|
197
|
-
const markerPath = path.join(markerDir, ".1p-synced");
|
|
198
|
-
if (fs.existsSync(markerPath)) return;
|
|
199
|
-
|
|
200
191
|
const templates = discoverTemplates(root);
|
|
201
192
|
if (templates.length === 0) return;
|
|
202
193
|
|
|
@@ -221,9 +212,6 @@ export function register(api) {
|
|
|
221
212
|
} else if (synced > 0) {
|
|
222
213
|
const files = synced === 1 ? ".env" : `${synced} .env files`;
|
|
223
214
|
console.log(chalk.green(` ✓ ${totalSecrets} secret(s) synced → ${files}`));
|
|
224
|
-
// Mark as done so subsequent `fops up` calls skip auto-sync
|
|
225
|
-
if (!fs.existsSync(markerDir)) fs.mkdirSync(markerDir, { recursive: true });
|
|
226
|
-
fs.writeFileSync(markerPath, new Date().toISOString() + "\n");
|
|
227
215
|
}
|
|
228
216
|
});
|
|
229
217
|
|
|
@@ -138,10 +138,8 @@ export async function register(api) {
|
|
|
138
138
|
} else if (process.platform === "linux") {
|
|
139
139
|
const { exitCode } = await execa("which", ["apt-get"], { reject: false });
|
|
140
140
|
if (exitCode !== 0) throw new Error("apt-get not found — use https://learn.microsoft.com/en-us/cli/azure/install-azure-cli for your distro");
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (sudoCheck.exitCode !== 0) throw new Error("sudo not available — install Azure CLI manually: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash");
|
|
144
|
-
await execa("sudo", ["sh", "-c", "curl -sL https://aka.ms/InstallAzureCLIDeb | bash"], { stdio: "inherit", timeout: 300000 });
|
|
141
|
+
await execa("sudo", ["apt-get", "update"], { stdio: "inherit", timeout: 120000 });
|
|
142
|
+
await execa("sudo", ["apt-get", "install", "-y", "azure-cli"], { stdio: "inherit", timeout: 120000 });
|
|
145
143
|
} else {
|
|
146
144
|
throw new Error("Auto-install only on macOS and Linux — use the Microsoft install link");
|
|
147
145
|
}
|
|
@@ -148,7 +148,7 @@ function resolveFluxConfig(clusterName, opts) {
|
|
|
148
148
|
return {
|
|
149
149
|
fluxRepo: opts?.fluxRepo ?? tracked?.flux?.repo ?? az.fluxRepo ?? project?.fluxRepo ?? AKS_DEFAULTS.fluxRepo,
|
|
150
150
|
fluxOwner: opts?.fluxOwner ?? tracked?.flux?.owner ?? az.fluxOwner ?? project?.fluxOwner ?? AKS_DEFAULTS.fluxOwner,
|
|
151
|
-
fluxPath: opts?.fluxPath || tracked?.flux?.path || az.fluxPath || project?.fluxPath ||
|
|
151
|
+
fluxPath: opts?.fluxPath || tracked?.flux?.path || az.fluxPath || project?.fluxPath || AKS_DEFAULTS.fluxPath,
|
|
152
152
|
fluxBranch: opts?.fluxBranch ?? tracked?.flux?.branch ?? az.fluxBranch ?? project?.fluxBranch ?? AKS_DEFAULTS.fluxBranch,
|
|
153
153
|
};
|
|
154
154
|
}
|
|
@@ -182,107 +182,6 @@ function requireCluster(name) {
|
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
// ── Flux local-repo scaffolding ───────────────────────────────────────────────
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Auto-detect the local flux repo clone.
|
|
189
|
-
* Searches common relative paths from the project root and CWD.
|
|
190
|
-
*/
|
|
191
|
-
function findFluxLocalRepo() {
|
|
192
|
-
const state = readState();
|
|
193
|
-
const projectRoot = state.azure?.projectRoot || state.projectRoot;
|
|
194
|
-
|
|
195
|
-
const candidates = [];
|
|
196
|
-
if (projectRoot) {
|
|
197
|
-
candidates.push(path.resolve(projectRoot, "..", "flux"));
|
|
198
|
-
candidates.push(path.resolve(projectRoot, "flux"));
|
|
199
|
-
}
|
|
200
|
-
candidates.push(path.resolve("../flux"));
|
|
201
|
-
candidates.push(path.resolve("../../flux"));
|
|
202
|
-
|
|
203
|
-
for (const p of candidates) {
|
|
204
|
-
if (fs.existsSync(path.join(p, "clusters"))) return p;
|
|
205
|
-
}
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Resolve the bundled cluster template directory shipped with the CLI.
|
|
211
|
-
*/
|
|
212
|
-
function resolveClusterTemplate() {
|
|
213
|
-
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
214
|
-
return path.resolve(thisDir, "..", "templates", "cluster");
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Scaffold a new cluster directory in the local flux repo from the bundled
|
|
219
|
-
* template, substituting {{CLUSTER_NAME}} and {{OVERLAY}} placeholders.
|
|
220
|
-
* Then commits and pushes the change.
|
|
221
|
-
*/
|
|
222
|
-
async function scaffoldFluxCluster(execa, { clusterName, fluxLocalRepo, overlay }) {
|
|
223
|
-
const templateDir = resolveClusterTemplate();
|
|
224
|
-
const destDir = path.join(fluxLocalRepo, "clusters", clusterName);
|
|
225
|
-
|
|
226
|
-
if (!fs.existsSync(templateDir)) {
|
|
227
|
-
console.log(WARN(` ⚠ Cluster template not found at ${templateDir}`));
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (fs.existsSync(destDir)) {
|
|
232
|
-
console.log(OK(` ✓ Cluster directory already exists: clusters/${clusterName}`));
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const vars = {
|
|
237
|
-
"{{CLUSTER_NAME}}": clusterName,
|
|
238
|
-
"{{OVERLAY}}": overlay || "demo-azure",
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
hint("Scaffolding Flux cluster manifests…");
|
|
242
|
-
|
|
243
|
-
function copyDir(src, dest) {
|
|
244
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
245
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
246
|
-
const srcPath = path.join(src, entry.name);
|
|
247
|
-
const destPath = path.join(dest, entry.name);
|
|
248
|
-
if (entry.isDirectory()) {
|
|
249
|
-
copyDir(srcPath, destPath);
|
|
250
|
-
} else {
|
|
251
|
-
let content = fs.readFileSync(srcPath, "utf8");
|
|
252
|
-
for (const [k, v] of Object.entries(vars)) {
|
|
253
|
-
content = content.replaceAll(k, v);
|
|
254
|
-
}
|
|
255
|
-
fs.writeFileSync(destPath, content);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
copyDir(templateDir, destDir);
|
|
261
|
-
console.log(OK(` ✓ Cluster directory created: clusters/${clusterName}`));
|
|
262
|
-
|
|
263
|
-
// List remaining placeholders
|
|
264
|
-
const remaining = [];
|
|
265
|
-
for (const line of fs.readFileSync(path.join(destDir, "kustomization.yaml"), "utf8").split("\n")) {
|
|
266
|
-
const m = line.match(/\{\{(\w+)\}\}/g);
|
|
267
|
-
if (m) remaining.push(...m);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Git add + commit + push
|
|
271
|
-
hint("Committing and pushing to flux repo…");
|
|
272
|
-
try {
|
|
273
|
-
await execa("git", ["-C", fluxLocalRepo, "add", `clusters/${clusterName}`], { timeout: 15000 });
|
|
274
|
-
await execa("git", ["-C", fluxLocalRepo, "commit", "-m", `Add cluster ${clusterName}`], { timeout: 15000 });
|
|
275
|
-
await execa("git", ["-C", fluxLocalRepo, "push"], { timeout: 60000 });
|
|
276
|
-
console.log(OK(` ✓ Pushed clusters/${clusterName} to flux repo`));
|
|
277
|
-
} catch (err) {
|
|
278
|
-
const msg = (err.stderr || err.message || "").split("\n")[0];
|
|
279
|
-
console.log(WARN(` ⚠ Git push failed: ${msg}`));
|
|
280
|
-
hint(`Manually commit and push clusters/${clusterName} in the flux repo`);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
185
|
// ── Flux helpers ──────────────────────────────────────────────────────────────
|
|
287
186
|
|
|
288
187
|
async function ensureFluxCli(execa) {
|
|
@@ -345,18 +244,6 @@ export async function aksUp(opts = {}) {
|
|
|
345
244
|
if (exists === 0) {
|
|
346
245
|
console.log(WARN(`\n Cluster "${clusterName}" already exists — reconciling…`));
|
|
347
246
|
|
|
348
|
-
// Scaffold cluster directory if it doesn't exist yet
|
|
349
|
-
if (!opts.noFlux) {
|
|
350
|
-
const fluxLocalRepo = opts.fluxLocalRepo || findFluxLocalRepo();
|
|
351
|
-
if (fluxLocalRepo) {
|
|
352
|
-
await scaffoldFluxCluster(execa, {
|
|
353
|
-
clusterName,
|
|
354
|
-
fluxLocalRepo,
|
|
355
|
-
overlay: opts.overlay,
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
247
|
const maxPods = opts.maxPods || 110;
|
|
361
248
|
const ctx = { execa, clusterName, rg, sub, opts, minCount, maxCount, maxPods };
|
|
362
249
|
await reconcileCluster(ctx);
|
|
@@ -460,22 +347,7 @@ export async function aksUp(opts = {}) {
|
|
|
460
347
|
const fluxRepo = opts.fluxRepo ?? AKS_DEFAULTS.fluxRepo;
|
|
461
348
|
const fluxOwner = opts.fluxOwner ?? AKS_DEFAULTS.fluxOwner;
|
|
462
349
|
const fluxBranch = opts.fluxBranch ?? AKS_DEFAULTS.fluxBranch;
|
|
463
|
-
const fluxPath = opts.fluxPath ||
|
|
464
|
-
|
|
465
|
-
// Scaffold cluster directory in the flux repo before bootstrapping
|
|
466
|
-
if (!opts.noFlux) {
|
|
467
|
-
const fluxLocalRepo = opts.fluxLocalRepo || findFluxLocalRepo();
|
|
468
|
-
if (fluxLocalRepo) {
|
|
469
|
-
await scaffoldFluxCluster(execa, {
|
|
470
|
-
clusterName,
|
|
471
|
-
fluxLocalRepo,
|
|
472
|
-
templateCluster: opts.templateCluster,
|
|
473
|
-
});
|
|
474
|
-
} else {
|
|
475
|
-
console.log(WARN(" ⚠ Local flux repo not found — skipping cluster scaffolding."));
|
|
476
|
-
hint("Pass --flux-local-repo <path> or clone meshxdata/flux next to foundation-compose.");
|
|
477
|
-
}
|
|
478
|
-
}
|
|
350
|
+
const fluxPath = opts.fluxPath || AKS_DEFAULTS.fluxPath;
|
|
479
351
|
|
|
480
352
|
if (opts.noFlux) {
|
|
481
353
|
console.log("");
|