rovloom 0.4.0 → 0.6.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 (39) hide show
  1. package/README.md +21 -41
  2. package/dist/src/admin/index.js +1 -1
  3. package/dist/src/{chunk-smpevv7n.js → chunk-0dqx2mmz.js} +9 -16
  4. package/dist/src/{chunk-z1vhdr3c.js → chunk-0qgk7mhz.js} +16 -15
  5. package/dist/src/{chunk-qfyefed3.js → chunk-1bf0wmgk.js} +1 -1
  6. package/dist/src/{chunk-ymsx7hhd.js → chunk-3dvn33c4.js} +61 -62
  7. package/dist/src/chunk-5b8g62kw.js +438 -0
  8. package/dist/src/chunk-5j0gj6w2.js +7 -0
  9. package/dist/src/{chunk-v1httsp6.js → chunk-8ykrqhxh.js} +10 -4
  10. package/dist/src/{chunk-76wgt2g5.js → chunk-9zkq0kc8.js} +14 -8
  11. package/dist/src/{chunk-6hxy1fcg.js → chunk-bspzg8er.js} +2 -2
  12. package/dist/src/{chunk-dtj9y82v.js → chunk-d2kz25f2.js} +269 -39
  13. package/dist/src/{chunk-khmgp03q.js → chunk-ez7d5dac.js} +77 -16
  14. package/dist/src/{chunk-emcefrc1.js → chunk-g27b2gmx.js} +3 -4
  15. package/dist/src/{chunk-keg92d31.js → chunk-gyvqqnjb.js} +34 -59
  16. package/dist/src/{chunk-bjdqj43d.js → chunk-hpzr94hm.js} +23 -6
  17. package/dist/src/{chunk-t3pn1435.js → chunk-jf4r0r44.js} +4 -4
  18. package/dist/src/{chunk-4etdq0cp.js → chunk-jka7v3wb.js} +4 -7
  19. package/dist/src/{chunk-5vx1b9z8.js → chunk-m9c1pnv9.js} +5 -5
  20. package/dist/src/{chunk-7k9g0x5e.js → chunk-mg0fd6ph.js} +1 -1
  21. package/dist/src/{chunk-1fyfrgte.js → chunk-n6wtjyfb.js} +10 -18
  22. package/dist/src/{chunk-nyn75b8v.js → chunk-qjh9x872.js} +30 -1
  23. package/dist/src/{chunk-xmkgq3nh.js → chunk-s3ydr65k.js} +2 -2
  24. package/dist/src/{chunk-4af38z2b.js → chunk-srfbjwtk.js} +4 -3
  25. package/dist/src/{chunk-8x6ygvhh.js → chunk-ssp57p43.js} +27 -31
  26. package/dist/src/{chunk-qab3m6nh.js → chunk-wxqhscsw.js} +43 -7
  27. package/dist/src/{chunk-7q96e161.js → chunk-x0td5wn4.js} +6 -12
  28. package/dist/src/{chunk-ydmabg5t.js → chunk-ytpfmcp1.js} +7 -6
  29. package/dist/src/{chunk-wdr3sdsm.js → chunk-yv9px0f5.js} +60 -32
  30. package/dist/src/{chunk-se5daacy.js → chunk-z7kkfrgz.js} +1 -1
  31. package/dist/src/{chunk-fd9n51mc.js → chunk-zjkjjm81.js} +87 -61
  32. package/dist/src/cli/index.js +27 -19
  33. package/dist/src/cli/main.js +18 -13
  34. package/dist/src/mcp/index.js +6 -5
  35. package/dist/src/runtime/index.js +2 -2
  36. package/dist/src/service/index.js +3 -4
  37. package/dist/src/store.js +1 -1
  38. package/dist/src/webui/index.js +1107 -158
  39. package/package.json +1 -1
@@ -14,6 +14,7 @@ import {
14
14
  parseManagementReadPath
15
15
  } from "./chunk-gav2y5y9.js";
16
16
  import {
17
+ isWildcardHostname,
17
18
  canonicalLoopbackHost
18
19
  } from "./chunk-6n9s7nfe.js";
19
20
 
@@ -37,6 +38,30 @@ var statuses = {
37
38
  invalid_response: 502,
38
39
  timeout: 504
39
40
  };
