minecodex 0.2.2 → 1.0.1
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 +2 -0
- package/features/hide-upsell-banner/src/hide-upsell-banner.mjs +2 -1
- package/features/images/codex-feature.json +2 -2
- package/features/images/src/http-server.mjs +67 -1
- package/features/images/src/prompt-index.mjs +45 -10
- package/features/images/web/app.js +555 -0
- package/features/images/web/index.html +16 -1
- package/features/images/web/styles.css +80 -0
- package/package.json +1 -1
- package/packages/cli/src/commands.mjs +117 -73
- package/packages/cli/src/{install-progress.mjs → progress.mjs} +2 -2
- package/packages/runtime-host/src/codex-runtime.mjs +318 -21
- package/packages/runtime-host/src/feature-registry.mjs +7 -0
- package/packages/runtime-host/src/main.mjs +1 -1
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
--color-token-main-surface-primary: #fff;
|
|
5
5
|
--color-token-bg-primary: #f9f9f9;
|
|
6
6
|
--color-token-bg-secondary: color-mix(in srgb, #f9f9f9 92%, transparent);
|
|
7
|
+
--color-surface-tertiary: #f0f0f0;
|
|
8
|
+
--color-surface-elevated-secondary: #fff;
|
|
9
|
+
--color-border-subtle: color-mix(in oklab, #1a1c1f 4%, transparent);
|
|
10
|
+
--color-background-primary-ghost-hover: color-mix(in oklab, #1a1c1f 5%, transparent);
|
|
11
|
+
--color-chart-blue: #339cff;
|
|
12
|
+
--color-text-inverse: #fff;
|
|
7
13
|
--color-background-elevated-primary-opaque: #fff;
|
|
8
14
|
--color-background-panel: #f5f5f5;
|
|
9
15
|
--color-background-primary-solid: #1a1c1f;
|
|
@@ -48,6 +54,12 @@
|
|
|
48
54
|
--color-token-main-surface-primary: #181818;
|
|
49
55
|
--color-token-bg-primary: #141414;
|
|
50
56
|
--color-token-bg-secondary: color-mix(in srgb, #141414 92%, transparent);
|
|
57
|
+
--color-surface-tertiary: rgb(40 40 40);
|
|
58
|
+
--color-surface-elevated-secondary: rgb(45 45 45);
|
|
59
|
+
--color-border-subtle: rgb(255 255 255 / 4.2%);
|
|
60
|
+
--color-background-primary-ghost-hover: rgb(255 255 255 / 7.8%);
|
|
61
|
+
--color-chart-blue: #339cff;
|
|
62
|
+
--color-text-inverse: rgb(13 13 13);
|
|
51
63
|
--color-background-elevated-primary-opaque: #363636;
|
|
52
64
|
--color-background-panel: #232323;
|
|
53
65
|
--color-background-primary-solid: #fff;
|
|
@@ -76,6 +88,12 @@
|
|
|
76
88
|
--color-token-main-surface-primary: #181818;
|
|
77
89
|
--color-token-bg-primary: #141414;
|
|
78
90
|
--color-token-bg-secondary: color-mix(in srgb, #141414 92%, transparent);
|
|
91
|
+
--color-surface-tertiary: rgb(40 40 40);
|
|
92
|
+
--color-surface-elevated-secondary: rgb(45 45 45);
|
|
93
|
+
--color-border-subtle: rgb(255 255 255 / 4.2%);
|
|
94
|
+
--color-background-primary-ghost-hover: rgb(255 255 255 / 7.8%);
|
|
95
|
+
--color-chart-blue: #339cff;
|
|
96
|
+
--color-text-inverse: rgb(13 13 13);
|
|
79
97
|
--color-background-elevated-primary-opaque: #363636;
|
|
80
98
|
--color-background-panel: #232323;
|
|
81
99
|
--color-background-primary-solid: #fff;
|
|
@@ -810,6 +828,68 @@ h1,
|
|
|
810
828
|
background: var(--color-token-bg-primary);
|
|
811
829
|
}
|
|
812
830
|
|
|
831
|
+
/* Native-token image editor: tokens arrive from RuntimeHost with the active Codex theme. */
|
|
832
|
+
.image-editor { position: relative; background: var(--color-token-bg-primary); }
|
|
833
|
+
.image-editor [hidden] { display: none !important; }
|
|
834
|
+
.image-editor-controls { position: sticky; z-index: 6; top: 0; background: var(--color-token-bg-primary); }
|
|
835
|
+
.image-editor-toolbar { position: relative; display: flex; justify-content: center; min-height: 48px; padding: 8px 16px; }
|
|
836
|
+
.image-edit-actions, .image-edit-mode { display: flex; min-width: 0; align-items: center; gap: 2px; padding: 2px; overflow: hidden; border-radius: 9999px; background: color-mix(in srgb, var(--color-surface-tertiary) 95%, transparent); box-shadow: 0 0 0 1px var(--color-border-subtle), var(--shadow-md); backdrop-filter: blur(4px); }
|
|
837
|
+
.image-edit-action, .image-edit-mode button { display: inline-flex; height: 28px; align-items: center; justify-content: center; gap: 4px; border: 1px solid transparent; border-radius: 999px; padding: 0 6px; background: transparent; color: var(--color-text-foreground); font-family: var(--font-sans-default); font-size: var(--text-base); font-weight: var(--font-weight-normal); line-height: 18px; white-space: nowrap; cursor: pointer; }
|
|
838
|
+
.image-edit-action:hover, .image-edit-action[aria-expanded="true"], .image-edit-mode button:hover { background: var(--color-background-primary-ghost-hover); }
|
|
839
|
+
.image-edit-action:focus-visible, .image-edit-mode button:focus-visible, .image-zoom-trigger:focus-visible, .image-edit-menu button:focus-visible { outline: 1px solid var(--color-border-focus); outline-offset: 1px; }
|
|
840
|
+
.image-edit-mode button:disabled { color: var(--color-text-foreground-tertiary); cursor: default; }
|
|
841
|
+
.image-edit-mode > span { min-width: 0; overflow: hidden; padding: 0 6px; color: var(--color-text-foreground-tertiary); font-size: var(--text-base); line-height: 18px; text-overflow: ellipsis; white-space: nowrap; }
|
|
842
|
+
.image-edit-mode button { width: 28px; padding: 0; flex: 0 0 auto; }
|
|
843
|
+
.image-edit-mode button > svg { width: 16px; height: 16px; fill: currentColor; }
|
|
844
|
+
.image-edit-mode button[aria-label="Redo"] > svg { transform: scaleX(-1); }
|
|
845
|
+
.image-edit-mode [data-edit-cancel] { color: var(--color-text-foreground-tertiary); }
|
|
846
|
+
.image-edit-mode [data-edit-cancel] > svg { width: 14px; height: 14px; }
|
|
847
|
+
.image-edit-mode [data-edit-send] { width: auto; padding: 0 6px; background: var(--color-chart-blue); color: var(--color-text-inverse); }
|
|
848
|
+
.image-edit-mode [data-edit-send]:hover { background: color-mix(in srgb, var(--color-chart-blue) 90%, transparent); }
|
|
849
|
+
.image-edit-mode [data-edit-send]:disabled { color: var(--color-text-inverse); opacity: .4; }
|
|
850
|
+
.image-zoom-trigger { position: absolute; top: 8px; right: 10px; height: 28px; display: inline-flex; align-items: center; gap: 2px; border: 1px solid transparent; border-radius: var(--radius-lg); padding: 0 8px; background: transparent; color: var(--color-text-foreground-tertiary); cursor: pointer; font-family: var(--font-sans-default); font-size: var(--text-sm); font-weight: var(--font-weight-normal); line-height: 20px; font-variant-numeric: tabular-nums; }
|
|
851
|
+
.image-zoom-trigger:hover, .image-zoom-trigger[aria-expanded="true"] { background: var(--color-background-primary-ghost-hover); }
|
|
852
|
+
.image-zoom-trigger [data-zoom-chevron] { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.5; opacity: .5; }
|
|
853
|
+
.image-edit-menu { position: absolute; z-index: 8; width: 160px; padding: 4px; border: 0; border-radius: var(--radius-xl); background: color-mix(in srgb, var(--color-surface-elevated-secondary) 90%, transparent); box-shadow: 0 0 0 .5px var(--color-border), var(--shadow-xl); backdrop-filter: blur(4px); }
|
|
854
|
+
.image-edit-menu button { display: flex; width: 100%; align-items: center; gap: 6px; border: 0; border-radius: var(--radius-lg); padding: 5px 8px; background: transparent; color: var(--color-text-foreground); font-family: var(--font-sans-default); font-size: var(--text-sm); font-weight: var(--font-weight-normal); line-height: var(--text-sm--line-height); text-align: left; cursor: pointer; }
|
|
855
|
+
.image-edit-menu button:hover { background: var(--color-background-primary-ghost-hover); }
|
|
856
|
+
.image-ratio-label { min-width: 0; flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
857
|
+
.image-ratio-label small { margin: 0; color: var(--color-text-foreground-tertiary); font-size: inherit; }
|
|
858
|
+
.image-ratio-icon { display: block; width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; opacity: .75; }
|
|
859
|
+
.image-menu-check { width: 17px; height: 17px; flex: 0 0 auto; fill: currentColor; opacity: .75; }
|
|
860
|
+
.image-menu-check.is-hidden { visibility: hidden; }
|
|
861
|
+
.image-menu-separator { height: 1px; margin: 0 8px; background: var(--color-border); }
|
|
862
|
+
.image-zoom-menu { width: 136px; padding: 6px; border-radius: 10px; }
|
|
863
|
+
.image-zoom-menu button { border-radius: 6px; padding: 5px 5px 5px 8px; line-height: 20px; }
|
|
864
|
+
.image-editor-stage { position: relative; overflow: auto; touch-action: none; }
|
|
865
|
+
.image-editor-stage img { transform-origin: center; transition: transform 120ms ease-out; }
|
|
866
|
+
.image-edit-mask, .image-comment-layer { position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
867
|
+
.image-edit-mask { cursor: crosshair; opacity: .5; color: var(--color-text-accent); }
|
|
868
|
+
.image-comment-layer { pointer-events: none; }
|
|
869
|
+
.image-comment-marker { position: absolute; display: flex; width: 30px; height: 30px; align-items: center; justify-content: center; border: 0; padding: 0; background: transparent; color: var(--color-text-accent); font-family: var(--font-sans-default); transform: translate(-50%, -50%); }
|
|
870
|
+
button.image-comment-marker { pointer-events: auto; cursor: pointer; }
|
|
871
|
+
.image-comment-marker > svg { position: absolute; inset: 0; width: 30px; height: 30px; fill: currentColor; overflow: visible; stroke: white; stroke-width: 1.65; }
|
|
872
|
+
.image-comment-marker > span { position: relative; z-index: 1; color: white; font-size: 10px; font-weight: 700; line-height: 10px; transform: translate(-1px, -1px); }
|
|
873
|
+
.image-comment-popover { position: absolute; z-index: 4; width: min(294px, calc(100% - 16px)); height: 44px; padding: 8px 8px 8px 16px; border: 0; border-radius: 22px; background: var(--color-surface-elevated-secondary); box-shadow: 0 0 0 1px var(--color-border-subtle), var(--shadow-md); }
|
|
874
|
+
.image-comment-popover input { width: 100%; height: 28px; border: 0; padding: 0; outline: 0; background: transparent; color: var(--color-token-input-foreground); font-family: var(--font-sans-default); font-size: var(--text-base); font-weight: var(--font-weight-normal); line-height: 21px; }
|
|
875
|
+
.image-comment-popover.is-existing-comment { height: 120px; padding: 8px 12px 8px 16px; }
|
|
876
|
+
.image-comment-popover textarea { display: block; width: 100%; height: 60px; resize: none; border: 0; padding: 2px 0 0; outline: 0; background: transparent; color: var(--color-token-input-foreground); font-family: var(--font-sans-default); font-size: var(--text-base); font-weight: var(--font-weight-normal); line-height: 24px; }
|
|
877
|
+
.image-comment-editor-footer { height: 44px; display: flex; align-items: end; justify-content: space-between; }
|
|
878
|
+
.image-comment-editor-actions { display: flex; gap: 6px; }
|
|
879
|
+
.image-comment-editor-footer button { height: 28px; border: 1px solid var(--color-border); border-radius: 999px; padding: 0 8px; background: color-mix(in srgb, var(--color-token-main-surface-primary) 96%, transparent); color: var(--color-text-foreground); font-family: var(--font-sans-default); font-size: var(--text-sm); font-weight: var(--font-weight-normal); line-height: 18px; cursor: pointer; }
|
|
880
|
+
.image-comment-editor-footer button:hover { background: var(--color-background-primary-ghost-hover); }
|
|
881
|
+
.image-comment-editor-footer .image-comment-delete { width: 28px; padding: 0; border-color: transparent; background: transparent; }
|
|
882
|
+
.image-comment-delete > svg { width: 20px; height: 20px; fill: currentColor; }
|
|
883
|
+
.image-comment-editor-footer .image-comment-save { border-color: transparent; background: var(--color-background-primary-solid); color: var(--color-text-button-primary); }
|
|
884
|
+
.image-comment-editor-footer button:disabled { cursor: default; opacity: .4; }
|
|
885
|
+
.image-brush-slider { position: absolute; z-index: 3; top: 50%; left: 16px; transform: translateY(-50%) rotate(-90deg); transform-origin: center; }
|
|
886
|
+
.image-brush-slider input { accent-color: var(--color-text-accent); }
|
|
887
|
+
.image-edit-menu[data-resize-menu] { top: 48px; left: 50%; transform: translateX(-50%); }
|
|
888
|
+
.image-zoom-menu { top: 48px; right: 10px; }
|
|
889
|
+
.image-edit-action > svg { width: 16px; height: 16px; flex: 0 0 auto; fill: currentColor; }
|
|
890
|
+
.image-edit-action > [data-native-image-icon="resize"] { width: 18px; height: 18px; }
|
|
891
|
+
@media (max-width: 520px) { .image-edit-action span { display: none; } .image-edit-action { width: 28px; padding: 0; } }
|
|
892
|
+
|
|
813
893
|
.drawer-figure img {
|
|
814
894
|
max-width: 100%;
|
|
815
895
|
max-height: min(68vh, 720px);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { assertNodeSupported, MacPlatformAdapter } from "./platform.mjs";
|
|
5
5
|
import { ensureConfig, ensureControlToken, readConfig } from "./config.mjs";
|
|
6
6
|
import { createControlServer } from "./control-server.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { createProgressReporter } from "./progress.mjs";
|
|
8
8
|
import { createNpmAdapter } from "./npm-adapter.mjs";
|
|
9
9
|
import { resolvePaths } from "./paths.mjs";
|
|
10
10
|
import { RuntimeManager, writeServicePid } from "./runtime-manager.mjs";
|
|
@@ -119,7 +119,7 @@ export async function confirmRestartNow({ input = process.stdin, terminalOutput
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
async function installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, confirmRestart, nodeVersion = process.versions.node }) {
|
|
122
|
-
const reporter =
|
|
122
|
+
const reporter = createProgressReporter({ output });
|
|
123
123
|
reporter.heading("Installing MineCodex");
|
|
124
124
|
let previousService = null;
|
|
125
125
|
try {
|
|
@@ -167,21 +167,30 @@ async function installCommand({ paths, platform, cliPath, output, serviceProbe,
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
async function openCommand({ paths, platform, serviceProbe, fetchImpl }) {
|
|
170
|
+
async function openCommand({ paths, platform, output, serviceProbe, fetchImpl }) {
|
|
171
171
|
const waitForServiceImpl = serviceProbe?.waitForService ?? waitForService;
|
|
172
|
+
let reporter = null;
|
|
172
173
|
if (!(await waitForServiceImpl(DEFAULT_CONTROL_PORT, { fetchImpl, timeoutMs: 750 }))) {
|
|
173
174
|
if (!(await serviceInstalled(platform, paths))) throw new Error("MineCodex is not installed. Run `mcx install` first.");
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
reporter = createProgressReporter({ output });
|
|
176
|
+
reporter.heading("Opening MineCodex");
|
|
177
|
+
await reporter.step(1, 2, "Starting the background helper", async () => {
|
|
178
|
+
await platform.startService(paths);
|
|
179
|
+
if (!(await waitForServiceImpl(DEFAULT_CONTROL_PORT, { fetchImpl }))) throw new Error("MineCodex service could not be started.");
|
|
180
|
+
});
|
|
176
181
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
const openControlPanel = async () => {
|
|
183
|
+
const exchangeResponse = await authorizedFetch(paths, "/api/auth/exchange", {
|
|
184
|
+
method: "POST",
|
|
185
|
+
body: "{}",
|
|
186
|
+
}, { fetchImpl });
|
|
187
|
+
if (!exchangeResponse.ok) throw new Error(`MineCodex browser exchange failed: HTTP ${exchangeResponse.status}`);
|
|
188
|
+
const { exchange } = await exchangeResponse.json();
|
|
189
|
+
if (!exchange) throw new Error("MineCodex browser exchange did not return a short-lived value.");
|
|
190
|
+
await platform.openBrowser(`${controlOrigin()}/auth?exchange=${encodeURIComponent(exchange)}`);
|
|
191
|
+
};
|
|
192
|
+
if (reporter) await reporter.step(2, 2, "Opening the control panel", openControlPanel);
|
|
193
|
+
else await openControlPanel();
|
|
185
194
|
}
|
|
186
195
|
|
|
187
196
|
function featureStatusText(feature) {
|
|
@@ -239,95 +248,130 @@ async function statusCommand({ paths, platform, jsonOutput, output, fetchImpl })
|
|
|
239
248
|
}
|
|
240
249
|
|
|
241
250
|
async function restartCommand({ paths, output, fetchImpl }) {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
const reporter = createProgressReporter({ output });
|
|
252
|
+
reporter.heading("Restarting MineCodex");
|
|
253
|
+
await reporter.step(1, 1, "Restarting Codex and applying plugin changes", async () => {
|
|
254
|
+
const response = await authorizedFetch(paths, "/api/restart", { method: "POST", body: "{}" }, { fetchImpl });
|
|
255
|
+
if (!response.ok) throw new Error(`MineCodex restart failed: HTTP ${response.status}`);
|
|
256
|
+
});
|
|
257
|
+
reporter.heading("MineCodex restarted and saved plugin switches are now applied.");
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
async function uninstallCommand({ paths, platform, purge, output, cliPath }) {
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
const reporter = createProgressReporter({ output });
|
|
262
|
+
reporter.heading("Uninstalling MineCodex");
|
|
263
|
+
const previousService = await reporter.step(1, 2, "Checking the current installation", async () => {
|
|
264
|
+
platform.assertInstallSupported();
|
|
265
|
+
return serviceSnapshot(platform, paths);
|
|
266
|
+
});
|
|
250
267
|
let wasManaged = false;
|
|
251
268
|
try {
|
|
252
|
-
await
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
await reporter.step(2, 2, purge ? "Removing MineCodex and its data" : "Removing the MineCodex integration", async () => {
|
|
270
|
+
await platform.removeService(paths);
|
|
271
|
+
wasManaged = await platform.terminateOwnedCodex(paths);
|
|
272
|
+
await platform.assertServiceStopped?.();
|
|
273
|
+
await platform.assertOwnedCodexStopped?.(paths);
|
|
274
|
+
if (purge) {
|
|
275
|
+
const targets = [paths.supportDir, paths.notesDataDir, paths.imagesDataDir];
|
|
276
|
+
for (const target of targets) {
|
|
277
|
+
if (!path.isAbsolute(target) || target === paths.homeDir || target.length <= paths.homeDir.length + 2) {
|
|
278
|
+
throw new Error(`Refusing unsafe purge target: ${target}`);
|
|
279
|
+
}
|
|
261
280
|
}
|
|
281
|
+
for (const target of targets) await rm(target, { recursive: true, force: true });
|
|
262
282
|
}
|
|
263
|
-
|
|
264
|
-
output("MineCodex integration and MineCodex-owned data were removed.");
|
|
265
|
-
} else {
|
|
266
|
-
output("MineCodex integration was removed. Plugin data was preserved.");
|
|
267
|
-
}
|
|
283
|
+
});
|
|
268
284
|
} catch (error) {
|
|
269
285
|
let rollbackError = null;
|
|
286
|
+
reporter.heading("Uninstall failed. Restoring the background helper.");
|
|
270
287
|
try {
|
|
271
|
-
await
|
|
288
|
+
await reporter.step(1, 1, "Restoring the background helper", async () => {
|
|
289
|
+
await restoreService(platform, previousService, { cliPath, paths });
|
|
290
|
+
});
|
|
272
291
|
} catch (failure) {
|
|
273
292
|
rollbackError = failure;
|
|
274
293
|
}
|
|
275
294
|
throw appendRollbackError(error, rollbackError);
|
|
276
295
|
}
|
|
277
|
-
|
|
278
|
-
|
|
296
|
+
reporter.heading(purge
|
|
297
|
+
? "MineCodex integration and MineCodex-owned data were removed."
|
|
298
|
+
: "MineCodex integration was removed. Plugin data was preserved.");
|
|
299
|
+
if (wasManaged) {
|
|
300
|
+
await reporter.step(1, 1, "Reopening Codex", async () => platform.openNativeCodex());
|
|
301
|
+
}
|
|
302
|
+
reporter.heading("Codex generated images and files referenced by Notes were not modified.");
|
|
279
303
|
}
|
|
280
304
|
|
|
281
305
|
async function updateCommand({ paths, platform, cliPath, output, npm = createNpmAdapter(), serviceProbe, fetchImpl }) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
306
|
+
const reporter = createProgressReporter({ output });
|
|
307
|
+
reporter.heading("Updating MineCodex");
|
|
308
|
+
const latest = await reporter.step(1, 5, "Checking for updates", async () => {
|
|
309
|
+
platform.assertInstallSupported();
|
|
310
|
+
const version = await npm.viewLatestVersion();
|
|
311
|
+
if (!version) throw new Error("Could not resolve the latest MineCodex version.");
|
|
312
|
+
return version;
|
|
313
|
+
});
|
|
285
314
|
if (latest === packageMetadata.version) {
|
|
286
|
-
|
|
315
|
+
reporter.heading(`MineCodex ${packageMetadata.version} is already up to date.`);
|
|
287
316
|
return;
|
|
288
317
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
318
|
+
let wasInstalled;
|
|
319
|
+
let previousService;
|
|
320
|
+
const rollback = await reporter.step(2, 5, "Backing up the current version", async () => {
|
|
321
|
+
wasInstalled = await serviceInstalled(platform, paths);
|
|
322
|
+
previousService = wasInstalled
|
|
323
|
+
? (await serviceSnapshot(platform, paths) ?? { running: true, plist: { exists: true } })
|
|
324
|
+
: null;
|
|
325
|
+
return npm.createRollbackArchive(paths.packageRoot);
|
|
326
|
+
});
|
|
294
327
|
const rollbackPath = typeof rollback === "string" ? rollback : rollback.archivePath;
|
|
295
328
|
if (!rollbackPath) throw new Error("Could not create a rollback archive for the installed MineCodex version.");
|
|
296
329
|
try {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
await npm.installGlobal("minecodex@latest");
|
|
302
|
-
if (wasInstalled) {
|
|
303
|
-
await platform.installService({ cliPath, paths, start: previousService.running });
|
|
304
|
-
if (previousService.running) {
|
|
305
|
-
const waitForServiceImpl = serviceProbe?.waitForService ?? waitForService;
|
|
306
|
-
const waitForManagedReadyImpl = serviceProbe?.waitForManagedReady ?? waitForManagedReady;
|
|
307
|
-
if (!(await waitForServiceImpl(DEFAULT_CONTROL_PORT, { fetchImpl }))
|
|
308
|
-
|| !(await waitForManagedReadyImpl(paths, { fetchImpl }))) {
|
|
309
|
-
throw new Error("MineCodex service did not become ready after update.");
|
|
310
|
-
}
|
|
311
|
-
} else {
|
|
330
|
+
await reporter.step(3, 5, "Pausing the background helper", async () => {
|
|
331
|
+
if (wasInstalled && previousService?.running) {
|
|
332
|
+
await platform.stopService(paths, { ignoreErrors: false });
|
|
312
333
|
await platform.assertServiceStopped?.();
|
|
313
334
|
}
|
|
314
|
-
}
|
|
315
|
-
|
|
335
|
+
});
|
|
336
|
+
await reporter.step(4, 5, `Installing MineCodex ${latest}`, async () => {
|
|
337
|
+
await npm.installGlobal("minecodex@latest");
|
|
338
|
+
});
|
|
339
|
+
await reporter.step(5, 5, "Restoring the background helper", async () => {
|
|
340
|
+
if (wasInstalled) {
|
|
341
|
+
await platform.installService({ cliPath, paths, start: previousService.running });
|
|
342
|
+
if (previousService.running) {
|
|
343
|
+
const waitForServiceImpl = serviceProbe?.waitForService ?? waitForService;
|
|
344
|
+
const waitForManagedReadyImpl = serviceProbe?.waitForManagedReady ?? waitForManagedReady;
|
|
345
|
+
if (!(await waitForServiceImpl(DEFAULT_CONTROL_PORT, { fetchImpl }))
|
|
346
|
+
|| !(await waitForManagedReadyImpl(paths, { fetchImpl }))) {
|
|
347
|
+
throw new Error("MineCodex service did not become ready after update.");
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
await platform.assertServiceStopped?.();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
reporter.heading(`MineCodex updated from ${packageMetadata.version} to ${latest}.`);
|
|
316
355
|
} catch (error) {
|
|
317
356
|
let rollbackError = null;
|
|
357
|
+
reporter.heading("Update failed. Restoring the previous version.");
|
|
318
358
|
try {
|
|
319
|
-
await
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
|
|
359
|
+
await reporter.step(1, 2, "Restoring the previous version", async () => {
|
|
360
|
+
await npm.installGlobal(rollbackPath);
|
|
361
|
+
});
|
|
362
|
+
await reporter.step(2, 2, "Restoring the background helper", async () => {
|
|
363
|
+
await restoreService(platform, previousService, { cliPath, paths });
|
|
364
|
+
if (wasInstalled && previousService?.running) {
|
|
365
|
+
const waitForServiceImpl = serviceProbe?.waitForService ?? waitForService;
|
|
366
|
+
const waitForManagedReadyImpl = serviceProbe?.waitForManagedReady ?? waitForManagedReady;
|
|
367
|
+
if (!(await waitForServiceImpl(DEFAULT_CONTROL_PORT, { fetchImpl }))
|
|
368
|
+
|| !(await waitForManagedReadyImpl(paths, { fetchImpl }))) {
|
|
369
|
+
throw new Error("Rolled-back MineCodex service did not become ready.");
|
|
370
|
+
}
|
|
371
|
+
} else if (wasInstalled && !previousService?.running) {
|
|
372
|
+
await platform.assertServiceStopped?.();
|
|
327
373
|
}
|
|
328
|
-
}
|
|
329
|
-
await platform.assertServiceStopped?.();
|
|
330
|
-
}
|
|
374
|
+
});
|
|
331
375
|
} catch (failure) {
|
|
332
376
|
rollbackError = failure;
|
|
333
377
|
}
|
|
@@ -397,7 +441,7 @@ export async function runCli(argv, {
|
|
|
397
441
|
if (["--help", "-h", "help"].includes(command)) return printHelp(output);
|
|
398
442
|
if (["--version", "-v"].includes(command)) return output(packageMetadata.version);
|
|
399
443
|
if (command === "install") return installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, confirmRestart, nodeVersion });
|
|
400
|
-
if (command === "open" || command === "gui") return openCommand({ paths, platform, serviceProbe, fetchImpl });
|
|
444
|
+
if (command === "open" || command === "gui") return openCommand({ paths, platform, output, serviceProbe, fetchImpl });
|
|
401
445
|
if (command === "status") return statusCommand({ paths, platform, jsonOutput: args.includes("--json"), output, fetchImpl });
|
|
402
446
|
if (command === "restart") return restartCommand({ paths, output, fetchImpl });
|
|
403
447
|
if (command === "uninstall" || command === "remove") {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2
2
|
|
|
3
|
-
function
|
|
3
|
+
function createProgressReporter({ output = console.log, terminalOutput = process.stdout } = {}) {
|
|
4
4
|
// 只有真实终端才显示 spinner;测试或脚本环境保持普通逐行文本。
|
|
5
5
|
const interactive = output === console.log && Boolean(terminalOutput?.isTTY);
|
|
6
6
|
let timer = null;
|
|
@@ -53,4 +53,4 @@ function createInstallReporter({ output = console.log, terminalOutput = process.
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export {
|
|
56
|
+
export { createProgressReporter };
|