@melra/cli 0.3.0-alpha.11 → 0.3.0-alpha.13
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 +14 -7
- package/dist/activity.d.ts +26 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +404 -0
- package/dist/activity.js.map +1 -0
- package/dist/approval-screen.d.ts +35 -0
- package/dist/approval-screen.d.ts.map +1 -0
- package/dist/approval-screen.js +130 -0
- package/dist/approval-screen.js.map +1 -0
- package/dist/client-status.d.ts +24 -0
- package/dist/client-status.d.ts.map +1 -0
- package/dist/client-status.js +273 -0
- package/dist/client-status.js.map +1 -0
- package/dist/clients.d.ts +32 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +588 -0
- package/dist/clients.js.map +1 -0
- package/dist/conformance.d.ts +7 -0
- package/dist/conformance.d.ts.map +1 -1
- package/dist/conformance.js +134 -30
- package/dist/conformance.js.map +1 -1
- package/dist/demo-workflow.d.ts +46 -0
- package/dist/demo-workflow.d.ts.map +1 -0
- package/dist/demo-workflow.js +58 -0
- package/dist/demo-workflow.js.map +1 -0
- package/dist/environment.d.ts +15 -1
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +30 -4
- package/dist/environment.js.map +1 -1
- package/dist/human.d.ts +67 -0
- package/dist/human.d.ts.map +1 -0
- package/dist/human.js +266 -0
- package/dist/human.js.map +1 -0
- package/dist/index.js +432 -77
- package/dist/index.js.map +1 -1
- package/dist/isolation.d.ts +35 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +66 -0
- package/dist/isolation.js.map +1 -0
- package/dist/jsonc.d.ts +24 -0
- package/dist/jsonc.d.ts.map +1 -0
- package/dist/jsonc.js +238 -0
- package/dist/jsonc.js.map +1 -0
- package/dist/output.d.ts +28 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +84 -0
- package/dist/output.js.map +1 -0
- package/package.json +11 -10
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Copyright 2026 XAGI Labs Private Limited
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Characters a terminal does not show as themselves: controls (C0, C1, DEL),
|
|
5
|
+
* format characters (bidi overrides, zero-width, soft hyphen, tag characters),
|
|
6
|
+
* line and paragraph separators, and the ones that render as nothing at all
|
|
7
|
+
* (variation selectors, Hangul fillers, the grapheme joiner).
|
|
8
|
+
*/
|
|
9
|
+
const HIDDEN = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\u034f\u115f\u1160\u17b4\u17b5\u180b-\u180f\u3164\ufe00-\ufe0f\uffa0\u{e0100}-\u{e01ef}]/gu;
|
|
10
|
+
/**
|
|
11
|
+
* Text a terminal shows as text and nothing else. Everything in an approval
|
|
12
|
+
* render except its labels was chosen by the agent asking for approval, and a
|
|
13
|
+
* raw escape sequence in it can erase the line saying so, paint a different
|
|
14
|
+
* effect, and hide the real one. Each hidden character is spelled out as
|
|
15
|
+
* `\uXXXX` per UTF-16 unit, which inside JSON is also a valid escape for it.
|
|
16
|
+
*/
|
|
17
|
+
export function printable(text, keepNewlines = false) {
|
|
18
|
+
return text.replace(HIDDEN, (character) => keepNewlines && character === "\n"
|
|
19
|
+
? character
|
|
20
|
+
: Array.from({ length: character.length }, (_, index) => `\\u${character.charCodeAt(index).toString(16).padStart(4, "0")}`).join(""));
|
|
21
|
+
}
|
|
22
|
+
/** At most `limit` characters, saying how many more there were. */
|
|
23
|
+
function bounded(text, limit = 160) {
|
|
24
|
+
const shown = printable(text);
|
|
25
|
+
return shown.length > limit ? `${shown.slice(0, limit)}…(+${shown.length - limit} chars)` : shown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The operation, one bounded line per leaf, so what the effect actually does
|
|
29
|
+
* (a command's arguments and environment, a move's destination, a request's
|
|
30
|
+
* method and body) sits in the summary and not only in the dump above it.
|
|
31
|
+
*
|
|
32
|
+
* Every operation schema is strict, so its top-level fields are a fixed set.
|
|
33
|
+
* Each list or record (args, env, headers, values, fields, filePaths) shows
|
|
34
|
+
* whole entries from both ends, the first and last always, within its own
|
|
35
|
+
* budget of lines, and says how many entries it left out, so no entry is shown
|
|
36
|
+
* in part. Lists print first, then objects (a browser target, env, headers),
|
|
37
|
+
* then plain values (a url, a cwd, a typed value) right above the effect: the
|
|
38
|
+
* fields of fixed size sit below everything an agent can make longer.
|
|
39
|
+
* Planning refuses a browser list its action never reads, so a list on screen
|
|
40
|
+
* is the action's own and never padding.
|
|
41
|
+
*/
|
|
42
|
+
function operationLines(operation, fieldLimit = 8) {
|
|
43
|
+
const leaf = (path, value) => `${bounded(path, 60)}: ${bounded(typeof value === "string" ? JSON.stringify(value) : String(value))}`;
|
|
44
|
+
const leaves = (value, path, into) => {
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
if (value.length === 0)
|
|
47
|
+
into.push(`${bounded(path, 60)}: []`);
|
|
48
|
+
value.forEach((item, index) => leaves(item, `${path}[${index}]`, into));
|
|
49
|
+
}
|
|
50
|
+
else if (value !== null && typeof value === "object") {
|
|
51
|
+
const entries = Object.entries(value);
|
|
52
|
+
if (entries.length === 0)
|
|
53
|
+
into.push(`${bounded(path, 60)}: {}`);
|
|
54
|
+
for (const [key, item] of entries)
|
|
55
|
+
leaves(item, `${path}.${key}`, into);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
into.push(leaf(path, value));
|
|
59
|
+
}
|
|
60
|
+
return into;
|
|
61
|
+
};
|
|
62
|
+
const summarise = (field, value) => {
|
|
63
|
+
if (value === null || typeof value !== "object")
|
|
64
|
+
return [leaf(field, value)];
|
|
65
|
+
const entries = Array.isArray(value)
|
|
66
|
+
? value.map((item, index) => leaves(item, `${field}[${index}]`, []))
|
|
67
|
+
: Object.entries(value).map(([key, item]) => leaves(item, `${field}.${key}`, []));
|
|
68
|
+
if (entries.length === 0)
|
|
69
|
+
return [`${bounded(field, 60)}: ${Array.isArray(value) ? "[]" : "{}"}`];
|
|
70
|
+
// Whole entries, alternately from each end, while they fit; when one end's
|
|
71
|
+
// next entry does not, the other end goes on alone.
|
|
72
|
+
let head = 1;
|
|
73
|
+
let tail = entries.length > 1 ? 1 : 0;
|
|
74
|
+
let used = entries[0].length + (tail === 1 ? entries.at(-1).length : 0);
|
|
75
|
+
let headOpen = true;
|
|
76
|
+
let tailOpen = true;
|
|
77
|
+
while (head + tail < entries.length && (headOpen || tailOpen)) {
|
|
78
|
+
const fromHead = headOpen && (!tailOpen || head <= tail);
|
|
79
|
+
const next = entries[fromHead ? head : entries.length - 1 - tail];
|
|
80
|
+
if (used + next.length > fieldLimit) {
|
|
81
|
+
if (fromHead)
|
|
82
|
+
headOpen = false;
|
|
83
|
+
else
|
|
84
|
+
tailOpen = false;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
used += next.length;
|
|
88
|
+
if (fromHead)
|
|
89
|
+
head += 1;
|
|
90
|
+
else
|
|
91
|
+
tail += 1;
|
|
92
|
+
}
|
|
93
|
+
const hidden = entries.slice(head, entries.length - tail);
|
|
94
|
+
return [
|
|
95
|
+
...entries.slice(0, head).flat(),
|
|
96
|
+
// A note as tall as what it stands for saves nothing: show that instead.
|
|
97
|
+
...(hidden.flat().length <= 1
|
|
98
|
+
? hidden.flat()
|
|
99
|
+
: [`…(+${hidden.length} more ${bounded(field, 60)} entries, all in the JSON above)`]),
|
|
100
|
+
...entries.slice(entries.length - tail).flat(),
|
|
101
|
+
];
|
|
102
|
+
};
|
|
103
|
+
if (operation === null || typeof operation !== "object")
|
|
104
|
+
return operation === undefined ? [] : [leaf("operation", operation)];
|
|
105
|
+
const rank = (value) => (Array.isArray(value) ? 0 : value !== null && typeof value === "object" ? 1 : 2);
|
|
106
|
+
return Object.entries(operation)
|
|
107
|
+
.sort(([, a], [, b]) => rank(a) - rank(b))
|
|
108
|
+
.flatMap(([field, value]) => summarise(field, value));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The approval screen, ending with what MELRA itself vouches for: the full
|
|
112
|
+
* contract first, so however long the agent's content runs it cannot push the
|
|
113
|
+
* effect off the screen, then the operation and the summary, each line
|
|
114
|
+
* bounded, right above the question.
|
|
115
|
+
*/
|
|
116
|
+
export function approvalScreen(view) {
|
|
117
|
+
const { contract } = view;
|
|
118
|
+
return [
|
|
119
|
+
printable(JSON.stringify(contract, null, 2), true),
|
|
120
|
+
"\u001b[0m",
|
|
121
|
+
`Task ${view.task.id}, waiting for approval until ${view.task.approval?.expiresAt ?? "unknown"}`,
|
|
122
|
+
"Operation:",
|
|
123
|
+
...operationLines(contract.operation).map((line) => ` ${line}`),
|
|
124
|
+
`Effect: ${bounded(contract.capability, 60)} on ${bounded(contract.target)} (${bounded(contract.effect, 20)}, ${bounded(contract.risk, 20)} risk)`,
|
|
125
|
+
`Goal (stated by the agent, unverified): ${bounded(contract.metadata.goal, 200)}`,
|
|
126
|
+
`Contract digest: ${view.renderDigest}`,
|
|
127
|
+
"",
|
|
128
|
+
].join("\n");
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=approval-screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-screen.js","sourceRoot":"","sources":["../src/approval-screen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ClientStatus {
|
|
2
|
+
client: string;
|
|
3
|
+
path: string;
|
|
4
|
+
state: "configured" | "not configured" | "unreadable" | "not inspected";
|
|
5
|
+
command?: string;
|
|
6
|
+
pin?: string;
|
|
7
|
+
note?: string;
|
|
8
|
+
problems: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function resolvesOnPath(command: string): boolean;
|
|
11
|
+
export declare function clientStatuses(workspace: string): Promise<ClientStatus[]>;
|
|
12
|
+
export interface ClientChoice {
|
|
13
|
+
client: string;
|
|
14
|
+
label: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
detected?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function detectClients(workspace: string): ClientChoice[];
|
|
19
|
+
export declare function clientsCheck(statuses: ClientStatus[]): {
|
|
20
|
+
name: string;
|
|
21
|
+
status: "pass" | "warn";
|
|
22
|
+
detail: string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=client-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-status.d.ts","sourceRoot":"","sources":["../src/client-status.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAGb,KAAK,EAAE,YAAY,GAAG,gBAAgB,GAAG,YAAY,GAAG,eAAe,CAAC;IAGxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAgCD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAcvD;AA+ID,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiB/E;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,EAAE,CAuB/D;AAID,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB,CAeA"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Copyright 2026 XAGI Labs Private Limited
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { accessSync, constants, existsSync, statSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { delimiter, dirname, extname, join } from "node:path";
|
|
7
|
+
import { PRODUCT_VERSION } from "@melra/protocol";
|
|
8
|
+
import { CLIENTS, isObject } from "./clients.js";
|
|
9
|
+
import { parseJsonc } from "./jsonc.js";
|
|
10
|
+
// Where each JSON shape keeps its servers, matching what `clientConfig` writes.
|
|
11
|
+
const SERVER_KEYS = {
|
|
12
|
+
mcpServers: ["mcpServers"],
|
|
13
|
+
vscode: ["servers"],
|
|
14
|
+
zed: ["context_servers"],
|
|
15
|
+
opencode: ["mcp"],
|
|
16
|
+
amp: ["amp.mcpServers"],
|
|
17
|
+
openclaw: ["mcp", "servers"],
|
|
18
|
+
copilot: ["mcpServers"],
|
|
19
|
+
droid: ["mcpServers"],
|
|
20
|
+
kilo: ["mcp"],
|
|
21
|
+
};
|
|
22
|
+
// The top-level YAML map each YAML shape keeps its servers under.
|
|
23
|
+
const YAML_KEYS = { goose: "extensions", hermes: "mcp_servers" };
|
|
24
|
+
function isExecutable(path) {
|
|
25
|
+
try {
|
|
26
|
+
// Windows has no execute bit; X_OK there is F_OK, so the extension is
|
|
27
|
+
// what makes a file runnable and PATHEXT below decides that.
|
|
28
|
+
accessSync(path, constants.X_OK);
|
|
29
|
+
return statSync(path).isFile();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// What the client would find when it spawns `command`. This sees doctor's own
|
|
36
|
+
// PATH, which a GUI client started from the dock may not share, so a hit is
|
|
37
|
+
// necessary rather than sufficient.
|
|
38
|
+
export function resolvesOnPath(command) {
|
|
39
|
+
if (/[\\/]/.test(command))
|
|
40
|
+
return isExecutable(command);
|
|
41
|
+
// Windows spawns `melra` as `melra.cmd`: a bare name is tried with every
|
|
42
|
+
// PATHEXT extension, a name that already has one is tried as written.
|
|
43
|
+
const extensions = process.platform === "win32" && extname(command) === ""
|
|
44
|
+
? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean)
|
|
45
|
+
: [""];
|
|
46
|
+
return (process.env.PATH ?? "")
|
|
47
|
+
.split(delimiter)
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.some((directory) => extensions.some((extension) => isExecutable(join(directory, command + extension))));
|
|
50
|
+
}
|
|
51
|
+
const strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
52
|
+
function launchOf(entry) {
|
|
53
|
+
if (!isObject(entry))
|
|
54
|
+
return undefined;
|
|
55
|
+
// OpenCode keeps the whole argv in `command`.
|
|
56
|
+
if (Array.isArray(entry.command))
|
|
57
|
+
return strings(entry.command);
|
|
58
|
+
return typeof entry.command === "string" ? [entry.command, ...strings(entry.args)] : undefined;
|
|
59
|
+
}
|
|
60
|
+
// Line-based rather than a TOML parser, to stay dependency-free: it reads the
|
|
61
|
+
// single-line `command =` / `args =` inside [mcp_servers.melra] that `setup`
|
|
62
|
+
// writes, and leaves anything fancier unchecked instead of guessing.
|
|
63
|
+
function codexLaunch(toml) {
|
|
64
|
+
let inside = false;
|
|
65
|
+
let command;
|
|
66
|
+
let args = [];
|
|
67
|
+
for (const line of toml.split(/\r?\n/)) {
|
|
68
|
+
const header = /^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(#.*)?$/.exec(line);
|
|
69
|
+
if (header !== null) {
|
|
70
|
+
inside = header[1] === "mcp_servers.melra";
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!inside)
|
|
74
|
+
continue;
|
|
75
|
+
const value = /^\s*(command|args)\s*=\s*(.+?)\s*$/.exec(line);
|
|
76
|
+
try {
|
|
77
|
+
// TOML basic strings and arrays of them are valid JSON.
|
|
78
|
+
if (value?.[1] === "command")
|
|
79
|
+
command = JSON.parse(value[2] ?? "");
|
|
80
|
+
if (value?.[1] === "args")
|
|
81
|
+
args = strings(JSON.parse(value[2] ?? ""));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A literal string or multi-line array: leave it unchecked.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return typeof command === "string" ? [command, ...args] : undefined;
|
|
88
|
+
}
|
|
89
|
+
// The `cmd:`/`command:` and `args:` lines `setup` writes into Goose's,
|
|
90
|
+
// Hermes's, and Continue's YAML, whose values are JSON-quoted. Anything written another way
|
|
91
|
+
// is left unchecked rather than guessed at.
|
|
92
|
+
function yamlLaunch(lines) {
|
|
93
|
+
let command;
|
|
94
|
+
let args = [];
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
const value = /^\s*(?:-\s+)?(cmd|command|args)\s*:\s*(.+?)\s*$/.exec(line);
|
|
97
|
+
try {
|
|
98
|
+
if (value?.[1] === "args")
|
|
99
|
+
args = strings(JSON.parse(value[2] ?? ""));
|
|
100
|
+
else if (value !== null)
|
|
101
|
+
command = JSON.parse(value[2] ?? "");
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// A plain or single-quoted scalar: leave it unchecked.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return typeof command === "string" ? [command, ...args] : undefined;
|
|
108
|
+
}
|
|
109
|
+
// The lines of the `<map>.melra` block (Goose's `extensions`, Hermes's
|
|
110
|
+
// `mcp_servers`), or undefined when there is none.
|
|
111
|
+
function yamlBlock(yaml, map) {
|
|
112
|
+
const lines = yaml.split(/\r?\n/);
|
|
113
|
+
const start = lines.findIndex((line) => line.replace(/\s*(#.*)?$/, "") === `${map}:`);
|
|
114
|
+
if (start < 0)
|
|
115
|
+
return undefined;
|
|
116
|
+
let block;
|
|
117
|
+
let depth = 0;
|
|
118
|
+
for (const line of lines.slice(start + 1)) {
|
|
119
|
+
if (/^\S/.test(line))
|
|
120
|
+
break; // the next top-level key
|
|
121
|
+
if (line.trim() === "")
|
|
122
|
+
continue;
|
|
123
|
+
const indent = /^ */.exec(line)?.[0].length ?? 0;
|
|
124
|
+
if (block !== undefined && indent <= depth)
|
|
125
|
+
break;
|
|
126
|
+
if (block !== undefined)
|
|
127
|
+
block.push(line);
|
|
128
|
+
else if (/^\s+["']?melra["']?:\s*(#.*)?$/.test(line)) {
|
|
129
|
+
block = [];
|
|
130
|
+
depth = indent;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return block;
|
|
134
|
+
}
|
|
135
|
+
function configured(base, launch) {
|
|
136
|
+
const [command] = launch ?? [];
|
|
137
|
+
if (command === undefined) {
|
|
138
|
+
return { ...base, state: "configured", note: "no command to check", problems: [] };
|
|
139
|
+
}
|
|
140
|
+
const problems = [];
|
|
141
|
+
if (!resolvesOnPath(command))
|
|
142
|
+
problems.push(`'${command}' not found on PATH`);
|
|
143
|
+
const pin = launch?.map((arg) => /^@melra\/cli@(\d\S*)$/.exec(arg)?.[1]).find(Boolean);
|
|
144
|
+
// A pin is what keeps an npx launch on one version, so after an upgrade it
|
|
145
|
+
// keeps launching the old server until the config is rewritten.
|
|
146
|
+
if (pin !== undefined && pin !== PRODUCT_VERSION) {
|
|
147
|
+
problems.push(`pinned to @melra/cli@${pin}, this is ${PRODUCT_VERSION}; fix: melra setup --client ${base.client} --write`);
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
...base,
|
|
151
|
+
state: "configured",
|
|
152
|
+
command,
|
|
153
|
+
...(pin === undefined ? {} : { pin }),
|
|
154
|
+
problems,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function inspect(client, shape, path, text) {
|
|
158
|
+
const base = { client, path };
|
|
159
|
+
const map = YAML_KEYS[shape];
|
|
160
|
+
if (map !== undefined) {
|
|
161
|
+
const block = yamlBlock(text, map);
|
|
162
|
+
return block === undefined
|
|
163
|
+
? { ...base, state: "not configured", problems: [] }
|
|
164
|
+
: configured(base, yamlLaunch(block));
|
|
165
|
+
}
|
|
166
|
+
// MELRA owns this file outright, so its existence is the configuration.
|
|
167
|
+
if (shape === "continue")
|
|
168
|
+
return configured(base, yamlLaunch(text.split(/\r?\n/)));
|
|
169
|
+
if (shape === "codex") {
|
|
170
|
+
return /^\s*\[\s*mcp_servers\.melra\s*\]/m.test(text)
|
|
171
|
+
? configured(base, codexLaunch(text))
|
|
172
|
+
: { ...base, state: "not configured", problems: [] };
|
|
173
|
+
}
|
|
174
|
+
const key = SERVER_KEYS[shape];
|
|
175
|
+
if (key === undefined) {
|
|
176
|
+
return { ...base, state: "not inspected", note: `present; ${shape} format not inspected`, problems: [] };
|
|
177
|
+
}
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
// JSONC, because Zed and VS Code settings usually carry comments and
|
|
181
|
+
// trailing commas; read with the same parser `setup --write` edits with.
|
|
182
|
+
parsed = parseJsonc(text).value;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Not even JSONC. A text search still answers the question people ask,
|
|
186
|
+
// just not what the entry launches.
|
|
187
|
+
return /"melra"\s*:/.test(text)
|
|
188
|
+
? { ...base, state: "configured", note: "not valid JSONC, found by text search; command not checked", problems: [] }
|
|
189
|
+
: { ...base, state: "not configured", note: "not valid JSONC, searched as text", problems: [] };
|
|
190
|
+
}
|
|
191
|
+
const servers = key.reduce((at, step) => (isObject(at) ? at[step] : undefined), parsed);
|
|
192
|
+
const entry = isObject(servers) ? servers.melra : undefined;
|
|
193
|
+
return entry === undefined
|
|
194
|
+
? { ...base, state: "not configured", problems: [] }
|
|
195
|
+
: configured(base, launchOf(entry));
|
|
196
|
+
}
|
|
197
|
+
// Read-only by construction: this only ever reads the files `setup --write`
|
|
198
|
+
// would write, so doctor can run against a machine without changing it.
|
|
199
|
+
export async function clientStatuses(workspace) {
|
|
200
|
+
const statuses = [];
|
|
201
|
+
for (const [client, { shape, path: pathOf }] of Object.entries(CLIENTS)) {
|
|
202
|
+
const path = pathOf?.(workspace);
|
|
203
|
+
if (path === undefined)
|
|
204
|
+
continue;
|
|
205
|
+
let text;
|
|
206
|
+
try {
|
|
207
|
+
text = await readFile(path, "utf8");
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const code = error.code;
|
|
211
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
212
|
+
continue;
|
|
213
|
+
statuses.push({ client, path, state: "unreadable", problems: [`unreadable: ${code ?? String(error)}`] });
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
statuses.push(inspect(client, shape, path, text));
|
|
217
|
+
}
|
|
218
|
+
return statuses;
|
|
219
|
+
}
|
|
220
|
+
// Which clients look installed: a config file, the directory it would go in,
|
|
221
|
+
// or the client's own executable on PATH. Any one is enough — a fresh install
|
|
222
|
+
// has a directory and no file, and a CLI client may have neither yet. The
|
|
223
|
+
// directory test is skipped where it is the workspace itself, which always
|
|
224
|
+
// exists and so says nothing about Claude Code.
|
|
225
|
+
// `/Applications` or `~/Applications`, where drag-installed apps go.
|
|
226
|
+
function macAppInstalled(app) {
|
|
227
|
+
return (process.platform === "darwin" &&
|
|
228
|
+
[join("/Applications", app), join(homedir(), "Applications", app)].some((path) => existsSync(path)));
|
|
229
|
+
}
|
|
230
|
+
export function detectClients(workspace) {
|
|
231
|
+
return Object.entries(CLIENTS).flatMap(([client, { label, path: pathOf, bin, app }]) => {
|
|
232
|
+
if (client === "generic")
|
|
233
|
+
return [];
|
|
234
|
+
const path = pathOf?.(workspace);
|
|
235
|
+
const detected = path !== undefined && existsSync(path)
|
|
236
|
+
? "config file found"
|
|
237
|
+
: path !== undefined && dirname(path) !== workspace && existsSync(dirname(path))
|
|
238
|
+
? "config folder found"
|
|
239
|
+
: bin !== undefined && resolvesOnPath(bin)
|
|
240
|
+
? `'${bin}' on PATH`
|
|
241
|
+
: app !== undefined && macAppInstalled(app)
|
|
242
|
+
? `${app} installed`
|
|
243
|
+
: undefined;
|
|
244
|
+
return [
|
|
245
|
+
{
|
|
246
|
+
client,
|
|
247
|
+
label,
|
|
248
|
+
...(path === undefined ? {} : { path }),
|
|
249
|
+
...(detected === undefined ? {} : { detected }),
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// `warn` at worst: a machine with no client configured is how SDK users run,
|
|
255
|
+
// and a client problem never stops MELRA itself from working.
|
|
256
|
+
export function clientsCheck(statuses) {
|
|
257
|
+
const hint = "run 'melra setup --client <name> --write' to add MELRA to one";
|
|
258
|
+
if (statuses.length === 0) {
|
|
259
|
+
return { name: "clients", status: "pass", detail: `no MCP client config files found; ${hint}` };
|
|
260
|
+
}
|
|
261
|
+
const lines = statuses.map(({ client, state, note, problems }) => {
|
|
262
|
+
const extra = [...(note === undefined ? [] : [note]), ...problems];
|
|
263
|
+
return `${client}: ${state}${extra.length === 0 ? "" : ` (${extra.join("; ")})`}`;
|
|
264
|
+
});
|
|
265
|
+
if (!statuses.some((status) => status.state === "configured"))
|
|
266
|
+
lines.push(hint);
|
|
267
|
+
return {
|
|
268
|
+
name: "clients",
|
|
269
|
+
status: statuses.some((status) => status.problems.length > 0) ? "warn" : "pass",
|
|
270
|
+
detail: lines.join("; "),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=client-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-status.js","sourceRoot":"","sources":["../src/client-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ServerEntry {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
env: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
type Shape = "mcpServers" | "vscode" | "zed" | "opencode" | "codex" | "goose" | "continue" | "amp" | "openclaw" | "hermes" | "copilot" | "droid" | "kilo";
|
|
7
|
+
interface Client {
|
|
8
|
+
label: string;
|
|
9
|
+
shape: Shape;
|
|
10
|
+
path?: (workspace: string) => string;
|
|
11
|
+
bin?: string;
|
|
12
|
+
app?: string;
|
|
13
|
+
next?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const CLIENTS: Record<string, Client>;
|
|
16
|
+
export declare function resolveClient(name: string): string;
|
|
17
|
+
export declare function clientConfig(name: string, entry: ServerEntry): Record<string, unknown> | string;
|
|
18
|
+
export declare function clientConfigPath(name: string, workspace: string): string | undefined;
|
|
19
|
+
export declare const isObject: (value: unknown) => value is Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Merge MELRA into the client's own config file, keeping everything else in
|
|
22
|
+
* it. The previous file is copied to `<file>.bak` first. JSON and JSONC files
|
|
23
|
+
* are edited in place by `withMelraJson`, so comments, trailing commas, and
|
|
24
|
+
* formatting outside MELRA's own entry survive; a file that is not valid JSONC
|
|
25
|
+
* is refused. Continue's file is MELRA's alone, so it is replaced whole.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeClientConfig(path: string, config: Record<string, unknown> | string, name: string): Promise<{
|
|
28
|
+
backup?: string;
|
|
29
|
+
unchanged?: true;
|
|
30
|
+
}>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=clients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAMD,KAAK,KAAK,GACN,YAAY,GACZ,QAAQ,GACR,KAAK,GACL,UAAU,GACV,OAAO,GACP,OAAO,GACP,UAAU,GACV,KAAK,GACL,UAAU,GACV,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,CAAC;AAEX,UAAU,MAAM;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAGrC,GAAG,CAAC,EAAE,MAAM,CAAC;IAGb,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAgBD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA+O1C,CAAC;AAoBF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD;AAuED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAoDlC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpF;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAgKvE;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CA4BhD"}
|