41
+ function lanAwareOrigin(request, loopback, lan) {
42
+ const raw = request.headers.get("host");
43
+ if (!raw || Buffer.byteLength(raw) > 2048) {
44
+ return null;
45
+ }
46
+ const expected = new URL(loopback);
47
+ const host = raw.toLowerCase();
48
+ if (host === expected.host.toLowerCase()) {
49
+ return expected.origin;
50
+ }
51
+ if (!lan) {
52
+ return null;
53
+ }
54
+ try {
55
+ const url = new URL(request.url);
56
+ const candidate = "http://" + host;
57
+ if (new URL(candidate).origin !== candidate) {
58
+ return null;
59
+ }
60
+ return url.host.toLowerCase() === host && url.protocol === "http:" ? candidate : null;
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
40
65
  function failure(code, status) {
41
66
  return new Response(JSON.stringify({ error: code }), {
42
67
  status,
@@ -77,6 +102,9 @@ function assets(input) {
77
102
  return result;
78
103
  }
79
104
  function allowed(request, expected) {
105
+ if (expected === null) {
106
+ return null;
107
+ }
80
108
  if (new TextEncoder().encode(request.url).byteLength > MANAGEMENT_URL_BYTES) {
81
109
  return null;
82
110
  }
@@ -118,7 +146,7 @@ function context(identity, signal) {
118
146
  async function initialize(implementation, request, body, signal) {
119
147
  return implementation(request, new Response(body.slice().buffer, { headers: { ...headers, "content-type": managementAssetType("/") } }), signal);
120
148
  }
121
- function prepare(options, origin) {
149
+ function prepare(options, origin, lan = () => false) {
122
150
  if (!options || typeof options.authenticate !== "function") {
123
151
  throw new Error("management_authentication_required");
124
152
  }
@@ -174,7 +202,7 @@ function prepare(options, origin) {
174
202
  }
175
203
  let url;
176
204
  try {
177
- url = allowed(request, origin());
205
+ url = allowed(request, lanAwareOrigin(request, origin(), lan()));
178
206
  } catch {
179
207
  url = null;
180
208
  }
@@ -264,24 +292,27 @@ function prepare(options, origin) {
264
292
  }
265
293
  function createManagementReadHandler2(options) {
266
294
  const url = new URL(options.origin);
267
- if (url.origin !== options.origin || url.protocol !== "http:" || url.hostname !== "127.0.0.1" || url.username || url.password) {
295
+ if (url.origin !== options.origin || url.protocol !== "http:" || !["127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password) {
268
296
  throw new Error("management_listener_invalid");
269
297
  }
270
- return prepare(options, () => options.origin);
298
+ return prepare(options, () => options.origin, () => options.lan === true);
271
299
  }
272
300
  function startManagementReadListener2(options) {
273
301
  if (!options) {
274
302
  throw new Error("management_listener_invalid");
275
303
  }
276
304
  let origin = "";
277
- const handler = prepare(options, () => origin);
305
+ let lan = false;
306
+ const handler = prepare(options, () => origin, () => lan);
278
307
  try {
279
308
  return startManagementListener2({
280
309
  port: options.port,
281
310
  hostname: options.hostname,
311
+ lan: options.lan === true,
282
312
  maxRequestBodySize: 1,
283
- handler: (actualOrigin) => {
313
+ handler: (actualOrigin, actualLan) => {
284
314
  origin = actualOrigin;
315
+ lan = actualLan;
285
316
  return handler;
286
317
  }
287
318
  });
@@ -307,7 +338,7 @@ function startManagementListener2(options) {
307
338
  });
308
339
  const url = new URL(`http://${canonicalLoopbackHost(options.hostname)}:${server.port}`).origin;
309
340
  try {
310
- handler = options.handler(url);
341
+ handler = options.handler(url, options.lan === true && isWildcardHostname(options.hostname));
311
342
  if (typeof handler?.fetch !== "function" || typeof handler.stop !== "function") {
312
343
  throw new Error("management_listener_invalid");
313
344
  }
@@ -844,7 +875,7 @@ function createManagementHandler2(options) {
844
875
  throw new Error("management_csrf_required");
845
876
  }
846
877
  const reads = createManagementReadHandler2(options);
847
- const origin = new URL(options.origin);
878
+ const expected = (request) => lanAwareOrigin(request, options.origin, options.lan === true);
848
879
  const pending = new Set;
849
880
  let stopped = false;
850
881
  let tokens = 16;
@@ -900,7 +931,7 @@ function createManagementHandler2(options) {
900
931
  return failure2("invalid_arguments", 400);
901
932
  }
902
933
  const context = { ...identity, wait_signal: controller.signal };
903
- const reply = await options.service.execute(operation, args, context);
934
+ const reply = operation.startsWith("resources/") ? await options.resources.execute(operation.slice("resources/".length), args, identity, controller.signal) : await options.service.execute(operation, args, context);
904
935
  if (controller.signal.aborted) {
905
936
  return failure2("outcome_unconfirmed", 503);
906
937
  }
@@ -921,7 +952,7 @@ function createManagementHandler2(options) {
921
952
  } catch {
922
953
  return failure2("forbidden", 403);
923
954
  }
924
- if (!url.pathname.startsWith("/admin/v1/actions/")) {
955
+ if (!url.pathname.startsWith("/admin/v1/actions/") && !url.pathname.startsWith("/admin/v1/resources/")) {
925
956
  return reads.fetch(request);
926
957
  }
927
958
  let size = 0;
@@ -930,13 +961,22 @@ function createManagementHandler2(options) {
930
961
  size += Buffer.byteLength(name + value);
931
962
  count++;
932
963
  }
933
- if (request.url.length > 2048 || size > 16384 || count > 48 || url.origin !== options.origin || request.headers.get("host") !== origin.host || request.headers.get("origin") !== options.origin || url.username || url.password || url.search || url.hash || request.headers.has("sec-fetch-site") && request.headers.get("sec-fetch-site") !== "same-origin") {
964
+ if (request.url.length > 2048 || size > 16384 || count > 48 || url.origin !== expected(request) || request.headers.get("origin") !== url.origin || url.username || url.password || url.search || url.hash || request.headers.has("sec-fetch-site") && request.headers.get("sec-fetch-site") !== "same-origin") {
934
965
  return failure2("forbidden", 403);
935
966
  }
936
967
  if (request.method !== "POST") {
937
968
  return failure2("method_not_allowed", 405);
938
969
  }
939
- const operation = routes.get(url.pathname);
970
+ const resource = url.pathname.startsWith("/admin/v1/resources/") ? url.pathname.slice("/admin/v1/resources/".length) : null;
971
+ const operation = resource !== null && options.resources && [
972
+ "tokens_list",
973
+ "tokens_create",
974
+ "tokens_delete",
975
+ "tokens_rotate",
976
+ "pairings_list",
977
+ "pairings_delete",
978
+ "devices_delete"
979
+ ].includes(resource) ? `resources/${resource}` : routes.get(url.pathname);
940
980
  if (!operation) {
941
981
  return failure2("not_found", 404);
942
982
  }
@@ -1025,16 +1065,37 @@ function createManagementSessionAdapter2(options) {
1025
1065
  throw new Error("management_cookie_name_invalid");
1026
1066
  }
1027
1067
  const origin = new URL(options.origin);
1028
- if (origin.origin !== options.origin || origin.protocol !== "http:" || origin.hostname !== "127.0.0.1" || origin.username || origin.password) {
1068
+ if (origin.origin !== options.origin || origin.protocol !== "http:" || !["127.0.0.1", "[::1]"].includes(origin.hostname) || origin.username || origin.password) {
1029
1069
  throw new Error("management_listener_invalid");
1030
1070
  }
1031
1071
  const identities = options.identities;
1032
1072
  const sessions = options.sessions;
1033
1073
  const now = options.now ?? Date.now;
1034
1074
  let observed = 0;
1075
+ function expected(request) {
1076
+ const raw = request.headers.get("host");
1077
+ if (!raw || Buffer.byteLength(raw) > 2048) {
1078
+ return null;
1079
+ }
1080
+ const host = raw.toLowerCase();
1081
+ if (host === origin.host.toLowerCase()) {
1082
+ return origin.origin;
1083
+ }
1084
+ if (!options.lan) {
1085
+ return null;
1086
+ }
1087
+ try {
1088
+ const url = new URL(request.url);
1089
+ const candidate = "http://" + host;
1090
+ return new URL(candidate).origin === candidate && url.host.toLowerCase() === host && url.protocol === "http:" ? candidate : null;
1091
+ } catch {
1092
+ return null;
1093
+ }
1094
+ }
1035
1095
  function cookie(request) {
1036
1096
  const url = new URL(request.url);
1037
- if (url.origin !== origin.origin || request.headers.get("host") !== origin.host || request.headers.has("origin") && request.headers.get("origin") !== origin.origin) {
1097
+ const trusted = expected(request);
1098
+ if (trusted === null || url.origin !== trusted || request.headers.has("origin") && request.headers.get("origin") !== trusted) {
1038
1099
  return null;
1039
1100
  }
1040
1101
  const site = request.headers.get("sec-fetch-site");
@@ -1115,7 +1176,7 @@ function createManagementSessionAdapter2(options) {
1115
1176
  if (request.method !== "POST") {
1116
1177
  return false;
1117
1178
  }
1118
- if (request.headers.get("origin") !== origin.origin || request.headers.has("sec-fetch-site") && request.headers.get("sec-fetch-site") !== "same-origin") {
1179
+ if (request.headers.get("origin") !== expected(request) || request.headers.has("sec-fetch-site") && request.headers.get("sec-fetch-site") !== "same-origin") {
1119
1180
  return false;
1120
1181
  }
1121
1182
  const candidate = request.headers.get("x-rovloom-csrf");
@@ -1132,4 +1193,4 @@ function createManagementSessionAdapter2(options) {
1132
1193
  }
1133
1194
  export { createManagementReadHandler2, startManagementReadListener2, startManagementListener2, loadManagementAssets2, ManagementServiceError2, StoreManagementWriteService2, createManagementHandler2, createManagementSessionAdapter2 };
1134
1195
 
1135
- //# debugId=FD715A6C580CBDB864756E2164756E21
1196
+ //# debugId=4BCF3E6652F2586E64756E2164756E21
@@ -1,13 +1,12 @@
1
1
  // @bun
2
2
  import {
3
- SERVICE_HELP2,
4
3
  parseServiceCommand2,
5
4
  handleServiceCommand2
6
- } from "./chunk-7q96e161.js";
5
+ } from "./chunk-x0td5wn4.js";
6
+ import"./chunk-5b8g62kw.js";
7
7
  export {
8
- SERVICE_HELP2 as SERVICE_HELP,
9
8
  handleServiceCommand2 as handleServiceCommand,
10
9
  parseServiceCommand2 as parseServiceCommand
11
10
  };
12
11
 
13
- //# debugId=C3691EFE843F735764756E2164756E21
12
+ //# debugId=2172CFBA81B35CDA64756E2164756E21
@@ -1,13 +1,17 @@
1
1
  // @bun
2
+ import {
3
+ cliMessages2,
4
+ formatMessage2
5
+ } from "./chunk-5b8g62kw.js";
2
6
  import {
3
7
  readRuntimeConfig2
4
- } from "./chunk-6hxy1fcg.js";
8
+ } from "./chunk-bspzg8er.js";
5
9
  import {
6
10
  readOrCreateRoleConfig
7
- } from "./chunk-v1httsp6.js";
11
+ } from "./chunk-8ykrqhxh.js";
8
12
  import {
9
13
  resolveRoleConfigPath2
10
- } from "./chunk-qfyefed3.js";
14
+ } from "./chunk-1bf0wmgk.js";
11
15
  import {
12
16
  CLI_ERROR_CODES,
13
17
  CliError2,
@@ -15,10 +19,10 @@ import {
15
19
  configDocument2,
16
20
  configTemplate2,
17
21
  readConfig2
18
- } from "./chunk-bjdqj43d.js";
22
+ } from "./chunk-hpzr94hm.js";
19
23
  import {
20
24
  PRODUCT_VERSION2
21
- } from "./chunk-xmkgq3nh.js";
25
+ } from "./chunk-s3ydr65k.js";
22
26
  import {
23
27
  isWildcardHostname
24
28
  } from "./chunk-6n9s7nfe.js";
@@ -62,43 +66,9 @@ function serviceDiagnostic(text) {
62
66
  }
63
67
 
64
68
  // src/cli/run.ts
65
- var WILDCARD_LISTEN_WARNING = `wildcard_listener
66
- MCP/management listen on all interfaces over plain HTTP; protect them with a firewall or a TLS reverse proxy.
67
- MCP/\u7BA1\u7406\u9875\u76D1\u542C\u6240\u6709\u7F51\u5361\u4E14\u4E3A\u660E\u6587 HTTP\uFF1B\u8BF7\u7528\u9632\u706B\u5899\u6216 TLS \u53CD\u5411\u4EE3\u7406\u6536\u53E3\u3002
68
- `;
69
- var HELP2 = `Rovloom ${PRODUCT_VERSION2}
70
-
71
- \u7528\u6CD5\uFF1A
72
- rovloom --help
73
- rovloom --version
74
- rovloom config template gateway|connector
75
- rovloom config init connector [--provider luvus|herdr|mixed] [--config /absolute/path/config.json]
76
- rovloom config init gateway [--container] [--config /absolute/path/config.json]
77
- rovloom config check [--role gateway|connector] [--config /absolute/path/config.json]
78
- rovloom config check-runtime [--role gateway|connector] [--config /absolute/path/config.json]
79
- rovloom gateway [--add-device | --repair-device] [--config /absolute/path/config.json] [--non-interactive]
80
- rovloom connector [--repair | --claim] [--config /absolute/path/config.json] [--non-interactive]
81
- rovloom service --help
82
- rovloom token --help
83
- rovloom doctor --help
84
- rovloom storage --help
85
- rovloom admin --help
86
-
87
- gateway / connector \u542F\u52A8\u5BF9\u5E94\u89D2\u8272\u3002\u9996\u6B21\u8FD0\u884C\u65F6\u5728\u771F\u5B9E\u672C\u5730\u7EC8\u7AEF\u5B8C\u6210\u4E00\u6B21\u6027\u914D\u5BF9\u4E0E\u539F\u751F\u5B89\u88C5\u6279\u51C6\uFF1B
88
- --add-device \u5728\u5DF2\u5B8C\u6210\u7684\u5F15\u5BFC\u4E4B\u4E0A\u518D\u914D\u5BF9\u4E00\u53F0 Connector\uFF08\u6CBF\u7528\u540C\u4E00 MCP \u51ED\u636E\uFF0C\u6388\u6743\u968F\u4E4B\u6269\u5927\u5230\u65B0\u8BBE\u5907\uFF09\uFF1B
89
- --repair-device / --repair \u91CD\u65B0\u914D\u5BF9\u5DF2\u914D\u5BF9\u7684\u540C\u4E00\u8BBE\u5907\uFF08\u51ED\u636E\u4E22\u5931\u6216\u6CC4\u6F0F\uFF09\uFF0C\u4FDD\u7559\u539F\u751F\u8BBE\u7F6E\u4E0E MCP \u6388\u6743\u3002
90
- --claim \u8BA9\u672A\u914D\u5BF9\u7684 Connector \u5411\u8FD0\u884C\u4E2D\u7684 Gateway \u5728\u7EBF\u8BA4\u9886\u7BA1\u7406\u5458\u521B\u5EFA\u7684\u914D\u5BF9\u7533\u8BF7\uFF08\u7BA1\u7406\u9875\u6838\u5BF9\u7801\u6279\u51C6\uFF0CGateway \u4E0D\u505C\u673A\uFF09\u3002
91
- \u8FD9\u56DB\u4E2A\u9009\u9879\u9700\u8981\u771F\u5B9E\u672C\u5730\u7EC8\u7AEF\uFF0C\u4E0D\u53EF\u4E0E --non-interactive \u5408\u7528\u3002--non-interactive \u5173\u95ED\u672C\u5730\u4EA4\u4E92\u5F15\u5BFC\uFF08\u670D\u52A1\u4EE5\u6B64\u8FD0\u884C\uFF09\u3002
92
- token \u7BA1\u7406 MCP Bearer \u4EE4\u724C\uFF0Cadmin \u7BA1\u7406\u7BA1\u7406\u9875\u7BA1\u7406\u5458\uFF0Cservice \u7BA1\u7406\u5F00\u673A\u670D\u52A1\uFF1B\u8BE6\u89C1\u5404\u81EA\u7684 --help\u3002
93
- config check \u53EA\u8BFB\u68C0\u67E5\u914D\u7F6E\u6587\u4EF6\uFF1Bcheck-runtime \u53E6\u6838\u5BF9\u7A0B\u5E8F\u7BA1\u7406\u7684\u72B6\u6001\uFF0C\u4E0D\u8BFB\u53D6\u51ED\u636E\u3002
94
-
95
- \u7701\u7565 --config \u65F6\u8BFB\u53D6 ~/.config/rovloom/gateway.json \u6216 connector.json\uFF08Windows \u540C\u6837\u4F7F\u7528\u7528\u6237 home\uFF09\u3002
96
- \u6587\u4EF6\u7F3A\u5931\u65F6\u9996\u6B21\u8FD0\u884C\u4F1A\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E\uFF0C\u5DF2\u6709\u6587\u4EF6\u4E0D\u4F1A\u88AB\u8986\u76D6\u6216\u91CD\u7F6E\u3002
97
- Gateway \u652F\u6301 Linux/macOS/Windows\uFF1BConnector \u652F\u6301 Linux x64\u3001Windows x64 \u4E0E macOS\uFF08x64 / arm64\uFF0C\u4EC5\u9644\u7740\u5DF2\u8FD0\u884C\u7684 Herdr / Luvus\uFF09\u3002
98
- \u9996\u6B21\u5F15\u5BFC\u6309 Connector \u7684 native.provider \u9009\u62E9 luvus\uFF08\u9ED8\u8BA4\uFF09\u3001herdr \u6216 mixed\u3002
99
- Agent \u4E0E\u6A21\u578B\u767B\u5F55\u9700\u72EC\u7ACB\u51C6\u5907\uFF1B\u539F\u751F ready \u4E0D\u4EE3\u8868\u6A21\u578B\u5DF2\u56DE\u590D\u3002
100
- \u9000\u51FA\u7801\uFF1A0 \u6210\u529F/\u6B63\u5E38\u5173\u95ED\uFF0C2 \u53C2\u6570\u6216\u914D\u7F6E\u9519\u8BEF\uFF0C3 \u540E\u7AEF\u4E0D\u53EF\u7528\uFF0C1 \u8FD0\u884C\u6216\u5173\u95ED\u5931\u8D25\u3002
101
- `;
69
+ function cliHelp2(language = "en") {
70
+ return formatMessage2(cliMessages2(language)["help.main"], { version: PRODUCT_VERSION2 });
71
+ }
102
72
  function parseCommand2(args, environment) {
103
73
  if (args.length === 0 || args.length === 1 && ["--help", "-h", "help"].includes(args[0])) {
104
74
  return { action: "help" };
@@ -182,9 +152,9 @@ async function untilStopped(running, signal) {
182
152
  async function runCli2(args, io, runtime = {}, environment) {
183
153
  try {
184
154
  if (args[0] === "service") {
185
- const { SERVICE_HELP, handleServiceCommand } = await import("./chunk-emcefrc1.js");
155
+ const { handleServiceCommand } = await import("./chunk-g27b2gmx.js");
186
156
  if (args.length === 2 && args[1] === "--help") {
187
- io.stderr.write(SERVICE_HELP);
157
+ io.stderr.write(cliMessages2(io.language)["help.service"]);
188
158
  return 0;
189
159
  }
190
160
  if (!runtime.service) {
@@ -193,6 +163,7 @@ async function runCli2(args, io, runtime = {}, environment) {
193
163
  const host = runtime.service;
194
164
  const code = await handleServiceCommand(args, {
195
165
  ...host,
166
+ language: io.language ?? "en",
196
167
  resolveDefaultConfig: host.resolveDefaultConfig ? (role, account) => {
197
168
  try {
198
169
  return host.resolveDefaultConfig(role, account);
@@ -211,19 +182,19 @@ async function runCli2(args, io, runtime = {}, environment) {
211
182
  throw new CliError2("invalid_arguments");
212
183
  }
213
184
  if (args[0] === "admin") {
214
- const { handleAdminCommand } = await import("./chunk-ydmabg5t.js");
185
+ const { handleAdminCommand } = await import("./chunk-ytpfmcp1.js");
215
186
  return await handleAdminCommand(args, io, environment);
216
187
  }
217
188
  if (args[0] === "doctor" || args[0] === "storage") {
218
- const { handleStorageCommand } = await import("./chunk-1fyfrgte.js");
189
+ const { handleStorageCommand } = await import("./chunk-n6wtjyfb.js");
219
190
  return await handleStorageCommand(args, io, environment);
220
191
  }
221
192
  if (args[0] === "token") {
222
- const { handleTokenCommand } = await import("./chunk-8x6ygvhh.js");
193
+ const { handleTokenCommand } = await import("./chunk-ssp57p43.js");
223
194
  return await handleTokenCommand(args, io, environment);
224
195
  }
225
196
  if (args[0] === "config" && args[1] === "init") {
226
- const { initializeRoleConfig } = await import("./chunk-5vx1b9z8.js");
197
+ const { initializeRoleConfig } = await import("./chunk-m9c1pnv9.js");
227
198
  initializeRoleConfig(args, environment);
228
199
  io.stderr.write(`config_ready
229
200
  `);
@@ -231,7 +202,7 @@ async function runCli2(args, io, runtime = {}, environment) {
231
202
  }
232
203
  const command = parseCommand2(args, environment);
233
204
  if (command.action === "help") {
234
- io.stderr.write(HELP2);
205
+ io.stderr.write(cliHelp2(io.language));
235
206
  return 0;
236
207
  }
237
208
  if (command.action === "version") {
@@ -305,32 +276,38 @@ async function runCli2(args, io, runtime = {}, environment) {
305
276
  }
306
277
  let reload = false;
307
278
  let warnedWildcard = false;
279
+ let shownDashboard = false;
308
280
  do {
309
281
  reload = false;
310
282
  const gateway = await runtime.gateway(config);
311
283
  if (!warnedWildcard && (isWildcardHostname(config.mcp_hostname) || config.management_enabled && isWildcardHostname(config.management_hostname))) {
312
284
  warnedWildcard = true;
313
- io.stderr.write(WILDCARD_LISTEN_WARNING);
285
+ io.stderr.write(cliMessages2(io.language)["run.wildcardListenWarning"]);
314
286
  }
315
287
  try {
316
- const { startHttpFacade } = await import("./chunk-4af38z2b.js");
288
+ const { startHttpFacade } = await import("./chunk-srfbjwtk.js");
317
289
  const http = startHttpFacade({
318
290
  service: gateway.service,
319
291
  authentication: gateway.authentication,
320
292
  hostname: config.mcp_hostname,
321
293
  port: config.mcp_port,
322
294
  ...config.mcp_public_url ? { publicUrl: config.mcp_public_url } : {},
295
+ ...config.mcp_lan ? { lan: true } : {},
296
+ ...io.language ? { language: io.language } : {},
323
297
  diagnostic: (code) => io.stderr.write(code + `
324
298
  `)
325
299
  });
326
300
  try {
327
301
  await gateway.listening?.(http.url);
302
+ if (!shownDashboard && gateway.managementUrl) {
303
+ shownDashboard = true;
304
+ io.stderr.write("dashboard_listening " + new URL(gateway.managementUrl).href + `
305
+ `);
306
+ }
328
307
  if (gateway.setupRequired) {
329
308
  io.stderr.write("gateway_listening " + http.url + `
330
309
  pairing_required
331
- Gateway is running; complete local pairing and native setup before using an Agent.
332
- Gateway \u5DF2\u542F\u52A8\uFF1B\u4F7F\u7528 Agent \u524D\u4ECD\u9700\u5B8C\u6210\u672C\u5730\u914D\u5BF9\u548C\u539F\u751F\u521D\u59CB\u5316\u3002
333
- `);
310
+ ` + cliMessages2(io.language)["run.pairingRequiredHint"]);
334
311
  }
335
312
  await untilStopped({
336
313
  closed: Promise.race([
@@ -360,9 +337,7 @@ Gateway \u5DF2\u542F\u52A8\uFF1B\u4F7F\u7528 Agent \u524D\u4ECD\u9700\u5B8C\u621
360
337
  io.stderr.write(error.code + `
361
338
  `);
362
339
  if (error.code === "configuration_required") {
363
- io.stderr.write(`Setup requires a local terminal, a reachable Gateway, completed pairing and a compatible installed Luvus. Preserve uncertain setup state for owner review.
364
- \u521D\u59CB\u5316\u9700\u672C\u5730\u7EC8\u7AEF\u3001\u53EF\u8FBE Gateway\u3001\u5B8C\u6574\u914D\u5BF9\u53CA\u517C\u5BB9 Luvus \u5B89\u88C5\uFF1B\u7ED3\u679C\u4E0D\u786E\u5B9A\u65F6\u4FDD\u7559\u539F\u72B6\u6001\u5E76\u7531\u6240\u6709\u8005\u6838\u67E5\u3002
365
- `);
340
+ io.stderr.write(cliMessages2(io.language)["run.configurationRequiredHint"]);
366
341
  }
367
342
  return error.exitCode;
368
343
  }
@@ -372,6 +347,6 @@ Gateway \u5DF2\u542F\u52A8\uFF1B\u4F7F\u7528 Agent \u524D\u4ECD\u9700\u5B8C\u621
372
347
  }
373
348
  }
374
349
 
375
- export { HELP2, parseCommand2, runCli2 };
350
+ export { cliHelp2, parseCommand2, runCli2 };
376
351
 
377
- //# debugId=D32F2D6B4ECB1A3C64756E2164756E21
352
+ //# debugId=618C67B77D8D88C964756E2164756E21
@@ -5,6 +5,9 @@ import {
5
5
  import {
6
6
  parseNativeJson2
7
7
  } from "./chunk-7crhmqn2.js";
8
+ import {
9
+ isWildcardHostname
10
+ } from "./chunk-6n9s7nfe.js";
8
11
  import {
9
12
  binDirectories
10
13
  } from "./chunk-3nnahj0j.js";
@@ -107,10 +110,14 @@ function parseConfig2(value, platform = process.platform) {
107
110
  if (!hosts.includes(row.hostname)) {
108
111
  return fail();
109
112
  }
113
+ if (row.lan !== undefined && row.lan !== true) {
114
+ return fail();
115
+ }
110
116
  return {
111
117
  hostname: row.hostname,
112
118
  port: port(row.port),
113
- public_url: row.public_url
119
+ public_url: row.public_url,
120
+ ...row.lan === true ? { lan: true } : {}
114
121
  };
115
122
  };
116
123
  if (input.schema_version !== 1 || !["gateway", "connector"].includes(input.role)) {
@@ -122,12 +129,15 @@ function parseConfig2(value, platform = process.platform) {
122
129
  }
123
130
  if (input.role === "gateway") {
124
131
  exact(input, ["schema_version", "role", "data", "mcp", "devices", "management"]);
125
- const mcp = listener(input.mcp, ["public_url"]);
132
+ const mcp = listener(input.mcp, ["public_url", "lan"]);
126
133
  if (mcp.public_url !== undefined && !publicMcpUrl(mcp.public_url)) {
127
134
  return fail();
128
135
  }
136
+ if (mcp.lan !== undefined && (mcp.lan !== true || !isWildcardHostname(mcp.hostname))) {
137
+ return fail();
138
+ }
129
139
  const devices = listener(input.devices);
130
- const management = exact(input.management, ["enabled", "port"], ["hostname"]);
140
+ const management = exact(input.management, ["enabled", "port"], ["hostname", "lan"]);
131
141
  if (typeof management.enabled !== "boolean") {
132
142
  return fail();
133
143
  }
@@ -136,6 +146,9 @@ function parseConfig2(value, platform = process.platform) {
136
146
  }
137
147
  const managementHostname = management.hostname ?? "127.0.0.1";
138
148
  const managementPort = port(management.port);
149
+ if (management.lan !== undefined && (management.lan !== true || managementHostname !== "0.0.0.0")) {
150
+ return fail();
151
+ }
139
152
  if (new Set([mcp.port, devices.port, ...management.enabled ? [managementPort] : []]).size !== (management.enabled ? 3 : 2)) {
140
153
  return fail();
141
154
  }
@@ -147,11 +160,13 @@ function parseConfig2(value, platform = process.platform) {
147
160
  mcp_hostname: mcp.hostname,
148
161
  mcp_port: mcp.port,
149
162
  ...mcp.public_url !== undefined ? { mcp_public_url: mcp.public_url } : {},
163
+ ...mcp.lan === true ? { mcp_lan: true } : {},
150
164
  device_hostname: devices.hostname,
151
165
  device_port: devices.port,
152
166
  management_enabled: management.enabled,
153
167
  management_hostname: managementHostname,
154
168
  management_port: managementPort,
169
+ ...management.lan === true ? { management_lan: true } : {},
155
170
  authentication_ref: "mcp-callers"
156
171
  });
157
172
  }
@@ -219,13 +234,15 @@ function configDocument2(config) {
219
234
  mcp: {
220
235
  hostname: config.mcp_hostname,
221
236
  port: config.mcp_port,
222
- ...config.mcp_public_url !== undefined ? { public_url: config.mcp_public_url } : {}
237
+ ...config.mcp_public_url !== undefined ? { public_url: config.mcp_public_url } : {},
238
+ ...config.mcp_lan === true ? { lan: true } : {}
223
239
  },
224
240
  devices: { hostname: config.device_hostname, port: config.device_port },
225
241
  management: {
226
242
  enabled: config.management_enabled,
227
243
  ...config.management_hostname !== "127.0.0.1" ? { hostname: config.management_hostname } : {},
228
- port: config.management_port
244
+ port: config.management_port,
245
+ ...config.management_lan === true ? { lan: true } : {}
229
246
  }
230
247
  } : {
231
248
  ...common,
@@ -286,4 +303,4 @@ function readConfig2(path) {
286
303
 
287
304
  export { herdrMode, luvusMode, selectedNativeProviders, CLI_ERROR_CODES, CliError2, CliCancelled, parseConfig2, configDocument2, configTemplate2, readConfig2 };
288
305
 
289
- //# debugId=8E2FBD65A09D61EC64756E2164756E21
306
+ //# debugId=75790FE78402A30364756E2164756E21
@@ -38,7 +38,7 @@ import {
38
38
  closeSession
39
39
  } from "./chunk-zjzrcc59.js";
40
40
  import"./chunk-zj48jvfe.js";
41
- import"./chunk-nyn75b8v.js";
41
+ import"./chunk-qjh9x872.js";
42
42
  import"./chunk-0xgpxdgc.js";
43
43
  import"./chunk-d4ejrtjq.js";
44
44
  import {
@@ -52,7 +52,7 @@ import {
52
52
  composeConnectorSessions2,
53
53
  createLuvusConnectorSessions2,
54
54
  createLuvusProductionSources2
55
- } from "./chunk-4etdq0cp.js";
55
+ } from "./chunk-jka7v3wb.js";
56
56
  import"./chunk-5h82060k.js";
57
57
  import {
58
58
  herdrDeadline,
@@ -69,10 +69,10 @@ import {
69
69
  } from "./chunk-2tn0r64b.js";
70
70
  import {
71
71
  readRuntimeFile
72
- } from "./chunk-se5daacy.js";
72
+ } from "./chunk-z7kkfrgz.js";
73
73
  import {
74
74
  CliError2
75
- } from "./chunk-bjdqj43d.js";
75
+ } from "./chunk-hpzr94hm.js";
76
76
  import"./chunk-vwcntax3.js";
77
77
  import"./chunk-gbsbwssz.js";
78
78
  import"./chunk-3nnahj0j.js";
@@ -55,7 +55,7 @@ import {
55
55
  import {
56
56
  configuredSnapshotDirectory,
57
57
  Store2
58
- } from "./chunk-nyn75b8v.js";
58
+ } from "./chunk-qjh9x872.js";
59
59
  import {
60
60
  createProviderResultAccess2,
61
61
  LUVUS_ENDPOINT_TRANSPORT,
@@ -2030,10 +2030,7 @@ function createManagementSessionAuthority2(source, options = {}) {
2030
2030
  if (lease.revoked && !lease.expiry_only || at < lease.session.expires_at || input.issued_at !== lease.upstream.issued_at || input.expires_at !== lease.upstream.expires_at) {
2031
2031
  throw new GatewayError2("unauthenticated");
2032
2032
  }
2033
- const session = Object.freeze({
2034
- ...lease.upstream,
2035
- expires_at: Math.min(lease.upstream.expires_at, at + 300000)
2036
- });
2033
+ const session = lease.upstream;
2037
2034
  try {
2038
2035
  base(session, lease.policy);
2039
2036
  } catch {
@@ -2054,7 +2051,7 @@ function createManagementSessionAuthority2(source, options = {}) {
2054
2051
  caller,
2055
2052
  management_session_ref: sessionRef,
2056
2053
  issued_at: input.issued_at,
2057
- expires_at: Math.min(input.expires_at, at + 300000)
2054
+ expires_at: input.expires_at
2058
2055
  });
2059
2056
  const current = base(session);
2060
2057
  if (leases.has(name) || leases.size >= capacity) {
@@ -2872,4 +2869,4 @@ function createLuvusProductionSources2() {
2872
2869
  }
2873
2870
  export { createTrustedRequestScope2, composeGatewaySessions2, composeConnectorSessions2, createGatewayRuntime2, createProviderGatewayRuntime2, openGatewayState2, createLuvusConnectorSessions2, createConnectorRuntime2, ConnectorHostStartupError2, createProviderConnectorRuntime2, createManagementSessionAuthority2, createLuvusOperationsProvider2, attachExistingLuvus2, LuvusProductionStartupError2, createLuvusProductionSources2 };
2874
2871
 
2875
- //# debugId=785921E4C40CA8AB64756E2164756E21
2872
+ //# debugId=8FAF80F854C0B57664756E2164756E21
@@ -6,13 +6,13 @@ import"./chunk-9tt13dm0.js";
6
6
  import"./chunk-g7pnjzkk.js";
7
7
  import {
8
8
  readOrCreateRoleConfig
9
- } from "./chunk-v1httsp6.js";
9
+ } from "./chunk-8ykrqhxh.js";
10
10
  import {
11
11
  resolveRoleConfigPath2
12
- } from "./chunk-qfyefed3.js";
12
+ } from "./chunk-1bf0wmgk.js";
13
13
  import {
14
14
  CliError2
15
- } from "./chunk-bjdqj43d.js";
15
+ } from "./chunk-hpzr94hm.js";
16
16
  import"./chunk-3nnahj0j.js";
17
17
  import"./chunk-0y5542k8.js";
18
18
 
@@ -50,7 +50,7 @@ function initializeRoleConfig(args, environment) {
50
50
  if (config.role === "connector" && provider && (config.native?.provider ?? "luvus") !== provider) {
51
51
  throw new CliError2("config_exists");
52
52
  }
53
- if (config.role === "gateway" && container && [config.mcp_hostname, config.device_hostname, config.management_hostname].some((host) => host !== "0.0.0.0")) {
53
+ if (config.role === "gateway" && container && (config.management_lan !== true || [config.mcp_hostname, config.device_hostname, config.management_hostname].some((host) => host !== "0.0.0.0"))) {
54
54
  throw new CliError2("config_exists");
55
55
  }
56
56
  return config;
@@ -59,4 +59,4 @@ export {
59
59
  initializeRoleConfig
60
60
  };
61
61
 
62
- //# debugId=130364AF0AAC3C8064756E2164756E21
62
+ //# debugId=5FE0BCD7E52AF87B64756E2164756E21
@@ -22,7 +22,7 @@ import {
22
22
  SERVICE_ERROR_CODES,
23
23
  FacadeServiceError2,
24
24
  NativeRequestNotDispatchedError
25
- } from "./chunk-smpevv7n.js";
25
+ } from "./chunk-0dqx2mmz.js";
26
26
  import {
27
27
  LuvusAdapterError2,
28
28
  terminalExecutionCatalog