@pleri/olam-cli 0.1.188 → 0.1.195
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/README.md +1 -1
- package/dist/ask/knowledge-pack.generated.d.ts.map +1 -1
- package/dist/ask/knowledge-pack.generated.js +37 -12
- package/dist/ask/knowledge-pack.generated.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +4 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +6 -9
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/clean.js +1 -1
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +1 -4
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +6 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/crystallize.js +12 -14
- package/dist/commands/crystallize.js.map +1 -1
- package/dist/commands/destroy.d.ts +13 -1
- package/dist/commands/destroy.d.ts.map +1 -1
- package/dist/commands/destroy.js +52 -6
- package/dist/commands/destroy.js.map +1 -1
- package/dist/commands/dispatch.d.ts +9 -0
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +21 -2
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +29 -22
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enter.d.ts +3 -3
- package/dist/commands/enter.d.ts.map +1 -1
- package/dist/commands/enter.js +57 -44
- package/dist/commands/enter.js.map +1 -1
- package/dist/commands/flywheel/index.d.ts.map +1 -1
- package/dist/commands/flywheel/index.js +1 -1
- package/dist/commands/flywheel/index.js.map +1 -1
- package/dist/commands/host-cp.d.ts.map +1 -1
- package/dist/commands/host-cp.js +2 -1
- package/dist/commands/host-cp.js.map +1 -1
- package/dist/commands/implode.d.ts.map +1 -1
- package/dist/commands/implode.js +1 -1
- package/dist/commands/implode.js.map +1 -1
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +102 -9
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/kg-build.d.ts.map +1 -1
- package/dist/commands/kg-build.js +3 -0
- package/dist/commands/kg-build.js.map +1 -1
- package/dist/commands/kg-classify.d.ts +20 -0
- package/dist/commands/kg-classify.d.ts.map +1 -1
- package/dist/commands/kg-classify.js +59 -42
- package/dist/commands/kg-classify.js.map +1 -1
- package/dist/commands/kg-mirror.d.ts +40 -0
- package/dist/commands/kg-mirror.d.ts.map +1 -0
- package/dist/commands/kg-mirror.js +228 -0
- package/dist/commands/kg-mirror.js.map +1 -0
- package/dist/commands/mcp/index.js +1 -1
- package/dist/commands/mcp/index.js.map +1 -1
- package/dist/commands/memory/index.d.ts.map +1 -1
- package/dist/commands/memory/index.js +1 -1
- package/dist/commands/memory/index.js.map +1 -1
- package/dist/commands/resume.d.ts.map +1 -1
- package/dist/commands/resume.js +1 -1
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/services-tls.d.ts +120 -0
- package/dist/commands/services-tls.d.ts.map +1 -0
- package/dist/commands/services-tls.js +434 -0
- package/dist/commands/services-tls.js.map +1 -0
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +28 -1
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/setup-linux-gate.d.ts.map +1 -1
- package/dist/commands/setup-linux-gate.js +1 -3
- package/dist/commands/setup-linux-gate.js.map +1 -1
- package/dist/commands/setup-metrics.d.ts.map +1 -1
- package/dist/commands/setup-metrics.js +1 -2
- package/dist/commands/setup-metrics.js.map +1 -1
- package/dist/commands/setup-phase-5a-skill-source.d.ts +17 -1
- package/dist/commands/setup-phase-5a-skill-source.d.ts.map +1 -1
- package/dist/commands/setup-phase-5a-skill-source.js +69 -6
- package/dist/commands/setup-phase-5a-skill-source.js.map +1 -1
- package/dist/commands/setup.d.ts +26 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +189 -47
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills-onboard.d.ts.map +1 -1
- package/dist/commands/skills-onboard.js +4 -1
- package/dist/commands/skills-onboard.js.map +1 -1
- package/dist/commands/skills-source.d.ts.map +1 -1
- package/dist/commands/skills-source.js +20 -4
- package/dist/commands/skills-source.js.map +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +1 -3
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/yolo.d.ts.map +1 -1
- package/dist/commands/yolo.js +1 -1
- package/dist/commands/yolo.js.map +1 -1
- package/dist/context.d.ts +4 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -2
- package/dist/context.js.map +1 -1
- package/dist/image-digests.json +8 -8
- package/dist/index.js +3846 -2232
- package/dist/index.js.map +1 -1
- package/dist/lib/auth-refresh-kubernetes.d.ts.map +1 -1
- package/dist/lib/auth-refresh-kubernetes.js +14 -5
- package/dist/lib/auth-refresh-kubernetes.js.map +1 -1
- package/dist/lib/bootstrap-kubernetes.d.ts +41 -0
- package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -1
- package/dist/lib/bootstrap-kubernetes.js +289 -36
- package/dist/lib/bootstrap-kubernetes.js.map +1 -1
- package/dist/lib/cf-access-token.d.ts.map +1 -1
- package/dist/lib/cf-access-token.js +2 -3
- package/dist/lib/cf-access-token.js.map +1 -1
- package/dist/lib/help-groups.d.ts +36 -0
- package/dist/lib/help-groups.d.ts.map +1 -0
- package/dist/lib/help-groups.js +124 -0
- package/dist/lib/help-groups.js.map +1 -0
- package/dist/lib/k8s-bootstrap.d.ts +6 -0
- package/dist/lib/k8s-bootstrap.d.ts.map +1 -1
- package/dist/lib/k8s-bootstrap.js +15 -2
- package/dist/lib/k8s-bootstrap.js.map +1 -1
- package/dist/lib/k8s-secret-render.d.ts.map +1 -1
- package/dist/lib/k8s-secret-render.js +17 -10
- package/dist/lib/k8s-secret-render.js.map +1 -1
- package/dist/lib/memory-secret.d.ts +15 -2
- package/dist/lib/memory-secret.d.ts.map +1 -1
- package/dist/lib/memory-secret.js +25 -8
- package/dist/lib/memory-secret.js.map +1 -1
- package/dist/lib/upgrade-check.d.ts +60 -0
- package/dist/lib/upgrade-check.d.ts.map +1 -0
- package/dist/lib/upgrade-check.js +169 -0
- package/dist/lib/upgrade-check.js.map +1 -0
- package/dist/lib/upgrade-kubernetes.d.ts +17 -0
- package/dist/lib/upgrade-kubernetes.d.ts.map +1 -1
- package/dist/lib/upgrade-kubernetes.js +125 -1
- package/dist/lib/upgrade-kubernetes.js.map +1 -1
- package/dist/mcp-server.js +2651 -2850
- package/hermes-bundle/version.json +1 -1
- package/host-cp/k8s/manifests/30-configmap.yaml +8 -1
- package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/60-service.yaml +12 -4
- package/host-cp/k8s/manifests/70-ingressroute.yaml +58 -0
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
- package/host-cp/src/plan-chat-secret.mjs +16 -1
- package/host-cp/src/plan-chat-service.mjs +493 -11
- package/host-cp/src/planning-sessions.mjs +252 -0
- package/host-cp/src/server.mjs +92 -2
- package/package.json +2 -1
|
@@ -16,7 +16,8 @@ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, renameSyn
|
|
|
16
16
|
import { homedir } from 'node:os';
|
|
17
17
|
import { join, dirname } from 'node:path';
|
|
18
18
|
import { randomBytes } from 'node:crypto';
|
|
19
|
-
|
|
19
|
+
import { resolveSecretPath, canonicalSecretPath } from '@olam/core/src/secrets/paths.js';
|
|
20
|
+
export const MEMORY_SECRET_PATH = resolveSecretPath('memory-secret');
|
|
20
21
|
/**
|
|
21
22
|
* Default cloud-mode secret path. Phase C C2.
|
|
22
23
|
*
|
|
@@ -25,7 +26,13 @@ export const MEMORY_SECRET_PATH = join(homedir(), '.olam', 'memory-secret');
|
|
|
25
26
|
* config-resolver (C4) handles the precedence; this constant is the
|
|
26
27
|
* fall-through when the operator doesn't override.
|
|
27
28
|
*/
|
|
28
|
-
export const CLOUD_MEMORY_SECRET_PATH =
|
|
29
|
+
export const CLOUD_MEMORY_SECRET_PATH = resolveSecretPath('cloud-memory-secret');
|
|
30
|
+
/**
|
|
31
|
+
* Canonical (new) location for memory secret — used by writers.
|
|
32
|
+
* Defined here as a convenience alias so callers don't need to import from core.
|
|
33
|
+
*/
|
|
34
|
+
export const MEMORY_SECRET_CANONICAL_PATH = canonicalSecretPath('memory-secret');
|
|
35
|
+
export const CLOUD_MEMORY_SECRET_CANONICAL_PATH = canonicalSecretPath('cloud-memory-secret');
|
|
29
36
|
export const SECRET_LEN_BYTES = 32; // 64 hex chars
|
|
30
37
|
/** Generate a 64-char hex secret. */
|
|
31
38
|
export function generateSecret() {
|
|
@@ -36,7 +43,7 @@ export function generateSecret() {
|
|
|
36
43
|
* created with default mode if absent.
|
|
37
44
|
*/
|
|
38
45
|
export function writeSecretAtPath(path, value) {
|
|
39
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
46
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
40
47
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
41
48
|
writeFileSync(tmp, value, { mode: 0o600 });
|
|
42
49
|
// writeFileSync's mode is umask-respecting; chmod explicitly to be safe.
|
|
@@ -65,15 +72,20 @@ export function readSecretAtPath(path) {
|
|
|
65
72
|
return v;
|
|
66
73
|
}
|
|
67
74
|
/**
|
|
68
|
-
* Ensure `~/.olam/memory-secret` exists with mode 0600. Generates a
|
|
75
|
+
* Ensure `~/.olam/secrets/memory-secret` exists with mode 0600. Generates a
|
|
69
76
|
* fresh 64-char hex secret on first call. Idempotent.
|
|
77
|
+
*
|
|
78
|
+
* Reads from the resolved path (new or legacy fallback); writes to canonical.
|
|
70
79
|
*/
|
|
71
80
|
export function ensureMemorySecret(path = MEMORY_SECRET_PATH) {
|
|
72
81
|
const existing = readSecretAtPathOrNull(path);
|
|
73
82
|
if (existing)
|
|
74
83
|
return existing;
|
|
75
84
|
const fresh = generateSecret();
|
|
76
|
-
|
|
85
|
+
// When using the default resolved path, write to canonical new location.
|
|
86
|
+
// When given an explicit path (test seam or direct caller), honour it for write too.
|
|
87
|
+
const writePath = path === MEMORY_SECRET_PATH ? MEMORY_SECRET_CANONICAL_PATH : path;
|
|
88
|
+
writeSecretAtPath(writePath, fresh);
|
|
77
89
|
return fresh;
|
|
78
90
|
}
|
|
79
91
|
/** Read the local-mode memory secret (no-throw shape). */
|
|
@@ -88,8 +100,10 @@ export function readMemorySecret(path = MEMORY_SECRET_PATH) {
|
|
|
88
100
|
* Rotate the secret: generate fresh value, atomically replace the file,
|
|
89
101
|
* return the new value. Caller is responsible for restarting any
|
|
90
102
|
* running memory service so it picks up the new value.
|
|
103
|
+
*
|
|
104
|
+
* Always writes to canonical path (~/.olam/secrets/memory-secret).
|
|
91
105
|
*/
|
|
92
|
-
export function rotateMemorySecret(path =
|
|
106
|
+
export function rotateMemorySecret(path = MEMORY_SECRET_CANONICAL_PATH) {
|
|
93
107
|
const fresh = generateSecret();
|
|
94
108
|
writeSecretAtPath(path, fresh);
|
|
95
109
|
return fresh;
|
|
@@ -119,8 +133,11 @@ export function readCloudMemorySecretOrNull(path = CLOUD_MEMORY_SECRET_PATH) {
|
|
|
119
133
|
export function readCloudMemorySecret(path = CLOUD_MEMORY_SECRET_PATH) {
|
|
120
134
|
return readSecretAtPath(path);
|
|
121
135
|
}
|
|
122
|
-
/**
|
|
123
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Atomic write 0600. Caller supplies the value (cloud secrets are
|
|
138
|
+
* operator-prompted, not generated). Always writes to canonical path.
|
|
139
|
+
*/
|
|
140
|
+
export function writeCloudMemorySecret(value, path = CLOUD_MEMORY_SECRET_CANONICAL_PATH) {
|
|
124
141
|
writeSecretAtPath(path, value);
|
|
125
142
|
}
|
|
126
143
|
/** Convenience: is a cloud secret on disk? */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-secret.js","sourceRoot":"","sources":["../../src/lib/memory-secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1H,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-secret.js","sourceRoot":"","sources":["../../src/lib/memory-secret.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upgrade-check.ts — non-blocking upgrade-available banner for olam CLI.
|
|
3
|
+
*
|
|
4
|
+
* On each `olam <command>` invocation, asynchronously checks whether a newer
|
|
5
|
+
* version of `@pleri/olam-cli` is available on npm. The result is cached for
|
|
6
|
+
* 24 hours in `~/.olam/cli-upgrade-cache.json` so the npm check fires at most
|
|
7
|
+
* once per day. Any network or FS failure silently skips the banner.
|
|
8
|
+
*
|
|
9
|
+
* Suppression rules (any match → silent):
|
|
10
|
+
* - OLAM_DISABLE_UPGRADE_BANNER=1
|
|
11
|
+
* - --json flag present in argv
|
|
12
|
+
* - stdout is not a TTY
|
|
13
|
+
*/
|
|
14
|
+
export declare const UPGRADE_CACHE_TTL_MS: number;
|
|
15
|
+
export declare const NPM_CHECK_TIMEOUT_MS = 2000;
|
|
16
|
+
export declare const DEFAULT_CACHE_PATH: string;
|
|
17
|
+
/**
|
|
18
|
+
* Fetch the latest published version of @pleri/olam-cli from the npm registry.
|
|
19
|
+
* Returns null on any network error or timeout.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fetchLatestVersion(timeoutMs?: number): Promise<string | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Determine the latest version, using cache when fresh.
|
|
24
|
+
* Always returns without throwing. Returns null when unavailable.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveLatestVersion(opts: {
|
|
27
|
+
cachePath?: string;
|
|
28
|
+
now?: number;
|
|
29
|
+
fetchFn?: (timeout: number) => Promise<string | null>;
|
|
30
|
+
}): Promise<string | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Build the upgrade banner string (with box-drawing characters).
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildBanner(latestVersion: string, currentVersion: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Returns true when the upgrade banner should be suppressed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function shouldSuppressBanner(opts: {
|
|
39
|
+
env?: Record<string, string | undefined>;
|
|
40
|
+
argv?: readonly string[];
|
|
41
|
+
isTTY?: boolean;
|
|
42
|
+
}): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Fire-and-forget upgrade check. Resolves the latest version, prints a banner
|
|
45
|
+
* to stdout if a newer version is available, and swallows all errors.
|
|
46
|
+
*
|
|
47
|
+
* Call this BEFORE program.parseAsync(). The promise is not awaited — the
|
|
48
|
+
* banner races the command action. For short commands this means the banner
|
|
49
|
+
* arrives before exit; for fast commands that call process.exit explicitly
|
|
50
|
+
* (e.g. olam --version), the banner may be suppressed naturally.
|
|
51
|
+
*/
|
|
52
|
+
export declare function scheduleUpgradeCheck(currentVersion: string, opts?: {
|
|
53
|
+
cachePath?: string;
|
|
54
|
+
fetchFn?: (timeout: number) => Promise<string | null>;
|
|
55
|
+
env?: Record<string, string | undefined>;
|
|
56
|
+
argv?: readonly string[];
|
|
57
|
+
isTTY?: boolean;
|
|
58
|
+
write?: (s: string) => void;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
//# sourceMappingURL=upgrade-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-check.d.ts","sourceRoot":"","sources":["../../src/lib/upgrade-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AACxD,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAE1C,eAAO,MAAM,kBAAkB,QAA6D,CAAC;AAgD7F;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,SAAuB,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuBxB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACvD,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBzB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAWjF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CASV;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upgrade-check.ts — non-blocking upgrade-available banner for olam CLI.
|
|
3
|
+
*
|
|
4
|
+
* On each `olam <command>` invocation, asynchronously checks whether a newer
|
|
5
|
+
* version of `@pleri/olam-cli` is available on npm. The result is cached for
|
|
6
|
+
* 24 hours in `~/.olam/cli-upgrade-cache.json` so the npm check fires at most
|
|
7
|
+
* once per day. Any network or FS failure silently skips the banner.
|
|
8
|
+
*
|
|
9
|
+
* Suppression rules (any match → silent):
|
|
10
|
+
* - OLAM_DISABLE_UPGRADE_BANNER=1
|
|
11
|
+
* - --json flag present in argv
|
|
12
|
+
* - stdout is not a TTY
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as os from 'node:os';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
export const UPGRADE_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
18
|
+
export const NPM_CHECK_TIMEOUT_MS = 2_000;
|
|
19
|
+
export const DEFAULT_CACHE_PATH = path.join(os.homedir(), '.olam', 'cli-upgrade-cache.json');
|
|
20
|
+
function parseVersion(v) {
|
|
21
|
+
const parts = v.replace(/^[^0-9]*/, '').split('.').map(Number);
|
|
22
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
23
|
+
}
|
|
24
|
+
function isNewer(latest, current) {
|
|
25
|
+
const [la, lb, lc] = parseVersion(latest);
|
|
26
|
+
const [ca, cb, cc] = parseVersion(current);
|
|
27
|
+
if (la !== ca)
|
|
28
|
+
return la > ca;
|
|
29
|
+
if (lb !== cb)
|
|
30
|
+
return lb > cb;
|
|
31
|
+
return lc > cc;
|
|
32
|
+
}
|
|
33
|
+
function readCache(cachePath) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(cachePath, 'utf-8');
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
if (typeof parsed === 'object' &&
|
|
38
|
+
parsed !== null &&
|
|
39
|
+
typeof parsed['checkedAt'] === 'number' &&
|
|
40
|
+
typeof parsed['latestVersion'] === 'string') {
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// unreadable or missing — treat as no cache
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function writeCache(cachePath, data) {
|
|
50
|
+
try {
|
|
51
|
+
const dir = path.dirname(cachePath);
|
|
52
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
53
|
+
fs.writeFileSync(cachePath, JSON.stringify(data), 'utf-8');
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// non-fatal
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Fetch the latest published version of @pleri/olam-cli from the npm registry.
|
|
61
|
+
* Returns null on any network error or timeout.
|
|
62
|
+
*/
|
|
63
|
+
export async function fetchLatestVersion(timeoutMs = NPM_CHECK_TIMEOUT_MS) {
|
|
64
|
+
const ac = new AbortController();
|
|
65
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch('https://registry.npmjs.org/@pleri/olam-cli/latest', {
|
|
68
|
+
signal: ac.signal,
|
|
69
|
+
headers: { accept: 'application/json' },
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok)
|
|
72
|
+
return null;
|
|
73
|
+
const json = (await res.json());
|
|
74
|
+
if (typeof json === 'object' &&
|
|
75
|
+
json !== null &&
|
|
76
|
+
typeof json['version'] === 'string') {
|
|
77
|
+
return json['version'];
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Determine the latest version, using cache when fresh.
|
|
90
|
+
* Always returns without throwing. Returns null when unavailable.
|
|
91
|
+
*/
|
|
92
|
+
export async function resolveLatestVersion(opts) {
|
|
93
|
+
const cachePath = opts.cachePath ?? DEFAULT_CACHE_PATH;
|
|
94
|
+
const now = opts.now ?? Date.now();
|
|
95
|
+
const fetchFn = opts.fetchFn ?? fetchLatestVersion;
|
|
96
|
+
const cached = readCache(cachePath);
|
|
97
|
+
if (cached !== null && now - cached.checkedAt < UPGRADE_CACHE_TTL_MS) {
|
|
98
|
+
return cached.latestVersion;
|
|
99
|
+
}
|
|
100
|
+
let latest = null;
|
|
101
|
+
try {
|
|
102
|
+
latest = await fetchFn(NPM_CHECK_TIMEOUT_MS);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (latest !== null) {
|
|
108
|
+
writeCache(cachePath, { checkedAt: now, latestVersion: latest });
|
|
109
|
+
}
|
|
110
|
+
return latest;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Build the upgrade banner string (with box-drawing characters).
|
|
114
|
+
*/
|
|
115
|
+
export function buildBanner(latestVersion, currentVersion) {
|
|
116
|
+
const line1 = ` olam v${latestVersion} available (you have v${currentVersion}) `;
|
|
117
|
+
const line2 = ` Update: npm install -g @pleri/olam-cli@latest `;
|
|
118
|
+
const width = Math.max(line1.length, line2.length);
|
|
119
|
+
const pad1 = line1.padEnd(width);
|
|
120
|
+
const pad2 = line2.padEnd(width);
|
|
121
|
+
const top = `╭${'─'.repeat(width)}╮`;
|
|
122
|
+
const mid1 = `│${pad1}│`;
|
|
123
|
+
const mid2 = `│${pad2}│`;
|
|
124
|
+
const bot = `╰${'─'.repeat(width)}╯`;
|
|
125
|
+
return [top, mid1, mid2, bot].join('\n') + '\n';
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns true when the upgrade banner should be suppressed.
|
|
129
|
+
*/
|
|
130
|
+
export function shouldSuppressBanner(opts) {
|
|
131
|
+
const env = opts.env ?? process.env;
|
|
132
|
+
const argv = opts.argv ?? process.argv;
|
|
133
|
+
const isTTY = opts.isTTY ?? process.stdout.isTTY;
|
|
134
|
+
if (env['OLAM_DISABLE_UPGRADE_BANNER'] === '1')
|
|
135
|
+
return true;
|
|
136
|
+
if (!isTTY)
|
|
137
|
+
return true;
|
|
138
|
+
if (argv.includes('--json'))
|
|
139
|
+
return true;
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Fire-and-forget upgrade check. Resolves the latest version, prints a banner
|
|
144
|
+
* to stdout if a newer version is available, and swallows all errors.
|
|
145
|
+
*
|
|
146
|
+
* Call this BEFORE program.parseAsync(). The promise is not awaited — the
|
|
147
|
+
* banner races the command action. For short commands this means the banner
|
|
148
|
+
* arrives before exit; for fast commands that call process.exit explicitly
|
|
149
|
+
* (e.g. olam --version), the banner may be suppressed naturally.
|
|
150
|
+
*/
|
|
151
|
+
export function scheduleUpgradeCheck(currentVersion, opts) {
|
|
152
|
+
if (shouldSuppressBanner(opts ?? {})) {
|
|
153
|
+
return Promise.resolve();
|
|
154
|
+
}
|
|
155
|
+
const write = opts?.write ?? ((s) => process.stdout.write(s));
|
|
156
|
+
return resolveLatestVersion({
|
|
157
|
+
cachePath: opts?.cachePath,
|
|
158
|
+
fetchFn: opts?.fetchFn,
|
|
159
|
+
})
|
|
160
|
+
.then((latest) => {
|
|
161
|
+
if (latest !== null && isNewer(latest, currentVersion)) {
|
|
162
|
+
write(buildBanner(latest, currentVersion));
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
.catch(() => {
|
|
166
|
+
// never surface
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=upgrade-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-check.js","sourceRoot":"","sources":["../../src/lib/upgrade-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -167,6 +167,23 @@ export interface UpgradeKubernetesResult {
|
|
|
167
167
|
readonly exitCode: number;
|
|
168
168
|
readonly summary: string;
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Early-exit probe for healthy-cluster re-runs.
|
|
172
|
+
*
|
|
173
|
+
* Reads the host-cp image spec from the on-disk 50-deployment.yaml, queries
|
|
174
|
+
* the running deployment's current image + readyReplicas, and returns true
|
|
175
|
+
* when the cluster is already at the desired state. When true the caller
|
|
176
|
+
* prints a single skip line and exits 0 without running any of the 8 steps.
|
|
177
|
+
*
|
|
178
|
+
* Fail-open: any read/parse/kubectl error returns false (run the full flow).
|
|
179
|
+
* Only triggers when --force-refresh-manifests is NOT set.
|
|
180
|
+
*
|
|
181
|
+
* Exported for unit testing (injectable deps).
|
|
182
|
+
*/
|
|
183
|
+
export declare function probeClusterAlreadyAtDesiredState(context: string, manifestsDir: string, deps: UpgradeKubernetesDeps): Promise<{
|
|
184
|
+
atDesiredState: boolean;
|
|
185
|
+
desiredDigest: string | null;
|
|
186
|
+
}>;
|
|
170
187
|
/**
|
|
171
188
|
* Main entrypoint for the kubernetes upgrade path.
|
|
172
189
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upgrade-kubernetes.d.ts","sourceRoot":"","sources":["../../src/lib/upgrade-kubernetes.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"upgrade-kubernetes.d.ts","sourceRoot":"","sources":["../../src/lib/upgrade-kubernetes.ts"],"names":[],"mappings":"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"}
|
|
@@ -479,6 +479,67 @@ async function createGhcrPullSecret(context, deps, stderr) {
|
|
|
479
479
|
// two-volume hostPath approach, which is fully retracted (Phase B B2).
|
|
480
480
|
// host-cp now reaches docker via TCP through the docker-socket-proxy
|
|
481
481
|
// ExternalName Service — no docker socket bind into k3d nodes at all.
|
|
482
|
+
/**
|
|
483
|
+
* Early-exit probe for healthy-cluster re-runs.
|
|
484
|
+
*
|
|
485
|
+
* Reads the host-cp image spec from the on-disk 50-deployment.yaml, queries
|
|
486
|
+
* the running deployment's current image + readyReplicas, and returns true
|
|
487
|
+
* when the cluster is already at the desired state. When true the caller
|
|
488
|
+
* prints a single skip line and exits 0 without running any of the 8 steps.
|
|
489
|
+
*
|
|
490
|
+
* Fail-open: any read/parse/kubectl error returns false (run the full flow).
|
|
491
|
+
* Only triggers when --force-refresh-manifests is NOT set.
|
|
492
|
+
*
|
|
493
|
+
* Exported for unit testing (injectable deps).
|
|
494
|
+
*/
|
|
495
|
+
export async function probeClusterAlreadyAtDesiredState(context, manifestsDir, deps) {
|
|
496
|
+
const readFileSyncImpl = deps.readFileSyncImpl ?? fs.readFileSync;
|
|
497
|
+
const wrap = deps.kubectlWrapImpl ?? kubectlWrap;
|
|
498
|
+
// 1. Read desired image from 50-deployment.yaml
|
|
499
|
+
const deploymentManifestPath = path.join(manifestsDir, '50-deployment.yaml');
|
|
500
|
+
let desiredImage = null;
|
|
501
|
+
try {
|
|
502
|
+
const raw = readFileSyncImpl(deploymentManifestPath, 'utf8');
|
|
503
|
+
// Match the main container image line: "image: <ref>@sha256:<digest>"
|
|
504
|
+
// The manifest uses `image: ghcr.io/pleri/olam-host-cp@sha256:<digest>`
|
|
505
|
+
const match = raw.match(/^\s+image:\s+(ghcr\.io\/pleri\/olam-host-cp@sha256:[a-f0-9]+)/m);
|
|
506
|
+
if (match?.[1]) {
|
|
507
|
+
desiredImage = match[1].trim();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
catch {
|
|
511
|
+
return { atDesiredState: false, desiredDigest: null };
|
|
512
|
+
}
|
|
513
|
+
if (!desiredImage) {
|
|
514
|
+
// Could not parse desired image — fail open.
|
|
515
|
+
return { atDesiredState: false, desiredDigest: null };
|
|
516
|
+
}
|
|
517
|
+
// Extract just the digest portion for reporting.
|
|
518
|
+
const digestMatch = desiredImage.match(/sha256:[a-f0-9]+/);
|
|
519
|
+
const desiredDigest = digestMatch?.[0] ?? null;
|
|
520
|
+
// 2. Query running deployment image + readyReplicas
|
|
521
|
+
const imageResult = await wrap([
|
|
522
|
+
'--context', context,
|
|
523
|
+
'get', 'deploy', HOST_CP_DEPLOYMENT_NAME,
|
|
524
|
+
'-n', K8S_NAMESPACE,
|
|
525
|
+
'-o', `jsonpath={.spec.template.spec.containers[?(@.name=="${HOST_CP_DEPLOYMENT_NAME}")].image}`,
|
|
526
|
+
], { timeout: 10_000 });
|
|
527
|
+
if (!imageResult.ok || !imageResult.stdout.trim()) {
|
|
528
|
+
return { atDesiredState: false, desiredDigest };
|
|
529
|
+
}
|
|
530
|
+
const runningImage = imageResult.stdout.trim();
|
|
531
|
+
// 3. Check readyReplicas
|
|
532
|
+
const replicasResult = await wrap([
|
|
533
|
+
'--context', context,
|
|
534
|
+
'get', 'deploy', HOST_CP_DEPLOYMENT_NAME,
|
|
535
|
+
'-n', K8S_NAMESPACE,
|
|
536
|
+
'-o', 'jsonpath={.status.readyReplicas}',
|
|
537
|
+
], { timeout: 10_000 });
|
|
538
|
+
const readyReplicas = parseInt(replicasResult.stdout.trim() || '0', 10);
|
|
539
|
+
// 4. Cluster is at desired state iff images match AND deployment is healthy.
|
|
540
|
+
const atDesiredState = runningImage === desiredImage && readyReplicas >= 1;
|
|
541
|
+
return { atDesiredState, desiredDigest };
|
|
542
|
+
}
|
|
482
543
|
/**
|
|
483
544
|
* Main entrypoint for the kubernetes upgrade path.
|
|
484
545
|
*
|
|
@@ -505,6 +566,43 @@ export async function runUpgradeKubernetes(opts = {}, deps = {}) {
|
|
|
505
566
|
}
|
|
506
567
|
// skipped: dir exists or bundle absent (dev context) — no output, continue.
|
|
507
568
|
}
|
|
569
|
+
// ── Pre-step 0b: early-exit probe — skip all 8 steps on healthy re-run ──
|
|
570
|
+
// Reads desired host-cp image from 50-deployment.yaml, compares to running
|
|
571
|
+
// deployment. When they match AND readyReplicas >= 1, nothing has changed:
|
|
572
|
+
// all 8 steps are no-ops, so we skip them and report in <1s.
|
|
573
|
+
// Only active when --force-refresh-manifests is NOT set (that flag is the
|
|
574
|
+
// explicit "re-run everything" escape hatch).
|
|
575
|
+
if (!opts.forceRefreshManifests) {
|
|
576
|
+
// Need a context for the probe — resolve it first with a lightweight helper.
|
|
577
|
+
const earlyCtxResolved = resolveKubectlContext(deps.configPath);
|
|
578
|
+
let earlyCtx = null;
|
|
579
|
+
if (earlyCtxResolved.error === undefined) {
|
|
580
|
+
earlyCtx = earlyCtxResolved.context;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
// Try auto-pin (same logic as the main flow below).
|
|
584
|
+
const autoPin = (deps.autoPinImpl ?? autoPinKubectlContext)({ configPath: deps.configPath });
|
|
585
|
+
if (autoPin.context !== undefined) {
|
|
586
|
+
earlyCtx = autoPin.context;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (earlyCtx !== null) {
|
|
590
|
+
const probe = await probeClusterAlreadyAtDesiredState(earlyCtx, manifestsDir, deps);
|
|
591
|
+
if (probe.atDesiredState) {
|
|
592
|
+
const digest = probe.desiredDigest ?? '(unknown)';
|
|
593
|
+
stdout.write(`${pc.green('✓')} cluster already at desired state — host-cp ${pc.dim(digest)} ready, skipping 8-step flow\n`);
|
|
594
|
+
// Still emit the instrumentation event so dashboards track re-runs.
|
|
595
|
+
const emitImpl = deps.emitImpl ?? emitUpgradeComplete;
|
|
596
|
+
emitImpl({
|
|
597
|
+
substrate: 'kubernetes',
|
|
598
|
+
duration_ms: Date.now() - startMs,
|
|
599
|
+
flavor: 'k3s',
|
|
600
|
+
skipped: true,
|
|
601
|
+
}, deps.emitOpts ?? {});
|
|
602
|
+
return { exitCode: 0, summary: `cluster already at desired state (host-cp ${digest})` };
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
508
606
|
// ── Step 0.4: R3-C — create/update ghcr-pull imagePullSecret ────────
|
|
509
607
|
// RELOCATED (R4-W2-A): this step previously ran before ensureK8sBootstrap,
|
|
510
608
|
// but the `olam` namespace doesn't exist yet on a fresh cluster — `kubectl
|
|
@@ -607,6 +705,11 @@ export async function runUpgradeKubernetes(opts = {}, deps = {}) {
|
|
|
607
705
|
rotateSecrets: opts.rotateSecrets === true,
|
|
608
706
|
}, {
|
|
609
707
|
kubectlWrapImpl: deps.kubectlWrapImpl,
|
|
708
|
+
// Route per-manifest progress through spinner.text so each file
|
|
709
|
+
// updates in-place instead of printing a new line (B4 spam fix).
|
|
710
|
+
onProgress: (label) => {
|
|
711
|
+
step05Spinner.text = `Bootstrapping olam namespace + RBAC + secrets (B4)${label}`;
|
|
712
|
+
},
|
|
610
713
|
...(deps.k8sBootstrapDeps ?? {}),
|
|
611
714
|
});
|
|
612
715
|
if (bootstrap.exitCode !== 0) {
|
|
@@ -868,7 +971,28 @@ export async function runUpgradeKubernetes(opts = {}, deps = {}) {
|
|
|
868
971
|
}
|
|
869
972
|
else {
|
|
870
973
|
stderr.write(`${pc.red('error:')} host-cp did not report engine=kubernetes.\n` +
|
|
871
|
-
`
|
|
974
|
+
` The /health port-forward succeeded but no X-Olam-Engine header was returned —\n` +
|
|
975
|
+
` usually means the pod isn't Ready yet, is crashlooping, or failed to pull its image.\n` +
|
|
976
|
+
` Pod-state diagnostics follow (share the FULL block when asking for help):\n`);
|
|
977
|
+
// Surface the actual pod state. The two commands below are read-only +
|
|
978
|
+
// safe to run multiple times. Output is captured + redirected to stderr
|
|
979
|
+
// so the operator sees it inline with the failure.
|
|
980
|
+
const wrap = deps.kubectlWrapImpl ?? kubectlWrap;
|
|
981
|
+
try {
|
|
982
|
+
const getPods = await wrap(['-n', 'olam', 'get', 'pods', '-l', 'app=olam-host-cp', '-o', 'wide'], { timeout: 5_000 });
|
|
983
|
+
stderr.write(`\n${pc.dim('--- kubectl -n olam get pods -l app=olam-host-cp ---')}\n` +
|
|
984
|
+
`${getPods.stdout || getPods.stderr || '(no output)'}\n`);
|
|
985
|
+
const describe = await wrap(['-n', 'olam', 'describe', 'pod', '-l', 'app=olam-host-cp'], { timeout: 5_000 });
|
|
986
|
+
// Tail the last 40 lines — Events block sits at the bottom and is
|
|
987
|
+
// the highest-signal slice (ImagePullBackOff / FailedScheduling /
|
|
988
|
+
// CrashLoopBackOff all surface there).
|
|
989
|
+
const tail = (describe.stdout || '').split('\n').slice(-40).join('\n');
|
|
990
|
+
stderr.write(`\n${pc.dim('--- kubectl -n olam describe pod (tail 40 lines, includes Events) ---')}\n` +
|
|
991
|
+
`${tail || '(no output)'}\n\n`);
|
|
992
|
+
}
|
|
993
|
+
catch (err) {
|
|
994
|
+
stderr.write(` ${pc.dim('(kubectl diagnostics unavailable: ' + err.message + ')')}\n`);
|
|
995
|
+
}
|
|
872
996
|
}
|
|
873
997
|
return { exitCode: 1, summary: 'health check failed — engine mismatch' };
|
|
874
998
|
}
|