@wrongstack/cli 0.305.0 → 0.305.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/dist/{auth-K7CYVVKH.js → auth-EPERLKYI.js} +3 -3
- package/dist/{chunk-C5NXM2SI.js → chunk-2H47YMCV.js} +5 -1
- package/dist/{chunk-FJVCVZIV.js → chunk-5KFLLTSQ.js} +2 -2
- package/dist/{cli-main-W3T56YCY.js → cli-main-6YE423OH.js} +91 -29
- package/dist/diag-doctor-WTBHPOMB.js +329 -0
- package/dist/{execution-YMBD7YWC.js → execution-QYFWDD3Y.js} +3 -18
- package/dist/index.js +5 -5
- package/dist/subcommands/handlers/daemon-inventory.d.ts +41 -0
- package/package.json +24 -23
- package/dist/diag-doctor-ZF5BF2MK.js +0 -169
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
runOAuthLoginKind,
|
|
14
14
|
runOAuthLoginMenu,
|
|
15
15
|
validateFamily
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-5KFLLTSQ.js";
|
|
17
17
|
import "./chunk-SSSJQVUY.js";
|
|
18
18
|
import {
|
|
19
19
|
restoreFlags
|
|
20
20
|
} from "./chunk-CSAPOCBP.js";
|
|
21
21
|
import {
|
|
22
22
|
parseAuthFlags
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-2H47YMCV.js";
|
|
24
24
|
import "./chunk-3IC4IEZC.js";
|
|
25
25
|
import {
|
|
26
26
|
loadConfigProviders,
|
|
@@ -657,4 +657,4 @@ async function runAuthRemove(deps, providerId) {
|
|
|
657
657
|
export {
|
|
658
658
|
authCmd
|
|
659
659
|
};
|
|
660
|
-
//# sourceMappingURL=auth-
|
|
660
|
+
//# sourceMappingURL=auth-EPERLKYI.js.map
|
|
@@ -62,6 +62,10 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
62
62
|
"insecure-open",
|
|
63
63
|
"strict-port",
|
|
64
64
|
"client",
|
|
65
|
+
// `wstack doctor` booleans. Without these, `--daemons --clear-stale` parses
|
|
66
|
+
// as `daemons="--clear-stale"` and the second flag disappears.
|
|
67
|
+
"daemons",
|
|
68
|
+
"clear-stale",
|
|
65
69
|
// `wstack update` booleans. Keeping these here prevents parseArgs from
|
|
66
70
|
// consuming a following positional token as an accidental flag value.
|
|
67
71
|
"check-only",
|
|
@@ -1052,4 +1056,4 @@ export {
|
|
|
1052
1056
|
runLiveProviderPicker,
|
|
1053
1057
|
runPicker
|
|
1054
1058
|
};
|
|
1055
|
-
//# sourceMappingURL=chunk-
|
|
1059
|
+
//# sourceMappingURL=chunk-2H47YMCV.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
LOCAL_LLM_PRESETS,
|
|
8
8
|
runLiveProviderPicker
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2H47YMCV.js";
|
|
10
10
|
import {
|
|
11
11
|
activeLabel,
|
|
12
12
|
loadConfigProviders,
|
|
@@ -1374,4 +1374,4 @@ export {
|
|
|
1374
1374
|
addCustomProvider,
|
|
1375
1375
|
addKeyForProvider
|
|
1376
1376
|
};
|
|
1377
|
-
//# sourceMappingURL=chunk-
|
|
1377
|
+
//# sourceMappingURL=chunk-5KFLLTSQ.js.map
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
runClaudeOAuthLogin,
|
|
60
60
|
runCopilotOAuthLogin,
|
|
61
61
|
validateFamily
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-5KFLLTSQ.js";
|
|
63
63
|
import {
|
|
64
64
|
runCodexOAuthLogin
|
|
65
65
|
} from "./chunk-SSSJQVUY.js";
|
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
import {
|
|
79
79
|
LOCAL_LLM_PRESETS,
|
|
80
80
|
parseSpawnFlags
|
|
81
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-2H47YMCV.js";
|
|
82
82
|
import {
|
|
83
83
|
buildPickableProviders,
|
|
84
84
|
visibleModelIds
|
|
@@ -9542,7 +9542,11 @@ import {
|
|
|
9542
9542
|
sessionScopedPath as sessionScopedPath3,
|
|
9543
9543
|
toErrorMessage as toErrorMessage5
|
|
9544
9544
|
} from "@wrongstack/core/utils";
|
|
9545
|
-
import {
|
|
9545
|
+
import {
|
|
9546
|
+
attachSessionKanbanMirror,
|
|
9547
|
+
hydrateSessionKanban,
|
|
9548
|
+
sessionKanbanDegradation
|
|
9549
|
+
} from "@wrongstack/tools/session-kanban";
|
|
9546
9550
|
async function setupSession(params) {
|
|
9547
9551
|
const {
|
|
9548
9552
|
config,
|
|
@@ -9706,6 +9710,12 @@ async function setupSession(params) {
|
|
|
9706
9710
|
const taskPath = sessionScopedPath3(wpaths.projectSessions, sessionId, ".tasks.json");
|
|
9707
9711
|
context.state.setMeta("task.path", taskPath);
|
|
9708
9712
|
await hydrateSessionKanban(context);
|
|
9713
|
+
const kanbanDown = sessionKanbanDegradation();
|
|
9714
|
+
if (kanbanDown) {
|
|
9715
|
+
renderer.writeInfo(
|
|
9716
|
+
`Kanban board sync unavailable \u2014 continuing without it (${kanbanDown}). Run \`wstack doctor --daemons\` for detail.`
|
|
9717
|
+
);
|
|
9718
|
+
}
|
|
9709
9719
|
const detachSessionKanbanMirror = attachSessionKanbanMirror(context);
|
|
9710
9720
|
const detachTodosCheckpoint = async () => {
|
|
9711
9721
|
detachSessionKanbanMirror();
|
|
@@ -25646,24 +25656,78 @@ function buildWebuiCommand() {
|
|
|
25646
25656
|
}
|
|
25647
25657
|
|
|
25648
25658
|
// src/slash-commands/theme.ts
|
|
25659
|
+
import { THEME_PRESET_IDS } from "@wrongstack/core/types";
|
|
25649
25660
|
import { color as color46, writeOut as writeOut3 } from "@wrongstack/core/utils";
|
|
25650
|
-
var
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25654
|
-
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
|
|
25661
|
+
var THEME_META = {
|
|
25662
|
+
catppuccin: { name: "Catppuccin Mocha", desc: "Soft pastel dark theme (Default)" },
|
|
25663
|
+
"tokyo-night": { name: "Tokyo Night", desc: "Deep violet, neon orange & cyan" },
|
|
25664
|
+
nord: { name: "Nord", desc: "Cool arctic blue & slate pastels" },
|
|
25665
|
+
cyberpunk: { name: "Cyberpunk Neon", desc: "High-contrast magenta, cyan & yellow" },
|
|
25666
|
+
dracula: { name: "Dracula", desc: "Vibrant purple, pink & green" },
|
|
25667
|
+
"gruvbox-dark": { name: "Gruvbox Dark", desc: "Warm earthy retro \u2014 orange, olive & aqua" },
|
|
25668
|
+
"solarized-dark": { name: "Solarized Dark", desc: "Base16 classic \u2014 teal & ochre precision" },
|
|
25669
|
+
"one-dark": { name: "One Dark", desc: "Atom's iconic palette \u2014 blue-led, warm accents" },
|
|
25670
|
+
monokai: { name: "Monokai", desc: "Sublime classic \u2014 vivid magenta, cyan & lime" },
|
|
25671
|
+
"rose-pine": { name: "Ros\xE9 Pine", desc: "Aesthetic pine & foam \u2014 soft evening pastels" },
|
|
25672
|
+
kanagawa: { name: "Kanagawa", desc: "Hokusai-inspired waves \u2014 sumi ink on washi" },
|
|
25673
|
+
"ayu-dark": { name: "Ayu Dark", desc: "Simple pleasant dark \u2014 warm orange + cool blue" },
|
|
25674
|
+
everforest: { name: "Everforest", desc: "Green-based comfort \u2014 forest greens & warm tans" },
|
|
25675
|
+
"night-owl": { name: "Night Owl", desc: "Sarah Drasner's night \u2014 deep navy + bold accents" },
|
|
25676
|
+
synthwave: { name: "Synthwave '84", desc: "Hot pink + neon cyan on deep purple" },
|
|
25677
|
+
"github-dark": { name: "GitHub Dark", desc: "GitHub's default dark \u2014 crisp blue on near-black" },
|
|
25678
|
+
"material-ocean": {
|
|
25679
|
+
name: "Material Ocean",
|
|
25680
|
+
desc: "Deepest Material variant \u2014 ink blue with pastel accents"
|
|
25681
|
+
},
|
|
25682
|
+
nightfox: { name: "Nightfox", desc: "Balanced slate blue with muted sage and rose" },
|
|
25683
|
+
oxocarbon: {
|
|
25684
|
+
name: "Oxocarbon",
|
|
25685
|
+
desc: "IBM Carbon-derived \u2014 neutral greys, electric blue & pink"
|
|
25686
|
+
},
|
|
25687
|
+
"catppuccin-macchiato": {
|
|
25688
|
+
name: "Catppuccin Macchiato",
|
|
25689
|
+
desc: "Warmer, one shade lighter than Mocha"
|
|
25690
|
+
},
|
|
25691
|
+
"catppuccin-frappe": {
|
|
25692
|
+
name: "Catppuccin Frapp\xE9",
|
|
25693
|
+
desc: "The lightest Catppuccin dark \u2014 gentle midday contrast"
|
|
25694
|
+
},
|
|
25695
|
+
"gruvbox-material": {
|
|
25696
|
+
name: "Gruvbox Material",
|
|
25697
|
+
desc: "Softened Gruvbox \u2014 same warmth, lower eye strain"
|
|
25698
|
+
},
|
|
25699
|
+
"tokyo-night-storm": {
|
|
25700
|
+
name: "Tokyo Night Storm",
|
|
25701
|
+
desc: "Tokyo Night on a lifted blue-grey base"
|
|
25702
|
+
},
|
|
25703
|
+
"rose-pine-moon": {
|
|
25704
|
+
name: "Ros\xE9 Pine Moon",
|
|
25705
|
+
desc: "Ros\xE9 Pine at dusk \u2014 deeper base, same soft accents"
|
|
25706
|
+
},
|
|
25707
|
+
zenburn: { name: "Zenburn", desc: "The classic low-contrast grey \u2014 desaturated and calm" },
|
|
25708
|
+
palenight: { name: "Palenight", desc: "Material Palenight \u2014 indigo base, candy accents" },
|
|
25709
|
+
horizon: { name: "Horizon", desc: "Warm coral and mint on charcoal \u2014 sunset gradient" },
|
|
25710
|
+
sonokai: { name: "Sonokai", desc: "Monokai Pro descendant \u2014 punchy on warm graphite" },
|
|
25711
|
+
"edge-dark": { name: "Edge Dark", desc: "Clean, evenly-weighted palette on desaturated navy" },
|
|
25712
|
+
moonfly: { name: "Moonfly", desc: "Near-black base with high-chroma accents \u2014 max contrast" },
|
|
25713
|
+
melange: { name: "Melange", desc: "Warm sepia and clay \u2014 the least blue dark theme here" },
|
|
25714
|
+
poimandres: { name: "Poimandres", desc: "Teal-forward, low-saturation \u2014 mint on deep indigo" },
|
|
25715
|
+
"vitesse-dark": {
|
|
25716
|
+
name: "Vitesse Dark",
|
|
25717
|
+
desc: "Anthony Fu's minimal palette \u2014 muted, print-like"
|
|
25718
|
+
},
|
|
25719
|
+
aura: { name: "Aura Dark", desc: "Vivid purple and spring green on near-black violet" },
|
|
25720
|
+
"dark-plus": { name: "VS Code Dark+", desc: "VS Code's default \u2014 familiar blue/orange/teal" }
|
|
25721
|
+
};
|
|
25722
|
+
var THEME_OPTIONS = THEME_PRESET_IDS.map((id) => ({ id, ...THEME_META[id] }));
|
|
25723
|
+
function presetHelpLines(perLine = 4) {
|
|
25724
|
+
const lines = [];
|
|
25725
|
+
for (let i = 0; i < THEME_PRESET_IDS.length; i += perLine) {
|
|
25726
|
+
const chunk = THEME_PRESET_IDS.slice(i, i + perLine).join(", ");
|
|
25727
|
+
lines.push(` ${chunk}${i + perLine < THEME_PRESET_IDS.length ? "," : ""}`);
|
|
25728
|
+
}
|
|
25729
|
+
return lines;
|
|
25730
|
+
}
|
|
25667
25731
|
async function runThemePicker(reader, activeId) {
|
|
25668
25732
|
let cursor = THEME_OPTIONS.findIndex((p) => p.id === activeId);
|
|
25669
25733
|
if (cursor < 0) cursor = 0;
|
|
@@ -25678,7 +25742,7 @@ ${color46.bold(color46.amber("WrongStack") + color46.dim(" \u2014 TUI Theme Sele
|
|
|
25678
25742
|
const mark = p.id === activeId ? color46.green(" [active]") : "";
|
|
25679
25743
|
const prefix = i === currentCursor ? color46.bold("\u276F ") : " ";
|
|
25680
25744
|
const name = i === currentCursor ? color46.bold(p.name) : p.name;
|
|
25681
|
-
lines.push(` ${prefix}${name.padEnd(
|
|
25745
|
+
lines.push(` ${prefix}${name.padEnd(21)} ${color46.dim(p.desc)}${mark}`);
|
|
25682
25746
|
}
|
|
25683
25747
|
lines.push("");
|
|
25684
25748
|
return lines.join("\n");
|
|
@@ -25708,16 +25772,14 @@ function buildThemeCommand(opts) {
|
|
|
25708
25772
|
name: "theme",
|
|
25709
25773
|
category: "Config",
|
|
25710
25774
|
description: "Switch or select the TUI color theme preset interactively",
|
|
25711
|
-
argsHint:
|
|
25775
|
+
argsHint: `[<preset>] (${THEME_PRESET_IDS.length} available \u2014 run /theme to pick)`,
|
|
25712
25776
|
help: [
|
|
25713
25777
|
"Usage:",
|
|
25714
25778
|
" /theme Interactive menu selection or view available theme presets",
|
|
25715
25779
|
" /theme <preset> Switch directly to a theme preset",
|
|
25716
25780
|
"",
|
|
25717
|
-
|
|
25718
|
-
|
|
25719
|
-
" gruvbox-dark, solarized-dark, one-dark, monokai, rose-pine,",
|
|
25720
|
-
" kanagawa, ayu-dark, everforest, night-owl, synthwave"
|
|
25781
|
+
`Available presets (${THEME_PRESET_IDS.length}):`,
|
|
25782
|
+
...presetHelpLines()
|
|
25721
25783
|
].join("\n"),
|
|
25722
25784
|
async run(args) {
|
|
25723
25785
|
const validPresets = THEME_OPTIONS.map((t) => t.id);
|
|
@@ -25747,7 +25809,7 @@ function buildThemeCommand(opts) {
|
|
|
25747
25809
|
];
|
|
25748
25810
|
for (const t of THEME_OPTIONS) {
|
|
25749
25811
|
const mark = t.id === activePreset ? color46.green(" [active]") : "";
|
|
25750
|
-
lines.push(` \u2022 ${color46.bold(t.id.padEnd(
|
|
25812
|
+
lines.push(` \u2022 ${color46.bold(t.id.padEnd(21))} ${t.desc}${mark}`);
|
|
25751
25813
|
}
|
|
25752
25814
|
lines.push("");
|
|
25753
25815
|
lines.push(color46.dim("Usage: /theme <preset>"));
|
|
@@ -32161,7 +32223,7 @@ async function runInteractive(cliCtx) {
|
|
|
32161
32223
|
onEvent: evOn
|
|
32162
32224
|
});
|
|
32163
32225
|
const savedProviderCfg = config.providers?.[config.provider];
|
|
32164
|
-
const { execute } = await import("./execution-
|
|
32226
|
+
const { execute } = await import("./execution-QYFWDD3Y.js");
|
|
32165
32227
|
const stopHeapWatchdog = startSharedHeapWatchdog({
|
|
32166
32228
|
collectStats: () => {
|
|
32167
32229
|
const hqQueue = hqPublisherRef.current?.getQueueStats();
|
|
@@ -32399,4 +32461,4 @@ export {
|
|
|
32399
32461
|
CLI_VERSION,
|
|
32400
32462
|
runInteractive
|
|
32401
32463
|
};
|
|
32402
|
-
//# sourceMappingURL=cli-main-
|
|
32464
|
+
//# sourceMappingURL=cli-main-6YE423OH.js.map
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
API_VERSION
|
|
3
|
+
} from "./chunk-XJXDOF63.js";
|
|
4
|
+
import "./chunk-7OCVIDC7.js";
|
|
5
|
+
|
|
6
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
7
|
+
import * as fs2 from "node:fs/promises";
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import * as path2 from "node:path";
|
|
10
|
+
import { color, toErrorMessage } from "@wrongstack/core/utils";
|
|
11
|
+
|
|
12
|
+
// src/subcommands/handlers/daemon-inventory.ts
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
chronicleProjectServerEndpoint,
|
|
17
|
+
chronicleProjectServerMetadataPath
|
|
18
|
+
} from "@wrongstack/core/chronicle";
|
|
19
|
+
import {
|
|
20
|
+
mailboxProjectServerEndpoint,
|
|
21
|
+
mailboxProjectServerMetadataPath
|
|
22
|
+
} from "@wrongstack/core/coordination";
|
|
23
|
+
import {
|
|
24
|
+
sessionCatalogProjectServerEndpoint,
|
|
25
|
+
sessionCatalogProjectServerMetadataPath
|
|
26
|
+
} from "@wrongstack/core/session-catalog";
|
|
27
|
+
import {
|
|
28
|
+
KANBAN_PROJECT_SERVER_METADATA_FILE,
|
|
29
|
+
kanbanProjectServerEndpoint
|
|
30
|
+
} from "@wrongstack/kanban";
|
|
31
|
+
import { isProjectEndpointLive } from "@wrongstack/persistence";
|
|
32
|
+
import { sageProjectServerEndpoint, sageProjectServerMetadataPath } from "@wrongstack/sage";
|
|
33
|
+
import {
|
|
34
|
+
projectIndexServerEndpoint,
|
|
35
|
+
projectIndexServerMetadataPath
|
|
36
|
+
} from "@wrongstack/tools/codebase-index";
|
|
37
|
+
function describeDaemons(options) {
|
|
38
|
+
const { projectRoot, projectDir } = options;
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
name: "kanban",
|
|
42
|
+
// In-repo `<projectRoot>/.wrongstack/`, not the global per-project state
|
|
43
|
+
// directory the other daemons use. Kanban deliberately keeps its server
|
|
44
|
+
// file next to the boards it owns.
|
|
45
|
+
metadataPath: path.join(projectRoot, ".wrongstack", KANBAN_PROJECT_SERVER_METADATA_FILE),
|
|
46
|
+
endpoint: kanbanProjectServerEndpoint(projectRoot)
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "sage",
|
|
50
|
+
endpoint: sageProjectServerEndpoint(projectRoot),
|
|
51
|
+
metadataPath: sageProjectServerMetadataPath(projectRoot)
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "chronicle",
|
|
55
|
+
endpoint: chronicleProjectServerEndpoint(projectDir),
|
|
56
|
+
metadataPath: chronicleProjectServerMetadataPath(projectDir)
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "mailbox",
|
|
60
|
+
endpoint: mailboxProjectServerEndpoint(projectDir),
|
|
61
|
+
metadataPath: mailboxProjectServerMetadataPath(projectDir)
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "session-catalog",
|
|
65
|
+
endpoint: sessionCatalogProjectServerEndpoint(projectDir),
|
|
66
|
+
metadataPath: sessionCatalogProjectServerMetadataPath(projectDir)
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "codebase-index",
|
|
70
|
+
endpoint: projectIndexServerEndpoint(projectRoot),
|
|
71
|
+
metadataPath: projectIndexServerMetadataPath(projectRoot)
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
async function readPid(metadataPath) {
|
|
76
|
+
if (!metadataPath) return void 0;
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(await fs.readFile(metadataPath, "utf8"));
|
|
79
|
+
return typeof parsed.pid === "number" ? parsed.pid : void 0;
|
|
80
|
+
} catch {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function endpointExists(endpoint) {
|
|
85
|
+
if (process.platform === "win32") return false;
|
|
86
|
+
try {
|
|
87
|
+
await fs.stat(endpoint);
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function collectDaemonReports(options) {
|
|
94
|
+
return Promise.all(
|
|
95
|
+
describeDaemons(options).map(async (descriptor) => {
|
|
96
|
+
const live = await isProjectEndpointLive(descriptor.endpoint);
|
|
97
|
+
const status = live ? "live" : await endpointExists(descriptor.endpoint) ? "stale" : "stopped";
|
|
98
|
+
const pid = live ? await readPid(descriptor.metadataPath) : void 0;
|
|
99
|
+
return pid === void 0 ? { name: descriptor.name, endpoint: descriptor.endpoint, status } : { name: descriptor.name, endpoint: descriptor.endpoint, status, pid };
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
async function clearStaleDaemonEndpoints(options) {
|
|
104
|
+
const cleared = [];
|
|
105
|
+
for (const report of await collectDaemonReports(options)) {
|
|
106
|
+
if (report.status !== "stale") continue;
|
|
107
|
+
try {
|
|
108
|
+
await fs.rm(report.endpoint, { force: true });
|
|
109
|
+
cleared.push(report.name);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return cleared;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
117
|
+
var diagCmd = async (_args, deps) => {
|
|
118
|
+
const cfg = deps.config;
|
|
119
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
120
|
+
const lines = [
|
|
121
|
+
color.bold("WrongStack diagnostics"),
|
|
122
|
+
` apiVersion: ${API_VERSION}`,
|
|
123
|
+
` cwd: ${deps.cwd}`,
|
|
124
|
+
` projectRoot: ${deps.projectRoot}`,
|
|
125
|
+
` projectHash: ${deps.paths.projectHash}`,
|
|
126
|
+
` projectDir: ${deps.paths.projectDir}`,
|
|
127
|
+
` globalRoot: ${deps.paths.globalRoot}`,
|
|
128
|
+
` modelsCache: ${deps.paths.modelsCache}`,
|
|
129
|
+
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
130
|
+
` node: ${process.version}`,
|
|
131
|
+
` os: ${os.platform()} ${os.release()}`,
|
|
132
|
+
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
133
|
+
` model: ${cfg.model ?? "<unset>"}`,
|
|
134
|
+
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
135
|
+
` plugins: ${cfg.plugins?.length ?? 0}`,
|
|
136
|
+
` mcpServers: ${Object.keys(cfg.mcpServers ?? {}).length}`
|
|
137
|
+
];
|
|
138
|
+
deps.renderer.write(lines.join("\n") + "\n");
|
|
139
|
+
return 0;
|
|
140
|
+
};
|
|
141
|
+
async function reportDaemons(deps, opts) {
|
|
142
|
+
const inventory = { projectRoot: deps.projectRoot, projectDir: deps.paths.projectDir };
|
|
143
|
+
let reports = await collectDaemonReports(inventory);
|
|
144
|
+
deps.renderer.write(color.bold("WrongStack project daemons\n\n"));
|
|
145
|
+
const icon = (status) => status === "live" ? color.green("\u2713") : status === "stale" ? color.red("\u2717") : color.dim("\xB7");
|
|
146
|
+
for (const report of reports) {
|
|
147
|
+
const suffix = report.pid === void 0 ? "" : ` pid ${report.pid}`;
|
|
148
|
+
deps.renderer.write(
|
|
149
|
+
` ${icon(report.status)} ${report.name.padEnd(16)} ${report.status.padEnd(8)}${color.dim(report.endpoint + suffix)}
|
|
150
|
+
`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const stale = reports.filter((report) => report.status === "stale");
|
|
154
|
+
if (stale.length === 0) {
|
|
155
|
+
deps.renderer.write(color.green("\nNo wedged endpoints.\n"));
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
if (!opts.clear) {
|
|
159
|
+
deps.renderer.write(
|
|
160
|
+
color.amber(
|
|
161
|
+
`
|
|
162
|
+
${stale.length} stale endpoint${stale.length === 1 ? "" : "s"}: ${stale.map((report) => report.name).join(", ")}
|
|
163
|
+
`
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
deps.renderer.write(
|
|
167
|
+
color.dim(
|
|
168
|
+
" A daemon died without releasing its endpoint. Current daemons reclaim\n this automatically on next start; clear it now with:\n wstack doctor --daemons --clear-stale\n"
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
const cleared = await clearStaleDaemonEndpoints(inventory);
|
|
174
|
+
reports = await collectDaemonReports(inventory);
|
|
175
|
+
const remaining = reports.filter((report) => report.status === "stale");
|
|
176
|
+
if (cleared.length > 0) {
|
|
177
|
+
deps.renderer.write(color.green(`
|
|
178
|
+
Cleared: ${cleared.join(", ")}
|
|
179
|
+
`));
|
|
180
|
+
}
|
|
181
|
+
if (remaining.length > 0) {
|
|
182
|
+
deps.renderer.write(
|
|
183
|
+
color.red(`Still wedged: ${remaining.map((report) => report.name).join(", ")}
|
|
184
|
+
`)
|
|
185
|
+
);
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
deps.renderer.write(color.dim("Daemons restart on demand \u2014 no further action needed.\n"));
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
var doctorCmd = async (args, deps) => {
|
|
192
|
+
const flagged = (name) => deps.flags?.[name] === true || deps.flags?.[name] === "true" || args.includes(`--${name}`);
|
|
193
|
+
if (flagged("daemons")) {
|
|
194
|
+
return reportDaemons(deps, { clear: flagged("clear-stale") });
|
|
195
|
+
}
|
|
196
|
+
const checks = [];
|
|
197
|
+
const cfg = deps.config;
|
|
198
|
+
if (!cfg.provider)
|
|
199
|
+
checks.push({
|
|
200
|
+
name: "provider",
|
|
201
|
+
status: "fail",
|
|
202
|
+
detail: "no provider configured \u2014 run `wstack auth` to set up"
|
|
203
|
+
});
|
|
204
|
+
else checks.push({ name: "provider", status: "ok", detail: cfg.provider });
|
|
205
|
+
if (!cfg.model)
|
|
206
|
+
checks.push({
|
|
207
|
+
name: "model",
|
|
208
|
+
status: "fail",
|
|
209
|
+
detail: "no model configured \u2014 run `wstack auth` to configure"
|
|
210
|
+
});
|
|
211
|
+
else checks.push({ name: "model", status: "ok", detail: cfg.model });
|
|
212
|
+
if (cfg.provider) {
|
|
213
|
+
const providerCfg = cfg.providers?.[cfg.provider];
|
|
214
|
+
const hasVaultKey = typeof providerCfg?.apiKey === "string" && providerCfg.apiKey.length > 0;
|
|
215
|
+
const envHit = providerCfg?.envVars?.some((v) => process.env[v]) ?? false;
|
|
216
|
+
if (hasVaultKey || envHit)
|
|
217
|
+
checks.push({
|
|
218
|
+
name: "api key",
|
|
219
|
+
status: "ok",
|
|
220
|
+
detail: hasVaultKey ? "found in vault" : "found in env"
|
|
221
|
+
});
|
|
222
|
+
else
|
|
223
|
+
checks.push({
|
|
224
|
+
name: "api key",
|
|
225
|
+
status: "fail",
|
|
226
|
+
detail: `no key for "${cfg.provider}" in vault or env \u2014 run \`wstack auth ${cfg.provider}\``
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
231
|
+
if (!isFinite(age))
|
|
232
|
+
checks.push({
|
|
233
|
+
name: "models cache",
|
|
234
|
+
status: "warn",
|
|
235
|
+
detail: "never fetched \u2014 run `wstack models refresh`"
|
|
236
|
+
});
|
|
237
|
+
else if (age > 7 * 24 * 3600)
|
|
238
|
+
checks.push({
|
|
239
|
+
name: "models cache",
|
|
240
|
+
status: "warn",
|
|
241
|
+
detail: `${Math.round(age / 86400)} days old \u2014 run \`wstack models refresh\``
|
|
242
|
+
});
|
|
243
|
+
else
|
|
244
|
+
checks.push({ name: "models cache", status: "ok", detail: `${Math.round(age / 60)}m old` });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
checks.push({
|
|
247
|
+
name: "models cache",
|
|
248
|
+
status: "warn",
|
|
249
|
+
detail: `read failed: ${toErrorMessage(err)}`
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
await fs2.access(deps.paths.secretsKey);
|
|
254
|
+
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
255
|
+
} catch {
|
|
256
|
+
checks.push({
|
|
257
|
+
name: "secret vault",
|
|
258
|
+
status: "warn",
|
|
259
|
+
detail: "not yet initialized (created lazily on first encrypt)"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await fs2.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
264
|
+
const probe = path2.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
265
|
+
await fs2.writeFile(probe, "");
|
|
266
|
+
await fs2.unlink(probe);
|
|
267
|
+
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
268
|
+
} catch (err) {
|
|
269
|
+
checks.push({
|
|
270
|
+
name: "sessions writable",
|
|
271
|
+
status: "fail",
|
|
272
|
+
detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
const mcpEntries = Object.entries(cfg.mcpServers ?? {});
|
|
276
|
+
for (const [name, srv] of mcpEntries) {
|
|
277
|
+
if (!srv.enabled) continue;
|
|
278
|
+
if ((srv.transport === "sse" || srv.transport === "streamable-http") && !srv.url)
|
|
279
|
+
checks.push({ name: `mcp:${name}`, status: "fail", detail: "transport requires url" });
|
|
280
|
+
else if (srv.transport === "stdio" && !srv.command)
|
|
281
|
+
checks.push({
|
|
282
|
+
name: `mcp:${name}`,
|
|
283
|
+
status: "fail",
|
|
284
|
+
detail: "stdio transport requires command"
|
|
285
|
+
});
|
|
286
|
+
else
|
|
287
|
+
checks.push({
|
|
288
|
+
name: `mcp:${name}`,
|
|
289
|
+
status: "ok",
|
|
290
|
+
detail: `${srv.transport} ${srv.command ?? srv.url ?? ""}`.trim()
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const major = Number.parseInt(process.version.replace(/^v/, "").split(".")[0] ?? "0", 10);
|
|
294
|
+
if (major < 22)
|
|
295
|
+
checks.push({ name: "node", status: "fail", detail: `${process.version} (need \u226522)` });
|
|
296
|
+
else checks.push({ name: "node", status: "ok", detail: process.version });
|
|
297
|
+
deps.renderer.write(color.bold("WrongStack doctor\n\n"));
|
|
298
|
+
let failed = 0;
|
|
299
|
+
let warned = 0;
|
|
300
|
+
for (const c of checks) {
|
|
301
|
+
const icon = c.status === "ok" ? color.green("\u2713") : c.status === "warn" ? color.amber("\u25CF") : color.red("\u2717");
|
|
302
|
+
deps.renderer.write(` ${icon} ${c.name.padEnd(20)} ${color.dim(c.detail)}
|
|
303
|
+
`);
|
|
304
|
+
if (c.status === "fail") failed++;
|
|
305
|
+
if (c.status === "warn") warned++;
|
|
306
|
+
}
|
|
307
|
+
deps.renderer.write("\n");
|
|
308
|
+
if (failed > 0) {
|
|
309
|
+
deps.renderer.write(
|
|
310
|
+
color.red(`${failed} failed, ${warned} warning${warned === 1 ? "" : "s"}
|
|
311
|
+
`)
|
|
312
|
+
);
|
|
313
|
+
return 1;
|
|
314
|
+
}
|
|
315
|
+
if (warned > 0) {
|
|
316
|
+
deps.renderer.write(
|
|
317
|
+
color.amber(`All checks passed (${warned} warning${warned === 1 ? "" : "s"})
|
|
318
|
+
`)
|
|
319
|
+
);
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
deps.renderer.write(color.green("All checks passed.\n"));
|
|
323
|
+
return 0;
|
|
324
|
+
};
|
|
325
|
+
export {
|
|
326
|
+
diagCmd,
|
|
327
|
+
doctorCmd
|
|
328
|
+
};
|
|
329
|
+
//# sourceMappingURL=diag-doctor-WTBHPOMB.js.map
|
|
@@ -1873,24 +1873,9 @@ function createSettingsAdapter(ctx) {
|
|
|
1873
1873
|
// src/boot/tui-theme-adapter.ts
|
|
1874
1874
|
import * as fs4 from "node:fs/promises";
|
|
1875
1875
|
import * as path9 from "node:path";
|
|
1876
|
+
import { THEME_PRESET_IDS } from "@wrongstack/core/types";
|
|
1876
1877
|
import { atomicWrite as atomicWrite2 } from "@wrongstack/core/utils";
|
|
1877
|
-
var VALID_PRESETS =
|
|
1878
|
-
"catppuccin",
|
|
1879
|
-
"tokyo-night",
|
|
1880
|
-
"nord",
|
|
1881
|
-
"cyberpunk",
|
|
1882
|
-
"dracula",
|
|
1883
|
-
"gruvbox-dark",
|
|
1884
|
-
"solarized-dark",
|
|
1885
|
-
"one-dark",
|
|
1886
|
-
"monokai",
|
|
1887
|
-
"rose-pine",
|
|
1888
|
-
"kanagawa",
|
|
1889
|
-
"ayu-dark",
|
|
1890
|
-
"everforest",
|
|
1891
|
-
"night-owl",
|
|
1892
|
-
"synthwave"
|
|
1893
|
-
]);
|
|
1878
|
+
var VALID_PRESETS = new Set(THEME_PRESET_IDS);
|
|
1894
1879
|
function createThemeAdapter({ configStore, wpaths }) {
|
|
1895
1880
|
return {
|
|
1896
1881
|
getThemePreset: () => {
|
|
@@ -5362,4 +5347,4 @@ export {
|
|
|
5362
5347
|
execute,
|
|
5363
5348
|
resolveReviewerFallbackModels
|
|
5364
5349
|
};
|
|
5365
|
-
//# sourceMappingURL=execution-
|
|
5350
|
+
//# sourceMappingURL=execution-QYFWDD3Y.js.map
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
parseArgs,
|
|
22
22
|
runPicker,
|
|
23
23
|
saveToGlobalConfig
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-2H47YMCV.js";
|
|
25
25
|
import {
|
|
26
26
|
isKeylessLocalProvider,
|
|
27
27
|
visibleModelIds
|
|
@@ -2308,7 +2308,7 @@ function compactSingleLine(text) {
|
|
|
2308
2308
|
var loaders = {
|
|
2309
2309
|
acp: async () => (await import("./acp-5ZLGFHWP.js")).acpCmd,
|
|
2310
2310
|
init: async () => (await import("./init-E2NDDOHI.js")).initCmd,
|
|
2311
|
-
auth: async () => (await import("./auth-
|
|
2311
|
+
auth: async () => (await import("./auth-EPERLKYI.js")).authCmd,
|
|
2312
2312
|
update: async () => (await import("./update-VZSOZGYC.js")).updateCmd,
|
|
2313
2313
|
sessions: async () => (await import("./sessions-config-YX2ZPAFM.js")).sessionsCmd,
|
|
2314
2314
|
config: async () => (await import("./sessions-config-YX2ZPAFM.js")).configCmd,
|
|
@@ -2322,8 +2322,8 @@ var loaders = {
|
|
|
2322
2322
|
mcp: async () => (await import("./mcp-MSARYOPN.js")).mcpCmd,
|
|
2323
2323
|
plugin: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
2324
2324
|
plugins: async () => (await import("./plugin-usage-PTEETL3J.js")).pluginCmd,
|
|
2325
|
-
diag: async () => (await import("./diag-doctor-
|
|
2326
|
-
doctor: async () => (await import("./diag-doctor-
|
|
2325
|
+
diag: async () => (await import("./diag-doctor-WTBHPOMB.js")).diagCmd,
|
|
2326
|
+
doctor: async () => (await import("./diag-doctor-WTBHPOMB.js")).doctorCmd,
|
|
2327
2327
|
export: async () => (await import("./export-CCUXRJOU.js")).exportCmd,
|
|
2328
2328
|
usage: async () => (await import("./plugin-usage-PTEETL3J.js")).usageCmd,
|
|
2329
2329
|
version: async () => (await import("./version-help-ASKQYXDJ.js")).versionCmd,
|
|
@@ -3089,7 +3089,7 @@ async function initializeCli(argv) {
|
|
|
3089
3089
|
async function main(argv) {
|
|
3090
3090
|
const cliCtx = await initializeCli(argv);
|
|
3091
3091
|
if (typeof cliCtx === "number") return cliCtx;
|
|
3092
|
-
const { runInteractive } = await import("./cli-main-
|
|
3092
|
+
const { runInteractive } = await import("./cli-main-6YE423OH.js");
|
|
3093
3093
|
return runInteractive(cliCtx);
|
|
3094
3094
|
}
|
|
3095
3095
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type DaemonStatus =
|
|
2
|
+
/** Answering on its endpoint. */
|
|
3
|
+
'live'
|
|
4
|
+
/**
|
|
5
|
+
* The endpoint exists on disk but nothing answers on it. This is the wedge:
|
|
6
|
+
* clients get ECONNREFUSED and conclude "spawn one", the replacement gets
|
|
7
|
+
* EADDRINUSE from the leftover file. Self-healing daemons reclaim it on
|
|
8
|
+
* their next start; seeing this means one has not been started since.
|
|
9
|
+
*/
|
|
10
|
+
| 'stale'
|
|
11
|
+
/** No endpoint and no daemon. The normal state before first use. */
|
|
12
|
+
| 'stopped';
|
|
13
|
+
export interface DaemonReport {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly endpoint: string;
|
|
16
|
+
readonly status: DaemonStatus;
|
|
17
|
+
/** Owning process id, when a metadata file was readable. */
|
|
18
|
+
readonly pid?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface DaemonInventoryOptions {
|
|
21
|
+
readonly projectRoot: string;
|
|
22
|
+
/**
|
|
23
|
+
* The project's slot under the global root — `~/.wrongstack/projects/<hash>`,
|
|
24
|
+
* NOT the in-repo `.wrongstack/`. Most daemons key their endpoint and
|
|
25
|
+
* metadata off this; kanban is the exception and uses the repo directory.
|
|
26
|
+
*/
|
|
27
|
+
readonly projectDir: string;
|
|
28
|
+
}
|
|
29
|
+
/** Probe every project daemon concurrently. Never spawns one. */
|
|
30
|
+
export declare function collectDaemonReports(options: DaemonInventoryOptions): Promise<readonly DaemonReport[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Delete stale endpoints so the next client spawn can bind.
|
|
33
|
+
*
|
|
34
|
+
* Only ever removes an endpoint that a fresh probe just reported as stale —
|
|
35
|
+
* unlinking a live daemon's socket would sever every connected client and
|
|
36
|
+
* elect a second writer over the same database. Restarting is therefore not
|
|
37
|
+
* an operation this offers: daemons are spawned on demand, so clearing the
|
|
38
|
+
* wedge is the whole job, and the next command that needs one starts it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function clearStaleDaemonEndpoints(options: DaemonInventoryOptions): Promise<readonly string[]>;
|
|
41
|
+
//# sourceMappingURL=daemon-inventory.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/cli",
|
|
3
|
-
"version": "0.305.
|
|
3
|
+
"version": "0.305.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack CLI — terminal AI coding agent with provider catalog from models.dev. Provides `wrongstack` and `wstack` binaries.",
|
|
6
6
|
"keywords": [
|
|
@@ -42,30 +42,31 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"ws": "^8.21.1",
|
|
45
|
-
"@wrongstack/
|
|
46
|
-
"@wrongstack/
|
|
47
|
-
"@wrongstack/acp": "0.305.
|
|
48
|
-
"@wrongstack/
|
|
49
|
-
"@wrongstack/
|
|
50
|
-
"@wrongstack/
|
|
51
|
-
"@wrongstack/
|
|
52
|
-
"@wrongstack/
|
|
53
|
-
"@wrongstack/
|
|
54
|
-
"@wrongstack/
|
|
55
|
-
"@wrongstack/runtime": "0.305.
|
|
56
|
-
"@wrongstack/
|
|
57
|
-
"@wrongstack/
|
|
58
|
-
"@wrongstack/
|
|
59
|
-
"@wrongstack/
|
|
60
|
-
"@wrongstack/
|
|
61
|
-
"@wrongstack/
|
|
62
|
-
"@wrongstack/
|
|
63
|
-
"@wrongstack/
|
|
64
|
-
"@wrongstack/
|
|
65
|
-
"@wrongstack/webui
|
|
45
|
+
"@wrongstack/core": "0.305.1",
|
|
46
|
+
"@wrongstack/mcp": "0.305.1",
|
|
47
|
+
"@wrongstack/acp": "0.305.1",
|
|
48
|
+
"@wrongstack/bench": "0.305.1",
|
|
49
|
+
"@wrongstack/persistence": "0.305.1",
|
|
50
|
+
"@wrongstack/kanban": "0.305.1",
|
|
51
|
+
"@wrongstack/plugins": "0.305.1",
|
|
52
|
+
"@wrongstack/providers": "0.305.1",
|
|
53
|
+
"@wrongstack/plug-lsp": "0.305.1",
|
|
54
|
+
"@wrongstack/sage": "0.305.1",
|
|
55
|
+
"@wrongstack/runtime": "0.305.1",
|
|
56
|
+
"@wrongstack/requirement-intake": "0.305.1",
|
|
57
|
+
"@wrongstack/simpleui": "0.305.1",
|
|
58
|
+
"@wrongstack/sdd": "0.305.1",
|
|
59
|
+
"@wrongstack/security-scanner": "0.305.1",
|
|
60
|
+
"@wrongstack/telegram": "0.305.1",
|
|
61
|
+
"@wrongstack/techstack": "0.305.1",
|
|
62
|
+
"@wrongstack/tools": "0.305.1",
|
|
63
|
+
"@wrongstack/tui": "0.305.1",
|
|
64
|
+
"@wrongstack/webui-hq": "0.305.1",
|
|
65
|
+
"@wrongstack/webui": "0.305.1",
|
|
66
|
+
"@wrongstack/webui-server": "0.305.1"
|
|
66
67
|
},
|
|
67
68
|
"optionalDependencies": {
|
|
68
|
-
"@wrongstack/desktop": "0.305.
|
|
69
|
+
"@wrongstack/desktop": "0.305.1"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@types/node": "^26.1.2",
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
API_VERSION
|
|
3
|
-
} from "./chunk-XJXDOF63.js";
|
|
4
|
-
import "./chunk-7OCVIDC7.js";
|
|
5
|
-
|
|
6
|
-
// src/subcommands/handlers/diag-doctor.ts
|
|
7
|
-
import * as fs from "node:fs/promises";
|
|
8
|
-
import * as os from "node:os";
|
|
9
|
-
import * as path from "node:path";
|
|
10
|
-
import { color, toErrorMessage } from "@wrongstack/core/utils";
|
|
11
|
-
var diagCmd = async (_args, deps) => {
|
|
12
|
-
const cfg = deps.config;
|
|
13
|
-
const age = await deps.modelsRegistry.ageSeconds();
|
|
14
|
-
const lines = [
|
|
15
|
-
color.bold("WrongStack diagnostics"),
|
|
16
|
-
` apiVersion: ${API_VERSION}`,
|
|
17
|
-
` cwd: ${deps.cwd}`,
|
|
18
|
-
` projectRoot: ${deps.projectRoot}`,
|
|
19
|
-
` projectHash: ${deps.paths.projectHash}`,
|
|
20
|
-
` projectDir: ${deps.paths.projectDir}`,
|
|
21
|
-
` globalRoot: ${deps.paths.globalRoot}`,
|
|
22
|
-
` modelsCache: ${deps.paths.modelsCache}`,
|
|
23
|
-
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
24
|
-
` node: ${process.version}`,
|
|
25
|
-
` os: ${os.platform()} ${os.release()}`,
|
|
26
|
-
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
27
|
-
` model: ${cfg.model ?? "<unset>"}`,
|
|
28
|
-
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
29
|
-
` plugins: ${cfg.plugins?.length ?? 0}`,
|
|
30
|
-
` mcpServers: ${Object.keys(cfg.mcpServers ?? {}).length}`
|
|
31
|
-
];
|
|
32
|
-
deps.renderer.write(lines.join("\n") + "\n");
|
|
33
|
-
return 0;
|
|
34
|
-
};
|
|
35
|
-
var doctorCmd = async (_args, deps) => {
|
|
36
|
-
const checks = [];
|
|
37
|
-
const cfg = deps.config;
|
|
38
|
-
if (!cfg.provider)
|
|
39
|
-
checks.push({
|
|
40
|
-
name: "provider",
|
|
41
|
-
status: "fail",
|
|
42
|
-
detail: "no provider configured \u2014 run `wstack auth` to set up"
|
|
43
|
-
});
|
|
44
|
-
else checks.push({ name: "provider", status: "ok", detail: cfg.provider });
|
|
45
|
-
if (!cfg.model)
|
|
46
|
-
checks.push({
|
|
47
|
-
name: "model",
|
|
48
|
-
status: "fail",
|
|
49
|
-
detail: "no model configured \u2014 run `wstack auth` to configure"
|
|
50
|
-
});
|
|
51
|
-
else checks.push({ name: "model", status: "ok", detail: cfg.model });
|
|
52
|
-
if (cfg.provider) {
|
|
53
|
-
const providerCfg = cfg.providers?.[cfg.provider];
|
|
54
|
-
const hasVaultKey = typeof providerCfg?.apiKey === "string" && providerCfg.apiKey.length > 0;
|
|
55
|
-
const envHit = providerCfg?.envVars?.some((v) => process.env[v]) ?? false;
|
|
56
|
-
if (hasVaultKey || envHit)
|
|
57
|
-
checks.push({
|
|
58
|
-
name: "api key",
|
|
59
|
-
status: "ok",
|
|
60
|
-
detail: hasVaultKey ? "found in vault" : "found in env"
|
|
61
|
-
});
|
|
62
|
-
else
|
|
63
|
-
checks.push({
|
|
64
|
-
name: "api key",
|
|
65
|
-
status: "fail",
|
|
66
|
-
detail: `no key for "${cfg.provider}" in vault or env \u2014 run \`wstack auth ${cfg.provider}\``
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const age = await deps.modelsRegistry.ageSeconds();
|
|
71
|
-
if (!isFinite(age))
|
|
72
|
-
checks.push({
|
|
73
|
-
name: "models cache",
|
|
74
|
-
status: "warn",
|
|
75
|
-
detail: "never fetched \u2014 run `wstack models refresh`"
|
|
76
|
-
});
|
|
77
|
-
else if (age > 7 * 24 * 3600)
|
|
78
|
-
checks.push({
|
|
79
|
-
name: "models cache",
|
|
80
|
-
status: "warn",
|
|
81
|
-
detail: `${Math.round(age / 86400)} days old \u2014 run \`wstack models refresh\``
|
|
82
|
-
});
|
|
83
|
-
else
|
|
84
|
-
checks.push({ name: "models cache", status: "ok", detail: `${Math.round(age / 60)}m old` });
|
|
85
|
-
} catch (err) {
|
|
86
|
-
checks.push({
|
|
87
|
-
name: "models cache",
|
|
88
|
-
status: "warn",
|
|
89
|
-
detail: `read failed: ${toErrorMessage(err)}`
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
await fs.access(deps.paths.secretsKey);
|
|
94
|
-
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
95
|
-
} catch {
|
|
96
|
-
checks.push({
|
|
97
|
-
name: "secret vault",
|
|
98
|
-
status: "warn",
|
|
99
|
-
detail: "not yet initialized (created lazily on first encrypt)"
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
await fs.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
104
|
-
const probe = path.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
105
|
-
await fs.writeFile(probe, "");
|
|
106
|
-
await fs.unlink(probe);
|
|
107
|
-
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
108
|
-
} catch (err) {
|
|
109
|
-
checks.push({
|
|
110
|
-
name: "sessions writable",
|
|
111
|
-
status: "fail",
|
|
112
|
-
detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
const mcpEntries = Object.entries(cfg.mcpServers ?? {});
|
|
116
|
-
for (const [name, srv] of mcpEntries) {
|
|
117
|
-
if (!srv.enabled) continue;
|
|
118
|
-
if ((srv.transport === "sse" || srv.transport === "streamable-http") && !srv.url)
|
|
119
|
-
checks.push({ name: `mcp:${name}`, status: "fail", detail: "transport requires url" });
|
|
120
|
-
else if (srv.transport === "stdio" && !srv.command)
|
|
121
|
-
checks.push({
|
|
122
|
-
name: `mcp:${name}`,
|
|
123
|
-
status: "fail",
|
|
124
|
-
detail: "stdio transport requires command"
|
|
125
|
-
});
|
|
126
|
-
else
|
|
127
|
-
checks.push({
|
|
128
|
-
name: `mcp:${name}`,
|
|
129
|
-
status: "ok",
|
|
130
|
-
detail: `${srv.transport} ${srv.command ?? srv.url ?? ""}`.trim()
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
const major = Number.parseInt(process.version.replace(/^v/, "").split(".")[0] ?? "0", 10);
|
|
134
|
-
if (major < 22)
|
|
135
|
-
checks.push({ name: "node", status: "fail", detail: `${process.version} (need \u226522)` });
|
|
136
|
-
else checks.push({ name: "node", status: "ok", detail: process.version });
|
|
137
|
-
deps.renderer.write(color.bold("WrongStack doctor\n\n"));
|
|
138
|
-
let failed = 0;
|
|
139
|
-
let warned = 0;
|
|
140
|
-
for (const c of checks) {
|
|
141
|
-
const icon = c.status === "ok" ? color.green("\u2713") : c.status === "warn" ? color.amber("\u25CF") : color.red("\u2717");
|
|
142
|
-
deps.renderer.write(` ${icon} ${c.name.padEnd(20)} ${color.dim(c.detail)}
|
|
143
|
-
`);
|
|
144
|
-
if (c.status === "fail") failed++;
|
|
145
|
-
if (c.status === "warn") warned++;
|
|
146
|
-
}
|
|
147
|
-
deps.renderer.write("\n");
|
|
148
|
-
if (failed > 0) {
|
|
149
|
-
deps.renderer.write(
|
|
150
|
-
color.red(`${failed} failed, ${warned} warning${warned === 1 ? "" : "s"}
|
|
151
|
-
`)
|
|
152
|
-
);
|
|
153
|
-
return 1;
|
|
154
|
-
}
|
|
155
|
-
if (warned > 0) {
|
|
156
|
-
deps.renderer.write(
|
|
157
|
-
color.amber(`All checks passed (${warned} warning${warned === 1 ? "" : "s"})
|
|
158
|
-
`)
|
|
159
|
-
);
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
deps.renderer.write(color.green("All checks passed.\n"));
|
|
163
|
-
return 0;
|
|
164
|
-
};
|
|
165
|
-
export {
|
|
166
|
-
diagCmd,
|
|
167
|
-
doctorCmd
|
|
168
|
-
};
|
|
169
|
-
//# sourceMappingURL=diag-doctor-ZF5BF2MK.js.map
|