@displaydev/cli 0.19.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 +53 -27
- package/dist/main.js +28 -5
- package/dist/mcp-server.js +39 -38
- package/dist/update-notice.js +488 -0
- package/package.json +1 -1
package/dist/api-client.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* HTTP client for the display.dev API.
|
|
3
|
-
* Used by the stdio MCP server to proxy tool calls to the REST API.
|
|
4
|
-
*/ function _assert_this_initialized(self) {
|
|
1
|
+
function _assert_this_initialized(self) {
|
|
5
2
|
if (self === void 0) {
|
|
6
3
|
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
7
4
|
}
|
|
@@ -284,6 +281,10 @@ function _ts_generator(thisArg, body) {
|
|
|
284
281
|
};
|
|
285
282
|
}
|
|
286
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* HTTP client for the display.dev API.
|
|
286
|
+
* Used by the stdio MCP server to proxy tool calls to the REST API.
|
|
287
|
+
*/ import { readLatestVersionFromResponse } from './update-notice.js';
|
|
287
288
|
export var ApiError = /*#__PURE__*/ function(Error1) {
|
|
288
289
|
"use strict";
|
|
289
290
|
_inherits(ApiError, Error1);
|
|
@@ -311,15 +312,44 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
311
312
|
"use strict";
|
|
312
313
|
function ApiClient(config) {
|
|
313
314
|
_class_call_check(this, ApiClient);
|
|
314
|
-
var _config_clientType;
|
|
315
|
+
var _config_clientType, _config_version;
|
|
315
316
|
_define_property(this, "baseUrl", void 0);
|
|
316
317
|
_define_property(this, "apiKey", void 0);
|
|
317
318
|
_define_property(this, "clientType", void 0);
|
|
319
|
+
_define_property(this, "version", void 0);
|
|
318
320
|
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
319
321
|
this.apiKey = config.apiKey;
|
|
320
322
|
this.clientType = (_config_clientType = config.clientType) !== null && _config_clientType !== void 0 ? _config_clientType : 'mcp-stdio';
|
|
323
|
+
this.version = (_config_version = config.version) !== null && _config_version !== void 0 ? _config_version : '';
|
|
321
324
|
}
|
|
322
325
|
_create_class(ApiClient, [
|
|
326
|
+
{
|
|
327
|
+
key: "clientHeaders",
|
|
328
|
+
value: /**
|
|
329
|
+
* Build the request-side header set. Always carries `X-Client-Type`;
|
|
330
|
+
* adds `X-Client-Version` when the client was constructed with one.
|
|
331
|
+
*/ function clientHeaders(extra) {
|
|
332
|
+
var headers = _object_spread({
|
|
333
|
+
'X-Client-Type': this.clientType
|
|
334
|
+
}, this.version ? {
|
|
335
|
+
'X-Client-Version': this.version
|
|
336
|
+
} : {}, extra);
|
|
337
|
+
return headers;
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
key: "observeResponse",
|
|
342
|
+
value: /**
|
|
343
|
+
* Read the registry-derived `X-Client-Latest-Version` response header
|
|
344
|
+
* and feed it to the update-notice comparator. Called from every
|
|
345
|
+
* fetch site. No-op when no version is configured (older constructions
|
|
346
|
+
* that didn't pass `version` should not trigger an update prompt).
|
|
347
|
+
*/ function observeResponse(res) {
|
|
348
|
+
if (this.version) {
|
|
349
|
+
readLatestVersionFromResponse(res, this.version);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
323
353
|
{
|
|
324
354
|
key: "publish",
|
|
325
355
|
value: function publish(params) {
|
|
@@ -355,9 +385,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
355
385
|
}
|
|
356
386
|
return [
|
|
357
387
|
2,
|
|
358
|
-
this.doFetch('POST', '/v1/public/artifacts', form,
|
|
359
|
-
'X-Client-Type': this.clientType
|
|
360
|
-
})
|
|
388
|
+
this.doFetch('POST', '/v1/public/artifacts', form, this.clientHeaders())
|
|
361
389
|
];
|
|
362
390
|
});
|
|
363
391
|
}).call(this);
|
|
@@ -804,14 +832,14 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
804
832
|
4,
|
|
805
833
|
fetch(url, {
|
|
806
834
|
method: 'GET',
|
|
807
|
-
headers: {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
835
|
+
headers: this.clientHeaders({
|
|
836
|
+
Authorization: "Bearer ".concat(this.apiKey)
|
|
837
|
+
})
|
|
811
838
|
})
|
|
812
839
|
];
|
|
813
840
|
case 1:
|
|
814
841
|
res = _state.sent();
|
|
842
|
+
this.observeResponse(res);
|
|
815
843
|
if (!!res.ok) return [
|
|
816
844
|
3,
|
|
817
845
|
3
|
|
@@ -961,10 +989,9 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
961
989
|
return [
|
|
962
990
|
4,
|
|
963
991
|
fetch("".concat(this.baseUrl, "/v1/artifacts?limit=1"), {
|
|
964
|
-
headers: {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
992
|
+
headers: this.clientHeaders({
|
|
993
|
+
Authorization: "Bearer ".concat(apiKey)
|
|
994
|
+
})
|
|
968
995
|
})
|
|
969
996
|
];
|
|
970
997
|
case 1:
|
|
@@ -980,6 +1007,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
980
1007
|
'network_error'
|
|
981
1008
|
];
|
|
982
1009
|
case 3:
|
|
1010
|
+
this.observeResponse(res);
|
|
983
1011
|
if (res.ok) {
|
|
984
1012
|
return [
|
|
985
1013
|
2,
|
|
@@ -1008,10 +1036,9 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1008
1036
|
return _ts_generator(this, function(_state) {
|
|
1009
1037
|
return [
|
|
1010
1038
|
2,
|
|
1011
|
-
this.doFetch(method, path, body, {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
})
|
|
1039
|
+
this.doFetch(method, path, body, this.clientHeaders({
|
|
1040
|
+
Authorization: "Bearer ".concat(this.apiKey)
|
|
1041
|
+
}))
|
|
1015
1042
|
];
|
|
1016
1043
|
});
|
|
1017
1044
|
}).call(this);
|
|
@@ -1024,9 +1051,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1024
1051
|
return _ts_generator(this, function(_state) {
|
|
1025
1052
|
return [
|
|
1026
1053
|
2,
|
|
1027
|
-
this.doFetch(method, path, body,
|
|
1028
|
-
'X-Client-Type': this.clientType
|
|
1029
|
-
})
|
|
1054
|
+
this.doFetch(method, path, body, this.clientHeaders())
|
|
1030
1055
|
];
|
|
1031
1056
|
});
|
|
1032
1057
|
}).call(this);
|
|
@@ -1045,15 +1070,15 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1045
1070
|
4,
|
|
1046
1071
|
fetch(url, {
|
|
1047
1072
|
method: method,
|
|
1048
|
-
headers: {
|
|
1049
|
-
'Content-Type': 'application/json'
|
|
1050
|
-
|
|
1051
|
-
},
|
|
1073
|
+
headers: this.clientHeaders({
|
|
1074
|
+
'Content-Type': 'application/json'
|
|
1075
|
+
}),
|
|
1052
1076
|
body: body ? JSON.stringify(body) : undefined
|
|
1053
1077
|
})
|
|
1054
1078
|
];
|
|
1055
1079
|
case 1:
|
|
1056
1080
|
res = _state.sent();
|
|
1081
|
+
this.observeResponse(res);
|
|
1057
1082
|
return [
|
|
1058
1083
|
2,
|
|
1059
1084
|
res.json()
|
|
@@ -1088,6 +1113,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1088
1113
|
];
|
|
1089
1114
|
case 1:
|
|
1090
1115
|
res = _state.sent();
|
|
1116
|
+
this.observeResponse(res);
|
|
1091
1117
|
if (!!res.ok) return [
|
|
1092
1118
|
3,
|
|
1093
1119
|
3
|
package/dist/main.js
CHANGED
|
@@ -198,9 +198,26 @@ import { Command } from 'commander';
|
|
|
198
198
|
import { ApiClient, ApiError } from './api-client.js';
|
|
199
199
|
import { loadConfig, saveConfig } from './config.js';
|
|
200
200
|
import { startMcpServer } from './mcp-server.js';
|
|
201
|
+
import { emitOnExit, setCurrentVersion, setMcpMode } from './update-notice.js';
|
|
201
202
|
import { DEFAULT_API_URL, DeviceCodeDeniedError, DeviceCodeExpiredError, DeviceCodeFailedError, InvalidFlagError, PublishArgsError, classifyBrandingError, parseShortIdAndVersion, parseShowBrandingFlag, pollDeviceToken, readApiKeyFromTty, readStreamToString, resolveAuth as resolveAuthFromEnvAndConfig, validatePublishArgs } from './main-helpers.js';
|
|
202
203
|
var require = createRequire(import.meta.url);
|
|
203
204
|
var version = require('../package.json').version;
|
|
205
|
+
// Update-notice scaffolding (spec/feat-cli-update-notification.md). The
|
|
206
|
+
// `'exit'` hook fires on every termination path — natural completion,
|
|
207
|
+
// `process.exit(N)`, uncaught throws — so the notice lands regardless of
|
|
208
|
+
// which exit path the command took. `'beforeExit'` would not work; it
|
|
209
|
+
// does not fire when `process.exit()` is called explicitly.
|
|
210
|
+
//
|
|
211
|
+
// Process-level idempotency: keyed on a Symbol.for so `vi.resetModules()`
|
|
212
|
+
// + dynamic re-import in tests does not stack a second listener (each
|
|
213
|
+
// re-import would otherwise re-run this top-level statement).
|
|
214
|
+
setCurrentVersion(version);
|
|
215
|
+
var EXIT_HOOK_FLAG = Symbol.for('@displaydev/cli.exit-hook-registered');
|
|
216
|
+
var flagged = process;
|
|
217
|
+
if (!flagged[EXIT_HOOK_FLAG]) {
|
|
218
|
+
flagged[EXIT_HOOK_FLAG] = true;
|
|
219
|
+
process.on('exit', emitOnExit);
|
|
220
|
+
}
|
|
204
221
|
function resolveAuthOrConfig() {
|
|
205
222
|
return _async_to_generator(function() {
|
|
206
223
|
var auth;
|
|
@@ -260,7 +277,8 @@ function createClient(auth) {
|
|
|
260
277
|
return new ApiClient({
|
|
261
278
|
baseUrl: auth.apiUrl,
|
|
262
279
|
apiKey: auth.apiKey,
|
|
263
|
-
clientType: 'cli'
|
|
280
|
+
clientType: 'cli',
|
|
281
|
+
version: version
|
|
264
282
|
});
|
|
265
283
|
}
|
|
266
284
|
var program = new Command().name('dsp').description('display.dev CLI — publish artifacts behind company auth').version(version);
|
|
@@ -407,7 +425,8 @@ program.command('publish <path>').description('Publish an HTML or Markdown file.
|
|
|
407
425
|
publicClient = new ApiClient({
|
|
408
426
|
baseUrl: resolvePublicApiUrl(),
|
|
409
427
|
apiKey: '',
|
|
410
|
-
clientType: 'cli'
|
|
428
|
+
clientType: 'cli',
|
|
429
|
+
version: version
|
|
411
430
|
});
|
|
412
431
|
_state.label = 10;
|
|
413
432
|
case 10:
|
|
@@ -1074,7 +1093,8 @@ program.command('login').description('Authenticate with display.dev').option('--
|
|
|
1074
1093
|
client = new ApiClient({
|
|
1075
1094
|
baseUrl: apiUrl,
|
|
1076
1095
|
apiKey: '',
|
|
1077
|
-
clientType: 'cli'
|
|
1096
|
+
clientType: 'cli',
|
|
1097
|
+
version: version
|
|
1078
1098
|
});
|
|
1079
1099
|
if (!(opts.apiKey !== undefined)) return [
|
|
1080
1100
|
3,
|
|
@@ -2247,6 +2267,7 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2247
2267
|
];
|
|
2248
2268
|
case 1:
|
|
2249
2269
|
auth = _state.sent();
|
|
2270
|
+
setMcpMode();
|
|
2250
2271
|
if (!auth) return [
|
|
2251
2272
|
3,
|
|
2252
2273
|
3
|
|
@@ -2254,7 +2275,8 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2254
2275
|
client = new ApiClient({
|
|
2255
2276
|
baseUrl: auth.apiUrl,
|
|
2256
2277
|
apiKey: auth.apiKey,
|
|
2257
|
-
clientType: 'mcp-stdio'
|
|
2278
|
+
clientType: 'mcp-stdio',
|
|
2279
|
+
version: version
|
|
2258
2280
|
});
|
|
2259
2281
|
return [
|
|
2260
2282
|
4,
|
|
@@ -2274,7 +2296,8 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2274
2296
|
publicClient = new ApiClient({
|
|
2275
2297
|
baseUrl: resolvePublicApiUrl(),
|
|
2276
2298
|
apiKey: '',
|
|
2277
|
-
clientType: 'mcp-stdio'
|
|
2299
|
+
clientType: 'mcp-stdio',
|
|
2300
|
+
version: version
|
|
2278
2301
|
});
|
|
2279
2302
|
return [
|
|
2280
2303
|
4,
|
package/dist/mcp-server.js
CHANGED
|
@@ -192,6 +192,7 @@ import { z } from 'zod';
|
|
|
192
192
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
193
193
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
194
194
|
import { ApiError } from './api-client.js';
|
|
195
|
+
import { withUpdateNotice } from './update-notice.js';
|
|
195
196
|
var FILE_ERROR_CODES = new Set([
|
|
196
197
|
'ENOENT',
|
|
197
198
|
'EACCES',
|
|
@@ -313,7 +314,7 @@ export function registerPublicTools(server, api) {
|
|
|
313
314
|
'html',
|
|
314
315
|
'md'
|
|
315
316
|
]).default('html').describe('Content format')
|
|
316
|
-
}, function(args) {
|
|
317
|
+
}, withUpdateNotice(function(args) {
|
|
317
318
|
return _async_to_generator(function() {
|
|
318
319
|
var hasContent, hasFilePath, content, _tmp, result, err;
|
|
319
320
|
return _ts_generator(this, function(_state) {
|
|
@@ -409,7 +410,7 @@ export function registerPublicTools(server, api) {
|
|
|
409
410
|
}
|
|
410
411
|
});
|
|
411
412
|
})();
|
|
412
|
-
});
|
|
413
|
+
}));
|
|
413
414
|
}
|
|
414
415
|
export function registerTools(server, api) {
|
|
415
416
|
server.tool('publish', 'Publish an HTML or Markdown artifact behind company auth', {
|
|
@@ -433,7 +434,7 @@ export function registerTools(server, api) {
|
|
|
433
434
|
'hide',
|
|
434
435
|
'inherit'
|
|
435
436
|
]).optional().describe('display.dev attribution bar override. Paid tier only; defaults to org setting when omitted.')
|
|
436
|
-
}, function(args) {
|
|
437
|
+
}, withUpdateNotice(function(args) {
|
|
437
438
|
return _async_to_generator(function() {
|
|
438
439
|
var _args_name, hasContent, hasFilePath, _args_name1, content, _tmp, result, _tmp1, err;
|
|
439
440
|
return _ts_generator(this, function(_state) {
|
|
@@ -611,7 +612,7 @@ export function registerTools(server, api) {
|
|
|
611
612
|
}
|
|
612
613
|
});
|
|
613
614
|
})();
|
|
614
|
-
});
|
|
615
|
+
}));
|
|
615
616
|
server.tool('find', 'List or search artifacts in the caller\'s organization. Omit `query` to list every artifact (paginated). Filter by name, one-or-more authors, one-or-more visibilities, or update time. Sort by `updated_at` (default), `view_count`, or `name`. Paginate via `cursor` — if the result includes a non-null `nextCursor`, pass it back to fetch the next page; if filters change between calls, drop the cursor. The response also carries `totalCount` (`-1` means the count query exceeded its budget — treat as "many"). `visibility: ["private"]` returns only your own private artifacts, not org-wide.', {
|
|
616
617
|
query: z.string().optional().describe('Search by artifact name (case-insensitive substring). Omit to list all artifacts.'),
|
|
617
618
|
author: z.array(z.string()).optional().describe('Filter by one or more authors. Each value: email, userId, or the literal "me". Multiple values are ORed. "me" requires a user-scoped credential — with an org API key the wire drops it.'),
|
|
@@ -632,7 +633,7 @@ export function registerTools(server, api) {
|
|
|
632
633
|
]).optional().describe('Sort direction. Defaults: desc for updated_at and view_count, asc for name.'),
|
|
633
634
|
cursor: z.string().optional().describe('Opaque pagination token from a prior response. Pass it back to fetch the next page. WARNING: only valid for the same `sort`/`dir`/filter set that issued it — change a filter and drop the cursor, or results will be skewed without an error.'),
|
|
634
635
|
limit: z.number().int().min(1).max(100).optional().describe('Max results per page (1–100, default 50).')
|
|
635
|
-
}, function(args) {
|
|
636
|
+
}, withUpdateNotice(function(args) {
|
|
636
637
|
return _async_to_generator(function() {
|
|
637
638
|
var result, err;
|
|
638
639
|
return _ts_generator(this, function(_state) {
|
|
@@ -667,11 +668,11 @@ export function registerTools(server, api) {
|
|
|
667
668
|
}
|
|
668
669
|
});
|
|
669
670
|
})();
|
|
670
|
-
});
|
|
671
|
+
}));
|
|
671
672
|
server.tool('get', 'Get full details of a specific artifact', {
|
|
672
673
|
short_id: z.string().describe('Artifact shortId'),
|
|
673
674
|
include: z.array(z.string()).optional().describe('Include additional data (e.g. "versions")')
|
|
674
|
-
}, function(args) {
|
|
675
|
+
}, withUpdateNotice(function(args) {
|
|
675
676
|
return _async_to_generator(function() {
|
|
676
677
|
var result, err;
|
|
677
678
|
return _ts_generator(this, function(_state) {
|
|
@@ -706,11 +707,11 @@ export function registerTools(server, api) {
|
|
|
706
707
|
}
|
|
707
708
|
});
|
|
708
709
|
})();
|
|
709
|
-
});
|
|
710
|
+
}));
|
|
710
711
|
server.tool('delete', 'Delete an artifact permanently', {
|
|
711
712
|
short_id: z.string().describe('Artifact shortId to delete'),
|
|
712
713
|
confirm: z.boolean().describe('Must be true to confirm deletion')
|
|
713
|
-
}, function(args) {
|
|
714
|
+
}, withUpdateNotice(function(args) {
|
|
714
715
|
return _async_to_generator(function() {
|
|
715
716
|
var result, err;
|
|
716
717
|
return _ts_generator(this, function(_state) {
|
|
@@ -764,7 +765,7 @@ export function registerTools(server, api) {
|
|
|
764
765
|
}
|
|
765
766
|
});
|
|
766
767
|
})();
|
|
767
|
-
});
|
|
768
|
+
}));
|
|
768
769
|
server.tool('share', 'Change an artifact\'s visibility and/or add/remove individual shared-with emails without republishing.', {
|
|
769
770
|
short_id: z.string().describe('Short ID of the artifact (8 chars).'),
|
|
770
771
|
visibility: z.enum([
|
|
@@ -774,7 +775,7 @@ export function registerTools(server, api) {
|
|
|
774
775
|
]).optional().describe('Change the visibility level. Omit to keep current. "private" requires the Pro plan.'),
|
|
775
776
|
add_users: z.array(z.string()).optional().describe('Emails to add to sharedWith (idempotent).'),
|
|
776
777
|
remove_users: z.array(z.string()).optional().describe('Emails to remove from sharedWith (idempotent).')
|
|
777
|
-
}, function(args) {
|
|
778
|
+
}, withUpdateNotice(function(args) {
|
|
778
779
|
return _async_to_generator(function() {
|
|
779
780
|
var result, err;
|
|
780
781
|
return _ts_generator(this, function(_state) {
|
|
@@ -832,11 +833,11 @@ export function registerTools(server, api) {
|
|
|
832
833
|
}
|
|
833
834
|
});
|
|
834
835
|
})();
|
|
835
|
-
});
|
|
836
|
+
}));
|
|
836
837
|
server.tool('rename', 'Rename a published artifact. The URL slug updates to match; existing URLs still resolve because routing uses the shortId.', {
|
|
837
838
|
short_id: z.string().describe('The 8-character shortId returned at publish.'),
|
|
838
839
|
name: z.string().describe('New display name. 1–200 characters.')
|
|
839
|
-
}, function(args) {
|
|
840
|
+
}, withUpdateNotice(function(args) {
|
|
840
841
|
return _async_to_generator(function() {
|
|
841
842
|
var result, err;
|
|
842
843
|
return _ts_generator(this, function(_state) {
|
|
@@ -875,7 +876,7 @@ export function registerTools(server, api) {
|
|
|
875
876
|
}
|
|
876
877
|
});
|
|
877
878
|
})();
|
|
878
|
-
});
|
|
879
|
+
}));
|
|
879
880
|
server.tool('export', 'Retrieve the source bytes of a published artifact. `format: "original"` returns the publisher\'s upload (markdown for .md, HTML for .html). `format: "markdown"` always returns markdown — `.md` is unchanged, `.html` is converted via Workers AI (cached).', {
|
|
880
881
|
short_id: z.string().describe('Short ID of the artifact (8 chars).'),
|
|
881
882
|
version: z.number().int().min(1).optional().describe('Pinned version number. Omit for current.'),
|
|
@@ -883,7 +884,7 @@ export function registerTools(server, api) {
|
|
|
883
884
|
'original',
|
|
884
885
|
'markdown'
|
|
885
886
|
]).default('original').describe('Representation requested. Default "original".')
|
|
886
|
-
}, function(args) {
|
|
887
|
+
}, withUpdateNotice(function(args) {
|
|
887
888
|
return _async_to_generator(function() {
|
|
888
889
|
var result, err;
|
|
889
890
|
return _ts_generator(this, function(_state) {
|
|
@@ -931,7 +932,7 @@ export function registerTools(server, api) {
|
|
|
931
932
|
}
|
|
932
933
|
});
|
|
933
934
|
})();
|
|
934
|
-
});
|
|
935
|
+
}));
|
|
935
936
|
// Metadata-only: does NOT bump the artifact version. Use the `publish`
|
|
936
937
|
// tool (with `show_branding`) when you're changing content at the same
|
|
937
938
|
// time so both writes happen in one transaction.
|
|
@@ -942,7 +943,7 @@ export function registerTools(server, api) {
|
|
|
942
943
|
'hide',
|
|
943
944
|
'inherit'
|
|
944
945
|
]).describe('show = force branding on; hide = force off; inherit = follow org default')
|
|
945
|
-
}, function(args) {
|
|
946
|
+
}, withUpdateNotice(function(args) {
|
|
946
947
|
return _async_to_generator(function() {
|
|
947
948
|
var result, err;
|
|
948
949
|
return _ts_generator(this, function(_state) {
|
|
@@ -977,14 +978,14 @@ export function registerTools(server, api) {
|
|
|
977
978
|
}
|
|
978
979
|
});
|
|
979
980
|
})();
|
|
980
|
-
});
|
|
981
|
+
}));
|
|
981
982
|
server.tool('set_logo', 'Upload or replace the org logo (paid tiers only). Used as the favicon on every artifact the org publishes. Stdio variant reads bytes from a local file path.', {
|
|
982
983
|
file_path: z.string().describe('Path to a local PNG or WebP file. Square, 32×32 to 1024×1024, ≤256 KB.'),
|
|
983
984
|
content_type: z.enum([
|
|
984
985
|
'image/png',
|
|
985
986
|
'image/webp'
|
|
986
987
|
]).optional().describe('Optional declared MIME type; inferred from the file extension when omitted.')
|
|
987
|
-
}, function(args) {
|
|
988
|
+
}, withUpdateNotice(function(args) {
|
|
988
989
|
return _async_to_generator(function() {
|
|
989
990
|
var bytes, err, contentType, lower, result, err1;
|
|
990
991
|
return _ts_generator(this, function(_state) {
|
|
@@ -1073,8 +1074,8 @@ export function registerTools(server, api) {
|
|
|
1073
1074
|
}
|
|
1074
1075
|
});
|
|
1075
1076
|
})();
|
|
1076
|
-
});
|
|
1077
|
-
server.tool('clear_logo', 'Remove the org logo. Idempotent — succeeds even when no logo is currently set.', {}, function() {
|
|
1077
|
+
}));
|
|
1078
|
+
server.tool('clear_logo', 'Remove the org logo. Idempotent — succeeds even when no logo is currently set.', {}, withUpdateNotice(function() {
|
|
1078
1079
|
return _async_to_generator(function() {
|
|
1079
1080
|
var err;
|
|
1080
1081
|
return _ts_generator(this, function(_state) {
|
|
@@ -1111,7 +1112,7 @@ export function registerTools(server, api) {
|
|
|
1111
1112
|
}
|
|
1112
1113
|
});
|
|
1113
1114
|
})();
|
|
1114
|
-
});
|
|
1115
|
+
}));
|
|
1115
1116
|
registerCommentTools(server, api);
|
|
1116
1117
|
}
|
|
1117
1118
|
/**
|
|
@@ -1179,7 +1180,7 @@ export function registerTools(server, api) {
|
|
|
1179
1180
|
body: z.string().min(1).max(10000).describe('Comment body (≤10k chars)'),
|
|
1180
1181
|
parent_id: z.string().optional().describe('Root comment id when posting a reply'),
|
|
1181
1182
|
anchor: anchorSchema
|
|
1182
|
-
}), function(args) {
|
|
1183
|
+
}), withUpdateNotice(function(args) {
|
|
1183
1184
|
return _async_to_generator(function() {
|
|
1184
1185
|
var shortIdOrErr, result, err;
|
|
1185
1186
|
return _ts_generator(this, function(_state) {
|
|
@@ -1233,7 +1234,7 @@ export function registerTools(server, api) {
|
|
|
1233
1234
|
}
|
|
1234
1235
|
});
|
|
1235
1236
|
})();
|
|
1236
|
-
});
|
|
1237
|
+
}));
|
|
1237
1238
|
server.tool('list_comments', 'List comment threads on a subject. Reads from Postgres (not the KV widget cache).', _object_spread_props(_object_spread({}, subjectArgs), {
|
|
1238
1239
|
status: z.enum([
|
|
1239
1240
|
'open',
|
|
@@ -1241,7 +1242,7 @@ export function registerTools(server, api) {
|
|
|
1241
1242
|
'all'
|
|
1242
1243
|
]).optional().describe('Defaults to "open"'),
|
|
1243
1244
|
since: z.string().optional().describe('ISO-8601 — return only threads with activity at or after this timestamp')
|
|
1244
|
-
}), function(args) {
|
|
1245
|
+
}), withUpdateNotice(function(args) {
|
|
1245
1246
|
return _async_to_generator(function() {
|
|
1246
1247
|
var shortIdOrErr, result, err;
|
|
1247
1248
|
return _ts_generator(this, function(_state) {
|
|
@@ -1294,11 +1295,11 @@ export function registerTools(server, api) {
|
|
|
1294
1295
|
}
|
|
1295
1296
|
});
|
|
1296
1297
|
})();
|
|
1297
|
-
});
|
|
1298
|
+
}));
|
|
1298
1299
|
server.tool('edit_comment', 'Edit your own comment within the 5-minute author window. After 5 minutes the body is locked.', {
|
|
1299
1300
|
comment_id: z.string(),
|
|
1300
1301
|
body: z.string().min(1).max(10000)
|
|
1301
|
-
}, function(args) {
|
|
1302
|
+
}, withUpdateNotice(function(args) {
|
|
1302
1303
|
return _async_to_generator(function() {
|
|
1303
1304
|
var result, err;
|
|
1304
1305
|
return _ts_generator(this, function(_state) {
|
|
@@ -1333,10 +1334,10 @@ export function registerTools(server, api) {
|
|
|
1333
1334
|
}
|
|
1334
1335
|
});
|
|
1335
1336
|
})();
|
|
1336
|
-
});
|
|
1337
|
+
}));
|
|
1337
1338
|
server.tool('delete_comment', 'Soft-delete a comment. Author / artifact creator / org admin. Replies remain visible; body becomes "[deleted]".', {
|
|
1338
1339
|
comment_id: z.string()
|
|
1339
|
-
}, function(args) {
|
|
1340
|
+
}, withUpdateNotice(function(args) {
|
|
1340
1341
|
return _async_to_generator(function() {
|
|
1341
1342
|
var err;
|
|
1342
1343
|
return _ts_generator(this, function(_state) {
|
|
@@ -1373,10 +1374,10 @@ export function registerTools(server, api) {
|
|
|
1373
1374
|
}
|
|
1374
1375
|
});
|
|
1375
1376
|
})();
|
|
1376
|
-
});
|
|
1377
|
+
}));
|
|
1377
1378
|
server.tool('resolve_thread', 'Mark a thread resolved (root comment id). Thread participant / artifact creator / org admin.', {
|
|
1378
1379
|
comment_id: z.string()
|
|
1379
|
-
}, function(args) {
|
|
1380
|
+
}, withUpdateNotice(function(args) {
|
|
1380
1381
|
return _async_to_generator(function() {
|
|
1381
1382
|
var result, err;
|
|
1382
1383
|
return _ts_generator(this, function(_state) {
|
|
@@ -1411,10 +1412,10 @@ export function registerTools(server, api) {
|
|
|
1411
1412
|
}
|
|
1412
1413
|
});
|
|
1413
1414
|
})();
|
|
1414
|
-
});
|
|
1415
|
+
}));
|
|
1415
1416
|
server.tool('reopen_thread', 'Reopen a resolved thread. Any authenticated user with view access on the artifact.', {
|
|
1416
1417
|
comment_id: z.string()
|
|
1417
|
-
}, function(args) {
|
|
1418
|
+
}, withUpdateNotice(function(args) {
|
|
1418
1419
|
return _async_to_generator(function() {
|
|
1419
1420
|
var result, err;
|
|
1420
1421
|
return _ts_generator(this, function(_state) {
|
|
@@ -1449,8 +1450,8 @@ export function registerTools(server, api) {
|
|
|
1449
1450
|
}
|
|
1450
1451
|
});
|
|
1451
1452
|
})();
|
|
1452
|
-
});
|
|
1453
|
-
server.tool('watch', 'Watch the subject for comment notifications. Per-user; service-account API keys cannot subscribe.', subjectArgs, function(args) {
|
|
1453
|
+
}));
|
|
1454
|
+
server.tool('watch', 'Watch the subject for comment notifications. Per-user; service-account API keys cannot subscribe.', subjectArgs, withUpdateNotice(function(args) {
|
|
1454
1455
|
return _async_to_generator(function() {
|
|
1455
1456
|
var shortIdOrErr, result, err;
|
|
1456
1457
|
return _ts_generator(this, function(_state) {
|
|
@@ -1500,8 +1501,8 @@ export function registerTools(server, api) {
|
|
|
1500
1501
|
}
|
|
1501
1502
|
});
|
|
1502
1503
|
})();
|
|
1503
|
-
});
|
|
1504
|
-
server.tool('unwatch', 'Unwatch the subject. Per-user; service-account API keys cannot subscribe.', subjectArgs, function(args) {
|
|
1504
|
+
}));
|
|
1505
|
+
server.tool('unwatch', 'Unwatch the subject. Per-user; service-account API keys cannot subscribe.', subjectArgs, withUpdateNotice(function(args) {
|
|
1505
1506
|
return _async_to_generator(function() {
|
|
1506
1507
|
var shortIdOrErr, err;
|
|
1507
1508
|
return _ts_generator(this, function(_state) {
|
|
@@ -1553,5 +1554,5 @@ export function registerTools(server, api) {
|
|
|
1553
1554
|
}
|
|
1554
1555
|
});
|
|
1555
1556
|
})();
|
|
1556
|
-
});
|
|
1557
|
+
}));
|
|
1557
1558
|
}
|
|
@@ -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
|
+
}
|