@displaydev/cli 0.19.0 → 0.21.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 +59 -27
- package/dist/main.js +125 -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,50 @@ 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, _config_clientSource;
|
|
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);
|
|
320
|
+
_define_property(this, "clientSource", void 0);
|
|
318
321
|
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
319
322
|
this.apiKey = config.apiKey;
|
|
320
323
|
this.clientType = (_config_clientType = config.clientType) !== null && _config_clientType !== void 0 ? _config_clientType : 'mcp-stdio';
|
|
324
|
+
this.version = (_config_version = config.version) !== null && _config_version !== void 0 ? _config_version : '';
|
|
325
|
+
this.clientSource = (_config_clientSource = config.clientSource) !== null && _config_clientSource !== void 0 ? _config_clientSource : '';
|
|
321
326
|
}
|
|
322
327
|
_create_class(ApiClient, [
|
|
328
|
+
{
|
|
329
|
+
key: "clientHeaders",
|
|
330
|
+
value: /**
|
|
331
|
+
* Build the request-side header set. Always carries `X-Client-Type`;
|
|
332
|
+
* adds `X-Client-Version` when the client was constructed with one;
|
|
333
|
+
* adds `X-Client-Source` when a distribution-channel attribution was
|
|
334
|
+
* configured.
|
|
335
|
+
*/ function clientHeaders(extra) {
|
|
336
|
+
var headers = _object_spread({
|
|
337
|
+
'X-Client-Type': this.clientType
|
|
338
|
+
}, this.version ? {
|
|
339
|
+
'X-Client-Version': this.version
|
|
340
|
+
} : {}, this.clientSource ? {
|
|
341
|
+
'X-Client-Source': this.clientSource
|
|
342
|
+
} : {}, extra);
|
|
343
|
+
return headers;
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
key: "observeResponse",
|
|
348
|
+
value: /**
|
|
349
|
+
* Read the registry-derived `X-Client-Latest-Version` response header
|
|
350
|
+
* and feed it to the update-notice comparator. Called from every
|
|
351
|
+
* fetch site. No-op when no version is configured (older constructions
|
|
352
|
+
* that didn't pass `version` should not trigger an update prompt).
|
|
353
|
+
*/ function observeResponse(res) {
|
|
354
|
+
if (this.version) {
|
|
355
|
+
readLatestVersionFromResponse(res, this.version);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
323
359
|
{
|
|
324
360
|
key: "publish",
|
|
325
361
|
value: function publish(params) {
|
|
@@ -355,9 +391,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
355
391
|
}
|
|
356
392
|
return [
|
|
357
393
|
2,
|
|
358
|
-
this.doFetch('POST', '/v1/public/artifacts', form,
|
|
359
|
-
'X-Client-Type': this.clientType
|
|
360
|
-
})
|
|
394
|
+
this.doFetch('POST', '/v1/public/artifacts', form, this.clientHeaders())
|
|
361
395
|
];
|
|
362
396
|
});
|
|
363
397
|
}).call(this);
|
|
@@ -804,14 +838,14 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
804
838
|
4,
|
|
805
839
|
fetch(url, {
|
|
806
840
|
method: 'GET',
|
|
807
|
-
headers: {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
841
|
+
headers: this.clientHeaders({
|
|
842
|
+
Authorization: "Bearer ".concat(this.apiKey)
|
|
843
|
+
})
|
|
811
844
|
})
|
|
812
845
|
];
|
|
813
846
|
case 1:
|
|
814
847
|
res = _state.sent();
|
|
848
|
+
this.observeResponse(res);
|
|
815
849
|
if (!!res.ok) return [
|
|
816
850
|
3,
|
|
817
851
|
3
|
|
@@ -961,10 +995,9 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
961
995
|
return [
|
|
962
996
|
4,
|
|
963
997
|
fetch("".concat(this.baseUrl, "/v1/artifacts?limit=1"), {
|
|
964
|
-
headers: {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
998
|
+
headers: this.clientHeaders({
|
|
999
|
+
Authorization: "Bearer ".concat(apiKey)
|
|
1000
|
+
})
|
|
968
1001
|
})
|
|
969
1002
|
];
|
|
970
1003
|
case 1:
|
|
@@ -980,6 +1013,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
980
1013
|
'network_error'
|
|
981
1014
|
];
|
|
982
1015
|
case 3:
|
|
1016
|
+
this.observeResponse(res);
|
|
983
1017
|
if (res.ok) {
|
|
984
1018
|
return [
|
|
985
1019
|
2,
|
|
@@ -1008,10 +1042,9 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1008
1042
|
return _ts_generator(this, function(_state) {
|
|
1009
1043
|
return [
|
|
1010
1044
|
2,
|
|
1011
|
-
this.doFetch(method, path, body, {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
})
|
|
1045
|
+
this.doFetch(method, path, body, this.clientHeaders({
|
|
1046
|
+
Authorization: "Bearer ".concat(this.apiKey)
|
|
1047
|
+
}))
|
|
1015
1048
|
];
|
|
1016
1049
|
});
|
|
1017
1050
|
}).call(this);
|
|
@@ -1024,9 +1057,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1024
1057
|
return _ts_generator(this, function(_state) {
|
|
1025
1058
|
return [
|
|
1026
1059
|
2,
|
|
1027
|
-
this.doFetch(method, path, body,
|
|
1028
|
-
'X-Client-Type': this.clientType
|
|
1029
|
-
})
|
|
1060
|
+
this.doFetch(method, path, body, this.clientHeaders())
|
|
1030
1061
|
];
|
|
1031
1062
|
});
|
|
1032
1063
|
}).call(this);
|
|
@@ -1045,15 +1076,15 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1045
1076
|
4,
|
|
1046
1077
|
fetch(url, {
|
|
1047
1078
|
method: method,
|
|
1048
|
-
headers: {
|
|
1049
|
-
'Content-Type': 'application/json'
|
|
1050
|
-
|
|
1051
|
-
},
|
|
1079
|
+
headers: this.clientHeaders({
|
|
1080
|
+
'Content-Type': 'application/json'
|
|
1081
|
+
}),
|
|
1052
1082
|
body: body ? JSON.stringify(body) : undefined
|
|
1053
1083
|
})
|
|
1054
1084
|
];
|
|
1055
1085
|
case 1:
|
|
1056
1086
|
res = _state.sent();
|
|
1087
|
+
this.observeResponse(res);
|
|
1057
1088
|
return [
|
|
1058
1089
|
2,
|
|
1059
1090
|
res.json()
|
|
@@ -1088,6 +1119,7 @@ export var ApiClient = /*#__PURE__*/ function() {
|
|
|
1088
1119
|
];
|
|
1089
1120
|
case 1:
|
|
1090
1121
|
res = _state.sent();
|
|
1122
|
+
this.observeResponse(res);
|
|
1091
1123
|
if (!!res.ok) return [
|
|
1092
1124
|
3,
|
|
1093
1125
|
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;
|
|
@@ -256,11 +273,50 @@ function resolvePublicApiUrl() {
|
|
|
256
273
|
var _process_env_DISPLAYDEV_API_URL;
|
|
257
274
|
return (_process_env_DISPLAYDEV_API_URL = process.env.DISPLAYDEV_API_URL) !== null && _process_env_DISPLAYDEV_API_URL !== void 0 ? _process_env_DISPLAYDEV_API_URL : DEFAULT_API_URL;
|
|
258
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Distribution-channel attribution stash. `--client-source <name>` is
|
|
278
|
+
* registered on `program` and on every subcommand in the tree (see
|
|
279
|
+
* `addClientSourceOptionRecursive` at the bottom of this file) and a
|
|
280
|
+
* `preAction` hook stashes the parsed value before any action runs.
|
|
281
|
+
* `createClient` and the two unauthenticated `new ApiClient(...)` sites
|
|
282
|
+
* then read the stash via `resolveClientSource()`.
|
|
283
|
+
*
|
|
284
|
+
* Why a global stash instead of threading through every action: there
|
|
285
|
+
* are ~20 direct-HTTP subcommands and a single resolution path; passing
|
|
286
|
+
* the value down through each action's opts type would touch every
|
|
287
|
+
* action handler for a flag that is structurally cross-cutting.
|
|
288
|
+
*
|
|
289
|
+
* The flag is also registered on `mcp` so `dsp mcp --client-source foo`
|
|
290
|
+
* is silently accepted (per spec §1 out-of-scope). The stash gets
|
|
291
|
+
* populated, but the mcp action builds its `ApiClient` without reading
|
|
292
|
+
* the stash, so the value never reaches the spawned MCP server's
|
|
293
|
+
* outgoing requests.
|
|
294
|
+
*
|
|
295
|
+
* Precedence: flag > `DISPLAYDEV_CLIENT_SOURCE` env > omitted. An
|
|
296
|
+
* explicitly empty flag (`--client-source=`) is treated as the user
|
|
297
|
+
* suppressing attribution: the env fallback is skipped and the header
|
|
298
|
+
* is omitted. The header is analytics-only; it does not affect audit /
|
|
299
|
+
* share-notification source normalization (which keys off
|
|
300
|
+
* `X-Client-Type`).
|
|
301
|
+
*/ var clientSourceFromFlag;
|
|
302
|
+
var clientSourceFlagPassed = false;
|
|
303
|
+
function resolveClientSource() {
|
|
304
|
+
if (clientSourceFlagPassed) {
|
|
305
|
+
return clientSourceFromFlag;
|
|
306
|
+
}
|
|
307
|
+
var env = process.env.DISPLAYDEV_CLIENT_SOURCE;
|
|
308
|
+
if (typeof env === 'string' && env.length > 0) {
|
|
309
|
+
return env;
|
|
310
|
+
}
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
259
313
|
function createClient(auth) {
|
|
260
314
|
return new ApiClient({
|
|
261
315
|
baseUrl: auth.apiUrl,
|
|
262
316
|
apiKey: auth.apiKey,
|
|
263
|
-
clientType: 'cli'
|
|
317
|
+
clientType: 'cli',
|
|
318
|
+
version: version,
|
|
319
|
+
clientSource: resolveClientSource()
|
|
264
320
|
});
|
|
265
321
|
}
|
|
266
322
|
var program = new Command().name('dsp').description('display.dev CLI — publish artifacts behind company auth').version(version);
|
|
@@ -407,7 +463,9 @@ program.command('publish <path>').description('Publish an HTML or Markdown file.
|
|
|
407
463
|
publicClient = new ApiClient({
|
|
408
464
|
baseUrl: resolvePublicApiUrl(),
|
|
409
465
|
apiKey: '',
|
|
410
|
-
clientType: 'cli'
|
|
466
|
+
clientType: 'cli',
|
|
467
|
+
version: version,
|
|
468
|
+
clientSource: resolveClientSource()
|
|
411
469
|
});
|
|
412
470
|
_state.label = 10;
|
|
413
471
|
case 10:
|
|
@@ -1074,7 +1132,9 @@ program.command('login').description('Authenticate with display.dev').option('--
|
|
|
1074
1132
|
client = new ApiClient({
|
|
1075
1133
|
baseUrl: apiUrl,
|
|
1076
1134
|
apiKey: '',
|
|
1077
|
-
clientType: 'cli'
|
|
1135
|
+
clientType: 'cli',
|
|
1136
|
+
version: version,
|
|
1137
|
+
clientSource: resolveClientSource()
|
|
1078
1138
|
});
|
|
1079
1139
|
if (!(opts.apiKey !== undefined)) return [
|
|
1080
1140
|
3,
|
|
@@ -2247,6 +2307,7 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2247
2307
|
];
|
|
2248
2308
|
case 1:
|
|
2249
2309
|
auth = _state.sent();
|
|
2310
|
+
setMcpMode();
|
|
2250
2311
|
if (!auth) return [
|
|
2251
2312
|
3,
|
|
2252
2313
|
3
|
|
@@ -2254,7 +2315,8 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2254
2315
|
client = new ApiClient({
|
|
2255
2316
|
baseUrl: auth.apiUrl,
|
|
2256
2317
|
apiKey: auth.apiKey,
|
|
2257
|
-
clientType: 'mcp-stdio'
|
|
2318
|
+
clientType: 'mcp-stdio',
|
|
2319
|
+
version: version
|
|
2258
2320
|
});
|
|
2259
2321
|
return [
|
|
2260
2322
|
4,
|
|
@@ -2274,7 +2336,8 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2274
2336
|
publicClient = new ApiClient({
|
|
2275
2337
|
baseUrl: resolvePublicApiUrl(),
|
|
2276
2338
|
apiKey: '',
|
|
2277
|
-
clientType: 'mcp-stdio'
|
|
2339
|
+
clientType: 'mcp-stdio',
|
|
2340
|
+
version: version
|
|
2278
2341
|
});
|
|
2279
2342
|
return [
|
|
2280
2343
|
4,
|
|
@@ -2291,4 +2354,61 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
|
|
|
2291
2354
|
});
|
|
2292
2355
|
})();
|
|
2293
2356
|
});
|
|
2357
|
+
/**
|
|
2358
|
+
* Register `--client-source <name>` on `program` AND every command in
|
|
2359
|
+
* the tree (top-level + nested under `comment` / `thread` + `mcp`).
|
|
2360
|
+
* This makes the flag parseable at any level — `dsp --client-source
|
|
2361
|
+
* foo publish ...`, `dsp publish --client-source foo ...`,
|
|
2362
|
+
* `dsp comment --client-source foo add ...`, etc. — so the skill
|
|
2363
|
+
* helpers (per spec §4.2 / §4.3 / §4.4) which exec `dsp publish
|
|
2364
|
+
* --client-source … "$@"` work without per-command duplication of
|
|
2365
|
+
* the action signature.
|
|
2366
|
+
*
|
|
2367
|
+
* `mcp` is included so the flag is silently accepted there too. The
|
|
2368
|
+
* mcp action does not call `resolveClientSource()`, so the stashed
|
|
2369
|
+
* value never reaches the spawned MCP server's `ApiClient` (spec §1
|
|
2370
|
+
* out-of-scope).
|
|
2371
|
+
*/ function addClientSourceOptionRecursive(cmd) {
|
|
2372
|
+
cmd.option('--client-source <name>', 'Distribution channel attribution (sent as X-Client-Source header for funnel analytics)');
|
|
2373
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2374
|
+
try {
|
|
2375
|
+
for(var _iterator = cmd.commands[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2376
|
+
var child = _step.value;
|
|
2377
|
+
addClientSourceOptionRecursive(child);
|
|
2378
|
+
}
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
_didIteratorError = true;
|
|
2381
|
+
_iteratorError = err;
|
|
2382
|
+
} finally{
|
|
2383
|
+
try {
|
|
2384
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2385
|
+
_iterator.return();
|
|
2386
|
+
}
|
|
2387
|
+
} finally{
|
|
2388
|
+
if (_didIteratorError) {
|
|
2389
|
+
throw _iteratorError;
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
addClientSourceOptionRecursive(program);
|
|
2395
|
+
/**
|
|
2396
|
+
* Resolve the parsed flag value before each action. Program-level
|
|
2397
|
+
* `preAction` hooks fire ahead of every subcommand action, so a single
|
|
2398
|
+
* registration is enough — the callback walks the active command up
|
|
2399
|
+
* through its parents and stops at the first command whose `opts()`
|
|
2400
|
+
* has a string-typed `clientSource` (distinguishing "not passed"
|
|
2401
|
+
* `undefined` from "explicit blank" `''`).
|
|
2402
|
+
*/ program.hook('preAction', function(_thisCommand, actionCommand) {
|
|
2403
|
+
var cursor = actionCommand;
|
|
2404
|
+
while(cursor){
|
|
2405
|
+
var val = cursor.opts().clientSource;
|
|
2406
|
+
if (typeof val === 'string') {
|
|
2407
|
+
clientSourceFlagPassed = true;
|
|
2408
|
+
clientSourceFromFlag = val.length > 0 ? val : undefined;
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
cursor = cursor.parent;
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
2294
2414
|
program.parse();
|
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
|
+
}
|