@displaydev/cli 0.21.1 → 0.23.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.
@@ -285,6 +285,22 @@ function _ts_generator(thisArg, body) {
285
285
  * HTTP client for the display.dev API.
286
286
  * Used by the stdio MCP server to proxy tool calls to the REST API.
287
287
  */ import { readLatestVersionFromResponse } from './update-notice.js';
288
+ /**
289
+ * Auto-detect actor type at ApiClient construction. Any of stdin /
290
+ * stdout / stderr being a TTY counts as interactive — handles the
291
+ * common `dsp publish < file.html` case where stdin is piped but the
292
+ * user is still at the terminal watching output. Non-TTY → `agent`,
293
+ * the assumption being that scripted/agent invocations outnumber
294
+ * humans-on-the-terminal as the agent-native model takes hold.
295
+ *
296
+ * Spec §3.2: detection happens once per `ApiClient` instance, not per
297
+ * request — process stdio doesn't change mid-run.
298
+ */ export function detectActorTypeFromTty() {
299
+ var stdinTty = Boolean(process.stdin.isTTY);
300
+ var stdoutTty = Boolean(process.stdout.isTTY);
301
+ var stderrTty = Boolean(process.stderr.isTTY);
302
+ return stdinTty || stdoutTty || stderrTty ? 'human' : 'agent';
303
+ }
288
304
  export var ApiError = /*#__PURE__*/ function(Error1) {
289
305
  "use strict";
290
306
  _inherits(ApiError, Error1);
@@ -312,26 +328,84 @@ export var ApiClient = /*#__PURE__*/ function() {
312
328
  "use strict";
313
329
  function ApiClient(config) {
314
330
  _class_call_check(this, ApiClient);
315
- var _config_clientType, _config_version, _config_clientSource;
331
+ var _config_clientType, _config_version, _config_clientSource, _config_actorType;
316
332
  _define_property(this, "baseUrl", void 0);
317
333
  _define_property(this, "apiKey", void 0);
318
334
  _define_property(this, "clientType", void 0);
319
335
  _define_property(this, "version", void 0);
320
336
  _define_property(this, "clientSource", void 0);
337
+ /**
338
+ * Final `X-Actor-Type` value. Defaults to the auto-detected value
339
+ * (TTY → `human`, non-TTY → `agent`) but `dsp mcp` clears it so
340
+ * the server's transport-based classification (`mcp` → `agent`)
341
+ * isn't layered on top of a CLI isTTY signal that's incoherent
342
+ * inside the spawned MCP server's request lifecycle.
343
+ */ _define_property(this, "actorType", void 0);
344
+ /**
345
+ * Per-request actor-name source. Resolved lazily at every fetch so
346
+ * the MCP server can plug in `() => server.server.getClientVersion()`
347
+ * without worrying about the `oninitialized` notification race —
348
+ * `getClientVersion()` is populated synchronously inside the SDK's
349
+ * `initialize` request handler, well before any subsequent tool call
350
+ * lands. Direct `dsp` subcommands set a static value via
351
+ * `setActorName(...)` from the `--actor-name` flag or
352
+ * `DISPLAYDEV_ACTOR_NAME` env.
353
+ */ _define_property(this, "actorNameProvider", null);
321
354
  this.baseUrl = config.baseUrl.replace(/\/$/, '');
322
355
  this.apiKey = config.apiKey;
323
356
  this.clientType = (_config_clientType = config.clientType) !== null && _config_clientType !== void 0 ? _config_clientType : 'mcp-stdio';
324
357
  this.version = (_config_version = config.version) !== null && _config_version !== void 0 ? _config_version : '';
325
358
  this.clientSource = (_config_clientSource = config.clientSource) !== null && _config_clientSource !== void 0 ? _config_clientSource : '';
359
+ this.actorType = (_config_actorType = config.actorType) !== null && _config_actorType !== void 0 ? _config_actorType : detectActorTypeFromTty();
360
+ if (config.actorName) {
361
+ this.setActorName(config.actorName);
362
+ }
326
363
  }
327
364
  _create_class(ApiClient, [
365
+ {
366
+ /** Clear the auto-detected `X-Actor-Type` (used by `dsp mcp`). */ key: "clearActorType",
367
+ value: function clearActorType() {
368
+ this.actorType = null;
369
+ }
370
+ },
371
+ {
372
+ /**
373
+ * Set a static `X-Actor-Name` value. Equivalent to passing a
374
+ * provider that always returns `name`. Pass `null` to clear.
375
+ * Normalization (ASCII strip + 128-char truncate) runs at wire
376
+ * time inside `resolveActorName`.
377
+ */ key: "setActorName",
378
+ value: function setActorName(name) {
379
+ if (name === null) {
380
+ this.actorNameProvider = null;
381
+ return;
382
+ }
383
+ this.actorNameProvider = function() {
384
+ return name;
385
+ };
386
+ }
387
+ },
388
+ {
389
+ /**
390
+ * Set a callback resolved on every outgoing request. Used by the
391
+ * stdio MCP server to forward `server.server.getClientVersion()`
392
+ * lazily — the cached value lands during the SDK's `initialize`
393
+ * request handler (synchronously), so by the time the next tool
394
+ * call lands, the provider returns the correct
395
+ * `${name}@${version}` regardless of when `oninitialized` fires.
396
+ */ key: "setActorNameProvider",
397
+ value: function setActorNameProvider(provider) {
398
+ this.actorNameProvider = provider;
399
+ }
400
+ },
328
401
  {
329
402
  key: "clientHeaders",
330
403
  value: /**
331
404
  * Build the request-side header set. Always carries `X-Client-Type`;
332
405
  * adds `X-Client-Version` when the client was constructed with one;
333
406
  * adds `X-Client-Source` when a distribution-channel attribution was
334
- * configured.
407
+ * configured; adds `X-Actor-Name` when a provider returns a non-empty
408
+ * normalized value.
335
409
  */ function clientHeaders(extra) {
336
410
  var headers = _object_spread({
337
411
  'X-Client-Type': this.clientType
@@ -339,10 +413,39 @@ export var ApiClient = /*#__PURE__*/ function() {
339
413
  'X-Client-Version': this.version
340
414
  } : {}, this.clientSource ? {
341
415
  'X-Client-Source': this.clientSource
416
+ } : {}, this.actorType ? {
417
+ 'X-Actor-Type': this.actorType
342
418
  } : {}, extra);
419
+ var actor = this.resolveActorName();
420
+ if (actor) {
421
+ headers['X-Actor-Name'] = actor;
422
+ }
343
423
  return headers;
344
424
  }
345
425
  },
426
+ {
427
+ key: "resolveActorName",
428
+ value: /**
429
+ * Resolve and normalize the current actor name. Returns `null` when
430
+ * no provider is set or the provider returns an empty string after
431
+ * sanitization. We strip non-ASCII and truncate to 128 chars as a
432
+ * defensive cleanup; the API re-normalizes (including comma-split
433
+ * + trim) in `normalizeActorName`, so the persisted value is
434
+ * authoritative — these client-side passes just keep the wire
435
+ * frame within bounds.
436
+ */ function resolveActorName() {
437
+ if (!this.actorNameProvider) {
438
+ return null;
439
+ }
440
+ var raw = this.actorNameProvider();
441
+ if (!raw) {
442
+ return null;
443
+ }
444
+ var cleaned = raw// eslint-disable-next-line no-control-regex
445
+ .replace(/[^\x20-\x7e]/g, '').slice(0, 128);
446
+ return cleaned.length > 0 ? cleaned : null;
447
+ }
448
+ },
346
449
  {
347
450
  key: "observeResponse",
348
451
  value: /**
package/dist/main.js CHANGED
@@ -310,13 +310,58 @@ function resolveClientSource() {
310
310
  }
311
311
  return undefined;
312
312
  }
313
+ /**
314
+ * User-grade override for the CLI's auto-detected isTTY classification
315
+ * — needed when a human pipes output (auto-detected as `agent`) or a
316
+ * custom script flips an interactive shell to `agent`. Server enforces
317
+ * the `{human, agent}` closed range; unknown values trip a clear error
318
+ * here so the user gets feedback instead of silent fallthrough. An
319
+ * explicit blank (`--actor-type=`) resets to "no override".
320
+ */ var actorFromFlag;
321
+ var actorFlagPassed = false;
322
+ function parseActorOrExit(raw, source) {
323
+ var lower = raw.trim().toLowerCase();
324
+ if (lower === 'human' || lower === 'agent') {
325
+ return lower;
326
+ }
327
+ console.error("Invalid ".concat(source, ": ").concat(raw, ". Use human or agent."));
328
+ process.exit(1);
329
+ }
330
+ function resolveActorOverride() {
331
+ if (actorFlagPassed) {
332
+ return actorFromFlag;
333
+ }
334
+ var env = process.env.DISPLAYDEV_ACTOR_TYPE;
335
+ if (typeof env === 'string' && env.length > 0) {
336
+ return parseActorOrExit(env, 'DISPLAYDEV_ACTOR_TYPE');
337
+ }
338
+ return undefined;
339
+ }
340
+ /**
341
+ * Independent of `--actor-type` — any actor type can carry a name
342
+ * (e.g. a script-mode label for a human, the key-name proxy for an
343
+ * org-key write).
344
+ */ var actorNameFromFlag;
345
+ var actorNameFlagPassed = false;
346
+ function resolveActorName() {
347
+ if (actorNameFlagPassed) {
348
+ return actorNameFromFlag;
349
+ }
350
+ var env = process.env.DISPLAYDEV_ACTOR_NAME;
351
+ if (typeof env === 'string' && env.length > 0) {
352
+ return env;
353
+ }
354
+ return undefined;
355
+ }
313
356
  function createClient(auth) {
314
357
  return new ApiClient({
315
358
  baseUrl: auth.apiUrl,
316
359
  apiKey: auth.apiKey,
317
360
  clientType: 'cli',
318
361
  version: version,
319
- clientSource: resolveClientSource()
362
+ clientSource: resolveClientSource(),
363
+ actorType: resolveActorOverride(),
364
+ actorName: resolveActorName()
320
365
  });
321
366
  }
322
367
  var program = new Command().name('dsp').description('display.dev CLI — publish artifacts behind company auth').version(version);
@@ -465,7 +510,9 @@ program.command('publish <path>').description('Publish an HTML or Markdown file.
465
510
  apiKey: '',
466
511
  clientType: 'cli',
467
512
  version: version,
468
- clientSource: resolveClientSource()
513
+ clientSource: resolveClientSource(),
514
+ actorType: resolveActorOverride(),
515
+ actorName: resolveActorName()
469
516
  });
470
517
  _state.label = 10;
471
518
  case 10:
@@ -2391,7 +2438,40 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
2391
2438
  }
2392
2439
  }
2393
2440
  }
2441
+ /**
2442
+ * Actor flags walk the command tree alongside `--client-source` so
2443
+ * every direct-HTTP subcommand can override the auto-detected isTTY
2444
+ * classification or stamp a display name. The flags are also
2445
+ * registered on `mcp` so `dsp mcp --actor-name foo` is silently
2446
+ * accepted; the mcp action builds its `ApiClient` without reading the
2447
+ * stash AND clears `actorType` afterwards so the spawned MCP server's
2448
+ * actor model stays clean.
2449
+ */ function addActorOptionsRecursive(cmd) {
2450
+ cmd.option('--actor-type <type>', 'Actor classification: human or agent. Overrides the auto-detected isTTY classification.');
2451
+ cmd.option('--actor-name <name>', 'Display name stamped on X-Actor-Name (≤128 chars, ASCII; e.g. ci-deploy-bot, manual-cleanup-batch-3).');
2452
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2453
+ try {
2454
+ for(var _iterator = cmd.commands[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2455
+ var child = _step.value;
2456
+ addActorOptionsRecursive(child);
2457
+ }
2458
+ } catch (err) {
2459
+ _didIteratorError = true;
2460
+ _iteratorError = err;
2461
+ } finally{
2462
+ try {
2463
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2464
+ _iterator.return();
2465
+ }
2466
+ } finally{
2467
+ if (_didIteratorError) {
2468
+ throw _iteratorError;
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2394
2473
  addClientSourceOptionRecursive(program);
2474
+ addActorOptionsRecursive(program);
2395
2475
  /**
2396
2476
  * Resolve the parsed flag value before each action. Program-level
2397
2477
  * `preAction` hooks fire ahead of every subcommand action, so a single
@@ -2402,11 +2482,18 @@ addClientSourceOptionRecursive(program);
2402
2482
  */ program.hook('preAction', function(_thisCommand, actionCommand) {
2403
2483
  var cursor = actionCommand;
2404
2484
  while(cursor){
2405
- var val = cursor.opts().clientSource;
2406
- if (typeof val === 'string') {
2485
+ var opts = cursor.opts();
2486
+ if (!clientSourceFlagPassed && typeof opts.clientSource === 'string') {
2407
2487
  clientSourceFlagPassed = true;
2408
- clientSourceFromFlag = val.length > 0 ? val : undefined;
2409
- return;
2488
+ clientSourceFromFlag = opts.clientSource.length > 0 ? opts.clientSource : undefined;
2489
+ }
2490
+ if (!actorFlagPassed && typeof opts.actorType === 'string') {
2491
+ actorFlagPassed = true;
2492
+ actorFromFlag = opts.actorType.length > 0 ? parseActorOrExit(opts.actorType, '--actor-type') : undefined;
2493
+ }
2494
+ if (!actorNameFlagPassed && typeof opts.actorName === 'string') {
2495
+ actorNameFlagPassed = true;
2496
+ actorNameFromFlag = opts.actorName.length > 0 ? opts.actorName : undefined;
2410
2497
  }
2411
2498
  cursor = cursor.parent;
2412
2499
  }
@@ -279,6 +279,29 @@ export function startMcpServer(_0) {
279
279
  }, {
280
280
  instructions: mode === 'public' ? PUBLIC_INSTRUCTIONS : AUTHENTICATED_INSTRUCTIONS
281
281
  });
282
+ // The spawned MCP server is its own actor model: transport=`mcp` +
283
+ // `clientInfo`-derived `actorName`. Layering the CLI's auto-
284
+ // detected `X-Actor-Type` on top of MCP requests would be incoherent
285
+ // (the `dsp mcp` host's TTY shape says nothing about the agent
286
+ // talking through the MCP server), so clear it here — server-side
287
+ // resolveActor classifies MCP transport as `agent` via rule 6.
288
+ apiClient.clearActorType();
289
+ // Plug the SDK's cached `clientInfo` into the apiClient on every
290
+ // outgoing HTTP request. The MCP protocol populates the cache
291
+ // synchronously inside the `initialize` request handler, which
292
+ // completes before any subsequent tool call lands — using a lazy
293
+ // provider (rather than the `oninitialized` notification handler)
294
+ // sidesteps the notification-dispatch race where a fast client
295
+ // pipelines tool calls right after `notifications/initialized`.
296
+ // Server-side construction per spec §3.3 — the wire value is built
297
+ // from SDK-cached fields, never from a client-supplied header.
298
+ apiClient.setActorNameProvider(function() {
299
+ var info = server.server.getClientVersion();
300
+ if (!info || !info.name) {
301
+ return null;
302
+ }
303
+ return info.version ? "".concat(info.name, "@").concat(info.version) : info.name;
304
+ });
282
305
  if (mode === 'public') {
283
306
  registerPublicTools(server, apiClient);
284
307
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displaydev/cli",
3
- "version": "0.21.1",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dsp": "dist/main.js"