mellos-mapping 0.20.3 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +130 -69
  2. package/README.zh-CN.md +126 -65
  3. package/dist/hook-session-start.mjs +28 -20
  4. package/dist/mmap.mjs +303 -171
  5. package/dist/preview.mjs +1457 -0
  6. package/dist/server.mjs +1984 -741
  7. package/dist/store-paths.mjs +69 -22
  8. package/dist/terminal-worker.mjs +3331 -0
  9. package/dist/watch.mjs +957 -571
  10. package/dist/web/TERMINAL-LICENSES.txt +70 -0
  11. package/dist/web/app.css +967 -0
  12. package/dist/web/app.js +1356 -0
  13. package/dist/web/index.html +9 -0
  14. package/dist/web/terminal.css +9 -0
  15. package/dist/web/terminal.html +7 -0
  16. package/dist/web/terminal.js +9293 -0
  17. package/dist/web/xterm.css +285 -0
  18. package/dist/web.mjs +5646 -0
  19. package/docs/codex.md +189 -0
  20. package/docs/map-api.md +148 -0
  21. package/lib/domain/context.d.ts +11 -0
  22. package/lib/domain/context.js +27 -0
  23. package/lib/domain/text.d.ts +9 -0
  24. package/lib/domain/text.js +54 -0
  25. package/lib/domain/types.d.ts +3 -0
  26. package/lib/preview/index.d.ts +3 -0
  27. package/lib/preview/index.js +3 -0
  28. package/lib/preview/markdown.d.ts +8 -0
  29. package/lib/preview/markdown.js +54 -0
  30. package/lib/preview/presentation.d.ts +6 -0
  31. package/lib/preview/presentation.js +14 -0
  32. package/lib/preview/publisher.d.ts +23 -0
  33. package/lib/preview/publisher.js +143 -0
  34. package/lib/preview/svg.d.ts +3 -0
  35. package/lib/preview/svg.js +74 -0
  36. package/lib/preview/text.d.ts +4 -0
  37. package/lib/preview/text.js +13 -0
  38. package/lib/render/canvas.d.ts +1 -1
  39. package/lib/render/canvas.js +4 -2
  40. package/lib/render/draw.js +9 -6
  41. package/lib/render/render.d.ts +6 -0
  42. package/lib/render/render.js +50 -18
  43. package/lib/render/width.js +3 -1
  44. package/lib/store/atomic.d.ts +18 -0
  45. package/lib/store/atomic.js +86 -0
  46. package/lib/store/channels.d.ts +40 -0
  47. package/lib/store/channels.js +135 -0
  48. package/lib/store/format.js +21 -4
  49. package/lib/store/json-text.d.ts +9 -0
  50. package/lib/store/json-text.js +16 -0
  51. package/lib/store/maps.d.ts +12 -0
  52. package/lib/store/maps.js +42 -0
  53. package/lib/store/migration.d.ts +12 -0
  54. package/lib/store/migration.js +46 -0
  55. package/lib/store/pages.d.ts +46 -0
  56. package/lib/store/pages.js +89 -0
  57. package/lib/store/policy.d.ts +74 -0
  58. package/lib/store/policy.js +144 -0
  59. package/lib/store/project.d.ts +2 -0
  60. package/lib/store/project.js +29 -0
  61. package/lib/store/store.d.ts +15 -257
  62. package/lib/store/store.js +16 -695
  63. package/lib/store/transaction.d.ts +12 -0
  64. package/lib/store/transaction.js +91 -0
  65. package/lib/store/viewers.d.ts +81 -0
  66. package/lib/store/viewers.js +186 -0
  67. package/package.json +27 -5
  68. package/scripts/codex-cli.mjs +42 -0
  69. package/scripts/codex-register.mjs +27 -94
  70. package/scripts/mmap.mjs +27 -17
  71. package/scripts/open-pane.mjs +37 -18
  72. package/scripts/pane-core.mjs +57 -220
  73. package/scripts/terminal-session.mjs +137 -0
  74. package/scripts/tmux-session.mjs +90 -0
  75. package/scripts/watcher-command.mjs +16 -0
package/dist/server.mjs CHANGED
@@ -15,9 +15,9 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __copyProps = (to, from, except, desc) => {
17
17
  if (from && typeof from === "object" || typeof from === "function") {
18
- for (let key of __getOwnPropNames(from))
19
- if (!__hasOwnProp.call(to, key) && key !== except)
20
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ for (let key2 of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key2) && key2 !== except)
20
+ __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
21
21
  }
22
22
  return to;
23
23
  };
