@displaydev/cli 0.18.0 → 0.20.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/dist/api-client.js +197 -27
- package/dist/main.js +559 -5
- package/dist/mcp-server.js +517 -22
- package/dist/update-notice.js +488 -0
- package/package.json +1 -1
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
function _array_like_to_array(arr, len) {
|
|
2
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
4
|
+
return arr2;
|
|
5
|
+
}
|
|
6
|
+
function _array_without_holes(arr) {
|
|
7
|
+
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
8
|
+
}
|
|
9
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
10
|
+
try {
|
|
11
|
+
var info = gen[key](arg);
|
|
12
|
+
var value = info.value;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
reject(error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (info.done) {
|
|
18
|
+
resolve(value);
|
|
19
|
+
} else {
|
|
20
|
+
Promise.resolve(value).then(_next, _throw);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function _async_to_generator(fn) {
|
|
24
|
+
return function() {
|
|
25
|
+
var self = this, args = arguments;
|
|
26
|
+
return new Promise(function(resolve, reject) {
|
|
27
|
+
var gen = fn.apply(self, args);
|
|
28
|
+
function _next(value) {
|
|
29
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
30
|
+
}
|
|
31
|
+
function _throw(err) {
|
|
32
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
33
|
+
}
|
|
34
|
+
_next(undefined);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function _iterable_to_array(iter) {
|
|
39
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
40
|
+
}
|
|
41
|
+
function _non_iterable_spread() {
|
|
42
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
43
|
+
}
|
|
44
|
+
function _to_consumable_array(arr) {
|
|
45
|
+
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
46
|
+
}
|
|
47
|
+
function _type_of(obj) {
|
|
48
|
+
"@swc/helpers - typeof";
|
|
49
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
50
|
+
}
|
|
51
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
52
|
+
if (!o) return;
|
|
53
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
54
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
55
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
56
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
57
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
58
|
+
}
|
|
59
|
+
function _ts_generator(thisArg, body) {
|
|
60
|
+
var f, y, t, _ = {
|
|
61
|
+
label: 0,
|
|
62
|
+
sent: function() {
|
|
63
|
+
if (t[0] & 1) throw t[1];
|
|
64
|
+
return t[1];
|
|
65
|
+
},
|
|
66
|
+
trys: [],
|
|
67
|
+
ops: []
|
|
68
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
69
|
+
return d(g, "next", {
|
|
70
|
+
value: verb(0)
|
|
71
|
+
}), d(g, "throw", {
|
|
72
|
+
value: verb(1)
|
|
73
|
+
}), d(g, "return", {
|
|
74
|
+
value: verb(2)
|
|
75
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
76
|
+
value: function() {
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
}), g;
|
|
80
|
+
function verb(n) {
|
|
81
|
+
return function(v) {
|
|
82
|
+
return step([
|
|
83
|
+
n,
|
|
84
|
+
v
|
|
85
|
+
]);
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function step(op) {
|
|
89
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
90
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
91
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
92
|
+
if (y = 0, t) op = [
|
|
93
|
+
op[0] & 2,
|
|
94
|
+
t.value
|
|
95
|
+
];
|
|
96
|
+
switch(op[0]){
|
|
97
|
+
case 0:
|
|
98
|
+
case 1:
|
|
99
|
+
t = op;
|
|
100
|
+
break;
|
|
101
|
+
case 4:
|
|
102
|
+
_.label++;
|
|
103
|
+
return {
|
|
104
|
+
value: op[1],
|
|
105
|
+
done: false
|
|
106
|
+
};
|
|
107
|
+
case 5:
|
|
108
|
+
_.label++;
|
|
109
|
+
y = op[1];
|
|
110
|
+
op = [
|
|
111
|
+
0
|
|
112
|
+
];
|
|
113
|
+
continue;
|
|
114
|
+
case 7:
|
|
115
|
+
op = _.ops.pop();
|
|
116
|
+
_.trys.pop();
|
|
117
|
+
continue;
|
|
118
|
+
default:
|
|
119
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
120
|
+
_ = 0;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
124
|
+
_.label = op[1];
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
128
|
+
_.label = t[1];
|
|
129
|
+
t = op;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (t && _.label < t[2]) {
|
|
133
|
+
_.label = t[2];
|
|
134
|
+
_.ops.push(op);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
if (t[2]) _.ops.pop();
|
|
138
|
+
_.trys.pop();
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
op = body.call(thisArg, _);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
op = [
|
|
144
|
+
6,
|
|
145
|
+
e
|
|
146
|
+
];
|
|
147
|
+
y = 0;
|
|
148
|
+
} finally{
|
|
149
|
+
f = t = 0;
|
|
150
|
+
}
|
|
151
|
+
if (op[0] & 5) throw op[1];
|
|
152
|
+
return {
|
|
153
|
+
value: op[0] ? op[1] : void 0,
|
|
154
|
+
done: true
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Update-notification surface for `@displaydev/cli`. Two paths consume it:
|
|
160
|
+
* plain CLI (stderr block printed from a `process.on('exit', ...)` hook)
|
|
161
|
+
* and MCP stdio (content block appended to the next `tools/call` result).
|
|
162
|
+
*
|
|
163
|
+
* Module-level state (`latestVersion`, `injectedThisProcess`,
|
|
164
|
+
* `currentVersion`) is intentional. Tests reset between cases via
|
|
165
|
+
* `vi.resetModules()` + dynamic re-import — there is no exported
|
|
166
|
+
* test-only reset.
|
|
167
|
+
*/ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
168
|
+
import { homedir } from 'node:os';
|
|
169
|
+
import { dirname, join } from 'node:path';
|
|
170
|
+
export var PACKAGE_NAME = '@displaydev/cli';
|
|
171
|
+
var NOTICE_PREFIX = '[displaydev]';
|
|
172
|
+
var THROTTLE_MS = 24 * 60 * 60 * 1000;
|
|
173
|
+
var latestVersion = null;
|
|
174
|
+
var injectedThisProcess = false;
|
|
175
|
+
var currentVersion = null;
|
|
176
|
+
var mcpMode = false;
|
|
177
|
+
/**
|
|
178
|
+
* Stamp the running CLI's version. main.ts calls this once at module
|
|
179
|
+
* scope; the value is read by `emitOnExit` and `injectIntoToolResult`
|
|
180
|
+
* when they format the notice text.
|
|
181
|
+
*/ export function setCurrentVersion(version) {
|
|
182
|
+
currentVersion = version;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Mark this process as a long-running MCP-stdio server. main.ts calls
|
|
186
|
+
* this from the `mcp` subcommand handler. Once set, `emitOnExit` skips —
|
|
187
|
+
* MCP mode surfaces the notice via `injectIntoToolResult`, and the
|
|
188
|
+
* stderr fallback would (a) write the wrong text variant ("npm i -g"
|
|
189
|
+
* vs. "bump the host config") and (b) reach nowhere useful since stdio
|
|
190
|
+
* MCP hosts do not surface stderr to the agent.
|
|
191
|
+
*/ export function setMcpMode() {
|
|
192
|
+
mcpMode = true;
|
|
193
|
+
}
|
|
194
|
+
/** Mark the CLI outdated, capturing the registry's `latest` value. */ export function markOutdated(version) {
|
|
195
|
+
latestVersion = version;
|
|
196
|
+
}
|
|
197
|
+
/** Read the most recently observed `latest`, or null. */ export function getLatestVersion() {
|
|
198
|
+
return latestVersion;
|
|
199
|
+
}
|
|
200
|
+
function statePath() {
|
|
201
|
+
return join(homedir(), '.displaydev', 'update-state.json');
|
|
202
|
+
}
|
|
203
|
+
/** Read the per-machine throttle state. Missing or corrupt → empty. */ export function loadUpdateState() {
|
|
204
|
+
try {
|
|
205
|
+
var raw = readFileSync(statePath(), 'utf-8');
|
|
206
|
+
var parsed = JSON.parse(raw);
|
|
207
|
+
if (parsed && (typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) === 'object') {
|
|
208
|
+
return parsed;
|
|
209
|
+
}
|
|
210
|
+
return {};
|
|
211
|
+
} catch (unused) {
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Persist the throttle state. Best-effort; failures swallow. */ export function saveUpdateState(state) {
|
|
216
|
+
try {
|
|
217
|
+
var path = statePath();
|
|
218
|
+
mkdirSync(dirname(path), {
|
|
219
|
+
recursive: true
|
|
220
|
+
});
|
|
221
|
+
writeFileSync(path, "".concat(JSON.stringify(state), "\n"));
|
|
222
|
+
} catch (unused) {
|
|
223
|
+
// Best-effort per spec failure-modes table — a write that fails
|
|
224
|
+
// means we re-fire next invocation, which is preferable to a
|
|
225
|
+
// crash on read-only home or a full disk.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Compare two semver strings; returns true when `a > b`. Implements the
|
|
230
|
+
* pre-release ranking rules from semver.org §11:
|
|
231
|
+
* - Numeric identifiers compare numerically.
|
|
232
|
+
* - Alphanumeric identifiers compare lexically (ASCII).
|
|
233
|
+
* - Numeric identifiers always rank below alphanumeric.
|
|
234
|
+
* - A longer pre-release identifier list outranks a prefix-equal shorter
|
|
235
|
+
* one (e.g. `0.20.0-alpha.1` > `0.20.0-alpha`).
|
|
236
|
+
* - A stable release outranks any pre-release with the same `x.y.z`.
|
|
237
|
+
*
|
|
238
|
+
* Returns false for anything that fails to parse.
|
|
239
|
+
*/ export function semverGt(a, b) {
|
|
240
|
+
// Strict semver (semver.org §9): numeric identifiers MUST NOT have
|
|
241
|
+
// leading zeros, identifiers MUST NOT be empty.
|
|
242
|
+
var NUMERIC_RE = /^(?:0|[1-9]\d*)$/;
|
|
243
|
+
var ALPHANUM_RE = /^[0-9A-Za-z-]+$/;
|
|
244
|
+
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
245
|
+
var isValidPreId = function isValidPreId(id) {
|
|
246
|
+
if (id.length === 0) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
// Numeric identifier with leading zeros is invalid (`-01`, `-007`).
|
|
250
|
+
if (/^\d+$/.test(id) && id.length > 1 && id.startsWith('0')) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
return ALPHANUM_RE.test(id);
|
|
254
|
+
};
|
|
255
|
+
var parsed = function parsed(v) {
|
|
256
|
+
var match = SEMVER_RE.exec(v);
|
|
257
|
+
if (!match) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
// Reject leading zeros on the numeric main triple too (`01.0.0`).
|
|
261
|
+
if (![
|
|
262
|
+
match[1],
|
|
263
|
+
match[2],
|
|
264
|
+
match[3]
|
|
265
|
+
].every(function(p) {
|
|
266
|
+
return NUMERIC_RE.test(p);
|
|
267
|
+
})) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
var preIds = match[4] ? match[4].split('.') : [];
|
|
271
|
+
if (!preIds.every(isValidPreId)) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
parts: [
|
|
276
|
+
Number(match[1]),
|
|
277
|
+
Number(match[2]),
|
|
278
|
+
Number(match[3])
|
|
279
|
+
],
|
|
280
|
+
preIds: preIds
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
var A = parsed(a);
|
|
284
|
+
var B = parsed(b);
|
|
285
|
+
if (!A || !B) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
for(var i = 0; i < 3; i++){
|
|
289
|
+
if (A.parts[i] !== B.parts[i]) {
|
|
290
|
+
return A.parts[i] > B.parts[i];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Equal main parts: stable release outranks any pre-release.
|
|
294
|
+
if (A.preIds.length === 0 && B.preIds.length > 0) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (A.preIds.length > 0 && B.preIds.length === 0) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
var len = Math.min(A.preIds.length, B.preIds.length);
|
|
301
|
+
for(var i1 = 0; i1 < len; i1++){
|
|
302
|
+
var cmp = comparePreId(A.preIds[i1], B.preIds[i1]);
|
|
303
|
+
if (cmp !== 0) {
|
|
304
|
+
return cmp > 0;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// All shared identifiers equal: longer list ranks higher.
|
|
308
|
+
return A.preIds.length > B.preIds.length;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Compare two pre-release identifiers per semver §11.4. Numeric < alphanumeric;
|
|
312
|
+
* pure-numeric identifiers compare numerically; alphanumerics compare lexically.
|
|
313
|
+
* Returns positive when `a > b`, negative when `a < b`, 0 when equal.
|
|
314
|
+
*/ function comparePreId(a, b) {
|
|
315
|
+
var numA = /^\d+$/.test(a);
|
|
316
|
+
var numB = /^\d+$/.test(b);
|
|
317
|
+
if (numA && numB) {
|
|
318
|
+
var na = Number(a);
|
|
319
|
+
var nb = Number(b);
|
|
320
|
+
return na === nb ? 0 : na > nb ? 1 : -1;
|
|
321
|
+
}
|
|
322
|
+
if (numA) {
|
|
323
|
+
return -1;
|
|
324
|
+
}
|
|
325
|
+
if (numB) {
|
|
326
|
+
return 1;
|
|
327
|
+
}
|
|
328
|
+
return a === b ? 0 : a > b ? 1 : -1;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Read `X-Client-Latest-Version` from a Response and, when greater than
|
|
332
|
+
* the local CLI version, mark the comparator outdated. Silently skips
|
|
333
|
+
* missing / blank / malformed values.
|
|
334
|
+
*/ export function readLatestVersionFromResponse(res, current) {
|
|
335
|
+
var header = res.headers.get('x-client-latest-version');
|
|
336
|
+
if (!header) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
var trimmed = header.trim();
|
|
340
|
+
if (!trimmed) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (semverGt(trimmed, current)) {
|
|
344
|
+
markOutdated(trimmed);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
export function formatNotice(opts) {
|
|
348
|
+
// Single-line dimmed banner for humans in a terminal — mode === 'cli'
|
|
349
|
+
// is the only path that ever sees `tty === true` (MCP is always stdio).
|
|
350
|
+
if (opts.mode === 'cli' && opts.tty) {
|
|
351
|
+
var dim = '\x1b[2m';
|
|
352
|
+
var reset = '\x1b[0m';
|
|
353
|
+
return "".concat(dim).concat(NOTICE_PREFIX, " update available: ").concat(opts.latest, " (you have ").concat(opts.current, "). Run `npm i -g ").concat(PACKAGE_NAME, "@latest` to update.").concat(reset);
|
|
354
|
+
}
|
|
355
|
+
if (opts.mode === 'cli') {
|
|
356
|
+
return [
|
|
357
|
+
"".concat(NOTICE_PREFIX, " Update available: ").concat(PACKAGE_NAME, " ").concat(opts.latest, " (you have ").concat(opts.current, ")."),
|
|
358
|
+
'Ask the user if they want to update.',
|
|
359
|
+
"If yes: run `npm i -g ".concat(PACKAGE_NAME, "@latest` (or the equivalent for their package manager — bun, pnpm, yarn — based on how they installed it) and retry the original command.")
|
|
360
|
+
].join('\n');
|
|
361
|
+
}
|
|
362
|
+
// MCP variant: npm i -g does not update an already-running stdio
|
|
363
|
+
// process; the host has to bump its config and restart.
|
|
364
|
+
return [
|
|
365
|
+
"".concat(NOTICE_PREFIX, " Update available: ").concat(PACKAGE_NAME, " ").concat(opts.latest, " (you have ").concat(opts.current, ")."),
|
|
366
|
+
'Ask the user if they want to update.',
|
|
367
|
+
"If yes: bump the version pin for ".concat(PACKAGE_NAME, " in their MCP host config, or if the config uses @latest, ask them to restart their MCP host.")
|
|
368
|
+
].join('\n');
|
|
369
|
+
}
|
|
370
|
+
function passesThrottle(now, state) {
|
|
371
|
+
if (!state.lastNoticeAt) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
var last = Date.parse(state.lastNoticeAt);
|
|
375
|
+
if (Number.isNaN(last)) {
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
return now - last >= THROTTLE_MS;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Synchronous `'exit'` handler for plain CLI mode. Fires after every
|
|
382
|
+
* termination path (natural completion, `process.exit(N)`, uncaught
|
|
383
|
+
* exception). Must remain synchronous — the event loop is winding
|
|
384
|
+
* down by the time it runs, so `await` would silently no-op.
|
|
385
|
+
*
|
|
386
|
+
* Skips when the process is running as a stdio MCP server (notice text
|
|
387
|
+
* differs and stderr is not surfaced to the host) or when injection has
|
|
388
|
+
* already fired in this process (MCP mode already prompted via the
|
|
389
|
+
* tool-result content block).
|
|
390
|
+
*/ export function emitOnExit() {
|
|
391
|
+
if (mcpMode) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (injectedThisProcess) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (latestVersion === null || currentVersion === null) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
var state = loadUpdateState();
|
|
401
|
+
var now = Date.now();
|
|
402
|
+
if (!passesThrottle(now, state)) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
var text = formatNotice({
|
|
406
|
+
current: currentVersion,
|
|
407
|
+
latest: latestVersion,
|
|
408
|
+
mode: 'cli',
|
|
409
|
+
tty: Boolean(process.stderr.isTTY)
|
|
410
|
+
});
|
|
411
|
+
process.stderr.write("".concat(text, "\n"));
|
|
412
|
+
saveUpdateState({
|
|
413
|
+
lastNoticeAt: new Date(now).toISOString(),
|
|
414
|
+
lastNoticeVersion: latestVersion
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Append a notice content block to a tool-call result when:
|
|
419
|
+
* 1. The comparator has marked the CLI outdated.
|
|
420
|
+
* 2. We have not already injected once in this process.
|
|
421
|
+
* 3. The cross-process 24 h throttle allows.
|
|
422
|
+
*
|
|
423
|
+
* Preserves `_meta`, `isError`, and any other top-level fields. Only
|
|
424
|
+
* `content` changes shape (a new entry appended).
|
|
425
|
+
*/ export function injectIntoToolResult(result) {
|
|
426
|
+
if (latestVersion === null || currentVersion === null) {
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
if (injectedThisProcess) {
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
var now = Date.now();
|
|
433
|
+
var state = loadUpdateState();
|
|
434
|
+
if (!passesThrottle(now, state)) {
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
var text = formatNotice({
|
|
438
|
+
current: currentVersion,
|
|
439
|
+
latest: latestVersion,
|
|
440
|
+
mode: 'mcp',
|
|
441
|
+
tty: false
|
|
442
|
+
});
|
|
443
|
+
result.content = _to_consumable_array(result.content).concat([
|
|
444
|
+
{
|
|
445
|
+
type: 'text',
|
|
446
|
+
text: text
|
|
447
|
+
}
|
|
448
|
+
]);
|
|
449
|
+
injectedThisProcess = true;
|
|
450
|
+
saveUpdateState({
|
|
451
|
+
lastNoticeAt: new Date(now).toISOString(),
|
|
452
|
+
lastNoticeVersion: latestVersion
|
|
453
|
+
});
|
|
454
|
+
return result;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Higher-order wrapper around an MCP tool handler. Awaits the inner
|
|
458
|
+
* handler then runs the result through `injectIntoToolResult`.
|
|
459
|
+
*
|
|
460
|
+
* Mounted in `mcp-server.ts` at every `server.tool(...)` registration
|
|
461
|
+
* site so injection runs uniformly regardless of which return shape
|
|
462
|
+
* (`okResponse` / `errorResponse` / direct literal / `_meta`-bearing)
|
|
463
|
+
* the inner handler returned.
|
|
464
|
+
*/ export function withUpdateNotice(handler) {
|
|
465
|
+
return function() {
|
|
466
|
+
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
467
|
+
args[_key] = arguments[_key];
|
|
468
|
+
}
|
|
469
|
+
return _async_to_generator(function() {
|
|
470
|
+
var result;
|
|
471
|
+
return _ts_generator(this, function(_state) {
|
|
472
|
+
switch(_state.label){
|
|
473
|
+
case 0:
|
|
474
|
+
return [
|
|
475
|
+
4,
|
|
476
|
+
handler.apply(void 0, _to_consumable_array(args))
|
|
477
|
+
];
|
|
478
|
+
case 1:
|
|
479
|
+
result = _state.sent();
|
|
480
|
+
return [
|
|
481
|
+
2,
|
|
482
|
+
injectIntoToolResult(result)
|
|
483
|
+
];
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
})();
|
|
487
|
+
};
|
|
488
|
+
}
|