@displaydev/cli 0.6.0 → 0.8.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/main.js CHANGED
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ function _array_like_to_array(arr, len) {
3
+ if (len == null || len > arr.length) len = arr.length;
4
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
5
+ return arr2;
6
+ }
7
+ function _array_without_holes(arr) {
8
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
9
+ }
2
10
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
3
11
  try {
4
12
  var info = gen[key](arg);
@@ -36,6 +44,23 @@ function _instanceof(left, right) {
36
44
  return left instanceof right;
37
45
  }
38
46
  }
47
+ function _iterable_to_array(iter) {
48
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
49
+ }
50
+ function _non_iterable_spread() {
51
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
52
+ }
53
+ function _to_consumable_array(arr) {
54
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
55
+ }
56
+ function _unsupported_iterable_to_array(o, minLen) {
57
+ if (!o) return;
58
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
59
+ var n = Object.prototype.toString.call(o).slice(8, -1);
60
+ if (n === "Object" && o.constructor) n = o.constructor.name;
61
+ if (n === "Map" || n === "Set") return Array.from(n);
62
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
63
+ }
39
64
  function _ts_generator(thisArg, body) {
40
65
  var f, y, t, _ = {
41
66
  label: 0,
@@ -135,91 +160,38 @@ function _ts_generator(thisArg, body) {
135
160
  };
136
161
  }
137
162
  }
138
- import { readFile, mkdir, writeFile, rename, chmod } from 'node:fs/promises';
163
+ import { readFile } from 'node:fs/promises';
139
164
  import { createInterface } from 'node:readline/promises';
140
165
  import { createRequire } from 'node:module';
141
166
  import { execFileSync } from 'node:child_process';
142
- import { homedir, platform } from 'node:os';
143
- import { join, extname } from 'node:path';
167
+ import { platform } from 'node:os';
144
168
  import { Command } from 'commander';
145
- import { ApiClient } from './api-client.js';
169
+ import { ApiClient, ApiError } from './api-client.js';
170
+ import { loadConfig, saveConfig } from './config.js';
146
171
  import { startMcpServer } from './mcp-server.js';
172
+ import { DEFAULT_API_URL, DeviceCodeDeniedError, DeviceCodeExpiredError, DeviceCodeFailedError, InvalidFlagError, PublishArgsError, classifyBrandingError, parseShortIdAndVersion, parseShowBrandingFlag, pollDeviceToken, readApiKeyFromTty, readStreamToString, resolveAuth as resolveAuthFromEnvAndConfig, validatePublishArgs } from './main-helpers.js';
147
173
  var require = createRequire(import.meta.url);
148
174
  var version = require('../package.json').version;
