@telepath-computer/television 0.1.216 → 1.3.3

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.
Files changed (81) hide show
  1. package/dist/THIRD-PARTY-NOTICES.txt +336 -0
  2. package/dist/canonical/v1/frozen.json +6 -0
  3. package/dist/canonical/v1/styles.css +7 -2
  4. package/dist/canonical/v2/components.js +1 -0
  5. package/dist/canonical/v2/fonts/Hind-Variable.933e9900.woff2 +0 -0
  6. package/dist/canonical/v2/styles.css +1241 -0
  7. package/dist/cli.cjs +1763 -761
  8. package/dist/onboarding/README.md +16 -14
  9. package/dist/onboarding/business-ops/about.html +4 -3
  10. package/dist/onboarding/business-ops/retention-vs-releases.html +25 -32
  11. package/dist/onboarding/business-ops/revenue-vs-goals.html +26 -31
  12. package/dist/onboarding/productivity/about.html +4 -3
  13. package/dist/onboarding/productivity/company-todos/THIRD-PARTY-NOTICES.txt +29 -0
  14. package/dist/onboarding/productivity/company-todos/index.html +20 -20
  15. package/dist/onboarding/productivity/company-todos/task.css +4 -5
  16. package/dist/onboarding/productivity/company-todos/task.js +1 -1
  17. package/dist/onboarding/productivity/meeting-prep.html +12 -12
  18. package/dist/onboarding/productivity/priorities-today.html +10 -10
  19. package/dist/onboarding/productivity/todays-calendar/calendar.css +1 -1
  20. package/dist/onboarding/productivity/todays-calendar/index.html +12 -14
  21. package/dist/onboarding/research/about.html +4 -3
  22. package/dist/onboarding/research/open-model-research.html +35 -35
  23. package/dist/onboarding/research/todays-news.html +72 -72
  24. package/dist/onboarding/tv-guide/welcome.html +178 -0
  25. package/dist/skills/television/SKILL.md +124 -67
  26. package/dist/skills/television/theming.md +1086 -0
  27. package/dist/skills/tv-calendar/SKILL.md +3 -1
  28. package/dist/skills/tv-calendar/calendar.css +1 -1
  29. package/dist/skills/tv-table/SKILL.md +5 -2
  30. package/dist/skills/tv-tasks/SKILL.md +19 -15
  31. package/dist/skills/tv-tasks/task.css +4 -26
  32. package/dist/skills/tv-tasks/task.js +1 -1
  33. package/dist/themes/README.md +5 -0
  34. package/dist/themes/aquarium/README.md +48 -0
  35. package/dist/themes/aquarium/iframe-background.js +137 -0
  36. package/dist/themes/aquarium/manifest.json +7 -0
  37. package/dist/themes/aquarium/theme.css +46 -0
  38. package/dist/themes/blueprint/README.md +31 -0
  39. package/dist/themes/blueprint/manifest.json +6 -0
  40. package/dist/themes/blueprint/theme.css +115 -0
  41. package/dist/themes/clouds/README.md +15 -0
  42. package/dist/themes/clouds/THIRD-PARTY-NOTICES.txt +81 -0
  43. package/dist/themes/clouds/manifest.json +6 -0
  44. package/dist/themes/clouds/theme.css +62 -0
  45. package/dist/themes/clouds/wallpaper-dark.webp +0 -0
  46. package/dist/themes/clouds/wallpaper.webp +0 -0
  47. package/dist/themes/crt-phosphor/README.md +31 -0
  48. package/dist/themes/crt-phosphor/manifest.json +6 -0
  49. package/dist/themes/crt-phosphor/theme.css +105 -0
  50. package/dist/themes/nord/README.md +9 -0
  51. package/dist/themes/nord/THIRD-PARTY-NOTICES.txt +29 -0
  52. package/dist/themes/nord/manifest.json +5 -0
  53. package/dist/themes/nord/theme.css +28 -0
  54. package/dist/themes/swiss/README.md +5 -0
  55. package/dist/themes/swiss/manifest.json +5 -0
  56. package/dist/themes/swiss/theme.css +34 -0
  57. package/dist/themes/tokyo-night/README.md +17 -0
  58. package/dist/themes/tokyo-night/THIRD-PARTY-NOTICES.txt +235 -0
  59. package/dist/themes/tokyo-night/manifest.json +5 -0
  60. package/dist/themes/tokyo-night/theme.css +31 -0
  61. package/dist/themes/tokyo-night/wallpaper.webp +0 -0
  62. package/dist/views/artifact-missing/index.html +40 -68
  63. package/dist/views/markdown/THIRD-PARTY-NOTICES.txt +26 -0
  64. package/dist/views/markdown/index.html +49 -23
  65. package/dist/web/THIRD-PARTY-NOTICES.txt +26 -0
  66. package/dist/web/assets/artifact-bridge-BEUWXKq7.js +1 -0
  67. package/dist/web/assets/{artifactMissing-BiK_l3Ts.js → artifactMissing-CUs1ZwDx.js} +1 -1
  68. package/dist/web/assets/main-Bi6QZnn7.css +1 -0
  69. package/dist/web/assets/main-Dw6rK_zF.js +706 -0
  70. package/dist/web/assets/{urlUnsupported-RGIrhMDe.js → urlUnsupported-powR79PT.js} +1 -1
  71. package/dist/web/index.html +40 -5
  72. package/dist/web/views/artifact-missing/index.html +40 -68
  73. package/dist/web/views/url-unsupported/index.html +40 -68
  74. package/package.json +1 -1
  75. package/dist/onboarding/tv-guide/welcome/assets/television.svg +0 -71
  76. package/dist/onboarding/tv-guide/welcome/index.html +0 -203
  77. package/dist/skills/tv-theme/SKILL.md +0 -449
  78. package/dist/web/assets/artifact-bridge-3Trk-B2L.js +0 -1
  79. package/dist/web/assets/clouds-CAYIArXj.jpg +0 -0
  80. package/dist/web/assets/main-Blg8fuaV.css +0 -1
  81. package/dist/web/assets/main-V14r9Psb.js +0 -596
