@agentcomms/whatsapp 0.17.0 → 0.18.0
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/THIRD_PARTY_LICENSES +5 -5
- package/dist/cli.mjs +2 -2
- package/dist/{dist-COPefvtq-COGS2a-K.mjs → dist-C7DG2LdL-l0mj9_dH.mjs} +55 -33
- package/dist/index.d.mts +141 -33
- package/dist/index.mjs +2 -2
- package/dist/{mcp-DXXb3Vv3-kCxsVLO4.mjs → mcp-DIH4cS6P-CIWWZQbz.mjs} +181 -68
- package/dist/{server-DChdfrt8.mjs → server-CIrWEJNB.mjs} +3 -3
- package/dist/{src-D_zzAWoS-BhK0GFdq-oo-8IJem.mjs → src-WCy6ifGf-DXrNj-15-D0oRtifb.mjs} +75 -41
- package/dist/{stdio-q4c3585j.mjs → stdio-B8UXEGiG.mjs} +28 -3
- package/dist/{stdio-CHZ2WRcx-B2_wGdA5.mjs → stdio-CyjIgrch-59zcjSEt.mjs} +9 -3
- package/dist/{stdio-entry-ZWI-4ayI.mjs → stdio-entry-BkmBd-r8.mjs} +1 -1
- package/dist/{sync-7LwbofMr.mjs → sync-BMpRzu1W.mjs} +655 -72
- package/package.json +5 -5
|
@@ -11,7 +11,7 @@ import { execFile, spawn } from "node:child_process";
|
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/version.ts
|
|
13
13
|
/** The package version, read from package.json at build time. */
|
|
14
|
-
const VERSION$1 = "0.
|
|
14
|
+
const VERSION$1 = "0.18.0";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/caller.ts
|
|
17
17
|
/** This package's name, as its manifest has it. */
|
|
@@ -28,7 +28,7 @@ const CALLER = Object.freeze({
|
|
|
28
28
|
packageName: PACKAGE_NAME
|
|
29
29
|
});
|
|
30
30
|
//#endregion
|
|
31
|
-
//#region ../core/dist/update-check-
|
|
31
|
+
//#region ../core/dist/update-check-C_PW2LlM.mjs
|
|
32
32
|
/**
|
|
33
33
|
* Every failure a user or an agent can act on carries a specific, stable code from this registry, a message, an
|
|
34
34
|
* optional one-line hint and structured `details`. The code decides the process exit status; agents and skills branch
|
|
@@ -1139,6 +1139,24 @@ const CHANNEL_SNAPSHOT = [
|
|
|
1139
1139
|
}
|
|
1140
1140
|
}
|
|
1141
1141
|
];
|
|
1142
|
+
/** Every declared service server: what core knows about it without installing it. */
|
|
1143
|
+
const SERVICE_SNAPSHOT = [{
|
|
1144
|
+
packageName: "@agentcomms/events-daemon",
|
|
1145
|
+
declaration: {
|
|
1146
|
+
kind: "service",
|
|
1147
|
+
binary: "agent-events",
|
|
1148
|
+
server: {
|
|
1149
|
+
defaultName: "events",
|
|
1150
|
+
entry: "src/mcp/server.ts",
|
|
1151
|
+
factory: "createEventsMcpServer"
|
|
1152
|
+
},
|
|
1153
|
+
operations: "src/operations",
|
|
1154
|
+
skills: {
|
|
1155
|
+
prefix: "agentcomms-",
|
|
1156
|
+
contract: "skills/_shared/contract-events.md"
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}];
|
|
1142
1160
|
const DISCLOSURE_VERSION_KINDS = [
|
|
1143
1161
|
"rule",
|
|
1144
1162
|
"target",
|
|
@@ -1326,8 +1344,8 @@ function bindingDigestOf(record) {
|
|
|
1326
1344
|
* Everywhere but Windows that shell is a POSIX one, and a word goes in single quotes, inside which nothing is special
|
|
1327
1345
|
* but the quote itself. Windows has two shells, and neither reads single quotes that way: cmd.exe does not take them
|
|
1328
1346
|
* as quotes at all, so `'C:\Profiles\First Last\outgoing'` reached the command as two words, quote marks and all, and
|
|
1329
|
-
* PowerShell does, but escapes a quote inside them by doubling it rather than as `'\''`. A
|
|
1330
|
-
* has to be safe in both
|
|
1347
|
+
* PowerShell does, but escapes a quote inside them by doubling it rather than as `'\''`. A common line printed on
|
|
1348
|
+
* Windows has to be safe in both — and safe is not enough:
|
|
1331
1349
|
* the program has to receive the same words from either. Three readers stand between the line and the program. cmd.exe
|
|
1332
1350
|
* hands the program the line as it is, and the program's own parser (the C runtime's, Node's) splits it. PowerShell
|
|
1333
1351
|
* reads the line itself, a double-quoted word with its backslashes as plain characters, and then writes a new command
|
|
@@ -1354,15 +1372,17 @@ function bindingDigestOf(record) {
|
|
|
1354
1372
|
* backslash, which the C runtime reads with the closing quote as `\"` — and doubled for it, PowerShell 7 passes both
|
|
1355
1373
|
* backslashes on where cmd.exe and Windows PowerShell pass one; and a word with `& | < > ^ ( )` but no whitespace,
|
|
1356
1374
|
* which PowerShell passes to a `.cmd` script unquoted, for cmd.exe to run `&whoami` from or to split at `|`.
|
|
1357
|
-
* - With any other word in it,
|
|
1375
|
+
* - With any other word in it, the common renderer has no line, and the printers show the command's words
|
|
1358
1376
|
* as JSON instead, saying it has to be typed. Not a line with the word left out: a placeholder in its place was
|
|
1359
1377
|
* still a command that ran — `claude mcp remove NAME` removed whatever entry was called `NAME`, and an install
|
|
1360
1378
|
* hint's `--force` replaced it — and before that, a word quoted for PowerShell's single quotes, which cmd.exe reads
|
|
1361
1379
|
* as characters, ran `whoami` from a server named `$x&whoami&`. See `commandAsJson` for why the JSON runs nothing.
|
|
1362
1380
|
*
|
|
1363
|
-
* - The program itself has to be left as it is. Quoted, it is a string to PowerShell rather than a
|
|
1364
|
-
*
|
|
1365
|
-
* quotes,
|
|
1381
|
+
* - The program itself has to be left as it is in the common form. Quoted, it is a string to PowerShell rather than a
|
|
1382
|
+
* command. When its path needs quoting, CUE-410 may emit a labelled PowerShell line: `&`, then every word in single
|
|
1383
|
+
* quotes, with an inner single quote doubled. Empty words, trailing backslashes, double quotes and control or
|
|
1384
|
+
* formatting characters retain the inert-JSON fallback because their native-program boundary cannot be proved
|
|
1385
|
+
* across the supported PowerShell versions.
|
|
1366
1386
|
*
|
|
1367
1387
|
* Everywhere else there is always a line: single quotes make any word safe.
|
|
1368
1388
|
*
|
|
@@ -1378,9 +1398,23 @@ function quoteCommand(words, platform) {
|
|
|
1378
1398
|
};
|
|
1379
1399
|
const printed = words.map(windowsShellWord);
|
|
1380
1400
|
const programBare = words.length === 0 || printed[0] === words[0];
|
|
1401
|
+
if (!printed.includes(null) && programBare) return {
|
|
1402
|
+
words,
|
|
1403
|
+
line: printed.join(" "),
|
|
1404
|
+
platform
|
|
1405
|
+
};
|
|
1406
|
+
if (!programBare && printed.slice(1).every((word) => word !== null)) {
|
|
1407
|
+
const powershell = words.map(powerShellWord);
|
|
1408
|
+
if (!powershell.includes(null)) return {
|
|
1409
|
+
words,
|
|
1410
|
+
line: `& ${powershell.join(" ")}`,
|
|
1411
|
+
platform,
|
|
1412
|
+
shell: "powershell"
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1381
1415
|
return {
|
|
1382
1416
|
words,
|
|
1383
|
-
line:
|
|
1417
|
+
line: null,
|
|
1384
1418
|
platform
|
|
1385
1419
|
};
|
|
1386
1420
|
}
|
|
@@ -1393,6 +1427,14 @@ function quotedLineWithWordsToFill(command, ...toFill) {
|
|
|
1393
1427
|
if (command.line === null) return null;
|
|
1394
1428
|
const sentinel = command.words.indexOf("--");
|
|
1395
1429
|
if (sentinel < 0) return [command.line, ...toFill].join(" ");
|
|
1430
|
+
if (command.shell === "powershell") {
|
|
1431
|
+
const printed = command.words.map((each) => powerShellWord(each));
|
|
1432
|
+
return `& ${[
|
|
1433
|
+
...printed.slice(0, sentinel),
|
|
1434
|
+
...toFill,
|
|
1435
|
+
...printed.slice(sentinel)
|
|
1436
|
+
].join(" ")}`;
|
|
1437
|
+
}
|
|
1396
1438
|
const word = command.platform === "win32" ? windowsShellWord : posixShellWord;
|
|
1397
1439
|
const printed = command.words.map((each) => word(each));
|
|
1398
1440
|
return [
|
|
@@ -1420,6 +1462,11 @@ function windowsShellWord(word) {
|
|
|
1420
1462
|
if (/[&|<>^()]/.test(word) && !/\s/.test(word)) return null;
|
|
1421
1463
|
return `"${word}"`;
|
|
1422
1464
|
}
|
|
1465
|
+
/** One native-command word in a PowerShell single-quoted literal, or null when a one-line call is not provable. */
|
|
1466
|
+
function powerShellWord(word) {
|
|
1467
|
+
if (word === "" || word.endsWith("\\") || /["\u201C-\u201E]|[\p{C}\p{Zl}\p{Zp}]/u.test(word)) return null;
|
|
1468
|
+
return `'${word.replaceAll("'", "''")}'`;
|
|
1469
|
+
}
|
|
1423
1470
|
/**
|
|
1424
1471
|
* The words of a command as a JSON array, for a command that cannot be printed as a line: something to read, and to
|
|
1425
1472
|
* parse, and nothing to run.
|
|
@@ -1450,17 +1497,19 @@ function commandAsJson(words) {
|
|
|
1450
1497
|
/** What is said beside a command shown as JSON. */
|
|
1451
1498
|
const TO_TYPE = "the command's words, written as JSON: one of them cannot be quoted the same way for cmd.exe and for PowerShell, so type the command yourself, with that word quoted for the shell you use";
|
|
1452
1499
|
/** What is said beside a command shown as JSON because its program's path needs quotes. */
|
|
1453
|
-
const TO_TYPE_PROGRAM = "the command's words, written as JSON: the program's path needs
|
|
1500
|
+
const TO_TYPE_PROGRAM = "the command's words, written as JSON: the program's path needs quoting and at least one word cannot be represented safely in a one-line PowerShell call, so type the command yourself, with each word quoted for the shell you use and, in PowerShell, `&` before the program";
|
|
1454
1501
|
function toType(command) {
|
|
1455
1502
|
return programNeedsQuotes(command) ? TO_TYPE_PROGRAM : TO_TYPE;
|
|
1456
1503
|
}
|
|
1457
1504
|
/** Quoted words in backticks, for a sentence — as words in JSON, saying it has to be typed, when there is no line. */
|
|
1458
1505
|
function inlineQuoted(command) {
|
|
1459
|
-
|
|
1506
|
+
if (command.line === null) return `\`${commandAsJson(command.words)}\` (${toType(command)})`;
|
|
1507
|
+
return `\`${command.line}\`${command.shell === "powershell" ? " (PowerShell)" : ""}`;
|
|
1460
1508
|
}
|
|
1461
1509
|
/** Quoted words as text of their own — a list's line, a field's value — or as words in JSON, saying to type them. */
|
|
1462
1510
|
function quotedText(command) {
|
|
1463
|
-
|
|
1511
|
+
if (command.line === null) return `${commandAsJson(command.words)} (${toType(command)})`;
|
|
1512
|
+
return command.shell === "powershell" ? `PowerShell:\n${command.line}` : command.line;
|
|
1464
1513
|
}
|
|
1465
1514
|
/**
|
|
1466
1515
|
* The same command with words the agent is to fill in — a placeholder such as `<folder>` — before its first `--`: for
|
|
@@ -1924,7 +1973,7 @@ function handoffSentence(handoff, say, options = {}) {
|
|
|
1924
1973
|
function handoffSentenceToFill(handoff, toFill, say, options = {}) {
|
|
1925
1974
|
if (!isCommand(handoff)) return noCommand(handoff, options);
|
|
1926
1975
|
const line = lineWithWordsToFill(handoff, ...toFill);
|
|
1927
|
-
if (line !== null) return say(`\`${line}
|
|
1976
|
+
if (line !== null) return say(`\`${line}\`${handoff.shell === "powershell" ? " (PowerShell)" : ""}`);
|
|
1928
1977
|
return say(inlineQuoted(quotedWithWordsToFill(handoff, toFill)));
|
|
1929
1978
|
}
|
|
1930
1979
|
/**
|
|
@@ -2124,7 +2173,7 @@ function nearestPackage(start) {
|
|
|
2124
2173
|
* Whether `path` is `root` or inside it, by whole path segments: `/x/core-evil` is not inside `/x/core`, which a
|
|
2125
2174
|
* comparison of strings would say it was. Both are taken as given; a caller compares real paths with real paths.
|
|
2126
2175
|
*/
|
|
2127
|
-
function isWithin(root, path) {
|
|
2176
|
+
function isWithin$1(root, path) {
|
|
2128
2177
|
const between = relative(resolve(root), resolve(path));
|
|
2129
2178
|
return between === "" || !isAbsolute(between) && between !== ".." && !/^\.\.[\\/]/.test(between);
|
|
2130
2179
|
}
|
|
@@ -2194,15 +2243,18 @@ var ExternalCommand = class {
|
|
|
2194
2243
|
#reason;
|
|
2195
2244
|
/** The words, as the program is to receive them. */
|
|
2196
2245
|
words;
|
|
2197
|
-
/** The line to paste; null when
|
|
2246
|
+
/** The line to paste; null when no supported common or labelled shell rendering is safe. */
|
|
2198
2247
|
line;
|
|
2199
2248
|
platform;
|
|
2249
|
+
shell;
|
|
2200
2250
|
constructor(gate, words, platform, reason) {
|
|
2201
2251
|
if (gate !== GATE$1) throw new TypeError("an external command is made by externalCommand, and only there");
|
|
2202
2252
|
this.#reason = reason;
|
|
2203
2253
|
this.words = Object.freeze([...words]);
|
|
2204
|
-
|
|
2254
|
+
const quoted = quoteCommand(this.words, platform);
|
|
2255
|
+
this.line = quoted.line;
|
|
2205
2256
|
this.platform = platform;
|
|
2257
|
+
if (quoted.shell !== void 0) this.shell = quoted.shell;
|
|
2206
2258
|
Object.freeze(this);
|
|
2207
2259
|
}
|
|
2208
2260
|
/** Why this program, which is not part of this suite, is one a person may be told to run. */
|
|
@@ -2916,6 +2968,343 @@ function commandLinePackage(command, args) {
|
|
|
2916
2968
|
const line = [command, ...withoutPathOptions(args).words].join(" ");
|
|
2917
2969
|
return /(@[\w.-]+\/[\w.-]+|(?<=\s)[\w.-]+-mcp)(?=@|\s|$)/.exec(line)?.[1];
|
|
2918
2970
|
}
|
|
2971
|
+
const RECEIPT_VERSION = 1;
|
|
2972
|
+
const RECEIPT_FILE = "managed-shims.json";
|
|
2973
|
+
const MUTATION_LOCK_FILE = "managed-shims.lock";
|
|
2974
|
+
const POSIX_SHIM_MODE = 493;
|
|
2975
|
+
const WINDOWS_SHIM_NOTICE = "Managed CLI shims are not offered on Windows yet because cmd.exe re-parses arguments forwarded through .cmd files, including shell metacharacters. Use the full-path command or the labelled PowerShell & line the result provides.";
|
|
2976
|
+
function platformOf(context) {
|
|
2977
|
+
return context.platform ?? process.platform;
|
|
2978
|
+
}
|
|
2979
|
+
/** Why the selected platform has no managed shim in this release. */
|
|
2980
|
+
function managedShimUnsupportedNotice(context = {}) {
|
|
2981
|
+
return platformOf(context) === "win32" ? WINDOWS_SHIM_NOTICE : "Managed CLI shims are offered only on macOS and Linux in this release. Use the full-path command the result provides.";
|
|
2982
|
+
}
|
|
2983
|
+
/** The per-user directory managed installs put their command shims in. */
|
|
2984
|
+
function managedShimDirectory(context) {
|
|
2985
|
+
if (!managedShimsSupported(context)) throw new CommsError("CONFIG", managedShimUnsupportedNotice(context));
|
|
2986
|
+
return join(homeDirectory(context.env), ".local", "bin");
|
|
2987
|
+
}
|
|
2988
|
+
/** Managed CLI shims are POSIX-only until Windows has an argument-forwarding form that does not re-parse input. */
|
|
2989
|
+
function managedShimsSupported(context = {}) {
|
|
2990
|
+
const platform = platformOf(context);
|
|
2991
|
+
return platform === "darwin" || platform === "linux";
|
|
2992
|
+
}
|
|
2993
|
+
/** A managed shim's name: one plain command word, never a path, so joining it to the directory stays inside it. */
|
|
2994
|
+
function isSafeBinary(binary) {
|
|
2995
|
+
return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(binary);
|
|
2996
|
+
}
|
|
2997
|
+
function checkedBinary(binary) {
|
|
2998
|
+
if (!isSafeBinary(binary)) throw new CommsError("CONFIG", `${JSON.stringify(binary)} is not a safe managed CLI binary name`);
|
|
2999
|
+
return binary;
|
|
3000
|
+
}
|
|
3001
|
+
/** The exact path for one declared binary's managed shim. */
|
|
3002
|
+
function managedShimPath(context, binary) {
|
|
3003
|
+
return join(managedShimDirectory(context), checkedBinary(binary));
|
|
3004
|
+
}
|
|
3005
|
+
function receiptPath(context) {
|
|
3006
|
+
return join(context.dataDir, RECEIPT_FILE);
|
|
3007
|
+
}
|
|
3008
|
+
function emptyReceipt() {
|
|
3009
|
+
return {
|
|
3010
|
+
version: RECEIPT_VERSION,
|
|
3011
|
+
shims: {}
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
async function readReceipt(context) {
|
|
3015
|
+
let parsed;
|
|
3016
|
+
try {
|
|
3017
|
+
parsed = JSON.parse(await readFile(receiptPath(context), "utf8"));
|
|
3018
|
+
} catch (error) {
|
|
3019
|
+
return error.code === "ENOENT" ? emptyReceipt() : null;
|
|
3020
|
+
}
|
|
3021
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== RECEIPT_VERSION || typeof parsed.shims !== "object" || parsed.shims === null) return null;
|
|
3022
|
+
const shims = {};
|
|
3023
|
+
for (const [binary, value] of Object.entries(parsed.shims)) {
|
|
3024
|
+
if (!isSafeBinary(binary) || typeof value !== "object" || value === null || ![
|
|
3025
|
+
"path",
|
|
3026
|
+
"digest",
|
|
3027
|
+
"node",
|
|
3028
|
+
"entry"
|
|
3029
|
+
].every((key) => typeof value[key] === "string")) return null;
|
|
3030
|
+
shims[binary] = value;
|
|
3031
|
+
}
|
|
3032
|
+
return {
|
|
3033
|
+
version: RECEIPT_VERSION,
|
|
3034
|
+
shims
|
|
3035
|
+
};
|
|
3036
|
+
}
|
|
3037
|
+
async function writeReceipt(context, receipt) {
|
|
3038
|
+
await writeFileAtomic(receiptPath(context), `${JSON.stringify(receipt, null, 2)}\n`);
|
|
3039
|
+
}
|
|
3040
|
+
function sha256(bytes) {
|
|
3041
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
3042
|
+
}
|
|
3043
|
+
function posixWord(word) {
|
|
3044
|
+
return `'${word.replaceAll("'", `'"'"'`)}'`;
|
|
3045
|
+
}
|
|
3046
|
+
function shimSource(spec) {
|
|
3047
|
+
return `#!/bin/sh\n# managed CLI shim v1\nexec ${spec.command.map(posixWord).join(" ")} "$@"\n`;
|
|
3048
|
+
}
|
|
3049
|
+
async function pinShimDirectory(context, create) {
|
|
3050
|
+
const publicDirectory = managedShimDirectory(context);
|
|
3051
|
+
if (create) await mkdir(publicDirectory, {
|
|
3052
|
+
recursive: true,
|
|
3053
|
+
mode: 493
|
|
3054
|
+
});
|
|
3055
|
+
let resolved;
|
|
3056
|
+
try {
|
|
3057
|
+
resolved = await realpath(publicDirectory);
|
|
3058
|
+
} catch (error) {
|
|
3059
|
+
if (!create && error.code === "ENOENT") return null;
|
|
3060
|
+
throw error;
|
|
3061
|
+
}
|
|
3062
|
+
return {
|
|
3063
|
+
public: publicDirectory,
|
|
3064
|
+
resolved
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
3067
|
+
async function assertPinnedDirectory(directory) {
|
|
3068
|
+
let now;
|
|
3069
|
+
try {
|
|
3070
|
+
now = await realpath(directory.public);
|
|
3071
|
+
} catch {
|
|
3072
|
+
throw new CommsError("CONFIG", `the managed shim directory's resolved directory changed from ${directory.resolved} during the operation`, { hint: "Nothing was changed. Inspect the directory link, then run the install, update or prune again." });
|
|
3073
|
+
}
|
|
3074
|
+
if (now !== directory.resolved) throw new CommsError("CONFIG", `the managed shim directory's resolved directory changed from ${directory.resolved} to ${now} during the operation`, { hint: "Nothing was changed. Inspect the directory link, then run the install, update or prune again." });
|
|
3075
|
+
}
|
|
3076
|
+
async function regularFileBytes(path) {
|
|
3077
|
+
try {
|
|
3078
|
+
if (!(await lstat(path)).isFile()) return null;
|
|
3079
|
+
return await readFile(path, "utf8");
|
|
3080
|
+
} catch (error) {
|
|
3081
|
+
if (error.code === "ENOENT") return null;
|
|
3082
|
+
throw error;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
async function pathExists(path) {
|
|
3086
|
+
try {
|
|
3087
|
+
await lstat(path);
|
|
3088
|
+
return true;
|
|
3089
|
+
} catch (error) {
|
|
3090
|
+
if (error.code === "ENOENT") return false;
|
|
3091
|
+
throw error;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
/** Serialize this installation's receipt and shim mutations across its processes. */
|
|
3095
|
+
async function withMutationLock(context, change) {
|
|
3096
|
+
await mkdir(context.dataDir, {
|
|
3097
|
+
recursive: true,
|
|
3098
|
+
mode: 448
|
|
3099
|
+
});
|
|
3100
|
+
return withFileLock(join(context.dataDir, MUTATION_LOCK_FILE), change, {
|
|
3101
|
+
timeoutMs: 5e3,
|
|
3102
|
+
staleMs: 3e4,
|
|
3103
|
+
renewMs: 1e4,
|
|
3104
|
+
...context.lockOptions
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
/** Publish complete shim bytes only if the target is still absent. */
|
|
3108
|
+
async function createShimExclusive(directory, path, source) {
|
|
3109
|
+
const nonce = randomBytes(6).toString("hex");
|
|
3110
|
+
const temp = join(directory.resolved, `.${basename(path)}.${nonce}.tmp`);
|
|
3111
|
+
await assertPinnedDirectory(directory);
|
|
3112
|
+
const handle = await open(temp, "wx", POSIX_SHIM_MODE);
|
|
3113
|
+
try {
|
|
3114
|
+
await handle.writeFile(source);
|
|
3115
|
+
await handle.sync();
|
|
3116
|
+
} finally {
|
|
3117
|
+
await handle.close();
|
|
3118
|
+
}
|
|
3119
|
+
await assertPinnedDirectory(directory);
|
|
3120
|
+
await chmod(temp, POSIX_SHIM_MODE);
|
|
3121
|
+
try {
|
|
3122
|
+
await assertPinnedDirectory(directory);
|
|
3123
|
+
await link(temp, path);
|
|
3124
|
+
} finally {
|
|
3125
|
+
await assertPinnedDirectory(directory);
|
|
3126
|
+
await rm(temp, { force: true });
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
/** Restore an object moved aside without ever replacing a raced object at its public path. */
|
|
3130
|
+
async function restoreMoved(directory, path, moved) {
|
|
3131
|
+
try {
|
|
3132
|
+
await assertPinnedDirectory(directory);
|
|
3133
|
+
await link(moved, path);
|
|
3134
|
+
} catch (error) {
|
|
3135
|
+
if (error.code === "EEXIST") return false;
|
|
3136
|
+
throw error;
|
|
3137
|
+
}
|
|
3138
|
+
await assertPinnedDirectory(directory);
|
|
3139
|
+
await rm(moved);
|
|
3140
|
+
return true;
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* Move the exact current object aside atomically, then prove that object still has the owned digest. A raced foreign
|
|
3144
|
+
* file is put back; if another object has already occupied its path, both are preserved and the change is refused.
|
|
3145
|
+
*/
|
|
3146
|
+
async function moveOwned(directory, path, digest, suffix, before) {
|
|
3147
|
+
await before?.(path);
|
|
3148
|
+
await assertPinnedDirectory(directory);
|
|
3149
|
+
const moved = join(directory.resolved, `.${basename(path)}.${randomBytes(6).toString("hex")}.${suffix}`);
|
|
3150
|
+
try {
|
|
3151
|
+
await rename(path, moved);
|
|
3152
|
+
} catch (error) {
|
|
3153
|
+
if (error.code === "ENOENT") return null;
|
|
3154
|
+
throw error;
|
|
3155
|
+
}
|
|
3156
|
+
const bytes = await regularFileBytes(moved);
|
|
3157
|
+
if (bytes !== null && sha256(bytes) === digest) return moved;
|
|
3158
|
+
if (!await restoreMoved(directory, path, moved)) throw new CommsError("CONFIG", `${path} changed during a managed shim operation; its bytes were preserved at ${moved}`, { hint: "Resolve the two files by hand, then run the install, update or prune again." });
|
|
3159
|
+
return null;
|
|
3160
|
+
}
|
|
3161
|
+
/** Remove only the exact object whose bytes prove it is still owned. */
|
|
3162
|
+
async function removeOwned(directory, path, digest, before) {
|
|
3163
|
+
const moved = await moveOwned(directory, path, digest, "remove", before);
|
|
3164
|
+
if (moved === null) return false;
|
|
3165
|
+
await assertPinnedDirectory(directory);
|
|
3166
|
+
await rm(moved);
|
|
3167
|
+
return true;
|
|
3168
|
+
}
|
|
3169
|
+
function normalPath(path, platform) {
|
|
3170
|
+
const trimmed = path.replace(/[\\/]+$/, "");
|
|
3171
|
+
return platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
3172
|
+
}
|
|
3173
|
+
function directoryOnPath(context, directory) {
|
|
3174
|
+
const platform = platformOf(context);
|
|
3175
|
+
const value = context.env.PATH ?? context.env.Path ?? "";
|
|
3176
|
+
const separator = platform === "win32" ? ";" : ":";
|
|
3177
|
+
return value.split(separator).filter(Boolean).some((part) => normalPath(part, platform) === normalPath(directory, platform));
|
|
3178
|
+
}
|
|
3179
|
+
function pathNotice(directory) {
|
|
3180
|
+
return `The shim directory ${directory} is not on PATH. For this shell, run: export PATH=${posixWord(directory)}:"$PATH". No shell profile was edited.`;
|
|
3181
|
+
}
|
|
3182
|
+
/** Create or repoint one shim, but only while its target is free or still matches this installation's receipt. */
|
|
3183
|
+
async function writeManagedShim(context, spec) {
|
|
3184
|
+
checkedBinary(spec.binary);
|
|
3185
|
+
const directory = await pinShimDirectory(context, true);
|
|
3186
|
+
if (directory === null) throw new CommsError("UNEXPECTED", "the managed shim directory was not created");
|
|
3187
|
+
const publicPath = join(directory.public, spec.binary);
|
|
3188
|
+
const path = join(directory.resolved, spec.binary);
|
|
3189
|
+
const result = (status) => {
|
|
3190
|
+
const onPath = directoryOnPath(context, directory.public);
|
|
3191
|
+
return {
|
|
3192
|
+
path: publicPath,
|
|
3193
|
+
directory: directory.public,
|
|
3194
|
+
status,
|
|
3195
|
+
onPath,
|
|
3196
|
+
...onPath ? {} : { pathNotice: pathNotice(directory.public) }
|
|
3197
|
+
};
|
|
3198
|
+
};
|
|
3199
|
+
return withMutationLock(context, async () => {
|
|
3200
|
+
const receipt = await readReceipt(context);
|
|
3201
|
+
const recorded = receipt?.shims[spec.binary];
|
|
3202
|
+
const current = await regularFileBytes(path);
|
|
3203
|
+
const owned = receipt !== null && recorded !== void 0 && recorded.path === path && current !== null && sha256(current) === recorded.digest;
|
|
3204
|
+
if (await pathExists(path) && !owned) return result("foreign");
|
|
3205
|
+
if (receipt === null || recorded !== void 0 && recorded.path !== path) return result("foreign");
|
|
3206
|
+
const node = spec.command[0];
|
|
3207
|
+
if (node === void 0) throw new CommsError("CONFIG", "the managed shim command has no program");
|
|
3208
|
+
const source = shimSource(spec);
|
|
3209
|
+
const digest = sha256(source);
|
|
3210
|
+
let previous = null;
|
|
3211
|
+
if (owned) {
|
|
3212
|
+
previous = await moveOwned(directory, path, recorded.digest, "old", context.hooks?.beforeReplace);
|
|
3213
|
+
if (previous === null) return result("foreign");
|
|
3214
|
+
}
|
|
3215
|
+
try {
|
|
3216
|
+
await createShimExclusive(directory, path, source);
|
|
3217
|
+
} catch (error) {
|
|
3218
|
+
if (previous !== null) {
|
|
3219
|
+
if (!await restoreMoved(directory, path, previous)) throw new CommsError("CONFIG", `${path} was occupied while its owned shim was being updated; the old shim remains at ${previous}`, {
|
|
3220
|
+
hint: "Resolve the two files by hand, then run the managed install again.",
|
|
3221
|
+
cause: error
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
if (error.code === "EEXIST") return result("foreign");
|
|
3225
|
+
throw error;
|
|
3226
|
+
}
|
|
3227
|
+
receipt.shims[spec.binary] = {
|
|
3228
|
+
path,
|
|
3229
|
+
digest,
|
|
3230
|
+
node,
|
|
3231
|
+
entry: spec.entry
|
|
3232
|
+
};
|
|
3233
|
+
try {
|
|
3234
|
+
await assertPinnedDirectory(directory);
|
|
3235
|
+
await writeReceipt(context, receipt);
|
|
3236
|
+
} catch (error) {
|
|
3237
|
+
const removed = await removeOwned(directory, path, digest);
|
|
3238
|
+
if (previous !== null && removed && !await restoreMoved(directory, path, previous)) throw new CommsError("CONFIG", `the shim receipt failed and the old shim could not be restored from ${previous}`, {
|
|
3239
|
+
hint: "Restore that file to the reported shim path, then run the managed install again.",
|
|
3240
|
+
cause: error
|
|
3241
|
+
});
|
|
3242
|
+
throw error;
|
|
3243
|
+
}
|
|
3244
|
+
if (previous !== null) {
|
|
3245
|
+
await assertPinnedDirectory(directory);
|
|
3246
|
+
await rm(previous);
|
|
3247
|
+
}
|
|
3248
|
+
return result(owned ? "updated" : "created");
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
function isWithin(root, path) {
|
|
3252
|
+
const child = relative(root, path);
|
|
3253
|
+
return child === "" || !child.startsWith("..") && !isAbsolute(child);
|
|
3254
|
+
}
|
|
3255
|
+
/** Remove unchanged owned shims whose recorded entry is inside one of the runtimes prune removed. */
|
|
3256
|
+
async function removeManagedShimsForRuntimes(context, runtimes, options = {}) {
|
|
3257
|
+
if (!managedShimsSupported(context)) return {
|
|
3258
|
+
removed: [],
|
|
3259
|
+
kept: []
|
|
3260
|
+
};
|
|
3261
|
+
const directory = await pinShimDirectory(context, false);
|
|
3262
|
+
if (directory === null) return {
|
|
3263
|
+
removed: [],
|
|
3264
|
+
kept: []
|
|
3265
|
+
};
|
|
3266
|
+
return withMutationLock(context, async () => {
|
|
3267
|
+
const receipt = await readReceipt(context);
|
|
3268
|
+
if (receipt === null) return {
|
|
3269
|
+
removed: [],
|
|
3270
|
+
kept: []
|
|
3271
|
+
};
|
|
3272
|
+
const removed = [];
|
|
3273
|
+
const kept = [];
|
|
3274
|
+
let changed = false;
|
|
3275
|
+
for (const [binary, recorded] of Object.entries(receipt.shims)) {
|
|
3276
|
+
if (!runtimes.some((runtime) => isWithin(runtime, recorded.entry))) continue;
|
|
3277
|
+
const publicPath = join(directory.public, binary);
|
|
3278
|
+
const path = join(directory.resolved, binary);
|
|
3279
|
+
if (recorded.path !== path) {
|
|
3280
|
+
kept.push(recorded.path);
|
|
3281
|
+
continue;
|
|
3282
|
+
}
|
|
3283
|
+
if (options.dryRun) {
|
|
3284
|
+
const bytes = await regularFileBytes(path);
|
|
3285
|
+
if (bytes !== null && sha256(bytes) === recorded.digest) removed.push(publicPath);
|
|
3286
|
+
else if (await pathExists(path)) kept.push(publicPath);
|
|
3287
|
+
continue;
|
|
3288
|
+
}
|
|
3289
|
+
if (await removeOwned(directory, path, recorded.digest, context.hooks?.beforeRemove)) {
|
|
3290
|
+
delete receipt.shims[binary];
|
|
3291
|
+
changed = true;
|
|
3292
|
+
removed.push(publicPath);
|
|
3293
|
+
} else if (!await pathExists(path)) {
|
|
3294
|
+
delete receipt.shims[binary];
|
|
3295
|
+
changed = true;
|
|
3296
|
+
} else kept.push(publicPath);
|
|
3297
|
+
}
|
|
3298
|
+
if (changed) {
|
|
3299
|
+
await assertPinnedDirectory(directory);
|
|
3300
|
+
await writeReceipt(context, receipt);
|
|
3301
|
+
}
|
|
3302
|
+
return {
|
|
3303
|
+
removed,
|
|
3304
|
+
kept
|
|
3305
|
+
};
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
2919
3308
|
/**
|
|
2920
3309
|
* The programs this package starts, named so that no folder it happens to be in can stand in for them.
|
|
2921
3310
|
*
|
|
@@ -4032,8 +4421,8 @@ async function mergeIntoJsonConfig(path, client, name, entry) {
|
|
|
4032
4421
|
}
|
|
4033
4422
|
/** Starts the server exactly as a client would, and completes the handshake. */
|
|
4034
4423
|
async function verifyEntry(entry, product) {
|
|
4035
|
-
const { Client } = await import("./dist-
|
|
4036
|
-
const { StdioClientTransport } = await import("./stdio-
|
|
4424
|
+
const { Client } = await import("./dist-C7DG2LdL-l0mj9_dH.mjs");
|
|
4425
|
+
const { StdioClientTransport } = await import("./stdio-CyjIgrch-59zcjSEt.mjs");
|
|
4037
4426
|
const client = new Client({
|
|
4038
4427
|
name: `${product.binary}-install-check`,
|
|
4039
4428
|
version: product.version
|
|
@@ -4158,7 +4547,11 @@ async function pruneManagedRuntimes(context, product, options = {}) {
|
|
|
4158
4547
|
runtimeDir: join(context.core.paths.dataDir, "runtime"),
|
|
4159
4548
|
dryRun: options.dryRun === true,
|
|
4160
4549
|
removed: [],
|
|
4161
|
-
kept: []
|
|
4550
|
+
kept: [],
|
|
4551
|
+
shims: {
|
|
4552
|
+
removed: [],
|
|
4553
|
+
kept: []
|
|
4554
|
+
}
|
|
4162
4555
|
};
|
|
4163
4556
|
const candidates = [];
|
|
4164
4557
|
for (const { path, version } of await listManagedRuntimes(context.core.paths.dataDir, product.packageName)) {
|
|
@@ -4225,6 +4618,11 @@ async function pruneManagedRuntimes(context, product, options = {}) {
|
|
|
4225
4618
|
const left = handedOut.filter((record) => !gone.some((path) => mentions([path], record.runtime)));
|
|
4226
4619
|
if (left.length !== handedOut.length) await writeFileAtomic(ledger, left.map((record) => `${JSON.stringify(record)}\n`).join(""));
|
|
4227
4620
|
}
|
|
4621
|
+
if (result.removed.length > 0) result.shims = await removeManagedShimsForRuntimes({
|
|
4622
|
+
env: context.env,
|
|
4623
|
+
dataDir: context.core.paths.dataDir,
|
|
4624
|
+
platform: context.platform
|
|
4625
|
+
}, result.removed.map((item) => item.path), { dryRun: result.dryRun });
|
|
4228
4626
|
return result;
|
|
4229
4627
|
}
|
|
4230
4628
|
/** Every runtime the installer handed out, or null when the record is there and cannot be read. */
|
|
@@ -4357,6 +4755,8 @@ function rivalWordWarnings(servers, word, can, product) {
|
|
|
4357
4755
|
}
|
|
4358
4756
|
/** Every channel in core's snapshot, the core first. */
|
|
4359
4757
|
const CHANNELS = Object.freeze(CHANNEL_SNAPSHOT.map((entry) => entry.manifest.channel));
|
|
4758
|
+
/** Every declared MCP server, channels first and then services, with no separate hand-kept list. */
|
|
4759
|
+
const SERVERS = Object.freeze([...CHANNELS, ...SERVICE_SNAPSHOT.map((entry) => entry.declaration.server.defaultName)]);
|
|
4360
4760
|
/**
|
|
4361
4761
|
* The value a pin's flag carries in an entry's arguments: `--account acme/resend`, or `--account=acme/resend` — the
|
|
4362
4762
|
* first given, in either form.
|
|
@@ -4417,8 +4817,28 @@ function serverFactsOf({ packageName, manifest }) {
|
|
|
4417
4817
|
};
|
|
4418
4818
|
}
|
|
4419
4819
|
const CHANNEL_SERVERS = Object.freeze(Object.fromEntries(CHANNEL_SNAPSHOT.map((entry) => [entry.manifest.channel, serverFactsOf(entry)])));
|
|
4820
|
+
/** Service server facts, derived from each service package's declaration. */
|
|
4821
|
+
const SERVICE_SERVERS = Object.freeze(Object.fromEntries(SERVICE_SNAPSHOT.map(({ packageName, declaration }) => [declaration.server.defaultName, {
|
|
4822
|
+
packageName,
|
|
4823
|
+
binary: declaration.binary,
|
|
4824
|
+
defaultServerName: declaration.server.defaultName,
|
|
4825
|
+
npxPackage: packageName,
|
|
4826
|
+
npxArgs: ["mcp"],
|
|
4827
|
+
serverArgs: () => [],
|
|
4828
|
+
narrowingOf: () => ({})
|
|
4829
|
+
}])));
|
|
4830
|
+
/** All managed servers, derived from channel manifests and service declarations. */
|
|
4831
|
+
const SERVERS_BY_ID = Object.freeze({
|
|
4832
|
+
...CHANNEL_SERVERS,
|
|
4833
|
+
...SERVICE_SERVERS
|
|
4834
|
+
});
|
|
4420
4835
|
/** How each server is named to a person: in a preview, and in what a tool returns. */
|
|
4421
4836
|
const CHANNEL_LABELS = Object.freeze(Object.fromEntries(CHANNEL_SNAPSHOT.map((entry) => [entry.manifest.channel, entry.manifest.label])));
|
|
4837
|
+
/** Labels for every managed server; a service declares its public server name. */
|
|
4838
|
+
const SERVER_LABELS = Object.freeze({
|
|
4839
|
+
...CHANNEL_LABELS,
|
|
4840
|
+
...Object.fromEntries(SERVICE_SNAPSHOT.map(({ declaration }) => [declaration.server.defaultName, declaration.server.defaultName]))
|
|
4841
|
+
});
|
|
4422
4842
|
/** A channel's manifest, from core's snapshot. */
|
|
4423
4843
|
function channelManifest(channel) {
|
|
4424
4844
|
return CHANNEL_SNAPSHOT.find((entry) => entry.manifest.channel === channel)?.manifest;
|
|
@@ -4426,19 +4846,32 @@ function channelManifest(channel) {
|
|
|
4426
4846
|
function isChannel(value) {
|
|
4427
4847
|
return typeof value === "string" && CHANNELS.includes(value);
|
|
4428
4848
|
}
|
|
4849
|
+
/** Whether a word identifies any channel or service server in this release. */
|
|
4850
|
+
function isServer(value) {
|
|
4851
|
+
return typeof value === "string" && SERVERS.includes(value);
|
|
4852
|
+
}
|
|
4429
4853
|
/** Refuses a word that is not a channel of this release, naming the ones that are. */
|
|
4430
4854
|
function notAChannel(value) {
|
|
4431
4855
|
return new CommsError("USAGE", `"${String(value)}" is not a channel`, { hint: `One of: ${CHANNELS.join(", ")}.` });
|
|
4432
4856
|
}
|
|
4857
|
+
/** Refuses a word that is not a declared server of this release. */
|
|
4858
|
+
function notAServer(value) {
|
|
4859
|
+
return new CommsError("USAGE", `"${String(value)}" is not a server`, { hint: `One of: ${SERVERS.join(", ")}.` });
|
|
4860
|
+
}
|
|
4433
4861
|
/** A channel's server facts, by its word; refused for a word that is not a channel. */
|
|
4434
4862
|
function channelServer(channel) {
|
|
4435
4863
|
if (!isChannel(channel)) throw notAChannel(channel);
|
|
4436
4864
|
return CHANNEL_SERVERS[channel];
|
|
4437
4865
|
}
|
|
4438
|
-
/**
|
|
4439
|
-
function
|
|
4440
|
-
if (!
|
|
4441
|
-
return
|
|
4866
|
+
/** A channel or service's server facts, by its declared server name. */
|
|
4867
|
+
function serverFacts(server) {
|
|
4868
|
+
if (!isServer(server)) throw notAServer(server);
|
|
4869
|
+
return SERVERS_BY_ID[server];
|
|
4870
|
+
}
|
|
4871
|
+
/** How a channel or service server is named to a person. */
|
|
4872
|
+
function serverLabel(server) {
|
|
4873
|
+
if (!isServer(server)) throw notAServer(server);
|
|
4874
|
+
return SERVER_LABELS[server];
|
|
4442
4875
|
}
|
|
4443
4876
|
/** A channel's manifest, by its word; refused for a word that is not a channel. */
|
|
4444
4877
|
function requireChannelManifest(channel) {
|
|
@@ -8862,7 +9295,7 @@ function folding(platform) {
|
|
|
8862
9295
|
return platform === "win32" ? (text) => text.toLowerCase() : (text) => text;
|
|
8863
9296
|
}
|
|
8864
9297
|
/** The package version, read from package.json at build time. */
|
|
8865
|
-
const VERSION = "0.
|
|
9298
|
+
const VERSION = "0.18.0";
|
|
8866
9299
|
/**
|
|
8867
9300
|
* Versions as semver writes and orders them — what an update compares, and what the daily update check's reader needs
|
|
8868
9301
|
* to decide whether the release it last heard of is newer than the one running.
|
|
@@ -8998,15 +9431,18 @@ var PrintedCommand = class {
|
|
|
8998
9431
|
#entry;
|
|
8999
9432
|
/** The words, as the program is to receive them: this Node, its flags, the entry, then the CLI's own words. */
|
|
9000
9433
|
words;
|
|
9001
|
-
/** The line to paste; null when
|
|
9434
|
+
/** The line to paste; null when no supported common or labelled shell rendering is safe. */
|
|
9002
9435
|
line;
|
|
9003
9436
|
platform;
|
|
9437
|
+
shell;
|
|
9004
9438
|
constructor(gate, words, platform, entry) {
|
|
9005
9439
|
if (gate !== GATE) throw new TypeError("a printed command is made by locateCliCommand, and only there");
|
|
9006
9440
|
this.#entry = entry;
|
|
9007
9441
|
this.words = Object.freeze([...words]);
|
|
9008
|
-
|
|
9442
|
+
const quoted = quoteCommand(this.words, platform);
|
|
9443
|
+
this.line = quoted.line;
|
|
9009
9444
|
this.platform = platform;
|
|
9445
|
+
if (quoted.shell !== void 0) this.shell = quoted.shell;
|
|
9010
9446
|
Object.freeze(this);
|
|
9011
9447
|
}
|
|
9012
9448
|
/**
|
|
@@ -9033,7 +9469,7 @@ const STRIP_TYPES = "--experimental-strip-types";
|
|
|
9033
9469
|
const TRANSFORM_TYPES = "--experimental-transform-types";
|
|
9034
9470
|
/** The suite's packages, by name, and its channel packages, which reach core through their own dependency on it. */
|
|
9035
9471
|
const SUITE_PACKAGES = new Set(CHANNEL_SNAPSHOT.map(({ packageName }) => packageName));
|
|
9036
|
-
const CHANNEL_PACKAGES
|
|
9472
|
+
const CHANNEL_PACKAGES = new Set(CHANNEL_SNAPSHOT.filter(({ manifest }) => manifest.channel !== "core").map(({ packageName }) => packageName));
|
|
9037
9473
|
/**
|
|
9038
9474
|
* Locates the command that runs `request.target`'s CLI with `request.words`, or says why there is none.
|
|
9039
9475
|
*
|
|
@@ -9116,7 +9552,7 @@ function locateCliCommand(request, runtime = {
|
|
|
9116
9552
|
sameCheckout: true
|
|
9117
9553
|
});
|
|
9118
9554
|
}
|
|
9119
|
-
if (target.manifest.channel === "core" && CHANNEL_PACKAGES
|
|
9555
|
+
if (target.manifest.channel === "core" && CHANNEL_PACKAGES.has(caller.packageName)) {
|
|
9120
9556
|
const installed = installedPackage(dirname(callerFile), target.packageName);
|
|
9121
9557
|
if (installed === null) return fail("core", `${caller.packageName} has no ${target.packageName} installed where it can find one`);
|
|
9122
9558
|
const manifest = readPackageManifest(installed);
|
|
@@ -9144,7 +9580,9 @@ function locateCliCommand(request, runtime = {
|
|
|
9144
9580
|
}
|
|
9145
9581
|
if (!SUITE_PACKAGES.has(caller.packageName)) return fail("direction", `${caller.packageName} is not a package of this suite`);
|
|
9146
9582
|
if (request.registrations === void 0) return fail("no-registrations", "nothing registered was given to look through for another product's command");
|
|
9147
|
-
const
|
|
9583
|
+
const channelTarget = CHANNEL_SNAPSHOT.find((entry) => entry.packageName === target.packageName);
|
|
9584
|
+
if (channelTarget === void 0) return fail("direction", `${target.packageName} is not a channel package this locator can find through a registration`);
|
|
9585
|
+
const facts = registrationFactsOf(channelTarget);
|
|
9148
9586
|
const notUsed = [];
|
|
9149
9587
|
const usable = [];
|
|
9150
9588
|
let engineRange;
|
|
@@ -9242,8 +9680,7 @@ function buildCommand(target, found, words, runtime, callerSource, platform) {
|
|
|
9242
9680
|
let entry;
|
|
9243
9681
|
if (found.source) entry = join(found.root, "src", "cli.ts");
|
|
9244
9682
|
else {
|
|
9245
|
-
const
|
|
9246
|
-
const binary = typeof declared === "object" && declared !== null ? declared.binary : void 0;
|
|
9683
|
+
const binary = declaredBinary(found.manifest);
|
|
9247
9684
|
const bin = found.manifest.bin;
|
|
9248
9685
|
const path = binary === target.manifest.binary && typeof bin === "object" && bin !== null && Object.hasOwn(bin, target.manifest.binary) ? bin[target.manifest.binary] : void 0;
|
|
9249
9686
|
if (typeof path !== "string" || path.length === 0) return {
|
|
@@ -9272,6 +9709,16 @@ function buildCommand(target, found, words, runtime, callerSource, platform) {
|
|
|
9272
9709
|
};
|
|
9273
9710
|
}
|
|
9274
9711
|
/**
|
|
9712
|
+
* The command a package declares for itself: a channel's in its `agentcomms` field, a service's in `agentcommsPackage`
|
|
9713
|
+
* (D14). A library declares none, and no package declares both: the registry refuses one that does.
|
|
9714
|
+
*/
|
|
9715
|
+
function declaredBinary(manifest) {
|
|
9716
|
+
const channel = manifest.agentcomms;
|
|
9717
|
+
if (typeof channel === "object" && channel !== null) return channel.binary;
|
|
9718
|
+
const service = manifest.agentcommsPackage;
|
|
9719
|
+
if (typeof service === "object" && service !== null && service.kind === "service") return service.binary;
|
|
9720
|
+
}
|
|
9721
|
+
/**
|
|
9275
9722
|
* The target's package a registration's files lead to, or null: a script it starts, real or as written when it is
|
|
9276
9723
|
* gone, inside the package (or inside the product's own server wrapper, which depends on it); or, for the product's
|
|
9277
9724
|
* own command, the package that command links into or that npm installed beside it.
|
|
@@ -9403,7 +9850,7 @@ function notLocated(target, version, reason, detail, nodeVersion, extra) {
|
|
|
9403
9850
|
* anywhere else for the product, PATH included, and a later locate checks the path again.
|
|
9404
9851
|
*/
|
|
9405
9852
|
function checkedEntry(root, entry) {
|
|
9406
|
-
if (!isWithin(root, entry)) return {
|
|
9853
|
+
if (!isWithin$1(root, entry)) return {
|
|
9407
9854
|
ok: false,
|
|
9408
9855
|
why: `${entry} is outside its package, ${root}`
|
|
9409
9856
|
};
|
|
@@ -9416,7 +9863,7 @@ function checkedEntry(root, entry) {
|
|
|
9416
9863
|
why: `${entry} is not there`
|
|
9417
9864
|
};
|
|
9418
9865
|
}
|
|
9419
|
-
if (!isWithin(root, real)) return {
|
|
9866
|
+
if (!isWithin$1(root, real)) return {
|
|
9420
9867
|
ok: false,
|
|
9421
9868
|
why: `${entry} leads to ${real}, outside its package, ${root}`
|
|
9422
9869
|
};
|
|
@@ -9751,6 +10198,10 @@ function renderInstall(result, color) {
|
|
|
9751
10198
|
lines.push(paint(color, "bold", `Add this to ${result.configPath ?? `the MCP configuration of ${result.client}`}:`), result.snippet.trimEnd());
|
|
9752
10199
|
}
|
|
9753
10200
|
if (result.backupPath) lines.push(`The entry it replaced was saved to ${result.backupPath}.`);
|
|
10201
|
+
if (result.shim && result.shim.status !== "foreign") lines.push(`CLI shim ${result.shim.status}: ${result.shim.path}.`);
|
|
10202
|
+
if (result.shimFailure) lines.push(paint(color, "yellow", `CLI shim failed: ${result.shimFailure}.`));
|
|
10203
|
+
if (result.shimNotice) lines.push(paint(color, "yellow", result.shimNotice));
|
|
10204
|
+
if (result.shim?.pathNotice) lines.push(result.shim.pathNotice);
|
|
9754
10205
|
if (result.verification === "passed") lines.push(paint(color, "green", `Checked: ${result.verifyDetail}`));
|
|
9755
10206
|
else if (result.verification === "failed") {
|
|
9756
10207
|
lines.push(paint(color, "red", `Failed to start: ${result.verifyDetail ?? "no reason given"}`));
|
|
@@ -9765,6 +10216,8 @@ function renderPrune(result, color) {
|
|
|
9765
10216
|
if (result.refused) lines.push(paint(color, "yellow", `Nothing removed: ${result.refused}.`));
|
|
9766
10217
|
const verb = result.dryRun ? "Would remove" : "Removed";
|
|
9767
10218
|
for (const item of result.removed) lines.push(paint(color, "green", `${verb} ${item.version}: ${item.path}`));
|
|
10219
|
+
for (const path of result.shims.removed) lines.push(paint(color, "green", `${verb} CLI shim: ${path}`));
|
|
10220
|
+
for (const path of result.shims.kept) lines.push(`Kept foreign CLI shim: ${path}`);
|
|
9768
10221
|
for (const item of result.kept) lines.push(`Kept ${item.version} (${item.reason}): ${item.path}`);
|
|
9769
10222
|
if (lines.length === 0) lines.push(`No managed runtimes to remove in ${result.runtimeDir}.`);
|
|
9770
10223
|
return lines.join("\n");
|
|
@@ -10857,10 +11310,10 @@ strictObject({
|
|
|
10857
11310
|
sha256: string().regex(/^[0-9a-f]{64}$/, "a SHA-256 is 64 lowercase hex digits")
|
|
10858
11311
|
});
|
|
10859
11312
|
/**
|
|
10860
|
-
* Registering, pruning and listing
|
|
11313
|
+
* Registering, pruning and listing every declared channel or service MCP server, from the core.
|
|
10861
11314
|
*
|
|
10862
|
-
* `agentcomms mcp install` registers
|
|
10863
|
-
*
|
|
11315
|
+
* `agentcomms mcp install` registers core or a declared service; `comms_server_install` registers any declared
|
|
11316
|
+
* server; and channel CLIs register their own. They are the one change here, over
|
|
10864
11317
|
* the shared installer (`mcp-install.ts`), and all go through the change flow: registering a server is a loosening in
|
|
10865
11318
|
* the design's terms (§3.1) — it hands a client a new set of tools — and so is asked for once and applied on the
|
|
10866
11319
|
* second call with the approval. Printing an entry registers nothing and asks nobody.
|
|
@@ -10871,7 +11324,7 @@ strictObject({
|
|
|
10871
11324
|
* the other — or worse, a channel's own command would register with no approval at all, which is what it did.
|
|
10872
11325
|
*
|
|
10873
11326
|
* The version installed is this core's own, or the calling channel's when it passes its product. The packages are
|
|
10874
|
-
* released together, at one version, so the core
|
|
11327
|
+
* released together, at one version, so the core asked to install Gmail or events installs its own release.
|
|
10875
11328
|
*/
|
|
10876
11329
|
const CLIENTS = Object.freeze([
|
|
10877
11330
|
"claude-code",
|
|
@@ -10912,10 +11365,10 @@ const exists = (path) => access(path, constants.R_OK).then(() => true, () => fal
|
|
|
10912
11365
|
* core depends on none of them. So the channel is looked for beside this core — `packages/gmail` next to
|
|
10913
11366
|
* `packages/core` in a checkout — and from source when this core runs from source, as the channel CLIs do.
|
|
10914
11367
|
*/
|
|
10915
|
-
async function localModuleUrl(
|
|
11368
|
+
async function localModuleUrl(server) {
|
|
10916
11369
|
const coreRoot = await corePackageRoot();
|
|
10917
|
-
const unscopedName =
|
|
10918
|
-
const root =
|
|
11370
|
+
const unscopedName = serverFacts(server).packageName.split("/").at(-1) ?? server;
|
|
11371
|
+
const root = server === "core" ? coreRoot : join(dirname(coreRoot), unscopedName);
|
|
10919
11372
|
const source = join(root, "src", "cli.ts");
|
|
10920
11373
|
const built = join(root, "dist", "cli.mjs");
|
|
10921
11374
|
const fromSource = fileURLToPath(import.meta.url).endsWith(".ts");
|
|
@@ -10923,7 +11376,7 @@ async function localModuleUrl(channel) {
|
|
|
10923
11376
|
if (!await exists(cli)) continue;
|
|
10924
11377
|
return pathToFileURL(cli === source ? join(root, "src", "mcp", "module.ts") : join(root, "dist", "module.mjs")).href;
|
|
10925
11378
|
}
|
|
10926
|
-
throw new CommsError("USAGE", `\`--launcher local\` registers a checkout's own code, and there is no ${
|
|
11379
|
+
throw new CommsError("USAGE", `\`--launcher local\` registers a checkout's own code, and there is no ${serverFacts(server).packageName} beside this one`, { hint: `Looked in ${root}. Use the managed launcher, or run the install from that checkout.` });
|
|
10927
11380
|
}
|
|
10928
11381
|
/**
|
|
10929
11382
|
* The product the shared installer registers, for one channel: at this core's version unless another is named.
|
|
@@ -10933,11 +11386,11 @@ async function localModuleUrl(channel) {
|
|
|
10933
11386
|
* newer release here is all it takes to register one this core is older than — which is what `agentcomms update` does.
|
|
10934
11387
|
* `comms_server_install` and every `mcp install` name none, and register their own release as they always have.
|
|
10935
11388
|
*/
|
|
10936
|
-
async function
|
|
11389
|
+
async function serverProduct(server, launcher, version = VERSION) {
|
|
10937
11390
|
return {
|
|
10938
|
-
...
|
|
11391
|
+
...serverFacts(server),
|
|
10939
11392
|
version,
|
|
10940
|
-
moduleUrl: launcher === "local" ? await localModuleUrl(
|
|
11393
|
+
moduleUrl: launcher === "local" ? await localModuleUrl(server) : ""
|
|
10941
11394
|
};
|
|
10942
11395
|
}
|
|
10943
11396
|
/**
|
|
@@ -10950,8 +11403,8 @@ async function channelProduct(channel, launcher, version = VERSION) {
|
|
|
10950
11403
|
* registered, from the channel; a product for another package would register something else under those words, and
|
|
10951
11404
|
* the person would have approved a sentence that was not true.
|
|
10952
11405
|
*/
|
|
10953
|
-
function ownProduct(
|
|
10954
|
-
if (own !== void 0 && own.packageName !==
|
|
11406
|
+
function ownProduct(server, own) {
|
|
11407
|
+
if (own !== void 0 && own.packageName !== serverFacts(server).packageName) throw new CommsError("UNEXPECTED", `${own.packageName} is not the ${serverLabel(server)} server's package, so it cannot register that server`);
|
|
10955
11408
|
return own;
|
|
10956
11409
|
}
|
|
10957
11410
|
/**
|
|
@@ -10961,11 +11414,11 @@ function ownProduct(channel, own) {
|
|
|
10961
11414
|
* register a server reaching every workspace for a caller who believed they had pinned it.
|
|
10962
11415
|
*/
|
|
10963
11416
|
function checkRequest(request) {
|
|
10964
|
-
if (!
|
|
11417
|
+
if (!isServer(request.channel)) throw new CommsError("USAGE", `"${String(request.channel)}" is not a channel`, { hint: `One of: ${CHANNELS.join(", ")}.` });
|
|
10965
11418
|
if (!CLIENTS.includes(request.client)) throw new CommsError("USAGE", `"${String(request.client)}" is not a client this can register with`, { hint: `One of: ${CLIENTS.join(", ")}.` });
|
|
10966
11419
|
if (request.launcher !== void 0 && !LAUNCHERS.includes(request.launcher)) throw new CommsError("USAGE", `"${String(request.launcher)}" is not a launcher`, { hint: `One of: ${LAUNCHERS.join(", ")}.` });
|
|
10967
11420
|
if (request.name !== void 0) checkServerName(request.name);
|
|
10968
|
-
const label =
|
|
11421
|
+
const label = serverLabel(request.channel);
|
|
10969
11422
|
const refuse = (option) => {
|
|
10970
11423
|
const owner = narrowingOwner(option);
|
|
10971
11424
|
throw new CommsError("USAGE", owner === void 0 ? `\`${option}\` is not an option of the ${label} server` : `\`${option}\` is an option of the ${owner.label} server; the ${label} server has no such option`);
|
|
@@ -11001,7 +11454,7 @@ function checkPin(config, request) {
|
|
|
11001
11454
|
return;
|
|
11002
11455
|
}
|
|
11003
11456
|
const { account } = resolveName(config, "account", pin);
|
|
11004
|
-
if (account.platform !== request.channel) throw new CommsError("USAGE", `"${pin}" is not a ${
|
|
11457
|
+
if (account.platform !== request.channel) throw new CommsError("USAGE", `"${pin}" is not a ${serverLabel(request.channel)} ${accountNoun(request.channel)}`);
|
|
11005
11458
|
}
|
|
11006
11459
|
function installOptions(request) {
|
|
11007
11460
|
const own = pinOption(request.channel);
|
|
@@ -11020,7 +11473,7 @@ function installOptions(request) {
|
|
|
11020
11473
|
};
|
|
11021
11474
|
}
|
|
11022
11475
|
/**
|
|
11023
|
-
* Registering one
|
|
11476
|
+
* Registering one declared server with one client, as a change.
|
|
11024
11477
|
*
|
|
11025
11478
|
* The effects are what a person is agreeing to, one sentence each, and everything that decides what the server may
|
|
11026
11479
|
* reach is in them — which server, which client, under which name, pinned to what, replacing what, started how. The
|
|
@@ -11035,21 +11488,22 @@ function installOptions(request) {
|
|
|
11035
11488
|
* resolves another — `CLAUDE_CONFIG_DIR` set to another account's, a PATH that finds another node, a core running
|
|
11036
11489
|
* from another build of the checkout — is another registration, refused as one.
|
|
11037
11490
|
*/
|
|
11038
|
-
function serverInstallChange(core, env, request, own) {
|
|
11491
|
+
function serverInstallChange(core, env, request, own, deps = {}) {
|
|
11039
11492
|
checkRequest(request);
|
|
11040
11493
|
ownProduct(request.channel, own);
|
|
11041
11494
|
const context = {
|
|
11042
11495
|
env,
|
|
11043
|
-
core
|
|
11496
|
+
core,
|
|
11497
|
+
...deps.platform === void 0 ? {} : { platform: deps.platform }
|
|
11044
11498
|
};
|
|
11045
|
-
const facts =
|
|
11046
|
-
const label =
|
|
11499
|
+
const facts = serverFacts(request.channel);
|
|
11500
|
+
const label = serverLabel(request.channel);
|
|
11047
11501
|
const noun = accountNoun(request.channel);
|
|
11048
11502
|
const pinName = pinOption(request.channel);
|
|
11049
11503
|
const switchable = hasNarrowing(request.channel, "readOnly");
|
|
11050
11504
|
const name = request.name ?? facts.defaultServerName;
|
|
11051
11505
|
const launcher = request.launcher ?? "managed";
|
|
11052
|
-
const productNow = async () => own ?? await
|
|
11506
|
+
const productNow = async () => own ?? await serverProduct(request.channel, request.launcher);
|
|
11053
11507
|
let planned;
|
|
11054
11508
|
return {
|
|
11055
11509
|
plan: async (config) => {
|
|
@@ -11080,6 +11534,14 @@ function serverInstallChange(core, env, request, own) {
|
|
|
11080
11534
|
else if (launcher === "local") effects.push(`${request.client} will start it from ${await localCliEntry(product.moduleUrl)}`);
|
|
11081
11535
|
}
|
|
11082
11536
|
if (request.print !== true && launcher === "managed" && await reusableRuntime(core.paths.dataDir, facts.packageName, version) === null) effects.push(`installs ${facts.packageName}@${version} from npm into ${managedRuntimeDir(core.paths.dataDir, facts.packageName, version)}`);
|
|
11537
|
+
if (request.print !== true && launcher === "managed" && managedShimsSupported(context)) {
|
|
11538
|
+
const path = managedShimPath({
|
|
11539
|
+
env,
|
|
11540
|
+
dataDir: core.paths.dataDir,
|
|
11541
|
+
platform: context.platform
|
|
11542
|
+
}, facts.binary);
|
|
11543
|
+
effects.push(`writes or repoints the owned CLI shim at ${path}; a foreign file there is left unchanged`);
|
|
11544
|
+
}
|
|
11083
11545
|
return {
|
|
11084
11546
|
before: config,
|
|
11085
11547
|
after: config,
|
|
@@ -11089,9 +11551,10 @@ function serverInstallChange(core, env, request, own) {
|
|
|
11089
11551
|
},
|
|
11090
11552
|
apply: async () => {
|
|
11091
11553
|
if (planned === void 0) throw new CommsError("UNEXPECTED", "the registration was applied before it was planned");
|
|
11092
|
-
const
|
|
11554
|
+
const installed = await mcpInstall(context, planned.product, installOptions(request), planned.install).catch(async (error) => {
|
|
11093
11555
|
throw await remedyLocated(error, core);
|
|
11094
11556
|
});
|
|
11557
|
+
const result = request.print === true ? installed : await withManagedShim(context, planned.product, installed);
|
|
11095
11558
|
return {
|
|
11096
11559
|
...result,
|
|
11097
11560
|
restart: result.applied ? `Restart ${request.client} to load "${result.name}": no MCP client loads a new server into a session that is already running.` : null
|
|
@@ -11100,6 +11563,68 @@ function serverInstallChange(core, env, request, own) {
|
|
|
11100
11563
|
};
|
|
11101
11564
|
}
|
|
11102
11565
|
/**
|
|
11566
|
+
* A managed install's CLI shim (CUE-406), once the registration's outcome is known: the command the locator finds in
|
|
11567
|
+
* the runtime the entry starts, written where nothing is or over this installation's own unchanged shim, and never
|
|
11568
|
+
* over anything else. A shim that cannot be written leaves the registration as it went, and says how to repair both.
|
|
11569
|
+
* The installer may not import the locator, so the operation writes it — for `mcp install` and `update` alike.
|
|
11570
|
+
*/
|
|
11571
|
+
async function withManagedShim(context, product, installed) {
|
|
11572
|
+
if (installed.launcher !== "managed") return installed;
|
|
11573
|
+
const cliEntry = installed.entry.args[0];
|
|
11574
|
+
if (cliEntry === void 0) throw new CommsError("UNEXPECTED", "the managed entry has no CLI path");
|
|
11575
|
+
const shimContext = {
|
|
11576
|
+
env: context.env,
|
|
11577
|
+
dataDir: context.core.paths.dataDir,
|
|
11578
|
+
platform: context.platform
|
|
11579
|
+
};
|
|
11580
|
+
if (!managedShimsSupported(shimContext)) return {
|
|
11581
|
+
...installed,
|
|
11582
|
+
shimNotice: managedShimUnsupportedNotice(shimContext)
|
|
11583
|
+
};
|
|
11584
|
+
const warnings = [...installed.warnings];
|
|
11585
|
+
let shim;
|
|
11586
|
+
let shimFailure;
|
|
11587
|
+
try {
|
|
11588
|
+
const located = locateCliCommand({
|
|
11589
|
+
caller: {
|
|
11590
|
+
url: pathToFileURL(cliEntry).href,
|
|
11591
|
+
packageName: product.packageName
|
|
11592
|
+
},
|
|
11593
|
+
target: {
|
|
11594
|
+
packageName: product.packageName,
|
|
11595
|
+
manifest: {
|
|
11596
|
+
label: product.defaultServerName,
|
|
11597
|
+
binary: product.binary
|
|
11598
|
+
}
|
|
11599
|
+
},
|
|
11600
|
+
words: [],
|
|
11601
|
+
uses: [],
|
|
11602
|
+
paths: {
|
|
11603
|
+
...context.core.paths,
|
|
11604
|
+
downloadsDir: context.core.paths.dataDir
|
|
11605
|
+
},
|
|
11606
|
+
platform: context.platform
|
|
11607
|
+
});
|
|
11608
|
+
if (!located.ok) throw new CommsError("CONFIG", `the managed CLI command could not be located: ${located.message}`);
|
|
11609
|
+
shim = await (context.writeShim ?? writeManagedShim)(shimContext, {
|
|
11610
|
+
binary: product.binary,
|
|
11611
|
+
command: located.command.words,
|
|
11612
|
+
entry: located.command.entry
|
|
11613
|
+
});
|
|
11614
|
+
} catch (error) {
|
|
11615
|
+
shimFailure = error instanceof Error ? error.message : String(error);
|
|
11616
|
+
const path = join(homeDirectory(context.env), ".local", "bin", product.binary);
|
|
11617
|
+
warnings.push(`The MCP registration ${installed.applied ? "succeeded" : "was not changed"}, but the CLI shim at ${path} could not be written (${shimFailure}). Fix that shim directory, then run this managed install again with \`--force\`; the owned registration is replaced without widening it and the shim is repaired.`);
|
|
11618
|
+
}
|
|
11619
|
+
if (shim?.status === "foreign") warnings.push(`${shim.path} already exists but was not written by this installation, so it was left unchanged; move it aside and run this managed install again with \`--force\` to create the CLI shim at that path.`);
|
|
11620
|
+
return {
|
|
11621
|
+
...installed,
|
|
11622
|
+
warnings,
|
|
11623
|
+
...shim === void 0 ? {} : { shim },
|
|
11624
|
+
...shimFailure === void 0 ? {} : { shimFailure }
|
|
11625
|
+
};
|
|
11626
|
+
}
|
|
11627
|
+
/**
|
|
11103
11628
|
* A refusal of `mcp install` with its remedy — another install of the same product — said with the command located:
|
|
11104
11629
|
* the printing package's own, core's, or another product's found among this machine's registrations (CUE-403). The
|
|
11105
11630
|
* installer says only the words; it may not import the locator.
|
|
@@ -11107,17 +11632,24 @@ function serverInstallChange(core, env, request, own) {
|
|
|
11107
11632
|
async function remedyLocated(error, core) {
|
|
11108
11633
|
const remedy = installRemedyOf(error);
|
|
11109
11634
|
if (remedy === void 0 || !(error instanceof CommsError)) return error;
|
|
11110
|
-
const
|
|
11111
|
-
if (
|
|
11635
|
+
const server = SERVERS.find((each) => serverFacts(each).binary === remedy.binary);
|
|
11636
|
+
if (server === void 0) return error;
|
|
11112
11637
|
const maker = await requireHandoffs(core).registered();
|
|
11638
|
+
const handoff = isChannel(server) ? maker.of(server, remedy.words) : maker.core([
|
|
11639
|
+
"mcp",
|
|
11640
|
+
"install",
|
|
11641
|
+
"--server",
|
|
11642
|
+
server,
|
|
11643
|
+
...remedy.words.slice(2)
|
|
11644
|
+
]);
|
|
11113
11645
|
return new CommsError(error.code, error.message, {
|
|
11114
|
-
hint: handoffSentence(
|
|
11646
|
+
hint: handoffSentence(handoff, remedy.say),
|
|
11115
11647
|
...error.details === void 0 ? {} : { details: error.details },
|
|
11116
11648
|
...error.cause === void 0 ? {} : { cause: error.cause }
|
|
11117
11649
|
});
|
|
11118
11650
|
}
|
|
11119
11651
|
/**
|
|
11120
|
-
* Removing a
|
|
11652
|
+
* Removing a declared server's unused managed runtimes, as a change.
|
|
11121
11653
|
*
|
|
11122
11654
|
* A dry run is free: it removes nothing. Otherwise the plan is that same dry run, and each runtime it would remove is
|
|
11123
11655
|
* one effect — the removal cannot be taken back — so the approval is bound to exactly that list; and the removal is
|
|
@@ -11127,20 +11659,20 @@ async function remedyLocated(error, core) {
|
|
|
11127
11659
|
* release of the command that was run; checked to be that channel's, as for an install.
|
|
11128
11660
|
*/
|
|
11129
11661
|
function serverPruneChange(core, env, request, own) {
|
|
11130
|
-
if (!
|
|
11662
|
+
if (!isServer(request.channel)) throw new CommsError("USAGE", `"${String(request.channel)}" is not a channel`, { hint: `One of: ${CHANNELS.join(", ")}.` });
|
|
11131
11663
|
const context = {
|
|
11132
11664
|
env,
|
|
11133
11665
|
core
|
|
11134
11666
|
};
|
|
11135
11667
|
const { packageName, version } = ownProduct(request.channel, own) ?? {
|
|
11136
|
-
packageName:
|
|
11137
|
-
version: "0.
|
|
11668
|
+
packageName: serverFacts(request.channel).packageName,
|
|
11669
|
+
version: "0.18.0"
|
|
11138
11670
|
};
|
|
11139
11671
|
const product = {
|
|
11140
11672
|
packageName,
|
|
11141
11673
|
version
|
|
11142
11674
|
};
|
|
11143
|
-
const label =
|
|
11675
|
+
const label = serverLabel(request.channel);
|
|
11144
11676
|
const base = {
|
|
11145
11677
|
includePrinted: request.includePrinted === true,
|
|
11146
11678
|
...request.processes ? { processes: request.processes } : {}
|
|
@@ -11161,7 +11693,7 @@ function serverPruneChange(core, env, request, own) {
|
|
|
11161
11693
|
return {
|
|
11162
11694
|
before: config,
|
|
11163
11695
|
after: config,
|
|
11164
|
-
effects: preview.removed.map((item) => `deletes the unused ${label} runtime ${item.version} at ${item.path}`),
|
|
11696
|
+
effects: [...preview.removed.map((item) => `deletes the unused ${label} runtime ${item.version} at ${item.path}`), ...preview.shims.removed.map((path) => `deletes the owned ${label} CLI shim at ${path}`)],
|
|
11165
11697
|
summary: `Remove ${approved.length} unused ${label} runtime${approved.length === 1 ? "" : "s"}`
|
|
11166
11698
|
};
|
|
11167
11699
|
},
|
|
@@ -11434,13 +11966,14 @@ function updateStopMessage(pending, where, commands) {
|
|
|
11434
11966
|
* the yes is a different change, and the claim is refused. Nothing behind, nothing is prepared.
|
|
11435
11967
|
*/
|
|
11436
11968
|
/**
|
|
11437
|
-
* The packages this suite publishes, whose latest releases an update reads: every
|
|
11438
|
-
* every package a
|
|
11969
|
+
* The packages this suite publishes, whose latest releases an update reads: every declared server owner's, from the
|
|
11970
|
+
* channel and service declarations, and every package a server is run through by `npx` when that is another one —
|
|
11971
|
+
* Gmail's `gmail-mcp`.
|
|
11439
11972
|
*/
|
|
11440
|
-
const
|
|
11441
|
-
const SERVER_PACKAGES = Object.freeze(
|
|
11442
|
-
Object.freeze([.../* @__PURE__ */ new Set([...
|
|
11443
|
-
|
|
11973
|
+
const OWNER_PACKAGES = Object.freeze(SERVERS.map((server) => serverFacts(server).packageName));
|
|
11974
|
+
const SERVER_PACKAGES = Object.freeze(SERVERS.map((server) => serverFacts(server).npxPackage).filter((name) => !OWNER_PACKAGES.includes(name)));
|
|
11975
|
+
Object.freeze([.../* @__PURE__ */ new Set([...OWNER_PACKAGES, ...SERVER_PACKAGES])].sort());
|
|
11976
|
+
serverFacts("core").packageName;
|
|
11444
11977
|
/**
|
|
11445
11978
|
* "Not now": no server or command on this machine stops for the update until the next local midnight.
|
|
11446
11979
|
*
|
|
@@ -11788,7 +12321,7 @@ async function askLine(streams, question) {
|
|
|
11788
12321
|
}
|
|
11789
12322
|
channelServer("core").packageName;
|
|
11790
12323
|
//#endregion
|
|
11791
|
-
//#region ../core/dist/tool-arguments-
|
|
12324
|
+
//#region ../core/dist/tool-arguments-C_RA7n3A.mjs
|
|
11792
12325
|
const APPLIED = Symbol.for("agentcomms.strictToolArguments");
|
|
11793
12326
|
const TARGET = "draft-2020-12";
|
|
11794
12327
|
/**
|
|
@@ -12096,10 +12629,60 @@ new TextEncoder();
|
|
|
12096
12629
|
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
12097
12630
|
const base64Lookup = /* @__PURE__ */ new Uint8Array(256);
|
|
12098
12631
|
for (let i = 0; i < 64; i++) base64Lookup[base64Chars.charCodeAt(i)] = i;
|
|
12632
|
+
new Set([
|
|
12633
|
+
"unicode-1-1-utf-8 unicode11utf8 unicode20utf8 utf-8 utf8 x-unicode20utf8",
|
|
12634
|
+
"866 cp866 csibm866 ibm866",
|
|
12635
|
+
"csisolatin2 iso-8859-2 iso-ir-101 iso8859-2 iso88592 iso_8859-2 iso_8859-2:1987 l2 latin2",
|
|
12636
|
+
"csisolatin3 iso-8859-3 iso-ir-109 iso8859-3 iso88593 iso_8859-3 iso_8859-3:1988 l3 latin3",
|
|
12637
|
+
"csisolatin4 iso-8859-4 iso-ir-110 iso8859-4 iso88594 iso_8859-4 iso_8859-4:1988 l4 latin4",
|
|
12638
|
+
"csisolatincyrillic cyrillic iso-8859-5 iso-ir-144 iso8859-5 iso88595 iso_8859-5 iso_8859-5:1988",
|
|
12639
|
+
"arabic asmo-708 csiso88596e csiso88596i csisolatinarabic ecma-114 iso-8859-6 iso-8859-6-e",
|
|
12640
|
+
"iso-8859-6-i iso-ir-127 iso8859-6 iso88596 iso_8859-6 iso_8859-6:1987",
|
|
12641
|
+
"csisolatingreek ecma-118 elot_928 greek greek8 iso-8859-7 iso-ir-126 iso8859-7 iso88597 iso_8859-7",
|
|
12642
|
+
"iso_8859-7:1987 sun_eu_greek",
|
|
12643
|
+
"csiso88598e csisolatinhebrew hebrew iso-8859-8 iso-8859-8-e iso-ir-138 iso8859-8 iso88598 iso_8859-8",
|
|
12644
|
+
"iso_8859-8:1988 visual",
|
|
12645
|
+
"csiso88598i iso-8859-8-i logical",
|
|
12646
|
+
"csisolatin6 iso-8859-10 iso-ir-157 iso8859-10 iso885910 l6 latin6",
|
|
12647
|
+
"iso-8859-13 iso8859-13 iso885913",
|
|
12648
|
+
"iso-8859-14 iso8859-14 iso885914",
|
|
12649
|
+
"csisolatin9 iso-8859-15 iso8859-15 iso885915 iso_8859-15 l9",
|
|
12650
|
+
"iso-8859-16",
|
|
12651
|
+
"cskoi8r koi koi8 koi8-r koi8_r",
|
|
12652
|
+
"koi8-ru koi8-u",
|
|
12653
|
+
"csmacintosh mac macintosh x-mac-roman",
|
|
12654
|
+
"dos-874 iso-8859-11 iso8859-11 iso885911 tis-620 windows-874",
|
|
12655
|
+
"cp1250 windows-1250 x-cp1250",
|
|
12656
|
+
"cp1251 windows-1251 x-cp1251",
|
|
12657
|
+
"ansi_x3.4-1968 ascii cp1252 cp819 csisolatin1 ibm819 iso-8859-1 iso-ir-100 iso8859-1 iso88591",
|
|
12658
|
+
"iso_8859-1 iso_8859-1:1987 l1 latin1 us-ascii windows-1252 x-cp1252",
|
|
12659
|
+
"cp1253 windows-1253 x-cp1253",
|
|
12660
|
+
"cp1254 csisolatin5 iso-8859-9 iso-ir-148 iso8859-9 iso88599 iso_8859-9 iso_8859-9:1989 l5 latin5",
|
|
12661
|
+
"windows-1254 x-cp1254",
|
|
12662
|
+
"cp1255 windows-1255 x-cp1255",
|
|
12663
|
+
"cp1256 windows-1256 x-cp1256",
|
|
12664
|
+
"cp1257 windows-1257 x-cp1257",
|
|
12665
|
+
"cp1258 windows-1258 x-cp1258",
|
|
12666
|
+
"x-mac-cyrillic x-mac-ukrainian",
|
|
12667
|
+
"chinese csgb2312 csiso58gb231280 gb2312 gb_2312 gb_2312-80 gbk iso-ir-58 x-gbk",
|
|
12668
|
+
"gb18030",
|
|
12669
|
+
"big5 big5-hkscs cn-big5 csbig5 x-x-big5",
|
|
12670
|
+
"cseucpkdfmtjapanese euc-jp x-euc-jp",
|
|
12671
|
+
"csiso2022jp iso-2022-jp",
|
|
12672
|
+
"csshiftjis ms932 ms_kanji shift-jis shift_jis sjis windows-31j x-sjis",
|
|
12673
|
+
"cseuckr csksc56011987 euc-kr iso-ir-149 korean ks_c_5601-1987 ks_c_5601-1989 ksc5601 ksc_5601",
|
|
12674
|
+
"windows-949",
|
|
12675
|
+
"csiso2022kr hz-gb-2312 iso-2022-cn iso-2022-cn-ext iso-2022-kr replacement",
|
|
12676
|
+
"unicodefffe utf-16be",
|
|
12677
|
+
"csunicode iso-10646-ucs-2 ucs-2 unicode unicodefeff utf-16 utf-16le",
|
|
12678
|
+
"x-user-defined"
|
|
12679
|
+
].join(" ").split(" "));
|
|
12680
|
+
new TextDecoder();
|
|
12099
12681
|
const ENCODED_WORD_PATTERN = "=\\?([^?\\s]+)\\?([QqBb])\\?([^?]*)\\?=";
|
|
12100
12682
|
new RegExp(ENCODED_WORD_PATTERN, "g");
|
|
12101
12683
|
new RegExp(`^(?:${ENCODED_WORD_PATTERN}\\s*)+$`);
|
|
12102
12684
|
new TextDecoder("utf-8", { ignoreBOM: true });
|
|
12685
|
+
new Uint8Array([10]);
|
|
12103
12686
|
//#endregion
|
|
12104
12687
|
//#region ../../node_modules/.pnpm/deepmerge-ts@8.0.2/node_modules/deepmerge-ts/dist/index.mjs
|
|
12105
12688
|
/**
|