149
- var DEFAULT_API_URL = 'https://api.display.dev';
150
- var CONFIG_DIR = join(homedir(), '.displaydev');
151
- var CONFIG_PATH = join(CONFIG_DIR, 'config.json');
152
- function loadConfig() {
153
- return _async_to_generator(function() {
154
- var raw, unused;
155
- return _ts_generator(this, function(_state) {
156
- switch(_state.label){
157
- case 0:
158
- _state.trys.push([
159
- 0,
160
- 2,
161
- ,
162
- 3
163
- ]);
164
- return [
165
- 4,
166
- readFile(CONFIG_PATH, 'utf-8')
167
- ];
168
- case 1:
169
- raw = _state.sent();
170
- return [
171
- 2,
172
- JSON.parse(raw)
173
- ];
174
- case 2:
175
- unused = _state.sent();
176
- return [
177
- 2,
178
- null
179
- ];
180
- case 3:
181
- return [
182
- 2
183
- ];
184
- }
185
- });
186
- })();
187
- }
188
- function saveConfig(config) {
175
+ function resolveAuthOrConfig() {
189
176
  return _async_to_generator(function() {
190
- var tmpPath;
177
+ var auth;
191
178
  return _ts_generator(this, function(_state) {
192
179
  switch(_state.label){
193
180
  case 0:
194
181
  return [
195
182
  4,
196
- mkdir(CONFIG_DIR, {
197
- recursive: true
198
- })
183
+ resolveAuthOrConfigOptional()
199
184
  ];
200
185
  case 1:
201
- _state.sent();
202
- tmpPath = "".concat(CONFIG_PATH, ".tmp");
203
- return [
204
- 4,
205
- writeFile(tmpPath, "".concat(JSON.stringify(config, null, 2), "\n"), {
206
- mode: 384
207
- })
208
- ];
209
- case 2:
210
- _state.sent();
211
- return [
212
- 4,
213
- rename(tmpPath, CONFIG_PATH)
214
- ];
215
- case 3:
216
- _state.sent();
217
- return [
218
- 4,
219
- chmod(CONFIG_PATH, 384)
220
- ];
221
- case 4:
222
- _state.sent();
186
+ auth = _state.sent();
187
+ if (auth) {
188
+ return [
189
+ 2,
190
+ auth
191
+ ];
192
+ }
193
+ console.error('Not authenticated. Set DISPLAYDEV_API_KEY or run: dsp login');
194
+ process.exit(1);
223
195
  return [
224
196
  2
225
197
  ];
@@ -227,55 +199,34 @@ function saveConfig(config) {
227
199
  });
228
200
  })();
229
201
  }
230
- function resolveAuth() {
231
- var apiKey = process.env.DISPLAYDEV_API_KEY;
232
- if (apiKey) {
233
- var _process_env_DISPLAYDEV_API_URL;
234
- return {
235
- apiKey: apiKey,
236
- apiUrl: (_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
237
- };
238
- }
239
- return null;
240
- }
241
- function resolveAuthOrConfig() {
202
+ /**
203
+ * Like `resolveAuthOrConfig()` but returns null instead of exiting when
204
+ * no credential is present. Used by flows that have a fallback (public
205
+ * publish) or a different behaviour without auth (MCP public-only tools).
206
+ */ function resolveAuthOrConfigOptional() {
242
207
  return _async_to_generator(function() {
243
- var env, config, _config_apiUrl;
208
+ var config;
244
209
  return _ts_generator(this, function(_state) {
245
210
  switch(_state.label){
246
211
  case 0:
247
- env = resolveAuth();
248
- if (env) {
249
- return [
250
- 2,
251
- env
252
- ];
253
- }
254
212
  return [
255
213
  4,
256
214
  loadConfig()
257
215
  ];
258
216
  case 1:
259
217
  config = _state.sent();
260
- if (config === null || config === void 0 ? void 0 : config.token) {
261
- ;
262
- return [
263
- 2,
264
- {
265
- apiKey: config.token,
266
- apiUrl: (_config_apiUrl = config.apiUrl) !== null && _config_apiUrl !== void 0 ? _config_apiUrl : DEFAULT_API_URL
267
- }
268
- ];
269
- }
270
- console.error('Not authenticated. Set DISPLAYDEV_API_KEY or run: dsp login');
271
- process.exit(1);
272
218
  return [
273
- 2
219
+ 2,
220
+ resolveAuthFromEnvAndConfig(process.env, config)
274
221
  ];
275
222
  }
276
223
  });
277
224
  })();
278
225
  }
226
+ function resolvePublicApiUrl() {
227
+ var _process_env_DISPLAYDEV_API_URL;
228
+ 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;
229
+ }
279
230
  function createClient(auth) {
280
231
  return new ApiClient({
281
232
  baseUrl: auth.apiUrl,
@@ -287,82 +238,225 @@ var program = new Command().name('dsp').description('display.dev CLI — publish
287
238
  function collectEmails(value, prev) {
288
239
  return prev.concat(value);
289
240
  }
290
- var SHOW_BRANDING_VALUES = new Set([
291
- 'show',
292
- 'hide',
293
- 'inherit'
294
- ]);
295
- function parseShowBrandingFlag(raw) {
296
- if (!raw) {
297
- return undefined;
298
- }
299
- var normalized = raw.toLowerCase();
300
- if (!SHOW_BRANDING_VALUES.has(normalized)) {
301
- console.error("Invalid --show-branding: ".concat(raw, ". Use show, hide, or inherit."));
302
- process.exit(1);
241
+ function parseShowBrandingOrExit(raw) {
242
+ try {
243
+ return parseShowBrandingFlag(raw);
244
+ } catch (err) {
245
+ if (_instanceof(err, InvalidFlagError)) {
246
+ console.error("Invalid ".concat(err.flag, ": ").concat(err.value, ". Use show, hide, or inherit."));
247
+ process.exit(1);
248
+ }
249
+ throw err;
303
250
  }
304
- return normalized;
305
251
  }
306
252
  // --- publish ---
307
- program.command('publish <path>').description('Publish an HTML or Markdown file').option('--name <name>', 'Artifact display name').option('--id <shortId>', 'Update existing artifact (new version)').option('--public', 'Make artifact publicly accessible').option('--company', 'Restrict artifact to company auth (default for new artifacts)').option('--share <email>', 'Share with guest email (repeatable)', collectEmails, []).option('--clear-shares', 'Remove all guest email shares (update only)').option('--theme <theme>', 'Markdown theme (default: github)').option('--show-branding <mode>', 'display.dev bar override: show, hide, or inherit (paid plans only)').action(function(filePath, opts) {
253
+ program.command('publish <path>').description('Publish an HTML or Markdown file. Use "-" to read from stdin (requires --id).').option('--name <name>', 'Artifact display name').option('--id <shortId>', 'Update existing artifact (new version)').option('--public', 'Make artifact publicly accessible (alias for --visibility public)').option('--company', 'Restrict artifact to company auth (alias for --visibility company)').option('--visibility <level>', 'public | company | private. "private" requires the Teams plan.').option('--share <email>', 'Add an email to sharedWith (repeatable; alias for --share-with)', collectEmails, []).option('--share-with <emails>', 'Comma-separated email list for sharedWith', function(val) {
254
+ return val.split(',').map(function(e) {
255
+ return e.trim();
256
+ }).filter(Boolean);
257
+ }).option('--clear-shares', 'Remove all sharedWith entries (update only)').option('--theme <theme>', 'Markdown theme (default: github)').option('--show-branding <mode>', 'display.dev bar override: show, hide, or inherit (paid plans only)').action(function(filePath, opts) {
308
258
  return _async_to_generator(function() {
309
- var showBranding, auth, client, ext, content, format, visibility, share, result, err, msg, verb;
259
+ var _opts_shareWith, showBranding, auth, fromStdin, format, content, inferClient, detail, err, msg, publicClient, publicResult, err1, msg1, client, visibility, mergedShare, share, result, err2, msg2, verb;
310
260
  return _ts_generator(this, function(_state) {
311
261
  switch(_state.label){
312
262
  case 0:
313
- if (!opts.id && !opts.name) {
314
- console.error('--name is required for new artifacts. Use --id to update an existing one.');
315
- process.exit(1);
263
+ showBranding = parseShowBrandingOrExit(opts.showBranding);
264
+ return [
265
+ 4,
266
+ resolveAuthOrConfigOptional()
267
+ ];
268
+ case 1:
269
+ auth = _state.sent();
270
+ fromStdin = filePath === '-';
271
+ if (fromStdin) {
272
+ // stdin path is for re-publishing existing artifacts (`dsp export | dsp
273
+ // publish --id <id> -`). Path-specific gates run first so the user gets
274
+ // a clear error before we touch the network or stdin.
275
+ if (!auth) {
276
+ console.error('Reading from stdin (-) requires auth. Run: dsp login');
277
+ process.exit(1);
278
+ }
279
+ if (!opts.id) {
280
+ console.error('Reading from stdin (-) requires --id <shortId>.');
281
+ process.exit(2);
282
+ }
283
+ if (process.stdin.isTTY) {
284
+ console.error('cannot read source from a TTY; pipe a file or use a path');
285
+ process.exit(2);
286
+ }
316
287
  }
317
- if (opts.public && opts.company) {
318
- console.error('Use --public or --company, not both.');
319
- process.exit(1);
288
+ try {
289
+ validatePublishArgs({
290
+ name: opts.name,
291
+ id: opts.id,
292
+ public: opts.public,
293
+ company: opts.company,
294
+ visibility: opts.visibility,
295
+ share: opts.share,
296
+ shareWith: opts.shareWith,
297
+ clearShares: opts.clearShares,
298
+ showBranding: opts.showBranding,
299
+ filePath: filePath,
300
+ authenticated: auth !== null,
301
+ fromStdin: fromStdin
302
+ });
303
+ } catch (err) {
304
+ if (_instanceof(err, PublishArgsError)) {
305
+ console.error(err.message);
306
+ process.exit(1);
307
+ }
308
+ throw err;
320
309
  }
321
- if (opts.clearShares && opts.share.length > 0) {
322
- console.error('Use --share or --clear-shares, not both.');
310
+ if (!fromStdin) return [
311
+ 3,
312
+ 7
313
+ ];
314
+ // The artifact's format is fixed at create — infer it via GET so the
315
+ // round-trip preserves it without making the caller pass --format.
316
+ inferClient = createClient(auth);
317
+ _state.label = 2;
318
+ case 2:
319
+ _state.trys.push([
320
+ 2,
321
+ 4,
322
+ ,
323
+ 5
324
+ ]);
325
+ return [
326
+ 4,
327
+ inferClient.get(opts.id)
328
+ ];
329
+ case 3:
330
+ detail = _state.sent();
331
+ format = detail.format === 'md' ? 'md' : 'html';
332
+ return [
333
+ 3,
334
+ 5
335
+ ];
336
+ case 4:
337
+ err = _state.sent();
338
+ if (_instanceof(err, ApiError) && (err.status === 401 || err.status === 403)) {
339
+ console.error('Your credential is no longer valid. Run: dsp login (or rotate DISPLAYDEV_API_KEY).');
323
340
  process.exit(1);
324
341
  }
325
- if (opts.clearShares && !opts.id) {
326
- console.error('--clear-shares only applies when updating (--id).');
327
- process.exit(1);
342
+ if (_instanceof(err, ApiError) && err.status === 404) {
343
+ console.error("Artifact not found: ".concat(opts.id));
344
+ process.exit(4);
328
345
  }
329
- showBranding = parseShowBrandingFlag(opts.showBranding);
346
+ msg = _instanceof(err, Error) ? err.message : String(err);
347
+ console.error(msg);
348
+ process.exit(1);
349
+ return [
350
+ 3,
351
+ 5
352
+ ];
353
+ case 5:
330
354
  return [
331
355
  4,
332
- resolveAuthOrConfig()
356
+ readStreamToString(process.stdin)
333
357
  ];
334
- case 1:
335
- auth = _state.sent();
336
- client = createClient(auth);
337
- ext = extname(filePath).toLowerCase();
338
- if (ext !== '.html' && ext !== '.md') {
339
- console.error("Unsupported file type: ".concat(ext, " (expected .html or .md)"));
340
- process.exit(1);
341
- }
358
+ case 6:
359
+ content = _state.sent();
360
+ return [
361
+ 3,
362
+ 9
363
+ ];
364
+ case 7:
365
+ format = filePath.toLowerCase().endsWith('.md') ? 'md' : 'html';
342
366
  return [
343
367
  4,
344
368
  readFile(filePath, 'utf-8')
345
369
  ];
346
- case 2:
370
+ case 8:
347
371
  content = _state.sent();
348
- format = ext === '.md' ? 'md' : 'html';
349
- if (opts.public) {
372
+ _state.label = 9;
373
+ case 9:
374
+ if (!!auth) return [
375
+ 3,
376
+ 14
377
+ ];
378
+ publicClient = new ApiClient({
379
+ baseUrl: resolvePublicApiUrl(),
380
+ apiKey: '',
381
+ clientType: 'cli'
382
+ });
383
+ _state.label = 10;
384
+ case 10:
385
+ _state.trys.push([
386
+ 10,
387
+ 12,
388
+ ,
389
+ 13
390
+ ]);
391
+ return [
392
+ 4,
393
+ publicClient.publishPublic({
394
+ content: content,
395
+ name: opts.name,
396
+ format: format
397
+ })
398
+ ];
399
+ case 11:
400
+ publicResult = _state.sent();
401
+ return [
402
+ 3,
403
+ 13
404
+ ];
405
+ case 12:
406
+ err1 = _state.sent();
407
+ msg1 = _instanceof(err1, Error) ? err1.message : String(err1);
408
+ console.error(msg1);
409
+ process.exit(1);
410
+ return [
411
+ 3,
412
+ 13
413
+ ];
414
+ case 13:
415
+ // Full response as JSON on stdout so `dsp publish … | jq -r .previewUrl`
416
+ // works alongside the human-readable block on stderr.
417
+ console.log(JSON.stringify(publicResult));
418
+ process.stderr.write([
419
+ '',
420
+ "Published anonymously — nobody owns this artifact yet.",
421
+ " Preview: ".concat(publicResult.previewUrl),
422
+ " Claim: ".concat(publicResult.claimUrl),
423
+ " Expires: ".concat(publicResult.expiresAt),
424
+ "Sign in at the claim URL to move it into your display.dev organization.",
425
+ ''
426
+ ].join('\n'));
427
+ return [
428
+ 2
429
+ ];
430
+ case 14:
431
+ // --- Authenticated path — `--id` is update, otherwise create. ---
432
+ client = createClient(auth);
433
+ if (opts.visibility) {
434
+ if (opts.visibility !== 'public' && opts.visibility !== 'company' && opts.visibility !== 'private') {
435
+ console.error("Invalid --visibility: ".concat(opts.visibility, ". Use public, company, or private."));
436
+ process.exit(1);
437
+ }
438
+ visibility = opts.visibility;
439
+ } else if (opts.public) {
350
440
  visibility = 'public';
351
441
  } else if (opts.company) {
352
442
  visibility = 'company';
353
443
  }
354
- share = opts.share.length > 0 ? opts.share : undefined;
355
- _state.label = 3;
356
- case 3:
444
+ // --share-with (csv) and --share (repeatable) both contribute to the
445
+ // sharedWith list. Concatenating rather than making them mutually
446
+ // exclusive keeps CLI composition simple for scripts that want to mix.
447
+ mergedShare = _to_consumable_array((_opts_shareWith = opts.shareWith) !== null && _opts_shareWith !== void 0 ? _opts_shareWith : []).concat(_to_consumable_array(opts.share));
448
+ share = mergedShare.length > 0 ? mergedShare : undefined;
449
+ _state.label = 15;
450
+ case 15:
357
451
  _state.trys.push([
358
- 3,
359
- 8,
452
+ 15,
453
+ 20,
360
454
  ,
361
- 9
455
+ 21
362
456
  ]);
363
457
  if (!opts.id) return [
364
458
  3,
365
- 5
459
+ 17
366
460
  ];
367
461
  return [
368
462
  4,
@@ -377,50 +471,205 @@ program.command('publish <path>').description('Publish an HTML or Markdown file'
377
471
  showBranding: showBranding
378
472
  })
379
473
  ];
380
- case 4:
474
+ case 16:
381
475
  result = _state.sent();
382
476
  return [
383
477
  3,
384
- 7
478
+ 19
479
+ ];
480
+ case 17:
481
+ return [
482
+ 4,
483
+ client.publish({
484
+ content: content,
485
+ name: opts.name,
486
+ format: format,
487
+ theme: opts.theme,
488
+ share: share,
489
+ visibility: visibility,
490
+ showBranding: showBranding
491
+ })
492
+ ];
493
+ case 18:
494
+ result = _state.sent();
495
+ _state.label = 19;
496
+ case 19:
497
+ return [
498
+ 3,
499
+ 21
500
+ ];
501
+ case 20:
502
+ err2 = _state.sent();
503
+ // Fail closed on an expired / revoked credential: do NOT fall through
504
+ // to the public endpoint, since the user intended a workspace publish.
505
+ if (_instanceof(err2, ApiError) && (err2.status === 401 || err2.status === 403)) {
506
+ console.error('Your credential is no longer valid. Run: dsp login (or rotate DISPLAYDEV_API_KEY).');
507
+ process.exit(1);
508
+ }
509
+ msg2 = _instanceof(err2, Error) ? err2.message : String(err2);
510
+ if (classifyBrandingError(msg2) === 'paid-plan') {
511
+ console.error('Error: Hiding display.dev branding requires a paid plan. See https://display.dev/billing');
512
+ } else {
513
+ console.error(msg2);
514
+ }
515
+ process.exit(1);
516
+ return [
517
+ 3,
518
+ 21
519
+ ];
520
+ case 21:
521
+ console.log(result.url);
522
+ verb = opts.id ? 'Updated' : 'Published';
523
+ console.log("".concat(verb, " ").concat(result.name, " (").concat(result.shortId, ") v").concat(result.version));
524
+ return [
525
+ 2
526
+ ];
527
+ }
528
+ });
529
+ })();
530
+ });
531
+ // --- share ---
532
+ program.command('share <shortId>').description('Change an artifact\'s visibility and/or add/remove shared-with emails.').option('--visibility <level>', 'public | company | private. "private" requires the Teams plan.').option('--add-users <emails>', 'Comma-separated emails to add to sharedWith', function(val) {
533
+ return val.split(',').map(function(e) {
534
+ return e.trim();
535
+ }).filter(Boolean);
536
+ }).option('--remove-users <emails>', 'Comma-separated emails to remove from sharedWith', function(val) {
537
+ return val.split(',').map(function(e) {
538
+ return e.trim();
539
+ }).filter(Boolean);
540
+ }).action(function(shortId, opts) {
541
+ return _async_to_generator(function() {
542
+ var auth, client, result, err, msg;
543
+ return _ts_generator(this, function(_state) {
544
+ switch(_state.label){
545
+ case 0:
546
+ if (opts.visibility === undefined && (!opts.addUsers || opts.addUsers.length === 0) && (!opts.removeUsers || opts.removeUsers.length === 0)) {
547
+ console.error('Provide at least one of --visibility, --add-users, --remove-users.');
548
+ process.exit(1);
549
+ }
550
+ if (opts.visibility !== undefined && opts.visibility !== 'public' && opts.visibility !== 'company' && opts.visibility !== 'private') {
551
+ console.error("Invalid --visibility: ".concat(opts.visibility, ". Use public, company, or private."));
552
+ process.exit(1);
553
+ }
554
+ return [
555
+ 4,
556
+ resolveAuthOrConfig()
557
+ ];
558
+ case 1:
559
+ auth = _state.sent();
560
+ client = createClient(auth);
561
+ _state.label = 2;
562
+ case 2:
563
+ _state.trys.push([
564
+ 2,
565
+ 4,
566
+ ,
567
+ 5
568
+ ]);
569
+ return [
570
+ 4,
571
+ client.share(shortId, {
572
+ visibility: opts.visibility,
573
+ addUsers: opts.addUsers,
574
+ removeUsers: opts.removeUsers
575
+ })
576
+ ];
577
+ case 3:
578
+ result = _state.sent();
579
+ console.log(JSON.stringify(result, null, 2));
580
+ return [
581
+ 3,
582
+ 5
583
+ ];
584
+ case 4:
585
+ err = _state.sent();
586
+ if (_instanceof(err, ApiError) && err.status === 402) {
587
+ console.error('Error: Private visibility requires the Teams plan. Upgrade at https://display.dev/pricing');
588
+ process.exit(1);
589
+ }
590
+ if (_instanceof(err, ApiError) && err.status === 404) {
591
+ console.error("Artifact not found: ".concat(shortId));
592
+ process.exit(4);
593
+ }
594
+ if (_instanceof(err, ApiError) && err.status === 401) {
595
+ // 401 is always "credential expired / revoked" — sessions + api keys
596
+ // both return it on an invalid secret. 403 is authorization ("you're
597
+ // signed in but not allowed"), which we surface with the API's own
598
+ // message below.
599
+ console.error('Your credential is no longer valid. Run: dsp login (or rotate DISPLAYDEV_API_KEY).');
600
+ process.exit(1);
601
+ }
602
+ if (_instanceof(err, ApiError) && (err.status === 400 || err.status === 403)) {
603
+ console.error(err.message);
604
+ process.exit(1);
605
+ }
606
+ msg = _instanceof(err, Error) ? err.message : String(err);
607
+ console.error(msg);
608
+ process.exit(1);
609
+ return [
610
+ 3,
611
+ 5
385
612
  ];
386
613
  case 5:
614
+ return [
615
+ 2
616
+ ];
617
+ }
618
+ });
619
+ })();
620
+ });
621
+ // --- rename ---
622
+ program.command('rename <shortId>').description('Rename a published artifact. Slug re-derives from the new name; old URLs still resolve via shortId.').requiredOption('--name <name>', 'New display name').action(function(shortId, opts) {
623
+ return _async_to_generator(function() {
624
+ var auth, client, result, err, msg;
625
+ return _ts_generator(this, function(_state) {
626
+ switch(_state.label){
627
+ case 0:
628
+ return [
629
+ 4,
630
+ resolveAuthOrConfig()
631
+ ];
632
+ case 1:
633
+ auth = _state.sent();
634
+ client = createClient(auth);
635
+ _state.label = 2;
636
+ case 2:
637
+ _state.trys.push([
638
+ 2,
639
+ 4,
640
+ ,
641
+ 5
642
+ ]);
387
643
  return [
388
644
  4,
389
- client.publish({
390
- content: content,
391
- name: opts.name,
392
- format: format,
393
- theme: opts.theme,
394
- share: share,
395
- visibility: visibility,
396
- showBranding: showBranding
397
- })
645
+ client.renameArtifact(shortId, opts.name)
398
646
  ];
399
- case 6:
647
+ case 3:
400
648
  result = _state.sent();
401
- _state.label = 7;
402
- case 7:
649
+ console.log(result.url);
650
+ console.log("Renamed to ".concat(result.name, " (").concat(result.shortId, ")"));
403
651
  return [
404
652
  3,
405
- 9
653
+ 5
406
654
  ];
407
- case 8:
655
+ case 4:
408
656
  err = _state.sent();
409
- msg = _instanceof(err, Error) ? err.message : String(err);
410
- if (/paid plan|upgrade/i.test(msg)) {
411
- console.error('Error: Hiding display.dev branding requires a paid plan. See https://display.dev/billing');
412
- } else {
413
- console.error(msg);
657
+ if (_instanceof(err, ApiError) && err.status === 404) {
658
+ console.error("Artifact not found: ".concat(shortId));
659
+ process.exit(4);
660
+ }
661
+ if (_instanceof(err, ApiError) && err.status === 401) {
662
+ console.error('Your credential is no longer valid. Run: dsp login (or rotate DISPLAYDEV_API_KEY).');
663
+ process.exit(1);
414
664
  }
665
+ msg = _instanceof(err, Error) ? err.message : String(err);
666
+ console.error(msg);
415
667
  process.exit(1);
416
668
  return [
417
669
  3,
418
- 9
670
+ 5
419
671
  ];
420
- case 9:
421
- console.log(result.url);
422
- verb = opts.id ? 'Updated' : 'Published';
423
- console.log("".concat(verb, " ").concat(result.name, " (").concat(result.shortId, ") v").concat(result.version));
672
+ case 5:
424
673
  return [
425
674
  2
426
675
  ];
@@ -531,6 +780,76 @@ program.command('get <shortId>').description('Get artifact details').option('--i
531
780
  });
532
781
  })();
533
782
  });
783
+ // --- export ---
784
+ program.command('export <shortId>').description('Print the published source bytes to stdout. Pin a version with @<n>.').action(function(shortIdArg) {
785
+ return _async_to_generator(function() {
786
+ var parsed, auth, client, result, err, msg;
787
+ return _ts_generator(this, function(_state) {
788
+ switch(_state.label){
789
+ case 0:
790
+ parsed = parseShortIdAndVersion(shortIdArg);
791
+ if (!parsed) {
792
+ console.error('Invalid argument. Expected <shortId> or <shortId>@<version> (version must be a positive integer).');
793
+ process.exit(2);
794
+ }
795
+ return [
796
+ 4,
797
+ resolveAuthOrConfig()
798
+ ];
799
+ case 1:
800
+ auth = _state.sent();
801
+ client = createClient(auth);
802
+ _state.label = 2;
803
+ case 2:
804
+ _state.trys.push([
805
+ 2,
806
+ 4,
807
+ ,
808
+ 5
809
+ ]);
810
+ return [
811
+ 4,
812
+ client.exportSource(parsed.shortId, parsed.version)
813
+ ];
814
+ case 3:
815
+ result = _state.sent();
816
+ return [
817
+ 3,
818
+ 5
819
+ ];
820
+ case 4:
821
+ err = _state.sent();
822
+ if (_instanceof(err, ApiError)) {
823
+ if (err.status === 404) {
824
+ console.error('Artifact not found');
825
+ process.exit(4);
826
+ }
827
+ if (err.status === 403) {
828
+ console.error('Forbidden');
829
+ process.exit(4);
830
+ }
831
+ if (err.status === 401) {
832
+ console.error('Your credential is no longer valid. Run: dsp login (or rotate DISPLAYDEV_API_KEY).');
833
+ process.exit(1);
834
+ }
835
+ }
836
+ msg = _instanceof(err, Error) ? err.message : String(err);
837
+ console.error(msg);
838
+ process.exit(1);
839
+ return [
840
+ 3,
841
+ 5
842
+ ];
843
+ case 5:
844
+ // Binary-clean: write Buffer directly, no console.log (which appends \n).
845
+ process.stdout.write(result.data);
846
+ return [
847
+ 2
848
+ ];
849
+ }
850
+ });
851
+ })();
852
+ });
534
853
  // --- delete ---
535
854
  program.command('delete <shortId>').description('Delete an artifact permanently').option('--confirm', 'Confirm deletion').action(function(shortId, opts) {
536
855
  return _async_to_generator(function() {
@@ -564,6 +883,9 @@ program.command('delete <shortId>').description('Delete an artifact permanently'
564
883
  })();
565
884
  });
566
885
  function openBrowser(url) {
886
+ if (process.env.DISPLAYDEV_SKIP_BROWSER) {
887
+ return;
888
+ }
567
889
  try {
568
890
  var os = platform();
569
891
  if (os === 'darwin') {
@@ -587,15 +909,10 @@ function openBrowser(url) {
587
909
  // URL is already printed — user can open it manually
588
910
  }
589
911
  }
590
- function sleep(ms) {
591
- return new Promise(function(resolve) {
592
- return setTimeout(resolve, ms);
593
- });
594
- }
595
912
  // --- login ---
596
913
  program.command('login').description('Authenticate with display.dev').option('--email <email>', 'Email address').option('--code <code>', 'OTP code (verify step; pair with --email)').option('--api-key [key]', 'Authenticate with an API key').option('--json', 'Machine-readable output (structured status, no prose)').action(function(opts) {
597
914
  return _async_to_generator(function() {
598
- var _process_env_DISPLAYDEV_API_URL, emit, emitErr, apiUrl, client, key, rl, validation, email, rl1, method, check, unused, err, msg, code, rl2, result, unused1, deviceResult, unused2, device_code, verification_uri_complete, initialInterval, interval, tokenResult, unused3, _$err;
915
+ var _process_env_DISPLAYDEV_API_URL, emit, emitErr, apiUrl, client, key, unused, rl, validation, email, rl1, method, check, unused1, err, msg, code, rl2, result, unused2, deviceResult, unused3, device_code, verification_uri_complete, initialInterval, expires_in, token, err1;
599
916
  return _ts_generator(this, function(_state) {
600
917
  switch(_state.label){
601
918
  case 0:
@@ -623,7 +940,7 @@ program.command('login').description('Authenticate with display.dev').option('--
623
940
  });
624
941
  if (!(opts.apiKey !== undefined)) return [
625
942
  3,
626
- 8
943
+ 11
627
944
  ];
628
945
  if (!(typeof opts.apiKey === 'string')) return [
629
946
  3,
@@ -632,54 +949,46 @@ program.command('login').description('Authenticate with display.dev').option('--
632
949
  key = opts.apiKey;
633
950
  return [
634
951
  3,
635
- 5
952
+ 8
636
953
  ];
637
954
  case 1:
638
955
  if (!process.stdin.isTTY) return [
639
956
  3,
640
- 3
957
+ 6
641
958
  ];
642
- // Mask input for interactive API key entry
959
+ // Raw-mode input suppresses terminal echo.
643
960
  process.stdout.write('API key: ');
961
+ _state.label = 2;
962
+ case 2:
963
+ _state.trys.push([
964
+ 2,
965
+ 4,
966
+ ,
967
+ 5
968
+ ]);
644
969
  return [
645
970
  4,
646
- new Promise(function(resolve) {
647
- var input = '';
648
- process.stdin.setRawMode(true);
649
- process.stdin.resume();
650
- process.stdin.setEncoding('utf8');
651
- var cleanup = function cleanup() {
652
- process.stdin.setRawMode(false);
653
- process.stdin.pause();
654
- process.stdin.removeListener('data', onData);
655
- };
656
- var onData = function onData(ch) {
657
- if (ch === '\r' || ch === '\n') {
658
- cleanup();
659
- process.stdout.write('\n');
660
- resolve(input);
661
- } else if (ch === '\u0003') {
662
- cleanup();
663
- process.stdout.write('\n');
664
- process.exit(1);
665
- } else if (ch === '\u007F' || ch === '\b') {
666
- if (input.length > 0) {
667
- input = input.slice(0, -1);
668
- }
669
- } else {
670
- input += ch;
671
- }
672
- };
673
- process.stdin.on('data', onData);
674
- })
971
+ readApiKeyFromTty(process.stdin, process.stdout)
675
972
  ];
676
- case 2:
973
+ case 3:
677
974
  key = _state.sent();
678
975
  return [
679
976
  3,
680
977
  5
681
978
  ];
682
- case 3:
979
+ case 4:
980
+ unused = _state.sent();
981
+ process.exit(1);
982
+ return [
983
+ 3,
984
+ 5
985
+ ];
986
+ case 5:
987
+ return [
988
+ 3,
989
+ 8
990
+ ];
991
+ case 6:
683
992
  // Non-TTY: read from stdin (piped input)
684
993
  rl = createInterface({
685
994
  input: process.stdin,
@@ -689,11 +998,11 @@ program.command('login').description('Authenticate with display.dev').option('--
689
998
  4,
690
999
  rl.question('API key: ')
691
1000
  ];
692
- case 4:
1001
+ case 7:
693
1002
  key = _state.sent();
694
1003
  rl.close();
695
- _state.label = 5;
696
- case 5:
1004
+ _state.label = 8;
1005
+ case 8:
697
1006
  key = key.trim();
698
1007
  if (!key) {
699
1008
  console.error('API key cannot be empty.');
@@ -703,7 +1012,7 @@ program.command('login').description('Authenticate with display.dev').option('--
703
1012
  4,
704
1013
  client.validateApiKey(key)
705
1014
  ];
706
- case 6:
1015
+ case 9:
707
1016
  validation = _state.sent();
708
1017
  if (validation === 'invalid') {
709
1018
  emitErr('Invalid API key. Check the key and try again.', {
@@ -722,7 +1031,7 @@ program.command('login').description('Authenticate with display.dev').option('--
722
1031
  apiUrl: apiUrl
723
1032
  })
724
1033
  ];
725
- case 7:
1034
+ case 10:
726
1035
  _state.sent();
727
1036
  emit('Authenticated.', {
728
1037
  status: 'authenticated',
@@ -731,12 +1040,12 @@ program.command('login').description('Authenticate with display.dev').option('--
731
1040
  return [
732
1041
  2
733
1042
  ];
734
- case 8:
1043
+ case 11:
735
1044
  // --- Mode 1 & 2: Email-based login ---
736
1045
  email = opts.email;
737
1046
  if (!!email) return [
738
1047
  3,
739
- 10
1048
+ 13
740
1049
  ];
741
1050
  if (!process.stdin.isTTY) {
742
1051
  console.error('Email is required. Use --email <email> for non-interactive mode.');
@@ -750,67 +1059,67 @@ program.command('login').description('Authenticate with display.dev').option('--
750
1059
  4,
751
1060
  rl1.question('Email: ')
752
1061
  ];
753
- case 9:
1062
+ case 12:
754
1063
  email = _state.sent();
755
1064
  rl1.close();
756
- _state.label = 10;
757
- case 10:
1065
+ _state.label = 13;
1066
+ case 13:
758
1067
  email = email.trim();
759
1068
  // Check auth method
760
1069
  method = 'otp';
761
- _state.label = 11;
762
- case 11:
1070
+ _state.label = 14;
1071
+ case 14:
763
1072
  _state.trys.push([
764
- 11,
765
- 13,
1073
+ 14,
1074
+ 16,
766
1075
  ,
767
- 14
1076
+ 17
768
1077
  ]);
769
1078
  return [
770
1079
  4,
771
1080
  client.authCheck(email)
772
1081
  ];
773
- case 12:
1082
+ case 15:
774
1083
  check = _state.sent();
775
1084
  method = check.method;
776
1085
  return [
777
1086
  3,
778
- 14
1087
+ 17
779
1088
  ];
780
- case 13:
781
- unused = _state.sent();
1089
+ case 16:
1090
+ unused1 = _state.sent();
782
1091
  return [
783
1092
  3,
784
- 14
1093
+ 17
785
1094
  ];
786
- case 14:
1095
+ case 17:
787
1096
  if (!(method === 'otp')) return [
788
1097
  3,
789
- 27
1098
+ 30
790
1099
  ];
791
1100
  if (!!opts.code) return [
792
1101
  3,
793
- 19
1102
+ 22
794
1103
  ];
795
- _state.label = 15;
796
- case 15:
1104
+ _state.label = 18;
1105
+ case 18:
797
1106
  _state.trys.push([
798
- 15,
799
- 17,
1107
+ 18,
1108
+ 20,
800
1109
  ,
801
- 18
1110
+ 21
802
1111
  ]);
803
1112
  return [
804
1113
  4,
805
1114
  client.sendOtp(email)
806
1115
  ];
807
- case 16:
1116
+ case 19:
808
1117
  _state.sent();
809
1118
  return [
810
1119
  3,
811
- 18
1120
+ 21
812
1121
  ];
813
- case 17:
1122
+ case 20:
814
1123
  err = _state.sent();
815
1124
  msg = _instanceof(err, Error) ? err.message : 'Something went wrong';
816
1125
  if (msg.includes('requires SSO')) {
@@ -827,9 +1136,9 @@ program.command('login').description('Authenticate with display.dev').option('--
827
1136
  process.exit(1);
828
1137
  return [
829
1138
  3,
830
- 18
1139
+ 21
831
1140
  ];
832
- case 18:
1141
+ case 21:
833
1142
  if (!process.stdin.isTTY) {
834
1143
  // Non-interactive: exit after sending. The caller (typically an
835
1144
  // agent) will collect the OTP from the user and re-invoke with --code.
@@ -859,18 +1168,18 @@ program.command('login').description('Authenticate with display.dev').option('--
859
1168
  if (!opts.json) {
860
1169
  console.log("Code sent to ".concat(email));
861
1170
  }
862
- _state.label = 19;
863
- case 19:
1171
+ _state.label = 22;
1172
+ case 22:
864
1173
  if (!opts.code) return [
865
1174
  3,
866
- 20
1175
+ 23
867
1176
  ];
868
1177
  code = opts.code;
869
1178
  return [
870
1179
  3,
871
- 22
1180
+ 25
872
1181
  ];
873
- case 20:
1182
+ case 23:
874
1183
  rl2 = createInterface({
875
1184
  input: process.stdin,
876
1185
  output: process.stdout
@@ -879,22 +1188,22 @@ program.command('login').description('Authenticate with display.dev').option('--
879
1188
  4,
880
1189
  rl2.question('Enter the 6-digit code: ')
881
1190
  ];
882
- case 21:
1191
+ case 24:
883
1192
  code = _state.sent();
884
1193
  rl2.close();
885
- _state.label = 22;
886
- case 22:
1194
+ _state.label = 25;
1195
+ case 25:
887
1196
  _state.trys.push([
888
- 22,
889
1197
  25,
1198
+ 28,
890
1199
  ,
891
- 26
1200
+ 29
892
1201
  ]);
893
1202
  return [
894
1203
  4,
895
1204
  client.verifyOtp(email, code.trim())
896
1205
  ];
897
- case 23:
1206
+ case 26:
898
1207
  result = _state.sent();
899
1208
  return [
900
1209
  4,
@@ -903,7 +1212,7 @@ program.command('login').description('Authenticate with display.dev').option('--
903
1212
  apiUrl: apiUrl
904
1213
  })
905
1214
  ];
906
- case 24:
1215
+ case 27:
907
1216
  _state.sent();
908
1217
  emit("Logged in as ".concat(email), {
909
1218
  status: 'authenticated',
@@ -912,10 +1221,10 @@ program.command('login').description('Authenticate with display.dev').option('--
912
1221
  });
913
1222
  return [
914
1223
  3,
915
- 26
1224
+ 29
916
1225
  ];
917
- case 25:
918
- unused1 = _state.sent();
1226
+ case 28:
1227
+ unused2 = _state.sent();
919
1228
  emitErr('Invalid or expired code. Try again.', {
920
1229
  status: 'error',
921
1230
  error: 'invalid_or_expired_code'
@@ -923,142 +1232,98 @@ program.command('login').description('Authenticate with display.dev').option('--
923
1232
  process.exit(1);
924
1233
  return [
925
1234
  3,
926
- 26
1235
+ 29
927
1236
  ];
928
- case 26:
1237
+ case 29:
929
1238
  return [
930
1239
  3,
931
- 40
1240
+ 39
932
1241
  ];
933
- case 27:
1242
+ case 30:
934
1243
  // --- Mode 2: SSO device flow ---
935
1244
  console.log('Your organization requires SSO. Opening browser...');
936
- _state.label = 28;
937
- case 28:
1245
+ _state.label = 31;
1246
+ case 31:
938
1247
  _state.trys.push([
939
- 28,
940
- 30,
1248
+ 31,
1249
+ 33,
941
1250
  ,
942
- 31
1251
+ 34
943
1252
  ]);
944
1253
  return [
945
1254
  4,
946
1255
  client.requestDeviceCode('dsp-cli')
947
1256
  ];
948
- case 29:
1257
+ case 32:
949
1258
  deviceResult = _state.sent();
950
1259
  return [
951
1260
  3,
952
- 31
1261
+ 34
953
1262
  ];
954
- case 30:
955
- unused2 = _state.sent();
1263
+ case 33:
1264
+ unused3 = _state.sent();
956
1265
  console.error('Something went wrong. Check your connection and try again.');
957
1266
  process.exit(1);
958
1267
  return [
959
1268
  3,
960
- 31
1269
+ 34
961
1270
  ];
962
- case 31:
963
- device_code = deviceResult.device_code, verification_uri_complete = deviceResult.verification_uri_complete, initialInterval = deviceResult.interval;
1271
+ case 34:
1272
+ device_code = deviceResult.device_code, verification_uri_complete = deviceResult.verification_uri_complete, initialInterval = deviceResult.interval, expires_in = deviceResult.expires_in;
964
1273
  console.log();
965
1274
  console.log(" ".concat(verification_uri_complete));
966
1275
  console.log();
967
1276
  openBrowser(verification_uri_complete);
968
- // Poll for token
969
- interval = (initialInterval || 5) * 1000;
970
1277
  process.stdout.write('Waiting for authentication...');
971
- _state.label = 32;
972
- case 32:
973
- if (!true) return [
974
- 3,
975
- 40
976
- ];
977
- return [
978
- 4,
979
- sleep(interval)
980
- ];
981
- case 33:
982
- _state.sent();
983
- tokenResult = void 0;
984
- _state.label = 34;
985
- case 34:
1278
+ _state.label = 35;
1279
+ case 35:
986
1280
  _state.trys.push([
987
- 34,
988
- 36,
1281
+ 35,
1282
+ 38,
989
1283
  ,
990
- 37
1284
+ 39
991
1285
  ]);
992
1286
  return [
993
1287
  4,
994
- client.pollDeviceToken(device_code, 'dsp-cli')
995
- ];
996
- case 35:
997
- tokenResult = _state.sent();
998
- return [
999
- 3,
1000
- 37
1288
+ pollDeviceToken(client, device_code, 'dsp-cli', {
1289
+ intervalMs: (initialInterval || 5) * 1000,
1290
+ expiresAt: Date.now() + (expires_in || 600) * 1000
1291
+ })
1001
1292
  ];
1002
1293
  case 36:
1003
- unused3 = _state.sent();
1004
- // Transient network error — retry on next interval
1005
- return [
1006
- 3,
1007
- 32
1008
- ];
1009
- case 37:
1010
- if (!('access_token' in tokenResult)) return [
1011
- 3,
1012
- 39
1013
- ];
1294
+ token = _state.sent();
1014
1295
  console.log(' done');
1015
1296
  return [
1016
1297
  4,
1017
1298
  saveConfig({
1018
- token: tokenResult.access_token,
1299
+ token: token,
1019
1300
  apiUrl: apiUrl
1020
1301
  })
1021
1302
  ];
1022
- case 38:
1303
+ case 37:
1023
1304
  _state.sent();
1024
1305
  console.log("Logged in as ".concat(email));
1025
1306
  return [
1026
1307
  2
1027
1308
  ];
1028
- case 39:
1029
- _$err = tokenResult;
1030
- if (_$err.error === 'authorization_pending') {
1031
- return [
1032
- 3,
1033
- 32
1034
- ];
1035
- }
1036
- if (_$err.error === 'slow_down') {
1037
- interval += 5000;
1038
- return [
1039
- 3,
1040
- 32
1041
- ];
1042
- }
1043
- if (_$err.error === 'access_denied') {
1044
- console.log();
1309
+ case 38:
1310
+ err1 = _state.sent();
1311
+ console.log();
1312
+ if (_instanceof(err1, DeviceCodeDeniedError)) {
1045
1313
  console.error('Authentication denied. Run dsp login to try again.');
1046
- process.exit(1);
1047
- }
1048
- if (_$err.error === 'expired_token') {
1049
- console.log();
1314
+ } else if (_instanceof(err1, DeviceCodeExpiredError)) {
1050
1315
  console.error('Code expired. Run dsp login to try again.');
1051
- process.exit(1);
1316
+ } else if (_instanceof(err1, DeviceCodeFailedError)) {
1317
+ console.error(err1.message || 'Authentication failed. Run dsp login to try again.');
1318
+ } else {
1319
+ console.error('Authentication failed. Run dsp login to try again.');
1052
1320
  }
1053
- // Unknown error
1054
- console.log();
1055
- console.error(_$err.error_description || 'Authentication failed. Run dsp login to try again.');
1056
1321
  process.exit(1);
1057
1322
  return [
1058
1323
  3,
1059
- 32
1324
+ 39
1060
1325
  ];
1061
- case 40:
1326
+ case 39:
1062
1327
  return [
1063
1328
  2
1064
1329
  ];
@@ -1073,7 +1338,7 @@ program.command('branding <shortId> <mode>').description('Override display.dev b
1073
1338
  return _ts_generator(this, function(_state) {
1074
1339
  switch(_state.label){
1075
1340
  case 0:
1076
- mode = parseShowBrandingFlag(modeRaw);
1341
+ mode = parseShowBrandingOrExit(modeRaw);
1077
1342
  if (!mode) {
1078
1343
  console.error('mode is required');
1079
1344
  process.exit(1);
@@ -1107,7 +1372,7 @@ program.command('branding <shortId> <mode>').description('Override display.dev b
1107
1372
  case 4:
1108
1373
  err = _state.sent();
1109
1374
  msg = _instanceof(err, Error) ? err.message : String(err);
1110
- if (/paid plan|upgrade/i.test(msg)) {
1375
+ if (classifyBrandingError(msg) === 'paid-plan') {
1111
1376
  console.error('Error: Hiding display.dev branding requires a paid plan. See https://display.dev/billing');
1112
1377
  } else {
1113
1378
  console.error(msg);
@@ -1128,16 +1393,20 @@ program.command('branding <shortId> <mode>').description('Override display.dev b
1128
1393
  // --- mcp ---
1129
1394
  program.command('mcp').description('Start MCP server over stdin/stdout').action(function() {
1130
1395
  return _async_to_generator(function() {
1131
- var auth, client;
1396
+ var auth, client, publicClient;
1132
1397
  return _ts_generator(this, function(_state) {
1133
1398
  switch(_state.label){
1134
1399
  case 0:
1135
1400
  return [
1136
1401
  4,
1137
- resolveAuthOrConfig()
1402
+ resolveAuthOrConfigOptional()
1138
1403
  ];
1139
1404
  case 1:
1140
1405
  auth = _state.sent();
1406
+ if (!auth) return [
1407
+ 3,
1408
+ 3
1409
+ ];
1141
1410
  client = new ApiClient({
1142
1411
  baseUrl: auth.apiUrl,
1143
1412
  apiKey: auth.apiKey,
@@ -1152,6 +1421,28 @@ program.command('mcp').description('Start MCP server over stdin/stdout').action(
1152
1421
  return [
1153
1422
  2
1154
1423
  ];
1424
+ case 3:
1425
+ // Unauthenticated: expose only `publish`, which maps to the public
1426
+ // claimable endpoint and returns a claim URL the user visits to own
1427
+ // the artifact. Workspace-bound tools are not registered — they have
1428
+ // no meaningful behaviour without auth and listing them would invite
1429
+ // agents to call them and error out.
1430
+ publicClient = new ApiClient({
1431
+ baseUrl: resolvePublicApiUrl(),
1432
+ apiKey: '',
1433
+ clientType: 'mcp-stdio'
1434
+ });
1435
+ return [
1436
+ 4,
1437
+ startMcpServer(publicClient, {
1438
+ mode: 'public'
1439
+ })
1440
+ ];
1441
+ case 4:
1442
+ _state.sent();
1443
+ return [
1444
+ 2
1445
+ ];
1155
1446
  }
1156
1447
  });
1157
1448
  })();