@@ -166,15 +166,15 @@ var require_code = __commonJS({
166
166
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
167
167
  }
168
168
  exports.safeStringify = safeStringify;
169
- function getProperty(key) {
170
- return typeof key == "string" && exports.IDENTIFIER.test(key) ? new _Code(`.${key}`) : _`[${key}]`;
169
+ function getProperty(key2) {
170
+ return typeof key2 == "string" && exports.IDENTIFIER.test(key2) ? new _Code(`.${key2}`) : _`[${key2}]`;
171
171
  }
172
172
  exports.getProperty = getProperty;
173
- function getEsmExportName(key) {
174
- if (typeof key == "string" && exports.IDENTIFIER.test(key)) {
175
- return new _Code(`${key}`);
173
+ function getEsmExportName(key2) {
174
+ if (typeof key2 == "string" && exports.IDENTIFIER.test(key2)) {
175
+ return new _Code(`${key2}`);
176
176
  }
177
- throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`);
177
+ throw new Error(`CodeGen: invalid export name: ${key2}, use explicit $id name mapping`);
178
178
  }
179
179
  exports.getEsmExportName = getEsmExportName;
180
180
  function regexpCode(rx) {
@@ -801,11 +801,11 @@ var require_codegen = __commonJS({
801
801
  // returns code for object literal for the passed argument list of key-value pairs
802
802
  object(...keyValues) {
803
803
  const code = ["{"];
804
- for (const [key, value] of keyValues) {
804
+ for (const [key2, value] of keyValues) {
805
805
  if (code.length > 1)
806
806
  code.push(",");
807
- code.push(key);
808
- if (key !== value || this.opts.es5) {
807
+ code.push(key2);
808
+ if (key2 !== value || this.opts.es5) {
809
809
  code.push(":");
810
810
  (0, code_1.addCodeArg)(code, value);
811
811
  }
@@ -1058,10 +1058,10 @@ var require_util = __commonJS({
1058
1058
  var codegen_1 = require_codegen();
1059
1059
  var code_1 = require_code();
1060
1060
  function toHash(arr) {
1061
- const hash = {};
1061
+ const hash2 = {};
1062
1062
  for (const item of arr)
1063
- hash[item] = true;
1064
- return hash;
1063
+ hash2[item] = true;
1064
+ return hash2;
1065
1065
  }
1066
1066
  exports.toHash = toHash;
1067
1067
  function alwaysValidSchema(it, schema) {
@@ -1080,17 +1080,17 @@ var require_util = __commonJS({
1080
1080
  if (typeof schema === "boolean")
1081
1081
  return;
1082
1082
  const rules = self.RULES.keywords;
1083
- for (const key in schema) {
1084
- if (!rules[key])
1085
- checkStrictMode(it, `unknown keyword: "${key}"`);
1083
+ for (const key2 in schema) {
1084
+ if (!rules[key2])
1085
+ checkStrictMode(it, `unknown keyword: "${key2}"`);
1086
1086
  }
1087
1087
  }
1088
1088
  exports.checkUnknownRules = checkUnknownRules;
1089
1089
  function schemaHasRules(schema, rules) {
1090
1090
  if (typeof schema == "boolean")
1091
1091
  return !schema;
1092
- for (const key in schema)
1093
- if (rules[key])
1092
+ for (const key2 in schema)
1093
+ if (rules[key2])
1094
1094
  return true;
1095
1095
  return false;
1096
1096
  }
@@ -1098,8 +1098,8 @@ var require_util = __commonJS({
1098
1098
  function schemaHasRulesButRef(schema, RULES) {
1099
1099
  if (typeof schema == "boolean")
1100
1100
  return !schema;
1101
- for (const key in schema)
1102
- if (key !== "$ref" && RULES.all[key])
1101
+ for (const key2 in schema)
1102
+ if (key2 !== "$ref" && RULES.all[key2])
1103
1103
  return true;
1104
1104
  return false;
1105
1105
  }
@@ -1677,8 +1677,8 @@ var require_defaults = __commonJS({
1677
1677
  function assignDefaults(it, ty) {
1678
1678
  const { properties, items } = it.schema;
1679
1679
  if (ty === "object" && properties) {
1680
- for (const key in properties) {
1681
- assignDefault(it, key, properties[key].default);
1680
+ for (const key2 in properties) {
1681
+ assignDefault(it, key2, properties[key2].default);
1682
1682
  }
1683
1683
  } else if (ty === "array" && Array.isArray(items)) {
1684
1684
  items.forEach((sch, i) => assignDefault(it, i, sch.default));
@@ -1760,7 +1760,7 @@ var require_code2 = __commonJS({
1760
1760
  return allSchemaProperties(schemaMap).filter((p) => !(0, util_1.alwaysValidSchema)(it, schemaMap[p]));
1761
1761
  }
1762
1762
  exports.schemaProperties = schemaProperties;
1763
- function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it }, func, context, passSchema) {
1763
+ function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it }, func, context2, passSchema) {
1764
1764
  const dataAndSchema = passSchema ? (0, codegen_1._)`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data;
1765
1765
  const valCxt = [
1766
1766
  [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, errorPath)],
@@ -1771,7 +1771,7 @@ var require_code2 = __commonJS({
1771
1771
  if (it.opts.dynamicRef)
1772
1772
  valCxt.push([names_1.default.dynamicAnchors, names_1.default.dynamicAnchors]);
1773
1773
  const args = (0, codegen_1._)`${dataAndSchema}, ${gen.object(...valCxt)}`;
1774
- return context !== codegen_1.nil ? (0, codegen_1._)`${func}.call(${context}, ${args})` : (0, codegen_1._)`${func}(${args})`;
1774
+ return context2 !== codegen_1.nil ? (0, codegen_1._)`${func}.call(${context2}, ${args})` : (0, codegen_1._)`${func}(${args})`;
1775
1775
  }
1776
1776
  exports.callValidateCode = callValidateCode;
1777
1777
  var newRegExp = (0, codegen_1._)`new RegExp`;
@@ -2062,8 +2062,8 @@ var require_fast_deep_equal = __commonJS({
2062
2062
  for (i = length; i-- !== 0; )
2063
2063
  if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
2064
2064
  for (i = length; i-- !== 0; ) {
2065
- var key = keys[i];
2066
- if (!equal(a[key], b[key])) return false;
2065
+ var key2 = keys[i];
2066
+ if (!equal(a[key2], b[key2])) return false;
2067
2067
  }
2068
2068
  return true;
2069
2069
  }
@@ -2135,20 +2135,20 @@ var require_json_schema_traverse = __commonJS({
2135
2135
  function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
2136
2136
  if (schema && typeof schema == "object" && !Array.isArray(schema)) {
2137
2137
  pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
2138
- for (var key in schema) {
2139
- var sch = schema[key];
2138
+ for (var key2 in schema) {
2139
+ var sch = schema[key2];
2140
2140
  if (Array.isArray(sch)) {
2141
- if (key in traverse.arrayKeywords) {
2141
+ if (key2 in traverse.arrayKeywords) {
2142
2142
  for (var i = 0; i < sch.length; i++)
2143
- _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key + "/" + i, rootSchema, jsonPtr, key, schema, i);
2143
+ _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key2 + "/" + i, rootSchema, jsonPtr, key2, schema, i);
2144
2144
  }
2145
- } else if (key in traverse.propsKeywords) {
2145
+ } else if (key2 in traverse.propsKeywords) {
2146
2146
  if (sch && typeof sch == "object") {
2147
2147
  for (var prop in sch)
2148
- _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop);
2148
+ _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key2 + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key2, schema, prop);
2149
2149
  }
2150
- } else if (key in traverse.keywords || opts.allKeys && !(key in traverse.skipKeywords)) {
2151
- _traverse(opts, pre, post, sch, jsonPtr + "/" + key, rootSchema, jsonPtr, key, schema);
2150
+ } else if (key2 in traverse.keywords || opts.allKeys && !(key2 in traverse.skipKeywords)) {
2151
+ _traverse(opts, pre, post, sch, jsonPtr + "/" + key2, rootSchema, jsonPtr, key2, schema);
2152
2152
  }
2153
2153
  }
2154
2154
  post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
@@ -2205,10 +2205,10 @@ var require_resolve = __commonJS({
2205
2205
  "$dynamicAnchor"
2206
2206
  ]);
2207
2207
  function hasRef(schema) {
2208
- for (const key in schema) {
2209
- if (REF_KEYWORDS.has(key))
2208
+ for (const key2 in schema) {
2209
+ if (REF_KEYWORDS.has(key2))
2210
2210
  return true;
2211
- const sch = schema[key];
2211
+ const sch = schema[key2];
2212
2212
  if (Array.isArray(sch) && sch.some(hasRef))
2213
2213
  return true;
2214
2214
  if (typeof sch == "object" && hasRef(sch))
@@ -2218,14 +2218,14 @@ var require_resolve = __commonJS({
2218
2218
  }
2219
2219
  function countKeys(schema) {
2220
2220
  let count = 0;
2221
- for (const key in schema) {
2222
- if (key === "$ref")
2221
+ for (const key2 in schema) {
2222
+ if (key2 === "$ref")
2223
2223
  return Infinity;
2224
2224
  count++;
2225
- if (SIMPLE_INLINED.has(key))
2225
+ if (SIMPLE_INLINED.has(key2))
2226
2226
  continue;
2227
- if (typeof schema[key] == "object") {
2228
- (0, util_1.eachItem)(schema[key], (sch) => count += countKeys(sch));
2227
+ if (typeof schema[key2] == "object") {
2228
+ (0, util_1.eachItem)(schema[key2], (sch) => count += countKeys(sch));
2229
2229
  }
2230
2230
  if (count === Infinity)
2231
2231
  return Infinity;
@@ -2414,8 +2414,8 @@ var require_validate = __commonJS({
2414
2414
  function schemaCxtHasRules({ schema, self }) {
2415
2415
  if (typeof schema == "boolean")
2416
2416
  return !schema;
2417
- for (const key in schema)
2418
- if (self.RULES.all[key])
2417
+ for (const key2 in schema)
2418
+ if (self.RULES.all[key2])
2419
2419
  return true;
2420
2420
  return false;
2421
2421
  }
@@ -2981,7 +2981,7 @@ var require_compile = __commonJS({
2981
2981
  const schOrFunc = root.refs[ref];
2982
2982
  if (schOrFunc)
2983
2983
  return schOrFunc;
2984
- let _sch = resolve.call(this, root, ref);
2984
+ let _sch = resolve4.call(this, root, ref);
2985
2985
  if (_sch === void 0) {
2986
2986
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2987
2987
  const { schemaId } = this.opts;
@@ -3008,7 +3008,7 @@ var require_compile = __commonJS({
3008
3008
  function sameSchemaEnv(s1, s2) {
3009
3009
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3010
3010
  }
3011
- function resolve(root, ref) {
3011
+ function resolve4(root, ref) {
3012
3012
  let sch;
3013
3013
  while (typeof (sch = this.refs[ref]) == "string")
3014
3014
  ref = sch;
@@ -3106,9 +3106,28 @@ var require_utils = __commonJS({
3106
3106
  "use strict";
3107
3107
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3108
3108
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3109
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3109
3110
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3110
3111
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3111
- var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3112
+ var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
3113
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
3114
+ var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
3115
+ var BYTE_HEX = new Array(256);
3116
+ {
3117
+ const HEX_DIGITS = "0123456789ABCDEF";
3118
+ for (let i = 0; i < 256; i++) {
3119
+ BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
3120
+ }
3121
+ }
3122
+ function percentEncodeNonAscii(cp) {
3123
+ if (cp < 2048) {
3124
+ return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
3125
+ }
3126
+ if (cp < 65536) {
3127
+ return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3128
+ }
3129
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3130
+ }
3112
3131
  function stringArrayToHexStripped(input) {
3113
3132
  let acc = "";
3114
3133
  let code = 0;
@@ -3133,91 +3152,105 @@ var require_utils = __commonJS({
3133
3152
  }
3134
3153
  return acc;
3135
3154
  }
3155
+ var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
3156
+ var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
3157
+ var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
3136
3158
  var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
3137
- function consumeIsZone(buffer) {
3138
- buffer.length = 0;
3139
- return true;
3140
- }
3141
- function consumeHextets(buffer, address, output) {
3142
- if (buffer.length) {
3143
- const hex = stringArrayToHexStripped(buffer);
3144
- if (hex !== "") {
3145
- address.push(hex);
3146
- } else {
3147
- output.error = true;
3148
- return false;
3159
+ function isZoneIdentifier(zone) {
3160
+ if (zone.length === 0) return false;
3161
+ for (let i = 0; i < zone.length; i++) {
3162
+ if (isZoneCharacter(zone[i])) continue;
3163
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
3164
+ i += 2;
3165
+ continue;
3149
3166
  }
3150
- buffer.length = 0;
3167
+ return false;
3151
3168
  }
3152
3169
  return true;
3153
3170
  }
3154
- function getIPV6(input) {
3155
- let tokenCount = 0;
3156
- const output = { error: false, address: "", zone: "" };
3157
- const address = [];
3158
- const buffer = [];
3159
- let endipv6Encountered = false;
3160
- let endIpv6 = false;
3161
- let consume = consumeHextets;
3162
- for (let i = 0; i < input.length; i++) {
3163
- const cursor = input[i];
3164
- if (cursor === "[" || cursor === "]") {
3165
- continue;
3166
- }
3167
- if (cursor === ":") {
3168
- if (endipv6Encountered === true) {
3169
- endIpv6 = true;
3171
+ function compressIPv6ZeroRun(hextets) {
3172
+ let bestStart = -1;
3173
+ let bestLength = 0;
3174
+ let runStart = -1;
3175
+ let runLength = 0;
3176
+ for (let i = 0; i < hextets.length; i++) {
3177
+ if (hextets[i] === "0") {
3178
+ if (runStart === -1) runStart = i;
3179
+ runLength++;
3180
+ if (runLength > bestLength) {
3181
+ bestLength = runLength;
3182
+ bestStart = runStart;
3170
3183
  }
3171
- if (!consume(buffer, address, output)) {
3172
- break;
3173
- }
3174
- if (++tokenCount > 7) {
3175
- output.error = true;
3176
- break;
3177
- }
3178
- if (i > 0 && input[i - 1] === ":") {
3179
- endipv6Encountered = true;
3180
- }
3181
- address.push(":");
3182
- continue;
3183
- } else if (cursor === "%") {
3184
- if (!consume(buffer, address, output)) {
3185
- break;
3186
- }
3187
- consume = consumeIsZone;
3188
3184
  } else {
3189
- buffer.push(cursor);
3185
+ runStart = -1;
3186
+ runLength = 0;
3187
+ }
3188
+ }
3189
+ if (bestLength < 2) return hextets.join(":");
3190
+ const head = hextets.slice(0, bestStart).join(":");
3191
+ const tail = hextets.slice(bestStart + bestLength).join(":");
3192
+ return head + "::" + tail;
3193
+ }
3194
+ function normalizeIPv6Address(input) {
3195
+ const compression = input.indexOf("::");
3196
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
3197
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
3198
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
3199
+ if (compression !== -1) {
3200
+ if (left.length === 1 && left[0] === "") left.length = 0;
3201
+ if (right.length === 1 && right[0] === "") right.length = 0;
3202
+ }
3203
+ const parts = left.concat(right);
3204
+ let hextetCount = 0;
3205
+ for (let i = 0; i < parts.length; i++) {
3206
+ const part = parts[i];
3207
+ if (part === "") return void 0;
3208
+ if (part.indexOf(".") !== -1) {
3209
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
3210
+ hextetCount += 2;
3190
3211
  continue;
3191
3212
  }
3213
+ if (!isHextet(part)) return void 0;
3214
+ parts[i] = parseInt(part, 16).toString(16);
3215
+ hextetCount++;
3192
3216
  }
3193
- if (buffer.length) {
3194
- if (consume === consumeIsZone) {
3195
- output.zone = buffer.join("");
3196
- } else if (endIpv6) {
3197
- address.push(buffer.join(""));
3198
- } else {
3199
- address.push(stringArrayToHexStripped(buffer));
3200
- }
3217
+ if (compression === -1) {
3218
+ if (hextetCount !== 8) return void 0;
3219
+ return compressIPv6ZeroRun(parts);
3201
3220
  }
3202
- output.address = address.join("");
3203
- return output;
3221
+ if (hextetCount >= 8) return void 0;
3222
+ const expanded = parts.slice(0, left.length);
3223
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
3224
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
3225
+ return compressIPv6ZeroRun(expanded);
3204
3226
  }
3205
3227
  function normalizeIPv6(host) {
3206
- if (findToken(host, ":") < 2) {
3207
- return { host, isIPV6: false };
3208
- }
3209
- const ipv62 = getIPV6(host);
3210
- if (!ipv62.error) {
3211
- let newHost = ipv62.address;
3212
- let escapedHost = ipv62.address;
3213
- if (ipv62.zone) {
3214
- newHost += "%" + ipv62.zone;
3215
- escapedHost += "%25" + ipv62.zone;
3216
- }
3217
- return { host: newHost, isIPV6: true, escapedHost };
3218
- } else {
3219
- return { host, isIPV6: false };
3220
- }
3228
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
3229
+ const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
3230
+ if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
3231
+ let input = bracketed ? host.slice(1, -1) : host;
3232
+ if (bracketed && isIPvFuture(input)) {
3233
+ input = input.toLowerCase();
3234
+ return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
3235
+ }
3236
+ if (findToken(input, ":") < 2) {
3237
+ return { host, isIPV6: false, error: bracketed };
3238
+ }
3239
+ let zoneIdentifier = "";
3240
+ const zoneSeparator = input.indexOf("%");
3241
+ if (zoneSeparator !== -1) {
3242
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
3243
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
3244
+ if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
3245
+ input = input.slice(0, zoneSeparator);
3246
+ }
3247
+ const address = normalizeIPv6Address(input);
3248
+ if (address === void 0) return { host, isIPV6: false, error: true };
3249
+ return {
3250
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
3251
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
3252
+ isIPV6: true
3253
+ };
3221
3254
  }
3222
3255
  function findToken(str, token) {
3223
3256
  let ind = 0;
@@ -3336,7 +3369,8 @@ var require_utils = __commonJS({
3336
3369
  function normalizePathEncoding(input) {
3337
3370
  let output = "";
3338
3371
  for (let i = 0; i < input.length; i++) {
3339
- if (input[i] === "%" && i + 2 < input.length) {
3372
+ const ch = input[i];
3373
+ if (ch === "%" && i + 2 < input.length) {
3340
3374
  const hex = input.slice(i + 1, i + 3);
3341
3375
  if (isHexPair(hex)) {
3342
3376
  const normalizedHex = hex.toUpperCase();
@@ -3350,10 +3384,152 @@ var require_utils = __commonJS({
3350
3384
  continue;
3351
3385
  }
3352
3386
  }
3353
- if (isPathCharacter(input[i])) {
3354
- output += input[i];
3387
+ if (isPathCharacter(ch)) {
3388
+ output += ch;
3355
3389
  } else {
3356
- output += escape(input[i]);
3390
+ const code = input.charCodeAt(i);
3391
+ if (code < 128) {
3392
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3393
+ } else if (code < 55296 || code > 57343) {
3394
+ output += percentEncodeNonAscii(code);
3395
+ } else if (code <= 56319 && i + 1 < input.length) {
3396
+ const low = input.charCodeAt(i + 1);
3397
+ if (low >= 56320 && low <= 57343) {
3398
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3399
+ i++;
3400
+ } else {
3401
+ output += percentEncodeNonAscii(65533);
3402
+ }
3403
+ } else {
3404
+ output += percentEncodeNonAscii(65533);
3405
+ }
3406
+ }
3407
+ }
3408
+ return output;
3409
+ }
3410
+ function serializePathEncoding(input, pathNoScheme = false) {
3411
+ let output = "";
3412
+ let firstSegment = pathNoScheme && input[0] !== "/";
3413
+ for (let i = 0; i < input.length; i++) {
3414
+ const ch = input[i];
3415
+ if (ch === "%" && i + 2 < input.length) {
3416
+ const hex = input.slice(i + 1, i + 3);
3417
+ if (isHexPair(hex)) {
3418
+ output += "%" + hex.toUpperCase();
3419
+ i += 2;
3420
+ continue;
3421
+ }
3422
+ }
3423
+ if (ch === "/") {
3424
+ firstSegment = false;
3425
+ }
3426
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
3427
+ output += ch;
3428
+ } else {
3429
+ const code = input.charCodeAt(i);
3430
+ if (code < 128) {
3431
+ output += BYTE_HEX[code];
3432
+ } else if (code < 55296 || code > 57343) {
3433
+ output += percentEncodeNonAscii(code);
3434
+ } else if (code <= 56319 && i + 1 < input.length) {
3435
+ const low = input.charCodeAt(i + 1);
3436
+ if (low >= 56320 && low <= 57343) {
3437
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3438
+ i++;
3439
+ } else {
3440
+ output += percentEncodeNonAscii(65533);
3441
+ }
3442
+ } else {
3443
+ output += percentEncodeNonAscii(65533);
3444
+ }
3445
+ }
3446
+ }
3447
+ return output;
3448
+ }
3449
+ function encodeComponent(input, isAllowed) {
3450
+ let output = "";
3451
+ for (let i = 0; i < input.length; i++) {
3452
+ const ch = input[i];
3453
+ if (ch === "%" && i + 2 < input.length) {
3454
+ const hex = input.slice(i + 1, i + 3);
3455
+ if (isHexPair(hex)) {
3456
+ output += "%" + hex.toUpperCase();
3457
+ i += 2;
3458
+ continue;
3459
+ }
3460
+ }
3461
+ if (isAllowed(ch)) {
3462
+ output += ch;
3463
+ } else {
3464
+ const code = input.charCodeAt(i);
3465
+ if (code < 128) {
3466
+ output += BYTE_HEX[code];
3467
+ } else if (code < 55296 || code > 57343) {
3468
+ output += percentEncodeNonAscii(code);
3469
+ } else if (code <= 56319 && i + 1 < input.length) {
3470
+ const low = input.charCodeAt(i + 1);
3471
+ if (low >= 56320 && low <= 57343) {
3472
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3473
+ i++;
3474
+ } else {
3475
+ output += percentEncodeNonAscii(65533);
3476
+ }
3477
+ } else {
3478
+ output += percentEncodeNonAscii(65533);
3479
+ }
3480
+ }
3481
+ }
3482
+ return output;
3483
+ }
3484
+ function encodeUserinfo(input) {
3485
+ return encodeComponent(input, isUserinfoCharacter);
3486
+ }
3487
+ function encodeQuery(input) {
3488
+ return encodeComponent(input, isQueryFragmentCharacter);
3489
+ }
3490
+ function encodeFragment(input) {
3491
+ return encodeComponent(input, isQueryFragmentCharacter);
3492
+ }
3493
+ function isEscapeSafe(cp) {
3494
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
3495
+ }
3496
+ function normalizeQueryFragmentEncoding(input) {
3497
+ let output = "";
3498
+ for (let i = 0; i < input.length; i++) {
3499
+ const ch = input[i];
3500
+ if (ch === "%" && i + 2 < input.length) {
3501
+ const hex = input.slice(i + 1, i + 3);
3502
+ if (isHexPair(hex)) {
3503
+ const normalizedHex = hex.toUpperCase();
3504
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3505
+ if (isUnreserved(decoded)) {
3506
+ output += decoded;
3507
+ } else {
3508
+ output += "%" + normalizedHex;
3509
+ }
3510
+ i += 2;
3511
+ continue;
3512
+ }
3513
+ }
3514
+ if (isQueryFragmentCharacter(ch)) {
3515
+ output += ch;
3516
+ } else {
3517
+ const code = input.charCodeAt(i);
3518
+ if (code < 128) {
3519
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3520
+ } else if (code < 55296 || code > 57343) {
3521
+ output += percentEncodeNonAscii(code);
3522
+ } else if (code <= 56319 && i + 1 < input.length) {
3523
+ const low = input.charCodeAt(i + 1);
3524
+ if (low >= 56320 && low <= 57343) {
3525
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3526
+ i++;
3527
+ } else {
3528
+ output += percentEncodeNonAscii(65533);
3529
+ }
3530
+ } else {
3531
+ output += percentEncodeNonAscii(65533);
3532
+ }
3357
3533
  }
3358
3534
  }
3359
3535
  return output;
@@ -3376,14 +3552,18 @@ var require_utils = __commonJS({
3376
3552
  function recomposeAuthority(component) {
3377
3553
  const uriTokens = [];
3378
3554
  if (component.userinfo !== void 0) {
3379
- uriTokens.push(component.userinfo);
3555
+ uriTokens.push(encodeUserinfo(component.userinfo));
3380
3556
  uriTokens.push("@");
3381
3557
  }
3382
3558
  if (component.host !== void 0) {
3383
- let host = unescape(component.host);
3559
+ let host = component.host;
3384
3560
  if (!isIPv4(host)) {
3385
- const ipV6res = normalizeIPv6(host);
3386
- if (ipV6res.isIPV6 === true) {
3561
+ let ipV6res = normalizeIPv6(host);
3562
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
3563
+ host = normalizePercentEncoding(host, true);
3564
+ ipV6res = normalizeIPv6(host);
3565
+ }
3566
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
3387
3567
  host = `[${ipV6res.escapedHost}]`;
3388
3568
  } else {
3389
3569
  host = reescapeHostDelimiters(host, false);
@@ -3392,8 +3572,12 @@ var require_utils = __commonJS({
3392
3572
  uriTokens.push(host);
3393
3573
  }
3394
3574
  if (typeof component.port === "number" || typeof component.port === "string") {
3575
+ const port = String(component.port);
3576
+ if (!isPort(port)) {
3577
+ throw new TypeError("URI port is malformed.");
3578
+ }
3395
3579
  uriTokens.push(":");
3396
- uriTokens.push(String(component.port));
3580
+ uriTokens.push(port);
3397
3581
  }
3398
3582
  return uriTokens.length ? uriTokens.join("") : void 0;
3399
3583
  }
@@ -3403,6 +3587,11 @@ var require_utils = __commonJS({
3403
3587
  reescapeHostDelimiters,
3404
3588
  normalizePercentEncoding,
3405
3589
  normalizePathEncoding,
3590
+ serializePathEncoding,
3591
+ normalizeQueryFragmentEncoding,
3592
+ encodeUserinfo,
3593
+ encodeQuery,
3594
+ encodeFragment,
3406
3595
  escapePreservingEscapes,
3407
3596
  removeDotSegments,
3408
3597
  isIPv4,
@@ -3418,7 +3607,7 @@ var require_schemes = __commonJS({
3418
3607
  "node_modules/fast-uri/lib/schemes.js"(exports, module) {
3419
3608
  "use strict";
3420
3609
  var { isUUID } = require_utils();
3421
- var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
3610
+ var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
3422
3611
  var supportedSchemeNames = (
3423
3612
  /** @type {const} */
3424
3613
  [
@@ -3479,9 +3668,10 @@ var require_schemes = __commonJS({
3479
3668
  wsComponent.secure = void 0;
3480
3669
  }
3481
3670
  if (wsComponent.resourceName) {
3482
- const [path, query] = wsComponent.resourceName.split("?");
3671
+ const queryIndex = wsComponent.resourceName.indexOf("?");
3672
+ const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3483
3673
  wsComponent.path = path && path !== "/" ? path : void 0;
3484
- wsComponent.query = query;
3674
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
3485
3675
  wsComponent.resourceName = void 0;
3486
3676
  }
3487
3677
  wsComponent.fragment = void 0;
@@ -3493,7 +3683,7 @@ var require_schemes = __commonJS({
3493
3683
  return urnComponent;
3494
3684
  }
3495
3685
  const matches = urnComponent.path.match(URN_REG);
3496
- if (matches) {
3686
+ if (matches && matches[0] === urnComponent.path) {
3497
3687
  const scheme = options.scheme || urnComponent.scheme || "urn";
3498
3688
  urnComponent.nid = matches[1].toLowerCase();
3499
3689
  urnComponent.nss = matches[2];
@@ -3627,8 +3817,17 @@ var require_schemes = __commonJS({
3627
3817
  var require_fast_uri = __commonJS({
3628
3818
  "node_modules/fast-uri/index.js"(exports, module) {
3629
3819
  "use strict";
3630
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3820
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3631
3821
  var { SCHEMES, getSchemeHandler } = require_schemes();
3822
+ var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
3823
+ var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
3824
+ function decodeValidScheme(scheme) {
3825
+ const decodedScheme = unescape(String(scheme));
3826
+ if (!VALID_SCHEME.test(decodedScheme)) {
3827
+ throw new TypeError(MALFORMED_SCHEME_ERROR);
3828
+ }
3829
+ return decodedScheme;
3830
+ }
3632
3831
  function normalize(uri, options) {
3633
3832
  if (typeof uri === "string") {
3634
3833
  uri = /** @type {T} */
@@ -3639,60 +3838,82 @@ var require_fast_uri = __commonJS({
3639
3838
  }
3640
3839
  return uri;
3641
3840
  }
3642
- function resolve(baseURI, relativeURI, options) {
3841
+ function resolve4(baseURI, relativeURI, options) {
3643
3842
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3644
- const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3645
- const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3646
- if (baseMalformed || relativeMalformed) {
3843
+ const {
3844
+ parsed: baseParsed,
3845
+ malformedAuthorityOrPort: baseMalformed,
3846
+ malformedPercentEncoding: baseMalformedPercentEncoding,
3847
+ malformedSchemeSpecific: baseMalformedSchemeSpecific,
3848
+ malformedHost: baseMalformedHost,
3849
+ malformedScheme: baseMalformedScheme
3850
+ } = parseWithStatus(baseURI, schemelessOptions);
3851
+ const {
3852
+ parsed: relativeParsed,
3853
+ malformedAuthorityOrPort: relativeMalformed,
3854
+ malformedPercentEncoding: relativeMalformedPercentEncoding,
3855
+ malformedSchemeSpecific: relativeMalformedSchemeSpecific,
3856
+ malformedHost: relativeMalformedHost,
3857
+ malformedScheme: relativeMalformedScheme
3858
+ } = parseWithStatus(relativeURI, schemelessOptions);
3859
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
3647
3860
  throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3648
3861
  }
3649
3862
  const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3863
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
3864
+ const resolvedHost = resolved.host;
3865
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
3866
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
3867
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
3868
+ if (resolved.error && !encodedASCIIHost) {
3869
+ throw new Error(resolved.error);
3870
+ }
3650
3871
  schemelessOptions.skipEscape = true;
3651
3872
  return serialize(resolved, schemelessOptions);
3652
3873
  }
3653
- function resolveComponent(base, relative, options, skipNormalization) {
3874
+ function resolveComponent(base, relative2, options, skipNormalization) {
3654
3875
  const target = {};
3655
3876
  if (!skipNormalization) {
3656
3877
  base = parse3(serialize(base, options), options);
3657
- relative = parse3(serialize(relative, options), options);
3878
+ relative2 = parse3(serialize(relative2, options), options);
3658
3879
  }
3659
3880
  options = options || {};
3660
- if (!options.tolerant && relative.scheme) {
3661
- target.scheme = relative.scheme;
3662
- target.userinfo = relative.userinfo;
3663
- target.host = relative.host;
3664
- target.port = relative.port;
3665
- target.path = removeDotSegments(relative.path || "");
3666
- target.query = relative.query;
3881
+ if (!options.tolerant && relative2.scheme) {
3882
+ target.scheme = relative2.scheme;
3883
+ target.userinfo = relative2.userinfo;
3884
+ target.host = relative2.host;
3885
+ target.port = relative2.port;
3886
+ target.path = removeDotSegments(relative2.path || "");
3887
+ target.query = relative2.query;
3667
3888
  } else {
3668
- if (relative.userinfo !== void 0 || relative.host !== void 0 || relative.port !== void 0) {
3669
- target.userinfo = relative.userinfo;
3670
- target.host = relative.host;
3671
- target.port = relative.port;
3672
- target.path = removeDotSegments(relative.path || "");
3673
- target.query = relative.query;
3889
+ if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3890
+ target.userinfo = relative2.userinfo;
3891
+ target.host = relative2.host;
3892
+ target.port = relative2.port;
3893
+ target.path = removeDotSegments(relative2.path || "");
3894
+ target.query = relative2.query;
3674
3895
  } else {
3675
- if (!relative.path) {
3896
+ if (!relative2.path) {
3676
3897
  target.path = base.path;
3677
- if (relative.query !== void 0) {
3678
- target.query = relative.query;
3898
+ if (relative2.query !== void 0) {
3899
+ target.query = relative2.query;
3679
3900
  } else {
3680
3901
  target.query = base.query;
3681
3902
  }
3682
3903
  } else {
3683
- if (relative.path[0] === "/") {
3684
- target.path = removeDotSegments(relative.path);
3904
+ if (relative2.path[0] === "/") {
3905
+ target.path = removeDotSegments(relative2.path);
3685
3906
  } else {
3686
3907
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3687
- target.path = "/" + relative.path;
3908
+ target.path = "/" + relative2.path;
3688
3909
  } else if (!base.path) {
3689
- target.path = relative.path;
3910
+ target.path = relative2.path;
3690
3911
  } else {
3691
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
3912
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3692
3913
  }
3693
3914
  target.path = removeDotSegments(target.path);
3694
3915
  }
3695
- target.query = relative.query;
3916
+ target.query = relative2.query;
3696
3917
  }
3697
3918
  target.userinfo = base.userinfo;
3698
3919
  target.host = base.host;
@@ -3700,13 +3921,13 @@ var require_fast_uri = __commonJS({
3700
3921
  }
3701
3922
  target.scheme = base.scheme;
3702
3923
  }
3703
- target.fragment = relative.fragment;
3924
+ target.fragment = relative2.fragment;
3704
3925
  return target;
3705
3926
  }
3706
3927
  function equal(uriA, uriB, options) {
3707
3928
  const normalizedA = normalizeComparableURI(uriA, options);
3708
3929
  const normalizedB = normalizeComparableURI(uriB, options);
3709
- return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3930
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
3710
3931
  }
3711
3932
  function serialize(cmpts, opts) {
3712
3933
  const component = {
@@ -3727,19 +3948,22 @@ var require_fast_uri = __commonJS({
3727
3948
  };
3728
3949
  const options = Object.assign({}, opts);
3729
3950
  const uriTokens = [];
3951
+ if (component.scheme) {
3952
+ component.scheme = decodeValidScheme(component.scheme);
3953
+ }
3730
3954
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
3731
3955
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
3956
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
3957
+ const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
3732
3958
  if (component.path !== void 0) {
3733
3959
  if (!options.skipEscape) {
3734
- component.path = escapePreservingEscapes(component.path);
3735
- if (component.scheme !== void 0) {
3736
- component.path = component.path.split("%3A").join(":");
3737
- }
3960
+ component.path = serializePathEncoding(component.path, pathNoScheme);
3738
3961
  } else {
3739
3962
  component.path = normalizePercentEncoding(component.path);
3740
3963
  }
3741
3964
  }
3742
3965
  if (options.reference !== "suffix" && component.scheme) {
3966
+ component.scheme = decodeValidScheme(component.scheme);
3743
3967
  uriTokens.push(component.scheme, ":");
3744
3968
  }
3745
3969
  const authority = recomposeAuthority(component);
@@ -3757,16 +3981,19 @@ var require_fast_uri = __commonJS({
3757
3981
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
3758
3982
  s = removeDotSegments(s);
3759
3983
  }
3984
+ if (pathNoScheme) {
3985
+ s = serializePathEncoding(s, true);
3986
+ }
3760
3987
  if (authority === void 0 && s[0] === "/" && s[1] === "/") {
3761
3988
  s = "/%2F" + s.slice(2);
3762
3989
  }
3763
3990
  uriTokens.push(s);
3764
3991
  }
3765
3992
  if (component.query !== void 0) {
3766
- uriTokens.push("?", component.query);
3993
+ uriTokens.push("?", encodeQuery(component.query));
3767
3994
  }
3768
3995
  if (component.fragment !== void 0) {
3769
- uriTokens.push("#", component.fragment);
3996
+ uriTokens.push("#", encodeFragment(component.fragment));
3770
3997
  }
3771
3998
  return uriTokens.join("");
3772
3999
  }
@@ -3782,6 +4009,35 @@ var require_fast_uri = __commonJS({
3782
4009
  }
3783
4010
  return void 0;
3784
4011
  }
4012
+ function hasMalformedPercentEncoding(component) {
4013
+ if (component === void 0) return false;
4014
+ let percent = component.indexOf("%");
4015
+ while (percent !== -1) {
4016
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
4017
+ return true;
4018
+ }
4019
+ percent = component.indexOf("%", percent + 3);
4020
+ }
4021
+ return false;
4022
+ }
4023
+ function isIPLiteral(host) {
4024
+ return host[0] === "[" && host[host.length - 1] === "]";
4025
+ }
4026
+ function hasMalformedComponentPercentEncoding(matches) {
4027
+ const host = matches[4];
4028
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4029
+ }
4030
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4031
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4032
+ try {
4033
+ parsed.host = new URL("http://" + parsed.host).hostname;
4034
+ } catch (e) {
4035
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
4036
+ return true;
4037
+ }
4038
+ }
4039
+ return false;
4040
+ }
3785
4041
  function parseWithStatus(uri, opts) {
3786
4042
  const options = Object.assign({}, opts);
3787
4043
  const parsed = {
@@ -3794,6 +4050,11 @@ var require_fast_uri = __commonJS({
3794
4050
  fragment: void 0
3795
4051
  };
3796
4052
  let malformedAuthorityOrPort = false;
4053
+ let malformedPercentEncoding = false;
4054
+ let malformedSchemeSpecific = false;
4055
+ let malformedHost = false;
4056
+ let malformedIPLiteral = false;
4057
+ let malformedScheme = false;
3797
4058
  let isIP = false;
3798
4059
  if (options.reference === "suffix") {
3799
4060
  if (options.scheme) {
@@ -3830,6 +4091,19 @@ var require_fast_uri = __commonJS({
3830
4091
  parsed.path = matches[6] || "";
3831
4092
  parsed.query = matches[7];
3832
4093
  parsed.fragment = matches[8];
4094
+ if (parsed.scheme !== void 0) {
4095
+ const decodedScheme = unescape(parsed.scheme);
4096
+ if (VALID_SCHEME.test(decodedScheme)) {
4097
+ parsed.scheme = decodedScheme.toLowerCase();
4098
+ } else {
4099
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
4100
+ malformedScheme = true;
4101
+ }
4102
+ }
4103
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
4104
+ if (malformedPercentEncoding) {
4105
+ parsed.error = parsed.error || "URI contains malformed percent-encoding.";
4106
+ }
3833
4107
  if (isNaN(parsed.port)) {
3834
4108
  parsed.port = matches[5];
3835
4109
  }
@@ -3841,9 +4115,16 @@ var require_fast_uri = __commonJS({
3841
4115
  if (parsed.host) {
3842
4116
  const ipv4result = isIPv4(parsed.host);
3843
4117
  if (ipv4result === false) {
4118
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4119
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
3844
4120
  const ipv6result = normalizeIPv6(parsed.host);
3845
- parsed.host = ipv6result.host.toLowerCase();
3846
- isIP = ipv6result.isIPV6;
4121
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4122
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4123
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4124
+ if (malformedIPLiteral) {
4125
+ parsed.error = parsed.error || "URI host is malformed.";
4126
+ malformedAuthorityOrPort = true;
4127
+ }
3847
4128
  } else {
3848
4129
  isIP = true;
3849
4130
  }
@@ -3861,42 +4142,36 @@ var require_fast_uri = __commonJS({
3861
4142
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
3862
4143
  }
3863
4144
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
3864
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3865
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3866
- try {
3867
- parsed.host = new URL("http://" + parsed.host).hostname;
3868
- } catch (e) {
3869
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3870
- }
3871
- }
4145
+ if (!malformedIPLiteral) {
4146
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
3872
4147
  }
3873
4148
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
3874
4149
  if (uri.indexOf("%") !== -1) {
3875
- if (parsed.scheme !== void 0) {
3876
- parsed.scheme = unescape(parsed.scheme);
3877
- }
3878
- if (parsed.host !== void 0) {
3879
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
4150
+ if (parsed.host !== void 0 && !malformedIPLiteral) {
4151
+ const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4152
+ parsed.host = reescapeHostDelimiters(host, isIP);
3880
4153
  }
3881
4154
  }
3882
4155
  if (parsed.path) {
3883
4156
  parsed.path = normalizePathEncoding(parsed.path);
3884
4157
  }
4158
+ if (parsed.query) {
4159
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
4160
+ }
3885
4161
  if (parsed.fragment) {
3886
- try {
3887
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3888
- } catch {
3889
- parsed.error = parsed.error || "URI malformed";
3890
- }
4162
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3891
4163
  }
3892
4164
  }
3893
4165
  if (schemeHandler && schemeHandler.parse) {
3894
4166
  schemeHandler.parse(parsed, options);
4167
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
4168
+ malformedSchemeSpecific = true;
4169
+ }
3895
4170
  }
3896
4171
  } else {
3897
4172
  parsed.error = parsed.error || "URI can not be parsed.";
3898
4173
  }
3899
- return { parsed, malformedAuthorityOrPort };
4174
+ return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
3900
4175
  }
3901
4176
  function parse3(uri, opts) {
3902
4177
  return parseWithStatus(uri, opts).parsed;
@@ -3905,25 +4180,33 @@ var require_fast_uri = __commonJS({
3905
4180
  return normalizeStringWithStatus(uri, opts).normalized;
3906
4181
  }
3907
4182
  function normalizeStringWithStatus(uri, opts) {
3908
- const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
4183
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
3909
4184
  return {
3910
- normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3911
- malformedAuthorityOrPort
4185
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
4186
+ malformedAuthorityOrPort,
4187
+ malformedPercentEncoding,
4188
+ malformedSchemeSpecific,
4189
+ malformedHost,
4190
+ malformedScheme
3912
4191
  };
3913
4192
  }
3914
4193
  function normalizeComparableURI(uri, opts) {
3915
- if (typeof uri === "string") {
3916
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3917
- return malformedAuthorityOrPort ? void 0 : normalized;
4194
+ if (typeof uri !== "string" && typeof uri !== "object") {
4195
+ return void 0;
3918
4196
  }
3919
- if (typeof uri === "object") {
3920
- return serialize(uri, opts);
4197
+ let value;
4198
+ try {
4199
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
4200
+ } catch {
4201
+ return void 0;
3921
4202
  }
4203
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
4204
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
3922
4205
  }
3923
4206
  var fastUri = {
3924
4207
  SCHEMES,
3925
4208
  normalize,
3926
- resolve,
4209
+ resolve: resolve4,
3927
4210
  resolveComponent,
3928
4211
  equal,
3929
4212
  serialize,
@@ -4170,7 +4453,7 @@ var require_core = __commonJS({
4170
4453
  }
4171
4454
  }
4172
4455
  // Adds schema to the instance
4173
- addSchema(schema, key, _meta, _validateSchema = this.opts.validateSchema) {
4456
+ addSchema(schema, key2, _meta, _validateSchema = this.opts.validateSchema) {
4174
4457
  if (Array.isArray(schema)) {
4175
4458
  for (const sch of schema)
4176
4459
  this.addSchema(sch, void 0, _meta, _validateSchema);
@@ -4184,15 +4467,15 @@ var require_core = __commonJS({
4184
4467
  throw new Error(`schema ${schemaId} must be string`);
4185
4468
  }
4186
4469
  }
4187
- key = (0, resolve_1.normalizeId)(key || id2);
4188
- this._checkUnique(key);
4189
- this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true);
4470
+ key2 = (0, resolve_1.normalizeId)(key2 || id2);
4471
+ this._checkUnique(key2);
4472
+ this.schemas[key2] = this._addSchema(schema, _meta, key2, _validateSchema, true);
4190
4473
  return this;
4191
4474
  }
4192
4475
  // Add schema that will be used to validate other schemas
4193
4476
  // options in META_IGNORE_OPTIONS are alway set to false
4194
- addMetaSchema(schema, key, _validateSchema = this.opts.validateSchema) {
4195
- this.addSchema(schema, key, true, _validateSchema);
4477
+ addMetaSchema(schema, key2, _validateSchema = this.opts.validateSchema) {
4478
+ this.addSchema(schema, key2, true, _validateSchema);
4196
4479
  return this;
4197
4480
  }
4198
4481
  // Validate schema against its meta-schema
@@ -4348,14 +4631,14 @@ var require_core = __commonJS({
4348
4631
  let keywords = metaSchema;
4349
4632
  for (const seg of segments)
4350
4633
  keywords = keywords[seg];
4351
- for (const key in rules) {
4352
- const rule = rules[key];
4634
+ for (const key2 in rules) {
4635
+ const rule = rules[key2];
4353
4636
  if (typeof rule != "object")
4354
4637
  continue;
4355
4638
  const { $data } = rule.definition;
4356
- const schema = keywords[key];
4639
+ const schema = keywords[key2];
4357
4640
  if ($data && schema)
4358
- keywords[key] = schemaOrData(schema);
4641
+ keywords[key2] = schemaOrData(schema);
4359
4642
  }
4360
4643
  }
4361
4644
  return metaSchema;
@@ -4428,10 +4711,10 @@ var require_core = __commonJS({
4428
4711
  Ajv2.MissingRefError = ref_error_1.default;
4429
4712
  exports.default = Ajv2;
4430
4713
  function checkOptions(checkOpts, options, msg, log = "error") {
4431
- for (const key in checkOpts) {
4432
- const opt = key;
4714
+ for (const key2 in checkOpts) {
4715
+ const opt = key2;
4433
4716
  if (opt in options)
4434
- this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`);
4717
+ this.logger[log](`${msg}: option ${key2}. ${checkOpts[opt]}`);
4435
4718
  }
4436
4719
  }
4437
4720
  function getSchEnv(keyRef) {
@@ -4445,8 +4728,8 @@ var require_core = __commonJS({
4445
4728
  if (Array.isArray(optsSchemas))
4446
4729
  this.addSchema(optsSchemas);
4447
4730
  else
4448
- for (const key in optsSchemas)
4449
- this.addSchema(optsSchemas[key], key);
4731
+ for (const key2 in optsSchemas)
4732
+ this.addSchema(optsSchemas[key2], key2);
4450
4733
  }
4451
4734
  function addInitialFormats() {
4452
4735
  for (const name in this.opts.formats) {
@@ -5494,11 +5777,11 @@ var require_dependencies = __commonJS({
5494
5777
  function splitDependencies({ schema }) {
5495
5778
  const propertyDeps = {};
5496
5779
  const schemaDeps = {};
5497
- for (const key in schema) {
5498
- if (key === "__proto__")
5780
+ for (const key2 in schema) {
5781
+ if (key2 === "__proto__")
5499
5782
  continue;
5500
- const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps;
5501
- deps[key] = schema[key];
5783
+ const deps = Array.isArray(schema[key2]) ? propertyDeps : schemaDeps;
5784
+ deps[key2] = schema[key2];
5502
5785
  }
5503
5786
  return [propertyDeps, schemaDeps];
5504
5787
  }
@@ -5575,13 +5858,13 @@ var require_propertyNames = __commonJS({
5575
5858
  if ((0, util_1.alwaysValidSchema)(it, schema))
5576
5859
  return;
5577
5860
  const valid = gen.name("valid");
5578
- gen.forIn("key", data, (key) => {
5579
- cxt.setParams({ propertyName: key });
5861
+ gen.forIn("key", data, (key2) => {
5862
+ cxt.setParams({ propertyName: key2 });
5580
5863
  cxt.subschema({
5581
5864
  keyword: "propertyNames",
5582
- data: key,
5865
+ data: key2,
5583
5866
  dataTypes: ["string"],
5584
- propertyName: key,
5867
+ propertyName: key2,
5585
5868
  compositeRule: true
5586
5869
  }, valid);
5587
5870
  gen.if((0, codegen_1.not)(valid), () => {
@@ -5630,38 +5913,38 @@ var require_additionalProperties = __commonJS({
5630
5913
  checkAdditionalProperties();
5631
5914
  cxt.ok((0, codegen_1._)`${errsCount} === ${names_1.default.errors}`);
5632
5915
  function checkAdditionalProperties() {
5633
- gen.forIn("key", data, (key) => {
5916
+ gen.forIn("key", data, (key2) => {
5634
5917
  if (!props.length && !patProps.length)
5635
- additionalPropertyCode(key);
5918
+ additionalPropertyCode(key2);
5636
5919
  else
5637
- gen.if(isAdditional(key), () => additionalPropertyCode(key));
5920
+ gen.if(isAdditional(key2), () => additionalPropertyCode(key2));
5638
5921
  });
5639
5922
  }
5640
- function isAdditional(key) {
5923
+ function isAdditional(key2) {
5641
5924
  let definedProp;
5642
5925
  if (props.length > 8) {
5643
5926
  const propsSchema = (0, util_1.schemaRefOrVal)(it, parentSchema.properties, "properties");
5644
- definedProp = (0, code_1.isOwnProperty)(gen, propsSchema, key);
5927
+ definedProp = (0, code_1.isOwnProperty)(gen, propsSchema, key2);
5645
5928
  } else if (props.length) {
5646
- definedProp = (0, codegen_1.or)(...props.map((p) => (0, codegen_1._)`${key} === ${p}`));
5929
+ definedProp = (0, codegen_1.or)(...props.map((p) => (0, codegen_1._)`${key2} === ${p}`));
5647
5930
  } else {
5648
5931
  definedProp = codegen_1.nil;
5649
5932
  }
5650
5933
  if (patProps.length) {
5651
- definedProp = (0, codegen_1.or)(definedProp, ...patProps.map((p) => (0, codegen_1._)`${(0, code_1.usePattern)(cxt, p)}.test(${key})`));
5934
+ definedProp = (0, codegen_1.or)(definedProp, ...patProps.map((p) => (0, codegen_1._)`${(0, code_1.usePattern)(cxt, p)}.test(${key2})`));
5652
5935
  }
5653
5936
  return (0, codegen_1.not)(definedProp);
5654
5937
  }
5655
- function deleteAdditional(key) {
5656
- gen.code((0, codegen_1._)`delete ${data}[${key}]`);
5938
+ function deleteAdditional(key2) {
5939
+ gen.code((0, codegen_1._)`delete ${data}[${key2}]`);
5657
5940
  }
5658
- function additionalPropertyCode(key) {
5941
+ function additionalPropertyCode(key2) {
5659
5942
  if (opts.removeAdditional === "all" || opts.removeAdditional && schema === false) {
5660
- deleteAdditional(key);
5943
+ deleteAdditional(key2);
5661
5944
  return;
5662
5945
  }
5663
5946
  if (schema === false) {
5664
- cxt.setParams({ additionalProperty: key });
5947
+ cxt.setParams({ additionalProperty: key2 });
5665
5948
  cxt.error();
5666
5949
  if (!allErrors)
5667
5950
  gen.break();
@@ -5670,22 +5953,22 @@ var require_additionalProperties = __commonJS({
5670
5953
  if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) {
5671
5954
  const valid = gen.name("valid");
5672
5955
  if (opts.removeAdditional === "failing") {
5673
- applyAdditionalSchema(key, valid, false);
5956
+ applyAdditionalSchema(key2, valid, false);
5674
5957
  gen.if((0, codegen_1.not)(valid), () => {
5675
5958
  cxt.reset();
5676
- deleteAdditional(key);
5959
+ deleteAdditional(key2);
5677
5960
  });
5678
5961
  } else {
5679
- applyAdditionalSchema(key, valid);
5962
+ applyAdditionalSchema(key2, valid);
5680
5963
  if (!allErrors)
5681
5964
  gen.if((0, codegen_1.not)(valid), () => gen.break());
5682
5965
  }
5683
5966
  }
5684
5967
  }
5685
- function applyAdditionalSchema(key, valid, errors) {
5968
+ function applyAdditionalSchema(key2, valid, errors) {
5686
5969
  const subschema = {
5687
5970
  keyword: "additionalProperties",
5688
- dataProp: key,
5971
+ dataProp: key2,
5689
5972
  dataPropType: util_1.Type.Str
5690
5973
  };
5691
5974
  if (errors === false) {
@@ -5810,19 +6093,19 @@ var require_patternProperties = __commonJS({
5810
6093
  }
5811
6094
  }
5812
6095
  function validateProperties(pat) {
5813
- gen.forIn("key", data, (key) => {
5814
- gen.if((0, codegen_1._)`${(0, code_1.usePattern)(cxt, pat)}.test(${key})`, () => {
6096
+ gen.forIn("key", data, (key2) => {
6097
+ gen.if((0, codegen_1._)`${(0, code_1.usePattern)(cxt, pat)}.test(${key2})`, () => {
5815
6098
  const alwaysValid = alwaysValidPatterns.includes(pat);
5816
6099
  if (!alwaysValid) {
5817
6100
  cxt.subschema({
5818
6101
  keyword: "patternProperties",
5819
6102
  schemaProp: pat,
5820
- dataProp: key,
6103
+ dataProp: key2,
5821
6104
  dataPropType: util_2.Type.Str
5822
6105
  }, valid);
5823
6106
  }
5824
6107
  if (it.opts.unevaluated && props !== true) {
5825
- gen.assign((0, codegen_1._)`${props}[${key}]`, true);
6108
+ gen.assign((0, codegen_1._)`${props}[${key2}]`, true);
5826
6109
  } else if (!alwaysValid && !it.allErrors) {
5827
6110
  gen.if((0, codegen_1.not)(valid), () => gen.break());
5828
6111
  }
@@ -6913,11 +7196,10 @@ var require_dist = __commonJS({
6913
7196
  });
6914
7197
 
6915
7198
  // src/server/server.ts
6916
- import { spawn } from "node:child_process";
6917
- import { existsSync as existsSync2, realpathSync } from "node:fs";
6918
- import { homedir } from "node:os";
6919
- import { dirname as dirname2, join as join2 } from "node:path";
6920
- import { fileURLToPath, pathToFileURL } from "node:url";
7199
+ import { existsSync as existsSync8, realpathSync as realpathSync4 } from "node:fs";
7200
+ import { homedir as homedir2 } from "node:os";
7201
+ import { join as join10 } from "node:path";
7202
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
6921
7203
 
6922
7204
  // node_modules/zod/v3/external.js
6923
7205
  var external_exports = {};
@@ -7065,9 +7347,9 @@ var util;
7065
7347
  };
7066
7348
  util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object3) => {
7067
7349
  const keys = [];
7068
- for (const key in object3) {
7069
- if (Object.prototype.hasOwnProperty.call(object3, key)) {
7070
- keys.push(key);
7350
+ for (const key2 in object3) {
7351
+ if (Object.prototype.hasOwnProperty.call(object3, key2)) {
7352
+ keys.push(key2);
7071
7353
  }
7072
7354
  }
7073
7355
  return keys;
@@ -7467,10 +7749,10 @@ var ParseStatus = class _ParseStatus {
7467
7749
  static async mergeObjectAsync(status2, pairs) {
7468
7750
  const syncPairs = [];
7469
7751
  for (const pair of pairs) {
7470
- const key = await pair.key;
7752
+ const key2 = await pair.key;
7471
7753
  const value = await pair.value;
7472
7754
  syncPairs.push({
7473
- key,
7755
+ key: key2,
7474
7756
  value
7475
7757
  });
7476
7758
  }
@@ -7479,17 +7761,17 @@ var ParseStatus = class _ParseStatus {
7479
7761
  static mergeObjectSync(status2, pairs) {
7480
7762
  const finalObject = {};
7481
7763
  for (const pair of pairs) {
7482
- const { key, value } = pair;
7483
- if (key.status === "aborted")
7764
+ const { key: key2, value } = pair;
7765
+ if (key2.status === "aborted")
7484
7766
  return INVALID;
7485
7767
  if (value.status === "aborted")
7486
7768
  return INVALID;
7487
- if (key.status === "dirty")
7769
+ if (key2.status === "dirty")
7488
7770
  status2.dirty();
7489
7771
  if (value.status === "dirty")
7490
7772
  status2.dirty();
7491
- if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
7492
- finalObject[key.value] = value.value;
7773
+ if (key2.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
7774
+ finalObject[key2.value] = value.value;
7493
7775
  }
7494
7776
  }
7495
7777
  return { status: status2.value, value: finalObject };
@@ -7514,12 +7796,12 @@ var errorUtil;
7514
7796
 
7515
7797
  // node_modules/zod/v3/types.js
7516
7798
  var ParseInputLazyPath = class {
7517
- constructor(parent, value, path, key) {
7799
+ constructor(parent, value, path, key2) {
7518
7800
  this._cachedPath = [];
7519
7801
  this.parent = parent;
7520
7802
  this.data = value;
7521
7803
  this._path = path;
7522
- this._key = key;
7804
+ this._key = key2;
7523
7805
  }
7524
7806
  get path() {
7525
7807
  if (!this._cachedPath.length) {
@@ -9264,9 +9546,9 @@ ZodArray.create = (schema, params) => {
9264
9546
  function deepPartialify(schema) {
9265
9547
  if (schema instanceof ZodObject) {
9266
9548
  const newShape = {};
9267
- for (const key in schema.shape) {
9268
- const fieldSchema = schema.shape[key];
9269
- newShape[key] = ZodOptional.create(deepPartialify(fieldSchema));
9549
+ for (const key2 in schema.shape) {
9550
+ const fieldSchema = schema.shape[key2];
9551
+ newShape[key2] = ZodOptional.create(deepPartialify(fieldSchema));
9270
9552
  }
9271
9553
  return new ZodObject({
9272
9554
  ...schema._def,
@@ -9317,29 +9599,29 @@ var ZodObject = class _ZodObject extends ZodType {
9317
9599
  const { shape, keys: shapeKeys } = this._getCached();
9318
9600
  const extraKeys = [];
9319
9601
  if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
9320
- for (const key in ctx.data) {
9321
- if (!shapeKeys.includes(key)) {
9322
- extraKeys.push(key);
9602
+ for (const key2 in ctx.data) {
9603
+ if (!shapeKeys.includes(key2)) {
9604
+ extraKeys.push(key2);
9323
9605
  }
9324
9606
  }
9325
9607
  }
9326
9608
  const pairs = [];
9327
- for (const key of shapeKeys) {
9328
- const keyValidator = shape[key];
9329
- const value = ctx.data[key];
9609
+ for (const key2 of shapeKeys) {
9610
+ const keyValidator = shape[key2];
9611
+ const value = ctx.data[key2];
9330
9612
  pairs.push({
9331
- key: { status: "valid", value: key },
9332
- value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)),
9333
- alwaysSet: key in ctx.data
9613
+ key: { status: "valid", value: key2 },
9614
+ value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key2)),
9615
+ alwaysSet: key2 in ctx.data
9334
9616
  });
9335
9617
  }
9336
9618
  if (this._def.catchall instanceof ZodNever) {
9337
9619
  const unknownKeys = this._def.unknownKeys;
9338
9620
  if (unknownKeys === "passthrough") {
9339
- for (const key of extraKeys) {
9621
+ for (const key2 of extraKeys) {
9340
9622
  pairs.push({
9341
- key: { status: "valid", value: key },
9342
- value: { status: "valid", value: ctx.data[key] }
9623
+ key: { status: "valid", value: key2 },
9624
+ value: { status: "valid", value: ctx.data[key2] }
9343
9625
  });
9344
9626
  }
9345
9627
  } else if (unknownKeys === "strict") {
@@ -9356,15 +9638,15 @@ var ZodObject = class _ZodObject extends ZodType {
9356
9638
  }
9357
9639
  } else {
9358
9640
  const catchall = this._def.catchall;
9359
- for (const key of extraKeys) {
9360
- const value = ctx.data[key];
9641
+ for (const key2 of extraKeys) {
9642
+ const value = ctx.data[key2];
9361
9643
  pairs.push({
9362
- key: { status: "valid", value: key },
9644
+ key: { status: "valid", value: key2 },
9363
9645
  value: catchall._parse(
9364
- new ParseInputLazyPath(ctx, value, ctx.path, key)
9646
+ new ParseInputLazyPath(ctx, value, ctx.path, key2)
9365
9647
  //, ctx.child(key), value, getParsedType(value)
9366
9648
  ),
9367
- alwaysSet: key in ctx.data
9649
+ alwaysSet: key2 in ctx.data
9368
9650
  });
9369
9651
  }
9370
9652
  }
@@ -9372,10 +9654,10 @@ var ZodObject = class _ZodObject extends ZodType {
9372
9654
  return Promise.resolve().then(async () => {
9373
9655
  const syncPairs = [];
9374
9656
  for (const pair of pairs) {
9375
- const key = await pair.key;
9657
+ const key2 = await pair.key;
9376
9658
  const value = await pair.value;
9377
9659
  syncPairs.push({
9378
- key,
9660
+ key: key2,
9379
9661
  value,
9380
9662
  alwaysSet: pair.alwaysSet
9381
9663
  });
@@ -9500,8 +9782,8 @@ var ZodObject = class _ZodObject extends ZodType {
9500
9782
  // }) as any;
9501
9783
  // return merged;
9502
9784
  // }
9503
- setKey(key, schema) {
9504
- return this.augment({ [key]: schema });
9785
+ setKey(key2, schema) {
9786
+ return this.augment({ [key2]: schema });
9505
9787
  }
9506
9788
  // merge<Incoming extends AnyZodObject>(
9507
9789
  // merging: Incoming
@@ -9532,9 +9814,9 @@ var ZodObject = class _ZodObject extends ZodType {
9532
9814
  }
9533
9815
  pick(mask) {
9534
9816
  const shape = {};
9535
- for (const key of util.objectKeys(mask)) {
9536
- if (mask[key] && this.shape[key]) {
9537
- shape[key] = this.shape[key];
9817
+ for (const key2 of util.objectKeys(mask)) {
9818
+ if (mask[key2] && this.shape[key2]) {
9819
+ shape[key2] = this.shape[key2];
9538
9820
  }
9539
9821
  }
9540
9822
  return new _ZodObject({
@@ -9544,9 +9826,9 @@ var ZodObject = class _ZodObject extends ZodType {
9544
9826
  }
9545
9827
  omit(mask) {
9546
9828
  const shape = {};
9547
- for (const key of util.objectKeys(this.shape)) {
9548
- if (!mask[key]) {
9549
- shape[key] = this.shape[key];
9829
+ for (const key2 of util.objectKeys(this.shape)) {
9830
+ if (!mask[key2]) {
9831
+ shape[key2] = this.shape[key2];
9550
9832
  }
9551
9833
  }
9552
9834
  return new _ZodObject({
@@ -9562,12 +9844,12 @@ var ZodObject = class _ZodObject extends ZodType {
9562
9844
  }
9563
9845
  partial(mask) {
9564
9846
  const newShape = {};
9565
- for (const key of util.objectKeys(this.shape)) {
9566
- const fieldSchema = this.shape[key];
9567
- if (mask && !mask[key]) {
9568
- newShape[key] = fieldSchema;
9847
+ for (const key2 of util.objectKeys(this.shape)) {
9848
+ const fieldSchema = this.shape[key2];
9849
+ if (mask && !mask[key2]) {
9850
+ newShape[key2] = fieldSchema;
9569
9851
  } else {
9570
- newShape[key] = fieldSchema.optional();
9852
+ newShape[key2] = fieldSchema.optional();
9571
9853
  }
9572
9854
  }
9573
9855
  return new _ZodObject({
@@ -9577,16 +9859,16 @@ var ZodObject = class _ZodObject extends ZodType {
9577
9859
  }
9578
9860
  required(mask) {
9579
9861
  const newShape = {};
9580
- for (const key of util.objectKeys(this.shape)) {
9581
- if (mask && !mask[key]) {
9582
- newShape[key] = this.shape[key];
9862
+ for (const key2 of util.objectKeys(this.shape)) {
9863
+ if (mask && !mask[key2]) {
9864
+ newShape[key2] = this.shape[key2];
9583
9865
  } else {
9584
- const fieldSchema = this.shape[key];
9866
+ const fieldSchema = this.shape[key2];
9585
9867
  let newField = fieldSchema;
9586
9868
  while (newField instanceof ZodOptional) {
9587
9869
  newField = newField._def.innerType;
9588
9870
  }
9589
- newShape[key] = newField;
9871
+ newShape[key2] = newField;
9590
9872
  }
9591
9873
  }
9592
9874
  return new _ZodObject({
@@ -9830,14 +10112,14 @@ function mergeValues(a, b) {
9830
10112
  return { valid: true, data: a };
9831
10113
  } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) {
9832
10114
  const bKeys = util.objectKeys(b);
9833
- const sharedKeys = util.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1);
10115
+ const sharedKeys = util.objectKeys(a).filter((key2) => bKeys.indexOf(key2) !== -1);
9834
10116
  const newObj = { ...a, ...b };
9835
- for (const key of sharedKeys) {
9836
- const sharedValue = mergeValues(a[key], b[key]);
10117
+ for (const key2 of sharedKeys) {
10118
+ const sharedValue = mergeValues(a[key2], b[key2]);
9837
10119
  if (!sharedValue.valid) {
9838
10120
  return { valid: false };
9839
10121
  }
9840
- newObj[key] = sharedValue.data;
10122
+ newObj[key2] = sharedValue.data;
9841
10123
  }
9842
10124
  return { valid: true, data: newObj };
9843
10125
  } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) {
@@ -10001,11 +10283,11 @@ var ZodRecord = class _ZodRecord extends ZodType {
10001
10283
  const pairs = [];
10002
10284
  const keyType = this._def.keyType;
10003
10285
  const valueType = this._def.valueType;
10004
- for (const key in ctx.data) {
10286
+ for (const key2 in ctx.data) {
10005
10287
  pairs.push({
10006
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
10007
- value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
10008
- alwaysSet: key in ctx.data
10288
+ key: keyType._parse(new ParseInputLazyPath(ctx, key2, ctx.path, key2)),
10289
+ value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key2], ctx.path, key2)),
10290
+ alwaysSet: key2 in ctx.data
10009
10291
  });
10010
10292
  }
10011
10293
  if (ctx.common.async) {
@@ -10053,9 +10335,9 @@ var ZodMap = class extends ZodType {
10053
10335
  }
10054
10336
  const keyType = this._def.keyType;
10055
10337
  const valueType = this._def.valueType;
10056
- const pairs = [...ctx.data.entries()].map(([key, value], index) => {
10338
+ const pairs = [...ctx.data.entries()].map(([key2, value], index) => {
10057
10339
  return {
10058
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])),
10340
+ key: keyType._parse(new ParseInputLazyPath(ctx, key2, ctx.path, [index, "key"])),
10059
10341
  value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"]))
10060
10342
  };
10061
10343
  });
@@ -10063,30 +10345,30 @@ var ZodMap = class extends ZodType {
10063
10345
  const finalMap = /* @__PURE__ */ new Map();
10064
10346
  return Promise.resolve().then(async () => {
10065
10347
  for (const pair of pairs) {
10066
- const key = await pair.key;
10348
+ const key2 = await pair.key;
10067
10349
  const value = await pair.value;
10068
- if (key.status === "aborted" || value.status === "aborted") {
10350
+ if (key2.status === "aborted" || value.status === "aborted") {
10069
10351
  return INVALID;
10070
10352
  }
10071
- if (key.status === "dirty" || value.status === "dirty") {
10353
+ if (key2.status === "dirty" || value.status === "dirty") {
10072
10354
  status2.dirty();
10073
10355
  }
10074
- finalMap.set(key.value, value.value);
10356
+ finalMap.set(key2.value, value.value);
10075
10357
  }
10076
10358
  return { status: status2.value, value: finalMap };
10077
10359
  });
10078
10360
  } else {
10079
10361
  const finalMap = /* @__PURE__ */ new Map();
10080
10362
  for (const pair of pairs) {
10081
- const key = pair.key;
10363
+ const key2 = pair.key;
10082
10364
  const value = pair.value;
10083
- if (key.status === "aborted" || value.status === "aborted") {
10365
+ if (key2.status === "aborted" || value.status === "aborted") {
10084
10366
  return INVALID;
10085
10367
  }
10086
- if (key.status === "dirty" || value.status === "dirty") {
10368
+ if (key2.status === "dirty" || value.status === "dirty") {
10087
10369
  status2.dirty();
10088
10370
  }
10089
- finalMap.set(key.value, value.value);
10371
+ finalMap.set(key2.value, value.value);
10090
10372
  }
10091
10373
  return { status: status2.value, value: finalMap };
10092
10374
  }
@@ -11128,19 +11410,19 @@ function floatSafeRemainder2(val, step) {
11128
11410
  const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
11129
11411
  return valInt % stepInt / 10 ** decCount;
11130
11412
  }
11131
- function defineLazy(object3, key, getter) {
11413
+ function defineLazy(object3, key2, getter) {
11132
11414
  const set = false;
11133
- Object.defineProperty(object3, key, {
11415
+ Object.defineProperty(object3, key2, {
11134
11416
  get() {
11135
11417
  if (!set) {
11136
11418
  const value = getter();
11137
- object3[key] = value;
11419
+ object3[key2] = value;
11138
11420
  return value;
11139
11421
  }
11140
11422
  throw new Error("cached value already set");
11141
11423
  },
11142
11424
  set(v) {
11143
- Object.defineProperty(object3, key, {
11425
+ Object.defineProperty(object3, key2, {
11144
11426
  value: v
11145
11427
  // configurable: true,
11146
11428
  });
@@ -11159,11 +11441,11 @@ function assignProp(target, prop, value) {
11159
11441
  function getElementAtPath(obj, path) {
11160
11442
  if (!path)
11161
11443
  return obj;
11162
- return path.reduce((acc, key) => acc?.[key], obj);
11444
+ return path.reduce((acc, key2) => acc?.[key2], obj);
11163
11445
  }
11164
11446
  function promiseAllObject(promisesObj) {
11165
11447
  const keys = Object.keys(promisesObj);
11166
- const promises = keys.map((key) => promisesObj[key]);
11448
+ const promises = keys.map((key2) => promisesObj[key2]);
11167
11449
  return Promise.all(promises).then((results) => {
11168
11450
  const resolvedObj = {};
11169
11451
  for (let i = 0; i < keys.length; i++) {
@@ -11216,8 +11498,8 @@ function isPlainObject(o) {
11216
11498
  }
11217
11499
  function numKeys(data) {
11218
11500
  let keyCount = 0;
11219
- for (const key in data) {
11220
- if (Object.prototype.hasOwnProperty.call(data, key)) {
11501
+ for (const key2 in data) {
11502
+ if (Object.prototype.hasOwnProperty.call(data, key2)) {
11221
11503
  keyCount++;
11222
11504
  }
11223
11505
  }
@@ -11353,13 +11635,13 @@ var BIGINT_FORMAT_RANGES = {
11353
11635
  function pick(schema, mask) {
11354
11636
  const newShape = {};
11355
11637
  const currDef = schema._zod.def;
11356
- for (const key in mask) {
11357
- if (!(key in currDef.shape)) {
11358
- throw new Error(`Unrecognized key: "${key}"`);
11638
+ for (const key2 in mask) {
11639
+ if (!(key2 in currDef.shape)) {
11640
+ throw new Error(`Unrecognized key: "${key2}"`);
11359
11641
  }
11360
- if (!mask[key])
11642
+ if (!mask[key2])
11361
11643
  continue;
11362
- newShape[key] = currDef.shape[key];
11644
+ newShape[key2] = currDef.shape[key2];
11363
11645
  }
11364
11646
  return clone(schema, {
11365
11647
  ...schema._zod.def,
@@ -11370,13 +11652,13 @@ function pick(schema, mask) {
11370
11652
  function omit(schema, mask) {
11371
11653
  const newShape = { ...schema._zod.def.shape };
11372
11654
  const currDef = schema._zod.def;
11373
- for (const key in mask) {
11374
- if (!(key in currDef.shape)) {
11375
- throw new Error(`Unrecognized key: "${key}"`);
11655
+ for (const key2 in mask) {
11656
+ if (!(key2 in currDef.shape)) {
11657
+ throw new Error(`Unrecognized key: "${key2}"`);
11376
11658
  }
11377
- if (!mask[key])
11659
+ if (!mask[key2])
11378
11660
  continue;
11379
- delete newShape[key];
11661
+ delete newShape[key2];
11380
11662
  }
11381
11663
  return clone(schema, {
11382
11664
  ...schema._zod.def,
@@ -11417,23 +11699,23 @@ function partial(Class2, schema, mask) {
11417
11699
  const oldShape = schema._zod.def.shape;
11418
11700
  const shape = { ...oldShape };
11419
11701
  if (mask) {
11420
- for (const key in mask) {
11421
- if (!(key in oldShape)) {
11422
- throw new Error(`Unrecognized key: "${key}"`);
11702
+ for (const key2 in mask) {
11703
+ if (!(key2 in oldShape)) {
11704
+ throw new Error(`Unrecognized key: "${key2}"`);
11423
11705
  }
11424
- if (!mask[key])
11706
+ if (!mask[key2])
11425
11707
  continue;
11426
- shape[key] = Class2 ? new Class2({
11708
+ shape[key2] = Class2 ? new Class2({
11427
11709
  type: "optional",
11428
- innerType: oldShape[key]
11429
- }) : oldShape[key];
11710
+ innerType: oldShape[key2]
11711
+ }) : oldShape[key2];
11430
11712
  }
11431
11713
  } else {
11432
- for (const key in oldShape) {
11433
- shape[key] = Class2 ? new Class2({
11714
+ for (const key2 in oldShape) {
11715
+ shape[key2] = Class2 ? new Class2({
11434
11716
  type: "optional",
11435
- innerType: oldShape[key]
11436
- }) : oldShape[key];
11717
+ innerType: oldShape[key2]
11718
+ }) : oldShape[key2];
11437
11719
  }
11438
11720
  }
11439
11721
  return clone(schema, {
@@ -11446,22 +11728,22 @@ function required(Class2, schema, mask) {
11446
11728
  const oldShape = schema._zod.def.shape;
11447
11729
  const shape = { ...oldShape };
11448
11730
  if (mask) {
11449
- for (const key in mask) {
11450
- if (!(key in shape)) {
11451
- throw new Error(`Unrecognized key: "${key}"`);
11731
+ for (const key2 in mask) {
11732
+ if (!(key2 in shape)) {
11733
+ throw new Error(`Unrecognized key: "${key2}"`);
11452
11734
  }
11453
- if (!mask[key])
11735
+ if (!mask[key2])
11454
11736
  continue;
11455
- shape[key] = new Class2({
11737
+ shape[key2] = new Class2({
11456
11738
  type: "nonoptional",
11457
- innerType: oldShape[key]
11739
+ innerType: oldShape[key2]
11458
11740
  });
11459
11741
  }
11460
11742
  } else {
11461
- for (const key in oldShape) {
11462
- shape[key] = new Class2({
11743
+ for (const key2 in oldShape) {
11744
+ shape[key2] = new Class2({
11463
11745
  type: "nonoptional",
11464
- innerType: oldShape[key]
11746
+ innerType: oldShape[key2]
11465
11747
  });
11466
11748
  }
11467
11749
  }
@@ -12659,28 +12941,28 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
12659
12941
  return payload;
12660
12942
  };
12661
12943
  });
12662
- function handleObjectResult(result, final, key) {
12944
+ function handleObjectResult(result, final, key2) {
12663
12945
  if (result.issues.length) {
12664
- final.issues.push(...prefixIssues(key, result.issues));
12946
+ final.issues.push(...prefixIssues(key2, result.issues));
12665
12947
  }
12666
- final.value[key] = result.value;
12948
+ final.value[key2] = result.value;
12667
12949
  }
12668
- function handleOptionalObjectResult(result, final, key, input) {
12950
+ function handleOptionalObjectResult(result, final, key2, input) {
12669
12951
  if (result.issues.length) {
12670
- if (input[key] === void 0) {
12671
- if (key in input) {
12672
- final.value[key] = void 0;
12952
+ if (input[key2] === void 0) {
12953
+ if (key2 in input) {
12954
+ final.value[key2] = void 0;
12673
12955
  } else {
12674
- final.value[key] = result.value;
12956
+ final.value[key2] = result.value;
12675
12957
  }
12676
12958
  } else {
12677
- final.issues.push(...prefixIssues(key, result.issues));
12959
+ final.issues.push(...prefixIssues(key2, result.issues));
12678
12960
  }
12679
12961
  } else if (result.value === void 0) {
12680
- if (key in input)
12681
- final.value[key] = void 0;
12962
+ if (key2 in input)
12963
+ final.value[key2] = void 0;
12682
12964
  } else {
12683
- final.value[key] = result.value;
12965
+ final.value[key2] = result.value;
12684
12966
  }
12685
12967
  }
12686
12968
  var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
@@ -12704,12 +12986,12 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12704
12986
  defineLazy(inst._zod, "propValues", () => {
12705
12987
  const shape = def.shape;
12706
12988
  const propValues = {};
12707
- for (const key in shape) {
12708
- const field = shape[key]._zod;
12989
+ for (const key2 in shape) {
12990
+ const field = shape[key2]._zod;
12709
12991
  if (field.values) {
12710
- propValues[key] ?? (propValues[key] = /* @__PURE__ */ new Set());
12992
+ propValues[key2] ?? (propValues[key2] = /* @__PURE__ */ new Set());
12711
12993
  for (const v of field.values)
12712
- propValues[key].add(v);
12994
+ propValues[key2].add(v);
12713
12995
  }
12714
12996
  }
12715
12997
  return propValues;
@@ -12717,22 +12999,22 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12717
12999
  const generateFastpass = (shape) => {
12718
13000
  const doc = new Doc(["shape", "payload", "ctx"]);
12719
13001
  const normalized = _normalized.value;
12720
- const parseStr = (key) => {
12721
- const k = esc(key);
13002
+ const parseStr = (key2) => {
13003
+ const k = esc(key2);
12722
13004
  return `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
12723
13005
  };
12724
13006
  doc.write(`const input = payload.value;`);
12725
13007
  const ids = /* @__PURE__ */ Object.create(null);
12726
13008
  let counter = 0;
12727
- for (const key of normalized.keys) {
12728
- ids[key] = `key_${counter++}`;
13009
+ for (const key2 of normalized.keys) {
13010
+ ids[key2] = `key_${counter++}`;
12729
13011
  }
12730
13012
  doc.write(`const newResult = {}`);
12731
- for (const key of normalized.keys) {
12732
- if (normalized.optionalKeys.has(key)) {
12733
- const id2 = ids[key];
12734
- doc.write(`const ${id2} = ${parseStr(key)};`);
12735
- const k = esc(key);
13013
+ for (const key2 of normalized.keys) {
13014
+ if (normalized.optionalKeys.has(key2)) {
13015
+ const id2 = ids[key2];
13016
+ doc.write(`const ${id2} = ${parseStr(key2)};`);
13017
+ const k = esc(key2);
12736
13018
  doc.write(`
12737
13019
  if (${id2}.issues.length) {
12738
13020
  if (input[${k}] === undefined) {
@@ -12754,14 +13036,14 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12754
13036
  }
12755
13037
  `);
12756
13038
  } else {
12757
- const id2 = ids[key];
12758
- doc.write(`const ${id2} = ${parseStr(key)};`);
13039
+ const id2 = ids[key2];
13040
+ doc.write(`const ${id2} = ${parseStr(key2)};`);
12759
13041
  doc.write(`
12760
13042
  if (${id2}.issues.length) payload.issues = payload.issues.concat(${id2}.issues.map(iss => ({
12761
13043
  ...iss,
12762
- path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}]
13044
+ path: iss.path ? [${esc(key2)}, ...iss.path] : [${esc(key2)}]
12763
13045
  })));`);
12764
- doc.write(`newResult[${esc(key)}] = ${id2}.value`);
13046
+ doc.write(`newResult[${esc(key2)}] = ${id2}.value`);
12765
13047
  }
12766
13048
  }
12767
13049
  doc.write(`payload.value = newResult;`);
@@ -12796,16 +13078,16 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12796
13078
  } else {
12797
13079
  payload.value = {};
12798
13080
  const shape = value.shape;
12799
- for (const key of value.keys) {
12800
- const el = shape[key];
12801
- const r = el._zod.run({ value: input[key], issues: [] }, ctx);
13081
+ for (const key2 of value.keys) {
13082
+ const el = shape[key2];
13083
+ const r = el._zod.run({ value: input[key2], issues: [] }, ctx);
12802
13084
  const isOptional = el._zod.optin === "optional" && el._zod.optout === "optional";
12803
13085
  if (r instanceof Promise) {
12804
- proms.push(r.then((r2) => isOptional ? handleOptionalObjectResult(r2, payload, key, input) : handleObjectResult(r2, payload, key)));
13086
+ proms.push(r.then((r2) => isOptional ? handleOptionalObjectResult(r2, payload, key2, input) : handleObjectResult(r2, payload, key2)));
12805
13087
  } else if (isOptional) {
12806
- handleOptionalObjectResult(r, payload, key, input);
13088
+ handleOptionalObjectResult(r, payload, key2, input);
12807
13089
  } else {
12808
- handleObjectResult(r, payload, key);
13090
+ handleObjectResult(r, payload, key2);
12809
13091
  }
12810
13092
  }
12811
13093
  }
@@ -12816,18 +13098,18 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
12816
13098
  const keySet = value.keySet;
12817
13099
  const _catchall = catchall._zod;
12818
13100
  const t = _catchall.def.type;
12819
- for (const key of Object.keys(input)) {
12820
- if (keySet.has(key))
13101
+ for (const key2 of Object.keys(input)) {
13102
+ if (keySet.has(key2))
12821
13103
  continue;
12822
13104
  if (t === "never") {
12823
- unrecognized.push(key);
13105
+ unrecognized.push(key2);
12824
13106
  continue;
12825
13107
  }
12826
- const r = _catchall.run({ value: input[key], issues: [] }, ctx);
13108
+ const r = _catchall.run({ value: input[key2], issues: [] }, ctx);
12827
13109
  if (r instanceof Promise) {
12828
- proms.push(r.then((r2) => handleObjectResult(r2, payload, key)));
13110
+ proms.push(r.then((r2) => handleObjectResult(r2, payload, key2)));
12829
13111
  } else {
12830
- handleObjectResult(r, payload, key);
13112
+ handleObjectResult(r, payload, key2);
12831
13113
  }
12832
13114
  }
12833
13115
  if (unrecognized.length) {
@@ -12989,17 +13271,17 @@ function mergeValues2(a, b) {
12989
13271
  }
12990
13272
  if (isPlainObject(a) && isPlainObject(b)) {
12991
13273
  const bKeys = Object.keys(b);
12992
- const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
13274
+ const sharedKeys = Object.keys(a).filter((key2) => bKeys.indexOf(key2) !== -1);
12993
13275
  const newObj = { ...a, ...b };
12994
- for (const key of sharedKeys) {
12995
- const sharedValue = mergeValues2(a[key], b[key]);
13276
+ for (const key2 of sharedKeys) {
13277
+ const sharedValue = mergeValues2(a[key2], b[key2]);
12996
13278
  if (!sharedValue.valid) {
12997
13279
  return {
12998
13280
  valid: false,
12999
- mergeErrorPath: [key, ...sharedValue.mergeErrorPath]
13281
+ mergeErrorPath: [key2, ...sharedValue.mergeErrorPath]
13000
13282
  };
13001
13283
  }
13002
- newObj[key] = sharedValue.data;
13284
+ newObj[key2] = sharedValue.data;
13003
13285
  }
13004
13286
  return { valid: true, data: newObj };
13005
13287
  }
@@ -13057,29 +13339,29 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
13057
13339
  if (def.keyType._zod.values) {
13058
13340
  const values = def.keyType._zod.values;
13059
13341
  payload.value = {};
13060
- for (const key of values) {
13061
- if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
13062
- const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
13342
+ for (const key2 of values) {
13343
+ if (typeof key2 === "string" || typeof key2 === "number" || typeof key2 === "symbol") {
13344
+ const result = def.valueType._zod.run({ value: input[key2], issues: [] }, ctx);
13063
13345
  if (result instanceof Promise) {
13064
13346
  proms.push(result.then((result2) => {
13065
13347
  if (result2.issues.length) {
13066
- payload.issues.push(...prefixIssues(key, result2.issues));
13348
+ payload.issues.push(...prefixIssues(key2, result2.issues));
13067
13349
  }
13068
- payload.value[key] = result2.value;
13350
+ payload.value[key2] = result2.value;
13069
13351
  }));
13070
13352
  } else {
13071
13353
  if (result.issues.length) {
13072
- payload.issues.push(...prefixIssues(key, result.issues));
13354
+ payload.issues.push(...prefixIssues(key2, result.issues));
13073
13355
  }
13074
- payload.value[key] = result.value;
13356
+ payload.value[key2] = result.value;
13075
13357
  }
13076
13358
  }
13077
13359
  }
13078
13360
  let unrecognized;
13079
- for (const key in input) {
13080
- if (!values.has(key)) {
13361
+ for (const key2 in input) {
13362
+ if (!values.has(key2)) {
13081
13363
  unrecognized = unrecognized ?? [];
13082
- unrecognized.push(key);
13364
+ unrecognized.push(key2);
13083
13365
  }
13084
13366
  }
13085
13367
  if (unrecognized && unrecognized.length > 0) {
@@ -13092,10 +13374,10 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
13092
13374
  }
13093
13375
  } else {
13094
13376
  payload.value = {};
13095
- for (const key of Reflect.ownKeys(input)) {
13096
- if (key === "__proto__")
13377
+ for (const key2 of Reflect.ownKeys(input)) {
13378
+ if (key2 === "__proto__")
13097
13379
  continue;
13098
- const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);
13380
+ const keyResult = def.keyType._zod.run({ value: key2, issues: [] }, ctx);
13099
13381
  if (keyResult instanceof Promise) {
13100
13382
  throw new Error("Async schemas not supported in object keys currently");
13101
13383
  }
@@ -13104,24 +13386,24 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
13104
13386
  origin: "record",
13105
13387
  code: "invalid_key",
13106
13388
  issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
13107
- input: key,
13108
- path: [key],
13389
+ input: key2,
13390
+ path: [key2],
13109
13391
  inst
13110
13392
  });
13111
13393
  payload.value[keyResult.value] = keyResult.value;
13112
13394
  continue;
13113
13395
  }
13114
- const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
13396
+ const result = def.valueType._zod.run({ value: input[key2], issues: [] }, ctx);
13115
13397
  if (result instanceof Promise) {
13116
13398
  proms.push(result.then((result2) => {
13117
13399
  if (result2.issues.length) {
13118
- payload.issues.push(...prefixIssues(key, result2.issues));
13400
+ payload.issues.push(...prefixIssues(key2, result2.issues));
13119
13401
  }
13120
13402
  payload.value[keyResult.value] = result2.value;
13121
13403
  }));
13122
13404
  } else {
13123
13405
  if (result.issues.length) {
13124
- payload.issues.push(...prefixIssues(key, result.issues));
13406
+ payload.issues.push(...prefixIssues(key2, result.issues));
13125
13407
  }
13126
13408
  payload.value[keyResult.value] = result.value;
13127
13409
  }
@@ -14178,15 +14460,15 @@ var JSONSchemaGenerator = class {
14178
14460
  json.type = "object";
14179
14461
  json.properties = {};
14180
14462
  const shape = def.shape;
14181
- for (const key in shape) {
14182
- json.properties[key] = this.process(shape[key], {
14463
+ for (const key2 in shape) {
14464
+ json.properties[key2] = this.process(shape[key2], {
14183
14465
  ...params,
14184
- path: [...params.path, "properties", key]
14466
+ path: [...params.path, "properties", key2]
14185
14467
  });
14186
14468
  }
14187
14469
  const allKeys = new Set(Object.keys(shape));
14188
- const requiredKeys = new Set([...allKeys].filter((key) => {
14189
- const v = def.shape[key]._zod;
14470
+ const requiredKeys = new Set([...allKeys].filter((key2) => {
14471
+ const v = def.shape[key2]._zod;
14190
14472
  if (this.io === "input") {
14191
14473
  return v.optin === void 0;
14192
14474
  } else {
@@ -14520,8 +14802,8 @@ var JSONSchemaGenerator = class {
14520
14802
  if (defId)
14521
14803
  seen.defId = defId;
14522
14804
  const schema2 = seen.schema;
14523
- for (const key in schema2) {
14524
- delete schema2[key];
14805
+ for (const key2 in schema2) {
14806
+ delete schema2[key2];
14525
14807
  }
14526
14808
  schema2.$ref = ref;
14527
14809
  };
@@ -14648,8 +14930,8 @@ function toJSONSchema(input, _params) {
14648
14930
  defs
14649
14931
  };
14650
14932
  for (const entry of input._idmap.entries()) {
14651
- const [key, schema] = entry;
14652
- schemas[key] = gen2.emit(schema, {
14933
+ const [key2, schema] = entry;
14934
+ schemas[key2] = gen2.emit(schema, {
14653
14935
  ..._params,
14654
14936
  external
14655
14937
  });
@@ -14696,8 +14978,8 @@ function isTransforming(_schema, _ctx) {
14696
14978
  return isTransforming(def.element, ctx);
14697
14979
  }
14698
14980
  case "object": {
14699
- for (const key in def.shape) {
14700
- if (isTransforming(def.shape[key], ctx))
14981
+ for (const key2 in def.shape) {
14982
+ if (isTransforming(def.shape[key2], ctx))
14701
14983
  return true;
14702
14984
  }
14703
14985
  return false;
@@ -17259,19 +17541,19 @@ var getRefs = (options) => {
17259
17541
  };
17260
17542
 
17261
17543
  // node_modules/zod-to-json-schema/dist/esm/errorMessages.js
17262
- function addErrorMessage(res, key, errorMessage, refs) {
17544
+ function addErrorMessage(res, key2, errorMessage, refs) {
17263
17545
  if (!refs?.errorMessages)
17264
17546
  return;
17265
17547
  if (errorMessage) {
17266
17548
  res.errorMessage = {
17267
17549
  ...res.errorMessage,
17268
- [key]: errorMessage
17550
+ [key2]: errorMessage
17269
17551
  };
17270
17552
  }
17271
17553
  }
17272
- function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
17273
- res[key] = value;
17274
- addErrorMessage(res, key, errorMessage, refs);
17554
+ function setResponseValueAndErrors(res, key2, value, errorMessage, refs) {
17555
+ res[key2] = value;
17556
+ addErrorMessage(res, key2, errorMessage, refs);
17275
17557
  }
17276
17558
 
17277
17559
  // node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
@@ -17863,11 +18145,11 @@ function parseRecordDef(def, refs) {
17863
18145
  return {
17864
18146
  type: "object",
17865
18147
  required: def.keyType._def.values,
17866
- properties: def.keyType._def.values.reduce((acc, key) => ({
18148
+ properties: def.keyType._def.values.reduce((acc, key2) => ({
17867
18149
  ...acc,
17868
- [key]: parseDef(def.valueType._def, {
18150
+ [key2]: parseDef(def.valueType._def, {
17869
18151
  ...refs,
17870
- currentPath: [...refs.currentPath, "properties", key]
18152
+ currentPath: [...refs.currentPath, "properties", key2]
17871
18153
  }) ?? parseAnyDef(refs)
17872
18154
  }), {}),
17873
18155
  additionalProperties: refs.rejectedAdditionalProperties
@@ -17934,10 +18216,10 @@ function parseMapDef(def, refs) {
17934
18216
  // node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js
17935
18217
  function parseNativeEnumDef(def) {
17936
18218
  const object3 = def.values;
17937
- const actualKeys = Object.keys(def.values).filter((key) => {
17938
- return typeof object3[object3[key]] !== "number";
18219
+ const actualKeys = Object.keys(def.values).filter((key2) => {
18220
+ return typeof object3[object3[key2]] !== "number";
17939
18221
  });
17940
- const actualValues = actualKeys.map((key) => object3[key]);
18222
+ const actualValues = actualKeys.map((key2) => object3[key2]);
17941
18223
  const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values)));
17942
18224
  return {
17943
18225
  type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"],
@@ -19029,7 +19311,7 @@ var Protocol = class {
19029
19311
  return;
19030
19312
  }
19031
19313
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
19032
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
19314
+ await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
19033
19315
  options?.signal?.throwIfAborted();
19034
19316
  }
19035
19317
  } catch (error2) {
@@ -19046,7 +19328,7 @@ var Protocol = class {
19046
19328
  */
19047
19329
  request(request, resultSchema, options) {
19048
19330
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
19049
- return new Promise((resolve, reject) => {
19331
+ return new Promise((resolve4, reject) => {
19050
19332
  const earlyReject = (error2) => {
19051
19333
  reject(error2);
19052
19334
  };
@@ -19124,7 +19406,7 @@ var Protocol = class {
19124
19406
  if (!parseResult.success) {
19125
19407
  reject(parseResult.error);
19126
19408
  } else {
19127
- resolve(parseResult.data);
19409
+ resolve4(parseResult.data);
19128
19410
  }
19129
19411
  } catch (error2) {
19130
19412
  reject(error2);
@@ -19385,12 +19667,12 @@ var Protocol = class {
19385
19667
  }
19386
19668
  } catch {
19387
19669
  }
19388
- return new Promise((resolve, reject) => {
19670
+ return new Promise((resolve4, reject) => {
19389
19671
  if (signal.aborted) {
19390
19672
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19391
19673
  return;
19392
19674
  }
19393
- const timeoutId = setTimeout(resolve, interval);
19675
+ const timeoutId = setTimeout(resolve4, interval);
19394
19676
  signal.addEventListener("abort", () => {
19395
19677
  clearTimeout(timeoutId);
19396
19678
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -19468,8 +19750,8 @@ function isPlainObject2(value) {
19468
19750
  }
19469
19751
  function mergeCapabilities(base, additional) {
19470
19752
  const result = { ...base };
19471
- for (const key in additional) {
19472
- const k = key;
19753
+ for (const key2 in additional) {
19754
+ const k = key2;
19473
19755
  const addValue = additional[k];
19474
19756
  if (addValue === void 0)
19475
19757
  continue;
@@ -20481,7 +20763,7 @@ var McpServer = class {
20481
20763
  let task = createTaskResult.task;
20482
20764
  const pollInterval = task.pollInterval ?? 5e3;
20483
20765
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
20484
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
20766
+ await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
20485
20767
  const updatedTask = await extra.taskStore.getTask(taskId);
20486
20768
  if (!updatedTask) {
20487
20769
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -21145,12 +21427,12 @@ var StdioServerTransport = class {
21145
21427
  this.onclose?.();
21146
21428
  }
21147
21429
  send(message) {
21148
- return new Promise((resolve) => {
21430
+ return new Promise((resolve4) => {
21149
21431
  const json = serializeMessage(message);
21150
21432
  if (this._stdout.write(json)) {
21151
- resolve();
21433
+ resolve4();
21152
21434
  } else {
21153
- this._stdout.once("drain", resolve);
21435
+ this._stdout.once("drain", resolve4);
21154
21436
  }
21155
21437
  });
21156
21438
  }
@@ -21587,6 +21869,70 @@ function flipForSequence(map) {
21587
21869
  };
21588
21870
  }
21589
21871
 
21872
+ // src/domain/context.ts
21873
+ function sourceError(raw) {
21874
+ if (!Array.isArray(raw) || raw.length > 100) return "sources must be an array of at most 100 file references";
21875
+ for (const item of raw) {
21876
+ if (!item || typeof item !== "object" || Array.isArray(item)) return "source must be an object";
21877
+ const s = item;
21878
+ if (Object.keys(s).some((k) => k !== "path" && k !== "sha256")) return "unknown source field";
21879
+ if (typeof s.path !== "string" || s.path.length > 1024 || !s.path || /[\u0000-\u001f\u007f-\u009f\\:]/.test(s.path) || s.path.startsWith("/") || s.path.split("/").some((p) => !p || p === "." || p === "..")) return "source path must be relative to the project, with forward slashes and no traversal";
21880
+ if (s.sha256 !== void 0 && (typeof s.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(s.sha256))) return "source sha256 must be a lowercase SHA256 hash";
21881
+ }
21882
+ return void 0;
21883
+ }
21884
+ function contextError(raw) {
21885
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return "context must be an object";
21886
+ for (const [key2, value] of Object.entries(raw)) {
21887
+ if (key2 !== "summary" && key2 !== "next") return "unknown context field";
21888
+ if (typeof value !== "string" || value.length > 2e3 || /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/.test(value)) return "context fields must be text of at most 2000 characters";
21889
+ }
21890
+ return void 0;
21891
+ }
21892
+
21893
+ // src/domain/text.ts
21894
+ var NO_CONTROLS = /^[^\u0000-\u001f\u007f-\u009f]*$/;
21895
+ var NO_CONTROLS_TEXT = "one line of text; control characters (ESC, newline, tab) are not allowed";
21896
+ var NO_CONTROLS_BUT_BREAKS = /^[^\u0000-\u0008\u000b-\u001f\u007f-\u009f]*$/;
21897
+ var NO_CONTROLS_BUT_BREAKS_TEXT = "text with optional newlines (\\n) and tabs; other control characters (ESC, BEL, CR) are not allowed";
21898
+ function mapTextError(map) {
21899
+ if (map.context !== void 0) {
21900
+ const error3 = contextError(map.context);
21901
+ if (error3) return error3;
21902
+ }
21903
+ const check2 = (field, value, multiline = false) => value === void 0 || (multiline ? NO_CONTROLS_BUT_BREAKS : NO_CONTROLS).test(value) ? void 0 : `${field}: ${multiline ? NO_CONTROLS_BUT_BREAKS_TEXT : NO_CONTROLS_TEXT}`;
21904
+ let error2 = check2("title", map.title);
21905
+ if (error2) return error2;
21906
+ for (const [i, layer] of map.layers.entries()) {
21907
+ error2 = check2(`layers[${i}].name`, layer.name);
21908
+ if (error2) return error2;
21909
+ }
21910
+ for (const name of ["lanes", "groups"]) {
21911
+ for (const [i, item] of map[name].entries()) {
21912
+ error2 = check2(`${name}[${i}].label`, item.label);
21913
+ if (error2) return error2;
21914
+ }
21915
+ }
21916
+ for (const [i, node] of map.nodes.entries()) {
21917
+ if (node.sources !== void 0) {
21918
+ const error3 = sourceError(node.sources);
21919
+ if (error3) return `nodes[${i}]: ${error3}`;
21920
+ }
21921
+ for (const name of ["label", "evidence", "detail"]) {
21922
+ error2 = check2(`nodes[${i}].${name}`, node[name], name !== "label");
21923
+ if (error2) return error2;
21924
+ }
21925
+ }
21926
+ for (const [i, edge] of map.edges.entries()) {
21927
+ error2 = check2(`edges[${i}].label`, edge.label);
21928
+ if (error2) return error2;
21929
+ }
21930
+ return void 0;
21931
+ }
21932
+ function terminalText(text2, multiline = false) {
21933
+ return text2.replace(multiline ? /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g : /[\u0000-\u001f\u007f-\u009f]/g, "?");
21934
+ }
21935
+
21590
21936
  // src/render/width.ts
21591
21937
  var WIDE_RANGES = [
21592
21938
  [4352, 4447],
@@ -21681,6 +22027,7 @@ function displayWidth(text2) {
21681
22027
  return w;
21682
22028
  }
21683
22029
  function fitWidth(s, width) {
22030
+ s = terminalText(s);
21684
22031
  if (displayWidth(s) <= width) return s;
21685
22032
  let out = "";
21686
22033
  let w = 0;
@@ -21696,7 +22043,7 @@ function wrapWidth(s, width) {
21696
22043
  const lines = [];
21697
22044
  let line2 = "";
21698
22045
  let w = 0;
21699
- for (const ch of s.replace(/\r/g, "")) {
22046
+ for (const ch of terminalText(s.replace(/\r/g, "").replace(/\t/g, " "), true)) {
21700
22047
  if (ch === "\n") {
21701
22048
  lines.push(line2);
21702
22049
  line2 = "";
@@ -21725,7 +22072,9 @@ var SGR = {
21725
22072
  greenDim: "32;2",
21726
22073
  // done, but nothing behind the claim: green, not fully lit
21727
22074
  red: "31",
21728
- faint: "90"
22075
+ faint: "90",
22076
+ focus: "97"
22077
+ // bright foreground without changing font weight
21729
22078
  };
21730
22079
  var ANSI_RESET = "\x1B[0m";
21731
22080
  var UP = 1;
@@ -21786,7 +22135,7 @@ var Canvas = class {
21786
22135
  /** Write literal text starting at (x, y). Returns the column just past it. */
21787
22136
  text(x, y, s, style, bold = false) {
21788
22137
  let cx = x;
21789
- for (const ch of s) {
22138
+ for (const ch of terminalText(s)) {
21790
22139
  const w = charWidth(ch.codePointAt(0));
21791
22140
  if (w === 0) {
21792
22141
  const base = this.cell(Math.max(0, cx - 1), y);
@@ -21845,7 +22194,7 @@ var Canvas = class {
21845
22194
  } else if (charWidth(ch.codePointAt(0)) === 2 && x + 1 >= vp.x + vp.width) {
21846
22195
  ch = " ";
21847
22196
  }
21848
- const params = ch === " " ? "" : isWire ? c.bright ? "1" : SGR.faint : [SGR[c.style], c.bold ? "1" : ""].filter(Boolean).join(";");
22197
+ const params = ch === " " ? "" : isWire ? c.bright ? SGR.focus : SGR.faint : [SGR[c.style], c.bold ? "1" : ""].filter(Boolean).join(";");
21849
22198
  if (opts.color && params !== open) {
21850
22199
  line2 += (open !== "" ? ANSI_RESET : "") + (params !== "" ? `\x1B[${params}m` : "");
21851
22200
  open = params;
@@ -22191,6 +22540,7 @@ function drawBands(canvas, columns, rows, wiredWidth, totalWidth) {
22191
22540
  function drawBox(canvas, box, opts, neutral, face, focused = false) {
22192
22541
  const { node, x, y, w } = box;
22193
22542
  const skin = neutral ? neutralSkin(opts.unicode) : skinFor(face, opts.unicode);
22543
+ const borderStyle = focused ? "focus" : skin.style;
22194
22544
  const slotGlyph = neutral ? neutralGlyph(node, opts.unicode) : glyphFor(face, opts);
22195
22545
  if (box.borderless) {
22196
22546
  canvas.text(x + 1, y, slotGlyph, skin.style, true);
@@ -22198,18 +22548,18 @@ function drawBox(canvas, box, opts, neutral, face, focused = false) {
22198
22548
  }
22199
22549
  const inner = w - 2;
22200
22550
  const pad = box.pad === 1 ? " " : "";
22201
- canvas.text(x, y, skin.corners[0] + skin.h.repeat(inner) + skin.corners[1], skin.style, focused);
22202
- canvas.text(x, y + 1, skin.v, skin.style, focused);
22551
+ canvas.text(x, y, skin.corners[0] + skin.h.repeat(inner) + skin.corners[1], borderStyle);
22552
+ canvas.text(x, y + 1, skin.v, borderStyle);
22203
22553
  canvas.text(x + 1, y + 1, `${pad}${slotGlyph} ${box.label}${pad}`, skin.style, true);
22204
- canvas.text(x + w - 1, y + 1, skin.v, skin.style, focused);
22554
+ canvas.text(x + w - 1, y + 1, skin.v, borderStyle);
22205
22555
  for (let i = 0; i < box.extra.length; i++) {
22206
22556
  const row = box.extra[i];
22207
22557
  const yy = y + 2 + i;
22208
- canvas.text(x, yy, skin.v, skin.style, focused);
22558
+ canvas.text(x, yy, skin.v, borderStyle);
22209
22559
  canvas.text(x + 1, yy, row.text, row.style);
22210
- canvas.text(x + w - 1, yy, skin.v, skin.style, focused);
22560
+ canvas.text(x + w - 1, yy, skin.v, borderStyle);
22211
22561
  }
22212
- canvas.text(x, y + box.h - 1, skin.corners[2] + skin.h.repeat(inner) + skin.corners[3], skin.style, focused);
22562
+ canvas.text(x, y + box.h - 1, skin.corners[2] + skin.h.repeat(inner) + skin.corners[3], borderStyle);
22213
22563
  }
22214
22564
  function drawEdges(canvas, edges, rows, opts) {
22215
22565
  for (const edge of edges) {
@@ -22388,29 +22738,43 @@ function layoutRows(columns, geo, gapRowCount, hasTitle, hasLanes) {
22388
22738
 
22389
22739
  // src/render/render.ts
22390
22740
  function renderMap(map, opts) {
22391
- const built = buildCanvas(map, opts);
22741
+ const built = paint(prepareScene(map, opts), opts);
22392
22742
  return built.canvas.emit(opts);
22393
22743
  }
22394
- function buildCanvas(map, opts) {
22744
+ function prepareScene(map, opts) {
22395
22745
  const oriented = flipForSequence(map);
22396
22746
  const plainGeo = zoomGeometry(opts.zoom ?? ZOOM_DEFAULT);
22397
22747
  const aggregated = plainGeo.mode === "constellation" ? aggregateMap(oriented) : void 0;
22398
22748
  const drawn = aggregated ?? oriented;
22399
- return paint(drawn, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo, unverifiedDoneIds(oriented, drawn));
22749
+ const unverified = unverifiedDoneIds(oriented, drawn);
22750
+ if (drawn.layers.length === 0) return { map: drawn, unverified };
22751
+ return { map: drawn, unverified, geometry: prepareGeometry(drawn, opts, aggregated !== void 0 ? AGGREGATE_GEO : plainGeo) };
22400
22752
  }
22401
- function paint(map, opts, geo, unverified) {
22402
- const canvas = new Canvas();
22403
- if (map.layers.length === 0) {
22404
- canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
22405
- canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
22406
- return { canvas, hits: [] };
22407
- }
22753
+ function prepareGeometry(map, opts, geo) {
22408
22754
  const neutral = isNeutralKind(map);
22409
22755
  const columns = layoutColumns(map, geo, opts.unicode, neutral);
22410
22756
  const routing = routeEdges(map, columns);
22411
22757
  const rows = layoutRows(columns, geo, routing.gapRowCount, map.title !== void 0, map.lanes.length > 0);
22412
22758
  const wiredWidth = routing.fallbackCount > 0 ? columns.contentWidth + 2 + routing.fallbackCount * 2 : columns.contentWidth;
22413
22759
  const totalWidth = wiredWidth + Math.max(...columns.bandLabel.map(displayWidth));
22760
+ const hits = [...rows.boxOf.values()].map((b) => ({
22761
+ id: b.node.id,
22762
+ x: b.x,
22763
+ y: b.y,
22764
+ w: b.w,
22765
+ h: b.h
22766
+ }));
22767
+ return { neutral, columns, routing, rows, wiredWidth, totalWidth, hits };
22768
+ }
22769
+ function paint(scene, opts) {
22770
+ const { map, unverified, geometry } = scene;
22771
+ const canvas = new Canvas();
22772
+ if (geometry === void 0) {
22773
+ canvas.text(0, 0, map.title ?? "mellos mapping", "none", true);
22774
+ canvas.text(0, 2, "(empty map \u2014 declare layers and nodes to begin)", "dim");
22775
+ return { canvas, hits: [] };
22776
+ }
22777
+ const { neutral, columns, routing, rows, wiredWidth, totalWidth, hits } = geometry;
22414
22778
  if (map.title !== void 0) drawTitle(canvas, map.title);
22415
22779
  drawLaneHeaders(canvas, map, columns, rows);
22416
22780
  drawBands(canvas, columns, rows, wiredWidth, totalWidth);
@@ -22421,20 +22785,9 @@ function paint(map, opts, geo, unverified) {
22421
22785
  }
22422
22786
  drawEdges(canvas, routing.edges, rows, opts);
22423
22787
  drawLegend(canvas, map, opts, rows.legendY, neutral, unverified.size > 0);
22424
- const hits = [...rows.boxOf.values()].map((b) => ({
22425
- id: b.node.id,
22426
- x: b.x,
22427
- y: b.y,
22428
- w: b.w,
22429
- h: b.h
22430
- }));
22431
22788
  return { canvas, hits };
22432
22789
  }
22433
22790
 
22434
- // src/store/store.ts
22435
- import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
22436
- import { basename, dirname, join } from "node:path";
22437
-
22438
22791
  // src/store/format.ts
22439
22792
  var STATE_FILE_VERSION = 1;
22440
22793
  function makePageId(raw) {
@@ -22468,25 +22821,25 @@ function describeValue(v) {
22468
22821
  function badShape(path, where, expected, got) {
22469
22822
  return err({ kind: "bad-shape", path, detail: `${where} is ${describeValue(got)}, expected ${expected}` });
22470
22823
  }
22471
- function arrayField(raw, key, path, presence) {
22472
- const v = raw[key];
22824
+ function arrayField(raw, key2, path, presence) {
22825
+ const v = raw[key2];
22473
22826
  if (Array.isArray(v)) return ok(v);
22474
22827
  if (v === void 0 && presence === "optional") return ok([]);
22475
- return badShape(path, `"${key}"`, "an array", v);
22828
+ return badShape(path, `"${key2}"`, "an array", v);
22476
22829
  }
22477
- function requiredString(rec, key, where, path) {
22478
- const v = rec[key];
22479
- return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
22830
+ function requiredString(rec, key2, where, path) {
22831
+ const v = rec[key2];
22832
+ return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key2}`, "a string", v);
22480
22833
  }
22481
- function optionalString(rec, key, where, path) {
22482
- const v = rec[key];
22834
+ function optionalString(rec, key2, where, path) {
22835
+ const v = rec[key2];
22483
22836
  if (v === void 0) return ok(void 0);
22484
- return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key}`, "a string", v);
22837
+ return typeof v === "string" ? ok(v) : badShape(path, `${where}.${key2}`, "a string", v);
22485
22838
  }
22486
22839
  function parseMap(raw, path) {
22487
22840
  if (!isRecord(raw)) return err({ kind: "bad-shape", path, detail: "root is not an object" });
22488
- if (raw["version"] !== STATE_FILE_VERSION) {
22489
- return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${STATE_FILE_VERSION}` });
22841
+ if (raw["version"] !== STATE_FILE_VERSION && raw["version"] !== 2) {
22842
+ return err({ kind: "bad-shape", path, detail: `version is ${String(raw["version"])}, expected ${STATE_FILE_VERSION} or 2` });
22490
22843
  }
22491
22844
  const layers = arrayField(raw, "layers", path, "required");
22492
22845
  if (!layers.ok) return layers;
@@ -22499,6 +22852,11 @@ function parseMap(raw, path) {
22499
22852
  const groups = arrayField(raw, "groups", path, "optional");
22500
22853
  if (!groups.ok) return groups;
22501
22854
  let map = EMPTY_MAP;
22855
+ if (raw["context"] !== void 0) {
22856
+ const error2 = contextError(raw["context"]);
22857
+ if (error2) return err({ kind: "bad-shape", path, detail: error2 });
22858
+ map = { ...map, context: raw["context"] };
22859
+ }
22502
22860
  const title = optionalString(raw, "title", "map", path);
22503
22861
  if (!title.ok) return title;
22504
22862
  if (title.value !== void 0) map = setTitle(map, title.value);
@@ -22627,6 +22985,11 @@ function parseMap(raw, path) {
22627
22985
  if (!updated.ok) return err({ kind: "invariant-violation", path, violation: updated.error });
22628
22986
  map = updated.value;
22629
22987
  }
22988
+ if (rawNode["sources"] !== void 0) {
22989
+ const error2 = sourceError(rawNode["sources"]);
22990
+ if (error2) return err({ kind: "bad-shape", path, detail: `${where}: ${error2}` });
22991
+ map = { ...map, nodes: map.nodes.map((n) => n.id === id2.value ? { ...n, sources: rawNode["sources"] } : n) };
22992
+ }
22630
22993
  }
22631
22994
  for (const [i, rawEdge] of edges.value.entries()) {
22632
22995
  const where = `edges[${i}]`;
@@ -22645,11 +23008,14 @@ function parseMap(raw, path) {
22645
23008
  if (!linked.ok) return err({ kind: "invariant-violation", path, violation: linked.error });
22646
23009
  map = linked.value;
22647
23010
  }
22648
- return ok(map);
23011
+ const textError = mapTextError(map);
23012
+ return textError ? err({ kind: "bad-shape", path, detail: textError }) : ok(map);
22649
23013
  }
22650
23014
  function serializeMap(map) {
22651
23015
  const body = {
22652
- version: STATE_FILE_VERSION,
23016
+ // Older runtimes must refuse maps with provenance rather than silently erasing it.
23017
+ version: map.context !== void 0 || map.nodes.some((n) => n.sources !== void 0) ? 2 : STATE_FILE_VERSION,
23018
+ ...map.context !== void 0 ? { context: map.context } : {},
22653
23019
  ...map.title !== void 0 ? { title: map.title } : {},
22654
23020
  ...map.kind !== void 0 ? { kind: map.kind } : {},
22655
23021
  layers: map.layers,
@@ -22661,7 +23027,9 @@ function serializeMap(map) {
22661
23027
  return JSON.stringify(body, null, 2) + "\n";
22662
23028
  }
22663
23029
 
22664
- // src/store/store.ts
23030
+ // src/store/atomic.ts
23031
+ import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
23032
+ import { dirname } from "node:path";
22665
23033
  var RENAME_MAX_ATTEMPTS = 10;
22666
23034
  var RENAME_BACKOFF_STEP_MS = 10;
22667
23035
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
@@ -22678,6 +23046,9 @@ function errnoOf(e) {
22678
23046
  return e.code ?? e.message;
22679
23047
  }
22680
23048
  function writeFileAtomic(path, contents) {
23049
+ return writeAtomic(path, contents, RENAME_MAX_ATTEMPTS);
23050
+ }
23051
+ function writeAtomic(path, contents, maxAttempts) {
22681
23052
  const tmp = `${path}.${process.pid}.${Math.random().toString(36).slice(2, 10)}.tmp`;
22682
23053
  try {
22683
23054
  mkdirSync(dirname(path), { recursive: true });
@@ -22693,7 +23064,7 @@ function writeFileAtomic(path, contents) {
22693
23064
  return ok(void 0);
22694
23065
  } catch (e) {
22695
23066
  const code = errnoOf(e);
22696
- if (!TRANSIENT_RENAME_CODES.has(code) || attempt >= RENAME_MAX_ATTEMPTS) {
23067
+ if (!TRANSIENT_RENAME_CODES.has(code) || attempt >= maxAttempts) {
22697
23068
  discardTemp(tmp);
22698
23069
  return err({ kind: "save-failed", path, detail: `${code} after ${attempt} attempt(s)` });
22699
23070
  }
@@ -22702,17 +23073,15 @@ function writeFileAtomic(path, contents) {
22702
23073
  }
22703
23074
  }
22704
23075
  }
22705
- function isRecord2(v) {
22706
- return typeof v === "object" && v !== null && !Array.isArray(v);
22707
- }
22708
- function stripBom(text2) {
22709
- return text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
22710
- }
23076
+
23077
+ // src/store/pages.ts
23078
+ import { existsSync, readdirSync, rmSync as rmSync2 } from "node:fs";
23079
+ import { basename, dirname as dirname2, join } from "node:path";
22711
23080
  var STORE_DIR_NAME = ".mellos";
22712
23081
  var STATE_FILE_RELATIVE_PATH = join(STORE_DIR_NAME, "map.json");
22713
23082
  var PAGES_DIR_NAME = "pages";
22714
23083
  function pageFilePath(defaultFile, page2) {
22715
- return page2 === void 0 ? defaultFile : join(dirname(defaultFile), PAGES_DIR_NAME, `${page2}.json`);
23084
+ return page2 === void 0 ? defaultFile : join(dirname2(defaultFile), PAGES_DIR_NAME, `${page2}.json`);
22716
23085
  }
22717
23086
  function pageIdOfFile(defaultFile, path) {
22718
23087
  if (path === defaultFile) return void 0;
@@ -22724,28 +23093,40 @@ function listPageFiles(defaultFile) {
22724
23093
  if (existsSync(defaultFile)) out.push(defaultFile);
22725
23094
  let entries = [];
22726
23095
  try {
22727
- entries = readdirSync(join(dirname(defaultFile), PAGES_DIR_NAME));
23096
+ entries = readdirSync(join(dirname2(defaultFile), PAGES_DIR_NAME));
22728
23097
  } catch {
22729
23098
  }
22730
23099
  for (const e of entries.sort()) {
22731
- if (e.endsWith(".json")) out.push(join(dirname(defaultFile), PAGES_DIR_NAME, e));
23100
+ if (e.endsWith(".json")) out.push(join(dirname2(defaultFile), PAGES_DIR_NAME, e));
22732
23101
  }
22733
23102
  return out;
22734
23103
  }
22735
23104
  function deletePageFile(path) {
22736
23105
  try {
22737
- rmSync(path, { force: true });
23106
+ rmSync2(path, { force: true });
22738
23107
  return ok(void 0);
22739
23108
  } catch (e) {
22740
23109
  return err({ kind: "delete-failed", path, detail: errnoOf(e) });
22741
23110
  }
22742
23111
  }
23112
+
23113
+ // src/store/json-text.ts
23114
+ function isRecord2(v) {
23115
+ return typeof v === "object" && v !== null && !Array.isArray(v);
23116
+ }
23117
+ function stripBom(text2) {
23118
+ return text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
23119
+ }
23120
+
23121
+ // src/store/viewers.ts
23122
+ import { readdirSync as readdirSync2, readFileSync, statSync, rmSync as rmSync3 } from "node:fs";
23123
+ import { dirname as dirname3, join as join2 } from "node:path";
22743
23124
  var VIEWERS_DIR_NAME = "viewers";
22744
23125
  var VIEWER_FILE_VERSION = 1;
22745
23126
  var VIEWER_STALE_MS = 5e3;
22746
23127
  var VIEWER_SWEEP_MS = 6e4;
22747
23128
  function viewersDirPath(defaultFile) {
22748
- return join(dirname(defaultFile), VIEWERS_DIR_NAME);
23129
+ return join2(dirname3(defaultFile), VIEWERS_DIR_NAME);
22749
23130
  }
22750
23131
  function viewerPidOf(fileName) {
22751
23132
  const m = /^(\d+)\.json$/.exec(fileName);
@@ -22762,17 +23143,20 @@ function parseViewerReport(raw) {
22762
23143
  if (parsed["version"] !== VIEWER_FILE_VERSION) return void 0;
22763
23144
  const follow = parsed["follow"];
22764
23145
  if (typeof follow !== "boolean") return void 0;
23146
+ const owner = parsed["owner"];
23147
+ if (owner !== void 0 && (typeof owner !== "string" || !makePageId(owner).ok)) return void 0;
23148
+ const binding = owner === void 0 ? {} : { owner };
22765
23149
  const page2 = parsed["page"];
22766
- if (page2 === null || page2 === void 0) return { page: void 0, follow };
23150
+ if (page2 === null || page2 === void 0) return { page: void 0, follow, ...binding };
22767
23151
  if (typeof page2 !== "string") return void 0;
22768
23152
  const id2 = makePageId(page2);
22769
- return id2.ok ? { page: id2.value, follow } : void 0;
23153
+ return id2.ok ? { page: id2.value, follow, ...binding } : void 0;
22770
23154
  }
22771
23155
  function readLiveViewers(defaultFile, nowMs) {
22772
23156
  const dir = viewersDirPath(defaultFile);
22773
23157
  let names;
22774
23158
  try {
22775
- names = readdirSync(dir);
23159
+ names = readdirSync2(dir);
22776
23160
  } catch {
22777
23161
  return [];
22778
23162
  }
@@ -22780,13 +23164,13 @@ function readLiveViewers(defaultFile, nowMs) {
22780
23164
  for (const name of names) {
22781
23165
  const pid = viewerPidOf(name);
22782
23166
  if (pid === void 0) continue;
22783
- const path = join(dir, name);
23167
+ const path = join2(dir, name);
22784
23168
  let raw;
22785
23169
  let ageMs;
22786
23170
  try {
22787
23171
  ageMs = Math.max(0, nowMs - statSync(path).mtimeMs);
22788
23172
  if (ageMs > VIEWER_SWEEP_MS) {
22789
- rmSync(path, { force: true });
23173
+ rmSync3(path, { force: true });
22790
23174
  continue;
22791
23175
  }
22792
23176
  if (ageMs > VIEWER_STALE_MS) continue;
@@ -22799,13 +23183,17 @@ function readLiveViewers(defaultFile, nowMs) {
22799
23183
  }
22800
23184
  return live.sort((a, b) => a.ageMs - b.ageMs || a.pid - b.pid);
22801
23185
  }
23186
+
23187
+ // src/store/policy.ts
23188
+ import { readFileSync as readFileSync2 } from "node:fs";
23189
+ import { dirname as dirname4, join as join3 } from "node:path";
22802
23190
  var CONFIG_FILE_NAME = "config.json";
22803
23191
  var CONFIG_FILE_VERSION = 1;
22804
23192
  function configFilePath(defaultFile) {
22805
- return join(dirname(defaultFile), CONFIG_FILE_NAME);
23193
+ return join3(dirname4(defaultFile), CONFIG_FILE_NAME);
22806
23194
  }
22807
23195
  function userConfigFilePath(userBase) {
22808
- return join(userBase, STORE_DIR_NAME, CONFIG_FILE_NAME);
23196
+ return join3(userBase, STORE_DIR_NAME, CONFIG_FILE_NAME);
22809
23197
  }
22810
23198
  var MAPPING_POLICIES = ["always", "complex", "on-request"];
22811
23199
  function makeMappingPolicy(raw) {
@@ -22824,7 +23212,7 @@ function describeMappingPolicy(policy) {
22824
23212
  function loadMappingPolicy(path) {
22825
23213
  let text2;
22826
23214
  try {
22827
- text2 = readFileSync(path, "utf8");
23215
+ text2 = readFileSync2(path, "utf8");
22828
23216
  } catch (e) {
22829
23217
  if (e.code === "ENOENT") return ok(void 0);
22830
23218
  throw e;
@@ -22859,27 +23247,34 @@ function effectiveMappingPolicy(projectConfigFile, userConfigFile) {
22859
23247
  const source = project.value !== void 0 ? "project" : user.value !== void 0 ? "user" : void 0;
22860
23248
  return ok({ project: project.value, user: user.value, effective, source });
22861
23249
  }
22862
- var LEGACY_STATE_FILE_RELATIVE_PATH = join(".claude", "mellos-mapping.json");
23250
+
23251
+ // src/store/migration.ts
23252
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync as renameSync2 } from "node:fs";
23253
+ import { dirname as dirname5, join as join4 } from "node:path";
23254
+ var LEGACY_STATE_FILE_RELATIVE_PATH = join4(".claude", "mellos-mapping.json");
22863
23255
  var LEGACY_PAGES_DIR_NAME = "mellos-mapping.pages";
22864
23256
  var LEGACY_CONFIG_FILE_NAME = "mellos-mapping.config.json";
22865
23257
  function migrateLegacyStore(defaultFile) {
22866
- const projectRoot = dirname(dirname(defaultFile));
22867
- const legacyDefault = join(projectRoot, LEGACY_STATE_FILE_RELATIVE_PATH);
22868
- const legacyPages = join(dirname(legacyDefault), LEGACY_PAGES_DIR_NAME);
22869
- const legacyConfig = join(dirname(legacyDefault), LEGACY_CONFIG_FILE_NAME);
22870
- const hasLegacy = existsSync(legacyDefault) || existsSync(legacyPages) || existsSync(legacyConfig);
22871
- const hasCurrent = existsSync(defaultFile) || existsSync(join(dirname(defaultFile), PAGES_DIR_NAME)) || existsSync(configFilePath(defaultFile));
23258
+ const projectRoot = dirname5(dirname5(defaultFile));
23259
+ const legacyDefault = join4(projectRoot, LEGACY_STATE_FILE_RELATIVE_PATH);
23260
+ const legacyPages = join4(dirname5(legacyDefault), LEGACY_PAGES_DIR_NAME);
23261
+ const legacyConfig = join4(dirname5(legacyDefault), LEGACY_CONFIG_FILE_NAME);
23262
+ const hasLegacy = existsSync2(legacyDefault) || existsSync2(legacyPages) || existsSync2(legacyConfig);
23263
+ const hasCurrent = existsSync2(defaultFile) || existsSync2(join4(dirname5(defaultFile), PAGES_DIR_NAME)) || existsSync2(configFilePath(defaultFile));
22872
23264
  if (!hasLegacy || hasCurrent) return false;
22873
- mkdirSync(dirname(defaultFile), { recursive: true });
22874
- if (existsSync(legacyDefault)) renameSync(legacyDefault, defaultFile);
22875
- if (existsSync(legacyPages)) renameSync(legacyPages, join(dirname(defaultFile), PAGES_DIR_NAME));
22876
- if (existsSync(legacyConfig)) renameSync(legacyConfig, configFilePath(defaultFile));
23265
+ mkdirSync2(dirname5(defaultFile), { recursive: true });
23266
+ if (existsSync2(legacyDefault)) renameSync2(legacyDefault, defaultFile);
23267
+ if (existsSync2(legacyPages)) renameSync2(legacyPages, join4(dirname5(defaultFile), PAGES_DIR_NAME));
23268
+ if (existsSync2(legacyConfig)) renameSync2(legacyConfig, configFilePath(defaultFile));
22877
23269
  return true;
22878
23270
  }
23271
+
23272
+ // src/store/maps.ts
23273
+ import { readFileSync as readFileSync3 } from "node:fs";
22879
23274
  function loadMapFile(path) {
22880
23275
  let text2;
22881
23276
  try {
22882
- text2 = readFileSync(path, "utf8");
23277
+ text2 = readFileSync3(path, "utf8");
22883
23278
  } catch (e) {
22884
23279
  const code = e.code;
22885
23280
  if (code === "ENOENT") return err({ kind: "not-found", path });
@@ -22894,14 +23289,206 @@ function loadMapFile(path) {
22894
23289
  return parseMap(raw, path);
22895
23290
  }
22896
23291
  function saveMapFile(path, map) {
23292
+ const textError = mapTextError(map);
23293
+ if (textError) return err({ kind: "save-failed", path, detail: textError });
22897
23294
  return writeFileAtomic(path, serializeMap(map));
22898
23295
  }
22899
23296
 
23297
+ // src/store/transaction.ts
23298
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync as rmSync4, writeFileSync as writeFileSync2 } from "node:fs";
23299
+ import { dirname as dirname6, join as join5 } from "node:path";
23300
+ import { randomUUID, createHash } from "node:crypto";
23301
+ var LedgerError = class extends Error {
23302
+ constructor(code, message, details = {}) {
23303
+ super(message);
23304
+ this.code = code;
23305
+ this.details = details;
23306
+ }
23307
+ };
23308
+ var revisionOf = (map) => createHash("sha256").update(serializeMap(map)).digest("hex");
23309
+ function assertRevision(actual, expected) {
23310
+ if (expected !== void 0 && expected !== actual) throw new LedgerError("CONFLICT", "Map changed; read the current revision before retrying.", { expectedRevision: expected, actualRevision: actual });
23311
+ }
23312
+ function storeDirectory(file) {
23313
+ const dir = dirname6(file);
23314
+ return dir.endsWith("/pages") || dir.endsWith("\\pages") ? dirname6(dir) : dir;
23315
+ }
23316
+ function deadOwner(lock) {
23317
+ try {
23318
+ const owner = JSON.parse(readFileSync4(join5(lock, "owner.json"), "utf8"));
23319
+ if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0) return false;
23320
+ try {
23321
+ process.kill(owner.pid, 0);
23322
+ return false;
23323
+ } catch (e) {
23324
+ return e.code === "ESRCH";
23325
+ }
23326
+ } catch {
23327
+ return false;
23328
+ }
23329
+ }
23330
+ function withStoreLock(file, action) {
23331
+ const dir = storeDirectory(file);
23332
+ mkdirSync3(dir, { recursive: true });
23333
+ const lock = join5(dir, ".write-lock");
23334
+ const owner = join5(lock, "owner.json");
23335
+ const token = randomUUID();
23336
+ try {
23337
+ mkdirSync3(lock);
23338
+ } catch (error2) {
23339
+ if (error2.code !== "EEXIST") throw error2;
23340
+ if (!deadOwner(lock)) throw new LedgerError("BUSY", `Another writer owns ${lock}; retry after it completes. An orphan without owner metadata needs manual inspection.`);
23341
+ try {
23342
+ mkdirSync3(join5(lock, ".reap"));
23343
+ } catch {
23344
+ throw new LedgerError("BUSY", "Another process is recovering the writer lock.");
23345
+ }
23346
+ if (!deadOwner(lock)) {
23347
+ rmSync4(join5(lock, ".reap"), { recursive: true, force: true });
23348
+ throw new LedgerError("BUSY", "Writer ownership changed.");
23349
+ }
23350
+ rmSync4(lock, { recursive: true });
23351
+ try {
23352
+ mkdirSync3(lock);
23353
+ } catch {
23354
+ throw new LedgerError("BUSY", "Another writer acquired the recovered lock.");
23355
+ }
23356
+ }
23357
+ let initialized = false;
23358
+ try {
23359
+ writeFileSync2(owner, JSON.stringify({ pid: process.pid, token }), { flag: "wx" });
23360
+ initialized = true;
23361
+ return action();
23362
+ } finally {
23363
+ try {
23364
+ if (!initialized || JSON.parse(readFileSync4(owner, "utf8")).token === token) rmSync4(lock, { recursive: true });
23365
+ } catch {
23366
+ }
23367
+ }
23368
+ }
23369
+
23370
+ // src/store/project.ts
23371
+ import { existsSync as existsSync3, realpathSync } from "node:fs";
23372
+ import { dirname as dirname7, join as join6, resolve } from "node:path";
23373
+ import { homedir, tmpdir } from "node:os";
23374
+ function resolveProjectDirectory(cwd, stopAt = [homedir(), tmpdir()]) {
23375
+ let start = resolve(cwd);
23376
+ try {
23377
+ start = realpathSync(start);
23378
+ } catch {
23379
+ }
23380
+ let dir = start;
23381
+ const boundaries = new Set(stopAt.map((path) => {
23382
+ try {
23383
+ return realpathSync(path);
23384
+ } catch {
23385
+ return resolve(path);
23386
+ }
23387
+ }));
23388
+ while (true) {
23389
+ if (dir !== start && boundaries.has(dir)) return start;
23390
+ if (existsSync3(join6(dir, ".git")) || existsSync3(join6(dir, ".mellos", "map.json")) || existsSync3(join6(dir, ".mellos", "pages"))) return dir;
23391
+ const parent = dirname7(dir);
23392
+ if (parent === dir) return start;
23393
+ dir = parent;
23394
+ }
23395
+ }
23396
+
23397
+ // src/server/mutations.ts
23398
+ var key = (resource, row) => resource === "edges" ? `${String(row.from)}->${String(row.to)}` : row.id;
23399
+ function patch(row, values) {
23400
+ const next = { ...row };
23401
+ for (const [field, value] of Object.entries(values)) {
23402
+ if (value === void 0 || field === "id") continue;
23403
+ if (value === null) delete next[field];
23404
+ else next[field] = value;
23405
+ }
23406
+ return next;
23407
+ }
23408
+ function update(draft, resource, locator, values) {
23409
+ const index = draft[resource].findIndex((r) => key(resource, r) === locator);
23410
+ if (index < 0) throw new Error(`unknown ${resource}: ${String(locator)}`);
23411
+ draft[resource][index] = patch(draft[resource][index], values);
23412
+ }
23413
+ function applyBatch(map, operations, page2) {
23414
+ if (!operations.length) return err("nothing to change: pass operations");
23415
+ const draft = JSON.parse(serializeMap(map));
23416
+ draft.groups ??= [];
23417
+ draft.lanes ??= [];
23418
+ try {
23419
+ for (const [index, operation] of operations.entries()) {
23420
+ try {
23421
+ const input = operation.data;
23422
+ if (operation.op === "declare") {
23423
+ const data = input;
23424
+ for (const resource of ["layers", "lanes", "groups", "nodes", "edges"]) {
23425
+ for (const item of data[resource] ?? []) {
23426
+ const row = { ...item };
23427
+ if (draft[resource].some((r) => key(resource, r) === key(resource, row))) throw new Error(`${resource}: already exists ${String(key(resource, row))}`);
23428
+ if (resource === "nodes") row.status ??= "planned";
23429
+ draft[resource].push(row);
23430
+ }
23431
+ }
23432
+ if (data.title !== void 0) {
23433
+ if (data.title === null) delete draft.title;
23434
+ else draft.title = data.title;
23435
+ }
23436
+ if (data.kind !== void 0) draft.kind = data.kind;
23437
+ if (data.context !== void 0) draft.context = data.context;
23438
+ } else if (operation.op === "update") {
23439
+ const data = input;
23440
+ if (data.title !== void 0) {
23441
+ if (data.title === null) delete draft.title;
23442
+ else draft.title = data.title;
23443
+ }
23444
+ if (data.kind !== void 0) draft.kind = data.kind;
23445
+ if (data.context !== void 0) {
23446
+ if (data.context === null) delete draft.context;
23447
+ else draft.context = data.context;
23448
+ }
23449
+ for (const resource of ["layers", "groups", "lanes"]) for (const item of data[resource] ?? []) update(draft, resource, item.id, { ...item });
23450
+ for (const item of data.updates ?? []) update(draft, "nodes", item.id, { ...item });
23451
+ for (const item of data.edges ?? []) {
23452
+ const { from, to, newFrom, newTo, ...fields } = item;
23453
+ update(draft, "edges", `${from}->${to}`, { ...fields, ...newFrom !== void 0 ? { from: newFrom } : {}, ...newTo !== void 0 ? { to: newTo } : {} });
23454
+ }
23455
+ if (data.laneOrder !== void 0) {
23456
+ if (data.laneOrder.length !== draft.lanes.length || new Set(data.laneOrder).size !== draft.lanes.length || data.laneOrder.some((id2) => !draft.lanes.some((l) => l.id === id2))) throw new Error("laneOrder must name every lane exactly once");
23457
+ draft.lanes = data.laneOrder.map((id2) => draft.lanes.find((l) => l.id === id2));
23458
+ }
23459
+ } else {
23460
+ const data = input;
23461
+ for (const resource of ["edges", "nodes", "groups", "lanes", "layers"]) {
23462
+ for (const item of data[resource] ?? []) {
23463
+ const id2 = resource === "edges" ? key("edges", item) : item;
23464
+ if (!draft[resource].some((r) => key(resource, r) === id2)) throw new Error(`unknown ${resource}: ${String(id2)}`);
23465
+ draft[resource] = draft[resource].filter((r) => key(resource, r) !== id2);
23466
+ if (resource === "nodes") draft.edges = draft.edges.filter((e) => e.from !== id2 && e.to !== id2);
23467
+ if (resource === "groups" || resource === "lanes") {
23468
+ const field = resource === "groups" ? "group" : "lane";
23469
+ draft.nodes = draft.nodes.map((n) => n[field] === id2 ? patch(n, { [field]: null }) : n);
23470
+ }
23471
+ }
23472
+ }
23473
+ }
23474
+ } catch (error2) {
23475
+ throw new Error(`operations[${index}]: ${error2.message}`);
23476
+ }
23477
+ }
23478
+ if (page2 !== void 0 && draft.nodes.some((n) => n.submap === page2)) return err("a node cannot dive into its own page");
23479
+ const parsed = parseMap(draft, "transaction");
23480
+ return parsed.ok ? ok(parsed.value) : err(describeStoreError(parsed.error));
23481
+ } catch (error2) {
23482
+ return err(error2.message);
23483
+ }
23484
+ }
23485
+
22900
23486
  // src/server/apply.ts
22901
23487
  function refuseSelfDive(where, submap, page2) {
22902
23488
  return submap === page2 ? `${where}: a node cannot dive into its own page ("${submap}"); a submap links a CHILD page` : void 0;
22903
23489
  }
22904
23490
  function applyDeclare(map, input) {
23491
+ if (input.context !== void 0 || input.nodes?.some((n) => n.sources !== void 0)) return applyBatch(map, [{ op: "declare", data: input }], input.page);
22905
23492
  let next = input.title !== void 0 ? setTitle(map, input.title) : map;
22906
23493
  if (input.kind !== void 0) {
22907
23494
  const kind = makeMapKind(input.kind);
@@ -22997,6 +23584,7 @@ function applyDeclare(map, input) {
22997
23584
  return ok(next);
22998
23585
  }
22999
23586
  function applyUpdate(map, input) {
23587
+ if (input.title !== void 0 || input.kind !== void 0 || input.context !== void 0 || input.edges !== void 0 || input.laneOrder !== void 0 || input.groups?.some((g) => g.layer !== void 0) || input.updates?.some((n) => n.sources !== void 0)) return applyBatch(map, [{ op: "update", data: input }], input.page);
23000
23588
  let next = map;
23001
23589
  const items = (input.updates?.length ?? 0) + (input.layers?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0);
23002
23590
  if (items === 0) return err("nothing to revise: pass updates, layers, groups or lanes");
@@ -23022,6 +23610,7 @@ function applyUpdate(map, input) {
23022
23610
  for (const [i, g] of (input.groups ?? []).entries()) {
23023
23611
  const id2 = makeGroupId(g.id);
23024
23612
  if (!id2.ok) return err(`groups[${i}]: ${describeMapError(id2.error)}`);
23613
+ if (g.label === void 0) return err(`groups[${i}]: nothing to change; give a label or layer`);
23025
23614
  const updated = updateGroup(next, id2.value, g.label);
23026
23615
  if (!updated.ok) return err(`groups[${i}]: ${describeMapError(updated.error)}`);
23027
23616
  next = updated.value;
@@ -23143,9 +23732,359 @@ function summarize(map) {
23143
23732
  return `map now: ${map.layers.length} layer(s), ${map.nodes.length} node(s)` + (statusPart ? ` [${statusPart}]` : "") + (map.groups.length > 0 ? `, ${map.groups.length} group(s)` : "") + (map.lanes.length > 0 ? `, ${map.lanes.length} lane(s)` : "") + `, ${map.edges.length} edge(s)` + (map.kind !== void 0 && map.kind !== "dev" ? ` (${map.kind})` : "");
23144
23733
  }
23145
23734
 
23146
- // src/server/server.ts
23147
- var SERVER_NAME = "mellos-mapping";
23148
- var SERVER_VERSION = "0.20.3";
23735
+ // src/preview/publisher.ts
23736
+ import { createHash as createHash2 } from "node:crypto";
23737
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync3, realpathSync as realpathSync2, rmdirSync } from "node:fs";
23738
+ import { dirname as dirname8, join as join7, resolve as resolve2 } from "node:path";
23739
+
23740
+ // src/preview/presentation.ts
23741
+ var LABELS = { planned: "\u5F85\u5F00\u53D1", "in-progress": "\u5F00\u53D1\u4E2D", done: "\u5DF2\u9A8C\u8BC1", regressed: "\u51FA\u73B0\u56DE\u5F52" };
23742
+ function isVerified(node) {
23743
+ return node.status === "done" && node.evidence !== void 0;
23744
+ }
23745
+ function statusText(node) {
23746
+ return node.status === "done" && !isVerified(node) ? `${unverifiedDoneGlyph(true)} \u5B8C\u6210\u4F46\u7F3A\u5C11\u8BC1\u636E` : `${statusGlyph(node.status, true)} ${LABELS[node.status]}`;
23747
+ }
23748
+ function documentName(page2) {
23749
+ return page2 === void 0 ? "map.md" : `page-${page2}.md`;
23750
+ }
23751
+
23752
+ // src/preview/text.ts
23753
+ function xml(text2) {
23754
+ return text2.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\ufffe\uffff]/g, "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
23755
+ }
23756
+ function markdown(text2) {
23757
+ return text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/([\\`*_[\]{}()#+.!|~-])/g, "\\$1").replace(/\r?\n/g, " \n");
23758
+ }
23759
+ function cell(text2) {
23760
+ return markdown(text2).replace(/ \n/g, "<br>");
23761
+ }
23762
+
23763
+ // src/preview/markdown.ts
23764
+ function renderMapMarkdown(map, image, pages) {
23765
+ const neutral = isNeutralKind(map);
23766
+ const rows = [
23767
+ `# ${markdown(map.title ?? "\u6885\u52D2\u65AF\u5730\u56FE")}`,
23768
+ "",
23769
+ "[\u6240\u6709\u5730\u56FE](index.md)",
23770
+ "",
23771
+ "> \u81EA\u52A8\u751F\u6210\u7684\u5730\u56FE\u9884\u89C8\uFF1B\u4FEE\u6539\u5730\u56FE\u6570\u636E\u540E\u91CD\u65B0\u751F\u6210\u3002",
23772
+ ""
23773
+ ];
23774
+ if (!neutral) {
23775
+ const active = map.nodes.filter((n) => n.status === "in-progress");
23776
+ rows.push(
23777
+ `**\u5F53\u524D\uFF1A** ${active.length ? active.map((n) => markdown(n.label)).join("\u3001") : "\u6682\u65E0\u8FDB\u884C\u4E2D\u7684\u6A21\u5757"}`,
23778
+ "",
23779
+ `\u5DF2\u9A8C\u8BC1 **${map.nodes.filter(isVerified).length} / ${map.nodes.length}**\u3000\uFF5C\u3000\u56DE\u5F52 **${map.nodes.filter((n) => n.status === "regressed").length}**`,
23780
+ ""
23781
+ );
23782
+ }
23783
+ rows.push("## \u5206\u5C42\u4F9D\u8D56", "", `![\u5206\u5C42\u4F9D\u8D56\u5730\u56FE](${image})`, "");
23784
+ if (!neutral) rows.push("\xB7 \u5F85\u5F00\u53D1\u3000\u283F \u5F00\u53D1\u4E2D\u3000\u25A0 \u5DF2\u9A8C\u8BC1\u3000\u2717 \u51FA\u73B0\u56DE\u5F52\u3000\u25A1 \u5B8C\u6210\u4F46\u7F3A\u5C11\u8BC1\u636E", "");
23785
+ rows.push(map.kind === "sequence" ? "\u65F6\u95F4\u4ECE\u4E0A\u5411\u4E0B\u63A8\u8FDB\uFF1B\u7BAD\u5934\u4FDD\u7559\u5730\u56FE\u4E2D\u7684\u4F9D\u8D56\u65B9\u5411\u3002" : "\u7BAD\u5934\u7531\u4F7F\u7528\u65B9\u6307\u5411\u5B83\u4F9D\u8D56\u7684\u6A21\u5757\uFF1B\u57FA\u7840\u5C42\u4F4D\u4E8E\u4E0B\u65B9\u3002", "", "## \u6A21\u5757\u8BE6\u60C5", "");
23786
+ const nodes = new Map(map.nodes.map((n) => [n.id, n]));
23787
+ const layers = [...map.layers].sort((a, b) => map.kind === "sequence" ? a.rank - b.rank : b.rank - a.rank);
23788
+ for (const layer of layers) {
23789
+ rows.push(`### ${markdown(layer.name)}`, "");
23790
+ const members = map.nodes.filter((n) => n.layer === layer.id);
23791
+ if (!members.length) rows.push("\u5C1A\u672A\u58F0\u660E\u6A21\u5757\u3002", "");
23792
+ for (const node of members) {
23793
+ rows.push(`#### ${markdown(node.label)}${neutral ? "" : `\u3000${statusText(node)}`}`, "");
23794
+ if (node.detail !== void 0) rows.push(markdown(node.detail), "");
23795
+ const used = map.edges.filter((e) => e.from === node.id);
23796
+ rows.push(`**\u4F9D\u8D56\uFF1A** ${used.length ? used.map((e) => `${markdown(nodes.get(e.to).label)}${e.label !== void 0 ? `\uFF08${markdown(e.label)}\uFF09` : ""}`).join("\u3001") : "\u65E0"}`, "");
23797
+ const meta = [
23798
+ node.group === void 0 ? void 0 : map.groups.find((g) => g.id === node.group)?.label,
23799
+ node.lane === void 0 ? void 0 : map.lanes.find((l) => l.id === node.lane)?.label,
23800
+ node.kind
23801
+ ].filter((v) => v !== void 0);
23802
+ if (meta.length) rows.push(`**\u5F52\u5C5E / \u7C7B\u578B\uFF1A** ${meta.map(markdown).join(" \xB7 ")}`, "");
23803
+ if (node.evidence !== void 0) rows.push(`**\u9A8C\u8BC1\u8BB0\u5F55\uFF1A** ${markdown(node.evidence)}`, "");
23804
+ if (node.submap !== void 0) rows.push(pages.some((p) => p.page === node.submap) ? `[\u6253\u5F00\u5B50\u56FE\uFF1A${markdown(node.submap)}](${documentName(node.submap)})` : `\u5B50\u56FE\u5C1A\u672A\u521B\u5EFA\uFF1A${markdown(node.submap)}`, "");
23805
+ }
23806
+ }
23807
+ if (!neutral) {
23808
+ rows.push("## \u9A8C\u8BC1\u8BB0\u5F55", "", "| \u6A21\u5757 | \u72B6\u6001 | \u6700\u8FD1\u8BC1\u636E |", "| --- | --- | --- |");
23809
+ for (const node of map.nodes) rows.push(`| ${cell(node.label)} | ${statusText(node)} | ${node.evidence === void 0 ? "\u5C1A\u672A\u8BB0\u5F55" : cell(node.evidence)} |`);
23810
+ rows.push("");
23811
+ }
23812
+ rows.push("---", "", "\u9759\u6001\u6587\u6863\uFF1A\u66F4\u65B0\u65F6\u91CD\u65B0\u751F\u6210\u5730\u56FE\u56FE\u7247\u4E0E\u6587\u5B57\u3002\u56FE\u4E2D\u8282\u70B9\u4E0D\u652F\u6301\u62D6\u62FD\u3001\u60AC\u505C\u5C55\u5F00\u6216\u52A8\u753B\u3002", "");
23813
+ return rows.join("\n");
23814
+ }
23815
+ function renderPreviewIndex(pages) {
23816
+ return [
23817
+ "# \u6885\u52D2\u65AF\u5730\u56FE \xB7 \u9875\u9762\u76EE\u5F55",
23818
+ "",
23819
+ ...pages.map((p) => `- [${markdown(p.map.title ?? p.page ?? "\u9ED8\u8BA4\u5730\u56FE")}](${documentName(p.page)})`),
23820
+ "",
23821
+ "\u5730\u56FE\u9884\u89C8\u7531\u9879\u76EE\u5185\u7684\u5730\u56FE\u6570\u636E\u751F\u6210\u3002",
23822
+ ""
23823
+ ].join("\n");
23824
+ }
23825
+
23826
+ // src/preview/svg.ts
23827
+ var X = 8;
23828
+ var Y = 26;
23829
+ var TOP = 20;
23830
+ var PALETTES = {
23831
+ planned: ["#f5f7fa", "#98a3b2", "#566477"],
23832
+ "in-progress": ["#fff4d9", "#c48c24", "#805910"],
23833
+ done: ["#e9f5ee", "#67a883", "#286247"],
23834
+ regressed: ["#fdecec", "#cc7575", "#923d3d"],
23835
+ unverified: ["#fff6e8", "#b49a77", "#785e3e"],
23836
+ neutral: ["#f2f5f9", "#a1adbc", "#364558"]
23837
+ };
23838
+ function renderMapSvg(map) {
23839
+ const neutral = isNeutralKind(map);
23840
+ const originals = new Map(map.nodes.map((n) => [n.id, n]));
23841
+ const oriented = flipForSequence(map);
23842
+ const shaped = { ...oriented, nodes: oriented.nodes.map((n) => {
23843
+ const label = fitWidth(n.label, 32);
23844
+ return { ...n, label: label + " ".repeat(Math.max(0, 20 - displayWidth(label))) };
23845
+ }) };
23846
+ const geo = { ...zoomGeometry(0), boxGap: 6 };
23847
+ const columns = layoutColumns(shaped, geo, true, neutral);
23848
+ const routing = routeEdges(shaped, columns);
23849
+ const rows = layoutRows(columns, geo, routing.gapRowCount, false, map.lanes.length > 0);
23850
+ const width = Math.max(440, (columns.contentWidth + 4 + routing.fallbackCount * 2) * X);
23851
+ const height = Math.max(100, TOP + rows.legendY * Y);
23852
+ const parts = [
23853
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-labelledby="map-title map-desc">`,
23854
+ `<title id="map-title">${xml(map.title ?? "\u6885\u52D2\u65AF\u5730\u56FE")}</title>`,
23855
+ `<desc id="map-desc">${map.nodes.length} \u4E2A\u8282\u70B9\uFF0C${map.edges.length} \u6761\u4F9D\u8D56\u3002\u5B8C\u6574\u8BF4\u660E\u4E0E\u9A8C\u8BC1\u8BB0\u5F55\u5728\u5730\u56FE\u6587\u6863\u4E2D\u3002</desc>`,
23856
+ '<defs><marker id="arrow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0 0 L8 4 L0 8 Z" fill="#8995a5"/></marker></defs>',
23857
+ `<rect width="${width}" height="${height}" fill="#ffffff"/>`,
23858
+ '<g font-family="Segoe UI, Microsoft YaHei, Noto Sans CJK SC, sans-serif">'
23859
+ ];
23860
+ if (map.nodes.length === 0) parts.push('<text x="20" y="52" font-size="14" fill="#657286">\u5C1A\u672A\u58F0\u660E\u6A21\u5757\uFF0C\u7B49\u5F85\u5730\u56FE\u66F4\u65B0\u3002</text>');
23861
+ columns.bands.forEach((band, i) => {
23862
+ const top = TOP + rows.barY[i] * Y;
23863
+ const boxes = rows.bandBoxes[i];
23864
+ const bottom = boxes.reduce((max, b) => Math.max(max, TOP + (b.y + b.h - 1) * Y), top + 60);
23865
+ parts.push(`<rect x="8" y="${top - 10}" width="${width - 16}" height="${bottom - top + 26}" rx="5" fill="#fafbfc"/>`);
23866
+ parts.push(`<text x="16" y="${top + 5}" font-size="12" fill="#6a7687">${xml(fitWidth(band.name, Math.floor((width - 40) / X)))}</text>`);
23867
+ });
23868
+ if (rows.laneHeaderY !== void 0) map.lanes.forEach((lane, i) => {
23869
+ const region = columns.lanes[i];
23870
+ parts.push(`<text x="${(region.x + region.w / 2) * X}" y="${TOP + rows.laneHeaderY * Y + 5}" text-anchor="middle" font-size="12" fill="#566477">${xml(fitWidth(lane.label, region.w))}</text>`);
23871
+ });
23872
+ for (const edge of routing.edges) {
23873
+ const points = edgePolyline(edge, rows).map(([x, y]) => `${x * X},${TOP + y * Y}`).join(" ");
23874
+ parts.push(`<polyline points="${points}" fill="none" stroke="#8995a5" stroke-width="1.4" marker-end="url(#arrow)"/>`);
23875
+ }
23876
+ for (const box of rows.boxOf.values()) {
23877
+ const node = originals.get(box.node.id);
23878
+ const palette = neutral ? PALETTES.neutral : node.status === "done" && !isVerified(node) ? PALETTES.unverified : PALETTES[node.status];
23879
+ const x = box.x * X, y = TOP + box.y * Y, w = (box.w - 1) * X, h = (box.h - 1) * Y;
23880
+ const dash = !neutral && node.status === "planned" ? ' stroke-dasharray="5 4"' : "";
23881
+ parts.push(`<g data-node="${xml(node.id)}"><title>${xml(node.label)}${neutral ? "" : ` \xB7 ${xml(statusText(node))}`}</title>`);
23882
+ parts.push(`<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="5" fill="${palette[0]}" stroke="${palette[1]}" stroke-width="1.5"${dash}/>`);
23883
+ parts.push(`<text x="${x + w / 2}" y="${y + 21}" text-anchor="middle" font-size="14" fill="${palette[2]}">${xml(box.label.trim())}</text>`);
23884
+ const secondary = neutral ? node.kind ?? node.id : statusText(node);
23885
+ parts.push(`<text x="${x + w / 2}" y="${y + 40}" text-anchor="middle" font-size="11" fill="${palette[2]}">${xml(fitWidth(secondary, box.w - 4))}</text></g>`);
23886
+ }
23887
+ parts.push("</g></svg>");
23888
+ return parts.join("\n");
23889
+ }
23890
+
23891
+ // src/preview/publisher.ts
23892
+ var PREVIEW_DIR_NAME = "previews";
23893
+ var ENABLED = ".enabled";
23894
+ var PUBLISH_LOCK = ".publish-lock";
23895
+ function previewDirectory(defaultFile) {
23896
+ return join7(dirname8(defaultFile), PREVIEW_DIR_NAME);
23897
+ }
23898
+ function previewFile(defaultFile, page2) {
23899
+ if (page2 !== void 0 && !ID_RULE.test(page2)) throw new Error("Invalid preview page id.");
23900
+ return join7(previewDirectory(defaultFile), documentName(page2));
23901
+ }
23902
+ function save(path, contents) {
23903
+ try {
23904
+ if (readFileSync5(path, "utf8") === contents) return;
23905
+ } catch (error2) {
23906
+ if (error2.code !== "ENOENT") throw error2;
23907
+ }
23908
+ const result = writeFileAtomic(path, contents);
23909
+ if (!result.ok) throw new Error(describeStoreError(result.error));
23910
+ }
23911
+ function ownedDirectory(path) {
23912
+ mkdirSync4(path, { recursive: true });
23913
+ const expected = join7(realpathSync2(dirname8(path)), path.slice(dirname8(path).length + 1));
23914
+ const actual = realpathSync2(path);
23915
+ if (process.platform === "win32" ? actual.toLowerCase() !== expected.toLowerCase() : actual !== expected) {
23916
+ throw new Error(`Preview directory redirects outside its parent: ${path}`);
23917
+ }
23918
+ }
23919
+ function acquireLock(directory) {
23920
+ const path = join7(directory, PUBLISH_LOCK);
23921
+ const deadline = Date.now() + 2e3;
23922
+ while (true) {
23923
+ try {
23924
+ mkdirSync4(path);
23925
+ return () => rmdirSync(path);
23926
+ } catch (error2) {
23927
+ if (error2.code !== "EEXIST") throw error2;
23928
+ if (Date.now() >= deadline) throw new Error(`Preview export is busy or was interrupted. Retry; if no exporter is running, remove the stale lock directory: ${path}`);
23929
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
23930
+ }
23931
+ }
23932
+ }
23933
+ function createPreviewPublisher(defaultFile) {
23934
+ const directory = previewDirectory(defaultFile);
23935
+ const enabledFile = join7(directory, ENABLED);
23936
+ const enabled = () => existsSync4(enabledFile);
23937
+ const refresh = (page2) => {
23938
+ try {
23939
+ const path = previewFile(defaultFile, page2);
23940
+ ownedDirectory(directory);
23941
+ const release = acquireLock(directory);
23942
+ try {
23943
+ const pages = [];
23944
+ for (const source of listPageFiles(defaultFile)) {
23945
+ const slug = pageIdOfFile(defaultFile, source);
23946
+ if (slug !== void 0 && !ID_RULE.test(slug)) throw new Error(`Invalid map page filename: ${source}`);
23947
+ const loaded = loadMapFile(source);
23948
+ if (!loaded.ok) throw new Error(describeStoreError(loaded.error));
23949
+ pages.push({ page: slug, map: loaded.value });
23950
+ }
23951
+ if (page2 !== void 0 && !pages.some((p) => p.page === page2)) return err(`No map page named "${page2}".`);
23952
+ if (page2 === void 0 && !pages.some((p) => p.page === void 0)) pages.unshift({ page: void 0, map: EMPTY_MAP });
23953
+ const images = join7(directory, "images");
23954
+ ownedDirectory(images);
23955
+ const present = /* @__PURE__ */ new Set();
23956
+ for (const item of pages) {
23957
+ const svg = renderMapSvg(item.map);
23958
+ const digest = createHash2("sha256").update(svg).digest("hex");
23959
+ const image = `images/${digest}.svg`;
23960
+ save(join7(images, `${digest}.svg`), svg);
23961
+ const filename = documentName(item.page);
23962
+ save(join7(directory, filename), renderMapMarkdown(item.map, image, pages));
23963
+ present.add(filename);
23964
+ }
23965
+ for (const filename of readdirSync3(directory)) {
23966
+ if (/^(map|page-[a-z0-9][a-z0-9-]{0,63})\.md$/.test(filename) && !present.has(filename)) {
23967
+ save(join7(directory, filename), "# \u5730\u56FE\u5DF2\u5220\u9664\n\n\u6B64\u9875\u9762\u5DF2\u4E0D\u5728\u9879\u76EE\u5730\u56FE\u4E2D\u3002\n\n[\u8FD4\u56DE\u5730\u56FE\u76EE\u5F55](index.md)\n");
23968
+ }
23969
+ }
23970
+ const index = join7(directory, "index.md");
23971
+ save(index, renderPreviewIndex(pages));
23972
+ return ok({ path: resolve2(path), index: resolve2(index), pages: pages.length });
23973
+ } finally {
23974
+ release();
23975
+ }
23976
+ } catch (error2) {
23977
+ return err(error2 instanceof Error ? error2.message : String(error2));
23978
+ }
23979
+ };
23980
+ const activate = (page2) => {
23981
+ const published = refresh(page2);
23982
+ if (!published.ok) return published;
23983
+ try {
23984
+ save(enabledFile, "Markdown preview updates are enabled for this project.\n");
23985
+ } catch (error2) {
23986
+ return err(error2 instanceof Error ? error2.message : String(error2));
23987
+ }
23988
+ return published;
23989
+ };
23990
+ return { enabled, refresh, activate };
23991
+ }
23992
+
23993
+ // src/web/launcher.ts
23994
+ import { spawn } from "node:child_process";
23995
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "node:fs";
23996
+ import { dirname as dirname10, join as join8 } from "node:path";
23997
+
23998
+ // src/web/source.ts
23999
+ import { createHash as createHash3 } from "node:crypto";
24000
+ import { statSync as statSync2 } from "node:fs";
24001
+ import { basename as basename2, dirname as dirname9 } from "node:path";
24002
+ function readWebSnapshot(defaultFile) {
24003
+ const pages = listPageFiles(defaultFile).map((file) => {
24004
+ const id2 = pageIdOfFile(defaultFile, file) ?? "";
24005
+ const title = id2 || "\u9ED8\u8BA4\u5730\u56FE";
24006
+ try {
24007
+ if (id2 !== "" && !ID_RULE.test(id2)) throw new Error("Invalid page filename");
24008
+ const modified = statSync2(file).mtimeMs;
24009
+ const result = loadMapFile(file);
24010
+ return result.ok ? { id: id2, title: result.value.title ?? title, modified, map: result.value } : { id: id2, title, modified, error: describeStoreError(result.error) };
24011
+ } catch (error2) {
24012
+ return { id: id2, title, modified: 0, error: String(error2) };
24013
+ }
24014
+ });
24015
+ if (pages.length === 0) pages.push({ id: "", title: "\u7B49\u5F85\u7B2C\u4E00\u5F20\u5730\u56FE", modified: 0, map: EMPTY_MAP });
24016
+ const value = { project: basename2(dirname9(dirname9(defaultFile))), pages };
24017
+ return { revision: createHash3("sha256").update(JSON.stringify(value)).digest("hex"), value };
24018
+ }
24019
+
24020
+ // src/web/launcher.ts
24021
+ var webRuntimeFile = (defaultFile) => join8(dirname10(defaultFile), "web", "server.json");
24022
+ async function runningWebUrl(defaultFile) {
24023
+ try {
24024
+ const info = JSON.parse(readFileSync6(webRuntimeFile(defaultFile), "utf8"));
24025
+ if (!Number.isInteger(info.port) || info.port < 1 || info.port > 65535 || !/^[a-f0-9]{48}$/.test(info.token)) return void 0;
24026
+ const url = `http://127.0.0.1:${info.port}/${info.token}/`;
24027
+ const response = await fetch(`${url}api/health`, { signal: AbortSignal.timeout(700) });
24028
+ if (response.ok && (await response.json()).file === defaultFile) return url;
24029
+ } catch {
24030
+ }
24031
+ return void 0;
24032
+ }
24033
+ async function openWebPreview(defaultFile, entry, page2, terminal = false) {
24034
+ if (page2 !== void 0 && (!ID_RULE.test(page2) || !readWebSnapshot(defaultFile).value.pages.some((p) => p.id === page2))) throw new Error(`No map page named "${page2}".`);
24035
+ let url = await runningWebUrl(defaultFile);
24036
+ if (url) {
24037
+ const health = await fetch(`${url}api/health`, { signal: AbortSignal.timeout(2e3) });
24038
+ const info = await health.json();
24039
+ if (!info.formats?.includes(2) || terminal && !info.surfaces?.includes("web-terminal")) {
24040
+ await fetch(`${url}api/stop`, { method: "POST", signal: AbortSignal.timeout(2e3) });
24041
+ const deadline = Date.now() + 3e3;
24042
+ while (await runningWebUrl(defaultFile)) {
24043
+ if (Date.now() > deadline) throw new Error("Old web viewer is still stopping. Retry opening the map.");
24044
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
24045
+ }
24046
+ url = void 0;
24047
+ }
24048
+ }
24049
+ if (!url) {
24050
+ if (!existsSync5(entry)) throw new Error(`Web runtime missing: ${entry}. Run npm run build or reinstall the plugin.`);
24051
+ const child = spawn(process.execPath, [entry, "--serve", defaultFile], { detached: true, windowsHide: true, stdio: "ignore" });
24052
+ let failure;
24053
+ child.on("error", (error2) => {
24054
+ failure = error2;
24055
+ });
24056
+ child.unref();
24057
+ const deadline = Date.now() + 8e3;
24058
+ while (!url && Date.now() < deadline) {
24059
+ if (failure) throw failure;
24060
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
24061
+ url = await runningWebUrl(defaultFile);
24062
+ }
24063
+ if (!url) throw new Error("Web preview did not start. Run the web CLI directly to inspect the error.");
24064
+ }
24065
+ const query = new URLSearchParams();
24066
+ if (terminal) query.set("view", "terminal");
24067
+ if (page2 !== void 0) query.set("page", page2);
24068
+ return query.size ? `${url}?${query}` : url;
24069
+ }
24070
+
24071
+ // src/server/terminal-handoff.ts
24072
+ function terminalHandoff(node, watcher, stateFile, page2) {
24073
+ const args = [watcher, "--file", stateFile, ...page2 ? ["--page", page2] : []];
24074
+ const tokens = [node, ...args];
24075
+ if (tokens.some((token) => /[\r\n\0]/.test(token))) throw new Error("Terminal paths must fit on one line.");
24076
+ const powershell = (token) => `'${token.replaceAll("'", "''")}'`;
24077
+ const posix = (token) => `'${token.replaceAll("'", `'"'"'`)}'`;
24078
+ return {
24079
+ command: node,
24080
+ args,
24081
+ powershell: `& ${tokens.map(powershell).join(" ")}`,
24082
+ posix: tokens.map(posix).join(" "),
24083
+ hostOpen: { placement: "right", target: { type: "terminal" } }
24084
+ };
24085
+ }
24086
+
24087
+ // src/server/tool-definitions.ts
23149
24088
  var TITLE_MAX = 120;
23150
24089
  var LABEL_MAX = 60;
23151
24090
  var DETAIL_MAX = 600;
@@ -23154,7 +24093,7 @@ var EDGE_LABEL_MAX = 80;
23154
24093
  function id(description) {
23155
24094
  return external_exports.string().regex(ID_RULE, ID_RULE_TEXT).describe(description);
23156
24095
  }
23157
- var PAGE_DESCRIPTION = "page (parallel map) this call targets; omit for the default page. One effort = one page: start a NEW effort on its own page named after the effort, so concurrent sessions never write over each other and the pane can switch between pages.";
24096
+ var PAGE_DESCRIPTION = "page (parallel map) this call targets; omit for the default page. A new conversation is not a new effort. Read existing pages with mmap_read first; reuse the same page for continued work. Create a new page only for a distinct effort.";
23158
24097
  function page() {
23159
24098
  return id(PAGE_DESCRIPTION).optional();
23160
24099
  }
@@ -23181,10 +24120,6 @@ function edgeEnds() {
23181
24120
  to: id("the node being used (must live on a strictly lower layer)")
23182
24121
  };
23183
24122
  }
23184
- var NO_CONTROLS = /^[^\u0000-\u001f\u007f-\u009f]*$/;
23185
- var NO_CONTROLS_TEXT = "one line of text; control characters (ESC, newline, tab) are not allowed";
23186
- var NO_CONTROLS_BUT_BREAKS = /^[^\u0000-\u0008\u000b-\u001f\u007f-\u009f]*$/;
23187
- var NO_CONTROLS_BUT_BREAKS_TEXT = "text with optional newlines (\\n) and tabs; other control characters (ESC, BEL, CR) are not allowed";
23188
24123
  function line(max, description) {
23189
24124
  return external_exports.string().min(1).max(max).regex(NO_CONTROLS, NO_CONTROLS_TEXT).describe(description);
23190
24125
  }
@@ -23194,6 +24129,334 @@ function note(max, description) {
23194
24129
  function closed(shape) {
23195
24130
  return external_exports.object(shape).strict();
23196
24131
  }
24132
+ var expectedRevision = () => external_exports.string().regex(/^(?:[a-f0-9]{64}|absent)$/).optional().describe("Revision from mmap_read; absent requires a new page. A stale revision returns CONFLICT.");
24133
+ var context = () => closed({ summary: note(2e3, "Concise purpose and confirmed decisions").optional(), next: note(2e3, "Next actions for resuming this effort").optional() });
24134
+ var sources = () => external_exports.array(closed({ path: external_exports.string().max(1024), sha256: external_exports.string().regex(/^[a-f0-9]{64}$/).optional() })).max(100).refine((value) => sourceError(value) === void 0, "Use project-relative file paths without traversal.").describe("Optional source files and verified SHA256 baselines. mmap_read changes checks them without rescanning the repository.");
24135
+ function declareTool() {
24136
+ return {
24137
+ title: "Declare map structure",
24138
+ description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the missing design after reading existing pages with mmap_read; reuse verified nodes. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing. Title and kind can also be changed with mmap_update; this legacy form remains supported. Revising what already exists (moving, renaming, relabeling, clearing) is mmap_update.",
24139
+ inputSchema: closed({
24140
+ page: page(),
24141
+ expectedRevision: expectedRevision(),
24142
+ context: context().optional(),
24143
+ title: line(TITLE_MAX, "map title, e.g. the feature being built; null removes it").nullable().optional(),
24144
+ kind: mapKind().optional(),
24145
+ lanes: external_exports.array(
24146
+ closed({
24147
+ id: id("stable kebab-case identifier of the lane"),
24148
+ label: line(LABEL_MAX, "column name, e.g. a sequence participant")
24149
+ })
24150
+ ).optional().describe("vertical columns crossing all bands; declaration order = left-to-right"),
24151
+ layers: external_exports.array(
24152
+ closed({
24153
+ id: id("stable kebab-case identifier of the band"),
24154
+ name: line(LABEL_MAX, "display name of the band"),
24155
+ rank: rank()
24156
+ })
24157
+ ).optional(),
24158
+ groups: external_exports.array(
24159
+ closed({
24160
+ id: id("stable kebab-case identifier of the group"),
24161
+ label: line(LABEL_MAX, "subsystem name shown at the far zoom"),
24162
+ layer: id("band this group clusters; members must live on the same band")
24163
+ })
24164
+ ).optional(),
24165
+ nodes: external_exports.array(
24166
+ closed({
24167
+ id: id("stable kebab-case identifier of the node"),
24168
+ sources: sources().optional(),
24169
+ label: line(LABEL_MAX, "display label inside the box"),
24170
+ layer: id("id of the band this node lives in"),
24171
+ status: status("defaults to planned").optional(),
24172
+ evidence: line(
24173
+ EVIDENCE_MAX,
24174
+ "how an already-verified node was verified; for regressed: what broke. Declaring a node straight to done needs it as much as updating one does."
24175
+ ).optional(),
24176
+ detail: note(
24177
+ DETAIL_MAX,
24178
+ "design notes shown in the pane detail panel: responsibility, contract, key decisions"
24179
+ ).optional(),
24180
+ group: id("same-band group this node belongs to").optional(),
24181
+ kind: nodeKind().optional(),
24182
+ lane: id("lane (column) this node belongs to").optional(),
24183
+ submap: id(
24184
+ "page slug of this node's child map \u2014 the pane badges the node \u229E and double-click dives in. Declare the child page separately. Create a sub-map only when the node's internals genuinely deserve their own picture; most nodes need none."
24185
+ ).optional()
24186
+ })
24187
+ ).optional(),
24188
+ edges: external_exports.array(
24189
+ closed({
24190
+ ...edgeEnds(),
24191
+ label: line(EDGE_LABEL_MAX, "what flows along the edge").optional()
24192
+ })
24193
+ ).optional()
24194
+ })
24195
+ };
24196
+ }
24197
+ function updateTool() {
24198
+ return {
24199
+ title: "Record progress and revise the map",
24200
+ description: "The revision tool, all-or-nothing. Record progress on nodes: in-progress when starting a node (the pane spins), done with evidence when its verification passes, regressed with evidence when a done node breaks. Revise what the ghost design got wrong: move a node to another band, join or leave a group or lane, rename a band (or re-rank it, which reorders the whole map), relabel a group or a lane. Every clearable field takes null to empty it \u2014 that is how a field is cleared, never an empty string. Bands, groups and lanes are applied before the node updates, and within one node update `layer` moves the node before its other fields. The map is a ledger: report honestly, it never blocks you.",
24201
+ inputSchema: closed({
24202
+ page: page(),
24203
+ expectedRevision: expectedRevision(),
24204
+ title: line(TITLE_MAX, "Map title; null clears").nullable().optional(),
24205
+ kind: mapKind().optional(),
24206
+ context: context().nullable().optional(),
24207
+ laneOrder: external_exports.array(id("existing lane id")).max(100).optional().describe("Every lane exactly once, in display order."),
24208
+ edges: external_exports.array(closed({ ...edgeEnds(), label: line(EDGE_LABEL_MAX, "New edge label; null clears").nullable().optional(), newFrom: id("replacement consumer").optional(), newTo: id("replacement dependency").optional() })).min(1).optional(),
24209
+ updates: external_exports.array(
24210
+ closed({
24211
+ id: id("id of the node to update"),
24212
+ sources: sources().nullable().optional(),
24213
+ status: status("the status to record").optional(),
24214
+ label: line(LABEL_MAX, "new display label inside the box").optional(),
24215
+ evidence: line(EVIDENCE_MAX, "for done: how it was verified; for regressed: what broke; null clears it").nullable().optional(),
24216
+ detail: note(
24217
+ DETAIL_MAX,
24218
+ "design notes shown in the pane detail panel: responsibility, contract, key decisions; null clears them"
24219
+ ).nullable().optional(),
24220
+ layer: id(
24221
+ "move the node to this band; applied before this item's other fields, so a node can move and join a group on the new band in one item. Every edge touching it must still point strictly downward, and a grouped node may only move to its group's band."
24222
+ ).optional(),
24223
+ group: id("join this same-band group; null leaves the current group").nullable().optional(),
24224
+ kind: nodeKind().nullable().optional(),
24225
+ lane: id("join this lane; null leaves the current lane").nullable().optional(),
24226
+ submap: id("link a child map page by slug; null unlinks it").nullable().optional()
24227
+ })
24228
+ ).min(1).optional(),
24229
+ layers: external_exports.array(
24230
+ closed({
24231
+ id: id("id of the band to revise"),
24232
+ name: line(LABEL_MAX, "new display name of the band").optional(),
24233
+ rank: rank().optional()
24234
+ })
24235
+ ).min(1).optional().describe("rename and/or re-rank existing bands; an item must carry a name, a rank, or both"),
24236
+ groups: external_exports.array(closed({ id: id("id of the group to revise"), label: line(LABEL_MAX, "new subsystem name").optional(), layer: id("new layer; move members in the same batch").optional() })).min(1).optional().describe("relabel or move a group; final membership must match its layer"),
24237
+ lanes: external_exports.array(closed({ id: id("id of the lane to relabel"), label: line(LABEL_MAX, "new column name") })).min(1).optional().describe("relabel existing lanes; order and membership are untouched")
24238
+ })
24239
+ };
24240
+ }
24241
+ function removeTool() {
24242
+ return {
24243
+ title: "Revise the map",
24244
+ description: 'Remove edges, nodes, groups and empty layer bands (in that order, all-or-nothing). Removing a node also removes every edge touching it; removing a group merely ungroups its members. Use when the ghost design turns out wrong \u2014 the map is a hypothesis, revising it is honest work. `pages` is the other scale: it DELETES whole page files, so a finished effort can be cleaned up instead of accumulating tabs forever. A bare `{pages: ["slug"]}` with no other field is the normal form; combined with map edits, the edits are applied first and the pages are deleted after. The deletion is permanent and cannot be undone, so delete only pages whose effort is over \u2014 and only ever with the user behind it. An unknown slug is refused with the project\'s real page list (naming a page that does not exist is a typo, not a request). The default page has no slug and is not deletable here. A node elsewhere still pointing at a deleted page with `submap` stays legal \u2014 a submap reference has no existence invariant \u2014 but it has nowhere to dive until the page comes back.',
24245
+ inputSchema: closed({
24246
+ page: page(),
24247
+ expectedRevision: expectedRevision(),
24248
+ deletePage: external_exports.boolean().optional().describe("Delete the page targeted by page, including the default page; cannot be combined with other edits."),
24249
+ references: external_exports.enum(["reject", "keep"]).optional().describe("For deletePage: reject inbound submap references by default, or explicitly keep them."),
24250
+ edges: external_exports.array(closed(edgeEnds())).optional(),
24251
+ nodes: external_exports.array(id("id of the node to remove, with every edge touching it")).optional(),
24252
+ groups: external_exports.array(id("id of the group to remove; members stay, merely ungrouped")).optional(),
24253
+ lanes: external_exports.array(id("id of the lane to remove; members stay, merely off-lane")).optional(),
24254
+ layers: external_exports.array(id("id of the band to remove; it must hold no nodes and no groups")).optional(),
24255
+ pages: external_exports.array(
24256
+ id(
24257
+ "slug of a page whose WHOLE map file is deleted \u2014 the page and everything drawn on it. Not the page this same call targets with `page`."
24258
+ )
24259
+ ).optional().describe("pages to delete entirely, after this call's map edits; permanent")
24260
+ })
24261
+ };
24262
+ }
24263
+ function setupTool() {
24264
+ return {
24265
+ title: "Configure when maps open",
24266
+ description: 'Get or set the mapping policy \u2014 WHEN the assistant opens a Mellos map. Call with no arguments to read it: the reply names the policy chosen for the USER (every project), the one this PROJECT overrides it with if any, and which of them is in effect. If it reports "not set", ask the USER to choose (never pick for them): always = ' + describeMappingPolicy("always") + "; complex = " + describeMappingPolicy("complex") + "; on-request = " + describeMappingPolicy("on-request") + '. Then call again with their choice to persist it. It defaults to user scope, which is the normal one \u2014 the question is about how someone works, so it is asked once ever, not once per repository. Pass scope: "project" only when the user wants THIS project to differ from that habit. The policy guides you; it never blocks the tools, and an explicit user request for a map always wins.',
24267
+ inputSchema: closed({
24268
+ policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy"),
24269
+ scope: external_exports.enum(POLICY_SCOPES).optional().describe(
24270
+ 'where to record the choice: "user" (default) applies to every project this user opens; "project" overrides that for this project alone. Ignored when reading.'
24271
+ )
24272
+ })
24273
+ };
24274
+ }
24275
+ function readTool() {
24276
+ return {
24277
+ title: "Read and resume saved maps",
24278
+ description: "Read existing pages before creating a map. Returns structured IDs, revisions and bounded results. pages lists summaries; map reads page metadata/context; nodes/edges/layers/groups/lanes read editable records; neighborhood reads related nodes; changes compares saved source hashes with local files. New conversations and compacted context should resume the existing effort. mmap_view remains the picture.",
24279
+ inputSchema: closed({
24280
+ resource: external_exports.enum(["pages", "map", "nodes", "edges", "layers", "groups", "lanes", "neighborhood", "changes"]).optional(),
24281
+ page: page(),
24282
+ id: external_exports.string().min(1).max(200).optional().describe("Exact ID; missing returns NOT_FOUND. Edge IDs use from->to; default page ID is _default."),
24283
+ ids: external_exports.array(external_exports.string().min(1).max(200)).max(100).optional(),
24284
+ query: external_exports.string().max(200).optional(),
24285
+ status: external_exports.enum(NODE_STATUSES).optional(),
24286
+ layer: id("filter by layer").optional(),
24287
+ group: id("filter by group").optional(),
24288
+ lane: id("filter by lane").optional(),
24289
+ fields: external_exports.array(external_exports.enum(["id", "label", "name", "rank", "status", "layer", "group", "lane", "kind", "submap", "detail", "evidence", "sources", "from", "to", "title", "context", "counts", "revision", "error", "state", "affectedConsumers"])).min(1).max(30).optional(),
24290
+ limit: external_exports.number().int().min(1).max(100).optional(),
24291
+ cursor: external_exports.string().max(2048).optional().describe("Opaque cursor; reuse the same query. Changes return CONFLICT instead of skipping records."),
24292
+ ifRevision: expectedRevision(),
24293
+ depth: external_exports.number().int().min(0).max(4).optional(),
24294
+ direction: external_exports.enum(["dependencies", "consumers", "both"]).optional()
24295
+ })
24296
+ };
24297
+ }
24298
+ function batchTool() {
24299
+ return {
24300
+ title: "Commit a single-page transaction",
24301
+ description: "Atomically combine additions, updates and removals on ONE page. Checks the final graph, so a coordinated move or edge replacement needs no intermediate saves. Read IDs and revision with mmap_read first. Cross-page deletion is excluded.",
24302
+ inputSchema: closed({
24303
+ page: page(),
24304
+ expectedRevision: expectedRevision(),
24305
+ operations: external_exports.array(external_exports.discriminatedUnion("op", [
24306
+ closed({ op: external_exports.literal("declare"), data: declareTool().inputSchema.omit({ page: true, expectedRevision: true }) }),
24307
+ closed({ op: external_exports.literal("update"), data: updateTool().inputSchema.omit({ page: true, expectedRevision: true }) }),
24308
+ closed({ op: external_exports.literal("remove"), data: removeTool().inputSchema.omit({ page: true, expectedRevision: true, pages: true, deletePage: true, references: true }) })
24309
+ ])).min(1).max(100)
24310
+ })
24311
+ };
24312
+ }
24313
+ function viewTool() {
24314
+ return {
24315
+ title: "View the current map",
24316
+ description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation. Every response ends with a `pages:` line naming the pages this project actually has and which one you are looking at, so this is also how you discover whether a map exists at all and under which slugs \u2014 never probe the files.",
24317
+ inputSchema: closed({
24318
+ page: page(),
24319
+ zoom: external_exports.number().int().min(ZOOM_MIN).max(ZOOM_MAX).optional().describe("zoom ladder: 1 = detail (notes unfold), 0 = standard (default), -1..-3 = scaled down, -4 = overview glyphs")
24320
+ })
24321
+ };
24322
+ }
24323
+ function openTool() {
24324
+ return {
24325
+ title: "Open the map pane",
24326
+ description: 'For automatic display beside the current ChatGPT desktop conversation in Codex mode, use surface: "web-terminal", then call open_in_codex with the returned browser hostOpen object. No paste or Computer Use is needed. For the native host terminal, use surface: "codex-terminal": prepare absolute watcher commands for the current project, then ask the host to open its right terminal. This does not launch the watcher or type into that terminal. Agent exec PTYs cannot be attached using their numeric session ids. For a document panel, use surface: "markdown": generate MD + SVG files, enable automatic preview updates after successful map writes, then use the HOST file-opening tool to display the returned absolute Markdown path on the right of the current conversation. Generated does not mean visible: this server cannot open or observe the desktop side panel. For interactive maps, choose surface: "web": start or reuse a project-local web viewer and pass the returned URL to the host browser-opening tool. Markdown and terminal remain available. The default surface is "terminal", preserving the terminal workflow. Put the live map on the user\'s screen: a terminal pane beside this conversation that redraws on every write. Call it whenever a result says `pane: CLOSED` \u2014 and do NOT ask permission first, because a user who has set a mapping policy has already said they want to see the map. With a pane already open this RETARGETS it to `page` instead of opening a second one, so it is also how you show the user a particular page when they ask for one. It never closes a pane: taking the map off the screen belongs to the user (the `q` key in the pane, or typing `mmap` in a terminal). The reply says whether a pane actually reported itself in afterwards, not merely that a command was run. Automatic terminal opening supports Windows Terminal and tmux on Linux/macOS. If opening fails, relay the reason and copyable command; retry only after the environment changes or the user asks.',
24327
+ inputSchema: closed({
24328
+ surface: external_exports.enum(["terminal", "codex-terminal", "markdown", "web", "web-terminal"]).optional().describe("web-terminal = automatically started mmap terminal in a local browser page; codex-terminal = prepare a command for the desktop host terminal; web = local browser viewer; markdown = MD/SVG; terminal = Windows Terminal or tmux launcher (default)"),
24329
+ page: id(
24330
+ "page to show first \u2014 the page THIS effort lives on, the same slug you pass to the other tools. Omit only for the default page: without it a fresh pane opens on whichever page was written last, which after a gap is rarely the one under discussion."
24331
+ ).optional(),
24332
+ window: external_exports.boolean().optional().describe(
24333
+ `open the map in its own "mellos-mapping" window (a new tmux window on Linux/macOS) instead of splitting this conversation's window. Pass it only when the user asked for the map separate (a second monitor, a small screen); the split is the default because the map is meant to sit beside what it describes.`
24334
+ )
24335
+ })
24336
+ };
24337
+ }
24338
+
24339
+ // src/server/read.ts
24340
+ import { createHash as createHash4 } from "node:crypto";
24341
+ import { readFileSync as readFileSync7, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
24342
+ import { dirname as dirname11, isAbsolute, relative, resolve as resolve3 } from "node:path";
24343
+ var hash = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
24344
+ function requireMap(file) {
24345
+ const result = loadMapFile(file);
24346
+ if (!result.ok) throw new LedgerError(result.error.kind === "not-found" ? "NOT_FOUND" : "INVALID_STORE", describeStoreError(result.error));
24347
+ return result.value;
24348
+ }
24349
+ function summary(map, page2) {
24350
+ return { id: page2 ?? "_default", title: map.title ?? page2 ?? "Default map", kind: map.kind ?? "dev", context: map.context ?? null, revision: revisionOf(map), counts: { nodes: map.nodes.length, edges: map.edges.length, layers: map.layers.length, groups: map.groups.length, lanes: map.lanes.length, ...Object.fromEntries(["planned", "in-progress", "done", "regressed"].map((s) => [s, map.nodes.filter((n) => n.status === s).length])) } };
24351
+ }
24352
+ function related(map, seeds, direction, depth) {
24353
+ const adjacency = /* @__PURE__ */ new Map();
24354
+ const add = (from, to) => {
24355
+ const row = adjacency.get(from) ?? [];
24356
+ row.push(to);
24357
+ adjacency.set(from, row);
24358
+ };
24359
+ for (const edge of map.edges) {
24360
+ if (direction !== "consumers") add(edge.from, edge.to);
24361
+ if (direction !== "dependencies") add(edge.to, edge.from);
24362
+ }
24363
+ const found = new Set(seeds);
24364
+ let frontier = seeds;
24365
+ for (let step = 0; step < depth; step++) {
24366
+ const next = /* @__PURE__ */ new Set();
24367
+ for (const id2 of frontier) for (const neighbor of adjacency.get(id2) ?? []) if (!found.has(neighbor)) next.add(neighbor);
24368
+ for (const id2 of next) found.add(id2);
24369
+ frontier = [...next];
24370
+ if (!frontier.length) break;
24371
+ }
24372
+ return found;
24373
+ }
24374
+ function changes(node, project, map) {
24375
+ const sources2 = (node.sources ?? []).map((source) => {
24376
+ try {
24377
+ const root = realpathSync3(project), target = realpathSync3(resolve3(root, source.path));
24378
+ const rel = relative(root, target);
24379
+ if (isAbsolute(rel) || rel === ".." || rel.startsWith("..\\") || rel.startsWith("../")) return { ...source, state: "outside-project" };
24380
+ const stat = statSync3(target);
24381
+ if (!stat.isFile() || stat.size > 8 * 1024 * 1024) return { ...source, state: "unreadable" };
24382
+ const currentSha256 = createHash4("sha256").update(readFileSync7(target)).digest("hex");
24383
+ return { ...source, currentSha256, state: source.sha256 === void 0 ? "unverified" : source.sha256 === currentSha256 ? "unchanged" : "changed" };
24384
+ } catch (e) {
24385
+ return { ...source, state: e.code === "ENOENT" ? "missing" : "unreadable" };
24386
+ }
24387
+ });
24388
+ const changed = sources2.some((s) => s.state === "changed" || s.state === "missing");
24389
+ const affected = changed ? [...related(map, [node.id], "consumers", map.nodes.length)].filter((id2) => id2 !== node.id) : [];
24390
+ return { id: node.id, label: node.label, state: changed ? "changed" : sources2.length && sources2.every((s) => s.state === "unchanged") ? "unchanged" : "unknown", sources: sources2, affectedConsumers: affected.slice(0, 100), affectedConsumersTruncated: affected.length > 100 };
24391
+ }
24392
+ function readMaps(stateFile, input) {
24393
+ const resource = input.resource ?? "pages";
24394
+ const project = dirname11(dirname11(stateFile));
24395
+ let records;
24396
+ let revision;
24397
+ let map;
24398
+ if (resource === "pages") {
24399
+ records = listPageFiles(stateFile).map((file) => {
24400
+ const page2 = pageIdOfFile(stateFile, file);
24401
+ try {
24402
+ return summary(requireMap(file), page2);
24403
+ } catch (error2) {
24404
+ return { id: page2 ?? "_default", error: error2.message };
24405
+ }
24406
+ });
24407
+ revision = hash(records);
24408
+ } else {
24409
+ map = requireMap(pageFilePath(stateFile, input.page));
24410
+ revision = revisionOf(map);
24411
+ if (resource === "map") records = [summary(map, input.page)];
24412
+ else if (resource === "neighborhood") {
24413
+ const seeds = input.id !== void 0 ? [input.id] : input.ids ?? [];
24414
+ if (!seeds.length) throw new LedgerError("INVALID_QUERY", "neighborhood requires id or ids");
24415
+ const missing = seeds.filter((id2) => !map.nodes.some((n) => n.id === id2));
24416
+ if (missing.length) throw new LedgerError("NOT_FOUND", "Unknown neighborhood roots", { ids: missing });
24417
+ const selected = related(map, seeds, input.direction ?? "both", input.depth ?? 1);
24418
+ records = map.nodes.filter((n) => selected.has(n.id)).map((n) => ({ ...n }));
24419
+ } else if (resource === "changes") records = map.nodes.map((n) => ({ ...n }));
24420
+ else records = map[resource].map((row) => ({ ...row, ...resource === "edges" ? { id: `${"from" in row ? row.from : ""}->${"to" in row ? row.to : ""}` } : {} }));
24421
+ }
24422
+ if (resource !== "neighborhood") {
24423
+ if (input.id !== void 0) {
24424
+ records = records.filter((r) => r.id === input.id);
24425
+ if (!records.length) throw new LedgerError("NOT_FOUND", `No ${resource} record with ID ${input.id}`);
24426
+ }
24427
+ if (input.ids !== void 0) records = records.filter((r) => input.ids.includes(String(r.id)));
24428
+ }
24429
+ for (const field of ["status", "layer", "group", "lane"]) if (input[field] !== void 0) records = records.filter((r) => r[field] === input[field]);
24430
+ if (input.query) {
24431
+ const query2 = input.query.toLowerCase();
24432
+ records = records.filter((r) => JSON.stringify(r).toLowerCase().includes(query2));
24433
+ }
24434
+ const { cursor: _cursor, ifRevision: _ifRevision, ...query } = input;
24435
+ const queryHash = hash(query);
24436
+ let offset = 0;
24437
+ if (input.cursor !== void 0) {
24438
+ let cursor;
24439
+ try {
24440
+ cursor = JSON.parse(Buffer.from(input.cursor, "base64url").toString("utf8"));
24441
+ } catch {
24442
+ throw new LedgerError("INVALID_CURSOR", "Malformed cursor");
24443
+ }
24444
+ if (!cursor || cursor.query !== queryHash || !Number.isSafeInteger(cursor.offset) || cursor.offset < 0) throw new LedgerError("INVALID_CURSOR", "Cursor does not belong to this query");
24445
+ if (cursor.revision !== revision) throw new LedgerError("CONFLICT", "Map changed during pagination; restart this query.", { actualRevision: revision });
24446
+ offset = cursor.offset;
24447
+ }
24448
+ if (input.ifRevision === revision && resource !== "changes") return { resource, project, page: input.page ?? null, revision, notModified: true };
24449
+ const limit = input.limit ?? 30, total = records.length;
24450
+ records = records.slice(offset, offset + limit);
24451
+ if (resource === "changes") records = records.map((r) => changes(r, project, map));
24452
+ records = records.map((r) => {
24453
+ const fields = input.fields ?? (resource === "nodes" || resource === "neighborhood" ? ["id", "label", "layer", "status", "group", "lane", "kind", "submap"] : Object.keys(r));
24454
+ return Object.fromEntries([.../* @__PURE__ */ new Set(["id", ..."from" in r ? ["from", "to"] : [], ...fields])].filter((f) => f in r).map((f) => [f, r[f]]));
24455
+ });
24456
+ return { resource, project, page: input.page ?? null, revision, total, items: records, nextCursor: offset + limit < total ? Buffer.from(JSON.stringify({ revision, query: queryHash, offset: offset + limit })).toString("base64url") : null };
24457
+ }
24458
+
24459
+ // src/server/presence.ts
23197
24460
  var DEFAULT_PAGE_NAME = "(default)";
23198
24461
  var DEFAULT_PAGE_ABSENT = "(default: absent)";
23199
24462
  function pagesLine(stateFile, shown) {
@@ -23206,26 +24469,51 @@ function pagesLine(stateFile, shown) {
23206
24469
  function pageName(page2) {
23207
24470
  return page2 ?? DEFAULT_PAGE_NAME;
23208
24471
  }
23209
- function paneLine(stateFile, touched) {
24472
+ function paneLine(stateFile, touched, openFailure) {
23210
24473
  const viewers = readLiveViewers(stateFile, Date.now());
23211
24474
  if (viewers.length === 0) {
24475
+ if (openFailure !== void 0) return `pane: CLOSED \u2014 automatic opening previously failed. Do not retry mmap_open until the terminal environment changes or the user asks to retry. The map is saved; mmap_view remains available inline. Last failure: ${openFailure}`;
23212
24476
  return `pane: CLOSED \u2014 nobody is seeing this map. Open it with mmap_open {page: ${touched === void 0 ? "(omit for the default page)" : `"${touched}"`}} and do not ask first: a user with a mapping policy has already said they want the picture.`;
23213
24477
  }
23214
- if (viewers.some((v) => v.page === touched)) return "pane: open on this page \u2014 the user is seeing this.";
24478
+ if (viewers.some((v) => v.page === touched)) return "pane: running on this page \u2014 a heartbeat confirms the process, not terminal visibility.";
23215
24479
  const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
23216
24480
  if (viewers.some((v) => v.follow)) {
23217
24481
  return `pane: open on ${elsewhere}, auto-follow on \u2014 it lands on this page within a second.`;
23218
24482
  }
23219
24483
  return `pane: open on ${elsewhere}, auto-follow OFF \u2014 the user pinned that page, so this change is NOT on their screen. Tell them rather than switching it behind them; mmap_open {page} retargets the pane if they want it moved.`;
23220
24484
  }
23221
- var LAUNCH_TIMEOUT_MS = 45e3;
24485
+
24486
+ // src/server/map-service.ts
24487
+ import { existsSync as existsSync6 } from "node:fs";
24488
+ function loadOrEmpty(file) {
24489
+ const loaded = loadMapFile(file);
24490
+ if (loaded.ok) return loaded;
24491
+ return loaded.error.kind === "not-found" ? { ok: true, value: EMPTY_MAP } : { ok: false, error: describeStoreError(loaded.error) };
24492
+ }
24493
+ function mutateMap(file, apply, expectedRevision2) {
24494
+ const current = loadOrEmpty(file);
24495
+ if (!current.ok) return { ok: false, error: { kind: "load", detail: current.error } };
24496
+ assertRevision(existsSync6(file) ? revisionOf(current.value) : "absent", expectedRevision2);
24497
+ const applied = apply(current.value);
24498
+ if (!applied.ok) return { ok: false, error: { kind: "refused", detail: applied.error } };
24499
+ const saved = saveMapFile(file, applied.value);
24500
+ return saved.ok ? applied : { ok: false, error: { kind: "save", error: saved.error } };
24501
+ }
24502
+
24503
+ // src/server/pane-launcher.ts
24504
+ import { spawn as spawn2 } from "node:child_process";
24505
+ import { existsSync as existsSync7 } from "node:fs";
24506
+ import { dirname as dirname12, join as join9 } from "node:path";
24507
+ import { fileURLToPath } from "node:url";
24508
+ var pageName2 = (page2) => page2 ?? "(default)";
24509
+ var LAUNCH_TIMEOUT_MS = 6e4;
23222
24510
  var PANE_REPORT_TIMEOUT_MS = 8e3;
23223
24511
  var PANE_REPORT_POLL_MS = 250;
23224
24512
  function launcherPath(moduleUrl) {
23225
- return join2(dirname2(dirname2(fileURLToPath(moduleUrl))), "scripts", "open-pane.mjs");
24513
+ return join9(dirname12(dirname12(fileURLToPath(moduleUrl))), "scripts", "open-pane.mjs");
23226
24514
  }
23227
24515
  function projectDirOf(stateFile) {
23228
- return dirname2(dirname2(stateFile));
24516
+ return dirname12(dirname12(stateFile));
23229
24517
  }
23230
24518
  function launcherArgs(projectDir, page2, window) {
23231
24519
  const args = [projectDir];
@@ -23234,8 +24522,8 @@ function launcherArgs(projectDir, page2, window) {
23234
24522
  return args;
23235
24523
  }
23236
24524
  function runLauncher(script, args) {
23237
- return new Promise((resolve) => {
23238
- const child = spawn(process.execPath, [script, ...args], {
24525
+ return new Promise((resolve4) => {
24526
+ const child = spawn2(process.execPath, [script, ...args], {
23239
24527
  windowsHide: true,
23240
24528
  stdio: ["ignore", "pipe", "pipe"]
23241
24529
  });
@@ -23248,70 +24536,108 @@ function runLauncher(script, args) {
23248
24536
  const abandon = setTimeout(() => child.kill(), LAUNCH_TIMEOUT_MS);
23249
24537
  child.on("error", (e) => {
23250
24538
  clearTimeout(abandon);
23251
- resolve({ ok: false, output: e.message });
24539
+ resolve4({ ok: false, output: e.message });
23252
24540
  });
23253
24541
  child.on("close", (code) => {
23254
24542
  clearTimeout(abandon);
23255
- resolve({ ok: code === 0, output: output.trim() });
24543
+ resolve4({ ok: code === 0, output: output.trim() });
23256
24544
  });
23257
24545
  });
23258
24546
  }
24547
+ function launchPane(args) {
24548
+ const script = launcherPath(import.meta.url);
24549
+ if (!existsSync7(script)) return Promise.resolve({
24550
+ ok: false,
24551
+ output: `the launcher is missing at ${script}. This install is incomplete \u2014 reinstall the plugin (a source checkout needs "npm run build").`
24552
+ });
24553
+ return runLauncher(script, args);
24554
+ }
23259
24555
  function paneShows(viewers, page2) {
23260
24556
  return page2 === void 0 ? viewers.length > 0 : viewers.some((v) => v.page === page2);
23261
24557
  }
23262
- async function awaitPane(stateFile, page2, deadlineMs) {
24558
+ async function awaitPane(stateFile, page2, deadlineMs, pid) {
23263
24559
  for (; ; ) {
23264
- const viewers = readLiveViewers(stateFile, Date.now());
24560
+ const viewers = readLiveViewers(stateFile, Date.now()).filter((viewer) => pid === void 0 || viewer.pid === pid);
23265
24561
  if (paneShows(viewers, page2)) return viewers;
23266
24562
  if (Date.now() >= deadlineMs) return viewers;
23267
24563
  await new Promise((r) => setTimeout(r, PANE_REPORT_POLL_MS));
23268
24564
  }
23269
24565
  }
23270
24566
  function openOutcome(run, viewers, page2) {
24567
+ const pid = launcherViewerPid(run);
24568
+ viewers = viewers.filter((viewer) => pid === void 0 || viewer.pid === pid);
23271
24569
  if (!run.ok) {
23272
24570
  return `could not open the pane: ${run.output === "" ? "the launcher failed without saying why" : run.output}
23273
- Relay this to the user \u2014 on a machine without Windows Terminal the map is opened by running the watcher in any second terminal or tmux split (see the plugin README).`;
24571
+ Relay the launcher reason and any copyable fallback command. Do not retry mmap_open until the terminal environment changes or the user asks to retry. A failed default split is not permission to open a separate window.`;
23274
24572
  }
23275
24573
  if (paneShows(viewers, page2)) {
23276
- return `pane: open and showing ${pageName(page2)} \u2014 the user can see the map now.
24574
+ const visible = /^MMAP_PANE [^\r\n]*\bvisibility=visible(?:\s|$)/m.test(run.output);
24575
+ return `pane: running and reporting ${pageName2(page2)} \u2014 ${visible ? "the launcher verified its tmux window is active and its pane is visible in the attached session." : "terminal visibility is not confirmed by the process heartbeat."}
23277
24576
  ${run.output}`;
23278
24577
  }
23279
24578
  if (viewers.length > 0) {
23280
- const elsewhere = [...new Set(viewers.map((v) => pageName(v.page)))].join(", ");
23281
- return `pane: open, but it reports ${elsewhere} rather than ${pageName(page2)}. With auto-follow on it lands there on your next write; with follow off the user is holding that page on purpose.
24579
+ const elsewhere = [...new Set(viewers.map((v) => pageName2(v.page)))].join(", ");
24580
+ return `pane: open, but it reports ${elsewhere} rather than ${pageName2(page2)}. With auto-follow on it lands there on your next write; with follow off the user is holding that page on purpose.
23282
24581
  ` + run.output;
23283
24582
  }
23284
24583
  return `the launcher succeeded but no pane has reported in within ${PANE_REPORT_TIMEOUT_MS / 1e3}s. It may still be starting; the \`pane:\` line on your next write says whether it made it.
23285
24584
  ` + run.output;
23286
24585
  }
24586
+ function launcherViewerPid(run) {
24587
+ const raw = /^MMAP_PANE [^\r\n]*\bpid=([1-9]\d*)(?:\s|$)/m.exec(run.output)?.[1];
24588
+ return raw === void 0 ? void 0 : Number(raw);
24589
+ }
24590
+
24591
+ // src/server/server.ts
24592
+ var SERVER_NAME = "mellos-mapping";
24593
+ var SERVER_VERSION = "0.23.0";
23287
24594
  function text(s, isError = false) {
23288
24595
  return { content: [{ type: "text", text: s }], ...isError ? { isError: true } : {} };
23289
24596
  }
24597
+ function structured(value) {
24598
+ return { content: [{ type: "text", text: JSON.stringify(value) }], structuredContent: value };
24599
+ }
24600
+ function guard(action) {
24601
+ try {
24602
+ return action();
24603
+ } catch (error2) {
24604
+ const failure = error2 instanceof LedgerError ? error2 : new LedgerError("IO_ERROR", String(error2));
24605
+ return { ...structured({ error: { code: failure.code, message: failure.message, ...failure.details } }), isError: true };
24606
+ }
24607
+ }
23290
24608
  function saveFailed(error2) {
23291
24609
  return text(`save failed, nothing changed (retry): ${describeStoreError(error2)}`, true);
23292
24610
  }
23293
- function loadOrEmpty(stateFile) {
23294
- const loaded = loadMapFile(stateFile);
23295
- if (loaded.ok) return { ok: true, value: loaded.value };
23296
- if (loaded.error.kind === "not-found") return { ok: true, value: EMPTY_MAP };
23297
- return { ok: false, error: describeStoreError(loaded.error) };
23298
- }
23299
- function buildServer(stateFile, userConfigFile) {
23300
- const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
24611
+ function buildServer(stateFile, userConfigFile, launch = launchPane) {
24612
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { instructions: "Maps persist across conversations. Before mapping or resuming work, use mmap_read to discover pages, then read the relevant records and revision. A new conversation is not a new effort. Reuse verified nodes; submit only necessary changes with expectedRevision. mmap_view is a picture, not the editable data. Read mmap_setup for the user mapping policy." });
24613
+ let paneOpenFailure;
24614
+ const currentPaneLine = (page2) => paneLine(stateFile, page2, paneOpenFailure);
23301
24615
  const projectConfigFile = configFilePath(stateFile);
24616
+ const previews = createPreviewPublisher(stateFile);
24617
+ const refreshPreview = (page2) => {
24618
+ if (!previews.enabled()) return "";
24619
+ const published = previews.refresh(page2);
24620
+ return published.ok ? `
24621
+ preview: updated
24622
+ markdown: ${published.value.path}
24623
+ File generated; desktop visibility is not tracked.` : `
24624
+ preview: STALE \u2014 map changes were saved, but preview generation failed: ${published.error}. Retry mmap_open {surface: "markdown"}; do not repeat the map mutation.`;
24625
+ };
23302
24626
  const fileOf = (page2) => pageFilePath(stateFile, page2);
23303
- const mutate = (page2, apply) => {
23304
- const file = fileOf(page2);
23305
- const current = loadOrEmpty(file);
23306
- if (!current.ok) return text(current.error, true);
23307
- const applied = apply(current.value);
23308
- if (!applied.ok) return text(`refused (nothing changed): ${applied.error}`, true);
23309
- const saved = saveMapFile(file, applied.value);
23310
- if (!saved.ok) return saveFailed(saved.error);
23311
- return text(summarize(applied.value) + (page2 !== void 0 ? ` [page: ${page2}]` : ""));
24627
+ const mutateUnlocked = (page2, apply, expectedRevision2) => {
24628
+ const result = mutateMap(fileOf(page2), apply, expectedRevision2);
24629
+ if (!result.ok) {
24630
+ const failure = result.error;
24631
+ if (failure.kind === "save") return { ...saveFailed(failure.error), structuredContent: { error: { code: "SAVE_FAILED", message: describeStoreError(failure.error) } } };
24632
+ return { ...text(failure.kind === "refused" ? `refused (nothing changed): ${failure.detail}` : failure.detail, true), structuredContent: { error: { code: failure.kind === "refused" ? "REFUSED" : "INVALID_STORE", message: failure.detail } } };
24633
+ }
24634
+ const revision = revisionOf(result.value);
24635
+ return { ...text(summarize(result.value) + (page2 !== void 0 ? ` [page: ${page2}]` : "") + `
24636
+ revision: ${revision}` + refreshPreview(page2)), structuredContent: { page: page2 ?? null, revision, counts: { nodes: result.value.nodes.length, edges: result.value.edges.length } } };
23312
24637
  };
23313
- const withPane = (result, page2) => result.isError === true ? result : text(`${result.content[0]?.text ?? ""}
23314
- ${paneLine(stateFile, page2)}`);
24638
+ const mutate = (page2, apply, expectedRevision2) => guard(() => withStoreLock(stateFile, () => mutateUnlocked(page2, apply, expectedRevision2)));
24639
+ const withPane = (result, page2) => result.isError === true || previews.enabled() ? result : { ...result, ...text(`${result.content[0]?.text ?? ""}
24640
+ ${existsSync8(webRuntimeFile(stateFile)) ? 'web: configured \u2014 the browser reads project map updates. Use mmap_open {surface: "web", page} to open or reconnect; desktop visibility is not tracked.' : currentPaneLine(page2)}`) };
23315
24641
  const knownPages = () => listPageFiles(stateFile).map((f) => pageIdOfFile(stateFile, f)).filter((p) => p !== void 0);
23316
24642
  const refusePageDeletion = (pages, target) => {
23317
24643
  if (target !== void 0 && pages.includes(target)) {
@@ -23330,7 +24656,7 @@ ${paneLine(stateFile, page2)}`);
23330
24656
  }
23331
24657
  return void 0;
23332
24658
  };
23333
- const deletePages = (pages, summary) => {
24659
+ const deletePages = (pages, summary2) => {
23334
24660
  const deleted = [];
23335
24661
  const failed = [];
23336
24662
  for (const p of pages) {
@@ -23338,10 +24664,10 @@ ${paneLine(stateFile, page2)}`);
23338
24664
  if (removed.ok) deleted.push(p);
23339
24665
  else failed.push(`${p} (${describeStoreError(removed.error)})`);
23340
24666
  }
23341
- const gone = `deleted page(s): ${deleted.length > 0 ? deleted.join(", ") : "(none)"}`;
23342
- if (failed.length === 0) return text(`${summary}${gone}`);
24667
+ const gone = `deleted page(s): ${deleted.length > 0 ? deleted.join(", ") : "(none)"}` + (deleted.length ? refreshPreview(void 0) : "");
24668
+ if (failed.length === 0) return text(`${summary2}${gone}`);
23343
24669
  return text(
23344
- `${summary}${gone}; could NOT delete: ${failed.join("; ")}. Deleting files is not a transaction: what is named deleted above is gone for good, and only the failures are worth retrying.`,
24670
+ `${summary2}${gone}; could NOT delete: ${failed.join("; ")}. Deleting files is not a transaction: what is named deleted above is gone for good, and only the failures are worth retrying.`,
23345
24671
  true
23346
24672
  );
23347
24673
  };
@@ -23354,155 +24680,62 @@ note: ${describeStoreError(scopes.error)} \u2014 fix it or rerun setup (mmap_set
23354
24680
  };
23355
24681
  server.registerTool(
23356
24682
  "mmap_declare",
23357
- {
23358
- title: "Declare map structure",
23359
- description: "Grow the Mellos map: set the title and diagram kind, add layer bands, lanes and groups (labeled subsystems within ONE band \u2014 declare them when a single band grows crowded, roughly five or more nodes in that band; a group must be a strict subset of its band, and a map spread thin across many bands needs none), add nodes, add dependency edges. Declare the whole ghost design up front, then grow it as understanding deepens. Edges must point strictly downward (a node may only use nodes on lower layers); the batch is all-or-nothing. The title lives here and only here: pass it again to replace it, or null to remove it. Revising what already exists (moving, renaming, relabeling, clearing) is mmap_update.",
23360
- inputSchema: closed({
23361
- page: page(),
23362
- title: line(TITLE_MAX, "map title, e.g. the feature being built; null removes it").nullable().optional(),
23363
- kind: mapKind().optional(),
23364
- lanes: external_exports.array(
23365
- closed({
23366
- id: id("stable kebab-case identifier of the lane"),
23367
- label: line(LABEL_MAX, "column name, e.g. a sequence participant")
23368
- })
23369
- ).optional().describe("vertical columns crossing all bands; declaration order = left-to-right"),
23370
- layers: external_exports.array(
23371
- closed({
23372
- id: id("stable kebab-case identifier of the band"),
23373
- name: line(LABEL_MAX, "display name of the band"),
23374
- rank: rank()
23375
- })
23376
- ).optional(),
23377
- groups: external_exports.array(
23378
- closed({
23379
- id: id("stable kebab-case identifier of the group"),
23380
- label: line(LABEL_MAX, "subsystem name shown at the far zoom"),
23381
- layer: id("band this group clusters; members must live on the same band")
23382
- })
23383
- ).optional(),
23384
- nodes: external_exports.array(
23385
- closed({
23386
- id: id("stable kebab-case identifier of the node"),
23387
- label: line(LABEL_MAX, "display label inside the box"),
23388
- layer: id("id of the band this node lives in"),
23389
- status: status("defaults to planned").optional(),
23390
- evidence: line(
23391
- EVIDENCE_MAX,
23392
- "how an already-verified node was verified; for regressed: what broke. Declaring a node straight to done needs it as much as updating one does."
23393
- ).optional(),
23394
- detail: note(
23395
- DETAIL_MAX,
23396
- "design notes shown in the pane detail panel: responsibility, contract, key decisions"
23397
- ).optional(),
23398
- group: id("same-band group this node belongs to").optional(),
23399
- kind: nodeKind().optional(),
23400
- lane: id("lane (column) this node belongs to").optional(),
23401
- submap: id(
23402
- "page slug of this node's child map \u2014 the pane badges the node \u229E and double-click dives in. Declare the child page separately. Create a sub-map only when the node's internals genuinely deserve their own picture; most nodes need none."
23403
- ).optional()
23404
- })
23405
- ).optional(),
23406
- edges: external_exports.array(
23407
- closed({
23408
- ...edgeEnds(),
23409
- label: line(EDGE_LABEL_MAX, "what flows along the edge").optional()
23410
- })
23411
- ).optional()
23412
- })
23413
- },
24683
+ declareTool(),
23414
24684
  (input) => {
23415
- const result = withPane(mutate(input.page, (map) => applyDeclare(map, input)), input.page);
24685
+ const result = withPane(mutate(input.page, (map) => applyDeclare(map, input), input.expectedRevision), input.page);
23416
24686
  if (result.isError === true) return result;
23417
24687
  const nudge = setupNudge();
23418
- return nudge === "" ? result : text((result.content[0]?.text ?? "") + nudge);
24688
+ return nudge === "" ? result : { ...result, ...text((result.content[0]?.text ?? "") + nudge) };
23419
24689
  }
23420
24690
  );
23421
24691
  server.registerTool(
23422
24692
  "mmap_update",
23423
- {
23424
- title: "Record progress and revise the map",
23425
- description: "The revision tool, all-or-nothing. Record progress on nodes: in-progress when starting a node (the pane spins), done with evidence when its verification passes, regressed with evidence when a done node breaks. Revise what the ghost design got wrong: move a node to another band, join or leave a group or lane, rename a band (or re-rank it, which reorders the whole map), relabel a group or a lane. Every clearable field takes null to empty it \u2014 that is how a field is cleared, never an empty string. Bands, groups and lanes are applied before the node updates, and within one node update `layer` moves the node before its other fields. The map is a ledger: report honestly, it never blocks you.",
23426
- inputSchema: closed({
23427
- page: page(),
23428
- updates: external_exports.array(
23429
- closed({
23430
- id: id("id of the node to update"),
23431
- status: status("the status to record").optional(),
23432
- label: line(LABEL_MAX, "new display label inside the box").optional(),
23433
- evidence: line(EVIDENCE_MAX, "for done: how it was verified; for regressed: what broke; null clears it").nullable().optional(),
23434
- detail: note(
23435
- DETAIL_MAX,
23436
- "design notes shown in the pane detail panel: responsibility, contract, key decisions; null clears them"
23437
- ).nullable().optional(),
23438
- layer: id(
23439
- "move the node to this band; applied before this item's other fields, so a node can move and join a group on the new band in one item. Every edge touching it must still point strictly downward, and a grouped node may only move to its group's band."
23440
- ).optional(),
23441
- group: id("join this same-band group; null leaves the current group").nullable().optional(),
23442
- kind: nodeKind().nullable().optional(),
23443
- lane: id("join this lane; null leaves the current lane").nullable().optional(),
23444
- submap: id("link a child map page by slug; null unlinks it").nullable().optional()
23445
- })
23446
- ).min(1).optional(),
23447
- layers: external_exports.array(
23448
- closed({
23449
- id: id("id of the band to revise"),
23450
- name: line(LABEL_MAX, "new display name of the band").optional(),
23451
- rank: rank().optional()
23452
- })
23453
- ).min(1).optional().describe("rename and/or re-rank existing bands; an item must carry a name, a rank, or both"),
23454
- groups: external_exports.array(closed({ id: id("id of the group to relabel"), label: line(LABEL_MAX, "new subsystem name") })).min(1).optional().describe("relabel existing groups; membership and band are untouched"),
23455
- lanes: external_exports.array(closed({ id: id("id of the lane to relabel"), label: line(LABEL_MAX, "new column name") })).min(1).optional().describe("relabel existing lanes; order and membership are untouched")
23456
- })
23457
- },
23458
- (input) => withPane(mutate(input.page, (map) => applyUpdate(map, input)), input.page)
24693
+ updateTool(),
24694
+ (input) => withPane(mutate(input.page, (map) => {
24695
+ requireMap(fileOf(input.page));
24696
+ return applyUpdate(map, input);
24697
+ }, input.expectedRevision), input.page)
23459
24698
  );
24699
+ server.registerTool("mmap_read", readTool(), (input) => guard(() => structured(readMaps(stateFile, input))));
24700
+ server.registerTool("mmap_batch", batchTool(), (input) => withPane(mutate(input.page, (map) => {
24701
+ if (!existsSync8(fileOf(input.page)) && !input.operations.some((op) => op.op === "declare")) throw new LedgerError("NOT_FOUND", "Create the page before updating it.");
24702
+ return applyBatch(map, input.operations, input.page);
24703
+ }, input.expectedRevision), input.page));
23460
24704
  server.registerTool(
23461
24705
  "mmap_remove",
23462
- {
23463
- title: "Revise the map",
23464
- description: 'Remove edges, nodes, groups and empty layer bands (in that order, all-or-nothing). Removing a node also removes every edge touching it; removing a group merely ungroups its members. Use when the ghost design turns out wrong \u2014 the map is a hypothesis, revising it is honest work. `pages` is the other scale: it DELETES whole page files, so a finished effort can be cleaned up instead of accumulating tabs forever. A bare `{pages: ["slug"]}` with no other field is the normal form; combined with map edits, the edits are applied first and the pages are deleted after. The deletion is permanent and cannot be undone, so delete only pages whose effort is over \u2014 and only ever with the user behind it. An unknown slug is refused with the project\'s real page list (naming a page that does not exist is a typo, not a request). The default page has no slug and is not deletable here. A node elsewhere still pointing at a deleted page with `submap` stays legal \u2014 a submap reference has no existence invariant \u2014 but it has nowhere to dive until the page comes back.',
23465
- inputSchema: closed({
23466
- page: page(),
23467
- edges: external_exports.array(closed(edgeEnds())).optional(),
23468
- nodes: external_exports.array(id("id of the node to remove, with every edge touching it")).optional(),
23469
- groups: external_exports.array(id("id of the group to remove; members stay, merely ungrouped")).optional(),
23470
- lanes: external_exports.array(id("id of the lane to remove; members stay, merely off-lane")).optional(),
23471
- layers: external_exports.array(id("id of the band to remove; it must hold no nodes and no groups")).optional(),
23472
- pages: external_exports.array(
23473
- id(
23474
- "slug of a page whose WHOLE map file is deleted \u2014 the page and everything drawn on it. Not the page this same call targets with `page`."
23475
- )
23476
- ).optional().describe("pages to delete entirely, after this call's map edits; permanent")
23477
- })
23478
- },
23479
- (input) => {
23480
- if (input.pages === void 0) return withPane(mutate(input.page, (map) => applyRemove(map, input)), input.page);
24706
+ removeTool(),
24707
+ (input) => guard(() => withStoreLock(stateFile, () => {
24708
+ if (input.deletePage) {
24709
+ if (input.pages !== void 0 || [input.nodes, input.edges, input.layers, input.groups, input.lanes].some((items) => items !== void 0)) throw new LedgerError("INVALID_ARGUMENT", "deletePage cannot be combined with other edits.");
24710
+ const map = requireMap(fileOf(input.page));
24711
+ const revision = revisionOf(map);
24712
+ assertRevision(revision, input.expectedRevision);
24713
+ const references = input.page === void 0 ? [] : listPageFiles(stateFile).filter((file) => file !== fileOf(input.page)).flatMap((file) => requireMap(file).nodes.filter((node) => node.submap === input.page).map((node) => ({ page: pageIdOfFile(stateFile, file) ?? null, node: node.id })));
24714
+ if (references.length && input.references !== "keep") throw new LedgerError("REFERENCED", "Page has inbound submap references; unlink them or explicitly choose references: keep.", { references });
24715
+ const deleted = deletePageFile(fileOf(input.page));
24716
+ if (!deleted.ok) throw new LedgerError("DELETE_FAILED", describeStoreError(deleted.error));
24717
+ const preview = refreshPreview(void 0);
24718
+ return structured({ deleted: true, page: input.page ?? null, revision: "absent", previousRevision: revision, references, ...preview ? { preview } : {} });
24719
+ }
24720
+ if (input.references !== void 0) throw new LedgerError("INVALID_ARGUMENT", "references requires deletePage: true");
24721
+ if (input.pages === void 0) return withPane(mutateUnlocked(input.page, (map) => applyRemove(map, input), input.expectedRevision), input.page);
24722
+ if (input.expectedRevision !== void 0) throw new LedgerError("INVALID_ARGUMENT", "Use deletePage for a version-checked page deletion. Legacy pages batches report partial success.");
23481
24723
  const refusal = refusePageDeletion(input.pages, input.page);
23482
24724
  if (refusal !== void 0) return refusal;
23483
24725
  const editsAnything = (input.edges?.length ?? 0) + (input.nodes?.length ?? 0) + (input.groups?.length ?? 0) + (input.lanes?.length ?? 0) + (input.layers?.length ?? 0) > 0;
23484
- let summary = "";
24726
+ let summary2 = "";
23485
24727
  if (editsAnything) {
23486
- const edited = mutate(input.page, (map) => applyRemove(map, input));
24728
+ const edited = mutateUnlocked(input.page, (map) => applyRemove(map, input));
23487
24729
  if (edited.isError === true) return edited;
23488
- summary = `${edited.content[0]?.text ?? ""}
24730
+ summary2 = `${edited.content[0]?.text ?? ""}
23489
24731
  `;
23490
24732
  }
23491
- return withPane(deletePages(input.pages, summary), input.page);
23492
- }
24733
+ return withPane(deletePages(input.pages, summary2), input.page);
24734
+ }))
23493
24735
  );
23494
24736
  server.registerTool(
23495
24737
  "mmap_setup",
23496
- {
23497
- title: "Configure when maps open",
23498
- description: 'Get or set the mapping policy \u2014 WHEN the assistant opens a Mellos map. Call with no arguments to read it: the reply names the policy chosen for the USER (every project), the one this PROJECT overrides it with if any, and which of them is in effect. If it reports "not set", ask the USER to choose (never pick for them): always = ' + describeMappingPolicy("always") + "; complex = " + describeMappingPolicy("complex") + "; on-request = " + describeMappingPolicy("on-request") + '. Then call again with their choice to persist it. It defaults to user scope, which is the normal one \u2014 the question is about how someone works, so it is asked once ever, not once per repository. Pass scope: "project" only when the user wants THIS project to differ from that habit. The policy guides you; it never blocks the tools, and an explicit user request for a map always wins.',
23499
- inputSchema: closed({
23500
- policy: external_exports.enum(MAPPING_POLICIES).optional().describe("the user's choice to persist; omit to read the current policy"),
23501
- scope: external_exports.enum(POLICY_SCOPES).optional().describe(
23502
- 'where to record the choice: "user" (default) applies to every project this user opens; "project" overrides that for this project alone. Ignored when reading.'
23503
- )
23504
- })
23505
- },
24738
+ setupTool(),
23506
24739
  (input) => {
23507
24740
  const scope = input.scope ?? "user";
23508
24741
  const configFile = scope === "project" ? projectConfigFile : userConfigFile;
@@ -23530,63 +24763,73 @@ note: ${describeStoreError(scopes.error)} \u2014 fix it or rerun setup (mmap_set
23530
24763
  );
23531
24764
  server.registerTool(
23532
24765
  "mmap_view",
23533
- {
23534
- title: "View the current map",
23535
- description: "Render the current Mellos map as monochrome text \u2014 the same picture the split-pane watcher shows live. Use it to check the map state or to show it inline in conversation. Every response ends with a `pages:` line naming the pages this project actually has and which one you are looking at, so this is also how you discover whether a map exists at all and under which slugs \u2014 never probe the files.",
23536
- inputSchema: closed({
23537
- page: page(),
23538
- zoom: external_exports.number().int().min(ZOOM_MIN).max(ZOOM_MAX).optional().describe("zoom ladder: 1 = detail (notes unfold), 0 = standard (default), -1..-3 = scaled down, -4 = overview glyphs")
23539
- })
23540
- },
24766
+ viewTool(),
23541
24767
  (input) => {
23542
24768
  const current = loadOrEmpty(fileOf(input.page));
23543
24769
  if (!current.ok) return text(current.error, true);
23544
24770
  const zoom = clampZoom(input.zoom ?? 0);
23545
24771
  const picture = renderMap(current.value, { color: false, unicode: true, spinnerFrame: 0, zoom }).join("\n");
24772
+ const surface = previews.enabled() ? `markdown: ${previewFile(stateFile, input.page)}
24773
+ Use mmap_open {surface: "markdown", page} to regenerate. Desktop visibility is not tracked.` : existsSync8(webRuntimeFile(stateFile)) ? 'web: configured \u2014 use mmap_open {surface: "web", page} to open or reconnect. Desktop visibility is not tracked.' : currentPaneLine(input.page);
23546
24774
  return text(`${picture}
23547
24775
  ${pagesLine(stateFile, input.page)}
23548
- ${paneLine(stateFile, input.page)}`);
24776
+ ${surface}`);
23549
24777
  }
23550
24778
  );
23551
24779
  server.registerTool(
23552
24780
  "mmap_open",
23553
- {
23554
- title: "Open the map pane",
23555
- description: "Put the live map on the user's screen: a terminal pane beside this conversation that redraws on every write. Call it whenever a result says `pane: CLOSED` \u2014 and do NOT ask permission first, because a user who has set a mapping policy has already said they want to see the map. With a pane already open this RETARGETS it to `page` instead of opening a second one, so it is also how you show the user a particular page when they ask for one. It never closes a pane: taking the map off the screen belongs to the user (the `q` key in the pane, or typing `mmap` in a terminal). The reply says whether a pane actually reported itself in afterwards, not merely that a command was run. Windows Terminal is the supported route; anywhere else it says so and you relay the manual command from the README.",
23556
- inputSchema: closed({
23557
- page: id(
23558
- "page to show first \u2014 the page THIS effort lives on, the same slug you pass to the other tools. Omit only for the default page: without it a fresh pane opens on whichever page was written last, which after a gap is rarely the one under discussion."
23559
- ).optional(),
23560
- window: external_exports.boolean().optional().describe(
23561
- `open the map in its own "mellos-mapping" window instead of splitting this conversation's window. Pass it only when the user asked for the map separate (a second monitor, a small screen); the split is the default because the map is meant to sit beside what it describes.`
23562
- )
23563
- })
23564
- },
24781
+ openTool(),
23565
24782
  async (input) => {
23566
- const script = launcherPath(import.meta.url);
23567
- if (!existsSync2(script)) {
23568
- return text(
23569
- `cannot open the pane: the launcher is missing at ${script}. This install is incomplete \u2014 tell the user to reinstall the plugin (a source checkout needs "npm run build").`,
23570
- true
23571
- );
24783
+ if (input.surface === "codex-terminal") {
24784
+ if (input.window === true) return text("codex-terminal uses the current conversation panel; window: true is not supported.", true);
24785
+ if (input.page && !listPageFiles(stateFile).some((file) => pageIdOfFile(stateFile, file) === input.page)) {
24786
+ return text(`Unknown page: ${input.page}. ${pagesLine(stateFile, input.page)}`, true);
24787
+ }
24788
+ const handoff = terminalHandoff(process.execPath, fileURLToPath2(new URL("./watch.mjs", import.meta.url)), stateFile, input.page);
24789
+ return text("terminal: ready-to-start\n" + JSON.stringify(handoff, null, 2) + "\nUse open_in_codex with hostOpen in the CURRENT conversation, without a threadId. If a supported host tool can run commands in that user terminal, use it. Otherwise give the user the command for their shell to paste once. An exec_command session_id belongs to the agent PTY, not this terminal. queued is not visible, and opened is not running. Use read_thread_terminal to confirm the map title and controls after startup. Preserve a page the user pinned.");
24790
+ }
24791
+ if (input.surface === "web" || input.surface === "web-terminal") {
24792
+ if (input.window === true) return text('surface: "web" cannot be combined with window: true. Open the returned URL using the desktop host.', true);
24793
+ try {
24794
+ const url = await openWebPreview(stateFile, fileURLToPath2(new URL("./web.mjs", import.meta.url)), input.page, input.surface === "web-terminal");
24795
+ return text(`surface: ${input.surface}
24796
+ hostOpen: ${JSON.stringify({ placement: "right", target: { type: "browser", url } })}
24797
+ preview: ready
24798
+ web: ${url}
24799
+ Open this URL in the current conversation's right browser panel using the host tool. The viewer refreshes from project maps while open. Existing Markdown previews remain enabled. Desktop visibility is not confirmed by this tool.`);
24800
+ } catch (error2) {
24801
+ return text(`Could not open web preview: ${String(error2)}`, true);
24802
+ }
24803
+ }
24804
+ if (input.surface === "markdown") {
24805
+ if (input.window === true) return text('surface: "markdown" cannot be combined with window: true. Open the returned file using the desktop host.', true);
24806
+ const published = previews.activate(input.page);
24807
+ if (!published.ok) return text(`Could not generate Markdown preview: ${published.error}`, true);
24808
+ return text(`preview: ready
24809
+ markdown: ${published.value.path}
24810
+ index: ${published.value.index}
24811
+ Automatic preview updates are enabled for this project. Open the Markdown file in the current conversation's right file panel using the host tool. No terminal was launched. Visibility and automatic file-viewer refresh are not confirmed by this tool.`);
23572
24812
  }
23573
- const run = await runLauncher(script, launcherArgs(projectDirOf(stateFile), input.page, input.window === true));
23574
- const viewers = run.ok ? await awaitPane(stateFile, input.page, Date.now() + PANE_REPORT_TIMEOUT_MS) : [];
23575
- return text(openOutcome(run, viewers, input.page), !run.ok);
24813
+ const run = await launch(launcherArgs(projectDirOf(stateFile), input.page, input.window === true));
24814
+ const viewers = run.ok ? await awaitPane(stateFile, input.page, Date.now() + PANE_REPORT_TIMEOUT_MS, launcherViewerPid(run)) : [];
24815
+ const outcome = openOutcome(run, viewers, input.page);
24816
+ const failed = !run.ok || !paneShows(viewers, input.page);
24817
+ paneOpenFailure = failed ? outcome : void 0;
24818
+ return text(outcome, failed);
23576
24819
  }
23577
24820
  );
23578
24821
  return server;
23579
24822
  }
23580
24823
  function resolveStateFile(env, cwd) {
23581
- const projectDir = env["MELLOS_MAPPING_CWD"] ?? env["CLAUDE_PROJECT_DIR"] ?? cwd;
23582
- return join2(projectDir, STATE_FILE_RELATIVE_PATH);
24824
+ const projectDir = env["MELLOS_MAPPING_CWD"] ?? env["CLAUDE_PROJECT_DIR"] ?? resolveProjectDirectory(cwd);
24825
+ return join10(projectDir, STATE_FILE_RELATIVE_PATH);
23583
24826
  }
23584
24827
  function resolveUserConfigFile(home) {
23585
24828
  return userConfigFilePath(home);
23586
24829
  }
23587
24830
  async function main() {
23588
24831
  const stateFile = resolveStateFile(process.env, process.cwd());
23589
- const userConfigFile = resolveUserConfigFile(homedir());
24832
+ const userConfigFile = resolveUserConfigFile(homedir2());
23590
24833
  if (migrateLegacyStore(stateFile)) console.error("mellos-mapping: moved the legacy .claude map store to .mellos/ \u2014 commit the move.");
23591
24834
  const server = buildServer(stateFile, userConfigFile);
23592
24835
  await server.connect(new StdioServerTransport());
@@ -23594,7 +24837,7 @@ async function main() {
23594
24837
  function launchedAsEntry(argv1, moduleUrl) {
23595
24838
  if (argv1 === void 0) return false;
23596
24839
  try {
23597
- return realpathSync(argv1) === realpathSync(fileURLToPath(moduleUrl));
24840
+ return realpathSync4(argv1) === realpathSync4(fileURLToPath2(moduleUrl));
23598
24841
  } catch {
23599
24842
  return pathToFileURL(argv1).href === moduleUrl;
23600
24843
  }