package/dist/cli.cjs CHANGED
@@ -1194,7 +1194,7 @@ var require_command = __commonJS({
1194
1194
  "../../node_modules/commander/lib/command.js"(exports2) {
1195
1195
  var EventEmitter2 = require("node:events").EventEmitter;
1196
1196
  var childProcess = require("node:child_process");
1197
- var path18 = require("node:path");
1197
+ var path19 = require("node:path");
1198
1198
  var fs4 = require("node:fs");
1199
1199
  var process3 = require("node:process");
1200
1200
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2207,9 +2207,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2207
2207
  let launchWithNode = false;
2208
2208
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2209
2209
  function findFile(baseDir, baseName) {
2210
- const localBin = path18.resolve(baseDir, baseName);
2210
+ const localBin = path19.resolve(baseDir, baseName);
2211
2211
  if (fs4.existsSync(localBin)) return localBin;
2212
- if (sourceExt.includes(path18.extname(baseName))) return void 0;
2212
+ if (sourceExt.includes(path19.extname(baseName))) return void 0;
2213
2213
  const foundExt = sourceExt.find(
2214
2214
  (ext) => fs4.existsSync(`${localBin}${ext}`)
2215
2215
  );
@@ -2227,17 +2227,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2227
2227
  } catch {
2228
2228
  resolvedScriptPath = this._scriptPath;
2229
2229
  }
2230
- executableDir = path18.resolve(
2231
- path18.dirname(resolvedScriptPath),
2230
+ executableDir = path19.resolve(
2231
+ path19.dirname(resolvedScriptPath),
2232
2232
  executableDir
2233
2233
  );
2234
2234
  }
2235
2235
  if (executableDir) {
2236
2236
  let localFile = findFile(executableDir, executableFile);
2237
2237
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2238
- const legacyName = path18.basename(
2238
+ const legacyName = path19.basename(
2239
2239
  this._scriptPath,
2240
- path18.extname(this._scriptPath)
2240
+ path19.extname(this._scriptPath)
2241
2241
  );
2242
2242
  if (legacyName !== this._name) {
2243
2243
  localFile = findFile(
@@ -2248,7 +2248,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2248
2248
  }
2249
2249
  executableFile = localFile || executableFile;
2250
2250
  }
2251
- launchWithNode = sourceExt.includes(path18.extname(executableFile));
2251
+ launchWithNode = sourceExt.includes(path19.extname(executableFile));
2252
2252
  let proc;
2253
2253
  if (process3.platform !== "win32") {
2254
2254
  if (launchWithNode) {
@@ -3163,7 +3163,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3163
3163
  * @return {Command}
3164
3164
  */
3165
3165
  nameFromFilename(filename) {
3166
- this._name = path18.basename(filename, path18.extname(filename));
3166
+ this._name = path19.basename(filename, path19.extname(filename));
3167
3167
  return this;
3168
3168
  }
3169
3169
  /**
@@ -3177,9 +3177,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3177
3177
  * @param {string} [path]
3178
3178
  * @return {(string|null|Command)}
3179
3179
  */
3180
- executableDir(path19) {
3181
- if (path19 === void 0) return this._executableDir;
3182
- this._executableDir = path19;
3180
+ executableDir(path20) {
3181
+ if (path20 === void 0) return this._executableDir;
3182
+ this._executableDir = path20;
3183
3183
  return this;
3184
3184
  }
3185
3185
  /**
@@ -21480,11 +21480,11 @@ var require_mime_types = __commonJS({
21480
21480
  }
21481
21481
  return exts[0];
21482
21482
  }
21483
- function lookup(path18) {
21484
- if (!path18 || typeof path18 !== "string") {
21483
+ function lookup(path19) {
21484
+ if (!path19 || typeof path19 !== "string") {
21485
21485
  return false;
21486
21486
  }
21487
- var extension3 = extname("x." + path18).toLowerCase().substr(1);
21487
+ var extension3 = extname("x." + path19).toLowerCase().substr(1);
21488
21488
  if (!extension3) {
21489
21489
  return false;
21490
21490
  }
@@ -24307,7 +24307,7 @@ var require_path_to_regexp = __commonJS({
24307
24307
  "../../node_modules/path-to-regexp/index.js"(exports2, module2) {
24308
24308
  module2.exports = pathToRegexp;
24309
24309
  var MATCHING_GROUP_REGEXP = /\\.|\((?:\?<(.*?)>)?(?!\?)/g;
24310
- function pathToRegexp(path18, keys, options) {
24310
+ function pathToRegexp(path19, keys, options) {
24311
24311
  options = options || {};
24312
24312
  keys = keys || [];
24313
24313
  var strict = options.strict;
@@ -24321,8 +24321,8 @@ var require_path_to_regexp = __commonJS({
24321
24321
  var pos = 0;
24322
24322
  var backtrack = "";
24323
24323
  var m2;
24324
- if (path18 instanceof RegExp) {
24325
- while (m2 = MATCHING_GROUP_REGEXP.exec(path18.source)) {
24324
+ if (path19 instanceof RegExp) {
24325
+ while (m2 = MATCHING_GROUP_REGEXP.exec(path19.source)) {
24326
24326
  if (m2[0][0] === "\\") continue;
24327
24327
  keys.push({
24328
24328
  name: m2[1] || name++,
@@ -24330,18 +24330,18 @@ var require_path_to_regexp = __commonJS({
24330
24330
  offset: m2.index
24331
24331
  });
24332
24332
  }
24333
- return path18;
24333
+ return path19;
24334
24334
  }
24335
- if (Array.isArray(path18)) {
24336
- path18 = path18.map(function(value) {
24335
+ if (Array.isArray(path19)) {
24336
+ path19 = path19.map(function(value) {
24337
24337
  return pathToRegexp(value, keys, options).source;
24338
24338
  });
24339
- return new RegExp(path18.join("|"), flags);
24339
+ return new RegExp(path19.join("|"), flags);
24340
24340
  }
24341
- if (typeof path18 !== "string") {
24341
+ if (typeof path19 !== "string") {
24342
24342
  throw new TypeError("path must be a string, array of strings, or regular expression");
24343
24343
  }
24344
- path18 = path18.replace(
24344
+ path19 = path19.replace(
24345
24345
  /\\.|(\/)?(\.)?:(\w+)(\(.*?\))?(\*)?(\?)?|[.*]|\/\(/g,
24346
24346
  function(match, slash, format, key, capture2, star, optional2, offset) {
24347
24347
  if (match[0] === "\\") {
@@ -24358,7 +24358,7 @@ var require_path_to_regexp = __commonJS({
24358
24358
  if (slash || format) {
24359
24359
  backtrack = "";
24360
24360
  } else {
24361
- backtrack += path18.slice(pos, offset);
24361
+ backtrack += path19.slice(pos, offset);
24362
24362
  }
24363
24363
  pos = offset + match.length;
24364
24364
  if (match === "*") {
@@ -24388,7 +24388,7 @@ var require_path_to_regexp = __commonJS({
24388
24388
  return result;
24389
24389
  }
24390
24390
  );
24391
- while (m2 = MATCHING_GROUP_REGEXP.exec(path18)) {
24391
+ while (m2 = MATCHING_GROUP_REGEXP.exec(path19)) {
24392
24392
  if (m2[0][0] === "\\") continue;
24393
24393
  if (keysOffset + i === keys.length || keys[keysOffset + i].offset > m2.index) {
24394
24394
  keys.splice(keysOffset + i, 0, {
@@ -24400,13 +24400,13 @@ var require_path_to_regexp = __commonJS({
24400
24400
  }
24401
24401
  i++;
24402
24402
  }
24403
- path18 += strict ? "" : path18[path18.length - 1] === "/" ? "?" : "/?";
24403
+ path19 += strict ? "" : path19[path19.length - 1] === "/" ? "?" : "/?";
24404
24404
  if (end) {
24405
- path18 += "$";
24406
- } else if (path18[path18.length - 1] !== "/") {
24407
- path18 += lookahead ? "(?=/|$)" : "(?:/|$)";
24405
+ path19 += "$";
24406
+ } else if (path19[path19.length - 1] !== "/") {
24407
+ path19 += lookahead ? "(?=/|$)" : "(?:/|$)";
24408
24408
  }
24409
- return new RegExp("^" + path18, flags);
24409
+ return new RegExp("^" + path19, flags);
24410
24410
  }
24411
24411
  }
24412
24412
  });
@@ -24419,19 +24419,19 @@ var require_layer = __commonJS({
24419
24419
  var debug = require_src3()("express:router:layer");
24420
24420
  var hasOwnProperty = Object.prototype.hasOwnProperty;
24421
24421
  module2.exports = Layer;
24422
- function Layer(path18, options, fn) {
24422
+ function Layer(path19, options, fn) {
24423
24423
  if (!(this instanceof Layer)) {
24424
- return new Layer(path18, options, fn);
24424
+ return new Layer(path19, options, fn);
24425
24425
  }
24426
- debug("new %o", path18);
24426
+ debug("new %o", path19);
24427
24427
  var opts = options || {};
24428
24428
  this.handle = fn;
24429
24429
  this.name = fn.name || "<anonymous>";
24430
24430
  this.params = void 0;
24431
24431
  this.path = void 0;
24432
- this.regexp = pathRegexp(path18, this.keys = [], opts);
24433
- this.regexp.fast_star = path18 === "*";
24434
- this.regexp.fast_slash = path18 === "/" && opts.end === false;
24432
+ this.regexp = pathRegexp(path19, this.keys = [], opts);
24433
+ this.regexp.fast_star = path19 === "*";
24434
+ this.regexp.fast_slash = path19 === "/" && opts.end === false;
24435
24435
  }
24436
24436
  Layer.prototype.handle_error = function handle_error(error48, req, res, next) {
24437
24437
  var fn = this.handle;
@@ -24455,20 +24455,20 @@ var require_layer = __commonJS({
24455
24455
  next(err);
24456
24456
  }
24457
24457
  };
24458
- Layer.prototype.match = function match(path18) {
24458
+ Layer.prototype.match = function match(path19) {
24459
24459
  var match2;
24460
- if (path18 != null) {
24460
+ if (path19 != null) {
24461
24461
  if (this.regexp.fast_slash) {
24462
24462
  this.params = {};
24463
24463
  this.path = "";
24464
24464
  return true;
24465
24465
  }
24466
24466
  if (this.regexp.fast_star) {
24467
- this.params = { "0": decode_param(path18) };
24468
- this.path = path18;
24467
+ this.params = { "0": decode_param(path19) };
24468
+ this.path = path19;
24469
24469
  return true;
24470
24470
  }
24471
- match2 = this.regexp.exec(path18);
24471
+ match2 = this.regexp.exec(path19);
24472
24472
  }
24473
24473
  if (!match2) {
24474
24474
  this.params = void 0;
@@ -24561,10 +24561,10 @@ var require_route = __commonJS({
24561
24561
  var slice = Array.prototype.slice;
24562
24562
  var toString = Object.prototype.toString;
24563
24563
  module2.exports = Route;
24564
- function Route(path18) {
24565
- this.path = path18;
24564
+ function Route(path19) {
24565
+ this.path = path19;
24566
24566
  this.stack = [];
24567
- debug("new %o", path18);
24567
+ debug("new %o", path19);
24568
24568
  this.methods = {};
24569
24569
  }
24570
24570
  Route.prototype._handles_method = function _handles_method(method) {
@@ -24776,8 +24776,8 @@ var require_router = __commonJS({
24776
24776
  if (++sync > 100) {
24777
24777
  return setImmediate(next, err);
24778
24778
  }
24779
- var path18 = getPathname(req);
24780
- if (path18 == null) {
24779
+ var path19 = getPathname(req);
24780
+ if (path19 == null) {
24781
24781
  return done(layerError);
24782
24782
  }
24783
24783
  var layer;
@@ -24785,7 +24785,7 @@ var require_router = __commonJS({
24785
24785
  var route;
24786
24786
  while (match !== true && idx < stack.length) {
24787
24787
  layer = stack[idx++];
24788
- match = matchLayer(layer, path18);
24788
+ match = matchLayer(layer, path19);
24789
24789
  route = layer.route;
24790
24790
  if (typeof match !== "boolean") {
24791
24791
  layerError = layerError || match;
@@ -24823,18 +24823,18 @@ var require_router = __commonJS({
24823
24823
  } else if (route) {
24824
24824
  layer.handle_request(req, res, next);
24825
24825
  } else {
24826
- trim_prefix(layer, layerError, layerPath, path18);
24826
+ trim_prefix(layer, layerError, layerPath, path19);
24827
24827
  }
24828
24828
  sync = 0;
24829
24829
  });
24830
24830
  }
24831
- function trim_prefix(layer, layerError, layerPath, path18) {
24831
+ function trim_prefix(layer, layerError, layerPath, path19) {
24832
24832
  if (layerPath.length !== 0) {
24833
- if (layerPath !== path18.slice(0, layerPath.length)) {
24833
+ if (layerPath !== path19.slice(0, layerPath.length)) {
24834
24834
  next(layerError);
24835
24835
  return;
24836
24836
  }
24837
- var c = path18[layerPath.length];
24837
+ var c = path19[layerPath.length];
24838
24838
  if (c && c !== "/" && c !== ".") return next(layerError);
24839
24839
  debug("trim prefix (%s) from url %s", layerPath, req.url);
24840
24840
  removed = layerPath;
@@ -24912,7 +24912,7 @@ var require_router = __commonJS({
24912
24912
  };
24913
24913
  proto.use = function use(fn) {
24914
24914
  var offset = 0;
24915
- var path18 = "/";
24915
+ var path19 = "/";
24916
24916
  if (typeof fn !== "function") {
24917
24917
  var arg = fn;
24918
24918
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -24920,7 +24920,7 @@ var require_router = __commonJS({
24920
24920
  }
24921
24921
  if (typeof arg !== "function") {
24922
24922
  offset = 1;
24923
- path18 = fn;
24923
+ path19 = fn;
24924
24924
  }
24925
24925
  }
24926
24926
  var callbacks = flatten(slice.call(arguments, offset));
@@ -24932,8 +24932,8 @@ var require_router = __commonJS({
24932
24932
  if (typeof fn !== "function") {
24933
24933
  throw new TypeError("Router.use() requires a middleware function but got a " + gettype(fn));
24934
24934
  }
24935
- debug("use %o %s", path18, fn.name || "<anonymous>");
24936
- var layer = new Layer(path18, {
24935
+ debug("use %o %s", path19, fn.name || "<anonymous>");
24936
+ var layer = new Layer(path19, {
24937
24937
  sensitive: this.caseSensitive,
24938
24938
  strict: false,
24939
24939
  end: false
@@ -24943,9 +24943,9 @@ var require_router = __commonJS({
24943
24943
  }
24944
24944
  return this;
24945
24945
  };
24946
- proto.route = function route(path18) {
24947
- var route2 = new Route(path18);
24948
- var layer = new Layer(path18, {
24946
+ proto.route = function route(path19) {
24947
+ var route2 = new Route(path19);
24948
+ var layer = new Layer(path19, {
24949
24949
  sensitive: this.caseSensitive,
24950
24950
  strict: this.strict,
24951
24951
  end: true
@@ -24955,8 +24955,8 @@ var require_router = __commonJS({
24955
24955
  return route2;
24956
24956
  };
24957
24957
  methods.concat("all").forEach(function(method) {
24958
- proto[method] = function(path18) {
24959
- var route = this.route(path18);
24958
+ proto[method] = function(path19) {
24959
+ var route = this.route(path19);
24960
24960
  route[method].apply(route, slice.call(arguments, 1));
24961
24961
  return this;
24962
24962
  };
@@ -24992,9 +24992,9 @@ var require_router = __commonJS({
24992
24992
  }
24993
24993
  return toString.call(obj).replace(objectRegExp, "$1");
24994
24994
  }
24995
- function matchLayer(layer, path18) {
24995
+ function matchLayer(layer, path19) {
24996
24996
  try {
24997
- return layer.match(path18);
24997
+ return layer.match(path19);
24998
24998
  } catch (err) {
24999
24999
  return err;
25000
25000
  }
@@ -25995,13 +25995,13 @@ var require_view = __commonJS({
25995
25995
  "../../node_modules/express/lib/view.js"(exports2, module2) {
25996
25996
  "use strict";
25997
25997
  var debug = require_src3()("express:view");
25998
- var path18 = require("path");
25998
+ var path19 = require("path");
25999
25999
  var fs4 = require("fs");
26000
- var dirname = path18.dirname;
26001
- var basename = path18.basename;
26002
- var extname = path18.extname;
26003
- var join2 = path18.join;
26004
- var resolve = path18.resolve;
26000
+ var dirname = path19.dirname;
26001
+ var basename = path19.basename;
26002
+ var extname = path19.extname;
26003
+ var join2 = path19.join;
26004
+ var resolve = path19.resolve;
26005
26005
  module2.exports = View;
26006
26006
  function View(name, options) {
26007
26007
  var opts = options || {};
@@ -26030,17 +26030,17 @@ var require_view = __commonJS({
26030
26030
  this.path = this.lookup(fileName);
26031
26031
  }
26032
26032
  View.prototype.lookup = function lookup(name) {
26033
- var path19;
26033
+ var path20;
26034
26034
  var roots = [].concat(this.root);
26035
26035
  debug('lookup "%s"', name);
26036
- for (var i = 0; i < roots.length && !path19; i++) {
26036
+ for (var i = 0; i < roots.length && !path20; i++) {
26037
26037
  var root = roots[i];
26038
26038
  var loc = resolve(root, name);
26039
26039
  var dir = dirname(loc);
26040
26040
  var file2 = basename(loc);
26041
- path19 = this.resolve(dir, file2);
26041
+ path20 = this.resolve(dir, file2);
26042
26042
  }
26043
- return path19;
26043
+ return path20;
26044
26044
  };
26045
26045
  View.prototype.render = function render(options, callback) {
26046
26046
  debug('render "%s"', this.path);
@@ -26048,21 +26048,21 @@ var require_view = __commonJS({
26048
26048
  };
26049
26049
  View.prototype.resolve = function resolve2(dir, file2) {
26050
26050
  var ext = this.ext;
26051
- var path19 = join2(dir, file2);
26052
- var stat = tryStat(path19);
26051
+ var path20 = join2(dir, file2);
26052
+ var stat = tryStat(path20);
26053
26053
  if (stat && stat.isFile()) {
26054
- return path19;
26054
+ return path20;
26055
26055
  }
26056
- path19 = join2(dir, basename(file2, ext), "index" + ext);
26057
- stat = tryStat(path19);
26056
+ path20 = join2(dir, basename(file2, ext), "index" + ext);
26057
+ stat = tryStat(path20);
26058
26058
  if (stat && stat.isFile()) {
26059
- return path19;
26059
+ return path20;
26060
26060
  }
26061
26061
  };
26062
- function tryStat(path19) {
26063
- debug('stat "%s"', path19);
26062
+ function tryStat(path20) {
26063
+ debug('stat "%s"', path20);
26064
26064
  try {
26065
- return fs4.statSync(path19);
26065
+ return fs4.statSync(path20);
26066
26066
  } catch (e) {
26067
26067
  return void 0;
26068
26068
  }
@@ -26840,7 +26840,7 @@ var require_types = __commonJS({
26840
26840
  // ../../node_modules/mime/mime.js
26841
26841
  var require_mime = __commonJS({
26842
26842
  "../../node_modules/mime/mime.js"(exports2, module2) {
26843
- var path18 = require("path");
26843
+ var path19 = require("path");
26844
26844
  var fs4 = require("fs");
26845
26845
  function Mime() {
26846
26846
  this.types = /* @__PURE__ */ Object.create(null);
@@ -26870,8 +26870,8 @@ var require_mime = __commonJS({
26870
26870
  this.define(map2);
26871
26871
  this._loading = null;
26872
26872
  };
26873
- Mime.prototype.lookup = function(path19, fallback) {
26874
- var ext = path19.replace(/^.*[\.\/\\]/, "").toLowerCase();
26873
+ Mime.prototype.lookup = function(path20, fallback) {
26874
+ var ext = path20.replace(/^.*[\.\/\\]/, "").toLowerCase();
26875
26875
  return this.types[ext] || fallback || this.default_type;
26876
26876
  };
26877
26877
  Mime.prototype.extension = function(mimeType) {
@@ -27105,28 +27105,28 @@ var require_send = __commonJS({
27105
27105
  var ms = require_ms5();
27106
27106
  var onFinished = require_on_finished();
27107
27107
  var parseRange = require_range_parser();
27108
- var path18 = require("path");
27108
+ var path19 = require("path");
27109
27109
  var statuses = require_statuses();
27110
27110
  var Stream = require("stream");
27111
27111
  var util = require("util");
27112
- var extname = path18.extname;
27113
- var join2 = path18.join;
27114
- var normalize = path18.normalize;
27115
- var resolve = path18.resolve;
27116
- var sep = path18.sep;
27112
+ var extname = path19.extname;
27113
+ var join2 = path19.join;
27114
+ var normalize = path19.normalize;
27115
+ var resolve = path19.resolve;
27116
+ var sep = path19.sep;
27117
27117
  var BYTES_RANGE_REGEXP = /^ *bytes=/;
27118
27118
  var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
27119
27119
  var UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
27120
27120
  module2.exports = send2;
27121
27121
  module2.exports.mime = mime;
27122
- function send2(req, path19, options) {
27123
- return new SendStream(req, path19, options);
27122
+ function send2(req, path20, options) {
27123
+ return new SendStream(req, path20, options);
27124
27124
  }
27125
- function SendStream(req, path19, options) {
27125
+ function SendStream(req, path20, options) {
27126
27126
  Stream.call(this);
27127
27127
  var opts = options || {};
27128
27128
  this.options = opts;
27129
- this.path = path19;
27129
+ this.path = path20;
27130
27130
  this.req = req;
27131
27131
  this._acceptRanges = opts.acceptRanges !== void 0 ? Boolean(opts.acceptRanges) : true;
27132
27132
  this._cacheControl = opts.cacheControl !== void 0 ? Boolean(opts.cacheControl) : true;
@@ -27172,8 +27172,8 @@ var require_send = __commonJS({
27172
27172
  this._index = index2;
27173
27173
  return this;
27174
27174
  }, "send.index: pass index as option");
27175
- SendStream.prototype.root = function root(path19) {
27176
- this._root = resolve(String(path19));
27175
+ SendStream.prototype.root = function root(path20) {
27176
+ this._root = resolve(String(path20));
27177
27177
  debug("root %s", this._root);
27178
27178
  return this;
27179
27179
  };
@@ -27286,10 +27286,10 @@ var require_send = __commonJS({
27286
27286
  var lastModified = this.res.getHeader("Last-Modified");
27287
27287
  return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
27288
27288
  };
27289
- SendStream.prototype.redirect = function redirect(path19) {
27289
+ SendStream.prototype.redirect = function redirect(path20) {
27290
27290
  var res = this.res;
27291
27291
  if (hasListeners(this, "directory")) {
27292
- this.emit("directory", res, path19);
27292
+ this.emit("directory", res, path20);
27293
27293
  return;
27294
27294
  }
27295
27295
  if (this.hasTrailingSlash()) {
@@ -27309,42 +27309,42 @@ var require_send = __commonJS({
27309
27309
  SendStream.prototype.pipe = function pipe2(res) {
27310
27310
  var root = this._root;
27311
27311
  this.res = res;
27312
- var path19 = decode3(this.path);
27313
- if (path19 === -1) {
27312
+ var path20 = decode3(this.path);
27313
+ if (path20 === -1) {
27314
27314
  this.error(400);
27315
27315
  return res;
27316
27316
  }
27317
- if (~path19.indexOf("\0")) {
27317
+ if (~path20.indexOf("\0")) {
27318
27318
  this.error(400);
27319
27319
  return res;
27320
27320
  }
27321
27321
  var parts;
27322
27322
  if (root !== null) {
27323
- if (path19) {
27324
- path19 = normalize("." + sep + path19);
27323
+ if (path20) {
27324
+ path20 = normalize("." + sep + path20);
27325
27325
  }
27326
- if (UP_PATH_REGEXP.test(path19)) {
27327
- debug('malicious path "%s"', path19);
27326
+ if (UP_PATH_REGEXP.test(path20)) {
27327
+ debug('malicious path "%s"', path20);
27328
27328
  this.error(403);
27329
27329
  return res;
27330
27330
  }
27331
- parts = path19.split(sep);
27332
- path19 = normalize(join2(root, path19));
27331
+ parts = path20.split(sep);
27332
+ path20 = normalize(join2(root, path20));
27333
27333
  } else {
27334
- if (UP_PATH_REGEXP.test(path19)) {
27335
- debug('malicious path "%s"', path19);
27334
+ if (UP_PATH_REGEXP.test(path20)) {
27335
+ debug('malicious path "%s"', path20);
27336
27336
  this.error(403);
27337
27337
  return res;
27338
27338
  }
27339
- parts = normalize(path19).split(sep);
27340
- path19 = resolve(path19);
27339
+ parts = normalize(path20).split(sep);
27340
+ path20 = resolve(path20);
27341
27341
  }
27342
27342
  if (containsDotFile(parts)) {
27343
27343
  var access2 = this._dotfiles;
27344
27344
  if (access2 === void 0) {
27345
27345
  access2 = parts[parts.length - 1][0] === "." ? this._hidden ? "allow" : "ignore" : "allow";
27346
27346
  }
27347
- debug('%s dotfile "%s"', access2, path19);
27347
+ debug('%s dotfile "%s"', access2, path20);
27348
27348
  switch (access2) {
27349
27349
  case "allow":
27350
27350
  break;
@@ -27358,13 +27358,13 @@ var require_send = __commonJS({
27358
27358
  }
27359
27359
  }
27360
27360
  if (this._index.length && this.hasTrailingSlash()) {
27361
- this.sendIndex(path19);
27361
+ this.sendIndex(path20);
27362
27362
  return res;
27363
27363
  }
27364
- this.sendFile(path19);
27364
+ this.sendFile(path20);
27365
27365
  return res;
27366
27366
  };
27367
- SendStream.prototype.send = function send3(path19, stat) {
27367
+ SendStream.prototype.send = function send3(path20, stat) {
27368
27368
  var len = stat.size;
27369
27369
  var options = this.options;
27370
27370
  var opts = {};
@@ -27376,9 +27376,9 @@ var require_send = __commonJS({
27376
27376
  this.headersAlreadySent();
27377
27377
  return;
27378
27378
  }
27379
- debug('pipe "%s"', path19);
27380
- this.setHeader(path19, stat);
27381
- this.type(path19);
27379
+ debug('pipe "%s"', path20);
27380
+ this.setHeader(path20, stat);
27381
+ this.type(path20);
27382
27382
  if (this.isConditionalGET()) {
27383
27383
  if (this.isPreconditionFailure()) {
27384
27384
  this.error(412);
@@ -27427,26 +27427,26 @@ var require_send = __commonJS({
27427
27427
  res.end();
27428
27428
  return;
27429
27429
  }
27430
- this.stream(path19, opts);
27430
+ this.stream(path20, opts);
27431
27431
  };
27432
- SendStream.prototype.sendFile = function sendFile(path19) {
27432
+ SendStream.prototype.sendFile = function sendFile(path20) {
27433
27433
  var i = 0;
27434
27434
  var self2 = this;
27435
- debug('stat "%s"', path19);
27436
- fs4.stat(path19, function onstat(err, stat) {
27437
- if (err && err.code === "ENOENT" && !extname(path19) && path19[path19.length - 1] !== sep) {
27435
+ debug('stat "%s"', path20);
27436
+ fs4.stat(path20, function onstat(err, stat) {
27437
+ if (err && err.code === "ENOENT" && !extname(path20) && path20[path20.length - 1] !== sep) {
27438
27438
  return next(err);
27439
27439
  }
27440
27440
  if (err) return self2.onStatError(err);
27441
- if (stat.isDirectory()) return self2.redirect(path19);
27442
- self2.emit("file", path19, stat);
27443
- self2.send(path19, stat);
27441
+ if (stat.isDirectory()) return self2.redirect(path20);
27442
+ self2.emit("file", path20, stat);
27443
+ self2.send(path20, stat);
27444
27444
  });
27445
27445
  function next(err) {
27446
27446
  if (self2._extensions.length <= i) {
27447
27447
  return err ? self2.onStatError(err) : self2.error(404);
27448
27448
  }
27449
- var p = path19 + "." + self2._extensions[i++];
27449
+ var p = path20 + "." + self2._extensions[i++];
27450
27450
  debug('stat "%s"', p);
27451
27451
  fs4.stat(p, function(err2, stat) {
27452
27452
  if (err2) return next(err2);
@@ -27456,7 +27456,7 @@ var require_send = __commonJS({
27456
27456
  });
27457
27457
  }
27458
27458
  };
27459
- SendStream.prototype.sendIndex = function sendIndex(path19) {
27459
+ SendStream.prototype.sendIndex = function sendIndex(path20) {
27460
27460
  var i = -1;
27461
27461
  var self2 = this;
27462
27462
  function next(err) {
@@ -27464,7 +27464,7 @@ var require_send = __commonJS({
27464
27464
  if (err) return self2.onStatError(err);
27465
27465
  return self2.error(404);
27466
27466
  }
27467
- var p = join2(path19, self2._index[i]);
27467
+ var p = join2(path20, self2._index[i]);
27468
27468
  debug('stat "%s"', p);
27469
27469
  fs4.stat(p, function(err2, stat) {
27470
27470
  if (err2) return next(err2);
@@ -27475,10 +27475,10 @@ var require_send = __commonJS({
27475
27475
  }
27476
27476
  next();
27477
27477
  };
27478
- SendStream.prototype.stream = function stream(path19, options) {
27478
+ SendStream.prototype.stream = function stream(path20, options) {
27479
27479
  var self2 = this;
27480
27480
  var res = this.res;
27481
- var stream2 = fs4.createReadStream(path19, options);
27481
+ var stream2 = fs4.createReadStream(path20, options);
27482
27482
  this.emit("stream", stream2);
27483
27483
  stream2.pipe(res);
27484
27484
  function cleanup() {
@@ -27493,10 +27493,10 @@ var require_send = __commonJS({
27493
27493
  self2.emit("end");
27494
27494
  });
27495
27495
  };
27496
- SendStream.prototype.type = function type(path19) {
27496
+ SendStream.prototype.type = function type(path20) {
27497
27497
  var res = this.res;
27498
27498
  if (res.getHeader("Content-Type")) return;
27499
- var type2 = mime.lookup(path19);
27499
+ var type2 = mime.lookup(path20);
27500
27500
  if (!type2) {
27501
27501
  debug("no content-type");
27502
27502
  return;
@@ -27505,9 +27505,9 @@ var require_send = __commonJS({
27505
27505
  debug("content-type %s", type2);
27506
27506
  res.setHeader("Content-Type", type2 + (charset ? "; charset=" + charset : ""));
27507
27507
  };
27508
- SendStream.prototype.setHeader = function setHeader(path19, stat) {
27508
+ SendStream.prototype.setHeader = function setHeader(path20, stat) {
27509
27509
  var res = this.res;
27510
- this.emit("headers", res, path19, stat);
27510
+ this.emit("headers", res, path20, stat);
27511
27511
  if (this._acceptRanges && !res.getHeader("Accept-Ranges")) {
27512
27512
  debug("accept ranges");
27513
27513
  res.setHeader("Accept-Ranges", "bytes");
@@ -27566,9 +27566,9 @@ var require_send = __commonJS({
27566
27566
  }
27567
27567
  return err instanceof Error ? createError(status, err, { expose: false }) : createError(status, err);
27568
27568
  }
27569
- function decode3(path19) {
27569
+ function decode3(path20) {
27570
27570
  try {
27571
- return decodeURIComponent(path19);
27571
+ return decodeURIComponent(path20);
27572
27572
  } catch (err) {
27573
27573
  return -1;
27574
27574
  }
@@ -28477,10 +28477,10 @@ var require_utils3 = __commonJS({
28477
28477
  var querystring = require("querystring");
28478
28478
  exports2.etag = createETagGenerator({ weak: false });
28479
28479
  exports2.wetag = createETagGenerator({ weak: true });
28480
- exports2.isAbsolute = function(path18) {
28481
- if ("/" === path18[0]) return true;
28482
- if (":" === path18[1] && ("\\" === path18[2] || "/" === path18[2])) return true;
28483
- if ("\\\\" === path18.substring(0, 2)) return true;
28480
+ exports2.isAbsolute = function(path19) {
28481
+ if ("/" === path19[0]) return true;
28482
+ if (":" === path19[1] && ("\\" === path19[2] || "/" === path19[2])) return true;
28483
+ if ("\\\\" === path19.substring(0, 2)) return true;
28484
28484
  };
28485
28485
  exports2.flatten = deprecate.function(
28486
28486
  flatten,
@@ -28691,7 +28691,7 @@ var require_application = __commonJS({
28691
28691
  };
28692
28692
  app.use = function use(fn) {
28693
28693
  var offset = 0;
28694
- var path18 = "/";
28694
+ var path19 = "/";
28695
28695
  if (typeof fn !== "function") {
28696
28696
  var arg = fn;
28697
28697
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -28699,7 +28699,7 @@ var require_application = __commonJS({
28699
28699
  }
28700
28700
  if (typeof arg !== "function") {
28701
28701
  offset = 1;
28702
- path18 = fn;
28702
+ path19 = fn;
28703
28703
  }
28704
28704
  }
28705
28705
  var fns = flatten(slice.call(arguments, offset));
@@ -28710,12 +28710,12 @@ var require_application = __commonJS({
28710
28710
  var router = this._router;
28711
28711
  fns.forEach(function(fn2) {
28712
28712
  if (!fn2 || !fn2.handle || !fn2.set) {
28713
- return router.use(path18, fn2);
28713
+ return router.use(path19, fn2);
28714
28714
  }
28715
- debug(".use app under %s", path18);
28716
- fn2.mountpath = path18;
28715
+ debug(".use app under %s", path19);
28716
+ fn2.mountpath = path19;
28717
28717
  fn2.parent = this;
28718
- router.use(path18, function mounted_app(req, res, next) {
28718
+ router.use(path19, function mounted_app(req, res, next) {
28719
28719
  var orig = req.app;
28720
28720
  fn2.handle(req, res, function(err) {
28721
28721
  setPrototypeOf(req, orig.request);
@@ -28727,9 +28727,9 @@ var require_application = __commonJS({
28727
28727
  }, this);
28728
28728
  return this;
28729
28729
  };
28730
- app.route = function route(path18) {
28730
+ app.route = function route(path19) {
28731
28731
  this.lazyrouter();
28732
- return this._router.route(path18);
28732
+ return this._router.route(path19);
28733
28733
  };
28734
28734
  app.engine = function engine(ext, fn) {
28735
28735
  if (typeof fn !== "function") {
@@ -28780,7 +28780,7 @@ var require_application = __commonJS({
28780
28780
  }
28781
28781
  return this;
28782
28782
  };
28783
- app.path = function path18() {
28783
+ app.path = function path19() {
28784
28784
  return this.parent ? this.parent.path() + this.mountpath : "";
28785
28785
  };
28786
28786
  app.enabled = function enabled(setting) {
@@ -28796,19 +28796,19 @@ var require_application = __commonJS({
28796
28796
  return this.set(setting, false);
28797
28797
  };
28798
28798
  methods.forEach(function(method) {
28799
- app[method] = function(path18) {
28799
+ app[method] = function(path19) {
28800
28800
  if (method === "get" && arguments.length === 1) {
28801
- return this.set(path18);
28801
+ return this.set(path19);
28802
28802
  }
28803
28803
  this.lazyrouter();
28804
- var route = this._router.route(path18);
28804
+ var route = this._router.route(path19);
28805
28805
  route[method].apply(route, slice.call(arguments, 1));
28806
28806
  return this;
28807
28807
  };
28808
28808
  });
28809
- app.all = function all(path18) {
28809
+ app.all = function all(path19) {
28810
28810
  this.lazyrouter();
28811
- var route = this._router.route(path18);
28811
+ var route = this._router.route(path19);
28812
28812
  var args = slice.call(arguments, 1);
28813
28813
  for (var i = 0; i < methods.length; i++) {
28814
28814
  route[methods[i]].apply(route, args);
@@ -29567,7 +29567,7 @@ var require_request = __commonJS({
29567
29567
  var subdomains2 = !isIP2(hostname3) ? hostname3.split(".").reverse() : [hostname3];
29568
29568
  return subdomains2.slice(offset);
29569
29569
  });
29570
- defineGetter(req, "path", function path18() {
29570
+ defineGetter(req, "path", function path19() {
29571
29571
  return parse3(this).pathname;
29572
29572
  });
29573
29573
  defineGetter(req, "hostname", function hostname3() {
@@ -29889,7 +29889,7 @@ var require_response = __commonJS({
29889
29889
  var http2 = require("http");
29890
29890
  var isAbsolute = require_utils3().isAbsolute;
29891
29891
  var onFinished = require_on_finished();
29892
- var path18 = require("path");
29892
+ var path19 = require("path");
29893
29893
  var statuses = require_statuses();
29894
29894
  var merge2 = require_utils_merge();
29895
29895
  var sign = require_cookie_signature().sign;
@@ -29898,9 +29898,9 @@ var require_response = __commonJS({
29898
29898
  var setCharset = require_utils3().setCharset;
29899
29899
  var cookie = require_cookie();
29900
29900
  var send2 = require_send();
29901
- var extname = path18.extname;
29901
+ var extname = path19.extname;
29902
29902
  var mime = send2.mime;
29903
- var resolve = path18.resolve;
29903
+ var resolve = path19.resolve;
29904
29904
  var vary = require_vary();
29905
29905
  var res = Object.create(http2.ServerResponse.prototype);
29906
29906
  module2.exports = res;
@@ -30077,26 +30077,26 @@ var require_response = __commonJS({
30077
30077
  this.type("txt");
30078
30078
  return this.send(body);
30079
30079
  };
30080
- res.sendFile = function sendFile(path19, options, callback) {
30080
+ res.sendFile = function sendFile(path20, options, callback) {
30081
30081
  var done = callback;
30082
30082
  var req = this.req;
30083
30083
  var res2 = this;
30084
30084
  var next = req.next;
30085
30085
  var opts = options || {};
30086
- if (!path19) {
30086
+ if (!path20) {
30087
30087
  throw new TypeError("path argument is required to res.sendFile");
30088
30088
  }
30089
- if (typeof path19 !== "string") {
30089
+ if (typeof path20 !== "string") {
30090
30090
  throw new TypeError("path must be a string to res.sendFile");
30091
30091
  }
30092
30092
  if (typeof options === "function") {
30093
30093
  done = options;
30094
30094
  opts = {};
30095
30095
  }
30096
- if (!opts.root && !isAbsolute(path19)) {
30096
+ if (!opts.root && !isAbsolute(path20)) {
30097
30097
  throw new TypeError("path must be absolute or specify root to res.sendFile");
30098
30098
  }
30099
- var pathname = encodeURI(path19);
30099
+ var pathname = encodeURI(path20);
30100
30100
  var file2 = send2(req, pathname, opts);
30101
30101
  sendfile(res2, file2, opts, function(err) {
30102
30102
  if (done) return done(err);
@@ -30106,7 +30106,7 @@ var require_response = __commonJS({
30106
30106
  }
30107
30107
  });
30108
30108
  };
30109
- res.sendfile = function(path19, options, callback) {
30109
+ res.sendfile = function(path20, options, callback) {
30110
30110
  var done = callback;
30111
30111
  var req = this.req;
30112
30112
  var res2 = this;
@@ -30116,7 +30116,7 @@ var require_response = __commonJS({
30116
30116
  done = options;
30117
30117
  opts = {};
30118
30118
  }
30119
- var file2 = send2(req, path19, opts);
30119
+ var file2 = send2(req, path20, opts);
30120
30120
  sendfile(res2, file2, opts, function(err) {
30121
30121
  if (done) return done(err);
30122
30122
  if (err && err.code === "EISDIR") return next();
@@ -30129,7 +30129,7 @@ var require_response = __commonJS({
30129
30129
  res.sendfile,
30130
30130
  "res.sendfile: Use res.sendFile instead"
30131
30131
  );
30132
- res.download = function download(path19, filename, options, callback) {
30132
+ res.download = function download(path20, filename, options, callback) {
30133
30133
  var done = callback;
30134
30134
  var name = filename;
30135
30135
  var opts = options || null;
@@ -30146,7 +30146,7 @@ var require_response = __commonJS({
30146
30146
  opts = filename;
30147
30147
  }
30148
30148
  var headers = {
30149
- "Content-Disposition": contentDisposition(name || path19)
30149
+ "Content-Disposition": contentDisposition(name || path20)
30150
30150
  };
30151
30151
  if (opts && opts.headers) {
30152
30152
  var keys = Object.keys(opts.headers);
@@ -30159,7 +30159,7 @@ var require_response = __commonJS({
30159
30159
  }
30160
30160
  opts = Object.create(opts);
30161
30161
  opts.headers = headers;
30162
- var fullPath = !opts.root ? resolve(path19) : path19;
30162
+ var fullPath = !opts.root ? resolve(path20) : path20;
30163
30163
  return this.sendFile(fullPath, opts, done);
30164
30164
  };
30165
30165
  res.contentType = res.type = function contentType(type) {
@@ -30460,11 +30460,11 @@ var require_serve_static = __commonJS({
30460
30460
  }
30461
30461
  var forwardError = !fallthrough;
30462
30462
  var originalUrl = parseUrl.original(req);
30463
- var path18 = parseUrl(req).pathname;
30464
- if (path18 === "/" && originalUrl.pathname.substr(-1) !== "/") {
30465
- path18 = "";
30463
+ var path19 = parseUrl(req).pathname;
30464
+ if (path19 === "/" && originalUrl.pathname.substr(-1) !== "/") {
30465
+ path19 = "";
30466
30466
  }
30467
- var stream = send2(req, path18, opts);
30467
+ var stream = send2(req, path19, opts);
30468
30468
  stream.on("directory", onDirectory);
30469
30469
  if (setHeaders) {
30470
30470
  stream.on("headers", setHeaders);
@@ -32803,7 +32803,7 @@ var require_websocket = __commonJS({
32803
32803
  var http2 = require("http");
32804
32804
  var net = require("net");
32805
32805
  var tls = require("tls");
32806
- var { randomBytes: randomBytes3, createHash: createHash3 } = require("crypto");
32806
+ var { randomBytes: randomBytes3, createHash: createHash4 } = require("crypto");
32807
32807
  var { Duplex, Readable } = require("stream");
32808
32808
  var { URL: URL2 } = require("url");
32809
32809
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -33463,7 +33463,7 @@ var require_websocket = __commonJS({
33463
33463
  abortHandshake(websocket, socket, "Invalid Upgrade header");
33464
33464
  return;
33465
33465
  }
33466
- const digest = createHash3("sha1").update(key + GUID).digest("base64");
33466
+ const digest = createHash4("sha1").update(key + GUID).digest("base64");
33467
33467
  if (res.headers["sec-websocket-accept"] !== digest) {
33468
33468
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
33469
33469
  return;
@@ -33830,7 +33830,7 @@ var require_websocket_server = __commonJS({
33830
33830
  var EventEmitter2 = require("events");
33831
33831
  var http2 = require("http");
33832
33832
  var { Duplex } = require("stream");
33833
- var { createHash: createHash3 } = require("crypto");
33833
+ var { createHash: createHash4 } = require("crypto");
33834
33834
  var extension2 = require_extension();
33835
33835
  var PerMessageDeflate2 = require_permessage_deflate();
33836
33836
  var subprotocol2 = require_subprotocol();
@@ -34131,7 +34131,7 @@ var require_websocket_server = __commonJS({
34131
34131
  );
34132
34132
  }
34133
34133
  if (this._state > RUNNING) return abortHandshake(socket, 503);
34134
- const digest = createHash3("sha1").update(key + GUID).digest("base64");
34134
+ const digest = createHash4("sha1").update(key + GUID).digest("base64");
34135
34135
  const headers = [
34136
34136
  "HTTP/1.1 101 Switching Protocols",
34137
34137
  "Upgrade: websocket",
@@ -34223,6 +34223,7 @@ __export(index_exports, {
34223
34223
  inspectTelemetryBuildConfig: () => inspectTelemetryBuildConfig,
34224
34224
  listVisibleCLICommandNames: () => listVisibleCLICommandNames,
34225
34225
  resolveBundledSkillsRoot: () => resolveBundledSkillsRoot,
34226
+ resolveBundledThemesPath: () => resolveBundledThemesPath,
34226
34227
  resolveBundledViewsPath: () => resolveBundledViewsPath,
34227
34228
  resolveCanonicalDir: () => resolveCanonicalDir,
34228
34229
  resolveOnboardingContentPath: () => resolveOnboardingContentPath,
@@ -34231,9 +34232,9 @@ __export(index_exports, {
34231
34232
  });
34232
34233
  module.exports = __toCommonJS(index_exports);
34233
34234
  var import_node_child_process3 = require("node:child_process");
34234
- var import_node_fs16 = require("node:fs");
34235
+ var import_node_fs17 = require("node:fs");
34235
34236
  var import_node_os5 = __toESM(require("node:os"), 1);
34236
- var import_node_path18 = __toESM(require("node:path"), 1);
34237
+ var import_node_path19 = __toESM(require("node:path"), 1);
34237
34238
  var import_node_module = require("node:module");
34238
34239
  var import_node_url2 = require("node:url");
34239
34240
 
@@ -34386,22 +34387,22 @@ var Daemon = class {
34386
34387
  }
34387
34388
  async install() {
34388
34389
  if (this.platform === "darwin") {
34389
- const path18 = plistPath(this.home, this.name);
34390
- if ((0, import_node_fs.existsSync)(path18)) {
34391
- await this.exec("launchctl", ["unload", "-w", path18]).catch(() => void 0);
34390
+ const path19 = plistPath(this.home, this.name);
34391
+ if ((0, import_node_fs.existsSync)(path19)) {
34392
+ await this.exec("launchctl", ["unload", "-w", path19]).catch(() => void 0);
34392
34393
  }
34393
34394
  await (0, import_promises.mkdir)((0, import_node_path.join)(this.home, "Library", "LaunchAgents"), { recursive: true });
34394
- await (0, import_promises.writeFile)(path18, renderPlist(this.name, this.programArgs, this.env), "utf8");
34395
- await this.exec("launchctl", ["load", "-w", path18]);
34395
+ await (0, import_promises.writeFile)(path19, renderPlist(this.name, this.programArgs, this.env), "utf8");
34396
+ await this.exec("launchctl", ["load", "-w", path19]);
34396
34397
  return;
34397
34398
  }
34398
34399
  if (this.platform === "linux") {
34399
- const path18 = unitPath(this.home, this.name);
34400
- if ((0, import_node_fs.existsSync)(path18)) {
34400
+ const path19 = unitPath(this.home, this.name);
34401
+ if ((0, import_node_fs.existsSync)(path19)) {
34401
34402
  await this.exec("systemctl", ["--user", "disable", "--now", `${this.name}.service`]).catch(() => void 0);
34402
34403
  }
34403
34404
  await (0, import_promises.mkdir)((0, import_node_path.join)(this.home, ".config", "systemd", "user"), { recursive: true });
34404
- await (0, import_promises.writeFile)(path18, renderUnit(this.description, this.programArgs, this.env), "utf8");
34405
+ await (0, import_promises.writeFile)(path19, renderUnit(this.description, this.programArgs, this.env), "utf8");
34405
34406
  await this.exec("systemctl", ["--user", "daemon-reload"]);
34406
34407
  await this.exec("systemctl", ["--user", "enable", "--now", `${this.name}.service`]);
34407
34408
  return;
@@ -34410,17 +34411,17 @@ var Daemon = class {
34410
34411
  }
34411
34412
  async uninstall() {
34412
34413
  if (this.platform === "darwin") {
34413
- const path18 = plistPath(this.home, this.name);
34414
- if ((0, import_node_fs.existsSync)(path18)) {
34415
- await this.exec("launchctl", ["unload", "-w", path18]).catch(() => void 0);
34416
- await (0, import_promises.rm)(path18, { force: true });
34414
+ const path19 = plistPath(this.home, this.name);
34415
+ if ((0, import_node_fs.existsSync)(path19)) {
34416
+ await this.exec("launchctl", ["unload", "-w", path19]).catch(() => void 0);
34417
+ await (0, import_promises.rm)(path19, { force: true });
34417
34418
  }
34418
34419
  return;
34419
34420
  }
34420
34421
  if (this.platform === "linux") {
34421
- const path18 = unitPath(this.home, this.name);
34422
+ const path19 = unitPath(this.home, this.name);
34422
34423
  await this.exec("systemctl", ["--user", "disable", "--now", `${this.name}.service`]).catch(() => void 0);
34423
- await (0, import_promises.rm)(path18, { force: true });
34424
+ await (0, import_promises.rm)(path19, { force: true });
34424
34425
  await this.exec("systemctl", ["--user", "daemon-reload"]);
34425
34426
  return;
34426
34427
  }
@@ -34428,8 +34429,8 @@ var Daemon = class {
34428
34429
  }
34429
34430
  async status() {
34430
34431
  if (this.platform === "darwin") {
34431
- const path18 = plistPath(this.home, this.name);
34432
- if (!(0, import_node_fs.existsSync)(path18))
34432
+ const path19 = plistPath(this.home, this.name);
34433
+ if (!(0, import_node_fs.existsSync)(path19))
34433
34434
  return { installed: false, running: false };
34434
34435
  try {
34435
34436
  await this.exec("launchctl", ["list", this.name]);
@@ -34439,8 +34440,8 @@ var Daemon = class {
34439
34440
  }
34440
34441
  }
34441
34442
  if (this.platform === "linux") {
34442
- const path18 = unitPath(this.home, this.name);
34443
- if (!(0, import_node_fs.existsSync)(path18))
34443
+ const path19 = unitPath(this.home, this.name);
34444
+ if (!(0, import_node_fs.existsSync)(path19))
34444
34445
  return { installed: false, running: false };
34445
34446
  try {
34446
34447
  const result = await this.exec("systemctl", [
@@ -34464,9 +34465,9 @@ require_shim7()();
34464
34465
 
34465
34466
  // ../server/src/server.ts
34466
34467
  var import_express2 = __toESM(require_express2(), 1);
34467
- var import_node_fs9 = require("node:fs");
34468
+ var import_node_fs10 = require("node:fs");
34468
34469
  var import_node_http = __toESM(require("node:http"), 1);
34469
- var import_node_path11 = __toESM(require("node:path"), 1);
34470
+ var import_node_path12 = __toESM(require("node:path"), 1);
34470
34471
  var import_node_url = require("node:url");
34471
34472
 
34472
34473
  // ../server/src/config.ts
@@ -35361,10 +35362,10 @@ function mergeDefs(...defs) {
35361
35362
  function cloneDef(schema) {
35362
35363
  return mergeDefs(schema._zod.def);
35363
35364
  }
35364
- function getElementAtPath(obj, path18) {
35365
- if (!path18)
35365
+ function getElementAtPath(obj, path19) {
35366
+ if (!path19)
35366
35367
  return obj;
35367
- return path18.reduce((acc, key) => acc?.[key], obj);
35368
+ return path19.reduce((acc, key) => acc?.[key], obj);
35368
35369
  }
35369
35370
  function promiseAllObject(promisesObj) {
35370
35371
  const keys = Object.keys(promisesObj);
@@ -35747,11 +35748,11 @@ function aborted(x2, startIndex = 0) {
35747
35748
  }
35748
35749
  return false;
35749
35750
  }
35750
- function prefixIssues(path18, issues) {
35751
+ function prefixIssues(path19, issues) {
35751
35752
  return issues.map((iss) => {
35752
35753
  var _a2;
35753
35754
  (_a2 = iss).path ?? (_a2.path = []);
35754
- iss.path.unshift(path18);
35755
+ iss.path.unshift(path19);
35755
35756
  return iss;
35756
35757
  });
35757
35758
  }
@@ -35934,7 +35935,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
35934
35935
  }
35935
35936
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
35936
35937
  const result = { errors: [] };
35937
- const processError = (error49, path18 = []) => {
35938
+ const processError = (error49, path19 = []) => {
35938
35939
  var _a2, _b;
35939
35940
  for (const issue2 of error49.issues) {
35940
35941
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -35944,7 +35945,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
35944
35945
  } else if (issue2.code === "invalid_element") {
35945
35946
  processError({ issues: issue2.issues }, issue2.path);
35946
35947
  } else {
35947
- const fullpath = [...path18, ...issue2.path];
35948
+ const fullpath = [...path19, ...issue2.path];
35948
35949
  if (fullpath.length === 0) {
35949
35950
  result.errors.push(mapper(issue2));
35950
35951
  continue;
@@ -35976,8 +35977,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
35976
35977
  }
35977
35978
  function toDotPath(_path) {
35978
35979
  const segs = [];
35979
- const path18 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
35980
- for (const seg of path18) {
35980
+ const path19 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
35981
+ for (const seg of path19) {
35981
35982
  if (typeof seg === "number")
35982
35983
  segs.push(`[${seg}]`);
35983
35984
  else if (typeof seg === "symbol")
@@ -47954,13 +47955,13 @@ function resolveRef(ref, ctx) {
47954
47955
  if (!ref.startsWith("#")) {
47955
47956
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
47956
47957
  }
47957
- const path18 = ref.slice(1).split("/").filter(Boolean);
47958
- if (path18.length === 0) {
47958
+ const path19 = ref.slice(1).split("/").filter(Boolean);
47959
+ if (path19.length === 0) {
47959
47960
  return ctx.rootSchema;
47960
47961
  }
47961
47962
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
47962
- if (path18[0] === defsKey) {
47963
- const key = path18[1];
47963
+ if (path19[0] === defsKey) {
47964
+ const key = path19[1];
47964
47965
  if (!key || !ctx.defs[key]) {
47965
47966
  throw new Error(`Reference not found: ${ref}`);
47966
47967
  }
@@ -48546,6 +48547,7 @@ var ChannelChangedEvent = defineEvent();
48546
48547
  var PinnedChannelsChangedEvent = defineEvent();
48547
48548
  var ArtifactFocusEvent = defineEvent();
48548
48549
  var ThemeChangedEvent = defineEvent();
48550
+ var AppearanceChangedEvent = defineEvent();
48549
48551
 
48550
48552
  // ../shared/src/errors.ts
48551
48553
  function defineError(name) {
@@ -48817,6 +48819,18 @@ var ArtifactClient = class {
48817
48819
  );
48818
48820
  }
48819
48821
  };
48822
+ var ThemeClient = class {
48823
+ #http;
48824
+ constructor(http2) {
48825
+ this.#http = http2;
48826
+ }
48827
+ list() {
48828
+ return this.#http.requestJSON("GET", "/themes");
48829
+ }
48830
+ refresh() {
48831
+ return this.#http.requestJSON("POST", "/themes/refresh");
48832
+ }
48833
+ };
48820
48834
  var MarkdownClient = class {
48821
48835
  #http;
48822
48836
  constructor(http2) {
@@ -48861,6 +48875,10 @@ var DisplayClient = class {
48861
48875
  if (input.focusedChannelId !== void 0) body.focusedChannelId = input.focusedChannelId;
48862
48876
  if (input.pinnedChannelIds !== void 0) body.pinnedChannelIds = input.pinnedChannelIds;
48863
48877
  if (input.activeThemeName !== void 0) body.activeThemeName = input.activeThemeName;
48878
+ if (input.appearanceMode !== void 0) body.appearanceMode = input.appearanceMode;
48879
+ if (input.themeJavaScriptConsentIds !== void 0) {
48880
+ body.themeJavaScriptConsentIds = input.themeJavaScriptConsentIds;
48881
+ }
48864
48882
  return this.#http.requestVoid("PATCH", "/display", body);
48865
48883
  }
48866
48884
  focus(input) {
@@ -48871,6 +48889,7 @@ var TelevisionClient = class {
48871
48889
  channels;
48872
48890
  artifacts;
48873
48891
  display;
48892
+ themes;
48874
48893
  markdown;
48875
48894
  telemetry;
48876
48895
  #http;
@@ -48879,6 +48898,7 @@ var TelevisionClient = class {
48879
48898
  this.channels = new ChannelClient(this.#http);
48880
48899
  this.artifacts = new ArtifactClient(this.#http);
48881
48900
  this.display = new DisplayClient(this.#http);
48901
+ this.themes = new ThemeClient(this.#http);
48882
48902
  this.markdown = new MarkdownClient(this.#http);
48883
48903
  this.telemetry = new TelemetryControlClient(this.#http);
48884
48904
  }
@@ -49085,11 +49105,654 @@ function createArtifact(input) {
49085
49105
  return ArtifactSchema.parse({ id: input.id ?? ulid3(), ...input });
49086
49106
  }
49087
49107
 
49108
+ // ../server/src/bundled-theme-ids.ts
49109
+ var BUNDLED_THEMES = [{ "id": "aquarium", "minimumVersion": "1.0.2" }, { "id": "blueprint", "minimumVersion": "1.0.0" }, { "id": "crt-phosphor", "minimumVersion": "1.0.0" }, { "id": "clouds", "minimumVersion": "2.1.0" }, { "id": "nord", "minimumVersion": "1.1.0" }, { "id": "swiss", "minimumVersion": "1.1.0" }, { "id": "tokyo-night", "minimumVersion": "1.1.0" }];
49110
+ var BUNDLED_THEME_IDS = BUNDLED_THEMES.map(({ id }) => id);
49111
+
49112
+ // ../server/src/bundled-theme-installer.ts
49113
+ var import_node_fs4 = require("node:fs");
49114
+ var import_node_path5 = __toESM(require("node:path"), 1);
49115
+
49116
+ // ../server/src/artifact-paths.ts
49117
+ var import_node_path3 = __toESM(require("node:path"), 1);
49118
+ function getStateDir(storagePath) {
49119
+ return import_node_path3.default.join(storagePath, "state");
49120
+ }
49121
+ function getChannelsDir(storagePath) {
49122
+ return import_node_path3.default.join(getStateDir(storagePath), "channels");
49123
+ }
49124
+ function getLegacyScreensDir(storagePath) {
49125
+ return import_node_path3.default.join(getStateDir(storagePath), "screens");
49126
+ }
49127
+ function getArtifactsMetadataDir(storagePath) {
49128
+ return import_node_path3.default.join(getStateDir(storagePath), "artifacts");
49129
+ }
49130
+ function getDisplayStatePath(storagePath) {
49131
+ return import_node_path3.default.join(getStateDir(storagePath), "display.json");
49132
+ }
49133
+ function getDisplayMigrationTempPath(storagePath) {
49134
+ return import_node_path3.default.join(getStateDir(storagePath), ".display.json.screen-to-channel.tmp");
49135
+ }
49136
+ function getTelemetryStatePath(storagePath) {
49137
+ return import_node_path3.default.join(getStateDir(storagePath), "telemetry.json");
49138
+ }
49139
+ function getTokenPath(storagePath) {
49140
+ return import_node_path3.default.join(getStateDir(storagePath), "token");
49141
+ }
49142
+ function getOnboardingStatePath(storagePath) {
49143
+ return import_node_path3.default.join(getStateDir(storagePath), "onboarding.json");
49144
+ }
49145
+ function getBundledThemesStatePath(storagePath) {
49146
+ return import_node_path3.default.join(getStateDir(storagePath), "bundled-themes.json");
49147
+ }
49148
+ function getOnboardingArtifactSentinelPath(storagePath) {
49149
+ return import_node_path3.default.join(getStateDir(storagePath), "onboarding-artifact.json");
49150
+ }
49151
+ function getAgentArtifactsDir(storagePath) {
49152
+ return import_node_path3.default.join(...[storagePath, "artifacts"]);
49153
+ }
49154
+ function getArtifactLiveMetadataPath(storagePath, artifactID) {
49155
+ return import_node_path3.default.join(getArtifactsMetadataDir(storagePath), `${artifactID}.json`);
49156
+ }
49157
+
49158
+ // ../server/src/themes.ts
49159
+ var import_node_crypto2 = require("node:crypto");
49160
+ var import_node_fs3 = require("node:fs");
49161
+ var import_node_path4 = __toESM(require("node:path"), 1);
49162
+ var ETAG_DIGEST_LENGTH = 16;
49163
+ var HTTP_NO_CONTENT_STATUS = 204;
49164
+ var HTTP_NOT_FOUND_STATUS = 404;
49165
+ var HTTP_NOT_MODIFIED_STATUS = 304;
49166
+ var THEME_MANIFEST_FILE = "manifest.json";
49167
+ var THEME_ENTRY_FILE = "theme.css";
49168
+ var THEME_SCRIPT_ENTRIES = [
49169
+ { declaration: "enableMainJS", filename: "main.js", requiresConsent: true },
49170
+ {
49171
+ declaration: "enableIframeBackgroundJS",
49172
+ filename: "iframe-background.js",
49173
+ requiresConsent: false
49174
+ },
49175
+ {
49176
+ declaration: "enableIframeOverlayJS",
49177
+ filename: "iframe-overlay.js",
49178
+ requiresConsent: false
49179
+ }
49180
+ ];
49181
+ var THEME_AUTHORING_CONTEXT_FILE = "README.md";
49182
+ var SEMVER_NUMERIC_IDENTIFIER = "(?:0|[1-9]\\d*)";
49183
+ var SEMVER_NON_NUMERIC_IDENTIFIER = "(?:\\d*[A-Za-z-][0-9A-Za-z-]*)";
49184
+ var SEMVER_PRERELEASE_IDENTIFIER = `(?:${SEMVER_NUMERIC_IDENTIFIER}|${SEMVER_NON_NUMERIC_IDENTIFIER})`;
49185
+ var SEMVER_BUILD_IDENTIFIER = "[0-9A-Za-z-]+";
49186
+ var SEMVER_PATTERN = new RegExp(
49187
+ `^${SEMVER_NUMERIC_IDENTIFIER}\\.${SEMVER_NUMERIC_IDENTIFIER}\\.${SEMVER_NUMERIC_IDENTIFIER}(?:-${SEMVER_PRERELEASE_IDENTIFIER}(?:\\.${SEMVER_PRERELEASE_IDENTIFIER})*)?(?:\\+${SEMVER_BUILD_IDENTIFIER}(?:\\.${SEMVER_BUILD_IDENTIFIER})*)?$`
49188
+ );
49189
+ function isSemanticVersion(value) {
49190
+ return SEMVER_PATTERN.test(value);
49191
+ }
49192
+ function compareSemanticVersions(left, right) {
49193
+ const leftVersion = parseSemanticVersion(left);
49194
+ const rightVersion = parseSemanticVersion(right);
49195
+ if (leftVersion === null || rightVersion === null) {
49196
+ throw new Error("Semantic Version comparison requires two valid versions");
49197
+ }
49198
+ for (let index = 0; index < leftVersion.core.length; index += 1) {
49199
+ const leftPart = leftVersion.core[index];
49200
+ const rightPart = rightVersion.core[index];
49201
+ if (leftPart < rightPart) return -1;
49202
+ if (leftPart > rightPart) return 1;
49203
+ }
49204
+ if (leftVersion.prerelease === null) return rightVersion.prerelease === null ? 0 : 1;
49205
+ if (rightVersion.prerelease === null) return -1;
49206
+ const identifierCount = Math.max(
49207
+ leftVersion.prerelease.length,
49208
+ rightVersion.prerelease.length
49209
+ );
49210
+ for (let index = 0; index < identifierCount; index += 1) {
49211
+ const leftIdentifier = leftVersion.prerelease[index];
49212
+ const rightIdentifier = rightVersion.prerelease[index];
49213
+ if (leftIdentifier === void 0) return -1;
49214
+ if (rightIdentifier === void 0) return 1;
49215
+ if (leftIdentifier === rightIdentifier) continue;
49216
+ const leftNumeric = /^\d+$/.test(leftIdentifier);
49217
+ const rightNumeric = /^\d+$/.test(rightIdentifier);
49218
+ if (leftNumeric && rightNumeric) {
49219
+ return BigInt(leftIdentifier) < BigInt(rightIdentifier) ? -1 : 1;
49220
+ }
49221
+ if (leftNumeric) return -1;
49222
+ if (rightNumeric) return 1;
49223
+ return leftIdentifier < rightIdentifier ? -1 : 1;
49224
+ }
49225
+ return 0;
49226
+ }
49227
+ function parseSemanticVersion(value) {
49228
+ if (!isSemanticVersion(value)) return null;
49229
+ const withoutBuild = value.split("+", 1)[0];
49230
+ const prereleaseSeparator = withoutBuild.indexOf("-");
49231
+ const coreText = prereleaseSeparator === -1 ? withoutBuild : withoutBuild.slice(0, prereleaseSeparator);
49232
+ const prerelease = prereleaseSeparator === -1 ? null : withoutBuild.slice(prereleaseSeparator + 1).split(".");
49233
+ const [major, minor, patch] = coreText.split(".").map((part) => BigInt(part));
49234
+ return {
49235
+ core: [major, minor, patch],
49236
+ prerelease
49237
+ };
49238
+ }
49239
+ function getThemesDir(storagePath) {
49240
+ return import_node_path4.default.join(storagePath, "themes");
49241
+ }
49242
+ function getThemeDir(storagePath, themeID) {
49243
+ return import_node_path4.default.join(getThemesDir(storagePath), themeID);
49244
+ }
49245
+ function normalizeThemeColorScheme(value) {
49246
+ if (typeof value !== "string") return null;
49247
+ const words = new Set(value.trim().toLowerCase().split(/\s+/));
49248
+ if (words.size === 1 && words.has("light")) return "light";
49249
+ if (words.size === 1 && words.has("dark")) return "dark";
49250
+ if (words.size === 2 && words.has("light") && words.has("dark")) {
49251
+ return "light dark";
49252
+ }
49253
+ return null;
49254
+ }
49255
+ function validateThemePackage(themeDir, themeID) {
49256
+ const errors = [];
49257
+ let manifest;
49258
+ let parsed;
49259
+ try {
49260
+ const raw = (0, import_node_fs3.readFileSync)(import_node_path4.default.join(themeDir, THEME_MANIFEST_FILE), "utf8");
49261
+ try {
49262
+ parsed = JSON.parse(raw);
49263
+ } catch {
49264
+ errors.push(`cannot parse ${THEME_MANIFEST_FILE}`);
49265
+ }
49266
+ } catch {
49267
+ errors.push(`cannot read ${THEME_MANIFEST_FILE}`);
49268
+ }
49269
+ if (parsed !== void 0) {
49270
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
49271
+ errors.push(`${THEME_MANIFEST_FILE} must contain a JSON object`);
49272
+ } else {
49273
+ const candidate = parsed;
49274
+ const nameValid = typeof candidate.name === "string" && candidate.name.trim().length > 0;
49275
+ if (!nameValid) {
49276
+ errors.push("manifest must have a non-empty name");
49277
+ }
49278
+ const versionValid = typeof candidate.version === "string" && isSemanticVersion(candidate.version);
49279
+ if (!versionValid) {
49280
+ errors.push("manifest version must be a valid Semantic Version");
49281
+ }
49282
+ const colorScheme = normalizeThemeColorScheme(candidate.colorScheme);
49283
+ if (colorScheme === null) {
49284
+ errors.push(
49285
+ "manifest colorScheme must be one of: light, dark, light dark"
49286
+ );
49287
+ }
49288
+ const authoredForAppVersion = typeof candidate.authoredForAppVersion === "string" && isSemanticVersion(candidate.authoredForAppVersion) ? candidate.authoredForAppVersion : void 0;
49289
+ const javascriptDeclarations = {};
49290
+ let javascriptDeclarationsValid = true;
49291
+ for (const { declaration, filename } of THEME_SCRIPT_ENTRIES) {
49292
+ if (!Object.hasOwn(candidate, declaration)) continue;
49293
+ if (typeof candidate[declaration] !== "boolean") {
49294
+ errors.push(`manifest ${declaration} must be a boolean`);
49295
+ javascriptDeclarationsValid = false;
49296
+ continue;
49297
+ }
49298
+ javascriptDeclarations[declaration] = candidate[declaration];
49299
+ validateReadableRegularFile(themeDir, filename, errors);
49300
+ }
49301
+ if (nameValid && versionValid && colorScheme !== null && javascriptDeclarationsValid) {
49302
+ manifest = {
49303
+ name: candidate.name,
49304
+ version: candidate.version,
49305
+ colorScheme,
49306
+ ...authoredForAppVersion === void 0 ? {} : { authoredForAppVersion },
49307
+ ...javascriptDeclarations
49308
+ };
49309
+ }
49310
+ }
49311
+ }
49312
+ validateReadableRegularFile(themeDir, THEME_ENTRY_FILE, errors);
49313
+ return errors.length === 0 && manifest !== void 0 ? { theme: { id: themeID, ...manifest }, errors: [] } : { errors };
49314
+ }
49315
+ function validateReadableRegularFile(themeDir, filename, errors) {
49316
+ try {
49317
+ const entryPath = import_node_path4.default.join(themeDir, filename);
49318
+ if (!(0, import_node_fs3.statSync)(entryPath).isFile()) {
49319
+ errors.push(`${filename} must be a readable regular file`);
49320
+ } else {
49321
+ (0, import_node_fs3.accessSync)(entryPath, import_node_fs3.constants.R_OK);
49322
+ }
49323
+ } catch {
49324
+ errors.push(`${filename} must be a readable regular file`);
49325
+ }
49326
+ }
49327
+ function scanThemesDirectory(themesDir) {
49328
+ let entries;
49329
+ try {
49330
+ entries = (0, import_node_fs3.readdirSync)(themesDir, { withFileTypes: true });
49331
+ } catch {
49332
+ return {
49333
+ themes: [],
49334
+ errors: [{ folder: null, error: "cannot read themes directory" }]
49335
+ };
49336
+ }
49337
+ const themes = [];
49338
+ const errors = [];
49339
+ for (const entry of entries) {
49340
+ if (!entry.isDirectory() || entry.name.startsWith(".")) {
49341
+ continue;
49342
+ }
49343
+ const result = validateThemePackage(import_node_path4.default.join(themesDir, entry.name), entry.name);
49344
+ if (result.theme !== void 0) {
49345
+ themes.push(result.theme);
49346
+ } else {
49347
+ errors.push({ folder: entry.name, error: result.errors.join("; ") });
49348
+ }
49349
+ }
49350
+ themes.sort(
49351
+ (left, right) => left.name.localeCompare(right.name, "en") || left.id.localeCompare(right.id, "en")
49352
+ );
49353
+ errors.sort((left, right) => (left.folder ?? "").localeCompare(right.folder ?? "", "en"));
49354
+ return { themes, errors };
49355
+ }
49356
+ function serveActiveTheme(store) {
49357
+ return (req, res, next) => {
49358
+ res.setHeader("Access-Control-Allow-Origin", "*");
49359
+ res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
49360
+ if (req.method === "OPTIONS") {
49361
+ res.status(HTTP_NO_CONTENT_STATUS).end();
49362
+ return;
49363
+ }
49364
+ if (req.method !== "GET" && req.method !== "HEAD") {
49365
+ next();
49366
+ return;
49367
+ }
49368
+ res.setHeader("Cache-Control", "no-cache");
49369
+ const relativePath = requestedThemePath(req.path);
49370
+ if (relativePath === null) {
49371
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
49372
+ return;
49373
+ }
49374
+ const activeThemeID = store.getActiveThemeName();
49375
+ const theme = activeThemeID === null ? void 0 : store.getThemeRegistry().themes.find((candidate) => candidate.id === activeThemeID);
49376
+ if (theme === void 0) {
49377
+ sendMissingThemeResponse(relativePath, res);
49378
+ return;
49379
+ }
49380
+ const scriptEntry = getThemeScriptEntry(relativePath);
49381
+ if (scriptEntry !== void 0 && (theme[scriptEntry.declaration] !== true || scriptEntry.requiresConsent && !store.hasThemeJavaScriptConsent(theme.id))) {
49382
+ sendEmptyThemeEntryResponse(scriptEntry.filename, theme, res);
49383
+ return;
49384
+ }
49385
+ try {
49386
+ const themesRoot = (0, import_node_fs3.realpathSync)(getThemesDir(store.storagePath));
49387
+ const packageRoot = (0, import_node_fs3.realpathSync)(getThemeDir(store.storagePath, theme.id));
49388
+ if (!isContainedPath(themesRoot, packageRoot)) {
49389
+ sendMissingThemeResponse(relativePath, res, theme);
49390
+ return;
49391
+ }
49392
+ if (scriptEntry === void 0) {
49393
+ const entryPath = (0, import_node_fs3.realpathSync)(import_node_path4.default.join(packageRoot, THEME_ENTRY_FILE));
49394
+ if (!isContainedPath(packageRoot, entryPath) || !(0, import_node_fs3.statSync)(entryPath).isFile()) {
49395
+ sendMissingThemeResponse(relativePath, res, theme);
49396
+ return;
49397
+ }
49398
+ }
49399
+ const candidate = import_node_path4.default.resolve(packageRoot, relativePath);
49400
+ if (!isContainedPath(packageRoot, candidate) || isThemeAuthoringContextPath(packageRoot, candidate)) {
49401
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
49402
+ return;
49403
+ }
49404
+ const realCandidate = (0, import_node_fs3.realpathSync)(candidate);
49405
+ if (!isContainedPath(packageRoot, realCandidate)) {
49406
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
49407
+ return;
49408
+ }
49409
+ if (!(0, import_node_fs3.statSync)(realCandidate).isFile()) {
49410
+ sendMissingThemeResponse(relativePath, res, theme);
49411
+ return;
49412
+ }
49413
+ const bytes = (0, import_node_fs3.readFileSync)(realCandidate);
49414
+ const etag = themeETag(theme.id, theme.version, bytes);
49415
+ res.type(import_node_path4.default.extname(relativePath));
49416
+ res.setHeader("ETag", etag);
49417
+ if (matchesIfNoneMatch(req.headers["if-none-match"], etag)) {
49418
+ res.status(HTTP_NOT_MODIFIED_STATUS).end();
49419
+ return;
49420
+ }
49421
+ res.send(bytes);
49422
+ } catch {
49423
+ sendMissingThemeResponse(relativePath, res, theme);
49424
+ }
49425
+ };
49426
+ }
49427
+ function requestedThemePath(requestPath) {
49428
+ try {
49429
+ const decoded = decodeURIComponent(requestPath).replace(/^\/+/, "");
49430
+ if (decoded === "" || decoded.includes("\0") || import_node_path4.default.isAbsolute(decoded)) {
49431
+ return null;
49432
+ }
49433
+ return decoded;
49434
+ } catch {
49435
+ return null;
49436
+ }
49437
+ }
49438
+ function isContainedPath(root, candidate) {
49439
+ return candidate === root || candidate.startsWith(`${root}${import_node_path4.default.sep}`);
49440
+ }
49441
+ function isThemeAuthoringContextPath(packageRoot, candidate) {
49442
+ return import_node_path4.default.relative(packageRoot, candidate).toLowerCase() === THEME_AUTHORING_CONTEXT_FILE.toLowerCase();
49443
+ }
49444
+ function isThemeEntryPath(relativePath, filename) {
49445
+ return import_node_path4.default.normalize(relativePath) === filename;
49446
+ }
49447
+ function getThemeScriptEntry(relativePath) {
49448
+ return THEME_SCRIPT_ENTRIES.find(
49449
+ ({ filename }) => isThemeEntryPath(relativePath, filename)
49450
+ );
49451
+ }
49452
+ function sendMissingThemeResponse(relativePath, res, theme) {
49453
+ if (isThemeEntryPath(relativePath, THEME_ENTRY_FILE)) {
49454
+ sendEmptyThemeEntryResponse(THEME_ENTRY_FILE, theme, res);
49455
+ return;
49456
+ }
49457
+ const scriptEntry = getThemeScriptEntry(relativePath);
49458
+ if (scriptEntry !== void 0) {
49459
+ sendEmptyThemeEntryResponse(scriptEntry.filename, theme, res);
49460
+ return;
49461
+ }
49462
+ res.sendStatus(HTTP_NOT_FOUND_STATUS);
49463
+ }
49464
+ function sendEmptyThemeEntryResponse(filename, theme, res) {
49465
+ const bytes = Buffer.alloc(0);
49466
+ const etag = themeETag(theme?.id ?? "default", theme?.version ?? "internal", bytes);
49467
+ res.type(filename === THEME_ENTRY_FILE ? "text/css" : "text/javascript");
49468
+ res.setHeader("ETag", etag);
49469
+ res.send(bytes);
49470
+ }
49471
+ function themeETag(themeID, version2, bytes) {
49472
+ const digest = (0, import_node_crypto2.createHash)("sha256").update(themeID).update("\0").update(version2).update("\0").update(bytes).digest("hex").slice(0, ETAG_DIGEST_LENGTH);
49473
+ return `W/"theme-${digest}"`;
49474
+ }
49475
+ function matchesIfNoneMatch(header, etag) {
49476
+ if (header === void 0) return false;
49477
+ const values = Array.isArray(header) ? header : [header];
49478
+ return values.some(
49479
+ (value) => value.split(",").map((candidate) => candidate.trim()).some((candidate) => candidate === "*" || candidate === etag)
49480
+ );
49481
+ }
49482
+
49483
+ // ../server/src/bundled-theme-installer.ts
49484
+ var JSON_INDENT_SPACES = 2;
49485
+ var BACKUP_TIMESTAMP_LENGTH = 19;
49486
+ var BUNDLED_THEME_STATE_VERSION = 2;
49487
+ var DEFAULT_BUNDLED_THEME_ID = "clouds";
49488
+ function getBundledThemeStateTempPath(storagePath) {
49489
+ const statePath = getBundledThemesStatePath(storagePath);
49490
+ return import_node_path5.default.join(import_node_path5.default.dirname(statePath), `.${import_node_path5.default.basename(statePath)}.tmp-${process.pid}`);
49491
+ }
49492
+ function getBundledThemeCopyTempPath(storagePath, themeID) {
49493
+ return import_node_path5.default.join(getThemesDir(storagePath), `.${themeID}.bundled-theme.tmp-${process.pid}`);
49494
+ }
49495
+ function getBundledThemeBackupTempPath(storagePath, themeID) {
49496
+ return import_node_path5.default.join(getThemesDir(storagePath), `.${themeID}.bundled-theme.backup-tmp-${process.pid}`);
49497
+ }
49498
+ function getBundledThemePreviousPath(storagePath, themeID) {
49499
+ return import_node_path5.default.join(getThemesDir(storagePath), `.${themeID}.bundled-theme.previous-${process.pid}`);
49500
+ }
49501
+ function readBundledThemeStateFile(storagePath) {
49502
+ const statePath = getBundledThemesStatePath(storagePath);
49503
+ if (!(0, import_node_fs4.existsSync)(statePath)) return { status: "absent" };
49504
+ let parsed;
49505
+ try {
49506
+ parsed = JSON.parse((0, import_node_fs4.readFileSync)(statePath, "utf8"));
49507
+ } catch (error48) {
49508
+ return { status: "invalid", error: describeError(error48) };
49509
+ }
49510
+ if (isBundledThemeState(parsed)) return { status: "ok", state: parsed };
49511
+ return {
49512
+ status: "invalid",
49513
+ error: "expected version 2 bundled-theme state with unique sorted theme IDs"
49514
+ };
49515
+ }
49516
+ function writeBundledThemeStateFile(storagePath, state) {
49517
+ const statePath = getBundledThemesStatePath(storagePath);
49518
+ const tempPath = getBundledThemeStateTempPath(storagePath);
49519
+ try {
49520
+ (0, import_node_fs4.writeFileSync)(tempPath, `${JSON.stringify(state, null, JSON_INDENT_SPACES)}
49521
+ `);
49522
+ (0, import_node_fs4.renameSync)(tempPath, statePath);
49523
+ } catch (error48) {
49524
+ try {
49525
+ (0, import_node_fs4.rmSync)(tempPath, { recursive: true, force: true });
49526
+ } catch {
49527
+ }
49528
+ throw error48;
49529
+ }
49530
+ }
49531
+ function runBundledThemeInstallation(storagePath, bundledThemesPath) {
49532
+ if (bundledThemesPath === void 0) return null;
49533
+ const stateRead = readBundledThemeStateFile(storagePath);
49534
+ if (stateRead.status === "invalid") {
49535
+ console.warn(
49536
+ "Bundled theme state is invalid; skipping bundled theme installation for this boot.",
49537
+ stateRead.error
49538
+ );
49539
+ return null;
49540
+ }
49541
+ let state = stateRead.status === "ok" ? stateRead.state : {
49542
+ version: BUNDLED_THEME_STATE_VERSION,
49543
+ installedThemeIDs: [],
49544
+ initialThemeSelectionComplete: false
49545
+ };
49546
+ const recoveryFailures = recoverInterruptedReplacements(storagePath);
49547
+ for (const { id, minimumVersion } of BUNDLED_THEMES) {
49548
+ if (recoveryFailures.has(id)) continue;
49549
+ const destinationPath = getThemeDir(storagePath, id);
49550
+ let handled = false;
49551
+ if (!pathExists(destinationPath)) {
49552
+ if (state.installedThemeIDs.includes(id)) continue;
49553
+ handled = installBundledTheme(storagePath, bundledThemesPath, id);
49554
+ } else if (installedThemeMeetsMinimum(destinationPath, id, minimumVersion)) {
49555
+ handled = true;
49556
+ } else {
49557
+ handled = replaceBundledTheme(storagePath, bundledThemesPath, id);
49558
+ }
49559
+ if (!handled || state.installedThemeIDs.includes(id)) continue;
49560
+ const nextState = {
49561
+ ...state,
49562
+ installedThemeIDs: [...state.installedThemeIDs, id].sort((left, right) => left.localeCompare(right, "en"))
49563
+ };
49564
+ try {
49565
+ writeBundledThemeStateFile(storagePath, nextState);
49566
+ state = nextState;
49567
+ } catch (error48) {
49568
+ console.warn(
49569
+ `Failed to record bundled theme ${JSON.stringify(id)}; it will retry on the next boot.`,
49570
+ error48
49571
+ );
49572
+ }
49573
+ }
49574
+ return state;
49575
+ }
49576
+ function installedThemeMeetsMinimum(destinationPath, themeID, minimumVersion) {
49577
+ try {
49578
+ if (!(0, import_node_fs4.lstatSync)(destinationPath).isDirectory()) return false;
49579
+ } catch {
49580
+ return false;
49581
+ }
49582
+ const result = validateThemePackage(destinationPath, themeID);
49583
+ return result.theme !== void 0 && compareSemanticVersions(result.theme.version, minimumVersion) >= 0;
49584
+ }
49585
+ function installBundledTheme(storagePath, bundledThemesPath, themeID) {
49586
+ const destinationPath = getThemeDir(storagePath, themeID);
49587
+ const tempPath = getBundledThemeCopyTempPath(storagePath, themeID);
49588
+ try {
49589
+ copyFresh(import_node_path5.default.join(bundledThemesPath, themeID), tempPath);
49590
+ if (pathExists(destinationPath)) {
49591
+ (0, import_node_fs4.rmSync)(tempPath, { recursive: true, force: true });
49592
+ return false;
49593
+ }
49594
+ (0, import_node_fs4.renameSync)(tempPath, destinationPath);
49595
+ return true;
49596
+ } catch (error48) {
49597
+ removeAfterFailure(tempPath);
49598
+ console.warn(
49599
+ `Failed to install bundled theme ${JSON.stringify(themeID)}; it will retry on the next boot.`,
49600
+ error48
49601
+ );
49602
+ return false;
49603
+ }
49604
+ }
49605
+ function replaceBundledTheme(storagePath, bundledThemesPath, themeID) {
49606
+ const destinationPath = getThemeDir(storagePath, themeID);
49607
+ const backupTempPath = getBundledThemeBackupTempPath(storagePath, themeID);
49608
+ try {
49609
+ copyFresh(destinationPath, backupTempPath);
49610
+ (0, import_node_fs4.renameSync)(backupTempPath, unusedBackupPath(storagePath, themeID));
49611
+ } catch (error48) {
49612
+ removeAfterFailure(backupTempPath);
49613
+ console.warn(
49614
+ `Failed to back up bundled theme ${JSON.stringify(themeID)}; it will retry on the next boot.`,
49615
+ error48
49616
+ );
49617
+ return false;
49618
+ }
49619
+ const replacementTempPath = getBundledThemeCopyTempPath(storagePath, themeID);
49620
+ const previousPath = getBundledThemePreviousPath(storagePath, themeID);
49621
+ let previousMoved = false;
49622
+ let replacementInstalled = false;
49623
+ try {
49624
+ copyFresh(import_node_path5.default.join(bundledThemesPath, themeID), replacementTempPath);
49625
+ (0, import_node_fs4.renameSync)(destinationPath, previousPath);
49626
+ previousMoved = true;
49627
+ (0, import_node_fs4.renameSync)(replacementTempPath, destinationPath);
49628
+ replacementInstalled = true;
49629
+ } catch (error48) {
49630
+ removeAfterFailure(replacementTempPath);
49631
+ if (previousMoved && !replacementInstalled && !pathExists(destinationPath)) {
49632
+ try {
49633
+ (0, import_node_fs4.renameSync)(previousPath, destinationPath);
49634
+ } catch (rollbackError) {
49635
+ console.warn(
49636
+ `Failed to restore bundled theme ${JSON.stringify(themeID)} after replacement failed.`,
49637
+ rollbackError
49638
+ );
49639
+ }
49640
+ }
49641
+ console.warn(
49642
+ `Failed to replace bundled theme ${JSON.stringify(themeID)}; it will retry on the next boot.`,
49643
+ error48
49644
+ );
49645
+ return false;
49646
+ }
49647
+ try {
49648
+ (0, import_node_fs4.rmSync)(previousPath, { recursive: true, force: true });
49649
+ } catch (error48) {
49650
+ console.warn(
49651
+ `Bundled theme ${JSON.stringify(themeID)} was replaced, but its hidden prior copy could not be removed.`,
49652
+ error48
49653
+ );
49654
+ }
49655
+ return true;
49656
+ }
49657
+ function copyFresh(sourcePath, destinationPath) {
49658
+ (0, import_node_fs4.cpSync)(sourcePath, destinationPath, {
49659
+ recursive: true,
49660
+ errorOnExist: true,
49661
+ force: false,
49662
+ verbatimSymlinks: true
49663
+ });
49664
+ }
49665
+ function unusedBackupPath(storagePath, themeID) {
49666
+ const initialTime = Date.now();
49667
+ for (let offsetSeconds = 0; ; offsetSeconds += 1) {
49668
+ const timestamp = new Date(initialTime + offsetSeconds * 1e3).toISOString().slice(0, BACKUP_TIMESTAMP_LENGTH).replace("T", "-").replaceAll(":", "-");
49669
+ const candidate = import_node_path5.default.join(getThemesDir(storagePath), `.${themeID}.backup.${timestamp}`);
49670
+ if (!pathExists(candidate)) return candidate;
49671
+ }
49672
+ }
49673
+ function recoverInterruptedReplacements(storagePath) {
49674
+ const failed = /* @__PURE__ */ new Set();
49675
+ const themesPath = getThemesDir(storagePath);
49676
+ const entries = (0, import_node_fs4.readdirSync)(themesPath, { withFileTypes: true });
49677
+ for (const { id } of BUNDLED_THEMES) {
49678
+ const previousPrefix = `.${id}.bundled-theme.previous-`;
49679
+ const previousNames = entries.map(({ name }) => name).filter((name) => name.startsWith(previousPrefix) && /^\d+$/.test(name.slice(previousPrefix.length))).sort();
49680
+ const destinationPath = getThemeDir(storagePath, id);
49681
+ if (!pathExists(destinationPath) && previousNames.length > 0) {
49682
+ const previousPath = import_node_path5.default.join(themesPath, previousNames[0]);
49683
+ try {
49684
+ (0, import_node_fs4.renameSync)(previousPath, destinationPath);
49685
+ } catch (error48) {
49686
+ failed.add(id);
49687
+ console.warn(
49688
+ `Failed to recover interrupted replacement of bundled theme ${JSON.stringify(id)}; it will retry on the next boot.`,
49689
+ error48
49690
+ );
49691
+ }
49692
+ }
49693
+ if (pathExists(destinationPath)) {
49694
+ for (const previousName of previousNames) {
49695
+ const previousPath = import_node_path5.default.join(themesPath, previousName);
49696
+ if (!pathExists(previousPath)) continue;
49697
+ try {
49698
+ (0, import_node_fs4.rmSync)(previousPath, { recursive: true, force: true });
49699
+ } catch (error48) {
49700
+ console.warn(
49701
+ `Failed to remove an interrupted replacement copy for bundled theme ${JSON.stringify(id)}.`,
49702
+ error48
49703
+ );
49704
+ }
49705
+ }
49706
+ }
49707
+ }
49708
+ for (const entry of entries) {
49709
+ const match = /^\.(.+)\.bundled-theme\.(?:tmp|backup-tmp)-(\d+)$/.exec(entry.name);
49710
+ if (match === null || !BUNDLED_THEME_IDS.includes(match[1])) continue;
49711
+ if (Number(match[2]) === process.pid && !entry.isDirectory()) continue;
49712
+ try {
49713
+ (0, import_node_fs4.rmSync)(import_node_path5.default.join(themesPath, entry.name), { recursive: true, force: true });
49714
+ } catch (error48) {
49715
+ console.warn(`Failed to remove bundled theme copy temporary ${JSON.stringify(entry.name)}.`, error48);
49716
+ }
49717
+ }
49718
+ return failed;
49719
+ }
49720
+ function removeAfterFailure(candidatePath) {
49721
+ try {
49722
+ (0, import_node_fs4.rmSync)(candidatePath, { recursive: true, force: true });
49723
+ } catch {
49724
+ }
49725
+ }
49726
+ function pathExists(candidatePath) {
49727
+ try {
49728
+ (0, import_node_fs4.lstatSync)(candidatePath);
49729
+ return true;
49730
+ } catch {
49731
+ return false;
49732
+ }
49733
+ }
49734
+ function hasValidThemeIDs(value) {
49735
+ if (!Array.isArray(value) || !value.every((themeID) => typeof themeID === "string")) {
49736
+ return false;
49737
+ }
49738
+ const sorted = [...value].sort((left, right) => left.localeCompare(right, "en"));
49739
+ return sorted.length === new Set(sorted).size && sorted.every((themeID, index) => themeID === value[index]);
49740
+ }
49741
+ function isBundledThemeState(value) {
49742
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
49743
+ const candidate = value;
49744
+ return Object.keys(candidate).sort().join(",") === "initialThemeSelectionComplete,installedThemeIDs,version" && candidate.version === BUNDLED_THEME_STATE_VERSION && typeof candidate.initialThemeSelectionComplete === "boolean" && hasValidThemeIDs(candidate.installedThemeIDs);
49745
+ }
49746
+ function describeError(error48) {
49747
+ return error48 instanceof Error ? error48.message : String(error48);
49748
+ }
49749
+
49088
49750
  // ../server/src/telemetry/types.ts
49089
49751
  var telemetryVersionBrand = Symbol("telemetryVersion");
49090
49752
  function telemetryVersion(value) {
49091
49753
  return value;
49092
49754
  }
49755
+ var THEME_STATES = ["none", ...BUNDLED_THEME_IDS, "custom"];
49093
49756
  var AGENT_TYPES = [
49094
49757
  "pi",
49095
49758
  "opencode",
@@ -49121,12 +49784,30 @@ function deriveArtifactProperties(artifact) {
49121
49784
  url_is_artifact_proxy: isTvArtifact(artifact.url)
49122
49785
  };
49123
49786
  }
49787
+ var BUNDLED_THEME_ID_SET = new Set(BUNDLED_THEME_IDS);
49788
+ function classifyThemeState(activeThemeName) {
49789
+ if (activeThemeName === null) return "none";
49790
+ if (BUNDLED_THEME_ID_SET.has(activeThemeName)) return activeThemeName;
49791
+ return "custom";
49792
+ }
49124
49793
  function deriveThemeProperties(input) {
49125
49794
  return {
49126
- theme_state: input.activeThemeName === null ? "default" : "custom",
49795
+ theme_state: classifyThemeState(input.activeThemeName),
49127
49796
  theme_count: input.themeCount
49128
49797
  };
49129
49798
  }
49799
+ function deriveThemeSettingsProperties(input) {
49800
+ const theme = input.themes.find((candidate) => candidate.id === input.activeThemeName);
49801
+ const mainDeclared = theme?.enableMainJS === true;
49802
+ return {
49803
+ theme_state: classifyThemeState(input.activeThemeName),
49804
+ theme_main_js_declared: mainDeclared,
49805
+ theme_main_js_enabled: mainDeclared && input.activeThemeName !== null && input.themeJavaScriptConsentIds.includes(input.activeThemeName),
49806
+ theme_iframe_background_enabled: theme?.enableIframeBackgroundJS === true,
49807
+ theme_iframe_overlay_enabled: theme?.enableIframeOverlayJS === true,
49808
+ appearance_mode: input.appearanceMode
49809
+ };
49810
+ }
49130
49811
  var KNOWN_AGENT_TYPES = AGENT_TYPES.filter((agentType) => agentType !== "dot-agents" && agentType !== "interactive-install" && agentType !== "other");
49131
49812
  var IPV4_VERSION = 4;
49132
49813
  var TAILNET_FIRST_OCTET = 100;
@@ -49305,10 +49986,10 @@ function telemetryContextFromMeta(value) {
49305
49986
  }
49306
49987
 
49307
49988
  // ../server/src/artifact-proxy.ts
49308
- var import_node_crypto2 = require("node:crypto");
49989
+ var import_node_crypto3 = require("node:crypto");
49309
49990
  var import_promises2 = require("node:fs/promises");
49310
- var import_node_fs3 = __toESM(require("node:fs"), 1);
49311
- var import_node_path3 = __toESM(require("node:path"), 1);
49991
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
49992
+ var import_node_path6 = __toESM(require("node:path"), 1);
49312
49993
  var import_send = __toESM(require_send(), 1);
49313
49994
 
49314
49995
  // ../../node_modules/marked/lib/marked.esm.js
@@ -50519,6 +51200,35 @@ var Kt = g.parseInline;
50519
51200
  var Xt = b.parse;
50520
51201
  var Jt = x.lex;
50521
51202
 
51203
+ // ../artifact/src/browser/appearance-resolver.ts
51204
+ function installAppearanceResolver(initialPreference, targetWindow = window) {
51205
+ const existing = targetWindow.__televisionAppearanceResolver;
51206
+ if (existing !== void 0) return existing;
51207
+ const query = targetWindow.matchMedia("(prefers-color-scheme: dark)");
51208
+ let preference = initialPreference;
51209
+ const isPreference = (value) => value === "system" || value === "light" || value === "dark";
51210
+ const apply = () => {
51211
+ const effective = preference === "system" ? query.matches ? "dark" : "light" : preference;
51212
+ targetWindow.document.documentElement.dataset.theme = effective;
51213
+ };
51214
+ const controller = {
51215
+ setPreference(nextPreference) {
51216
+ if (!isPreference(nextPreference)) return;
51217
+ preference = nextPreference;
51218
+ apply();
51219
+ }
51220
+ };
51221
+ targetWindow.__televisionAppearanceResolver = controller;
51222
+ query.addEventListener("change", () => {
51223
+ if (preference === "system") apply();
51224
+ });
51225
+ apply();
51226
+ return controller;
51227
+ }
51228
+ function appearanceResolverScriptSource(preferenceExpression) {
51229
+ return `(${installAppearanceResolver.toString()})(${preferenceExpression});`;
51230
+ }
51231
+
50522
51232
  // ../utils/src/disposable.ts
50523
51233
  function wrapDispose(target, isDisposed, setDisposed) {
50524
51234
  const disposeImpl = target.dispose.bind(target);
@@ -50606,6 +51316,27 @@ function installBridge(win = window, options = {}) {
50606
51316
  keyboard.preventDefault();
50607
51317
  win.parent.postMessage({ type: "navigation-key", key: keyboard.key }, "*");
50608
51318
  }, true);
51319
+ const pointerEventTypes = [
51320
+ "pointermove",
51321
+ "pointerdown",
51322
+ "pointerup",
51323
+ "pointercancel",
51324
+ "click"
51325
+ ];
51326
+ for (const eventType of pointerEventTypes) {
51327
+ win.addEventListener(eventType, (event) => {
51328
+ const pointer = event;
51329
+ if (!pointer.isTrusted) return;
51330
+ win.parent.postMessage({
51331
+ type: "artifact-pointer",
51332
+ eventType,
51333
+ clientX: pointer.clientX,
51334
+ clientY: pointer.clientY,
51335
+ button: pointer.button,
51336
+ buttons: pointer.buttons
51337
+ }, "*");
51338
+ }, true);
51339
+ }
50609
51340
  const capturedURL = win.location.href;
50610
51341
  const isTvArtifactURL = (url2) => {
50611
51342
  try {
@@ -50841,7 +51572,7 @@ function installBridge(win = window, options = {}) {
50841
51572
  postBridgeReady();
50842
51573
  }
50843
51574
  function bridgeScriptSource(options = {}) {
50844
- return `(function(){const __name=(target)=>target;(${installBridge.toString()})(window,${JSON.stringify(options)});})();`;
51575
+ return `(function(){const __name=(target)=>target;(${installAppearanceResolver.toString()})("system",window);(${installBridge.toString()})(window,${JSON.stringify(options)});})();`;
50845
51576
  }
50846
51577
 
50847
51578
  // ../server/src/markdown-doc-style.ts
@@ -50938,7 +51669,7 @@ li::before {
50938
51669
  width: var(--md-gutter);
50939
51670
  margin-left: calc(-1 * var(--md-gutter));
50940
51671
  text-align: center;
50941
- color: rgba(0, 0, 0, 0.4);
51672
+ color: var(--color-text-muted);
50942
51673
  }
50943
51674
 
50944
51675
  ol > li {
@@ -50990,8 +51721,8 @@ pre code {
50990
51721
 
50991
51722
  /* Blockquote: editor's grey border + muted text, with its indent. */
50992
51723
  blockquote {
50993
- border-left: 3px solid rgba(0, 0, 0, 0.2);
50994
- color: rgba(0, 0, 0, 0.55);
51724
+ border-left: 3px solid var(--color-border);
51725
+ color: var(--color-text-muted);
50995
51726
  padding-left: 1rem;
50996
51727
  margin-left: 0.25rem;
50997
51728
  }
@@ -51076,7 +51807,7 @@ function sendArtifactNotFound(req, res, store) {
51076
51807
  return;
51077
51808
  }
51078
51809
  try {
51079
- const body = await (0, import_promises2.readFile)(import_node_path3.default.join(viewPath, "index.html"));
51810
+ const body = await (0, import_promises2.readFile)(import_node_path6.default.join(viewPath, "index.html"));
51080
51811
  applyProxyHeaders(res);
51081
51812
  res.status(HTTP_NOT_FOUND).type("text/html; charset=utf-8");
51082
51813
  res.setHeader("Content-Length", String(body.byteLength));
@@ -51100,30 +51831,68 @@ function isHTMLResponse(res) {
51100
51831
  const value = Array.isArray(contentType) ? contentType.join("; ") : String(contentType ?? "");
51101
51832
  return value.toLowerCase().startsWith("text/html");
51102
51833
  }
51103
- function injectedETag(stat, body, bridgeSource) {
51104
- const hash2 = (0, import_node_crypto2.createHash)("sha256");
51834
+ function injectedETag(stat, body, bridgeSource, appearanceSource = "") {
51835
+ const hash2 = (0, import_node_crypto3.createHash)("sha256");
51105
51836
  hash2.update(String(stat.size));
51106
51837
  hash2.update("\0");
51107
51838
  hash2.update(String(stat.mtimeMs));
51108
51839
  hash2.update("\0");
51109
51840
  hash2.update(bridgeSource);
51110
51841
  hash2.update("\0");
51842
+ hash2.update(appearanceSource);
51843
+ hash2.update("\0");
51111
51844
  hash2.update(body);
51112
51845
  return `"${hash2.digest("base64url")}"`;
51113
51846
  }
51114
- function appendBridge(body, bridgeSource) {
51115
- return Buffer.concat([body, Buffer.from(`
51847
+ function appearanceScript(source) {
51848
+ return `<script>
51849
+ ${source}
51850
+ </script>`;
51851
+ }
51852
+ function injectAppearance(source, appearanceSource) {
51853
+ const script = appearanceScript(appearanceSource);
51854
+ const head = /<head\b[^>]*>/i.exec(source);
51855
+ if (head) {
51856
+ const afterHead = source.slice(head.index + head[0].length);
51857
+ const headEnd = /<\/head\s*>/i.exec(afterHead);
51858
+ const searchEnd2 = headEnd === null ? source.length : head.index + head[0].length + headEnd.index;
51859
+ const headContent = source.slice(head.index + head[0].length, searchEnd2);
51860
+ let insertion2 = head.index + head[0].length;
51861
+ for (const match of headContent.matchAll(/<meta\b[^>]*>/gi)) {
51862
+ if (/\bhttp-equiv\s*=\s*["']?Content-Security-Policy["']?/i.test(match[0])) {
51863
+ insertion2 = head.index + head[0].length + match.index + match[0].length;
51864
+ }
51865
+ }
51866
+ return `${source.slice(0, insertion2)}
51867
+ ${script}${source.slice(insertion2)}`;
51868
+ }
51869
+ const firstStyleOrBody = /<(?:link\b[^>]*rel\s*=\s*["']?stylesheet|style\b|body\b)/i.exec(source);
51870
+ const searchEnd = firstStyleOrBody?.index ?? source.length;
51871
+ const prefix = source.slice(0, searchEnd);
51872
+ const doctype = /<!doctype\b[^>]*>/i.exec(prefix);
51873
+ let insertion = doctype === null ? 0 : doctype.index + doctype[0].length;
51874
+ for (const match of prefix.matchAll(/<meta\b[^>]*>/gi)) {
51875
+ if (/\bhttp-equiv\s*=\s*["']?Content-Security-Policy["']?/i.test(match[0])) {
51876
+ insertion = match.index + match[0].length;
51877
+ }
51878
+ }
51879
+ return `${source.slice(0, insertion)}
51880
+ ${script}${source.slice(insertion)}`;
51881
+ }
51882
+ function injectDocument(body, appearanceSource, bridgeSource) {
51883
+ const early = injectAppearance(body.toString("utf8"), appearanceSource);
51884
+ return Buffer.from(`${early}
51116
51885
  <script>
51117
51886
  ${bridgeSource}
51118
51887
  </script>
51119
- `)]);
51888
+ `);
51120
51889
  }
51121
- function sendInjectedHTML(req, res, stream, resolvedPath, stat, bridgeSource) {
51890
+ function sendInjectedHTML(req, res, stream, resolvedPath, stat, appearanceSource, bridgeSource) {
51122
51891
  void (async () => {
51123
51892
  try {
51124
51893
  const source = await (0, import_promises2.readFile)(resolvedPath);
51125
- const body = appendBridge(source, bridgeSource);
51126
- const etag = injectedETag(stat, body, bridgeSource);
51894
+ const body = injectDocument(source, appearanceSource, bridgeSource);
51895
+ const etag = injectedETag(stat, body, bridgeSource, appearanceSource);
51127
51896
  stream.setHeader(resolvedPath, stat);
51128
51897
  stream.type(resolvedPath);
51129
51898
  res.setHeader("Cache-Control", HTML_CACHE_CONTROL);
@@ -51149,7 +51918,7 @@ function markdownDocument(renderedHTML) {
51149
51918
  <head>
51150
51919
  <meta charset="utf-8">
51151
51920
  <meta name="viewport" content="width=device-width, initial-scale=1">
51152
- <link rel="stylesheet" href="/canonical/v1/styles.css">
51921
+ <link rel="stylesheet" href="/canonical/v2/styles.css">
51153
51922
  <style>${MARKDOWN_DOC_CSS}</style>
51154
51923
  </head>
51155
51924
  <body>
@@ -51157,11 +51926,16 @@ ${renderedHTML}
51157
51926
  </body>
51158
51927
  </html>`;
51159
51928
  }
51160
- function sendRenderedMarkdown(req, res, resolvedPath, stat, bridgeSource) {
51929
+ function sendRenderedMarkdown(req, res, resolvedPath, stat, appearanceSource, bridgeSource) {
51161
51930
  void (async () => {
51162
51931
  try {
51163
51932
  const source = await (0, import_promises2.readFile)(resolvedPath, "utf8");
51164
- const etag = injectedETag(stat, Buffer.from(source + markdownDocument("")), bridgeSource);
51933
+ const etag = injectedETag(
51934
+ stat,
51935
+ Buffer.from(source + markdownDocument("")),
51936
+ bridgeSource,
51937
+ appearanceSource
51938
+ );
51165
51939
  applyProxyHeaders(res);
51166
51940
  res.status(HTTP_OK2).type("text/html; charset=utf-8");
51167
51941
  res.setHeader("Cache-Control", HTML_CACHE_CONTROL);
@@ -51175,7 +51949,11 @@ function sendRenderedMarkdown(req, res, resolvedPath, stat, bridgeSource) {
51175
51949
  return;
51176
51950
  }
51177
51951
  const rendered = g.parse(source, { async: false });
51178
- const body = appendBridge(Buffer.from(markdownDocument(rendered)), bridgeSource);
51952
+ const body = injectDocument(
51953
+ Buffer.from(markdownDocument(rendered)),
51954
+ appearanceSource,
51955
+ bridgeSource
51956
+ );
51179
51957
  res.setHeader("Content-Length", String(body.byteLength));
51180
51958
  res.end(body);
51181
51959
  } catch {
@@ -51183,7 +51961,7 @@ function sendRenderedMarkdown(req, res, resolvedPath, stat, bridgeSource) {
51183
51961
  }
51184
51962
  })();
51185
51963
  }
51186
- function pipeWithInjection(req, res, subpath, options, bridgeSource, notFound) {
51964
+ function pipeWithInjection(req, res, subpath, options, appearanceSource, bridgeSource, notFound) {
51187
51965
  applyProxyHeaders(res);
51188
51966
  const stream = (0, import_send.default)(req, subpath, { ...options, etag: true });
51189
51967
  const sendOriginal = stream.send.bind(stream);
@@ -51200,7 +51978,7 @@ function pipeWithInjection(req, res, subpath, options, bridgeSource, notFound) {
51200
51978
  return;
51201
51979
  }
51202
51980
  res.removeHeader("ETag");
51203
- sendInjectedHTML(req, res, stream, resolvedPath, stat, bridgeSource);
51981
+ sendInjectedHTML(req, res, stream, resolvedPath, stat, appearanceSource, bridgeSource);
51204
51982
  };
51205
51983
  stream.on("error", (error48) => {
51206
51984
  if ((error48.status ?? HTTP_NOT_FOUND) === HTTP_NOT_FOUND && notFound) {
@@ -51212,18 +51990,18 @@ function pipeWithInjection(req, res, subpath, options, bridgeSource, notFound) {
51212
51990
  });
51213
51991
  stream.pipe(res);
51214
51992
  }
51215
- function serveDirectoryArtifact(req, res, store, artifact, subpath, bridgeSource) {
51993
+ function serveDirectoryArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource) {
51216
51994
  if (subpath === "") {
51217
51995
  applyProxyHeaders(res);
51218
51996
  res.redirect(HTTP_MOVED_PERMANENTLY, `${proxyMountPath(artifact.id)}/`);
51219
51997
  return;
51220
51998
  }
51221
- pipeWithInjection(req, res, subpath, { root: artifact.path, index: ["index.html", "index.htm"], dotfiles: "allow" }, bridgeSource, subpath === "/" ? () => sendArtifactNotFound(req, res, store) : void 0);
51999
+ pipeWithInjection(req, res, subpath, { root: artifact.path, index: ["index.html", "index.htm"], dotfiles: "allow" }, appearanceSource, bridgeSource, subpath === "/" ? () => sendArtifactNotFound(req, res, store) : void 0);
51222
52000
  }
51223
- function serveFileArtifact(req, res, store, artifact, subpath, bridgeSource) {
52001
+ function serveFileArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource) {
51224
52002
  let stat;
51225
52003
  try {
51226
- stat = import_node_fs3.default.statSync(artifact.path);
52004
+ stat = import_node_fs5.default.statSync(artifact.path);
51227
52005
  } catch {
51228
52006
  const basename2 = artifactBasename(artifact.path);
51229
52007
  if (subpath === `/${encodeURIComponent(basename2)}`) sendArtifactNotFound(req, res, store);
@@ -51246,14 +52024,14 @@ function serveFileArtifact(req, res, store, artifact, subpath, bridgeSource) {
51246
52024
  sendNotFound(res);
51247
52025
  return;
51248
52026
  }
51249
- pipeWithInjection(req, res, subpath, { root: import_node_path3.default.dirname(artifact.path), index: false, dotfiles: "allow" }, bridgeSource, () => sendArtifactNotFound(req, res, store));
52027
+ pipeWithInjection(req, res, subpath, { root: import_node_path6.default.dirname(artifact.path), index: false, dotfiles: "allow" }, appearanceSource, bridgeSource, () => sendArtifactNotFound(req, res, store));
51250
52028
  }
51251
- function serveMarkdownArtifact(req, res, store, artifact, subpath, bridgeSource) {
52029
+ function serveMarkdownArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource) {
51252
52030
  const basename = artifactBasename(artifact.path);
51253
52031
  const canonical = proxyFilePath(artifact.id, basename);
51254
52032
  let stat;
51255
52033
  try {
51256
- stat = import_node_fs3.default.statSync(artifact.path);
52034
+ stat = import_node_fs5.default.statSync(artifact.path);
51257
52035
  } catch {
51258
52036
  if (subpath === `/${encodeURIComponent(basename)}`) sendArtifactNotFound(req, res, store);
51259
52037
  else sendNotFound(res);
@@ -51273,9 +52051,10 @@ function serveMarkdownArtifact(req, res, store, artifact, subpath, bridgeSource)
51273
52051
  sendNotFound(res);
51274
52052
  return;
51275
52053
  }
51276
- sendRenderedMarkdown(req, res, artifact.path, stat, bridgeSource);
52054
+ sendRenderedMarkdown(req, res, artifact.path, stat, appearanceSource, bridgeSource);
51277
52055
  }
51278
52056
  function serveArtifactProxy(store, options = {}) {
52057
+ const appearanceSource = appearanceResolverScriptSource('"system"');
51279
52058
  const bridgeSource = bridgeScriptSource({ pollCadence: options.pollCadence });
51280
52059
  return (req, res) => {
51281
52060
  if (req.method !== "GET" && req.method !== "HEAD") {
@@ -51293,15 +52072,15 @@ function serveArtifactProxy(store, options = {}) {
51293
52072
  sendNotFound(res);
51294
52073
  return;
51295
52074
  }
51296
- if (isMarkdownPath(artifact.path)) serveMarkdownArtifact(req, res, store, artifact, subpath, bridgeSource);
51297
- else if (hasTrailingSeparator(artifact.path)) serveDirectoryArtifact(req, res, store, artifact, subpath, bridgeSource);
51298
- else serveFileArtifact(req, res, store, artifact, subpath, bridgeSource);
52075
+ if (isMarkdownPath(artifact.path)) serveMarkdownArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource);
52076
+ else if (hasTrailingSeparator(artifact.path)) serveDirectoryArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource);
52077
+ else serveFileArtifact(req, res, store, artifact, subpath, appearanceSource, bridgeSource);
51299
52078
  };
51300
52079
  }
51301
52080
 
51302
52081
  // ../server/src/markdown.ts
51303
52082
  var fs2 = __toESM(require("node:fs/promises"), 1);
51304
- var import_node_fs4 = require("node:fs");
52083
+ var import_node_fs6 = require("node:fs");
51305
52084
  var import_express = __toESM(require_express2(), 1);
51306
52085
  var HTTP_NO_CONTENT = 204;
51307
52086
  var HTTP_NOT_FOUND2 = 404;
@@ -51347,7 +52126,7 @@ function serveMarkdownContent(store) {
51347
52126
  }
51348
52127
  const content = typeof req.body === "string" ? req.body : "";
51349
52128
  try {
51350
- await fs2.access(artifact.path, import_node_fs4.constants.F_OK);
52129
+ await fs2.access(artifact.path, import_node_fs6.constants.F_OK);
51351
52130
  await fs2.writeFile(artifact.path, content, "utf8");
51352
52131
  res.status(HTTP_NO_CONTENT).end();
51353
52132
  } catch (error48) {
@@ -51367,83 +52146,6 @@ function serveMarkdownContent(store) {
51367
52146
  };
51368
52147
  }
51369
52148
 
51370
- // ../server/src/themes.ts
51371
- var import_node_fs5 = require("node:fs");
51372
- var import_node_path4 = __toESM(require("node:path"), 1);
51373
- var HTTP_NOT_FOUND_STATUS = 404;
51374
- var THEME_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
51375
- var THEME_ENTRY_FILE = "theme.css";
51376
- var URL_RE = /url\(\s*(["']?)([^"')\s]+)\1\s*\)/g;
51377
- function isValidThemeName(name) {
51378
- return THEME_NAME_PATTERN.test(name);
51379
- }
51380
- function assertValidThemeName(name) {
51381
- if (!isValidThemeName(name)) {
51382
- throw new Error(`Invalid theme name: ${name}`);
51383
- }
51384
- }
51385
- function getThemesDir(storagePath) {
51386
- return import_node_path4.default.join(storagePath, "themes");
51387
- }
51388
- function getThemeDir(storagePath, name) {
51389
- assertValidThemeName(name);
51390
- return import_node_path4.default.join(getThemesDir(storagePath), name);
51391
- }
51392
- function getThemeEntryPath(storagePath, name) {
51393
- return import_node_path4.default.join(getThemeDir(storagePath, name), THEME_ENTRY_FILE);
51394
- }
51395
- function themeExists(storagePath, name) {
51396
- if (!isValidThemeName(name)) {
51397
- return false;
51398
- }
51399
- try {
51400
- return (0, import_node_fs5.statSync)(getThemeEntryPath(storagePath, name)).isFile();
51401
- } catch {
51402
- return false;
51403
- }
51404
- }
51405
- function listThemeNames(storagePath) {
51406
- const themesDir = getThemesDir(storagePath);
51407
- if (!(0, import_node_fs5.existsSync)(themesDir)) {
51408
- return [];
51409
- }
51410
- let entries;
51411
- try {
51412
- entries = (0, import_node_fs5.readdirSync)(themesDir);
51413
- } catch {
51414
- return [];
51415
- }
51416
- return entries.filter((name) => themeExists(storagePath, name)).sort((left, right) => left.localeCompare(right));
51417
- }
51418
- function rewriteThemeURLs(css, themeName) {
51419
- return css.replace(URL_RE, (whole, quote, url2) => {
51420
- if (/^(data:|https?:)/.test(url2) || url2.startsWith("/")) {
51421
- return whole;
51422
- }
51423
- const rewritten = `/themes/${themeName}/${url2}`;
51424
- return `url(${quote}${rewritten}${quote})`;
51425
- });
51426
- }
51427
- function serveThemeCSS(storagePath) {
51428
- return (req, res, next) => {
51429
- const { name } = req.params;
51430
- if (!isValidThemeName(name) || !themeExists(storagePath, name)) {
51431
- res.sendStatus(HTTP_NOT_FOUND_STATUS);
51432
- return;
51433
- }
51434
- try {
51435
- const css = (0, import_node_fs5.readFileSync)(getThemeEntryPath(storagePath, name), "utf8");
51436
- res.type("text/css").setHeader("Cache-Control", "no-cache").send(rewriteThemeURLs(css, name));
51437
- } catch (error48) {
51438
- if (error48.code === "ENOENT") {
51439
- res.sendStatus(HTTP_NOT_FOUND_STATUS);
51440
- return;
51441
- }
51442
- next(error48);
51443
- }
51444
- };
51445
- }
51446
-
51447
52149
  // ../server/src/routes.ts
51448
52150
  var patchChannelSchema = external_exports.object({
51449
52151
  name: external_exports.string().optional(),
@@ -51485,7 +52187,11 @@ var patchArtifactSchema = external_exports.object({
51485
52187
  var patchDisplaySchema = external_exports.object({
51486
52188
  focusedChannelId: external_exports.string().nullable().optional(),
51487
52189
  pinnedChannelIds: external_exports.array(external_exports.string()).optional(),
51488
- activeThemeName: external_exports.string().regex(THEME_NAME_PATTERN).nullable().optional()
52190
+ activeThemeName: external_exports.string().nullable().optional(),
52191
+ appearanceMode: external_exports.enum(["system", "light", "dark"]).optional(),
52192
+ themeJavaScriptConsentIds: external_exports.array(external_exports.string().min(1)).refine((ids) => new Set(ids).size === ids.length, {
52193
+ message: "themeJavaScriptConsentIds must not contain duplicate theme ids"
52194
+ }).optional()
51489
52195
  }).strict();
51490
52196
  var focusSchema = external_exports.object({
51491
52197
  artifactID: external_exports.string()
@@ -51563,8 +52269,8 @@ function registerRoutes(app, store, options) {
51563
52269
  app.options("/artifacts/:id", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51564
52270
  app.options("/display", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51565
52271
  app.options("/display/focus", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51566
- app.options("/themes/:name/theme.css", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51567
- app.options("/themes/:name/*", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
52272
+ app.options("/themes", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
52273
+ app.options("/themes/refresh", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51568
52274
  app.options("/markdown/:id", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51569
52275
  app.options("/telemetry", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
51570
52276
  app.options("/telemetry/enable", (_req, res) => res.status(HTTP_NO_CONTENT2).end());
@@ -51578,6 +52284,12 @@ function registerRoutes(app, store, options) {
51578
52284
  app.post("/telemetry/disable", ...auth, async (_req, res) => {
51579
52285
  res.json(await options.disableTelemetry?.() ?? { state: "unavailable", reason: null, guidPresent: false, region: "us" });
51580
52286
  });
52287
+ app.get("/themes", ...auth, (_req, res) => {
52288
+ res.json(store.getThemeRegistry());
52289
+ });
52290
+ app.post("/themes/refresh", ...auth, (req, res) => {
52291
+ res.json(store.refreshThemeRegistry(telemetryContext(req)));
52292
+ });
51581
52293
  app.get("/channels", ...auth, (_req, res) => {
51582
52294
  res.json({ channels: store.listChannels() });
51583
52295
  });
@@ -51742,11 +52454,11 @@ var import_websocket = __toESM(require_websocket(), 1);
51742
52454
  var import_websocket_server = __toESM(require_websocket_server(), 1);
51743
52455
 
51744
52456
  // ../server/src/auth.ts
51745
- var import_node_crypto3 = require("node:crypto");
52457
+ var import_node_crypto4 = require("node:crypto");
51746
52458
  var AUTH_TOKEN_BYTES = 32;
51747
52459
  var AUTH_PARSE_BASE_URL = "http://localhost";
51748
52460
  function createToken() {
51749
- return (0, import_node_crypto3.randomBytes)(AUTH_TOKEN_BYTES).toString("hex");
52461
+ return (0, import_node_crypto4.randomBytes)(AUTH_TOKEN_BYTES).toString("hex");
51750
52462
  }
51751
52463
  function isAuthorizedBearer(authorizationHeader, expectedToken) {
51752
52464
  if (typeof authorizationHeader !== "string" || !authorizationHeader.startsWith("Bearer ")) {
@@ -51908,7 +52620,15 @@ var EventStreamServer = class extends withDisposable(class {
51908
52620
  this.broadcast({ type: "pinned-channels-changed", pinnedChannelIds: event.pinnedChannelIds });
51909
52621
  };
51910
52622
  const onThemeChanged = (event) => {
51911
- this.broadcast({ type: "theme-changed", themeName: event.themeName });
52623
+ this.broadcast({
52624
+ type: "theme-changed",
52625
+ themeName: event.themeName,
52626
+ activeThemeColorScheme: event.activeThemeColorScheme,
52627
+ themeJavaScriptConsentIds: [...event.themeJavaScriptConsentIds]
52628
+ });
52629
+ };
52630
+ const onAppearanceChanged = (event) => {
52631
+ this.broadcast({ type: "appearance-changed", appearanceMode: event.appearanceMode });
51912
52632
  };
51913
52633
  const onArtifactFocus = (event) => {
51914
52634
  this.broadcast({ type: "artifact-focus", channelID: event.channelID, artifactID: event.artifactID });
@@ -51923,6 +52643,7 @@ var EventStreamServer = class extends withDisposable(class {
51923
52643
  this.store.addEventListener("channel-changed", onChannelChanged);
51924
52644
  this.store.addEventListener("pinned-channels-changed", onPinnedChannelsChanged);
51925
52645
  this.store.addEventListener("theme-changed", onThemeChanged);
52646
+ this.store.addEventListener("appearance-changed", onAppearanceChanged);
51926
52647
  this.store.addEventListener("artifact-focus", onArtifactFocus);
51927
52648
  this.unsubscribe.push(
51928
52649
  () => this.store.removeEventListener("artifact-created", onArtifactCreated),
@@ -51935,6 +52656,7 @@ var EventStreamServer = class extends withDisposable(class {
51935
52656
  () => this.store.removeEventListener("channel-changed", onChannelChanged),
51936
52657
  () => this.store.removeEventListener("pinned-channels-changed", onPinnedChannelsChanged),
51937
52658
  () => this.store.removeEventListener("theme-changed", onThemeChanged),
52659
+ () => this.store.removeEventListener("appearance-changed", onAppearanceChanged),
51938
52660
  () => this.store.removeEventListener("artifact-focus", onArtifactFocus)
51939
52661
  );
51940
52662
  }
@@ -51964,7 +52686,7 @@ function isTelemetryActivityMessage(parsed, expectedClientId) {
51964
52686
  var import_node_child_process2 = require("node:child_process");
51965
52687
  var import_promises3 = require("node:fs/promises");
51966
52688
  var import_node_os3 = require("node:os");
51967
- var import_node_path5 = __toESM(require("node:path"), 1);
52689
+ var import_node_path7 = __toESM(require("node:path"), 1);
51968
52690
  var launchACPProcess = (command, args, options) => {
51969
52691
  const child = (0, import_node_child_process2.spawn)(command, args, options);
51970
52692
  const directKill = child.kill.bind(child);
@@ -52007,7 +52729,7 @@ function trackChild(child) {
52007
52729
  child.once("exit", untrack);
52008
52730
  child.once("error", untrack);
52009
52731
  }
52010
- var ACP_STUB_CWD = import_node_path5.default.join((0, import_node_os3.homedir)(), ".television", "acp");
52732
+ var ACP_STUB_CWD = import_node_path7.default.join((0, import_node_os3.homedir)(), ".television", "acp");
52011
52733
  var ACPBridge = class extends withDisposable(class {
52012
52734
  }) {
52013
52735
  child = null;
@@ -52024,7 +52746,7 @@ var ACPBridge = class extends withDisposable(class {
52024
52746
  this.send = options.send;
52025
52747
  this.profile = options.profile;
52026
52748
  this.launchProcess = options.launchProcess ?? launchACPProcess;
52027
- this.sessionCwd = import_node_path5.default.resolve(process.cwd());
52749
+ this.sessionCwd = import_node_path7.default.resolve(process.cwd());
52028
52750
  }
52029
52751
  handleClientMessage(message) {
52030
52752
  switch (message.type) {
@@ -52260,87 +52982,63 @@ function splitListenValues(values) {
52260
52982
  }
52261
52983
 
52262
52984
  // ../server/src/canonical.ts
52263
- var import_node_crypto4 = require("node:crypto");
52264
- var import_node_fs6 = require("node:fs");
52265
- var import_node_path6 = __toESM(require("node:path"), 1);
52985
+ var import_node_crypto5 = require("node:crypto");
52986
+ var import_node_fs7 = require("node:fs");
52987
+ var import_node_path8 = __toESM(require("node:path"), 1);
52266
52988
  var CACHE_CONTROL_NO_CACHE = "no-cache";
52267
- var HTTP_NOT_MODIFIED_STATUS = 304;
52268
- var HASH_HEX_LENGTH = 8;
52269
- var NO_THEME_HASH = "none";
52989
+ var ETAG_DIGEST_LENGTH2 = 16;
52990
+ var HTTP_NOT_MODIFIED_STATUS2 = 304;
52270
52991
  var CANONICAL_STYLESHEET = "styles.css";
52271
- function serveCanonicalStyles(options) {
52272
- const bundle = loadCanonicalBundle(options.canonicalDir);
52273
- return (req, res, next) => {
52274
- try {
52275
- const response = buildCanonicalResponse(bundle, options.store);
52276
- res.type("text/css");
52277
- res.setHeader("Cache-Control", CACHE_CONTROL_NO_CACHE);
52278
- res.setHeader("ETag", response.etag);
52279
- if (matchesIfNoneMatch(req.headers["if-none-match"], response.etag)) {
52280
- res.status(HTTP_NOT_MODIFIED_STATUS).end();
52281
- return;
52282
- }
52283
- res.send(response.body);
52284
- } catch (error48) {
52285
- next(error48);
52992
+ function canonicalWrapper(version2, frozen = false, querySuffix = "") {
52993
+ const baseImport = `@import url("/canonical/${version2}/base.css${querySuffix}");
52994
+ `;
52995
+ if (frozen) return baseImport;
52996
+ return `${baseImport}@import url("/theme/theme.css${querySuffix}");
52997
+ `;
52998
+ }
52999
+ function serveCanonicalBase(canonicalDir) {
53000
+ const body = (0, import_node_fs7.readFileSync)(import_node_path8.default.join(canonicalDir, CANONICAL_STYLESHEET));
53001
+ const digest = (0, import_node_crypto5.createHash)("sha256").update(body).digest("hex").slice(0, ETAG_DIGEST_LENGTH2);
53002
+ const etag = `W/"canonical-base-${digest}"`;
53003
+ return (req, res) => {
53004
+ res.type("text/css");
53005
+ res.setHeader("Cache-Control", CACHE_CONTROL_NO_CACHE);
53006
+ res.setHeader("ETag", etag);
53007
+ if (matchesIfNoneMatch2(req.headers["if-none-match"], etag)) {
53008
+ res.status(HTTP_NOT_MODIFIED_STATUS2).end();
53009
+ return;
52286
53010
  }
53011
+ res.send(body);
52287
53012
  };
52288
53013
  }
52289
- function loadCanonicalBundle(canonicalDir) {
52290
- const css = (0, import_node_fs6.readFileSync)(import_node_path6.default.join(canonicalDir, CANONICAL_STYLESHEET), "utf8");
52291
- return { css, hash: hashContent(css) };
52292
- }
52293
- function buildCanonicalResponse(bundle, store) {
52294
- const activeThemeName = store.getActiveThemeName();
52295
- if (activeThemeName === null) {
52296
- return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
52297
- }
52298
- if (!themeExists(store.storagePath, activeThemeName)) {
52299
- logMissingActiveTheme(store.storagePath, activeThemeName);
52300
- return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
52301
- }
52302
- try {
52303
- const themeCSS = (0, import_node_fs6.readFileSync)(getThemeEntryPath(store.storagePath, activeThemeName), "utf8");
52304
- return {
52305
- body: `${bundle.css}
52306
-
52307
- /* active theme: ${activeThemeName} */
52308
- ${rewriteThemeURLs(themeCSS, activeThemeName)}`,
52309
- etag: buildETag(bundle.hash, hashContent(themeCSS))
52310
- };
52311
- } catch (error48) {
52312
- if (error48.code === "ENOENT") {
52313
- logMissingActiveTheme(store.storagePath, activeThemeName);
52314
- return { body: bundle.css, etag: buildETag(bundle.hash, NO_THEME_HASH) };
53014
+ function serveCanonicalStyles(version2, frozen = false) {
53015
+ return (req, res) => {
53016
+ const queryIndex = req.originalUrl.indexOf("?");
53017
+ const querySuffix = queryIndex === -1 ? "" : req.originalUrl.slice(queryIndex);
53018
+ const body = canonicalWrapper(version2, frozen, querySuffix);
53019
+ const digest = (0, import_node_crypto5.createHash)("sha256").update(body).digest("hex").slice(0, ETAG_DIGEST_LENGTH2);
53020
+ const etag = `W/"canonical-${version2}-${digest}"`;
53021
+ res.type("text/css");
53022
+ res.setHeader("Cache-Control", CACHE_CONTROL_NO_CACHE);
53023
+ res.setHeader("ETag", etag);
53024
+ if (matchesIfNoneMatch2(req.headers["if-none-match"], etag)) {
53025
+ res.status(HTTP_NOT_MODIFIED_STATUS2).end();
53026
+ return;
52315
53027
  }
52316
- throw error48;
52317
- }
52318
- }
52319
- function buildETag(bundleHash, themeHash) {
52320
- return `W/"v1-${bundleHash}-${themeHash}"`;
52321
- }
52322
- function hashContent(content) {
52323
- return (0, import_node_crypto4.createHash)("sha256").update(content).digest("hex").slice(0, HASH_HEX_LENGTH);
52324
- }
52325
- function matchesIfNoneMatch(header, etag) {
52326
- if (!header) {
52327
- return false;
52328
- }
52329
- const rawValues = Array.isArray(header) ? header : [header];
52330
- return rawValues.some(
52331
- (rawValue) => rawValue.split(",").map((value) => value.trim()).some((value) => value === "*" || value === etag)
52332
- );
53028
+ res.send(body);
53029
+ };
52333
53030
  }
52334
- function logMissingActiveTheme(storagePath, themeName) {
52335
- const location = isValidThemeName(themeName) ? getThemeEntryPath(storagePath, themeName) : `(invalid theme name: ${themeName})`;
52336
- console.warn(
52337
- `[canonical] Active theme "${themeName}" missing at ${location}; serving canonical bundle only.`
53031
+ function matchesIfNoneMatch2(header, etag) {
53032
+ if (header === void 0) return false;
53033
+ const values = Array.isArray(header) ? header : [header];
53034
+ return values.some(
53035
+ (value) => value.split(",").map((candidate) => candidate.trim()).some((candidate) => candidate === "*" || candidate === etag)
52338
53036
  );
52339
53037
  }
52340
53038
 
52341
53039
  // ../server/src/logger.ts
52342
- var import_node_fs7 = require("node:fs");
52343
- var import_node_path7 = __toESM(require("node:path"), 1);
53040
+ var import_node_fs8 = require("node:fs");
53041
+ var import_node_path9 = __toESM(require("node:path"), 1);
52344
53042
  var LOG_DIR_NAME = "logs";
52345
53043
  var ACTIVE_LOG_FILE = "tv.log";
52346
53044
  var LOG_RETENTION_DAYS = 14;
@@ -52356,9 +53054,9 @@ var ROTATION_LOCK_TIMEOUT_MS = 5e3;
52356
53054
  var INT32_BYTES = 4;
52357
53055
  function log(storagePath, msg, data) {
52358
53056
  try {
52359
- const logsDir = import_node_path7.default.join(storagePath, LOG_DIR_NAME);
52360
- (0, import_node_fs7.mkdirSync)(logsDir, { recursive: true });
52361
- const logPath = import_node_path7.default.join(logsDir, ACTIVE_LOG_FILE);
53057
+ const logsDir = import_node_path9.default.join(storagePath, LOG_DIR_NAME);
53058
+ (0, import_node_fs8.mkdirSync)(logsDir, { recursive: true });
53059
+ const logPath = import_node_path9.default.join(logsDir, ACTIVE_LOG_FILE);
52362
53060
  rotateIfStale(logsDir, logPath);
52363
53061
  const record2 = {
52364
53062
  ts: localTimestamp(/* @__PURE__ */ new Date()),
@@ -52366,7 +53064,7 @@ function log(storagePath, msg, data) {
52366
53064
  msg,
52367
53065
  ...serializeData(data)
52368
53066
  };
52369
- (0, import_node_fs7.appendFileSync)(logPath, `${JSON.stringify(record2)}
53067
+ (0, import_node_fs8.appendFileSync)(logPath, `${JSON.stringify(record2)}
52370
53068
  `, { encoding: "utf8" });
52371
53069
  } catch (error48) {
52372
53070
  const message = error48 instanceof Error ? error48.message : String(error48);
@@ -52389,21 +53087,21 @@ function serializeError(error48) {
52389
53087
  return { name: "NonError", message: String(error48) };
52390
53088
  }
52391
53089
  function rotateIfStale(logsDir, logPath) {
52392
- const lockPath = import_node_path7.default.join(logsDir, ROTATION_LOCK_DIR);
53090
+ const lockPath = import_node_path9.default.join(logsDir, ROTATION_LOCK_DIR);
52393
53091
  if (!acquireRotationLock(lockPath)) return;
52394
53092
  try {
52395
53093
  let stats;
52396
53094
  try {
52397
- stats = (0, import_node_fs7.statSync)(logPath);
53095
+ stats = (0, import_node_fs8.statSync)(logPath);
52398
53096
  } catch (error48) {
52399
53097
  if (error48.code === "ENOENT") return;
52400
53098
  throw error48;
52401
53099
  }
52402
53100
  if (!isPreviousLocalCalendarDay(stats.mtime, /* @__PURE__ */ new Date())) return;
52403
53101
  const archiveDate = localDateString(stats.mtime);
52404
- const archivePath = uniqueArchivePath(import_node_path7.default.join(logsDir, `${ACTIVE_LOG_FILE}.${archiveDate}`));
53102
+ const archivePath = uniqueArchivePath(import_node_path9.default.join(logsDir, `${ACTIVE_LOG_FILE}.${archiveDate}`));
52405
53103
  try {
52406
- (0, import_node_fs7.renameSync)(logPath, archivePath);
53104
+ (0, import_node_fs8.renameSync)(logPath, archivePath);
52407
53105
  } catch (error48) {
52408
53106
  if (error48.code === "ENOENT") return;
52409
53107
  throw error48;
@@ -52417,7 +53115,7 @@ function acquireRotationLock(lockPath) {
52417
53115
  const deadline = Date.now() + ROTATION_LOCK_TIMEOUT_MS;
52418
53116
  while (true) {
52419
53117
  try {
52420
- (0, import_node_fs7.mkdirSync)(lockPath);
53118
+ (0, import_node_fs8.mkdirSync)(lockPath);
52421
53119
  return true;
52422
53120
  } catch (error48) {
52423
53121
  if (error48.code !== "EEXIST") throw error48;
@@ -52428,7 +53126,7 @@ function acquireRotationLock(lockPath) {
52428
53126
  }
52429
53127
  function releaseRotationLock(lockPath) {
52430
53128
  try {
52431
- (0, import_node_fs7.rmdirSync)(lockPath);
53129
+ (0, import_node_fs8.rmdirSync)(lockPath);
52432
53130
  } catch {
52433
53131
  }
52434
53132
  }
@@ -52436,21 +53134,21 @@ function sleepSync(ms) {
52436
53134
  Atomics.wait(new Int32Array(new SharedArrayBuffer(INT32_BYTES)), 0, 0, ms);
52437
53135
  }
52438
53136
  function uniqueArchivePath(basePath) {
52439
- if (!(0, import_node_fs7.existsSync)(basePath)) return basePath;
53137
+ if (!(0, import_node_fs8.existsSync)(basePath)) return basePath;
52440
53138
  for (let index = 1; ; index += 1) {
52441
53139
  const candidate = `${basePath}-${index}`;
52442
- if (!(0, import_node_fs7.existsSync)(candidate)) return candidate;
53140
+ if (!(0, import_node_fs8.existsSync)(candidate)) return candidate;
52443
53141
  }
52444
53142
  }
52445
53143
  function removeExpiredArchives(logsDir, retainedArchivePath) {
52446
53144
  const cutoff = Date.now() - LOG_RETENTION_DAYS * MS_PER_DAY;
52447
- for (const entry of (0, import_node_fs7.readdirSync)(logsDir, { withFileTypes: true })) {
53145
+ for (const entry of (0, import_node_fs8.readdirSync)(logsDir, { withFileTypes: true })) {
52448
53146
  if (!entry.isFile() || !entry.name.startsWith(`${ACTIVE_LOG_FILE}.`)) continue;
52449
- const fullPath = import_node_path7.default.join(logsDir, entry.name);
53147
+ const fullPath = import_node_path9.default.join(logsDir, entry.name);
52450
53148
  if (fullPath === retainedArchivePath) continue;
52451
53149
  try {
52452
- if ((0, import_node_fs7.statSync)(fullPath).mtime.getTime() < cutoff) {
52453
- (0, import_node_fs7.rmSync)(fullPath, { force: true });
53150
+ if ((0, import_node_fs8.statSync)(fullPath).mtime.getTime() < cutoff) {
53151
+ (0, import_node_fs8.rmSync)(fullPath, { force: true });
52454
53152
  }
52455
53153
  } catch {
52456
53154
  }
@@ -52624,14 +53322,23 @@ function createServerStoreTelemetryHooks(store, capture2) {
52624
53322
  }
52625
53323
  });
52626
53324
  },
52627
- themeChanged(clientContext) {
53325
+ appearanceModeChanged(appearanceMode, clientContext) {
53326
+ safeEmit(() => capture2({
53327
+ name: "appearance_mode_changed",
53328
+ properties: { appearance_mode: appearanceMode }
53329
+ }, clientContext));
53330
+ },
53331
+ themeChanged(reason, clientContext) {
52628
53332
  safeEmit(() => {
52629
53333
  capture2({
52630
53334
  name: "theme_changed",
52631
- properties: deriveThemeProperties({
52632
- activeThemeName: store.getActiveThemeName(),
52633
- themeCount: listThemeNames(store.storagePath).length
52634
- })
53335
+ properties: {
53336
+ ...deriveThemeProperties({
53337
+ activeThemeName: store.getActiveThemeName(),
53338
+ themeCount: store.getThemeRegistry().themes.length
53339
+ }),
53340
+ theme_change_reason: reason
53341
+ }
52635
53342
  }, clientContext);
52636
53343
  });
52637
53344
  }
@@ -52693,50 +53400,9 @@ function safeEmit(callback) {
52693
53400
  }
52694
53401
 
52695
53402
  // ../server/src/telemetry/identity.ts
52696
- var import_node_crypto5 = require("node:crypto");
53403
+ var import_node_crypto6 = require("node:crypto");
52697
53404
  var import_promises4 = require("node:fs/promises");
52698
- var import_node_path9 = __toESM(require("node:path"), 1);
52699
-
52700
- // ../server/src/artifact-paths.ts
52701
- var import_node_path8 = __toESM(require("node:path"), 1);
52702
- function getStateDir(storagePath) {
52703
- return import_node_path8.default.join(storagePath, "state");
52704
- }
52705
- function getChannelsDir(storagePath) {
52706
- return import_node_path8.default.join(getStateDir(storagePath), "channels");
52707
- }
52708
- function getLegacyScreensDir(storagePath) {
52709
- return import_node_path8.default.join(getStateDir(storagePath), "screens");
52710
- }
52711
- function getArtifactsMetadataDir(storagePath) {
52712
- return import_node_path8.default.join(getStateDir(storagePath), "artifacts");
52713
- }
52714
- function getDisplayStatePath(storagePath) {
52715
- return import_node_path8.default.join(getStateDir(storagePath), "display.json");
52716
- }
52717
- function getDisplayMigrationTempPath(storagePath) {
52718
- return import_node_path8.default.join(getStateDir(storagePath), ".display.json.screen-to-channel.tmp");
52719
- }
52720
- function getTelemetryStatePath(storagePath) {
52721
- return import_node_path8.default.join(getStateDir(storagePath), "telemetry.json");
52722
- }
52723
- function getTokenPath(storagePath) {
52724
- return import_node_path8.default.join(getStateDir(storagePath), "token");
52725
- }
52726
- function getOnboardingStatePath(storagePath) {
52727
- return import_node_path8.default.join(getStateDir(storagePath), "onboarding.json");
52728
- }
52729
- function getOnboardingArtifactSentinelPath(storagePath) {
52730
- return import_node_path8.default.join(getStateDir(storagePath), "onboarding-artifact.json");
52731
- }
52732
- function getAgentArtifactsDir(storagePath) {
52733
- return import_node_path8.default.join(...[storagePath, "artifacts"]);
52734
- }
52735
- function getArtifactLiveMetadataPath(storagePath, artifactID) {
52736
- return import_node_path8.default.join(getArtifactsMetadataDir(storagePath), `${artifactID}.json`);
52737
- }
52738
-
52739
- // ../server/src/telemetry/identity.ts
53405
+ var import_node_path10 = __toESM(require("node:path"), 1);
52740
53406
  var TELEMETRY_STATE_SCHEMA_VERSION = 1;
52741
53407
  var SEMVER_MAJOR_CAPTURE_INDEX = 1;
52742
53408
  var SEMVER_MINOR_CAPTURE_INDEX = 2;
@@ -52753,7 +53419,7 @@ async function readTelemetryState(storagePath) {
52753
53419
  }
52754
53420
  async function writeTelemetryState(storagePath, state) {
52755
53421
  const statePath = getTelemetryStatePath(storagePath);
52756
- await (0, import_promises4.mkdir)(import_node_path9.default.dirname(statePath), { recursive: true });
53422
+ await (0, import_promises4.mkdir)(import_node_path10.default.dirname(statePath), { recursive: true });
52757
53423
  const tempPath = `${statePath}.${process.pid}.${Date.now()}.tmp`;
52758
53424
  await (0, import_promises4.writeFile)(tempPath, `${JSON.stringify(state, null, 2)}
52759
53425
  `, "utf8");
@@ -52764,7 +53430,7 @@ async function loadOrMintTelemetryState(storagePath, options = {}) {
52764
53430
  if (existing) return { state: existing, created: false };
52765
53431
  const state = {
52766
53432
  schemaVersion: TELEMETRY_STATE_SCHEMA_VERSION,
52767
- userId: options.userIdFactory?.() ?? (0, import_node_crypto5.randomUUID)(),
53433
+ userId: options.userIdFactory?.() ?? (0, import_node_crypto6.randomUUID)(),
52768
53434
  optedOut: false,
52769
53435
  lastVersion: options.lastVersion ?? ""
52770
53436
  };
@@ -52842,12 +53508,12 @@ function isProductionTelemetryBuild(env) {
52842
53508
  return telemetryBuildMarker(env) === "production";
52843
53509
  }
52844
53510
  function isTelemetryVersionOlder(previousVersion, runningVersion) {
52845
- const previous = parseSemanticVersion(previousVersion);
52846
- const running = parseSemanticVersion(runningVersion);
53511
+ const previous = parseSemanticVersion2(previousVersion);
53512
+ const running = parseSemanticVersion2(runningVersion);
52847
53513
  if (!previous || !running) return false;
52848
- return compareSemanticVersions(previous, running) < 0;
53514
+ return compareSemanticVersions2(previous, running) < 0;
52849
53515
  }
52850
- function parseSemanticVersion(value) {
53516
+ function parseSemanticVersion2(value) {
52851
53517
  const match = value.trim().match(/^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
52852
53518
  if (!match) return null;
52853
53519
  return {
@@ -52857,7 +53523,7 @@ function parseSemanticVersion(value) {
52857
53523
  prerelease: match[SEMVER_PRERELEASE_CAPTURE_INDEX]?.split(".") ?? []
52858
53524
  };
52859
53525
  }
52860
- function compareSemanticVersions(left, right) {
53526
+ function compareSemanticVersions2(left, right) {
52861
53527
  const core = compareNumbers(left.major, right.major) || compareNumbers(left.minor, right.minor) || compareNumbers(left.patch, right.patch);
52862
53528
  if (core !== 0) return core;
52863
53529
  return comparePrerelease(left.prerelease, right.prerelease);
@@ -52937,9 +53603,9 @@ function buildTelemetryEvent(event, context) {
52937
53603
  }
52938
53604
 
52939
53605
  // ../server/src/telemetry/posthog-config.ts
52940
- var import_node_fs8 = require("node:fs");
53606
+ var import_node_fs9 = require("node:fs");
52941
53607
  var import_node_os4 = __toESM(require("node:os"), 1);
52942
- var import_node_path10 = __toESM(require("node:path"), 1);
53608
+ var import_node_path11 = __toESM(require("node:path"), 1);
52943
53609
  var POSTHOG_US_INGESTION_HOST = "https://us.i.posthog.com";
52944
53610
  var POSTHOG_US_API_HOST = "https://us.posthog.com";
52945
53611
  var POSTHOG_PRODUCTION_PROJECT_ID = 482022;
@@ -52967,14 +53633,14 @@ function resolveTelemetryDeveloperHome(env = process.env) {
52967
53633
  }
52968
53634
  function detectTelemetryDeveloperHost(homeDir = import_node_os4.default.homedir()) {
52969
53635
  try {
52970
- return (0, import_node_fs8.existsSync)(import_node_path10.default.join(homeDir, ".tv-developer"));
53636
+ return (0, import_node_fs9.existsSync)(import_node_path11.default.join(homeDir, ".tv-developer"));
52971
53637
  } catch {
52972
53638
  return false;
52973
53639
  }
52974
53640
  }
52975
53641
 
52976
53642
  // ../server/src/telemetry/sessions.ts
52977
- var import_node_crypto6 = require("node:crypto");
53643
+ var import_node_crypto7 = require("node:crypto");
52978
53644
  var IDLE_TIMEOUT_MINUTES = 30;
52979
53645
  var MAX_SESSION_HOURS = 24;
52980
53646
  var SECONDS_PER_MINUTE2 = 60;
@@ -53001,7 +53667,7 @@ function createTelemetrySessionManager() {
53001
53667
  return new InMemoryTelemetrySessionManager();
53002
53668
  }
53003
53669
  function createUUIDv7(timestampMs) {
53004
- const bytes = (0, import_node_crypto6.randomBytes)(UUID_BYTE_COUNT);
53670
+ const bytes = (0, import_node_crypto7.randomBytes)(UUID_BYTE_COUNT);
53005
53671
  writeTimestamp(bytes, timestampMs);
53006
53672
  bytes[UUID_VERSION_BYTE_INDEX] = UUID_VERSION_7_HIGH_BITS | bytes[UUID_VERSION_BYTE_INDEX] & UUID_VERSION_LOW_MASK;
53007
53673
  bytes[UUID_VARIANT_BYTE_INDEX] = UUID_VARIANT_RFC_9562_HIGH_BITS | bytes[UUID_VARIANT_BYTE_INDEX] & UUID_VARIANT_LOW_MASK;
@@ -53206,10 +53872,20 @@ async function createTelemetryRuntime(options) {
53206
53872
  const sessionIdFor = (clientContext) => {
53207
53873
  return sessions.sessionIdFor(clientContext?.clientId ?? null, nowMs());
53208
53874
  };
53209
- const captureWithPendingPersonProperties = (event, sessionId) => {
53210
- const shouldAttachPendingProperties = pendingPersonProperties !== null && telemetryEnabled(env, currentState);
53211
- capture(shouldAttachPendingProperties ? { ...event, personProperties: { ...event.personProperties ?? {}, ...pendingPersonProperties } } : event, { state: currentState, env, sink, sessionId });
53212
- if (shouldAttachPendingProperties) pendingPersonProperties = null;
53875
+ const captureWithCurrentProperties = (event, sessionId) => {
53876
+ if (!telemetryEnabled(env, currentState)) return;
53877
+ try {
53878
+ const settings = options.readThemeSettings();
53879
+ const { appearance_mode, ...themeSettings } = settings;
53880
+ const settingsForEvent = event.name === "session_activity" ? settings : event.name === "theme_changed" ? themeSettings : event.name === "appearance_mode_changed" ? { appearance_mode } : {};
53881
+ capture({
53882
+ ...event,
53883
+ properties: { ...event.properties, ...settingsForEvent },
53884
+ personProperties: { ...pendingPersonProperties, ...event.personProperties, ...settings }
53885
+ }, { state: currentState, env, sink, sessionId });
53886
+ pendingPersonProperties = null;
53887
+ } catch {
53888
+ }
53213
53889
  };
53214
53890
  return {
53215
53891
  boot,
@@ -53222,10 +53898,10 @@ async function createTelemetryRuntime(options) {
53222
53898
  },
53223
53899
  async disable() {
53224
53900
  if (!currentState.optedOut) {
53225
- capture({
53901
+ captureWithCurrentProperties({
53226
53902
  name: "telemetry_opted_out",
53227
53903
  personProperties: { telemetry_opted_out: true }
53228
- }, { state: currentState, env, sink });
53904
+ });
53229
53905
  currentState = await setTelemetryOptedOut(options.storagePath, currentState, true);
53230
53906
  }
53231
53907
  return telemetryStatus(env, currentState);
@@ -53238,7 +53914,7 @@ async function createTelemetryRuntime(options) {
53238
53914
  return telemetryStatus(env, currentState);
53239
53915
  },
53240
53916
  capture(event, clientContext) {
53241
- captureWithPendingPersonProperties(event, sessionIdFor(clientContext));
53917
+ captureWithCurrentProperties(event, sessionIdFor(clientContext));
53242
53918
  },
53243
53919
  observeClientRequest(clientContext) {
53244
53920
  return sessionIdFor(clientContext);
@@ -53247,7 +53923,7 @@ async function createTelemetryRuntime(options) {
53247
53923
  if (!clientContext) return;
53248
53924
  const sessionId = sessionIdFor(clientContext);
53249
53925
  if (!sessionId) return;
53250
- captureWithPendingPersonProperties({
53926
+ captureWithCurrentProperties({
53251
53927
  name: "session_activity",
53252
53928
  properties: {
53253
53929
  server_version: options.version,
@@ -53294,7 +53970,7 @@ function disabledTelemetryRuntime() {
53294
53970
 
53295
53971
  // ../server/src/updates/version.ts
53296
53972
  function readVersionStamp() {
53297
- if ("0.1.216".length > 0) return "0.1.216";
53973
+ if ("1.3.3".length > 0) return "1.3.3";
53298
53974
  return void 0;
53299
53975
  }
53300
53976
  function resolveUpdateReleaseVersion(env = process.env, stamp = readVersionStamp()) {
@@ -53311,7 +53987,7 @@ function resolveRequiredDesktopVersion(baked, env = process.env, stamp = readVer
53311
53987
  }
53312
53988
 
53313
53989
  // ../server/src/required-desktop-version.ts
53314
- var REQUIRED_DESKTOP_VERSION = "0.1.207";
53990
+ var REQUIRED_DESKTOP_VERSION = "1.3.1";
53315
53991
 
53316
53992
  // ../server/src/updates/server-status.ts
53317
53993
  function buildServerStatus(env = process.env, stamp = readVersionStamp(), baked = REQUIRED_DESKTOP_VERSION, update = null) {
@@ -53461,6 +54137,28 @@ function parseIntervalOverride(value) {
53461
54137
  var import_meta = {};
53462
54138
  var HTTP_UNAUTHORIZED_STATUS = 401;
53463
54139
  var LOOPBACK_IPV4 = "127.0.0.1";
54140
+ var CANONICAL_VERSION_DIRECTORY_PATTERN = /^v\d+(?:\.\d+)?$/;
54141
+ function resolveCanonicalVersionDirectories(canonicalDir) {
54142
+ const canonicalRoot = import_node_path12.default.resolve(canonicalDir);
54143
+ const versions = (0, import_node_fs10.readdirSync)(canonicalRoot, { withFileTypes: true }).filter(
54144
+ (entry) => entry.isDirectory() && CANONICAL_VERSION_DIRECTORY_PATTERN.test(entry.name)
54145
+ ).map((entry) => {
54146
+ const directory = import_node_path12.default.join(canonicalRoot, entry.name);
54147
+ return {
54148
+ version: entry.name,
54149
+ directory,
54150
+ frozen: (0, import_node_fs10.existsSync)(import_node_path12.default.join(directory, "frozen.json"))
54151
+ };
54152
+ }).sort(
54153
+ (left, right) => left.version.localeCompare(right.version, void 0, { numeric: true })
54154
+ );
54155
+ if (versions.length === 0) {
54156
+ throw new Error(
54157
+ `canonical root ${canonicalRoot} contains no version directories matching v<n>`
54158
+ );
54159
+ }
54160
+ return versions;
54161
+ }
53464
54162
  var Server = class {
53465
54163
  app;
53466
54164
  httpServer;
@@ -53536,31 +54234,39 @@ var Server = class {
53536
54234
  testArtifactPollCadence: options.testArtifactPollCadence
53537
54235
  });
53538
54236
  if (options.canonicalDir) {
53539
- const canonicalDir = options.canonicalDir;
53540
- this.app.use("/canonical/v1", (_req, res, next) => {
53541
- res.setHeader("Access-Control-Allow-Origin", "*");
53542
- next();
53543
- });
53544
- this.app.get(
53545
- "/canonical/v1/styles.css",
53546
- serveCanonicalStyles({ canonicalDir, store: this.store })
53547
- );
53548
- this.app.use("/canonical/v1", import_express2.default.static(canonicalDir, {
53549
- setHeaders: (res, filePath) => {
53550
- if (filePath.startsWith(`${import_node_path11.default.resolve(canonicalDir, "fonts")}${import_node_path11.default.sep}`)) {
53551
- res.setHeader(
53552
- "Cache-Control",
53553
- "public, max-age=31536000, immutable"
53554
- );
53555
- } else {
53556
- res.setHeader("Cache-Control", "no-cache");
53557
- }
53558
- }
53559
- }));
54237
+ for (const { version: version2, directory, frozen } of resolveCanonicalVersionDirectories(
54238
+ options.canonicalDir
54239
+ )) {
54240
+ const mountPath = `/canonical/${version2}`;
54241
+ this.app.use(mountPath, (_req, res, next) => {
54242
+ res.setHeader("Access-Control-Allow-Origin", "*");
54243
+ next();
54244
+ });
54245
+ this.app.get(
54246
+ `${mountPath}/styles.css`,
54247
+ serveCanonicalStyles(version2, frozen)
54248
+ );
54249
+ this.app.get(`${mountPath}/base.css`, serveCanonicalBase(directory));
54250
+ this.app.use(
54251
+ mountPath,
54252
+ import_express2.default.static(directory, {
54253
+ setHeaders: (res, filePath) => {
54254
+ if (filePath.startsWith(
54255
+ `${import_node_path12.default.resolve(directory, "fonts")}${import_node_path12.default.sep}`
54256
+ )) {
54257
+ res.setHeader(
54258
+ "Cache-Control",
54259
+ "public, max-age=31536000, immutable"
54260
+ );
54261
+ } else {
54262
+ res.setHeader("Cache-Control", "no-cache");
54263
+ }
54264
+ }
54265
+ })
54266
+ );
54267
+ }
53560
54268
  }
53561
- const themesDir = getThemesDir(this.store.storagePath);
53562
- this.app.get("/themes/:name/theme.css", serveThemeCSS(this.store.storagePath));
53563
- this.app.use("/themes", import_express2.default.static(themesDir));
54269
+ this.app.use("/theme", serveActiveTheme(this.store));
53564
54270
  for (const viewID of ["artifact-missing", "markdown"]) {
53565
54271
  const viewPath = this.store.getViewPath(viewID);
53566
54272
  if (viewPath) {
@@ -53573,7 +54279,7 @@ var Server = class {
53573
54279
  if (filePath.endsWith(".html") || filePath.endsWith(".htm")) {
53574
54280
  res.setHeader("Referrer-Policy", "no-referrer");
53575
54281
  res.setHeader("Cache-Control", "no-cache");
53576
- } else if (filePath.includes(`${import_node_path11.default.sep}assets${import_node_path11.default.sep}`)) {
54282
+ } else if (filePath.includes(`${import_node_path12.default.sep}assets${import_node_path12.default.sep}`)) {
53577
54283
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
53578
54284
  }
53579
54285
  }
@@ -53739,6 +54445,10 @@ var Server = class {
53739
54445
  storagePath: this.store.storagePath,
53740
54446
  version: version2,
53741
54447
  dataDirCreated: this.store.dataDirCreated,
54448
+ readThemeSettings: () => deriveThemeSettingsProperties({
54449
+ ...this.store.getDisplayState(),
54450
+ themes: this.store.getThemeRegistry().themes
54451
+ }),
53742
54452
  ...this.telemetryOptions.env === void 0 ? {} : { env: this.telemetryOptions.env },
53743
54453
  ...this.telemetryOptions.sink === void 0 ? {} : { sink: this.telemetryOptions.sink },
53744
54454
  ...this.telemetryOptions.developerHost === void 0 ? {} : { developerHost: this.telemetryOptions.developerHost },
@@ -53806,43 +54516,46 @@ var Server = class {
53806
54516
  };
53807
54517
  };
53808
54518
  function readServerPackageVersion() {
53809
- if ("0.1.216".length > 0) {
53810
- return telemetryVersion("0.1.216");
54519
+ if ("1.3.3".length > 0) {
54520
+ return telemetryVersion("1.3.3");
53811
54521
  }
53812
- const packageJsonPath = import_node_path11.default.resolve(import_node_path11.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
53813
- if (!(0, import_node_fs9.existsSync)(packageJsonPath)) return telemetryVersion("0.0.0");
53814
- return telemetryVersion(JSON.parse((0, import_node_fs9.readFileSync)(packageJsonPath, "utf8")).version);
54522
+ const packageJsonPath = import_node_path12.default.resolve(import_node_path12.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)), "..", "package.json");
54523
+ if (!(0, import_node_fs10.existsSync)(packageJsonPath)) return telemetryVersion("0.0.0");
54524
+ return telemetryVersion(JSON.parse((0, import_node_fs10.readFileSync)(packageJsonPath, "utf8")).version);
53815
54525
  }
53816
54526
 
53817
54527
  // ../server/src/server-store.ts
53818
- var import_node_fs15 = require("node:fs");
53819
- var import_node_path17 = __toESM(require("node:path"), 1);
54528
+ var import_node_fs16 = require("node:fs");
54529
+ var import_node_path18 = __toESM(require("node:path"), 1);
53820
54530
 
53821
54531
  // ../server/src/file-watcher.ts
53822
54532
  var import_node_events = require("node:events");
53823
54533
  var fs3 = __toESM(require("node:fs"), 1);
53824
- var import_node_path12 = __toESM(require("node:path"), 1);
54534
+ var import_node_path13 = __toESM(require("node:path"), 1);
53825
54535
  var DEFAULT_REARM_INTERVAL_MS = 250;
53826
54536
  function isMissingWatchRootError(error48) {
53827
54537
  return typeof error48 === "object" && error48 !== null && "code" in error48 && error48.code === "ENOENT";
53828
54538
  }
53829
54539
  var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53830
54540
  #dir;
53831
- #watchedBasenames;
54541
+ #watchedBasename;
53832
54542
  #watchRootBasename;
53833
54543
  #onChange;
53834
54544
  #options;
54545
+ #recursive;
53835
54546
  #watcher = null;
53836
54547
  #ancestorWatchers = [];
53837
54548
  #closed = false;
53838
54549
  #dead = false;
53839
54550
  #rearmTimer = null;
53840
54551
  #rootEventImmediate = null;
53841
- constructor(filePath, onChange, basenames, options = {}) {
54552
+ #recursiveRefreshImmediate = null;
54553
+ constructor(contentPath, onChange, options = {}) {
53842
54554
  super();
53843
- this.#dir = basenames === void 0 ? import_node_path12.default.dirname(filePath) : filePath;
53844
- this.#watchedBasenames = new Set(basenames ?? [import_node_path12.default.basename(filePath)]);
53845
- this.#watchRootBasename = import_node_path12.default.basename(import_node_path12.default.normalize(this.#dir));
54555
+ this.#recursive = options.recursive === true;
54556
+ this.#dir = this.#recursive ? contentPath : import_node_path13.default.dirname(contentPath);
54557
+ this.#watchedBasename = import_node_path13.default.basename(contentPath);
54558
+ this.#watchRootBasename = import_node_path13.default.basename(import_node_path13.default.normalize(this.#dir));
53846
54559
  this.#onChange = onChange;
53847
54560
  this.#options = options;
53848
54561
  try {
@@ -53864,16 +54577,30 @@ var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53864
54577
  clearImmediate(this.#rootEventImmediate);
53865
54578
  this.#rootEventImmediate = null;
53866
54579
  }
54580
+ if (this.#recursiveRefreshImmediate) {
54581
+ clearImmediate(this.#recursiveRefreshImmediate);
54582
+ this.#recursiveRefreshImmediate = null;
54583
+ }
53867
54584
  this.#closeActiveWatchers();
53868
54585
  }
53869
54586
  #attach() {
53870
- const watcher = fs3.watch(this.#dir, (_event, filename) => {
53871
- const basename = filename?.toString();
53872
- if (basename !== void 0 && this.#watchedBasenames.has(basename)) {
54587
+ const watcher = fs3.watch(this.#dir, { recursive: this.#recursive }, (_event, filename) => {
54588
+ const relativePath = filename?.toString();
54589
+ const isWatchRootEvent = relativePath === void 0 || relativePath === "" || relativePath === this.#watchRootBasename;
54590
+ if (this.#recursive) {
54591
+ if (isWatchRootEvent) {
54592
+ this.#handleWatchRootEvent();
54593
+ } else {
54594
+ this.#onChange();
54595
+ this.#scheduleRecursiveRefresh();
54596
+ }
54597
+ return;
54598
+ }
54599
+ if (relativePath === this.#watchedBasename) {
53873
54600
  this.#onChange();
53874
54601
  return;
53875
54602
  }
53876
- if (basename === void 0 || basename === "" || basename === this.#watchRootBasename) {
54603
+ if (isWatchRootEvent) {
53877
54604
  this.#handleWatchRootEvent();
53878
54605
  }
53879
54606
  });
@@ -53886,7 +54613,7 @@ var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53886
54613
  });
53887
54614
  const ancestorWatchers = [];
53888
54615
  for (const ancestorDir of this.#ancestorDirs()) {
53889
- const ancestorBasename = import_node_path12.default.basename(import_node_path12.default.normalize(ancestorDir));
54616
+ const ancestorBasename = import_node_path13.default.basename(import_node_path13.default.normalize(ancestorDir));
53890
54617
  try {
53891
54618
  const ancestorWatcher = fs3.watch(ancestorDir, (_event, filename) => {
53892
54619
  const basename = filename?.toString();
@@ -53909,6 +54636,27 @@ var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53909
54636
  this.#ancestorWatchers = ancestorWatchers;
53910
54637
  this.#dead = false;
53911
54638
  }
54639
+ #scheduleRecursiveRefresh() {
54640
+ if (this.#recursiveRefreshImmediate) return;
54641
+ this.#recursiveRefreshImmediate = setImmediate(() => {
54642
+ this.#recursiveRefreshImmediate = null;
54643
+ if (this.#closed) return;
54644
+ if (!this.#isWatchRootPresent()) {
54645
+ this.#markDead();
54646
+ return;
54647
+ }
54648
+ this.#closeActiveWatchers();
54649
+ try {
54650
+ this.#attach();
54651
+ } catch (error48) {
54652
+ if (this.#isWatchRootPresent()) {
54653
+ this.emit("error", error48);
54654
+ return;
54655
+ }
54656
+ this.#markDead();
54657
+ }
54658
+ });
54659
+ }
53912
54660
  #handleWatchRootEvent() {
53913
54661
  if (this.#rootEventImmediate) return;
53914
54662
  this.#rootEventImmediate = setImmediate(() => {
@@ -53932,10 +54680,10 @@ var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53932
54680
  }
53933
54681
  #ancestorDirs() {
53934
54682
  const ancestors = [];
53935
- let current = import_node_path12.default.dirname(this.#dir);
53936
- while (current !== import_node_path12.default.dirname(current)) {
54683
+ let current = import_node_path13.default.dirname(this.#dir);
54684
+ while (current !== import_node_path13.default.dirname(current)) {
53937
54685
  ancestors.push(current);
53938
- current = import_node_path12.default.dirname(current);
54686
+ current = import_node_path13.default.dirname(current);
53939
54687
  }
53940
54688
  return ancestors;
53941
54689
  }
@@ -53987,13 +54735,13 @@ var ReArmingContentWatcher = class extends import_node_events.EventEmitter {
53987
54735
  }
53988
54736
  }
53989
54737
  };
53990
- var defaultWatchContentFile = (filePath, onChange, basenames, options) => {
53991
- return new ReArmingContentWatcher(filePath, onChange, basenames, options);
54738
+ var defaultWatchContentFile = (contentPath, onChange, options) => {
54739
+ return new ReArmingContentWatcher(contentPath, onChange, options);
53992
54740
  };
53993
54741
 
53994
54742
  // ../server/src/redesign-storage-migration.ts
53995
- var import_node_fs11 = require("node:fs");
53996
- var import_node_path13 = __toESM(require("node:path"), 1);
54743
+ var import_node_fs12 = require("node:fs");
54744
+ var import_node_path14 = __toESM(require("node:path"), 1);
53997
54745
 
53998
54746
  // ../server/src/redesign-record-migration.ts
53999
54747
  function convertStoredChannelRecord(record2) {
@@ -54068,8 +54816,8 @@ function newestChannelId(channelIds) {
54068
54816
  }
54069
54817
 
54070
54818
  // ../server/src/storage-name-migration.ts
54071
- var import_node_fs10 = require("node:fs");
54072
- var JSON_INDENT_SPACES = 2;
54819
+ var import_node_fs11 = require("node:fs");
54820
+ var JSON_INDENT_SPACES2 = 2;
54073
54821
  function isActiveID(value) {
54074
54822
  return typeof value === "string" || value === null;
54075
54823
  }
@@ -54079,23 +54827,23 @@ function isThemeName(value) {
54079
54827
  function migrateChannelMetadataDirectory(storagePath) {
54080
54828
  const legacyDir = getLegacyScreensDir(storagePath);
54081
54829
  const currentDir = getChannelsDir(storagePath);
54082
- const legacyExists = (0, import_node_fs10.existsSync)(legacyDir);
54083
- const currentExists = (0, import_node_fs10.existsSync)(currentDir);
54830
+ const legacyExists = (0, import_node_fs11.existsSync)(legacyDir);
54831
+ const currentExists = (0, import_node_fs11.existsSync)(currentDir);
54084
54832
  if (!legacyExists) {
54085
54833
  return;
54086
54834
  }
54087
54835
  if (!currentExists) {
54088
- (0, import_node_fs10.renameSync)(legacyDir, currentDir);
54836
+ (0, import_node_fs11.renameSync)(legacyDir, currentDir);
54089
54837
  return;
54090
54838
  }
54091
- const legacyEntries = (0, import_node_fs10.readdirSync)(legacyDir);
54092
- const currentEntries = (0, import_node_fs10.readdirSync)(currentDir);
54839
+ const legacyEntries = (0, import_node_fs11.readdirSync)(legacyDir);
54840
+ const currentEntries = (0, import_node_fs11.readdirSync)(currentDir);
54093
54841
  if (legacyEntries.length === 0) {
54094
- (0, import_node_fs10.rmdirSync)(legacyDir);
54842
+ (0, import_node_fs11.rmdirSync)(legacyDir);
54095
54843
  return;
54096
54844
  }
54097
54845
  if (currentEntries.length === 0) {
54098
- (0, import_node_fs10.renameSync)(legacyDir, currentDir);
54846
+ (0, import_node_fs11.renameSync)(legacyDir, currentDir);
54099
54847
  return;
54100
54848
  }
54101
54849
  throw new Error(
@@ -54104,12 +54852,12 @@ function migrateChannelMetadataDirectory(storagePath) {
54104
54852
  }
54105
54853
  function migrateDisplayStateField(storagePath) {
54106
54854
  const displayPath = getDisplayStatePath(storagePath);
54107
- if (!(0, import_node_fs10.existsSync)(displayPath)) {
54855
+ if (!(0, import_node_fs11.existsSync)(displayPath)) {
54108
54856
  return;
54109
54857
  }
54110
54858
  let parsed;
54111
54859
  try {
54112
- parsed = JSON.parse((0, import_node_fs10.readFileSync)(displayPath, "utf8"));
54860
+ parsed = JSON.parse((0, import_node_fs11.readFileSync)(displayPath, "utf8"));
54113
54861
  } catch {
54114
54862
  return;
54115
54863
  }
@@ -54150,12 +54898,12 @@ function migrateDisplayStateField(storagePath) {
54150
54898
  next[key] = value;
54151
54899
  }
54152
54900
  const temporaryPath = getDisplayMigrationTempPath(storagePath);
54153
- (0, import_node_fs10.writeFileSync)(temporaryPath, JSON.stringify(next, null, JSON_INDENT_SPACES));
54154
- (0, import_node_fs10.renameSync)(temporaryPath, displayPath);
54901
+ (0, import_node_fs11.writeFileSync)(temporaryPath, JSON.stringify(next, null, JSON_INDENT_SPACES2));
54902
+ (0, import_node_fs11.renameSync)(temporaryPath, displayPath);
54155
54903
  }
54156
54904
 
54157
54905
  // ../server/src/redesign-storage-migration.ts
54158
- var JSON_INDENT_SPACES2 = 2;
54906
+ var JSON_INDENT_SPACES3 = 2;
54159
54907
  var JSON_FILE_SUFFIX = ".json";
54160
54908
  function runRedesignStorageMigration(storagePath) {
54161
54909
  migrateChannelMetadataDirectory(storagePath);
@@ -54163,19 +54911,20 @@ function runRedesignStorageMigration(storagePath) {
54163
54911
  const channelIds = migrateChannelRecords(storagePath);
54164
54912
  migrateDisplayRecord(storagePath, channelIds);
54165
54913
  backfillChannelPageSizes(storagePath);
54914
+ migrateDisplayAppearanceMode(storagePath);
54166
54915
  }
54167
54916
  function getRedesignRecordMigrationTempPath(recordPath) {
54168
- return import_node_path13.default.join(import_node_path13.default.dirname(recordPath), `.${import_node_path13.default.basename(recordPath)}.redesign.tmp`);
54917
+ return import_node_path14.default.join(import_node_path14.default.dirname(recordPath), `.${import_node_path14.default.basename(recordPath)}.redesign.tmp`);
54169
54918
  }
54170
54919
  function migrateChannelRecords(storagePath) {
54171
54920
  const channelsDir = getChannelsDir(storagePath);
54172
- if (!(0, import_node_fs11.existsSync)(channelsDir)) {
54921
+ if (!(0, import_node_fs12.existsSync)(channelsDir)) {
54173
54922
  return [];
54174
54923
  }
54175
54924
  const channelIds = [];
54176
- for (const file2 of (0, import_node_fs11.readdirSync)(channelsDir).sort()) {
54925
+ for (const file2 of (0, import_node_fs12.readdirSync)(channelsDir).sort()) {
54177
54926
  if (!file2.endsWith(JSON_FILE_SUFFIX)) continue;
54178
- const recordPath = import_node_path13.default.join(channelsDir, file2);
54927
+ const recordPath = import_node_path14.default.join(channelsDir, file2);
54179
54928
  const parsed = readJSON(recordPath);
54180
54929
  const inspected = inspectChannelRecord(parsed);
54181
54930
  if (inspected === null) continue;
@@ -54191,7 +54940,7 @@ function migrateChannelRecords(storagePath) {
54191
54940
  }
54192
54941
  function migrateDisplayRecord(storagePath, channelIds) {
54193
54942
  const displayPath = getDisplayStatePath(storagePath);
54194
- if (!(0, import_node_fs11.existsSync)(displayPath)) return;
54943
+ if (!(0, import_node_fs12.existsSync)(displayPath)) return;
54195
54944
  const parsed = readJSON(displayPath);
54196
54945
  const record2 = parseConvertibleDisplayRecord(parsed);
54197
54946
  if (record2 === null) return;
@@ -54202,10 +54951,10 @@ function migrateDisplayRecord(storagePath, channelIds) {
54202
54951
  }
54203
54952
  function backfillChannelPageSizes(storagePath) {
54204
54953
  const channelsDir = getChannelsDir(storagePath);
54205
- if (!(0, import_node_fs11.existsSync)(channelsDir)) return;
54206
- for (const file2 of (0, import_node_fs11.readdirSync)(channelsDir).sort()) {
54954
+ if (!(0, import_node_fs12.existsSync)(channelsDir)) return;
54955
+ for (const file2 of (0, import_node_fs12.readdirSync)(channelsDir).sort()) {
54207
54956
  if (!file2.endsWith(JSON_FILE_SUFFIX)) continue;
54208
- const recordPath = import_node_path13.default.join(channelsDir, file2);
54957
+ const recordPath = import_node_path14.default.join(channelsDir, file2);
54209
54958
  const record2 = inspectPageSizeBackfillRecord(readJSON(recordPath));
54210
54959
  if (record2 === null) continue;
54211
54960
  const backfilled = backfillStoredChannelPageSizes(record2);
@@ -54214,13 +54963,23 @@ function backfillChannelPageSizes(storagePath) {
54214
54963
  }
54215
54964
  }
54216
54965
  }
54966
+ function migrateDisplayAppearanceMode(storagePath) {
54967
+ const displayPath = getDisplayStatePath(storagePath);
54968
+ if (!(0, import_node_fs12.existsSync)(displayPath)) return;
54969
+ const record2 = readJSON(displayPath);
54970
+ if (!isRecord2(record2) || Object.hasOwn(record2, "appearanceMode")) return;
54971
+ if (!isNullableString(record2.focusedChannelId) || !Array.isArray(record2.pinnedChannelIds) || !record2.pinnedChannelIds.every((id) => typeof id === "string") || !isThemeName2(record2.activeThemeName)) {
54972
+ return;
54973
+ }
54974
+ writeAtomicJSON(displayPath, { ...record2, appearanceMode: "system" });
54975
+ }
54217
54976
  function writeAtomicJSON(recordPath, value) {
54218
54977
  const temporaryPath = getRedesignRecordMigrationTempPath(recordPath);
54219
- (0, import_node_fs11.writeFileSync)(temporaryPath, JSON.stringify(value, null, JSON_INDENT_SPACES2));
54220
- (0, import_node_fs11.renameSync)(temporaryPath, recordPath);
54978
+ (0, import_node_fs12.writeFileSync)(temporaryPath, JSON.stringify(value, null, JSON_INDENT_SPACES3));
54979
+ (0, import_node_fs12.renameSync)(temporaryPath, recordPath);
54221
54980
  }
54222
54981
  function readJSON(recordPath) {
54223
- const bytes = (0, import_node_fs11.readFileSync)(recordPath, "utf8");
54982
+ const bytes = (0, import_node_fs12.readFileSync)(recordPath, "utf8");
54224
54983
  try {
54225
54984
  return JSON.parse(bytes);
54226
54985
  } catch {
@@ -54313,12 +55072,12 @@ function isRecord2(value) {
54313
55072
  }
54314
55073
 
54315
55074
  // ../server/src/onboarding-installer.ts
54316
- var import_node_fs14 = require("node:fs");
54317
- var import_node_path16 = __toESM(require("node:path"), 1);
55075
+ var import_node_fs15 = require("node:fs");
55076
+ var import_node_path17 = __toESM(require("node:path"), 1);
54318
55077
 
54319
55078
  // ../server/src/onboarding-content.ts
54320
- var import_node_fs12 = require("node:fs");
54321
- var import_node_path14 = __toESM(require("node:path"), 1);
55079
+ var import_node_fs13 = require("node:fs");
55080
+ var import_node_path15 = __toESM(require("node:path"), 1);
54322
55081
  var ONBOARDING_CONFIG_FILENAME = "onboarding-channels.json";
54323
55082
  var ONBOARDING_CONFIG_VERSION = 3;
54324
55083
  var ONBOARDING_ARTIFACT_ID_PREFIX = "television-onboarding";
@@ -54330,18 +55089,18 @@ function isValidOnboardingSlug(slug) {
54330
55089
  return ONBOARDING_SLUG_PATTERN.test(slug) && !slug.includes("--");
54331
55090
  }
54332
55091
  function loadOnboardingConfig(contentRoot) {
54333
- const configPath = import_node_path14.default.join(contentRoot, ONBOARDING_CONFIG_FILENAME);
55092
+ const configPath = import_node_path15.default.join(contentRoot, ONBOARDING_CONFIG_FILENAME);
54334
55093
  let raw;
54335
55094
  try {
54336
- raw = (0, import_node_fs12.readFileSync)(configPath, "utf8");
55095
+ raw = (0, import_node_fs13.readFileSync)(configPath, "utf8");
54337
55096
  } catch (error48) {
54338
- return { errors: [`cannot read ${ONBOARDING_CONFIG_FILENAME}: ${describeError(error48)}`] };
55097
+ return { errors: [`cannot read ${ONBOARDING_CONFIG_FILENAME}: ${describeError2(error48)}`] };
54339
55098
  }
54340
55099
  let parsed;
54341
55100
  try {
54342
55101
  parsed = JSON.parse(raw);
54343
55102
  } catch (error48) {
54344
- return { errors: [`failed to parse ${ONBOARDING_CONFIG_FILENAME}: ${describeError(error48)}`] };
55103
+ return { errors: [`failed to parse ${ONBOARDING_CONFIG_FILENAME}: ${describeError2(error48)}`] };
54345
55104
  }
54346
55105
  const errors = validateConfigSchema(parsed);
54347
55106
  if (errors.length > 0) {
@@ -54351,15 +55110,15 @@ function loadOnboardingConfig(contentRoot) {
54351
55110
  }
54352
55111
  function resolveOnboardingArtifactSource(contentRoot, channelSlug, artifactSlug) {
54353
55112
  const label = `artifact "${artifactSlug}" in channel "${channelSlug}"`;
54354
- const channelDir = import_node_path14.default.join(contentRoot, channelSlug);
55113
+ const channelDir = import_node_path15.default.join(contentRoot, channelSlug);
54355
55114
  const shapes = [];
54356
55115
  for (const extension2 of [".html", ".md"]) {
54357
- const candidate = import_node_path14.default.join(channelDir, `${artifactSlug}${extension2}`);
55116
+ const candidate = import_node_path15.default.join(channelDir, `${artifactSlug}${extension2}`);
54358
55117
  if (isFile(candidate)) {
54359
55118
  shapes.push({ kind: "file", path: candidate, display: `${artifactSlug}${extension2}` });
54360
55119
  }
54361
55120
  }
54362
- const dirPath = import_node_path14.default.join(channelDir, artifactSlug);
55121
+ const dirPath = import_node_path15.default.join(channelDir, artifactSlug);
54363
55122
  if (isDirectory(dirPath)) {
54364
55123
  shapes.push({ kind: "directory", path: dirPath, display: `${artifactSlug}/` });
54365
55124
  }
@@ -54376,7 +55135,7 @@ function resolveOnboardingArtifactSource(contentRoot, channelSlug, artifactSlug)
54376
55135
  error: `${label} has no source: expected ${artifactSlug}.html, ${artifactSlug}.md, or ${artifactSlug}/`
54377
55136
  };
54378
55137
  }
54379
- if (shape.kind === "directory" && !isFile(import_node_path14.default.join(shape.path, "index.html"))) {
55138
+ if (shape.kind === "directory" && !isFile(import_node_path15.default.join(shape.path, "index.html"))) {
54380
55139
  return { ok: false, error: `${label} is a directory artifact missing index.html at its root` };
54381
55140
  }
54382
55141
  return { ok: true, source: { kind: shape.kind, path: shape.path } };
@@ -54475,28 +55234,28 @@ function validateArtifactSchema(rawArtifact, channelLabel, index, artifactSlugs)
54475
55234
  return errors;
54476
55235
  }
54477
55236
  function isFile(candidate) {
54478
- return (0, import_node_fs12.existsSync)(candidate) && (0, import_node_fs12.statSync)(candidate).isFile();
55237
+ return (0, import_node_fs13.existsSync)(candidate) && (0, import_node_fs13.statSync)(candidate).isFile();
54479
55238
  }
54480
55239
  function isDirectory(candidate) {
54481
- return (0, import_node_fs12.existsSync)(candidate) && (0, import_node_fs12.statSync)(candidate).isDirectory();
55240
+ return (0, import_node_fs13.existsSync)(candidate) && (0, import_node_fs13.statSync)(candidate).isDirectory();
54482
55241
  }
54483
- function describeError(error48) {
55242
+ function describeError2(error48) {
54484
55243
  return error48 instanceof Error ? error48.message : String(error48);
54485
55244
  }
54486
55245
 
54487
55246
  // ../server/src/onboarding-state.ts
54488
- var import_node_fs13 = require("node:fs");
54489
- var import_node_path15 = __toESM(require("node:path"), 1);
55247
+ var import_node_fs14 = require("node:fs");
55248
+ var import_node_path16 = __toESM(require("node:path"), 1);
54490
55249
 
54491
55250
  // ../server/src/onboarding.ts
54492
55251
  var ONBOARDING_ARTIFACT_ID = "television-onboarding";
54493
55252
 
54494
55253
  // ../server/src/onboarding-state.ts
54495
- var JSON_INDENT_SPACES3 = 2;
55254
+ var JSON_INDENT_SPACES4 = 2;
54496
55255
  var ONBOARDING_STATE_VERSION = 3;
54497
55256
  function readOnboardingStateFile(storagePath) {
54498
55257
  const statePath = getOnboardingStatePath(storagePath);
54499
- if (!(0, import_node_fs13.existsSync)(statePath)) {
55258
+ if (!(0, import_node_fs14.existsSync)(statePath)) {
54500
55259
  return { status: "absent" };
54501
55260
  }
54502
55261
  const parsed = parseJSONFile(statePath);
@@ -54512,7 +55271,7 @@ function readOnboardingStateFile(storagePath) {
54512
55271
  }
54513
55272
  function readLegacyOnboardingFile(storagePath) {
54514
55273
  const legacyPath = getOnboardingArtifactSentinelPath(storagePath);
54515
- if (!(0, import_node_fs13.existsSync)(legacyPath)) {
55274
+ if (!(0, import_node_fs14.existsSync)(legacyPath)) {
54516
55275
  return { status: "absent" };
54517
55276
  }
54518
55277
  const parsed = parseJSONFile(legacyPath);
@@ -54538,17 +55297,17 @@ function readLegacyOnboardingFile(storagePath) {
54538
55297
  }
54539
55298
  function getOnboardingStateTempPath(storagePath) {
54540
55299
  const statePath = getOnboardingStatePath(storagePath);
54541
- return import_node_path15.default.join(import_node_path15.default.dirname(statePath), `.${import_node_path15.default.basename(statePath)}.tmp-${process.pid}`);
55300
+ return import_node_path16.default.join(import_node_path16.default.dirname(statePath), `.${import_node_path16.default.basename(statePath)}.tmp-${process.pid}`);
54542
55301
  }
54543
55302
  function writeOnboardingStateFile(storagePath, state) {
54544
55303
  const statePath = getOnboardingStatePath(storagePath);
54545
55304
  const tempPath = getOnboardingStateTempPath(storagePath);
54546
55305
  try {
54547
- (0, import_node_fs13.writeFileSync)(tempPath, JSON.stringify(state, null, JSON_INDENT_SPACES3));
54548
- (0, import_node_fs13.renameSync)(tempPath, statePath);
55306
+ (0, import_node_fs14.writeFileSync)(tempPath, JSON.stringify(state, null, JSON_INDENT_SPACES4));
55307
+ (0, import_node_fs14.renameSync)(tempPath, statePath);
54549
55308
  } catch (error48) {
54550
55309
  try {
54551
- (0, import_node_fs13.rmSync)(tempPath, { force: true });
55310
+ (0, import_node_fs14.rmSync)(tempPath, { force: true });
54552
55311
  } catch {
54553
55312
  }
54554
55313
  throw error48;
@@ -54557,14 +55316,14 @@ function writeOnboardingStateFile(storagePath, state) {
54557
55316
  function parseJSONFile(filePath) {
54558
55317
  let raw;
54559
55318
  try {
54560
- raw = (0, import_node_fs13.readFileSync)(filePath, "utf8");
55319
+ raw = (0, import_node_fs14.readFileSync)(filePath, "utf8");
54561
55320
  } catch (error48) {
54562
- return { ok: false, error: `cannot read ${filePath}: ${describeError2(error48)}` };
55321
+ return { ok: false, error: `cannot read ${filePath}: ${describeError3(error48)}` };
54563
55322
  }
54564
55323
  try {
54565
55324
  return { ok: true, value: JSON.parse(raw) };
54566
55325
  } catch (error48) {
54567
- return { ok: false, error: `cannot parse ${filePath}: ${describeError2(error48)}` };
55326
+ return { ok: false, error: `cannot parse ${filePath}: ${describeError3(error48)}` };
54568
55327
  }
54569
55328
  }
54570
55329
  function parseV3State(value) {
@@ -54608,7 +55367,7 @@ function parseInstallRecords(value) {
54608
55367
  function isUsableTimestamp(value) {
54609
55368
  return typeof value === "string" && !Number.isNaN(Date.parse(value));
54610
55369
  }
54611
- function describeError2(error48) {
55370
+ function describeError3(error48) {
54612
55371
  return error48 instanceof Error ? error48.message : String(error48);
54613
55372
  }
54614
55373
 
@@ -54732,17 +55491,17 @@ function installChannel(target, contentPath, channelConfig) {
54732
55491
  const artifactID = onboardingArtifactID(channelConfig.slug, artifactConfig.slug);
54733
55492
  const artifactsDir = getAgentArtifactsDir(target.storagePath);
54734
55493
  if (resolved.source.kind === "file") {
54735
- const destination = import_node_path16.default.join(artifactsDir, `${artifactID}${import_node_path16.default.extname(resolved.source.path)}`);
54736
- (0, import_node_fs14.rmSync)(destination, { recursive: true, force: true });
54737
- (0, import_node_fs14.cpSync)(resolved.source.path, destination);
55494
+ const destination = import_node_path17.default.join(artifactsDir, `${artifactID}${import_node_path17.default.extname(resolved.source.path)}`);
55495
+ (0, import_node_fs15.rmSync)(destination, { recursive: true, force: true });
55496
+ (0, import_node_fs15.cpSync)(resolved.source.path, destination);
54738
55497
  copies.push({ artifactConfig, artifactID, destination });
54739
55498
  } else {
54740
- const destinationDir = import_node_path16.default.join(artifactsDir, artifactID);
54741
- if ((0, import_node_fs14.existsSync)(destinationDir)) {
54742
- (0, import_node_fs14.rmSync)(destinationDir, { recursive: true, force: true });
55499
+ const destinationDir = import_node_path17.default.join(artifactsDir, artifactID);
55500
+ if ((0, import_node_fs15.existsSync)(destinationDir)) {
55501
+ (0, import_node_fs15.rmSync)(destinationDir, { recursive: true, force: true });
54743
55502
  }
54744
- (0, import_node_fs14.cpSync)(resolved.source.path, destinationDir, { recursive: true });
54745
- copies.push({ artifactConfig, artifactID, destination: destinationDir + import_node_path16.default.sep });
55503
+ (0, import_node_fs15.cpSync)(resolved.source.path, destinationDir, { recursive: true });
55504
+ copies.push({ artifactConfig, artifactID, destination: destinationDir + import_node_path17.default.sep });
54746
55505
  }
54747
55506
  }
54748
55507
  for (const { artifactConfig, artifactID, destination } of copies) {
@@ -54769,7 +55528,7 @@ function installChannel(target, contentPath, channelConfig) {
54769
55528
  }
54770
55529
 
54771
55530
  // ../server/src/server-store.ts
54772
- var JSON_INDENT_SPACES4 = 2;
55531
+ var JSON_INDENT_SPACES5 = 2;
54773
55532
  var LAYOUT_VERSION = 2;
54774
55533
  var CONTENT_WATCH_DEBOUNCE_MS = 100;
54775
55534
  var JSON_FILE_SUFFIX2 = ".json";
@@ -54781,14 +55540,28 @@ function isBundledViewID(id) {
54781
55540
  function sameIds(left, right) {
54782
55541
  return left.length === right.length && left.every((id, index) => id === right[index]);
54783
55542
  }
55543
+ function sameIdMembership(left, right) {
55544
+ if (left.length !== right.length) return false;
55545
+ const rightIds = new Set(right);
55546
+ return left.every((id) => rightIds.has(id));
55547
+ }
55548
+ function sameThemeJavaScriptDeclarations(left, right) {
55549
+ return left.every((value, index) => value === right[index]);
55550
+ }
55551
+ function isAppearanceMode(value) {
55552
+ return value === "system" || value === "light" || value === "dark";
55553
+ }
55554
+ function isThemeJavaScriptConsentIds(value) {
55555
+ return Array.isArray(value) && value.every((id) => typeof id === "string" && id.length > 0) && new Set(value).size === value.length;
55556
+ }
54784
55557
  function hasSameArtifactMetadata(left, right) {
54785
55558
  if (left.kind !== right.kind || left.title !== right.title) return false;
54786
55559
  return left.kind === "path" && right.kind === "path" ? left.path === right.path : left.kind === "url" && right.kind === "url" && left.url === right.url;
54787
55560
  }
54788
55561
  function hasExistingTelevisionDataStructure(storagePath) {
54789
- if ((0, import_node_fs15.existsSync)(getDisplayStatePath(storagePath))) return true;
54790
- if ((0, import_node_fs15.existsSync)(getOnboardingStatePath(storagePath))) return true;
54791
- if ((0, import_node_fs15.existsSync)(getOnboardingArtifactSentinelPath(storagePath))) return true;
55562
+ if ((0, import_node_fs16.existsSync)(getDisplayStatePath(storagePath))) return true;
55563
+ if ((0, import_node_fs16.existsSync)(getOnboardingStatePath(storagePath))) return true;
55564
+ if ((0, import_node_fs16.existsSync)(getOnboardingArtifactSentinelPath(storagePath))) return true;
54792
55565
  if (directoryIsNonEmpty(getChannelsDir(storagePath))) return true;
54793
55566
  if (directoryIsNonEmpty(getLegacyScreensDir(storagePath))) return true;
54794
55567
  if (directoryIsNonEmpty(getArtifactsMetadataDir(storagePath))) return true;
@@ -54798,7 +55571,7 @@ function hasExistingTelevisionDataStructure(storagePath) {
54798
55571
  }
54799
55572
  function directoryIsNonEmpty(dir) {
54800
55573
  try {
54801
- return (0, import_node_fs15.readdirSync)(dir).length > 0;
55574
+ return (0, import_node_fs16.readdirSync)(dir).length > 0;
54802
55575
  } catch {
54803
55576
  return false;
54804
55577
  }
@@ -54808,10 +55581,10 @@ function artifactWatchTarget(artifact) {
54808
55581
  return null;
54809
55582
  }
54810
55583
  if (hasTrailingSeparator(artifact.path)) {
54811
- return { watchPath: artifact.path, basenames: DIRECTORY_INDEX_BASENAMES };
55584
+ return { watchPath: artifact.path, recursive: true };
54812
55585
  }
54813
55586
  if (isMarkdownPath(artifact.path) || isHtmlPath(artifact.path)) {
54814
- return { watchPath: artifact.path };
55587
+ return { watchPath: artifact.path, recursive: false };
54815
55588
  }
54816
55589
  return null;
54817
55590
  }
@@ -54823,6 +55596,9 @@ var ServerStore = class extends EventTarget {
54823
55596
  focusedChannelId = null;
54824
55597
  pinnedChannelIds = [];
54825
55598
  activeThemeName = null;
55599
+ appearanceMode = "system";
55600
+ themeJavaScriptConsentIds = [];
55601
+ themeRegistry = { themes: [], errors: [] };
54826
55602
  displayStateReady = false;
54827
55603
  _artifacts = /* @__PURE__ */ new Map();
54828
55604
  artifactChannels = /* @__PURE__ */ new Map();
@@ -54834,22 +55610,24 @@ var ServerStore = class extends EventTarget {
54834
55610
  watchContentFile;
54835
55611
  bundledViewsPath;
54836
55612
  onboardingContentPath;
55613
+ bundledThemesPath;
54837
55614
  serving;
54838
55615
  // True only while the constructor's install phase runs: content watchers
54839
55616
  // must not start before the final bootstrap step
54840
- // (specs/arch/onboarding/installer.md#^bootstrap-sequence step 7).
55617
+ // (specs/arch/onboarding/installer.md#^bootstrap-sequence step 8).
54841
55618
  inBootstrapInstall = false;
54842
55619
  telemetryHooks = null;
54843
55620
  // The serving bootstrap sequence and the token-only cutoff are spec'd:
54844
55621
  // specs/arch/onboarding/installer.md#^bootstrap-sequence, #^token-only-boot.
54845
55622
  constructor(options) {
54846
55623
  super();
54847
- this.storagePath = import_node_path17.default.resolve(options.storagePath);
55624
+ this.storagePath = import_node_path18.default.resolve(options.storagePath);
54848
55625
  this.serving = options.installOnboardingChannels === true;
54849
55626
  this.dataDirCreated = !hasExistingTelevisionDataStructure(this.storagePath);
54850
55627
  this.watchContentFile = options.watchContentFile ?? defaultWatchContentFile;
54851
55628
  this.bundledViewsPath = options.bundledViewsPath;
54852
55629
  this.onboardingContentPath = options.onboardingContentPath;
55630
+ this.bundledThemesPath = options.bundledThemesPath === void 0 ? void 0 : import_node_path18.default.resolve(options.bundledThemesPath);
54853
55631
  if (this.serving) {
54854
55632
  runRedesignStorageMigration(this.storagePath);
54855
55633
  }
@@ -54877,10 +55655,17 @@ var ServerStore = class extends EventTarget {
54877
55655
  if (this.channels.size === 0) {
54878
55656
  this.createChannel({ name: "Default" });
54879
55657
  }
55658
+ const bundledThemeState = runBundledThemeInstallation(
55659
+ this.storagePath,
55660
+ this.bundledThemesPath
55661
+ );
55662
+ this.refreshThemeRegistry();
54880
55663
  if (!this.loadDisplayState()) {
54881
55664
  this.initializeDisplayState();
54882
55665
  }
55666
+ this.completeDefaultThemeSelection(bundledThemeState);
54883
55667
  this.displayStateReady = true;
55668
+ this.clearUnregisteredActiveTheme();
54884
55669
  if (noContentAtBoot && onboarding.focusChannelID !== void 0 && this.channels.has(onboarding.focusChannelID)) {
54885
55670
  this.patchDisplay({ focusedChannelId: onboarding.focusChannelID });
54886
55671
  }
@@ -54899,8 +55684,8 @@ var ServerStore = class extends EventTarget {
54899
55684
  if (!isBundledViewID(id) || this.bundledViewsPath === void 0) {
54900
55685
  return null;
54901
55686
  }
54902
- const viewPath = import_node_path17.default.join(this.bundledViewsPath, id);
54903
- return (0, import_node_fs15.existsSync)(import_node_path17.default.join(viewPath, "index.html")) ? viewPath : null;
55687
+ const viewPath = import_node_path18.default.join(this.bundledViewsPath, id);
55688
+ return (0, import_node_fs16.existsSync)(import_node_path18.default.join(viewPath, "index.html")) ? viewPath : null;
54904
55689
  }
54905
55690
  getChannel(id) {
54906
55691
  const channel = this.channels.get(id);
@@ -54939,7 +55724,7 @@ var ServerStore = class extends EventTarget {
54939
55724
  this.persistChannel(channel);
54940
55725
  } catch (error48) {
54941
55726
  channel.layout = previousLayout;
54942
- (0, import_node_fs15.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), { force: true });
55727
+ (0, import_node_fs16.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), { force: true });
54943
55728
  throw error48;
54944
55729
  }
54945
55730
  this._artifacts.set(artifact.id, artifact);
@@ -54987,7 +55772,10 @@ var ServerStore = class extends EventTarget {
54987
55772
  this._artifacts.set(input.artifactID, updated);
54988
55773
  if (pointerChanged) {
54989
55774
  this.startWatchingArtifactContent(updated);
54990
- this.scheduleArtifactContentChangedEvent(updated.id);
55775
+ const watcher = this.contentWatchers.get(updated.id);
55776
+ if (watcher) {
55777
+ this.scheduleArtifactContentChangedEvent(updated.id, watcher);
55778
+ }
54991
55779
  }
54992
55780
  this.dispatchEvent(new ArtifactUpdatedEvent("artifact-updated", { artifact: updated }));
54993
55781
  if (metadataChanged) {
@@ -55024,7 +55812,7 @@ var ServerStore = class extends EventTarget {
55024
55812
  }
55025
55813
  this.stopWatchingArtifactContent(artifactID);
55026
55814
  this._artifacts.delete(artifactID);
55027
- (0, import_node_fs15.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
55815
+ (0, import_node_fs16.rmSync)(getArtifactLiveMetadataPath(this.storagePath, artifactID), { force: true });
55028
55816
  if (channelID !== void 0) {
55029
55817
  this.dispatchEvent(new ArtifactRemovedEvent("artifact-removed", { artifactID, channelID }));
55030
55818
  }
@@ -55049,7 +55837,9 @@ var ServerStore = class extends EventTarget {
55049
55837
  this.persistDisplayState({
55050
55838
  focusedChannelId: channel.id,
55051
55839
  pinnedChannelIds: this.pinnedChannelIds,
55052
- activeThemeName: this.activeThemeName
55840
+ activeThemeName: this.activeThemeName,
55841
+ appearanceMode: this.appearanceMode,
55842
+ themeJavaScriptConsentIds: [...this.themeJavaScriptConsentIds]
55053
55843
  });
55054
55844
  this.focusedChannelId = channel.id;
55055
55845
  }
@@ -55111,19 +55901,52 @@ var ServerStore = class extends EventTarget {
55111
55901
  getActiveThemeName() {
55112
55902
  return this.activeThemeName;
55113
55903
  }
55904
+ hasThemeJavaScriptConsent(themeID) {
55905
+ return this.themeJavaScriptConsentIds.includes(themeID);
55906
+ }
55907
+ getThemeRegistry() {
55908
+ return {
55909
+ themes: this.themeRegistry.themes.map((theme) => ({ ...theme })),
55910
+ errors: this.themeRegistry.errors.map((error48) => ({ ...error48 }))
55911
+ };
55912
+ }
55913
+ refreshThemeRegistry(telemetryContext) {
55914
+ const selectedThemeID = this.activeThemeName;
55915
+ const previousColorScheme = this.getActiveThemeColorScheme();
55916
+ const previousJavaScriptDeclarations = selectedThemeID === null ? null : this.registeredThemeJavaScriptDeclarations(selectedThemeID);
55917
+ this.themeRegistry = scanThemesDirectory(getThemesDir(this.storagePath));
55918
+ if (this.displayStateReady) {
55919
+ this.clearUnregisteredActiveTheme(telemetryContext);
55920
+ if (selectedThemeID !== null && this.activeThemeName === selectedThemeID && previousJavaScriptDeclarations !== null && (previousColorScheme !== this.getActiveThemeColorScheme() || !sameThemeJavaScriptDeclarations(
55921
+ previousJavaScriptDeclarations,
55922
+ this.registeredThemeJavaScriptDeclarations(selectedThemeID)
55923
+ ))) {
55924
+ this.dispatchThemeChanged(selectedThemeID);
55925
+ }
55926
+ }
55927
+ return this.getThemeRegistry();
55928
+ }
55114
55929
  getDisplayState() {
55115
55930
  return {
55116
55931
  focusedChannelId: this.getFocusedChannelId(),
55117
55932
  pinnedChannelIds: [...this.pinnedChannelIds],
55118
- activeThemeName: this.activeThemeName
55933
+ activeThemeName: this.activeThemeName,
55934
+ activeThemeColorScheme: this.getActiveThemeColorScheme(),
55935
+ appearanceMode: this.appearanceMode,
55936
+ themeJavaScriptConsentIds: [...this.themeJavaScriptConsentIds]
55119
55937
  };
55120
55938
  }
55121
55939
  patchDisplay(input, telemetryContext) {
55940
+ this.applyDisplayPatch(input, "selection", telemetryContext);
55941
+ }
55942
+ applyDisplayPatch(input, themeChangeReason, telemetryContext) {
55122
55943
  const currentFocusedChannelId = this.getFocusedChannelId();
55123
55944
  const previousPinnedChannelIds = this.pinnedChannelIds;
55124
55945
  let nextFocusedChannelId = this.focusedChannelId;
55125
55946
  let nextPinnedChannelIds = this.pinnedChannelIds;
55126
55947
  let nextActiveThemeName = this.activeThemeName;
55948
+ let nextAppearanceMode = this.appearanceMode;
55949
+ let nextThemeJavaScriptConsentIds = this.themeJavaScriptConsentIds;
55127
55950
  if (input.focusedChannelId !== void 0) {
55128
55951
  if (input.focusedChannelId === null) {
55129
55952
  if (this.channels.size !== 0) {
@@ -55144,33 +55967,51 @@ var ServerStore = class extends EventTarget {
55144
55967
  nextPinnedChannelIds = [...input.pinnedChannelIds];
55145
55968
  }
55146
55969
  if (input.activeThemeName !== void 0) {
55147
- if (input.activeThemeName !== null) {
55148
- if (!isValidThemeName(input.activeThemeName)) {
55149
- throw new InvalidRequestError(`Invalid theme name: ${input.activeThemeName}`);
55150
- }
55151
- if (!themeExists(this.storagePath, input.activeThemeName)) {
55152
- throw new NotFoundError(`Theme not found: ${input.activeThemeName}`, {
55153
- entityType: "theme",
55154
- entityID: input.activeThemeName
55155
- });
55156
- }
55970
+ if (input.activeThemeName !== null && !this.isThemeRegistered(input.activeThemeName)) {
55971
+ throw new NotFoundError(`Theme not found: ${input.activeThemeName}`, {
55972
+ entityType: "theme",
55973
+ entityID: input.activeThemeName
55974
+ });
55157
55975
  }
55158
55976
  nextActiveThemeName = input.activeThemeName;
55159
55977
  }
55978
+ if (input.appearanceMode !== void 0) {
55979
+ if (!isAppearanceMode(input.appearanceMode)) {
55980
+ throw new InvalidRequestError(`Invalid appearance mode: ${String(input.appearanceMode)}`);
55981
+ }
55982
+ nextAppearanceMode = input.appearanceMode;
55983
+ }
55984
+ if (input.themeJavaScriptConsentIds !== void 0) {
55985
+ if (input.themeJavaScriptConsentIds.some((id) => typeof id !== "string" || id.length === 0) || new Set(input.themeJavaScriptConsentIds).size !== input.themeJavaScriptConsentIds.length) {
55986
+ throw new InvalidRequestError(
55987
+ "themeJavaScriptConsentIds must contain unique non-empty theme ids"
55988
+ );
55989
+ }
55990
+ nextThemeJavaScriptConsentIds = [...input.themeJavaScriptConsentIds];
55991
+ }
55160
55992
  const focusedChannelChanged = nextFocusedChannelId !== this.focusedChannelId;
55161
55993
  const pinnedChannelsChanged = !sameIds(nextPinnedChannelIds, this.pinnedChannelIds);
55162
55994
  const themeChanged = nextActiveThemeName !== this.activeThemeName;
55163
- if (!focusedChannelChanged && !pinnedChannelsChanged && !themeChanged) {
55995
+ const appearanceChanged = nextAppearanceMode !== this.appearanceMode;
55996
+ const themeJavaScriptConsentChanged = !sameIdMembership(
55997
+ nextThemeJavaScriptConsentIds,
55998
+ this.themeJavaScriptConsentIds
55999
+ );
56000
+ if (!focusedChannelChanged && !pinnedChannelsChanged && !themeChanged && !appearanceChanged && !themeJavaScriptConsentChanged) {
55164
56001
  return;
55165
56002
  }
55166
56003
  this.persistDisplayState({
55167
56004
  focusedChannelId: this.resolveFocusedChannelId(nextFocusedChannelId),
55168
56005
  pinnedChannelIds: nextPinnedChannelIds,
55169
- activeThemeName: nextActiveThemeName
56006
+ activeThemeName: nextActiveThemeName,
56007
+ appearanceMode: nextAppearanceMode,
56008
+ themeJavaScriptConsentIds: nextThemeJavaScriptConsentIds
55170
56009
  });
55171
56010
  this.focusedChannelId = nextFocusedChannelId;
55172
56011
  this.pinnedChannelIds = nextPinnedChannelIds;
55173
56012
  this.activeThemeName = nextActiveThemeName;
56013
+ this.appearanceMode = nextAppearanceMode;
56014
+ this.themeJavaScriptConsentIds = nextThemeJavaScriptConsentIds;
55174
56015
  if (themeChanged) {
55175
56016
  this.startWatchingActiveTheme();
55176
56017
  }
@@ -55194,11 +56035,19 @@ var ServerStore = class extends EventTarget {
55194
56035
  )
55195
56036
  );
55196
56037
  }
56038
+ if (themeChanged || themeJavaScriptConsentChanged) {
56039
+ this.dispatchThemeChanged();
56040
+ }
55197
56041
  if (themeChanged) {
56042
+ this.emitTelemetry((hooks) => hooks.themeChanged(themeChangeReason, telemetryContext));
56043
+ }
56044
+ if (appearanceChanged) {
56045
+ this.emitTelemetry((hooks) => hooks.appearanceModeChanged(this.appearanceMode, telemetryContext));
55198
56046
  this.dispatchEvent(
55199
- new ThemeChangedEvent("theme-changed", { themeName: this.activeThemeName })
56047
+ new AppearanceChangedEvent("appearance-changed", {
56048
+ appearanceMode: this.appearanceMode
56049
+ })
55200
56050
  );
55201
- this.emitTelemetry((hooks) => hooks.themeChanged(telemetryContext));
55202
56051
  }
55203
56052
  }
55204
56053
  /**
@@ -55236,8 +56085,8 @@ var ServerStore = class extends EventTarget {
55236
56085
  const artifactResults = artifactIDs.map(
55237
56086
  (artifactID) => this.deleteArtifactWithCause(artifactID, "channel_deleted", telemetryContext)
55238
56087
  );
55239
- const metadataPath = import_node_path17.default.join(this.channelsDir, `${channel.id}.json`);
55240
- (0, import_node_fs15.rmSync)(metadataPath, { force: true });
56088
+ const metadataPath = import_node_path18.default.join(this.channelsDir, `${channel.id}.json`);
56089
+ (0, import_node_fs16.rmSync)(metadataPath, { force: true });
55241
56090
  this.channels.delete(channel.id);
55242
56091
  const nextFocusedChannelId = previousFocusedChannelId === channel.id ? this.pickDeletionSuccessorChannelID(nextPinnedChannelIds) : previousFocusedChannelId;
55243
56092
  const pinnedChannelsChanged = !sameIds(nextPinnedChannelIds, this.pinnedChannelIds);
@@ -55246,7 +56095,9 @@ var ServerStore = class extends EventTarget {
55246
56095
  this.persistDisplayState({
55247
56096
  focusedChannelId: nextFocusedChannelId,
55248
56097
  pinnedChannelIds: nextPinnedChannelIds,
55249
- activeThemeName: this.activeThemeName
56098
+ activeThemeName: this.activeThemeName,
56099
+ appearanceMode: this.appearanceMode,
56100
+ themeJavaScriptConsentIds: [...this.themeJavaScriptConsentIds]
55250
56101
  });
55251
56102
  this.pinnedChannelIds = nextPinnedChannelIds;
55252
56103
  this.focusedChannelId = nextFocusedChannelId;
@@ -55282,13 +56133,13 @@ var ServerStore = class extends EventTarget {
55282
56133
  }
55283
56134
  }
55284
56135
  load() {
55285
- if ((0, import_node_fs15.existsSync)(this.channelsDir)) {
55286
- for (const file2 of (0, import_node_fs15.readdirSync)(this.channelsDir)) {
56136
+ if ((0, import_node_fs16.existsSync)(this.channelsDir)) {
56137
+ for (const file2 of (0, import_node_fs16.readdirSync)(this.channelsDir)) {
55287
56138
  if (!file2.endsWith(JSON_FILE_SUFFIX2)) continue;
55288
- const filePath = import_node_path17.default.join(this.channelsDir, file2);
56139
+ const filePath = import_node_path18.default.join(this.channelsDir, file2);
55289
56140
  let raw;
55290
56141
  try {
55291
- raw = JSON.parse((0, import_node_fs15.readFileSync)(filePath, "utf8"));
56142
+ raw = JSON.parse((0, import_node_fs16.readFileSync)(filePath, "utf8"));
55292
56143
  } catch (error48) {
55293
56144
  console.warn(`Skipping malformed JSON file: ${filePath}`, error48);
55294
56145
  continue;
@@ -55306,15 +56157,15 @@ var ServerStore = class extends EventTarget {
55306
56157
  });
55307
56158
  }
55308
56159
  }
55309
- if (!(0, import_node_fs15.existsSync)(this.artifactsDir)) {
56160
+ if (!(0, import_node_fs16.existsSync)(this.artifactsDir)) {
55310
56161
  return;
55311
56162
  }
55312
- for (const file2 of (0, import_node_fs15.readdirSync)(this.artifactsDir)) {
56163
+ for (const file2 of (0, import_node_fs16.readdirSync)(this.artifactsDir)) {
55313
56164
  if (!file2.endsWith(JSON_FILE_SUFFIX2)) {
55314
56165
  continue;
55315
56166
  }
55316
56167
  const id = file2.slice(0, -JSON_FILE_SUFFIX2.length);
55317
- const metadataPath = import_node_path17.default.join(this.artifactsDir, file2);
56168
+ const metadataPath = import_node_path18.default.join(this.artifactsDir, file2);
55318
56169
  const metadata = this.readArtifactFile(metadataPath, id);
55319
56170
  this._artifacts.set(id, metadata);
55320
56171
  }
@@ -55328,27 +56179,38 @@ var ServerStore = class extends EventTarget {
55328
56179
  if (target === null) {
55329
56180
  return;
55330
56181
  }
56182
+ let watcher;
56183
+ let watcherOwned = false;
56184
+ let pendingChange = false;
56185
+ const scheduleChange = () => {
56186
+ if (!watcher || !watcherOwned) {
56187
+ pendingChange = true;
56188
+ return;
56189
+ }
56190
+ this.scheduleArtifactContentChangedEvent(artifact.id, watcher);
56191
+ };
55331
56192
  try {
55332
- const watcher = this.watchContentFile(target.watchPath, () => {
55333
- this.scheduleArtifactContentChangedEvent(artifact.id);
55334
- }, target.basenames, {
55335
- onWatcherDead: () => {
55336
- this.scheduleArtifactContentChangedEvent(artifact.id);
55337
- },
55338
- onWatcherRearmed: () => {
55339
- this.scheduleArtifactContentChangedEvent(artifact.id);
55340
- }
56193
+ watcher = this.watchContentFile(target.watchPath, scheduleChange, {
56194
+ recursive: target.recursive,
56195
+ onWatcherDead: scheduleChange,
56196
+ onWatcherRearmed: scheduleChange
55341
56197
  });
55342
56198
  watcher.on("error", (error48) => {
56199
+ if (this.contentWatchers.get(artifact.id) !== watcher) {
56200
+ return;
56201
+ }
55343
56202
  console.warn(
55344
56203
  `Watcher error for artifact ${artifact.id}: ${target.watchPath}`,
55345
56204
  error48
55346
56205
  );
55347
- if (this.contentWatchers.get(artifact.id) === watcher) {
55348
- this.stopWatchingArtifactContent(artifact.id);
55349
- }
56206
+ this.stopWatchingArtifactContent(artifact.id);
55350
56207
  });
55351
56208
  this.contentWatchers.set(artifact.id, watcher);
56209
+ watcherOwned = true;
56210
+ if (pendingChange) {
56211
+ pendingChange = false;
56212
+ scheduleChange();
56213
+ }
55352
56214
  } catch (error48) {
55353
56215
  console.warn(
55354
56216
  `Failed to watch artifact content path for ${artifact.id}: ${target.watchPath}`,
@@ -55375,26 +56237,44 @@ var ServerStore = class extends EventTarget {
55375
56237
  }
55376
56238
  startWatchingActiveTheme() {
55377
56239
  this.stopWatchingActiveTheme();
55378
- if (this.activeThemeName === null || !themeExists(this.storagePath, this.activeThemeName)) {
56240
+ if (this.activeThemeName === null || !this.isThemeRegistered(this.activeThemeName)) {
55379
56241
  return;
55380
56242
  }
55381
56243
  const themeName = this.activeThemeName;
55382
- const themePath = getThemeEntryPath(this.storagePath, themeName);
56244
+ const themePath = getThemeDir(this.storagePath, themeName);
56245
+ let watcher;
56246
+ let watcherOwned = false;
56247
+ let pendingChange = false;
56248
+ const scheduleChange = () => {
56249
+ if (!watcher || !watcherOwned) {
56250
+ pendingChange = true;
56251
+ return;
56252
+ }
56253
+ this.scheduleThemeChangedEvent(themeName, watcher);
56254
+ };
55383
56255
  try {
55384
- const watcher = this.watchContentFile(themePath, () => {
55385
- this.scheduleThemeChangedEvent(themeName);
56256
+ watcher = this.watchContentFile(themePath, scheduleChange, {
56257
+ recursive: true,
56258
+ onWatcherDead: scheduleChange,
56259
+ onWatcherRearmed: scheduleChange
55386
56260
  });
55387
56261
  watcher.on("error", (error48) => {
56262
+ if (this.themeWatcher !== watcher) {
56263
+ return;
56264
+ }
55388
56265
  console.warn(
55389
56266
  `Watcher error for active theme ${themeName}: ${themePath}`,
55390
56267
  error48
55391
56268
  );
55392
- if (this.themeWatcher === watcher) {
55393
- this.stopWatchingActiveTheme();
55394
- }
56269
+ this.stopWatchingActiveTheme();
55395
56270
  });
55396
56271
  this.themeWatcher = watcher;
55397
56272
  this.themeWatcherFor = themeName;
56273
+ watcherOwned = true;
56274
+ if (pendingChange) {
56275
+ pendingChange = false;
56276
+ scheduleChange();
56277
+ }
55398
56278
  } catch (error48) {
55399
56279
  console.warn(
55400
56280
  `Failed to watch active theme path for ${themeName}: ${themePath}`,
@@ -55411,29 +56291,39 @@ var ServerStore = class extends EventTarget {
55411
56291
  this.themeWatchDebounceTimer = null;
55412
56292
  }
55413
56293
  }
55414
- scheduleThemeChangedEvent(themeName) {
56294
+ scheduleThemeChangedEvent(themeName, sourceWatcher) {
56295
+ if (this.themeWatcher !== sourceWatcher || this.themeWatcherFor !== themeName || this.activeThemeName !== themeName) {
56296
+ return;
56297
+ }
55415
56298
  if (this.themeWatchDebounceTimer) {
55416
56299
  clearTimeout(this.themeWatchDebounceTimer);
55417
56300
  }
55418
- this.themeWatchDebounceTimer = setTimeout(() => {
55419
- this.themeWatchDebounceTimer = null;
55420
- if (this.themeWatcher === null) {
56301
+ const timer = setTimeout(() => {
56302
+ if (this.themeWatchDebounceTimer !== timer) {
55421
56303
  return;
55422
56304
  }
55423
- if (this.themeWatcherFor !== themeName || this.activeThemeName !== themeName) {
56305
+ this.themeWatchDebounceTimer = null;
56306
+ if (this.themeWatcher !== sourceWatcher || this.themeWatcherFor !== themeName || this.activeThemeName !== themeName) {
55424
56307
  return;
55425
56308
  }
55426
- this.dispatchEvent(new ThemeChangedEvent("theme-changed", { themeName }));
56309
+ this.dispatchThemeChanged(themeName);
55427
56310
  }, CONTENT_WATCH_DEBOUNCE_MS);
56311
+ this.themeWatchDebounceTimer = timer;
55428
56312
  }
55429
- scheduleArtifactContentChangedEvent(artifactID) {
56313
+ scheduleArtifactContentChangedEvent(artifactID, sourceWatcher) {
56314
+ if (this.contentWatchers.get(artifactID) !== sourceWatcher || !this._artifacts.has(artifactID)) {
56315
+ return;
56316
+ }
55430
56317
  const existingTimer = this.contentWatchDebounceTimers.get(artifactID);
55431
56318
  if (existingTimer) {
55432
56319
  clearTimeout(existingTimer);
55433
56320
  }
55434
56321
  const timer = setTimeout(() => {
56322
+ if (this.contentWatchDebounceTimers.get(artifactID) !== timer) {
56323
+ return;
56324
+ }
55435
56325
  this.contentWatchDebounceTimers.delete(artifactID);
55436
- if (!this.contentWatchers.has(artifactID) || !this._artifacts.has(artifactID)) {
56326
+ if (this.contentWatchers.get(artifactID) !== sourceWatcher || !this._artifacts.has(artifactID)) {
55437
56327
  return;
55438
56328
  }
55439
56329
  this.dispatchEvent(
@@ -55485,20 +56375,20 @@ var ServerStore = class extends EventTarget {
55485
56375
  * without statting.
55486
56376
  */
55487
56377
  normalizeArtifactPath(artifactPath) {
55488
- if (!import_node_path17.default.isAbsolute(artifactPath)) {
56378
+ if (!import_node_path18.default.isAbsolute(artifactPath)) {
55489
56379
  throw new InvalidRequestError("path must be absolute");
55490
56380
  }
55491
56381
  const stripped = stripTrailingSeparators(artifactPath);
55492
56382
  let stat;
55493
56383
  try {
55494
- (0, import_node_fs15.accessSync)(stripped, import_node_fs15.constants.R_OK);
55495
- stat = (0, import_node_fs15.statSync)(stripped);
56384
+ (0, import_node_fs16.accessSync)(stripped, import_node_fs16.constants.R_OK);
56385
+ stat = (0, import_node_fs16.statSync)(stripped);
55496
56386
  } catch {
55497
56387
  throw new InvalidRequestError(`path does not exist or is not readable: ${artifactPath}`);
55498
56388
  }
55499
56389
  if (stat.isDirectory()) {
55500
- const canonical = hasTrailingSeparator(stripped) ? stripped : `${stripped}${import_node_path17.default.sep}`;
55501
- if (!DIRECTORY_INDEX_BASENAMES.some((basename) => (0, import_node_fs15.existsSync)(import_node_path17.default.join(canonical, basename)))) {
56390
+ const canonical = hasTrailingSeparator(stripped) ? stripped : `${stripped}${import_node_path18.default.sep}`;
56391
+ if (!DIRECTORY_INDEX_BASENAMES.some((basename) => (0, import_node_fs16.existsSync)(import_node_path18.default.join(canonical, basename)))) {
55502
56392
  throw new InvalidRequestError(`directory artifact must contain index.html or index.htm: ${canonical}`);
55503
56393
  }
55504
56394
  return canonical;
@@ -55526,14 +56416,60 @@ var ServerStore = class extends EventTarget {
55526
56416
  this.focusedChannelId = this.pickPreferredChannelID();
55527
56417
  this.pinnedChannelIds = [];
55528
56418
  this.activeThemeName = null;
56419
+ this.appearanceMode = "system";
56420
+ this.themeJavaScriptConsentIds = [];
55529
56421
  this.persistDisplayState();
55530
56422
  }
56423
+ completeDefaultThemeSelection(state) {
56424
+ if (state === null || state.initialThemeSelectionComplete || !state.installedThemeIDs.includes(DEFAULT_BUNDLED_THEME_ID)) {
56425
+ return;
56426
+ }
56427
+ if (this.activeThemeName === null && this.isThemeRegistered(DEFAULT_BUNDLED_THEME_ID)) {
56428
+ this.persistDisplayState({
56429
+ ...this.getDisplayState(),
56430
+ activeThemeName: DEFAULT_BUNDLED_THEME_ID
56431
+ });
56432
+ this.activeThemeName = DEFAULT_BUNDLED_THEME_ID;
56433
+ }
56434
+ writeBundledThemeStateFile(this.storagePath, {
56435
+ version: BUNDLED_THEME_STATE_VERSION,
56436
+ installedThemeIDs: state.installedThemeIDs,
56437
+ initialThemeSelectionComplete: true
56438
+ });
56439
+ }
56440
+ dispatchThemeChanged(themeName = this.activeThemeName) {
56441
+ this.dispatchEvent(new ThemeChangedEvent("theme-changed", {
56442
+ themeName,
56443
+ activeThemeColorScheme: this.getActiveThemeColorScheme(themeName),
56444
+ themeJavaScriptConsentIds: [...this.themeJavaScriptConsentIds]
56445
+ }));
56446
+ }
56447
+ getActiveThemeColorScheme(themeName = this.activeThemeName) {
56448
+ if (themeName === null) return null;
56449
+ return this.themeRegistry.themes.find((theme) => theme.id === themeName)?.colorScheme ?? null;
56450
+ }
56451
+ isThemeRegistered(themeID) {
56452
+ return this.themeRegistry.themes.some((theme) => theme.id === themeID);
56453
+ }
56454
+ registeredThemeJavaScriptDeclarations(themeID) {
56455
+ const theme = this.themeRegistry.themes.find((candidate) => candidate.id === themeID);
56456
+ return [
56457
+ theme?.enableMainJS,
56458
+ theme?.enableIframeBackgroundJS,
56459
+ theme?.enableIframeOverlayJS
56460
+ ];
56461
+ }
56462
+ clearUnregisteredActiveTheme(telemetryContext) {
56463
+ if (this.activeThemeName !== null && !this.isThemeRegistered(this.activeThemeName)) {
56464
+ this.applyDisplayPatch({ activeThemeName: null }, "fallback", telemetryContext);
56465
+ }
56466
+ }
55531
56467
  ensureDirectories() {
55532
- (0, import_node_fs15.mkdirSync)(import_node_path17.default.join(this.storagePath, "state"), { recursive: true });
55533
- (0, import_node_fs15.mkdirSync)(this.channelsDir, { recursive: true });
55534
- (0, import_node_fs15.mkdirSync)(this.artifactsDir, { recursive: true });
55535
- (0, import_node_fs15.mkdirSync)(getThemesDir(this.storagePath), { recursive: true });
55536
- (0, import_node_fs15.mkdirSync)(getAgentArtifactsDir(this.storagePath), { recursive: true });
56468
+ (0, import_node_fs16.mkdirSync)(import_node_path18.default.join(this.storagePath, "state"), { recursive: true });
56469
+ (0, import_node_fs16.mkdirSync)(this.channelsDir, { recursive: true });
56470
+ (0, import_node_fs16.mkdirSync)(this.artifactsDir, { recursive: true });
56471
+ (0, import_node_fs16.mkdirSync)(getThemesDir(this.storagePath), { recursive: true });
56472
+ (0, import_node_fs16.mkdirSync)(getAgentArtifactsDir(this.storagePath), { recursive: true });
55537
56473
  }
55538
56474
  rebuildArtifactChannels() {
55539
56475
  this.artifactChannels.clear();
@@ -55552,12 +56488,12 @@ var ServerStore = class extends EventTarget {
55552
56488
  }
55553
56489
  }
55554
56490
  loadDisplayState() {
55555
- if (!(0, import_node_fs15.existsSync)(this.displayStatePath)) {
56491
+ if (!(0, import_node_fs16.existsSync)(this.displayStatePath)) {
55556
56492
  return false;
55557
56493
  }
55558
56494
  let raw;
55559
56495
  try {
55560
- raw = (0, import_node_fs15.readFileSync)(this.displayStatePath, "utf8");
56496
+ raw = (0, import_node_fs16.readFileSync)(this.displayStatePath, "utf8");
55561
56497
  } catch {
55562
56498
  return false;
55563
56499
  }
@@ -55567,18 +56503,34 @@ var ServerStore = class extends EventTarget {
55567
56503
  } catch {
55568
56504
  return false;
55569
56505
  }
55570
- if (parsed && typeof parsed === "object" && "focusedChannelId" in parsed && (typeof parsed.focusedChannelId === "string" || parsed.focusedChannelId === null) && "pinnedChannelIds" in parsed && Array.isArray(parsed.pinnedChannelIds) && parsed.pinnedChannelIds.every((id) => typeof id === "string") && "activeThemeName" in parsed && (typeof parsed.activeThemeName === "string" || parsed.activeThemeName === null)) {
56506
+ if (parsed && typeof parsed === "object" && "focusedChannelId" in parsed && (typeof parsed.focusedChannelId === "string" || parsed.focusedChannelId === null) && "pinnedChannelIds" in parsed && Array.isArray(parsed.pinnedChannelIds) && parsed.pinnedChannelIds.every((id) => typeof id === "string") && "activeThemeName" in parsed && (typeof parsed.activeThemeName === "string" || parsed.activeThemeName === null) && "appearanceMode" in parsed && isAppearanceMode(parsed.appearanceMode) && (!("themeJavaScriptConsentIds" in parsed) || isThemeJavaScriptConsentIds(
56507
+ parsed.themeJavaScriptConsentIds
56508
+ ))) {
55571
56509
  this.focusedChannelId = parsed.focusedChannelId;
55572
56510
  this.pinnedChannelIds = [...parsed.pinnedChannelIds];
55573
56511
  this.activeThemeName = parsed.activeThemeName;
56512
+ this.appearanceMode = parsed.appearanceMode;
56513
+ this.themeJavaScriptConsentIds = "themeJavaScriptConsentIds" in parsed ? [...parsed.themeJavaScriptConsentIds] : [];
55574
56514
  return true;
55575
56515
  }
55576
56516
  return false;
55577
56517
  }
55578
- persistDisplayState(state = this.getDisplayState()) {
55579
- (0, import_node_fs15.writeFileSync)(
56518
+ persistDisplayState(state = {
56519
+ focusedChannelId: this.getFocusedChannelId(),
56520
+ pinnedChannelIds: [...this.pinnedChannelIds],
56521
+ activeThemeName: this.activeThemeName,
56522
+ appearanceMode: this.appearanceMode,
56523
+ themeJavaScriptConsentIds: [...this.themeJavaScriptConsentIds]
56524
+ }) {
56525
+ (0, import_node_fs16.writeFileSync)(
55580
56526
  this.displayStatePath,
55581
- JSON.stringify(state, null, JSON_INDENT_SPACES4)
56527
+ JSON.stringify({
56528
+ focusedChannelId: state.focusedChannelId,
56529
+ pinnedChannelIds: state.pinnedChannelIds,
56530
+ activeThemeName: state.activeThemeName,
56531
+ appearanceMode: state.appearanceMode,
56532
+ themeJavaScriptConsentIds: state.themeJavaScriptConsentIds
56533
+ }, null, JSON_INDENT_SPACES5)
55582
56534
  );
55583
56535
  }
55584
56536
  get displayStatePath() {
@@ -55612,11 +56564,11 @@ var ServerStore = class extends EventTarget {
55612
56564
  return newestChannelID;
55613
56565
  }
55614
56566
  loadOrCreateAuthToken() {
55615
- if ((0, import_node_fs15.existsSync)(this.tokenPath)) {
55616
- return (0, import_node_fs15.readFileSync)(this.tokenPath, "utf8").trim();
56567
+ if ((0, import_node_fs16.existsSync)(this.tokenPath)) {
56568
+ return (0, import_node_fs16.readFileSync)(this.tokenPath, "utf8").trim();
55617
56569
  }
55618
56570
  const token = createToken();
55619
- (0, import_node_fs15.writeFileSync)(this.tokenPath, `${token}
56571
+ (0, import_node_fs16.writeFileSync)(this.tokenPath, `${token}
55620
56572
  `);
55621
56573
  return token;
55622
56574
  }
@@ -55631,15 +56583,15 @@ var ServerStore = class extends EventTarget {
55631
56583
  layout: channel.layout,
55632
56584
  ...channel.onboarding ? { onboarding: channel.onboarding } : {}
55633
56585
  };
55634
- (0, import_node_fs15.writeFileSync)(
55635
- import_node_path17.default.join(this.channelsDir, `${channel.id}.json`),
55636
- JSON.stringify(stored, null, JSON_INDENT_SPACES4)
56586
+ (0, import_node_fs16.writeFileSync)(
56587
+ import_node_path18.default.join(this.channelsDir, `${channel.id}.json`),
56588
+ JSON.stringify(stored, null, JSON_INDENT_SPACES5)
55637
56589
  );
55638
56590
  }
55639
56591
  readArtifactFile(filePath, expectedID) {
55640
56592
  let raw;
55641
56593
  try {
55642
- raw = (0, import_node_fs15.readFileSync)(filePath, "utf8");
56594
+ raw = (0, import_node_fs16.readFileSync)(filePath, "utf8");
55643
56595
  } catch (error48) {
55644
56596
  throw this.invalidMetadataFile(filePath, "unreadable", error48);
55645
56597
  }
@@ -55661,24 +56613,19 @@ var ServerStore = class extends EventTarget {
55661
56613
  `id ${result.data.id} does not match filename ${expectedID}`
55662
56614
  );
55663
56615
  }
55664
- if (result.data.kind === "path" && !import_node_path17.default.isAbsolute(result.data.path)) {
56616
+ if (result.data.kind === "path" && !import_node_path18.default.isAbsolute(result.data.path)) {
55665
56617
  throw this.invalidMetadataFile(filePath, "path must be absolute");
55666
56618
  }
55667
56619
  return result.data;
55668
56620
  }
55669
- /**
55670
- * Build a descriptive error for a malformed artifact metadata file. The
55671
- * message points at `docs/storage.md` so the user can reconcile the file
55672
- * (edit to match the schema, or delete to remove the artifact) without
55673
- * having to hunt for the schema definition.
55674
- */
56621
+ /** Explain the invalid metadata and recovery without relying on repository docs. */
55675
56622
  invalidMetadataFile(filePath, reason, cause) {
55676
56623
  const message = `Invalid artifact metadata file ${filePath}: ${reason}
55677
- Review docs/storage.md for the current schema, and either update the file to match or delete it to remove the artifact.`;
56624
+ Correct this metadata file and restart Television, or delete only this metadata file to unregister the artifact; its artifact content will remain untouched.`;
55678
56625
  return cause !== void 0 ? new Error(message, { cause }) : new Error(message);
55679
56626
  }
55680
56627
  writeArtifact(artifact) {
55681
- (0, import_node_fs15.writeFileSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), JSON.stringify(artifact, null, JSON_INDENT_SPACES4));
56628
+ (0, import_node_fs16.writeFileSync)(getArtifactLiveMetadataPath(this.storagePath, artifact.id), JSON.stringify(artifact, null, JSON_INDENT_SPACES5));
55682
56629
  }
55683
56630
  get channelsDir() {
55684
56631
  return getChannelsDir(this.storagePath);
@@ -55716,7 +56663,7 @@ var SKILL_INSTALL_TELEMETRY_TIMEOUT_MS2 = 1e3;
55716
56663
  var MAX_PORT2 = 65535;
55717
56664
  var MAX_PROCESS_EXIT_STATUS = 255;
55718
56665
  var HTTP_UNAUTHORIZED_STATUS2 = 401;
55719
- var HELP_POINTER = "Television ships bundled skills. The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, the `tv` CLI, and artifact workflow. Re-read it only if it is not already in context or you know the installed skill changed. Additional `tv-*` skills cover specialized artifact types and theming. Install all bundled skills with `tv skills install <path>` (e.g. ~/.openclaw/skills) or `tv skills install -i`.";
56666
+ var HELP_POINTER = "Television ships bundled skills. The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, the `tv` CLI, artifact workflow, and theming. Re-read it only if it is not already in context or you know the installed skill changed. Additional `tv-*` skills cover specialized artifact types. Install all bundled skills with `tv skills install <path>` (e.g. ~/.openclaw/skills) or `tv skills install -i`.";
55720
56667
  var PORT_ZERO_WARNING = "WARNING: --port 0 can make Television hard for later agent CLI commands to rediscover. Use a stable port and export TELEVISION_PORT in the user's environment when agents need to find the server later.";
55721
56668
  var SAFE_ARG_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/;
55722
56669
  var CLIDirectiveError = class extends Error {
@@ -55766,24 +56713,31 @@ function createDirectiveError(message) {
55766
56713
  return new CLIDirectiveError(ensureHelpPointer(message));
55767
56714
  }
55768
56715
  function getDevPackageDir() {
55769
- return import_node_path18.default.resolve(import_node_path18.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "..");
56716
+ return import_node_path19.default.resolve(import_node_path19.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "..");
55770
56717
  }
55771
56718
  function resolveVercelSkillsInstallerBin() {
55772
56719
  if (typeof require === "function" && typeof require.resolve === "function") {
55773
56720
  return require.resolve("skills/bin/cli.mjs");
55774
56721
  }
55775
- const localRequire = (0, import_node_module.createRequire)(import_node_path18.default.join(getDevPackageDir(), "package.json"));
56722
+ const localRequire = (0, import_node_module.createRequire)(import_node_path19.default.join(getDevPackageDir(), "package.json"));
55776
56723
  return localRequire.resolve("skills/bin/cli.mjs");
55777
56724
  }
55778
56725
  function readCLIVersion() {
55779
- if ("0.1.216".length > 0) {
55780
- return "0.1.216";
56726
+ if ("1.3.3".length > 0) {
56727
+ return "1.3.3";
55781
56728
  }
55782
- const devPackageJsonPath = import_node_path18.default.join(getDevPackageDir(), "package.json");
55783
- if (!(0, import_node_fs16.existsSync)(devPackageJsonPath)) {
56729
+ const devPackageJsonPath = import_node_path19.default.join(getDevPackageDir(), "package.json");
56730
+ if (!(0, import_node_fs17.existsSync)(devPackageJsonPath)) {
55784
56731
  throw new Error("Could not resolve package.json for repo-local CLI version.");
55785
56732
  }
55786
- return JSON.parse((0, import_node_fs16.readFileSync)(devPackageJsonPath, "utf8")).version;
56733
+ return JSON.parse((0, import_node_fs17.readFileSync)(devPackageJsonPath, "utf8")).version;
56734
+ }
56735
+ function readCLIVersionOutput(resolveHomeDir) {
56736
+ const releaseVersion = readCLIVersion();
56737
+ if (false) {
56738
+ return `${releaseVersion} (commit ${void 0})`;
56739
+ }
56740
+ return releaseVersion;
55787
56741
  }
55788
56742
  function inspectTelemetryBuildConfig(env = {}, options = {}) {
55789
56743
  const project = resolvePostHogProject(env, options);
@@ -55803,9 +56757,9 @@ function buildAgentHelpNote() {
55803
56757
  return [
55804
56758
  "",
55805
56759
  "Agent workflow note:",
55806
- " The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, the `tv` CLI, and artifact workflow.",
56760
+ " The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, the `tv` CLI, artifact workflow, and theming.",
55807
56761
  " Re-read it only if it is not already in context or you know the installed skill changed.",
55808
- " Additional `tv-*` skills cover specialized artifact types and theming.",
56762
+ " Additional `tv-*` skills cover specialized artifact types.",
55809
56763
  " Install all bundled skills: `tv skills install <path>` (e.g. ~/.openclaw/skills) or `tv skills install -i`."
55810
56764
  ].join("\n");
55811
56765
  }
@@ -55813,11 +56767,11 @@ function buildArtifactWorkflowHelpNote(includeHTMLNote) {
55813
56767
  const lines = [
55814
56768
  "",
55815
56769
  "Agent note:",
55816
- " The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, and artifact workflow.",
56770
+ " The main skill is `television` \u2014 keep its guidance available for channels, lifecycle, the `tv` CLI, artifact workflow, and theming.",
55817
56771
  " Re-read it only if it is not already in context or you know the installed skill changed."
55818
56772
  ];
55819
56773
  if (includeHTMLNote) {
55820
- lines.push(" Additional `tv-*` skills cover specialized artifact types and theming.");
56774
+ lines.push(" Additional `tv-*` skills cover specialized artifact types.");
55821
56775
  lines.push(" Install all bundled skills: `tv skills install <path>` (e.g. ~/.openclaw/skills) or `tv skills install -i`.");
55822
56776
  }
55823
56777
  return lines.join("\n");
@@ -55861,7 +56815,7 @@ function formatCLIError(error48, argv = []) {
55861
56815
  const status = typeof error48.status === "number" ? Number(error48.status) : void 0;
55862
56816
  if (status === HTTP_UNAUTHORIZED_STATUS2) {
55863
56817
  return ensureHelpPointer(
55864
- `Television server at ${serverURL} rejected the request as unauthorized. Check the token in ${import_node_path18.default.join(storagePathFromArgv(argv) ?? getTelevisionStoragePath(), "state", "token")}.`
56818
+ `Television server at ${serverURL} rejected the request as unauthorized. Check the token in ${import_node_path19.default.join(storagePathFromArgv(argv) ?? getTelevisionStoragePath(), "state", "token")}.`
55865
56819
  );
55866
56820
  }
55867
56821
  if (status !== void 0) {
@@ -55912,66 +56866,76 @@ function resolveServeAuth(argv, hasListen) {
55912
56866
  }
55913
56867
  function resolveStaticDir() {
55914
56868
  if (process.argv[1]) {
55915
- const entryDir = import_node_path18.default.dirname((0, import_node_fs16.realpathSync)(process.argv[1]));
55916
- const resolved = import_node_path18.default.resolve(entryDir, "./web");
55917
- return (0, import_node_fs16.existsSync)(import_node_path18.default.join(resolved, "index.html")) ? resolved : void 0;
56869
+ const entryDir = import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1]));
56870
+ const resolved = import_node_path19.default.resolve(entryDir, "./web");
56871
+ return (0, import_node_fs17.existsSync)(import_node_path19.default.join(resolved, "index.html")) ? resolved : void 0;
55918
56872
  }
55919
- const devStaticPath = import_node_path18.default.resolve(getDevPackageDir(), "../web/dist");
55920
- return (0, import_node_fs16.existsSync)(import_node_path18.default.join(devStaticPath, "index.html")) ? devStaticPath : void 0;
56873
+ const devStaticPath = import_node_path19.default.resolve(getDevPackageDir(), "../web/dist");
56874
+ return (0, import_node_fs17.existsSync)(import_node_path19.default.join(devStaticPath, "index.html")) ? devStaticPath : void 0;
55921
56875
  }
55922
56876
  function resolveBundledViewsPath() {
55923
56877
  if (process.argv[1]) {
55924
- const entryDir = import_node_path18.default.dirname((0, import_node_fs16.realpathSync)(process.argv[1]));
55925
- const resolved = import_node_path18.default.resolve(entryDir, "./views");
55926
- return (0, import_node_fs16.existsSync)(resolved) ? resolved : void 0;
56878
+ const entryDir = import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1]));
56879
+ const resolved = import_node_path19.default.resolve(entryDir, "./views");
56880
+ return (0, import_node_fs17.existsSync)(resolved) ? resolved : void 0;
55927
56881
  }
55928
- const devViewsPath = import_node_path18.default.join(getDevPackageDir(), "dist/views");
55929
- return (0, import_node_fs16.existsSync)(devViewsPath) ? devViewsPath : void 0;
56882
+ const devViewsPath = import_node_path19.default.join(getDevPackageDir(), "dist/views");
56883
+ return (0, import_node_fs17.existsSync)(devViewsPath) ? devViewsPath : void 0;
55930
56884
  }
55931
56885
  function resolveCanonicalDir() {
55932
56886
  if (process.argv[1]) {
55933
- const entryDir = import_node_path18.default.dirname((0, import_node_fs16.realpathSync)(process.argv[1]));
55934
- const resolved = import_node_path18.default.resolve(entryDir, "./canonical/v1");
55935
- return (0, import_node_fs16.existsSync)(resolved) ? resolved : void 0;
56887
+ const entryDir = import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1]));
56888
+ const resolved = import_node_path19.default.resolve(entryDir, "./canonical");
56889
+ return (0, import_node_fs17.existsSync)(resolved) ? resolved : void 0;
55936
56890
  }
55937
- const devCanonicalPath = import_node_path18.default.resolve(
56891
+ const devCanonicalPath = import_node_path19.default.resolve(
55938
56892
  getDevPackageDir(),
55939
- "../server/dist/canonical/v1"
56893
+ "../server/dist/canonical"
55940
56894
  );
55941
- return (0, import_node_fs16.existsSync)(devCanonicalPath) ? devCanonicalPath : void 0;
56895
+ return (0, import_node_fs17.existsSync)(devCanonicalPath) ? devCanonicalPath : void 0;
55942
56896
  }
55943
56897
  function resolveOnboardingContentPath() {
55944
56898
  if (process.argv[1]) {
55945
- const entryDir = import_node_path18.default.dirname((0, import_node_fs16.realpathSync)(process.argv[1]));
55946
- const resolved = import_node_path18.default.resolve(entryDir, "./onboarding");
55947
- return (0, import_node_fs16.existsSync)(resolved) ? resolved : void 0;
56899
+ const entryDir = import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1]));
56900
+ const resolved = import_node_path19.default.resolve(entryDir, "./onboarding");
56901
+ return (0, import_node_fs17.existsSync)(resolved) ? resolved : void 0;
55948
56902
  }
55949
- const devPath = import_node_path18.default.resolve(
56903
+ const devPath = import_node_path19.default.resolve(
55950
56904
  getDevPackageDir(),
55951
56905
  "../server/assets/onboarding-channels"
55952
56906
  );
55953
- return (0, import_node_fs16.existsSync)(devPath) ? devPath : void 0;
56907
+ return (0, import_node_fs17.existsSync)(devPath) ? devPath : void 0;
56908
+ }
56909
+ function resolveBundledThemesPath() {
56910
+ if (process.argv[1]) {
56911
+ const entryDir = import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1]));
56912
+ const resolved = import_node_path19.default.resolve(entryDir, "./themes");
56913
+ return (0, import_node_fs17.existsSync)(resolved) ? resolved : void 0;
56914
+ }
56915
+ const devPath = import_node_path19.default.resolve(getDevPackageDir(), "../server/assets/themes");
56916
+ return (0, import_node_fs17.existsSync)(devPath) ? devPath : void 0;
55954
56917
  }
55955
56918
  function resolveBundledSkillsRoot() {
55956
- const builtPath = typeof process.argv[1] === "string" ? import_node_path18.default.resolve(import_node_path18.default.dirname((0, import_node_fs16.realpathSync)(process.argv[1])), "./skills") : void 0;
55957
- if (builtPath && (0, import_node_fs16.existsSync)(builtPath)) {
56919
+ const builtPath = typeof process.argv[1] === "string" ? import_node_path19.default.resolve(import_node_path19.default.dirname((0, import_node_fs17.realpathSync)(process.argv[1])), "./skills") : void 0;
56920
+ if (builtPath && (0, import_node_fs17.existsSync)(builtPath)) {
55958
56921
  return builtPath;
55959
56922
  }
55960
- const devPath = import_node_path18.default.resolve(getDevPackageDir(), "../skills/dist");
55961
- return (0, import_node_fs16.existsSync)(devPath) ? devPath : void 0;
56923
+ const devPath = import_node_path19.default.resolve(getDevPackageDir(), "../skills/dist");
56924
+ return (0, import_node_fs17.existsSync)(devPath) ? devPath : void 0;
55962
56925
  }
55963
56926
  function copyBundledSkillsToDestination(bundledSkillsRoot, destinationRoot) {
55964
- if ((0, import_node_fs16.existsSync)(destinationRoot) && !(0, import_node_fs16.statSync)(destinationRoot).isDirectory()) {
56927
+ if ((0, import_node_fs17.existsSync)(destinationRoot) && !(0, import_node_fs17.statSync)(destinationRoot).isDirectory()) {
55965
56928
  throw new Error(`Skills destination is not a directory: ${destinationRoot}`);
55966
56929
  }
55967
- (0, import_node_fs16.mkdirSync)(destinationRoot, { recursive: true });
56930
+ (0, import_node_fs17.mkdirSync)(destinationRoot, { recursive: true });
56931
+ (0, import_node_fs17.rmSync)(import_node_path19.default.join(destinationRoot, "tv-theme"), { recursive: true, force: true });
55968
56932
  const copied = [];
55969
- const entries = (0, import_node_fs16.readdirSync)(bundledSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b2) => a.name.localeCompare(b2.name));
56933
+ const entries = (0, import_node_fs17.readdirSync)(bundledSkillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).sort((a, b2) => a.name.localeCompare(b2.name));
55970
56934
  for (const entry of entries) {
55971
- const sourcePath = import_node_path18.default.join(bundledSkillsRoot, entry.name);
55972
- const destinationPath = import_node_path18.default.join(destinationRoot, entry.name);
55973
- (0, import_node_fs16.rmSync)(destinationPath, { recursive: true, force: true });
55974
- (0, import_node_fs16.cpSync)(sourcePath, destinationPath, { recursive: true });
56935
+ const sourcePath = import_node_path19.default.join(bundledSkillsRoot, entry.name);
56936
+ const destinationPath = import_node_path19.default.join(destinationRoot, entry.name);
56937
+ (0, import_node_fs17.rmSync)(destinationPath, { recursive: true, force: true });
56938
+ (0, import_node_fs17.cpSync)(sourcePath, destinationPath, { recursive: true });
55975
56939
  copied.push({ name: entry.name, sourcePath, destinationPath });
55976
56940
  }
55977
56941
  return copied;
@@ -56054,11 +57018,11 @@ function formatCommanderError(argv, error48) {
56054
57018
  return null;
56055
57019
  }
56056
57020
  function readAuthToken(storagePath) {
56057
- const tokenPath = import_node_path18.default.join(storagePath, "state", "token");
56058
- if (!(0, import_node_fs16.existsSync)(tokenPath)) {
57021
+ const tokenPath = import_node_path19.default.join(storagePath, "state", "token");
57022
+ if (!(0, import_node_fs17.existsSync)(tokenPath)) {
56059
57023
  return void 0;
56060
57024
  }
56061
- const token = (0, import_node_fs16.readFileSync)(tokenPath, "utf8").trim();
57025
+ const token = (0, import_node_fs17.readFileSync)(tokenPath, "utf8").trim();
56062
57026
  return token.length > 0 ? token : void 0;
56063
57027
  }
56064
57028
  function readOrCreateAuthToken(storagePath) {
@@ -56104,6 +57068,7 @@ function createEnvironment(environment) {
56104
57068
  storagePath: options.storagePath ?? getTelevisionStoragePath(),
56105
57069
  ...options.bundledViewsPath ? { bundledViewsPath: options.bundledViewsPath } : {},
56106
57070
  ...options.onboardingContentPath ? { onboardingContentPath: options.onboardingContentPath } : {},
57071
+ ...options.bundledThemesPath ? { bundledThemesPath: options.bundledThemesPath } : {},
56107
57072
  installOnboardingChannels: true
56108
57073
  }),
56109
57074
  listen: options.listen,
@@ -56134,6 +57099,7 @@ function createEnvironment(environment) {
56134
57099
  resolveCanonicalDir: environment.resolveCanonicalDir ?? (() => resolveCanonicalDir()),
56135
57100
  resolveBundledViewsPath: environment.resolveBundledViewsPath ?? (() => resolveBundledViewsPath()),
56136
57101
  resolveOnboardingContentPath: environment.resolveOnboardingContentPath ?? (() => resolveOnboardingContentPath()),
57102
+ resolveBundledThemesPath: environment.resolveBundledThemesPath ?? (() => resolveBundledThemesPath()),
56137
57103
  resolveBundledSkillsRoot: environment.resolveBundledSkillsRoot ?? (() => resolveBundledSkillsRoot()),
56138
57104
  resolveHomeDir: environment.resolveHomeDir ?? (() => import_node_os5.default.homedir()),
56139
57105
  runSkillsInstaller: environment.runSkillsInstaller ?? (async (args) => {
@@ -56186,7 +57152,7 @@ function createProgram(env, argv = []) {
56186
57152
  log(storagePath, "persisted service uninstalled", { daemonName: DAEMON_NAME, outcome });
56187
57153
  writeJSON(env.stdout, { status: "stopped" });
56188
57154
  };
56189
- program2.name("tv").description("Television \u2014 virtual display for agents").version(readCLIVersion()).option("-v", "Output the version number").showSuggestionAfterError(false).addHelpText("after", buildAgentHelpNote()).configureOutput({
57155
+ program2.name("tv").description("Television \u2014 virtual display for agents").version(readCLIVersionOutput(env.resolveHomeDir)).option("-v", "Output the version number").showSuggestionAfterError(false).addHelpText("after", buildAgentHelpNote()).configureOutput({
56190
57156
  writeOut: (str) => env.stdout.write(str),
56191
57157
  writeErr: (str) => env.stderr.write(str),
56192
57158
  outputError: () => {
@@ -56216,7 +57182,7 @@ function createProgram(env, argv = []) {
56216
57182
  );
56217
57183
  }
56218
57184
  const clientPort = resolveClientPort(opts.port);
56219
- const storagePath = import_node_path18.default.resolve(opts.storagePath ?? getTelevisionStoragePath());
57185
+ const storagePath = import_node_path19.default.resolve(opts.storagePath ?? getTelevisionStoragePath());
56220
57186
  const profile2 = resolveACPAgentProfile(process.env);
56221
57187
  const daemonEnv = buildPersistedACPEnvironment(process.env, { developerHome: env.resolveHomeDir() });
56222
57188
  if (process.env.TELEVISION_PORT !== void 0 && process.env.TELEVISION_PORT !== "") {
@@ -56282,6 +57248,7 @@ function createProgram(env, argv = []) {
56282
57248
  throw error48;
56283
57249
  }
56284
57250
  const onboardingContentPath = env.resolveOnboardingContentPath();
57251
+ const bundledThemesPath = env.resolveBundledThemesPath();
56285
57252
  const server = env.createServer({
56286
57253
  listen: opts.listen,
56287
57254
  auth,
@@ -56291,6 +57258,7 @@ function createProgram(env, argv = []) {
56291
57258
  canonicalDir: env.resolveCanonicalDir(),
56292
57259
  bundledViewsPath: env.resolveBundledViewsPath(),
56293
57260
  ...onboardingContentPath !== void 0 ? { onboardingContentPath } : {},
57261
+ ...bundledThemesPath !== void 0 ? { bundledThemesPath } : {},
56294
57262
  launchMode: resolveTelemetryLaunchMode(process.env),
56295
57263
  ...opts.installedByAgent === void 0 ? {} : { installedByAgent: opts.installedByAgent },
56296
57264
  ...profile ? { acpProfile: profile } : {}
@@ -56409,6 +57377,14 @@ function createProgram(env, argv = []) {
56409
57377
  }
56410
57378
  writeLine(env.stdout, `Channel created: ${channel.id} (${channel.name})`);
56411
57379
  });
57380
+ program2.command("update-channel").description("Rename a channel in place").requiredOption("--channel <id>", "Channel ID").requiredOption("--name <name>", "New channel name").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").action(async (opts) => {
57381
+ const client = createAuthenticatedClient(opts);
57382
+ const { channel } = await client.channels.update({
57383
+ channelID: opts.channel,
57384
+ name: opts.name
57385
+ });
57386
+ writeLine(env.stdout, `Channel updated: ${channel.id} (${channel.name})`);
57387
+ });
56412
57388
  program2.command("remove-channel").description(
56413
57389
  "Delete a channel. Artifacts on the channel are permanently deleted from the Television registry; their pointed-to path or URL targets are not touched."
56414
57390
  ).requiredOption("--channel <id>", "Channel ID").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").action(async (opts) => {
@@ -56429,7 +57405,7 @@ function createProgram(env, argv = []) {
56429
57405
  writeJSON(env.stdout, await client.channels.get({ channelID: opts.channel }));
56430
57406
  });
56431
57407
  program2.command("focus-status").description(
56432
- "Print display state as JSON: the active (persistently focused) channel ID and the active theme name."
57408
+ "Print display state as JSON: the active (persistently focused) channel ID and the active theme ID."
56433
57409
  ).option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").action(async (opts) => {
56434
57410
  const client = createAuthenticatedClient(opts);
56435
57411
  const display = await client.display.get();
@@ -56447,19 +57423,44 @@ function createProgram(env, argv = []) {
56447
57423
  writeLine(env.stdout, `Focused channel ${opts.channel}.`);
56448
57424
  });
56449
57425
  program2.command("set-theme").description(
56450
- 'Activate a display theme by name. The special theme name `default` clears the active theme and falls back to Television\'s bundled default look. A user-authored theme literally named `default` cannot be activated through this CLI convenience; call `PATCH /display { activeThemeName: "default" }` directly if you need that exact theme.'
56451
- ).argument("<theme>", "Theme name to activate, or `default` to clear the active theme").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").addHelpText("after", [
57426
+ "Activate an installed display theme by its exact theme ID, or use `none` to run without a theme."
57427
+ ).argument("<theme>", "Exact theme ID to activate, or `none` to use no theme").option("--port <number>", "Server port", parsePortOption).option("--storage-path <path>", "Directory containing the Television token").addHelpText("after", [
56452
57428
  "",
56453
57429
  "Examples:",
56454
57430
  " tv set-theme paperlike Activate the `paperlike` theme.",
56455
- " tv set-theme default Clear the active theme and use the bundled defaults.",
57431
+ " tv set-theme none Use no theme (case-insensitive).",
56456
57432
  "",
56457
57433
  "Note:",
56458
- " `default` is a CLI sentinel that clears the active theme.",
56459
- ' To activate a user-authored theme literally named `default`, call `PATCH /display { activeThemeName: "default" }` directly.'
57434
+ " `none` means no theme."
56460
57435
  ].join("\n")).action(async (theme, opts) => {
56461
57436
  const client = createAuthenticatedClient(opts);
56462
- await client.display.patch({ activeThemeName: theme === "default" ? null : theme });
57437
+ let previousSelection = null;
57438
+ let previousSelectionAvailable = false;
57439
+ try {
57440
+ previousSelection = (await client.display.get()).activeThemeName;
57441
+ previousSelectionAvailable = true;
57442
+ } catch {
57443
+ }
57444
+ const nextSelection = theme.toLowerCase() === "none" ? null : theme;
57445
+ if (nextSelection !== null) {
57446
+ const registry2 = await client.themes.refresh();
57447
+ const validationError = registry2.errors.find((entry) => entry.folder === theme);
57448
+ if (validationError !== void 0) {
57449
+ throw new ValidationError(`Theme folder ${theme} is invalid: ${validationError.error}`);
57450
+ }
57451
+ }
57452
+ await client.display.patch({ activeThemeName: nextSelection });
57453
+ const nextLabel = nextSelection ?? "None";
57454
+ if (!previousSelectionAvailable) {
57455
+ writeLine(env.stdout, `Active theme: '${nextLabel}'.`);
57456
+ return;
57457
+ }
57458
+ const previousLabel = previousSelection ?? "None";
57459
+ if (previousSelection === nextSelection) {
57460
+ writeLine(env.stdout, `Active theme unchanged: '${nextLabel}'.`);
57461
+ return;
57462
+ }
57463
+ writeLine(env.stdout, `Active theme changed from '${previousLabel}' to '${nextLabel}'.`);
56463
57464
  });
56464
57465
  program2.command("focus-artifact").description(
56465
57466
  "Send a transient focus nudge for a specific artifact. Connected clients select the artifact's tab page and switch to its channel when needed. This is NOT persisted as state \u2014 there is no concept of a 'focused artifact' that survives reconnects (the focused channel is persistent, but artifact focus is a one-shot event)."
@@ -56500,7 +57501,7 @@ function createProgram(env, argv = []) {
56500
57501
  if (!bundledTelevisionSkillsCollectionRoot) {
56501
57502
  throw new Error("Could not resolve the bundled Television skills root.");
56502
57503
  }
56503
- const destinationRoot = import_node_path18.default.resolve(inputPath);
57504
+ const destinationRoot = import_node_path19.default.resolve(inputPath);
56504
57505
  const copied = copyBundledSkillsToDestination(bundledTelevisionSkillsCollectionRoot, destinationRoot);
56505
57506
  writeLine(env.stdout, `Copied ${copied.length} bundled Television skill(s):`);
56506
57507
  for (const skill of copied) {
@@ -56535,7 +57536,7 @@ function createProgram(env, argv = []) {
56535
57536
  const client = createAuthenticatedClient(opts);
56536
57537
  const health = await client.health();
56537
57538
  result.healthy = true;
56538
- result.version = health.version ?? "unknown (pre-0.1.179)";
57539
+ if (health.version !== void 0) result.version = health.version;
56539
57540
  result.bindAddresses = health.bindAddresses;
56540
57541
  result.port = health.port;
56541
57542
  result.telemetry = await client.telemetry.status();
@@ -56559,7 +57560,7 @@ async function runCLI(argv, environment = {}) {
56559
57560
  const env = createEnvironment(environment);
56560
57561
  try {
56561
57562
  if (argv.length === 1 && argv[0] === "-v") {
56562
- writeLine(env.stdout, readCLIVersion());
57563
+ writeLine(env.stdout, readCLIVersionOutput(env.resolveHomeDir));
56563
57564
  return 0;
56564
57565
  }
56565
57566
  const program2 = createProgram(env, argv);
@@ -56598,6 +57599,7 @@ if (!isVitestRuntime()) {
56598
57599
  inspectTelemetryBuildConfig,
56599
57600
  listVisibleCLICommandNames,
56600
57601
  resolveBundledSkillsRoot,
57602
+ resolveBundledThemesPath,
56601
57603
  resolveBundledViewsPath,
56602
57604
  resolveCanonicalDir,
56603
57605
  resolveOnboardingContentPath,