@automagik/omni 2.260504.2 → 2.260505.2

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.
@@ -61,6 +61,14 @@ export interface DoctorOptions {
61
61
  fix?: boolean;
62
62
  json?: boolean;
63
63
  verbose?: boolean;
64
+ /**
65
+ * Force read-only mode. When true, any `fix: true` is suppressed and
66
+ * `runDoctor` only collects the check report. Used by `omni update`'s
67
+ * post-update maintenance hook (`runPostUpdateMaintenance`) so the
68
+ * probe stays read-only — `omni doctor --fix` remains the explicit
69
+ * operator action for repair.
70
+ */
71
+ dryRun?: boolean;
64
72
  }
65
73
  /**
66
74
  * Narrow shape of a pm2 process entry from `pm2 jlist`. We only care about
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,8 @@
22
22
  */
23
23
  import { Command } from 'commander';
24
24
  import { type Config } from '../config.js';
25
+ import { type ParallelInstallReport } from '../update-diagnostics.js';
26
+ import { type DoctorReport, runDoctor } from './doctor.js';
25
27
  export type UpdateChannel = 'latest' | 'next';
26
28
  /**
27
29
  * Resolve the npm dist-tag to install. Priority:
@@ -44,11 +46,26 @@ export interface HealthBody {
44
46
  * trigger a spurious mismatch.
45
47
  */
46
48
  export declare function normalizeVersion(version: string): string;
49
+ /**
50
+ * Reasons the verify step can be skipped without a server probe. Aligned
51
+ * byte-for-byte with the genie-side `VerifyResult.skipped.reason` shape so
52
+ * cross-CLI diagnostics tooling can read either repo's output uniformly
53
+ * (see `.genie/wishes/update-unify-stages/SHARED-DESIGN.md`).
54
+ *
55
+ * - `no-restart`: operator passed `--no-restart`, so no service was touched.
56
+ * - `no-verify-flag`: operator passed `--no-verify`, restart ran but probe was suppressed.
57
+ * - `no-running-services`: no tracked PM2 services were online before the install.
58
+ */
59
+ export type VerifySkipReason = 'no-restart' | 'no-verify-flag' | 'no-running-services';
47
60
  /**
48
61
  * Result of the pure 3-step update verification. Exported so tests can
49
62
  * exercise the logic without mocking pm2 / fetch / process.exit.
63
+ *
64
+ * Public-shape parity with the genie wish — each variant's keys match
65
+ * `automagik-dev/genie` exactly so a shared diagnostics consumer can decode
66
+ * either CLI's output without per-repo branching.
50
67
  */
