omegon 0.6.19 → 0.6.21
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.
|
@@ -317,6 +317,79 @@ const ociProvider: AuthProvider = {
|
|
|
317
317
|
},
|
|
318
318
|
};
|
|
319
319
|
|
|
320
|
+
const vaultProvider: AuthProvider = {
|
|
321
|
+
id: "vault",
|
|
322
|
+
name: "Vault",
|
|
323
|
+
cli: "vault",
|
|
324
|
+
tokenEnvVar: "VAULT_TOKEN",
|
|
325
|
+
refreshCommand: "vault login",
|
|
326
|
+
|
|
327
|
+
async check(pi, signal) {
|
|
328
|
+
// 1. Check CLI is installed
|
|
329
|
+
const which = await pi.exec("which", ["vault"], { signal, timeout: 3_000 });
|
|
330
|
+
if (which.code !== 0) {
|
|
331
|
+
return { provider: this.id, status: "missing", detail: "vault CLI not installed" };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 2. Check VAULT_ADDR is configured — without it, no meaningful check is possible
|
|
335
|
+
const addr = process.env["VAULT_ADDR"];
|
|
336
|
+
if (!addr) {
|
|
337
|
+
return {
|
|
338
|
+
provider: this.id,
|
|
339
|
+
status: "none",
|
|
340
|
+
detail: "VAULT_ADDR not set",
|
|
341
|
+
refresh: this.refreshCommand,
|
|
342
|
+
secretHint: "VAULT_ADDR",
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 3. Run vault token lookup — read-only, returns token metadata (never the token itself)
|
|
347
|
+
// VAULT_TOKEN is read by the vault CLI from the environment; we never access it directly.
|
|
348
|
+
const result = await pi.exec("vault", ["token", "lookup", "-format=json"], { signal, timeout: 10_000 });
|
|
349
|
+
|
|
350
|
+
if (result.code === 0) {
|
|
351
|
+
try {
|
|
352
|
+
const data = JSON.parse(result.stdout.trim());
|
|
353
|
+
const tokenData = data?.data ?? {};
|
|
354
|
+
|
|
355
|
+
// Extract safe metadata — policies and expiry only, never the token value
|
|
356
|
+
const policies: string[] = tokenData.policies ?? [];
|
|
357
|
+
const displayName: string = tokenData.display_name ?? "";
|
|
358
|
+
const expireTime: string = tokenData.expire_time ?? "";
|
|
359
|
+
|
|
360
|
+
// Build a human-readable detail string
|
|
361
|
+
const parts: string[] = [];
|
|
362
|
+
if (displayName) parts.push(displayName);
|
|
363
|
+
if (policies.length > 0) parts.push(`policies: ${policies.filter(p => p !== "default").join(", ") || "default"}`);
|
|
364
|
+
if (expireTime) parts.push(`expires: ${expireTime.split("T")[0]}`);
|
|
365
|
+
else parts.push("no expiry");
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
provider: this.id,
|
|
369
|
+
status: "ok",
|
|
370
|
+
detail: parts.join(" · ") || "authenticated",
|
|
371
|
+
refresh: this.refreshCommand,
|
|
372
|
+
};
|
|
373
|
+
} catch {
|
|
374
|
+
// JSON parse failed but command succeeded — still authenticated
|
|
375
|
+
return { provider: this.id, status: "ok", detail: "authenticated", refresh: this.refreshCommand };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 4. Diagnose failure — truncate to 300 chars, never log token values
|
|
380
|
+
const output = (result.stdout + "\n" + result.stderr).trim();
|
|
381
|
+
const diag = diagnoseError(output);
|
|
382
|
+
return {
|
|
383
|
+
provider: this.id,
|
|
384
|
+
status: diag.status,
|
|
385
|
+
detail: `${addr} — ${diag.reason}`,
|
|
386
|
+
error: output.slice(0, 300),
|
|
387
|
+
refresh: this.refreshCommand,
|
|
388
|
+
secretHint: "VAULT_TOKEN",
|
|
389
|
+
};
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
|
|
320
393
|
// ─── Provider Registry ───────────────────────────────────────────
|
|
321
394
|
|
|
322
395
|
/** All providers, ordered by typical check priority. */
|
|
@@ -327,6 +400,7 @@ export const ALL_PROVIDERS: AuthProvider[] = [
|
|
|
327
400
|
awsProvider,
|
|
328
401
|
kubernetesProvider,
|
|
329
402
|
ociProvider,
|
|
403
|
+
vaultProvider,
|
|
330
404
|
];
|
|
331
405
|
|
|
332
406
|
export function findProvider(idOrName: string): AuthProvider | undefined {
|
|
@@ -31,6 +31,12 @@ export interface Dep {
|
|
|
31
31
|
url?: string;
|
|
32
32
|
/** Dep IDs that must be installed first */
|
|
33
33
|
requires?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Optional preflight check. If it returns a string, that string is a
|
|
36
|
+
* blocking message shown to the operator explaining what they need to do
|
|
37
|
+
* manually before this dep can be installed. Return undefined if ready.
|
|
38
|
+
*/
|
|
39
|
+
preflight?: () => string | undefined;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
export interface InstallOption {
|
|
@@ -53,6 +59,12 @@ function ensureToolPaths(): void {
|
|
|
53
59
|
"/nix/var/nix/profiles/default/bin",
|
|
54
60
|
join(home, ".nix-profile", "bin"),
|
|
55
61
|
join(home, ".cargo", "bin"),
|
|
62
|
+
// Linuxbrew
|
|
63
|
+
"/home/linuxbrew/.linuxbrew/bin",
|
|
64
|
+
join(home, ".linuxbrew", "bin"),
|
|
65
|
+
// macOS Homebrew
|
|
66
|
+
"/opt/homebrew/bin",
|
|
67
|
+
"/usr/local/bin",
|
|
56
68
|
];
|
|
57
69
|
const current = process.env.PATH ?? "";
|
|
58
70
|
const parts = current.split(":");
|
|
@@ -63,6 +75,52 @@ function ensureToolPaths(): void {
|
|
|
63
75
|
}
|
|
64
76
|
ensureToolPaths();
|
|
65
77
|
|
|
78
|
+
/** Detect ostree-based immutable Linux (Bazzite, Silverblue, Kinoite, Bluefin, etc.) */
|
|
79
|
+
function isOstree(): boolean {
|
|
80
|
+
if (process.platform !== "linux") return false;
|
|
81
|
+
try {
|
|
82
|
+
execSync("which rpm-ostree", { stdio: "ignore" });
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* On Fedora 42+ ostree systems, `/` is read-only by default (composefs).
|
|
91
|
+
* Nix needs to create `/nix` which requires `root.transient = true` in the
|
|
92
|
+
* ostree prepare-root config. Returns blocking instructions if not ready.
|
|
93
|
+
*/
|
|
94
|
+
function checkOstreeReadyForNix(): string | undefined {
|
|
95
|
+
// If /nix already exists, we're good (previous install or already configured)
|
|
96
|
+
if (existsSync("/nix")) return undefined;
|
|
97
|
+
|
|
98
|
+
// Check if root.transient is enabled
|
|
99
|
+
try {
|
|
100
|
+
const conf = execSync("cat /etc/ostree/prepare-root.conf 2>/dev/null", { encoding: "utf-8" });
|
|
101
|
+
if (/transient\s*=\s*true/i.test(conf)) return undefined;
|
|
102
|
+
} catch { /* file doesn't exist */ }
|
|
103
|
+
|
|
104
|
+
return [
|
|
105
|
+
"⚠️ Your system has a read-only root filesystem (ostree/composefs).",
|
|
106
|
+
"Nix needs `/nix` to exist, which requires enabling root.transient.",
|
|
107
|
+
"",
|
|
108
|
+
"Run these commands in your terminal, then reboot and run /bootstrap again:",
|
|
109
|
+
"",
|
|
110
|
+
"```",
|
|
111
|
+
"sudo tee /etc/ostree/prepare-root.conf <<'EOL'",
|
|
112
|
+
"[composefs]",
|
|
113
|
+
"enabled = yes",
|
|
114
|
+
"[root]",
|
|
115
|
+
"transient = true",
|
|
116
|
+
"EOL",
|
|
117
|
+
"",
|
|
118
|
+
"sudo rpm-ostree initramfs-etc --track=/etc/ostree/prepare-root.conf",
|
|
119
|
+
"systemctl reboot",
|
|
120
|
+
"```",
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
|
|
66
124
|
function hasCmd(cmd: string): boolean {
|
|
67
125
|
try {
|
|
68
126
|
execSync(`which ${cmd}`, { stdio: "ignore" });
|
|
@@ -105,10 +163,19 @@ export const DEPS: Dep[] = [
|
|
|
105
163
|
tier: "core",
|
|
106
164
|
check: () => hasCmd("nix"),
|
|
107
165
|
install: [
|
|
108
|
-
//
|
|
109
|
-
|
|
166
|
+
// Immutable ostree-based Linux (Bazzite, Silverblue, Bluefin, etc.)
|
|
167
|
+
// needs root.transient enabled and --persistence=/var/lib/nix so the
|
|
168
|
+
// nix store lives on a writable partition. The upstream installer uses
|
|
169
|
+
// the ostree planner automatically when it detects ostree.
|
|
170
|
+
{ platform: "linux", cmd: isOstree()
|
|
171
|
+
? "curl -sSfL https://install.determinate.systems/nix | sh -s -- install --no-confirm --persistence=/var/lib/nix"
|
|
172
|
+
: "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm" },
|
|
173
|
+
{ platform: "darwin", cmd: "curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm" },
|
|
110
174
|
],
|
|
111
175
|
url: "https://zero-to-nix.com",
|
|
176
|
+
// On ostree systems, root.transient must be enabled first for /nix to be created.
|
|
177
|
+
// preflight returns instructions if the system isn't ready.
|
|
178
|
+
preflight: isOstree() ? checkOstreeReadyForNix : undefined,
|
|
112
179
|
},
|
|
113
180
|
{
|
|
114
181
|
id: "ollama",
|
|
@@ -138,6 +205,19 @@ export const DEPS: Dep[] = [
|
|
|
138
205
|
},
|
|
139
206
|
|
|
140
207
|
// --- Recommended: common workflows ---
|
|
208
|
+
{
|
|
209
|
+
id: "vault",
|
|
210
|
+
name: "Vault CLI",
|
|
211
|
+
purpose: "HashiCorp Vault authentication status checking and secret management",
|
|
212
|
+
usedBy: ["01-auth"],
|
|
213
|
+
tier: "optional",
|
|
214
|
+
check: () => hasCmd("vault"),
|
|
215
|
+
requires: ["nix"],
|
|
216
|
+
install: [
|
|
217
|
+
{ platform: "any", cmd: "nix profile install nixpkgs#vault" },
|
|
218
|
+
],
|
|
219
|
+
url: "https://developer.hashicorp.com/vault/install",
|
|
220
|
+
},
|
|
141
221
|
{
|
|
142
222
|
id: "gh",
|
|
143
223
|
name: "GitHub CLI",
|
|
@@ -1075,6 +1075,15 @@ async function installDeps(ctx: CommandContext, deps: DepStatus[]): Promise<void
|
|
|
1075
1075
|
const { dep } = sorted[i];
|
|
1076
1076
|
const step = `[${i + 1}/${total}]`;
|
|
1077
1077
|
|
|
1078
|
+
// Preflight check — some deps need manual system prep before install
|
|
1079
|
+
if (dep.preflight) {
|
|
1080
|
+
const blocker = dep.preflight();
|
|
1081
|
+
if (blocker) {
|
|
1082
|
+
ctx.ui.notify(`\n${step} 🛑 ${dep.name} — manual setup required:\n\n${blocker}`);
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1078
1087
|
// Check prerequisites — re-verify availability live (not from stale array)
|
|
1079
1088
|
if (dep.requires?.length) {
|
|
1080
1089
|
const unmet = dep.requires.filter((reqId) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omegon",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.21",
|
|
4
4
|
"description": "Omegon — an opinionated distribution of pi (by Mario Zechner) with extensions for lifecycle management, memory, orchestration, and visualization",
|
|
5
5
|
"bin": {
|
|
6
6
|
"omegon": "bin/omegon.mjs",
|