51
- export type UpdateVerifyResult = {
68
+ export type VerifyResult = {
52
69
  kind: 'ok';
53
70
  cliVersion: string;
54
71
  serverVersion: string;
@@ -61,22 +78,155 @@ export type UpdateVerifyResult = {
61
78
  serverVersion: string | null;
62
79
  } | {
63
80
  kind: 'auth-invalid';
81
+ } | {
82
+ kind: 'skipped';
83
+ reason: VerifySkipReason;
64
84
  };
65
85
  /**
66
- * Pure decision function for update verification. Given the raw inputs
67
- * (health body + key-valid flag + CLI version + port), return a tagged
68
- * union describing the outcome. The caller decides how to render and
69
- * whether to exit non-zero.
86
+ * @deprecated Use {@link VerifyResult}. Retained for backward compatibility
87
+ * with any external consumer that imported the original name.
88
+ */
89
+ export type UpdateVerifyResult = VerifyResult;
90
+ /**
91
+ * Args accepted by {@link decideVerify}. Either `skipReason` is set (the
92
+ * verify step short-circuits to `{ kind: 'skipped' }`) or all of `latest` /
93
+ * `apiPort` / `healthBody` / `keyValid` are provided for the full decision.
70
94
  */
71
- export declare function decideUpdateVerify(args: {
95
+ export type DecideVerifyArgs = {
72
96
  latest: string;
73
97
  apiPort: number;
74
98
  healthBody: HealthBody | null;
75
99
  keyValid: boolean;
76
- }): UpdateVerifyResult;
100
+ skipReason?: undefined;
101
+ } | {
102
+ skipReason: VerifySkipReason;
103
+ };
104
+ /**
105
+ * Pure decision function for update verification. Given the raw inputs
106
+ * (health body + key-valid flag + CLI version + port), return a tagged
107
+ * union describing the outcome. The caller decides how to render and
108
+ * whether to exit non-zero.
109
+ *
110
+ * When `skipReason` is provided, the function short-circuits to
111
+ * `{ kind: 'skipped', reason }` without inspecting the other fields. This
112
+ * is the path used by `--no-restart`, `--no-verify`, and the
113
+ * no-running-services case.
114
+ */
115
+ export declare function decideVerify(args: DecideVerifyArgs): VerifyResult;
116
+ /**
117
+ * @deprecated Use {@link decideVerify}. Pointer-equal alias retained for
118
+ * backward compatibility with any external consumer that imported the
119
+ * original name.
120
+ */
121
+ export declare const decideUpdateVerify: typeof decideVerify;
77
122
  /** Error message strings — exported for tests and documentation. */
78
123
  export declare function updateErrorVersionMismatch(cli: string, server: string | null): string;
79
124
  export declare const UPDATE_ERROR_AUTH_INVALID = "Auth key invalid after restart. Run: omni doctor --fix";
125
+ /**
126
+ * Detect a parallel npm-global install of `@automagik/omni`. Omni doesn't
127
+ * support npm-global server (we install via `bun add -g`), but a parallel
128
+ * install hides stale binaries on PATH and confuses `which omni`. When
129
+ * detected, the operator gets a one-line warning naming the offending path
130
+ * with the recommended remediation: `npm uninstall -g @automagik/omni`.
131
+ *
132
+ * Pure-ish: probes `npm root -g`, then checks if `<root>/@automagik/omni`
133
+ * exists. Never throws — when `npm` is absent the probe simply reports
134
+ * `skipped: 'npm-not-on-path'` and the warning never fires.
135
+ *
136
+ * Exported for tests so we can exercise both paths without spawning npm.
137
+ */
138
+ export declare function detectParallelNpmGlobalInstall(deps?: {
139
+ npmRoot?: () => string | null;
140
+ exists?: (p: string) => boolean;
141
+ }): ParallelInstallReport;
142
+ /**
143
+ * Reasons the post-update maintenance hook can be skipped.
144
+ *
145
+ * - `cli-flag`: operator passed `--skip-maintenance`.
146
+ * - `env`: `OMNI_UPDATE_SKIP_MAINTENANCE` was set in the environment.
147
+ * - `verify-failed`: upstream `decideVerify` outcome was not `ok`, so probing
148
+ * further is pointless (the operator already has a
149
+ * `Run: omni doctor` pointer from the verify step).
150
+ */
151
+ export type MaintenanceSkipReason = 'cli-flag' | 'env' | 'verify-failed';
152
+ /**
153
+ * Outcome of a single post-update maintenance run. Public-shape parity with
154
+ * the genie wish — the same field names land in diagnostics so a shared
155
+ * consumer can read either CLI's output uniformly.
156
+ *
157
+ * - `completed`: `runDoctor` returned a report (regardless of WARN/FAIL counts).
158
+ * - `failed`: `runDoctor` threw; the call was non-blocking, exit code stays 0.
159
+ * - `skipped`: maintenance was opted out (flag/env) or upstream verify wasn't OK.
160
+ */
161
+ export type MaintenanceOutcome = 'completed' | 'failed' | 'skipped';
162
+ /**
163
+ * Captured shape of the post-update maintenance hook. Always populated, even
164
+ * when skipped — diagnostics (Group 4) consumes the same struct on every
165
+ * code path so the JSON file shape is invariant.
166
+ */
167
+ export interface MaintenanceReport {
168
+ outcome: MaintenanceOutcome;
169
+ /** Wall time spent inside `runDoctor` (or 0 when skipped). */
170
+ durationMs: number;
171
+ /** Present only when `outcome === 'completed'`. */
172
+ doctorReport?: DoctorReport;
173
+ /** Present only when `outcome === 'skipped'`. */
174
+ skipReason?: MaintenanceSkipReason;
175
+ /** Present only when `outcome === 'failed'` — the thrown error message. */
176
+ error?: string;
177
+ }
178
+ /** Env-var name that opts out of the post-update maintenance hook. */
179
+ export declare const OMNI_UPDATE_SKIP_MAINTENANCE_ENV = "OMNI_UPDATE_SKIP_MAINTENANCE";
180
+ /**
181
+ * Resolve the effective skip reason for the post-update maintenance hook.
182
+ * Returns `null` when maintenance should run. Pure — exported for tests so
183
+ * we can exercise the precedence logic without spinning up `runDoctor`.
184
+ *
185
+ * Precedence (first match wins):
186
+ * 1. `verify-failed` — upstream verify wasn't `ok`; nothing to probe.
187
+ * 2. `cli-flag` — operator passed `--skip-maintenance`.
188
+ * 3. `env` — `OMNI_UPDATE_SKIP_MAINTENANCE` is set to a truthy value.
189
+ *
190
+ * The truthy check for the env var matches the same loose semantics as
191
+ * `--yes`/CI flags elsewhere: any non-empty value other than `0`/`false`
192
+ * counts as opt-out.
193
+ */
194
+ export declare function resolveMaintenanceSkipReason(args: {
195
+ verifyOk: boolean;
196
+ skipMaintenance: boolean | undefined;
197
+ env: Record<string, string | undefined>;
198
+ }): MaintenanceSkipReason | null;
199
+ /**
200
+ * Format the one-line summary printed after a completed maintenance run.
201
+ * Pure — exported so tests can lock the exact shape and so diagnostics
202
+ * (Group 4) can reuse it without re-deriving the format.
203
+ *
204
+ * Shape: `Maintenance: <ok> ok, <warn> warn, <fail> fail`.
205
+ */
206
+ export declare function formatMaintenanceSummary(report: DoctorReport): string;
207
+ /**
208
+ * Run the post-update maintenance hook — a read-only `omni doctor` sweep
209
+ * that captures a `DoctorReport` for diagnostics.
210
+ *
211
+ * The call is non-blocking by contract: any thrown error is captured into
212
+ * the returned `MaintenanceReport` (`outcome: 'failed'`) and the caller
213
+ * proceeds with exit code 0. This matches the shared exit-code contract
214
+ * (see `SHARED-DESIGN.md` §4.5: "Maintenance failed (non-blocking) → 0,
215
+ * with banner warning").
216
+ *
217
+ * `runDoctor({ json: true, dryRun: true })` is the canonical call shape:
218
+ * `dryRun: true` defeats any accidental `fix: true` injection so the probe
219
+ * never mutates pm2 / config / DB state. `omni doctor --fix` remains the
220
+ * explicit operator action for repair (decision #4).
221
+ *
222
+ * `runDoctorImpl` is injected for tests so we can stub the doctor surface
223
+ * without monkey-patching the module — module-level mocks leak across
224
+ * test files in bun and would pollute `doctor.test.ts`.
225
+ */
226
+ export declare function runPostUpdateMaintenance(args: {
227
+ skipReason: MaintenanceSkipReason | null;
228
+ runDoctorImpl?: typeof runDoctor;
229
+ }): Promise<MaintenanceReport>;
80
230
  /**
81
231
  * Halt updates that would cross the phase-2 default-flip cutoff on hosts
82
232
  * still running legacy embedded pgserve without the canonical binary
@@ -90,5 +240,10 @@ export declare function checkCanonicalPgservePreflight(args: {
90
240
  useCanonicalPgserve: boolean | undefined;
91
241
  pgserveOnPath: boolean;
92
242
  }): string | null;
243
+ /**
244
+ * Parse the `--skip-cleanup=name1,name2` value into a Set. Empty / undefined
245
+ * yields an empty set. Whitespace and empty entries are tolerated.
246
+ */
247
+ export declare function parseSkipCleanupList(value: string | undefined): Set<string>;
93
248
  export declare function createUpdateCommand(): Command;
94
249
  //# sourceMappingURL=update.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,MAAM,EAA4C,MAAM,cAAc,CAAC;AA8BrF,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAQ5G;AAiID,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,kBAAkB,CAarB;AAED,oEAAoE;AACpE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAErF;AAED,eAAO,MAAM,yBAAyB,2DAA2D,CAAC;AA+MlG;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,MAAM,GAAG,IAAI,CAkChB;AAuFD,wBAAgB,mBAAmB,IAAI,OAAO,CAoD7C"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -78229,7 +78229,7 @@ var require_path = __commonJS((exports) => {
78229
78229
  function isAbsolute(path) {
78230
78230
  return path.charAt(0) === "/";
78231
78231
  }
78232
- function join16(...args) {
78232
+ function join18(...args) {
78233
78233
  return normalizePath(args.join("/"));
78234
78234
  }
78235
78235
  function dirname6(path) {
@@ -78254,7 +78254,7 @@ var require_path = __commonJS((exports) => {
78254
78254
  exports.basename = basename6;
78255
78255
  exports.dirname = dirname6;
78256
78256
  exports.isAbsolute = isAbsolute;
78257
- exports.join = join16;
78257
+ exports.join = join18;
78258
78258
  exports.normalizePath = normalizePath;
78259
78259
  exports.relative = relative;
78260
78260
  exports.resolve = resolve4;
@@ -100994,12 +100994,12 @@ var require_instrumentation9 = __commonJS((exports) => {
100994
100994
  pendingMetrics.forEach((m2) => m2());
100995
100995
  return result;
100996
100996
  }).catch((reason) => {
100997
- let errorMessage;
100997
+ let errorMessage2;
100998
100998
  let errorType = semantic_conventions_1.ERROR_TYPE_VALUE_OTHER;
100999
100999
  if (typeof reason === "string" || reason === undefined) {
101000
- errorMessage = reason;
101000
+ errorMessage2 = reason;
101001
101001
  } else if (typeof reason === "object" && Object.prototype.hasOwnProperty.call(reason, "message")) {
101002
- errorMessage = reason.message;
101002
+ errorMessage2 = reason.message;
101003
101003
  errorType = reason.constructor.name;
101004
101004
  }
101005
101005
  pendingMetrics.forEach((m2) => m2(errorType));
@@ -101007,7 +101007,7 @@ var require_instrumentation9 = __commonJS((exports) => {
101007
101007
  span.setAttribute(semantic_conventions_1.ATTR_ERROR_TYPE, errorType);
101008
101008
  span.setStatus({
101009
101009
  code: api_1.SpanStatusCode.ERROR,
101010
- message: errorMessage
101010
+ message: errorMessage2
101011
101011
  });
101012
101012
  });
101013
101013
  throw reason;
@@ -114211,7 +114211,7 @@ import { fileURLToPath } from "url";
114211
114211
  // package.json
114212
114212
  var package_default = {
114213
114213
  name: "@automagik/omni",
114214
- version: "2.260504.2",
114214
+ version: "2.260505.2",
114215
114215
  description: "LLM-optimized CLI for Omni",
114216
114216
  type: "module",
114217
114217
  bin: {
@@ -119684,7 +119684,7 @@ async function runDoctor(options, depsOverride) {
119684
119684
  const deps = depsOverride ?? productionDeps();
119685
119685
  let checks = await runAllChecks(deps);
119686
119686
  const fixesApplied = [];
119687
- if (options.fix) {
119687
+ if (options.fix && options.dryRun !== true) {
119688
119688
  const phase1 = await runPhase1MigrationFix(deps, checks, fixesApplied);
119689
119689
  checks = phase1.checks;
119690
119690
  await runPhase2Fixes(deps, checks, phase1.canonicalFailed, fixesApplied);
@@ -125829,10 +125829,11 @@ function createTurnsCommand() {
125829
125829
  }
125830
125830
 
125831
125831
  // src/commands/update.ts
125832
+ import { existsSync as existsSync17 } from "fs";
125833
+ import { join as join16 } from "path";
125832
125834
  import { createInterface as createInterface3 } from "readline";
125833
125835
  init_source();
125834
125836
  init_config();
125835
- init_output();
125836
125837
 
125837
125838
  // src/sidecar-cleanup.ts
125838
125839
  var SIDECAR_BASENAME = "nats-reply-sidecar.mjs";
@@ -126009,8 +126010,185 @@ function formatCleanupSummary(result) {
126009
126010
  return lines.join(`
126010
126011
  `);
126011
126012
  }
126012
- function cleanupSucceeded(result) {
126013
- return result.pm2Failed.length === 0 && result.rawFailed.length === 0;
126013
+
126014
+ // src/legacy-cleanup.ts
126015
+ function createNatsReplySidecarArtifact() {
126016
+ let lastResult = null;
126017
+ return {
126018
+ name: "nats-reply-sidecar",
126019
+ async detect() {
126020
+ return true;
126021
+ },
126022
+ async cleanup() {
126023
+ const result = await cleanupSidecars();
126024
+ lastResult = result;
126025
+ const removed = [];
126026
+ const warnings = [];
126027
+ for (const m2 of result.pm2Stopped) {
126028
+ removed.push(`pm2:${m2.name.length > 0 ? m2.name : `pm_id=${m2.pmId}`}`);
126029
+ }
126030
+ for (const m2 of result.rawKilled) {
126031
+ removed.push(`pid:${m2.pid}`);
126032
+ }
126033
+ for (const m2 of result.pm2Failed) {
126034
+ warnings.push(`pm2-failed:${m2.name.length > 0 ? m2.name : `pm_id=${m2.pmId}`}`);
126035
+ }
126036
+ for (const m2 of result.rawFailed) {
126037
+ warnings.push(`raw-failed:pid=${m2.pid}`);
126038
+ }
126039
+ return { removed, warnings };
126040
+ },
126041
+ summary() {
126042
+ return lastResult === null ? "" : formatCleanupSummary(lastResult);
126043
+ }
126044
+ };
126045
+ }
126046
+ var REGISTRY = [createNatsReplySidecarArtifact()];
126047
+ function errorMessage(error2) {
126048
+ return error2 instanceof Error ? error2.message : String(error2);
126049
+ }
126050
+ function erroredOutcome(name, phase, error2) {
126051
+ const message2 = errorMessage(error2);
126052
+ return {
126053
+ name,
126054
+ state: "errored",
126055
+ removed: [],
126056
+ warnings: [`${phase}-threw:${message2}`],
126057
+ summary: "",
126058
+ error: message2
126059
+ };
126060
+ }
126061
+ async function runArtifact(artifact) {
126062
+ let detected;
126063
+ try {
126064
+ detected = await artifact.detect();
126065
+ } catch (error2) {
126066
+ return erroredOutcome(artifact.name, "detect", error2);
126067
+ }
126068
+ if (!detected) {
126069
+ return {
126070
+ name: artifact.name,
126071
+ state: "not-detected",
126072
+ removed: [],
126073
+ warnings: [],
126074
+ summary: ""
126075
+ };
126076
+ }
126077
+ try {
126078
+ const { removed, warnings } = await artifact.cleanup();
126079
+ return {
126080
+ name: artifact.name,
126081
+ state: "ran",
126082
+ removed,
126083
+ warnings,
126084
+ summary: artifact.summary()
126085
+ };
126086
+ } catch (error2) {
126087
+ return erroredOutcome(artifact.name, "cleanup", error2);
126088
+ }
126089
+ }
126090
+ async function cleanupLegacyArtifacts(skipList) {
126091
+ const outcomes = [];
126092
+ const skipped = [];
126093
+ for (const artifact of REGISTRY) {
126094
+ if (skipList.has(artifact.name)) {
126095
+ skipped.push(artifact.name);
126096
+ outcomes.push({
126097
+ name: artifact.name,
126098
+ state: "skipped",
126099
+ removed: [],
126100
+ warnings: [],
126101
+ summary: ""
126102
+ });
126103
+ continue;
126104
+ }
126105
+ outcomes.push(await runArtifact(artifact));
126106
+ }
126107
+ const succeeded = outcomes.every((o2) => o2.state !== "errored" && (o2.state !== "ran" || o2.warnings.length === 0));
126108
+ return { outcomes, succeeded, skipped };
126109
+ }
126110
+
126111
+ // src/commands/update.ts
126112
+ init_output();
126113
+
126114
+ // src/update-diagnostics.ts
126115
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
126116
+ import { homedir as homedir11 } from "os";
126117
+ import { join as join15 } from "path";
126118
+ var UPDATE_DIAGNOSTICS_SCHEMA_VERSION = 1;
126119
+ function createDiagnostics(args) {
126120
+ const startedAt = new Date().toISOString();
126121
+ return {
126122
+ schemaVersion: UPDATE_DIAGNOSTICS_SCHEMA_VERSION,
126123
+ startedAt,
126124
+ finishedAt: startedAt,
126125
+ cli: { runningVersion: args.runningVersion, channel: args.channel },
126126
+ registry: { latestVersion: null },
126127
+ preflight: { ran: false, blocked: false },
126128
+ install: { attempted: false, succeeded: null, targetVersion: null },
126129
+ restart: { attempted: false, succeeded: null, services: [] },
126130
+ verify: null,
126131
+ cleanups: null,
126132
+ maintenance: null,
126133
+ parallelNpmGlobal: null,
126134
+ recentLogSignals: [],
126135
+ exitCode: 0
126136
+ };
126137
+ }
126138
+ function getDiagnosticsDir() {
126139
+ const base = process.env.OMNI_CONFIG_DIR ?? join15(homedir11(), ".omni");
126140
+ return join15(base, "logs");
126141
+ }
126142
+ function getDiagnosticsPath(startedAt) {
126143
+ const safe = startedAt.replace(/:/g, "-");
126144
+ return join15(getDiagnosticsDir(), `update-diagnostics-${safe}.json`);
126145
+ }
126146
+ function tailFileLines(path, maxLines) {
126147
+ if (!existsSync16(path))
126148
+ return [];
126149
+ try {
126150
+ const raw2 = readFileSync11(path, "utf8");
126151
+ const lines = raw2.split(/\r?\n/);
126152
+ if (lines.length > 0 && lines[lines.length - 1] === "")
126153
+ lines.pop();
126154
+ return lines.slice(-maxLines);
126155
+ } catch {
126156
+ return [];
126157
+ }
126158
+ }
126159
+ function collectRecentLogSignals(maxLinesPerStream = 30, logPathsFor = getPm2LogPaths) {
126160
+ const signals2 = [];
126161
+ for (const name of Object.values(PM2_PROCESSES)) {
126162
+ const { out, error: error2 } = logPathsFor(name);
126163
+ const outLines = tailFileLines(out, maxLinesPerStream);
126164
+ if (outLines.length > 0) {
126165
+ signals2.push({ source: name, stream: "out", lines: outLines });
126166
+ }
126167
+ const errLines = tailFileLines(error2, maxLinesPerStream);
126168
+ if (errLines.length > 0) {
126169
+ signals2.push({ source: name, stream: "error", lines: errLines });
126170
+ }
126171
+ }
126172
+ return signals2;
126173
+ }
126174
+ function writeDiagnostics(state, exitCode) {
126175
+ state.exitCode = exitCode;
126176
+ state.finishedAt = new Date().toISOString();
126177
+ if (state.recentLogSignals.length === 0) {
126178
+ state.recentLogSignals = collectRecentLogSignals();
126179
+ }
126180
+ const dir = getDiagnosticsDir();
126181
+ const path = getDiagnosticsPath(state.startedAt);
126182
+ try {
126183
+ if (!existsSync16(dir)) {
126184
+ mkdirSync10(dir, { recursive: true, mode: 448 });
126185
+ }
126186
+ writeFileSync9(path, `${JSON.stringify(state, null, 2)}
126187
+ `, { mode: 384 });
126188
+ return path;
126189
+ } catch {
126190
+ return null;
126191
+ }
126014
126192
  }
126015
126193
 
126016
126194
  // src/commands/update.ts
@@ -126111,7 +126289,10 @@ async function restartPm2Services(processNames) {
126111
126289
  function normalizeVersion(version2) {
126112
126290
  return version2.split("+")[0] ?? version2;
126113
126291
  }
126114
- function decideUpdateVerify(args) {
126292
+ function decideVerify(args) {
126293
+ if (args.skipReason !== undefined) {
126294
+ return { kind: "skipped", reason: args.skipReason };
126295
+ }
126115
126296
  const cliVersion = normalizeVersion(args.latest);
126116
126297
  if (args.healthBody === null) {
126117
126298
  return { kind: "health-unreachable", apiPort: args.apiPort };
@@ -126166,52 +126347,158 @@ function printVerifyBanner(latest) {
126166
126347
  console.log(`${source_default.green("\u2713")} Server: v${latest} (healthy)`);
126167
126348
  console.log(`${source_default.green("\u2713")} Auth: key valid`);
126168
126349
  }
126169
- async function runSidecarCleanup() {
126170
- const cleanupSpinner = ora("Checking for legacy nats-reply-sidecar processes...").start();
126171
- const result = await cleanupSidecars();
126350
+ function printVerifySkippedBanner(latest) {
126351
+ console.log(`${source_default.green("\u2713")} CLI: v${latest}`);
126352
+ console.log(`${source_default.yellow("-")} Server: v${latest} (skipped)`);
126353
+ console.log(`${source_default.yellow("-")} Auth: skipped`);
126354
+ }
126355
+ function detectParallelNpmGlobalInstall(deps) {
126356
+ const npmRootFn = deps?.npmRoot ?? defaultNpmRoot;
126357
+ const existsFn = deps?.exists ?? existsSync17;
126358
+ let root;
126359
+ try {
126360
+ root = npmRootFn();
126361
+ } catch {
126362
+ return { detected: false, skipped: "npm-root-failed" };
126363
+ }
126364
+ if (root === null || root.length === 0) {
126365
+ return { detected: false, skipped: "npm-not-on-path" };
126366
+ }
126367
+ const candidate = join16(root, "@automagik", "omni");
126368
+ if (existsFn(candidate)) {
126369
+ return { detected: true, path: candidate };
126370
+ }
126371
+ return { detected: false };
126372
+ }
126373
+ function defaultNpmRoot() {
126374
+ try {
126375
+ const result = Bun.spawnSync({
126376
+ cmd: ["npm", "root", "-g"],
126377
+ stdout: "pipe",
126378
+ stderr: "pipe",
126379
+ timeout: 5000
126380
+ });
126381
+ if (result.exitCode !== 0)
126382
+ return null;
126383
+ const text3 = new TextDecoder().decode(result.stdout).trim();
126384
+ return text3.length > 0 ? text3 : null;
126385
+ } catch {
126386
+ return null;
126387
+ }
126388
+ }
126389
+ async function runLegacyCleanup(skipList) {
126390
+ const cleanupSpinner = ora("Checking for legacy artifacts to clean up...").start();
126391
+ const report = await cleanupLegacyArtifacts(skipList);
126172
126392
  cleanupSpinner.stop();
126173
- const summary = formatCleanupSummary(result);
126174
- if (summary.length > 0) {
126175
- console.log(summary);
126393
+ for (const outcome of report.outcomes) {
126394
+ if (outcome.state === "ran" && outcome.summary.length > 0) {
126395
+ console.log(outcome.summary);
126396
+ }
126176
126397
  }
126177
- return result;
126398
+ return report;
126178
126399
  }
126179
- async function restartServicesAndVerify(servicesToRestart, latest, options3) {
126400
+ var OMNI_UPDATE_SKIP_MAINTENANCE_ENV = "OMNI_UPDATE_SKIP_MAINTENANCE";
126401
+ function resolveMaintenanceSkipReason(args) {
126402
+ if (!args.verifyOk)
126403
+ return "verify-failed";
126404
+ if (args.skipMaintenance === true)
126405
+ return "cli-flag";
126406
+ const raw2 = args.env[OMNI_UPDATE_SKIP_MAINTENANCE_ENV];
126407
+ if (raw2 !== undefined && raw2 !== "" && raw2 !== "0" && raw2.toLowerCase() !== "false") {
126408
+ return "env";
126409
+ }
126410
+ return null;
126411
+ }
126412
+ function formatMaintenanceSummary(report) {
126413
+ const { ok: ok2, warn: warn2, fail } = report.summary;
126414
+ return `Maintenance: ${ok2} ok, ${warn2} warn, ${fail} fail`;
126415
+ }
126416
+ async function runPostUpdateMaintenance(args) {
126417
+ if (args.skipReason !== null) {
126418
+ return { outcome: "skipped", durationMs: 0, skipReason: args.skipReason };
126419
+ }
126420
+ const impl = args.runDoctorImpl ?? runDoctor;
126421
+ const startedAt = Date.now();
126422
+ try {
126423
+ const doctorReport = await impl({ json: true, dryRun: true });
126424
+ return {
126425
+ outcome: "completed",
126426
+ durationMs: Date.now() - startedAt,
126427
+ doctorReport
126428
+ };
126429
+ } catch (err2) {
126430
+ return {
126431
+ outcome: "failed",
126432
+ durationMs: Date.now() - startedAt,
126433
+ error: err2 instanceof Error ? err2.message : String(err2)
126434
+ };
126435
+ }
126436
+ }
126437
+ async function restartServicesAndVerify(servicesToRestart, latest, options3, diagnostics, finalize) {
126180
126438
  const apiPort = loadServerConfig().port;
126439
+ diagnostics.restart.attempted = true;
126440
+ diagnostics.restart.services = [...servicesToRestart];
126181
126441
  const restartSpinner = ora("Restarting services...").start();
126182
126442
  const restartSucceeded = await restartPm2Services(servicesToRestart);
126183
126443
  restartSpinner.stop();
126444
+ diagnostics.restart.succeeded = restartSucceeded;
126184
126445
  if (!restartSucceeded) {
126185
126446
  warn(`omni CLI updated to v${latest}, but one or more service restarts failed. Run \`omni status\`.`);
126186
- process.exit(1);
126187
- }
126188
- let sidecarCleanupResult = null;
126189
- if (options3.sidecarCleanup) {
126190
- sidecarCleanupResult = await runSidecarCleanup();
126447
+ finalize(1);
126448
+ }
126449
+ let cleanupReport = null;
126450
+ if (options3.legacyCleanup) {
126451
+ cleanupReport = await runLegacyCleanup(options3.skipList);
126452
+ diagnostics.cleanups = cleanupReport;
126453
+ }
126454
+ if (options3.skipVerify) {
126455
+ const result2 = decideVerify({ skipReason: "no-verify-flag" });
126456
+ diagnostics.verify = result2;
126457
+ printVerifySkippedBanner(latest);
126458
+ if (cleanupReport !== null && !cleanupReport.succeeded) {
126459
+ warn("One or more legacy artifacts could not be cleaned up automatically. " + "See messages above and docs/migration/nats-genie-sidecar-decommission.md.");
126460
+ }
126461
+ const maintenance = await runPostUpdateMaintenance({ skipReason: "verify-failed" });
126462
+ diagnostics.maintenance = maintenance;
126463
+ return;
126191
126464
  }
126192
126465
  const verifySpinner = ora("Verifying server version...").start();
126193
126466
  const healthBody = await fetchHealthBody(apiPort, UPDATE_HEALTH_TIMEOUT_MS);
126194
126467
  verifySpinner.stop();
126195
126468
  const keyValid = healthBody !== null ? await validateStoredKey(apiPort) : false;
126196
- const result = decideUpdateVerify({ latest, apiPort, healthBody, keyValid });
126469
+ const result = decideVerify({ latest, apiPort, healthBody, keyValid });
126470
+ diagnostics.verify = result;
126197
126471
  switch (result.kind) {
126198
- case "ok":
126472
+ case "ok": {
126199
126473
  printVerifyBanner(result.cliVersion);
126200
- if (sidecarCleanupResult !== null && !cleanupSucceeded(sidecarCleanupResult)) {
126201
- warn("One or more legacy nats-reply-sidecar processes could not be stopped automatically. " + "See messages above and docs/migration/nats-genie-sidecar-decommission.md.");
126474
+ if (cleanupReport !== null && !cleanupReport.succeeded) {
126475
+ warn("One or more legacy artifacts could not be cleaned up automatically. " + "See messages above and docs/migration/nats-genie-sidecar-decommission.md.");
126476
+ }
126477
+ const skipReason = resolveMaintenanceSkipReason({
126478
+ verifyOk: true,
126479
+ skipMaintenance: options3.skipMaintenance,
126480
+ env: process.env
126481
+ });
126482
+ const maintenance = await runPostUpdateMaintenance({ skipReason });
126483
+ diagnostics.maintenance = maintenance;
126484
+ if (maintenance.outcome === "completed" && maintenance.doctorReport) {
126485
+ info(formatMaintenanceSummary(maintenance.doctorReport));
126486
+ } else if (maintenance.outcome === "failed") {
126487
+ warn(`Maintenance: skipped (runDoctor failed: ${maintenance.error ?? "unknown error"})`);
126202
126488
  }
126203
126489
  return;
126490
+ }
126204
126491
  case "health-unreachable":
126205
126492
  warn(`omni CLI updated to v${latest}, but health check failed on port ${result.apiPort}. Run \`omni status\`.`);
126206
- process.exit(1);
126493
+ finalize(1);
126207
126494
  break;
126208
126495
  case "version-mismatch":
126209
126496
  console.error(`${source_default.red("\u2717")} ${updateErrorVersionMismatch(result.cliVersion, result.serverVersion)}`);
126210
- process.exit(1);
126497
+ finalize(1);
126211
126498
  break;
126212
126499
  case "auth-invalid":
126213
126500
  console.error(`${source_default.red("\u2717")} ${UPDATE_ERROR_AUTH_INVALID}`);
126214
- process.exit(1);
126501
+ finalize(1);
126215
126502
  break;
126216
126503
  }
126217
126504
  }
@@ -126288,20 +126575,35 @@ async function runUpdate(options3) {
126288
126575
  if (options3.next || options3.stable) {
126289
126576
  persistChannel(channel2);
126290
126577
  }
126578
+ const currentClean = VERSION.split("+")[0];
126579
+ const diagnostics = createDiagnostics({ runningVersion: currentClean, channel: channel2 });
126580
+ const finalize = (exitCode) => {
126581
+ const path = writeDiagnostics(diagnostics, exitCode);
126582
+ if (path !== null && process.env.OMNI_UPDATE_DIAGNOSTICS_VERBOSE === "1") {
126583
+ info(`Diagnostics written: ${path}`);
126584
+ }
126585
+ process.exit(exitCode);
126586
+ };
126587
+ const parallelInstall = detectParallelNpmGlobalInstall();
126588
+ diagnostics.parallelNpmGlobal = parallelInstall;
126589
+ if (parallelInstall.detected && parallelInstall.path) {
126590
+ warn(`Parallel npm-global install of ${PACKAGE_NAME} detected at ${parallelInstall.path}. This may shadow the bun-installed binary on PATH. Recommended: npm uninstall -g ${PACKAGE_NAME}`);
126591
+ }
126291
126592
  info(`Channel: ${channel2}${channel2 === "next" ? " (dev builds)" : " (stable)"}`);
126292
126593
  const versionSpinner = ora(`Checking ${channel2} version of ${PACKAGE_NAME}...`).start();
126293
126594
  const latest = await fetchLatestVersion(channel2);
126294
126595
  versionSpinner.stop();
126596
+ diagnostics.registry.latestVersion = latest;
126295
126597
  if (latest === null) {
126296
126598
  warn("Could not reach npm registry. Check your network connection and try again.");
126297
- process.exit(1);
126599
+ return finalize(1);
126298
126600
  }
126299
- const currentClean = VERSION.split("+")[0];
126300
126601
  if (currentClean === latest) {
126301
126602
  success(`Already up to date (v${latest}, channel: ${channel2})`);
126302
- process.exit(0);
126603
+ return finalize(0);
126303
126604
  }
126304
126605
  if (!options3.skipCanonicalPreflight) {
126606
+ diagnostics.preflight.ran = true;
126305
126607
  const serverConfig = loadServerConfig();
126306
126608
  const preflightError = checkCanonicalPgservePreflight({
126307
126609
  currentVersion: currentClean,
@@ -126310,8 +126612,11 @@ async function runUpdate(options3) {
126310
126612
  pgserveOnPath: isPgserveOnPath()
126311
126613
  });
126312
126614
  if (preflightError !== null) {
126615
+ diagnostics.preflight.blocked = true;
126616
+ diagnostics.preflight.reason = preflightError.split(`
126617
+ `)[0];
126313
126618
  warn(preflightError);
126314
- process.exit(1);
126619
+ return finalize(1);
126315
126620
  }
126316
126621
  }
126317
126622
  info(`Update available: v${currentClean} \u2192 v${latest} (${channel2})`);
@@ -126319,27 +126624,47 @@ async function runUpdate(options3) {
126319
126624
  const confirmed = await promptConfirm(`Update from v${currentClean} to v${latest}? [Y/n] `);
126320
126625
  if (!confirmed) {
126321
126626
  info("Update cancelled.");
126322
- process.exit(0);
126627
+ return finalize(0);
126323
126628
  }
126324
126629
  }
126325
126630
  const servicesToRestart = options3.restart !== false ? getRunningPm2Services() : [];
126326
126631
  const installSpinner = ora(`Updating ${PACKAGE_NAME}@${channel2}...`).start();
126632
+ diagnostics.install.attempted = true;
126633
+ diagnostics.install.targetVersion = latest;
126327
126634
  const installed = await installLatest(channel2);
126328
126635
  installSpinner.stop();
126636
+ diagnostics.install.succeeded = installed;
126329
126637
  if (!installed) {
126330
126638
  warn(`Installation failed. Your current version (v${currentClean}) is still intact.`);
126331
- process.exit(1);
126639
+ return finalize(1);
126640
+ }
126641
+ const sidecarCleanupExplicit = options3.sidecarCleanup === false;
126642
+ if (sidecarCleanupExplicit) {
126643
+ info("--no-sidecar-cleanup (deprecated alias for --no-legacy-cleanup)");
126332
126644
  }
126645
+ const legacyCleanupEnabled = options3.legacyCleanup !== false && options3.sidecarCleanup !== false;
126646
+ const skipList = parseSkipCleanupList(options3.skipCleanup);
126333
126647
  if (servicesToRestart.length > 0) {
126334
126648
  await restartServicesAndVerify(servicesToRestart, latest, {
126335
- sidecarCleanup: options3.sidecarCleanup !== false
126336
- });
126337
- return;
126338
- }
126649
+ legacyCleanup: legacyCleanupEnabled,
126650
+ skipList,
126651
+ skipMaintenance: options3.skipMaintenance === true,
126652
+ skipVerify: options3.verify === false
126653
+ }, diagnostics, finalize);
126654
+ return finalize(0);
126655
+ }
126656
+ diagnostics.verify = decideVerify({ skipReason: "no-restart" });
126339
126657
  success(`omni updated to v${latest}`);
126658
+ return finalize(0);
126659
+ }
126660
+ function parseSkipCleanupList(value) {
126661
+ if (!value)
126662
+ return new Set;
126663
+ const names = value.split(",").map((s2) => s2.trim()).filter((s2) => s2.length > 0);
126664
+ return new Set(names);
126340
126665
  }
126341
126666
  function createUpdateCommand() {
126342
- return new Command("update").description(`Update ${PACKAGE_NAME} to the latest version (restart only services already running)`).option("-y, --yes", "Skip confirmation prompts (non-interactive)").option("--no-restart", "Update CLI only; skip service restarts and verification").option("--no-sidecar-cleanup", "Skip the legacy nats-reply-sidecar.mjs cleanup step").option("--next", "Switch to dev builds (npm @next tag) and persist as default").option("--stable", "Switch to stable releases (npm @latest tag) and persist as default").option("--skip-canonical-preflight", "Bypass the canonical-pgserve phase-2 pre-flight (NOT recommended; for operators who pre-installed pgserve via a path the auto-detector misses)").addHelpText("after", `
126667
+ return new Command("update").description(`Update ${PACKAGE_NAME} to the latest version (restart only services already running)`).option("-y, --yes", "Skip confirmation prompts (non-interactive)").option("--no-restart", "Update CLI only; skip service restarts and verification").option("--no-verify", "Restart services but skip the post-restart probe (use when a release ships a broken /api/v2/health and operators need to roll forward)").option("--no-legacy-cleanup", "Skip every registered legacy-artifact cleanup (e.g. nats-reply-sidecar)").option("--no-sidecar-cleanup", "Deprecated alias for --no-legacy-cleanup").option("--skip-cleanup <names>", 'Comma-separated list of legacy-cleanup registry entries to skip (e.g. "nats-reply-sidecar")').option("--next", "Switch to dev builds (npm @next tag) and persist as default").option("--stable", "Switch to stable releases (npm @latest tag) and persist as default").option("--skip-canonical-preflight", "Bypass the canonical-pgserve phase-2 pre-flight (NOT recommended; for operators who pre-installed pgserve via a path the auto-detector misses)").option("--skip-maintenance", `Skip the post-update maintenance hook (read-only \`omni doctor\` sweep). Also honored via the ${OMNI_UPDATE_SKIP_MAINTENANCE_ENV} env var.`).addHelpText("after", `
126343
126668
  Channels:
126344
126669
  - stable (default) \u2014 tracks the npm @latest tag, bumped from main branch releases.
126345
126670
  - next (dev builds) \u2014 tracks the npm @next tag, bumped on every CI-green dev merge.
@@ -126365,14 +126690,38 @@ Behavior:
126365
126690
  "Server version mismatch: cli=v<X> server=v<Y>. Run: omni doctor"
126366
126691
  - On auth failure, exits non-zero with:
126367
126692
  "Auth key invalid after restart. Run: omni doctor --fix"
126368
- - After a successful restart, scans for any legacy nats-reply-sidecar.mjs
126369
- process (PM2-managed or raw) and stops it. The sidecar was an external
126370
- workaround for bugs that were fixed in #362; leaving it running causes
126371
- every agent reply to be delivered twice. Skippable with
126372
- --no-sidecar-cleanup. Manual runbook:
126693
+ - After a successful restart, runs every registered legacy-artifact cleanup.
126694
+ The day-one entry is nats-reply-sidecar: it scans for any legacy
126695
+ nats-reply-sidecar.mjs process (PM2-managed or raw) and stops it. The
126696
+ sidecar was an external workaround for bugs that were fixed in #362;
126697
+ leaving it running causes every agent reply to be delivered twice.
126698
+ Skip every cleanup with --no-legacy-cleanup, or skip a single registry
126699
+ entry with --skip-cleanup=<name1,name2>. The deprecated alias
126700
+ --no-sidecar-cleanup still works and behaves identically. Manual runbook:
126373
126701
  docs/migration/nats-genie-sidecar-decommission.md
126374
126702
  - Use --no-restart to skip restart + verification entirely. --no-restart
126375
- also skips sidecar cleanup; manage the sidecar manually.
126703
+ also skips legacy-artifact cleanup; manage those services manually.
126704
+ - Use --no-verify to restart services but skip the post-restart probe.
126705
+ The banner shows the new CLI version on both lines but tags Server /
126706
+ Auth as "(skipped)"; maintenance is auto-skipped because verify never
126707
+ confirmed health.
126708
+ - Detects parallel npm-global installs of ${PACKAGE_NAME} (omni installs
126709
+ via bun-global; an npm-global install hides stale binaries on PATH and
126710
+ confuses 'which omni'). When found, prints a one-line warning naming
126711
+ the offending path; recommended remediation: npm uninstall -g ${PACKAGE_NAME}.
126712
+ - Every invocation writes a diagnostics record to
126713
+ ~/.omni/logs/update-diagnostics-<iso>.json (schemaVersion: 1). Captures
126714
+ install attempt, registry probe, restart outcome, verify result, cleanup
126715
+ registry result, maintenance hook, and a tail of pm2 log signals. Set
126716
+ OMNI_UPDATE_DIAGNOSTICS_VERBOSE=1 to print the path on completion. A
126717
+ failed write never changes the update exit code.
126718
+ - On a successful restart + verify, runs a post-update maintenance hook:
126719
+ a read-only \`omni doctor\` sweep that prints a one-line summary
126720
+ ("Maintenance: <ok> ok, <warn> warn, <fail> fail"). The probe never
126721
+ mutates pm2 / config / DB state (\`omni doctor --fix\` remains the
126722
+ explicit operator action for repair). A failing maintenance call is
126723
+ non-fatal \u2014 exit code stays 0 with a banner warning. Skip with
126724
+ --skip-maintenance or ${OMNI_UPDATE_SKIP_MAINTENANCE_ENV}=1.
126376
126725
  - Verify runtime health after update with: omni status
126377
126726
  `).action(runUpdate);
126378
126727
  }
@@ -126484,9 +126833,9 @@ function createVoiceCommand() {
126484
126833
  const startTime = Date.now();
126485
126834
  let saveDir = "";
126486
126835
  if (opts.save) {
126487
- const { mkdirSync: mkdirSync10 } = await import("fs");
126836
+ const { mkdirSync: mkdirSync11 } = await import("fs");
126488
126837
  saveDir = opts.save;
126489
- mkdirSync10(saveDir, { recursive: true });
126838
+ mkdirSync11(saveDir, { recursive: true });
126490
126839
  info(`Saving audio to: ${saveDir}`);
126491
126840
  }
126492
126841
  ws.onopen = () => {
@@ -126710,17 +127059,17 @@ init_config();
126710
127059
  init_output();
126711
127060
 
126712
127061
  // src/manifest-pin.ts
126713
- import { existsSync as existsSync16, readFileSync as readFileSync11, renameSync as renameSync3, writeFileSync as writeFileSync9 } from "fs";
126714
- import { homedir as homedir11 } from "os";
126715
- import { join as join15 } from "path";
127062
+ import { existsSync as existsSync18, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
127063
+ import { homedir as homedir12 } from "os";
127064
+ import { join as join17 } from "path";
126716
127065
  var PACKAGE_NAME2 = "@automagik/omni";
126717
- var BUN_GLOBAL_MANIFEST = join15(homedir11(), ".bun", "install", "global", "package.json");
127066
+ var BUN_GLOBAL_MANIFEST = join17(homedir12(), ".bun", "install", "global", "package.json");
126718
127067
  function pinManifestEntry(manifestPath, exactVersion) {
126719
- if (!existsSync16(manifestPath))
127068
+ if (!existsSync18(manifestPath))
126720
127069
  return false;
126721
127070
  let manifest;
126722
127071
  try {
126723
- manifest = JSON.parse(readFileSync11(manifestPath, "utf-8"));
127072
+ manifest = JSON.parse(readFileSync12(manifestPath, "utf-8"));
126724
127073
  } catch {
126725
127074
  return false;
126726
127075
  }
@@ -126746,7 +127095,7 @@ function pinManifestEntry(manifestPath, exactVersion) {
126746
127095
  return false;
126747
127096
  const tmp = `${manifestPath}.tmp.${process.pid}`;
126748
127097
  try {
126749
- writeFileSync9(tmp, `${JSON.stringify(manifest, null, 2)}
127098
+ writeFileSync10(tmp, `${JSON.stringify(manifest, null, 2)}
126750
127099
  `, { mode: 420 });
126751
127100
  renameSync3(tmp, manifestPath);
126752
127101
  } catch {
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Legacy artifact cleanup registry.
3
+ *
4
+ * Generalizes the bespoke `cleanupSidecars()` step in `omni update` into a
5
+ * registry of `LegacyArtifact` entries so future deprecation cleanups (e.g.
6
+ * obsolete WhatsApp baileys session formats, dead pm2 process names, stale
7
+ * config files) drop in without code changes to the update orchestration.
8
+ *
9
+ * The day-one entry is `nats-reply-sidecar`, which wraps the existing
10
+ * `cleanupSidecars()` / `cleanupSucceeded()` / `formatCleanupSummary()`
11
+ * helpers in `sidecar-cleanup.ts`. Operator-facing output is byte-identical
12
+ * to the pre-registry behavior — the registry only changes the dispatch.
13
+ *
14
+ * Public shape mirrors the genie wish (independent code, identical signature
15
+ * per SHARED-DESIGN.md decision #3).
16
+ */
17
+ /**
18
+ * One legacy artifact eligible for cleanup. Implementations are stateful:
19
+ * `cleanup()` may stash the raw result so `summary()` can render it.
20
+ */
21
+ export interface LegacyArtifact {
22
+ readonly name: string;
23
+ /**
24
+ * Best-effort fast probe — return true if cleanup() should be run. Returning
25
+ * false is reserved for hard short-circuits (e.g. the artifact's namespace
26
+ * doesn't exist on this OS). If detection itself is cheap relative to
27
+ * cleanup, implementations may simply return true and let the empty-case
28
+ * path in cleanup() produce an empty summary.
29
+ */
30
+ detect(): Promise<boolean>;
31
+ /**
32
+ * Run the cleanup. Returns the user-visible artifacts (`removed`) and any
33
+ * non-fatal problems (`warnings`). Implementations should never throw —
34
+ * the registry treats a thrown error as "warning, succeeded=false".
35
+ */
36
+ cleanup(): Promise<{
37
+ removed: string[];
38
+ warnings: string[];
39
+ }>;
40
+ /**
41
+ * Human-readable multi-line summary of the most recent cleanup() call.
42
+ * Empty string when nothing happened (so the caller can suppress the block).
43
+ */
44
+ summary(): string;
45
+ }
46
+ /**
47
+ * Per-artifact outcome captured in the report.
48
+ */
49
+ export interface ArtifactOutcome {
50
+ readonly name: string;
51
+ readonly state: 'ran' | 'skipped' | 'not-detected' | 'errored';
52
+ readonly removed: string[];
53
+ readonly warnings: string[];
54
+ readonly summary: string;
55
+ readonly error?: string;
56
+ }
57
+ /**
58
+ * Aggregate cleanup outcome across the registry. Consumed by diagnostics
59
+ * (Group 4) and by the update banner ("warn loudly when a cleanup partially
60
+ * failed").
61
+ */
62
+ export interface CleanupReport {
63
+ readonly outcomes: ArtifactOutcome[];
64
+ /** True iff every ran artifact reported zero warnings AND nothing errored. */
65
+ readonly succeeded: boolean;
66
+ /** Names that were skipped via `skipList`. */
67
+ readonly skipped: string[];
68
+ }
69
+ /**
70
+ * The live registry. Order matters — artifacts run sequentially so an earlier
71
+ * cleanup's side effects are visible to a later one (e.g. stopping a process
72
+ * before deleting its config file).
73
+ *
74
+ * To add a new artifact:
75
+ * 1. Implement `LegacyArtifact` (idempotent, never-throws).
76
+ * 2. Append a factory call below.
77
+ * 3. Add a unit test that runs the registry round-trip.
78
+ * 4. Document the deprecation in `docs/migration/`.
79
+ */
80
+ export declare const REGISTRY: LegacyArtifact[];
81
+ export declare function cleanupLegacyArtifacts(skipList: Set<string>): Promise<CleanupReport>;
82
+ //# sourceMappingURL=legacy-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-cleanup.d.ts","sourceRoot":"","sources":["../src/legacy-cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC9D;;;OAGG;IACH,OAAO,IAAI,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;CAC5B;AA+CD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,EAAE,cAAc,EAAuC,CAAC;AA2D7E,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAuB1F"}
@@ -224688,7 +224688,7 @@ var init_sentry_scrub = __esm(() => {
224688
224688
  var require_package8 = __commonJS((exports, module) => {
224689
224689
  module.exports = {
224690
224690
  name: "@omni/api",
224691
- version: "2.260504.2",
224691
+ version: "2.260505.2",
224692
224692
  type: "module",
224693
224693
  exports: {
224694
224694
  ".": {
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Update diagnostics capture.
3
+ *
4
+ * Every `omni update` invocation writes a structured JSON report to
5
+ * `~/.omni/logs/update-diagnostics-<iso>.json` capturing the install attempt,
6
+ * registry probe, restart, verify outcome, cleanup registry result,
7
+ * maintenance hook, and a tail of recent pm2 log signals. The file is the
8
+ * canonical post-mortem artifact: operators paste it into bug reports; the
9
+ * shape mirrors the genie wish (independent code, parity per
10
+ * `SHARED-DESIGN.md` decision #3) so a shared diagnostics consumer can read
11
+ * either CLI's output uniformly. omni starts at `schemaVersion: 1` (decision
12
+ * #4 in the wish: per-repo schema versions are intentionally asymmetric).
13
+ *
14
+ * The writer is best-effort: any failure (no perms, disk full, etc.) is
15
+ * swallowed so update never exits non-zero because diagnostics couldn't be
16
+ * persisted.
17
+ */
18
+ import type { MaintenanceReport, UpdateChannel, VerifyResult } from './commands/update.js';
19
+ import type { CleanupReport } from './legacy-cleanup.js';
20
+ /** Stable schema version for omni-side update diagnostics. */
21
+ export declare const UPDATE_DIAGNOSTICS_SCHEMA_VERSION: 1;
22
+ /** Captured tail of one pm2 log file (best-effort). */
23
+ export interface LogSignal {
24
+ /** Tracked pm2 process name (e.g. `omni-v2-api`). */
25
+ source: string;
26
+ /** Which file (`out` or `error`). */
27
+ stream: 'out' | 'error';
28
+ /** Up to N most recent lines, oldest first. Empty when the file does not exist. */
29
+ lines: string[];
30
+ }
31
+ /**
32
+ * Outcome of the parallel npm-global install probe (Group 5a). Omni doesn't
33
+ * support npm-global server, but a parallel install hides stale binaries on
34
+ * PATH and confuses `which omni`. Captured here so a future bug report
35
+ * includes the smoking-gun path.
36
+ */
37
+ export interface ParallelInstallReport {
38
+ detected: boolean;
39
+ /** Filesystem path to the parallel install (when detected). */
40
+ path?: string;
41
+ /** Reason detection couldn't run, e.g. `npm` not on PATH. */
42
+ skipped?: string;
43
+ }
44
+ /** Top-level diagnostics record. */
45
+ export interface UpdateDiagnostics {
46
+ readonly schemaVersion: typeof UPDATE_DIAGNOSTICS_SCHEMA_VERSION;
47
+ /** ISO-8601 timestamp the run started at (filename derives from this). */
48
+ startedAt: string;
49
+ /** ISO-8601 timestamp diagnostics was finalized (= just before write). */
50
+ finishedAt: string;
51
+ cli: {
52
+ /** CLI version executing this update (the previous one — install hasn't restarted us). */
53
+ runningVersion: string;
54
+ channel: UpdateChannel;
55
+ };
56
+ registry: {
57
+ latestVersion: string | null;
58
+ };
59
+ preflight: {
60
+ /** Did the canonical-pgserve phase-2 check run? False when `--skip-canonical-preflight`. */
61
+ ran: boolean;
62
+ /** Did the check return a blocking error? When true, the run aborted. */
63
+ blocked: boolean;
64
+ /** First line of the rendered error message, when blocked. */
65
+ reason?: string;
66
+ };
67
+ install: {
68
+ attempted: boolean;
69
+ succeeded: boolean | null;
70
+ targetVersion: string | null;
71
+ };
72
+ restart: {
73
+ attempted: boolean;
74
+ succeeded: boolean | null;
75
+ services: string[];
76
+ };
77
+ verify: VerifyResult | null;
78
+ cleanups: CleanupReport | null;
79
+ maintenance: MaintenanceReport | null;
80
+ parallelNpmGlobal: ParallelInstallReport | null;
81
+ recentLogSignals: LogSignal[];
82
+ /** Final exit code — populated by the writer; useful for bug-report triage. */
83
+ exitCode: number;
84
+ }
85
+ /**
86
+ * Build a fresh diagnostics state with the invariant fields populated.
87
+ * Mutated in-place by `runUpdate` as it advances through the pipeline.
88
+ */
89
+ export declare function createDiagnostics(args: {
90
+ runningVersion: string;
91
+ channel: UpdateChannel;
92
+ }): UpdateDiagnostics;
93
+ /** Get the directory diagnostics are written to. Honors `OMNI_CONFIG_DIR`. */
94
+ export declare function getDiagnosticsDir(): string;
95
+ /**
96
+ * Compute the canonical filename for a diagnostics record. The ISO timestamp
97
+ * is sanitized to be filename-safe (`:` → `-`).
98
+ */
99
+ export declare function getDiagnosticsPath(startedAt: string): string;
100
+ /**
101
+ * Read the last `maxLines` lines of a UTF-8 text file. Best-effort: returns
102
+ * an empty array on any error (missing file, perms, decoding issue).
103
+ *
104
+ * Implementation notes:
105
+ * - We slurp the whole file (pm2 log files are small in steady state and
106
+ * log-rotated by pm2-logrotate; reading the whole thing is simpler than
107
+ * a reverse-byte tail and avoids edge cases on multi-byte UTF-8).
108
+ * - Trailing empty line from a trailing newline is dropped.
109
+ */
110
+ export declare function tailFileLines(path: string, maxLines: number): string[];
111
+ /**
112
+ * Collect the most recent log lines from each tracked pm2 process. Skips
113
+ * processes whose log files don't exist (i.e. that have never run on this
114
+ * host). Limited to `maxLinesPerStream` per file to keep diagnostics small.
115
+ *
116
+ * `logPathsFor` is injectable so tests can point at a tmp dir without
117
+ * touching the developer's real `~/.omni/logs` (pm2 log paths don't honor
118
+ * `OMNI_CONFIG_DIR` and we'd rather not change that surface here).
119
+ *
120
+ * This is best-effort and called from the diagnostics writer; never throws.
121
+ */
122
+ export declare function collectRecentLogSignals(maxLinesPerStream?: number, logPathsFor?: (name: string) => {
123
+ out: string;
124
+ error: string;
125
+ }): LogSignal[];
126
+ /**
127
+ * Persist the diagnostics record to `~/.omni/logs/update-diagnostics-*.json`.
128
+ * Returns the path that was written (or null on failure). Never throws —
129
+ * diagnostics is observability, not load-bearing for the update outcome.
130
+ *
131
+ * The exit code is captured into `state.exitCode` and the timestamp into
132
+ * `state.finishedAt` before serialization so the JSON file is a complete
133
+ * snapshot at write time.
134
+ */
135
+ export declare function writeDiagnostics(state: UpdateDiagnostics, exitCode: number): string | null;
136
+ //# sourceMappingURL=update-diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-diagnostics.d.ts","sourceRoot":"","sources":["../src/update-diagnostics.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260504.2",
3
+ "version": "2.260505.2",
4
4
  "description": "LLM-optimized CLI for Omni",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,15 +51,15 @@
51
51
  "qrcode-terminal": "^0.12.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@omni/api": "2.260504.1",
55
- "@omni/channel-discord": "2.260504.1",
56
- "@omni/channel-gupshup": "2.260504.1",
57
- "@omni/channel-sdk": "2.260504.1",
58
- "@omni/channel-slack": "2.260504.1",
59
- "@omni/channel-telegram": "2.260504.1",
60
- "@omni/channel-whatsapp": "2.260504.1",
61
- "@omni/core": "2.260504.1",
62
- "@omni/sdk": "2.260504.1",
54
+ "@omni/api": "2.260505.1",
55
+ "@omni/channel-discord": "2.260505.1",
56
+ "@omni/channel-gupshup": "2.260505.1",
57
+ "@omni/channel-sdk": "2.260505.1",
58
+ "@omni/channel-slack": "2.260505.1",
59
+ "@omni/channel-telegram": "2.260505.1",
60
+ "@omni/channel-whatsapp": "2.260505.1",
61
+ "@omni/core": "2.260505.1",
62
+ "@omni/sdk": "2.260505.1",
63
63
  "@types/node": "^22.10.3",
64
64
  "@types/qrcode-terminal": "^0.12.2",
65
65
  "typescript": "^5.7.3"