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
package/README.md CHANGED
@@ -27,47 +27,16 @@ port is opened on the machines that run them.
27
27
  ## How it works
28
28
 
29
29
  AI Agents and CLIs talk to the Gateway over MCP. Each device runs one Connector with local provider
30
- adapters, which can manage several runtime instances.
31
-
32
- ```mermaid
33
- flowchart TB
34
- subgraph ACCESS["Access layer"]
35
- direction LR
36
- AI["AI Agent / AI Bot"]
37
- CLI["CLI"]
38
- end
39
-
40
- GW["Gateway<br/>MCP facade · registry · routing · policy<br/>call ledger · result storage · management UI"]
41
-
42
- subgraph DEVICE_A["Device A"]
43
- direction TB
44
- CA["One Connector<br/>one device identity · local policy"]
45
- PA["Provider adapters<br/>trusted binding per instance"]
46
- LA["Local Luvus<br/>Host / session"]
47
- HA["Local Herdr<br/>session runtime"]
48
- CA <--> PA
49
- PA <--> LA
50
- PA <--> HA
51
- end
52
-
53
- subgraph DEVICE_B["Device B"]
54
- direction TB
55
- CB["One Connector<br/>one device identity · local policy"]
56
- PB["Provider adapters<br/>trusted binding per instance"]
57
- LB["Local Luvus<br/>Host / session"]
58
- HB["Local Herdr<br/>session runtime"]
59
- CB <--> PB
60
- PB <--> LB
61
- PB <--> HB
62
- end
63
-
64
- AI <-->|"MCP requests / results"| GW
65
- CLI <-->|"commands / results"| GW
66
- GW <-->|"requests ↓ · results / events ↑"| CA
67
- GW <-->|"requests ↓ · results / events ↑"| CB
68
- CA -.->|"initiates outbound WSS"| GW
69
- CB -.->|"initiates outbound WSS"| GW
70
- ```
30
+ adapters, which can manage several runtime instances. Sessions in those runtimes run local Agents
31
+ such as Claude Code, Codex, Pi, OpenCode and Fx; the Agent software and logins stay on that device.
32
+
33
+ <p align="center">
34
+ <a href="docs/assets/architecture/architecture.en.svg">
35
+ <img src="docs/assets/architecture/architecture.en.png" alt="Rovloom architecture: MCP clients access a Gateway; one outbound Connector per device routes calls through provider adapters to local Luvus and Herdr runtimes and their session Agents, such as Claude Code, Codex, Pi, OpenCode and Fx." width="1200">
36
+ </a>
37
+ </p>
38
+
39
+ Solid arrows show requests and returned results/events; dashed arrows show Connector-initiated outbound WSS. Click the image for the scalable SVG.
71
40
 
72
41
  ## Typical setup: two machines, one Agent
73
42
 
@@ -79,12 +48,23 @@ workstation's real sessions.
79
48
  2. On the workstation: `rovloom connector`, then enter the Gateway address and the one-time code.
80
49
  3. Add the Gateway's MCP URL and Bearer to your AI client and ask it to list the workstation's workspaces.
81
50
 
51
+ ## CLI language
52
+
53
+ CLI output (help, pairing prompts, status hints) is English by default. Set `ROVLOOM_LANG=zh-CN`
54
+ to switch to Simplified Chinese; without it the CLI stays in English regardless of the system
55
+ locale (`LC_ALL`/`LANG` have no effect). Error and status codes (`config_valid`,
56
+ `pairing_required`, …) are stable identifiers and never change with the language. The management
57
+ page picks its language in the browser, independently of this setting.
58
+
82
59
  ## Install
83
60
 
84
61
  ```sh
85
62
  curl --fail --silent --show-error --location https://raw.githubusercontent.com/ArnoChenFx/rovloom/main/scripts/install.sh | sh
86
63
  ```
87
64
 
65
+ Already have Bun (1.4.3 or newer)? `bun add -g rovloom` installs the npm package instead — no
66
+ bundled runtime to download — and `bunx rovloom` runs it without installing.
67
+
88
68
  Windows PowerShell and the Docker Gateway are covered in the [Quick Start](docs/QUICK-START.md).
89
69
 
90
70
  | | Linux | macOS | Windows |
@@ -8,7 +8,7 @@ import {
8
8
  StoreManagementWriteService2,
9
9
  createManagementHandler2,
10
10
  createManagementSessionAdapter2
11
- } from "../chunk-khmgp03q.js";
11
+ } from "../chunk-ez7d5dac.js";
12
12
  import {
13
13
  sampleManagementTransport2,
14
14
  createManagementTransportProjection2
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  canonical2
4
4
  } from "./chunk-d71r315c.js";
5
+ import {
6
+ cliMessages2
7
+ } from "./chunk-5b8g62kw.js";
5
8
 
6
9
  // src/mcp/schema.ts
7
10
  import { Ajv } from "ajv";
@@ -61,19 +64,9 @@ var schemas = {
61
64
  runtime_result_read: object({ result_id: id, cursor, max_bytes: { type: "integer", minimum: 1, maximum: MAX_RESULT_BYTES2 } }, ["result_id"]),
62
65
  runtime_response_discard: object({ call_id: id }, ["call_id"])
63
66
  };
64
- var descriptions = {
65
- machines_list: "\u5217\u51FA\u5F53\u524D caller \u53EF\u89C1\u7684\u8BBE\u5907\uFF1B\u5206\u9875\u548C\u8BA1\u6570\u987B\u7ECF\u8FC7\u5F53\u524D ACL\u3002",
66
- machines_get: "\u6309\u7A33\u5B9A ID \u6216\u7CBE\u786E\u540D\u79F0\u8BFB\u53D6\u53EF\u89C1\u8BBE\u5907\u3002",
67
- providers_list: "\u5217\u51FA\u6307\u5B9A\u8BBE\u5907\u4E0B\u5F53\u524D caller \u53EF\u89C1\u7684\u5DF2\u914D\u7F6E provider\uFF1B\u4E0D\u8FD4\u56DE\u672C\u673A\u914D\u7F6E\u3002",
68
- providers_get: "\u8BFB\u53D6\u663E\u5F0F provider \u7684 kind\u3001revision \u548C readiness\u3002",
69
- runtime_discover: "\u53D1\u73B0\u663E\u5F0F Target/revision \u7684\u6709\u6548\u80FD\u529B\u53CA\u6765\u6E90\uFF1B\u4E0D\u6388\u4E88\u6267\u884C\u6743\u9650\u3002",
70
- runtime_describe: "\u63CF\u8FF0\u663E\u5F0F Target/revision \u4E0B\u7CBE\u786E\u65B9\u6CD5\u7684\u539F\u751F\u53C2\u6570\u3001\u5206\u7C7B\u548C\u9650\u5236\u3002",
71
- runtime_call: "\u63D0\u4EA4\u4E00\u6B21\u663E\u5F0F Target/revision/\u5951\u7EA6\u7684\u539F\u751F\u8C03\u7528\u3002deadline \u662F\u7EDD\u5BF9\u6BEB\u79D2\u671F\u9650\uFF0Cwait_ms \u53EA\u63A7\u5236\u77ED\u7B49\uFF1B\u4E0D\u91CD\u8BD5\u6216\u7BA1\u7406 provider\u3002",
72
- runtime_call_get: "\u6309\u5F53\u524D\u5F52\u5C5E\u3001\u53CC\u7AEF\u7B56\u7565\u548C\u5185\u5BB9\u95E8\u7981\u8BFB\u53D6\u8C03\u7528\uFF1B\u77ED\u7B49\u5230\u671F\u4E0D\u6539\u53D8\u6267\u884C\u671F\u9650\u3002",
73
- runtime_call_cancel: "\u8BF7\u6C42\u505C\u6B62\u7B49\u5F85\u6216\u6865\u63A5\uFF1B\u4EC5\u62A5\u544A\u670D\u52A1\u53EF\u786E\u8BA4\u7A0B\u5EA6\uFF0C\u4E0D\u627F\u8BFA\u539F\u751F\u56DE\u6EDA\u3002",
74
- runtime_result_read: "\u6309\u5F53\u524D\u8BFB\u53D6\u6388\u6743\u548C\u671F\u9650\u8BFB\u53D6\u6709\u754C\u7ED3\u679C\u9875\uFF1Bcursor \u7531\u670D\u52A1\u7ED1\u5B9A caller \u548C\u539F\u7ED3\u679C\u3002",
75
- runtime_response_discard: "\u5904\u7406\u5B8C\u7ED3\u679C\u540E\u663E\u5F0F\u5220\u9664\u539F\u6587\u4E0E\u53E5\u67C4\u5E76\u91CA\u653E\u8BFB\u53D6\u5BB9\u91CF\uFF1B\u5FC5\u987B\u9A8C\u8BC1\u5F52\u5C5E\u3001\u5F53\u524D\u539F\u65B9\u6CD5\u7B56\u7565\u53CA\u72EC\u7ACB discard_response \u6743\u9650\u3002\u8BFB\u53D6\u5230 EOF \u4E0D\u4F1A\u81EA\u52A8\u5220\u9664\u7ED3\u679C\u3002"
76
- };
67
+ function toolDescription(name, language = "en") {
68
+ return cliMessages2(language)["mcp.tool." + name];
69
+ }
77
70
  var readOnly = new Set([
78
71
  "machines_list",
79
72
  "machines_get",
@@ -89,10 +82,10 @@ var validators = new Map(Object.entries(schemas).map(([name, schema]) => [name,
89
82
  function toolNames2() {
90
83
  return Object.keys(schemas);
91
84
  }
92
- function toolDefinition2(name) {
85
+ function toolDefinition2(name, language = "en") {
93
86
  return {
94
87
  name,
95
- description: descriptions[name],
88
+ description: toolDescription(name, language),
96
89
  inputSchema: structuredClone(schemas[name]),
97
90
  outputSchema: {
98
91
  type: "object",
@@ -178,4 +171,4 @@ class NativeRequestNotDispatchedError extends FacadeServiceError2 {
178
171
 
179
172
  export { MAX_MCP_BYTES2, MAX_RESULT_BYTES2, MAX_WAIT_MS2, TOOLS_PAGE_SIZE, toolNames2, toolDefinition2, isToolName, parseToolArguments2, SERVICE_ERROR_CODES, FacadeServiceError2, NativeRequestNotDispatchedError };
180
173
 
181
- //# debugId=08E91064C7F6137064756E2164756E21
174
+ //# debugId=E037D9DEF799B17A64756E2164756E21
@@ -1,20 +1,21 @@
1
1
  // @bun
2
2
  import {
3
3
  createProductionCliRuntime2
4
- } from "./chunk-dtj9y82v.js";
5
- import"./chunk-fd9n51mc.js";
6
- import"./chunk-wdr3sdsm.js";
7
- import"./chunk-qab3m6nh.js";
4
+ } from "./chunk-d2kz25f2.js";
5
+ import"./chunk-zjkjjm81.js";
6
+ import"./chunk-yv9px0f5.js";
7
+ import"./chunk-wxqhscsw.js";
8
8
  import"./chunk-2tn0r64b.js";
9
- import"./chunk-khmgp03q.js";
10
- import"./chunk-7k9g0x5e.js";
11
- import"./chunk-smpevv7n.js";
12
- import"./chunk-6hxy1fcg.js";
13
- import"./chunk-v1httsp6.js";
14
- import"./chunk-qfyefed3.js";
15
- import"./chunk-se5daacy.js";
16
- import"./chunk-bjdqj43d.js";
17
- import"./chunk-4etdq0cp.js";
9
+ import"./chunk-ez7d5dac.js";
10
+ import"./chunk-mg0fd6ph.js";
11
+ import"./chunk-0dqx2mmz.js";
12
+ import"./chunk-bspzg8er.js";
13
+ import"./chunk-8ykrqhxh.js";
14
+ import"./chunk-1bf0wmgk.js";
15
+ import"./chunk-z7kkfrgz.js";
16
+ import"./chunk-hpzr94hm.js";
17
+ import"./chunk-5b8g62kw.js";
18
+ import"./chunk-jka7v3wb.js";
18
19
  import"./chunk-5s2emvww.js";
19
20
  import"./chunk-d4ejrtjq.js";
20
21
  import"./chunk-zj48jvfe.js";
@@ -30,7 +31,7 @@ import"./chunk-57ayxqnb.js";
30
31
  import"./chunk-3nnahj0j.js";
31
32
  import"./chunk-5h82060k.js";
32
33
  import"./chunk-5svt6rwr.js";
33
- import"./chunk-nyn75b8v.js";
34
+ import"./chunk-qjh9x872.js";
34
35
  import"./chunk-zdxww7ms.js";
35
36
  import"./chunk-0pewfj89.js";
36
37
  import"./chunk-0y5542k8.js";
@@ -48,4 +49,4 @@ export {
48
49
  createProductionCliRuntime2 as createProductionCliRuntime
49
50
  };
50
51
 
51
- //# debugId=BEA716956F67769264756E2164756E21
52
+ //# debugId=9700A4A99B06459764756E2164756E21
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  CliError2
4
- } from "./chunk-bjdqj43d.js";
4
+ } from "./chunk-hpzr94hm.js";
5
5
 
6
6
  // src/cli/config-path.ts
7
7
  import { homedir } from "os";
@@ -21,7 +21,11 @@ import {
21
21
  observedHerdrSource
22
22
  } from "./chunk-9tt13dm0.js";
23
23
  import"./chunk-g7pnjzkk.js";
24
- import"./chunk-wdr3sdsm.js";
24
+ import {
25
+ cliMessages2,
26
+ formatMessage2
27
+ } from "./chunk-5b8g62kw.js";
28
+ import"./chunk-yv9px0f5.js";
25
29
  import"./chunk-mqyq5jh6.js";
26
30
  import"./chunk-zj48jvfe.js";
27
31
  import {
@@ -30,7 +34,7 @@ import {
30
34
  setupReference,
31
35
  setupSessionsFor,
32
36
  GatewaySetup
33
- } from "./chunk-fd9n51mc.js";
37
+ } from "./chunk-zjkjjm81.js";
34
38
  import {
35
39
  compileHerdrSchemas2
36
40
  } from "./chunk-5h82060k.js";
@@ -72,17 +76,17 @@ import {
72
76
  writeSetupFile,
73
77
  readSetupJson,
74
78
  writeSetupJson
75
- } from "./chunk-qab3m6nh.js";
79
+ } from "./chunk-wxqhscsw.js";
76
80
  import {
77
81
  readRuntimeConfig2
78
- } from "./chunk-6hxy1fcg.js";
82
+ } from "./chunk-bspzg8er.js";
79
83
  import {
80
84
  privateDirectory
81
- } from "./chunk-v1httsp6.js";
82
- import"./chunk-qfyefed3.js";
85
+ } from "./chunk-8ykrqhxh.js";
86
+ import"./chunk-1bf0wmgk.js";
83
87
  import {
84
88
  runtimeReference
85
- } from "./chunk-se5daacy.js";
89
+ } from "./chunk-z7kkfrgz.js";
86
90
  import {
87
91
  herdrMode,
88
92
  luvusMode,
@@ -92,7 +96,7 @@ import {
92
96
  parseConfig2,
93
97
  configDocument2,
94
98
  readConfig2
95
- } from "./chunk-bjdqj43d.js";
99
+ } from "./chunk-hpzr94hm.js";
96
100
  import {
97
101
  createFileCredentialSink2,
98
102
  loadCredential2
@@ -248,23 +252,20 @@ import { homedir } from "os";
248
252
  import { isAbsolute, join } from "path";
249
253
 
250
254
  // src/cli/setup-prompts.ts
251
- function connectorNativePrompt(enabled, kind = "Luvus", attach = false) {
252
- if (attach) {
253
- return enabled === true ? `Attach to your running ${kind} session (or start one in your own config directory if none is running); remote callers will act on your REAL workspaces (terminal execution ENABLED in user config)? / \u9644\u7740\u5230\u4F60\u6B63\u5728\u8FD0\u884C\u7684 ${kind} \u4F1A\u8BDD\uFF08\u6CA1\u6709\u5219\u5728\u4F60\u81EA\u5DF1\u7684\u914D\u7F6E\u76EE\u5F55\u91CC\u542F\u52A8\u4E00\u4E2A\uFF09\uFF0C\u8FDC\u7A0B\u8C03\u7528\u5C06\u76F4\u63A5\u64CD\u4F5C\u4F60\u7684\u771F\u5B9E\u5DE5\u4F5C\u533A\uFF08\u7528\u6237\u914D\u7F6E\u5DF2\u5F00\u542F\u7EC8\u7AEF\u6267\u884C\uFF09\uFF1F` : `Attach to your running ${kind} session (or start one in your own config directory if none is running); remote callers will act on your REAL workspaces (terminal execution disabled)? / \u9644\u7740\u5230\u4F60\u6B63\u5728\u8FD0\u884C\u7684 ${kind} \u4F1A\u8BDD\uFF08\u6CA1\u6709\u5219\u5728\u4F60\u81EA\u5DF1\u7684\u914D\u7F6E\u76EE\u5F55\u91CC\u542F\u52A8\u4E00\u4E2A\uFF09\uFF0C\u8FDC\u7A0B\u8C03\u7528\u5C06\u76F4\u63A5\u64CD\u4F5C\u4F60\u7684\u771F\u5B9E\u5DE5\u4F5C\u533A\uFF08\u5173\u95ED\u7EC8\u7AEF\u6267\u884C\uFF09\uFF1F`;
254
- }
255
- return enabled === true ? `Discover ${kind} and create isolated state (terminal execution ENABLED in user config)? / \u53D1\u73B0 ${kind} \u5E76\u521B\u5EFA\u9694\u79BB\u72B6\u6001\uFF08\u7528\u6237\u914D\u7F6E\u5DF2\u5F00\u542F\u7EC8\u7AEF\u6267\u884C\uFF09\uFF1F` : `Discover this ${kind} installation and create its isolated local state (terminal execution disabled)? / \u53D1\u73B0 ${kind} \u5E76\u521B\u5EFA\u9694\u79BB\u72B6\u6001\uFF08\u5173\u95ED\u7EC8\u7AEF\u6267\u884C\uFF09\uFF1F`;
255
+ function connectorNativePrompt(enabled, kind = "Luvus", attach = false, language = "en") {
256
+ const messages = cliMessages2(language);
257
+ const key = attach ? enabled === true ? "setup.prompt.connectorNativeAttachEnabled" : "setup.prompt.connectorNativeAttachDisabled" : enabled === true ? "setup.prompt.connectorNativeIsolatedEnabled" : "setup.prompt.connectorNativeIsolatedDisabled";
258
+ return formatMessage2(messages[key], { kind });
256
259
  }
257
260
 
258
261
  // src/cli/setup-native.ts
259
262
  function assertSetupNativePath(config, terminal) {
260
263
  if (selectedNativeProviders(config).includes("luvus") && luvusMode(config) === "managed" && Buffer.byteLength(join(config.state_dir, "n")) > 52) {
261
- terminal.show(`native_state_path_too_long: choose a shorter data.directory before initialization (Luvus state path maximum 52 UTF-8 bytes). / \u521D\u59CB\u5316\u524D\u8BF7\u7F29\u77ED data.directory\uFF08Luvus \u72B6\u6001\u8DEF\u5F84\u6700\u591A52\u4E2AUTF-8\u5B57\u8282\uFF09\u3002
262
- `);
264
+ terminal.show(cliMessages2(terminal.io.language)["setup.native.statePathTooLongLuvus"]);
263
265
  throw new CliError2("configuration_required");
264
266
  }
265
267
  if (selectedNativeProviders(config).includes("herdr") && herdrMode(config) === "managed" && process.platform !== "win32" && Buffer.byteLength(join(config.state_dir, "hr/c/herdr/herdr-client.sock")) > 107) {
266
- terminal.show(`native_state_path_too_long: choose a shorter data.directory for the private Herdr sockets (herdr.sock and herdr-client.sock). / \u8BF7\u7F29\u77ED data.directory \u4EE5\u5BB9\u7EB3\u9694\u79BB Herdr \u7684 socket\uFF08\u542B herdr-client.sock\uFF09\u3002
267
- `);
268
+ terminal.show(cliMessages2(terminal.io.language)["setup.native.statePathTooLongHerdr"]);
268
269
  throw new CliError2("configuration_required");
269
270
  }
270
271
  }
@@ -311,16 +312,20 @@ async function prepareSetupNative(config, terminal, credential, attempt, authori
311
312
  const binary = setupBinary(config);
312
313
  const attach = luvusMode(config) === "attach";
313
314
  if (!attach && process.platform === "win32") {
314
- terminal.show(`luvus_managed_unsupported_on_windows: set native.luvus.mode to "attach". / Windows \u4E0A\u6682\u4E0D\u652F\u6301 managed\uFF0C\u8BF7\u628A native.luvus.mode \u8BBE\u4E3A "attach"\u3002
315
- `);
315
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.native.managedUnsupportedWindows"], {
316
+ provider: "luvus",
317
+ configKey: "luvus"
318
+ }));
316
319
  throw new CliError2("configuration_required");
317
320
  }
318
321
  if (!attach && process.platform === "darwin") {
319
- terminal.show(`luvus_managed_unsupported_on_macos: set native.luvus.mode to "attach". / macOS \u4E0A\u6682\u4E0D\u652F\u6301 managed\uFF0C\u8BF7\u628A native.luvus.mode \u8BBE\u4E3A "attach"\u3002
320
- `);
322
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.native.managedUnsupportedMacos"], {
323
+ provider: "luvus",
324
+ configKey: "luvus"
325
+ }));
321
326
  throw new CliError2("configuration_required");
322
327
  }
323
- if (!await terminal.confirm(connectorNativePrompt(config.allow_terminal_execution, "Luvus", attach))) {
328
+ if (!await terminal.confirm(connectorNativePrompt(config.allow_terminal_execution, "Luvus", attach, terminal.io.language))) {
324
329
  throw new CliError2("configuration_required");
325
330
  }
326
331
  const control = join(config.state_dir, "control");
@@ -529,16 +534,20 @@ async function prepareSetupHerdr(config, terminal, credential, attempt, authoriz
529
534
  const binary = setupBinary(config, "herdr");
530
535
  const attach = herdrMode(config) === "attach";
531
536
  if (!attach && process.platform === "win32") {
532
- terminal.show(`herdr_managed_unsupported_on_windows: set native.herdr.mode to "attach". / Windows \u4E0A\u6682\u4E0D\u652F\u6301 managed\uFF0C\u8BF7\u628A native.herdr.mode \u8BBE\u4E3A "attach"\u3002
533
- `);
537
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.native.managedUnsupportedWindows"], {
538
+ provider: "herdr",
539
+ configKey: "herdr"
540
+ }));
534
541
  throw new CliError2("configuration_required");
535
542
  }
536
543
  if (!attach && process.platform === "darwin") {
537
- terminal.show(`herdr_managed_unsupported_on_macos: set native.herdr.mode to "attach". / macOS \u4E0A\u6682\u4E0D\u652F\u6301 managed\uFF0C\u8BF7\u628A native.herdr.mode \u8BBE\u4E3A "attach"\u3002
538
- `);
544
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.native.managedUnsupportedMacos"], {
545
+ provider: "herdr",
546
+ configKey: "herdr"
547
+ }));
539
548
  throw new CliError2("configuration_required");
540
549
  }
541
- if (!await terminal.confirm(connectorNativePrompt(config.allow_terminal_execution, "Herdr", attach))) {
550
+ if (!await terminal.confirm(connectorNativePrompt(config.allow_terminal_execution, "Herdr", attach, terminal.io.language))) {
542
551
  throw new CliError2("configuration_required");
543
552
  }
544
553
  assertSetupNativePath(config, terminal);
@@ -703,7 +712,7 @@ async function pair(config, terminal) {
703
712
  let current;
704
713
  let credential;
705
714
  if (previous === undefined) {
706
- const value = await terminal.ask("One-time pairing code / \u4E00\u6B21\u6027\u914D\u5BF9\u7801: ", true);
715
+ const value = await terminal.ask(cliMessages2(terminal.io.language)["setup.connector.pairingCodePrompt"], true);
707
716
  const parts = value.split(".");
708
717
  if (parts.length !== 3) {
709
718
  throw new CliError2("configuration_required");
@@ -714,7 +723,7 @@ async function pair(config, terminal) {
714
723
  if (!enrollment || !/^[A-Za-z0-9_-]{32,256}$/.test(enrollment)) {
715
724
  throw new CliError2("configuration_required");
716
725
  }
717
- if (!await terminal.confirm("Trust this Gateway address for one-time device enrollment and reviewed native setup? / \u4FE1\u4EFB\u6B64 Gateway \u5730\u5740\u5E76\u5151\u6362\u4E00\u6B21\u6027\u8BBE\u5907\u914D\u5BF9\uFF1F")) {
726
+ if (!await terminal.confirm(cliMessages2(terminal.io.language)["setup.connector.trustGateway"])) {
718
727
  throw new CliError2("configuration_required");
719
728
  }
720
729
  current = {
@@ -815,7 +824,7 @@ async function setupConnector(config, terminal) {
815
824
  if (boundedJson2(configDocument2(readConfig2(terminal.configPath))) !== boundedJson2(configDocument2(config))) {
816
825
  throw new CliError2("config_changed");
817
826
  }
818
- const address = await terminal.ask("Gateway address (ws://host:port or wss://host) / Gateway \u5730\u5740: ");
827
+ const address = await terminal.ask(cliMessages2(terminal.io.language)["setup.gatewayAddressPrompt"]);
819
828
  config = parseConfig2({ ...configDocument2(config), gateway: { url: address } });
820
829
  writeSetupFile(terminal.configPath, JSON.stringify(configDocument2(config), null, 2) + `
821
830
  `, true, previous);
@@ -824,8 +833,7 @@ async function setupConnector(config, terminal) {
824
833
  privateDirectory(config.state_dir);
825
834
  privateDirectory(join3(config.state_dir, "managed"));
826
835
  const paired = await pair(config, terminal);
827
- terminal.show(`pairing_completed / \u8BBE\u5907\u8EAB\u4EFD\u5DF2\u6301\u4E45\u914D\u5BF9\uFF1B\u63A5\u4E0B\u6765\u53D1\u73B0\u6240\u9009\u539F\u751F\u5B89\u88C5\u3002
828
- `);
836
+ terminal.show(cliMessages2(terminal.io.language)["setup.connector.pairingCompleted"]);
829
837
  const prepared = [];
830
838
  for (const kind of selectedNativeProviders(config)) {
831
839
  prepared.push(await (kind === "luvus" ? prepareSetupNative : prepareSetupHerdr)(config, terminal, paired.credential, paired.current.attempt_id, paired.authorization));
@@ -843,8 +851,7 @@ async function setupConnector(config, terminal) {
843
851
  throw new CliError2("configuration_required");
844
852
  }
845
853
  } catch {
846
- terminal.show(`Native setup response unavailable; retain the original setup and rerun Connector to check it. / \u539F\u751F\u8BBE\u7F6E\u54CD\u5E94\u4E0D\u53EF\u7528\uFF1B\u4FDD\u7559\u539F\u72B6\u6001\uFF0C\u91CD\u65B0\u8FD0\u884C Connector \u6838\u5BF9\u3002
847
- `);
854
+ terminal.show(cliMessages2(terminal.io.language)["setup.connector.nativeSetupResponseUnavailable"]);
848
855
  throw new CliError2("configuration_required");
849
856
  }
850
857
  writeSetupJson(runtimeReference(config.state_dir, "providers"), {
@@ -856,8 +863,7 @@ async function setupConnector(config, terminal) {
856
863
  }))
857
864
  }, false, 1048576);
858
865
  writeSetupJson(runtimeReference(config.state_dir, "pairing"), { ...paired.current, phase: "configured" }, true);
859
- terminal.show(`Native state prepared; starting the real Connector. Watch Gateway for connector_ready. / \u539F\u751F\u72B6\u6001\u5DF2\u51C6\u5907\uFF1B\u542F\u52A8\u6B63\u5F0F Connector\uFF0C\u4EE5 Gateway \u7684\u5B9E\u9645\u5C31\u7EEA\u63D0\u793A\u4E3A\u51C6\u3002
860
- `);
866
+ terminal.show(cliMessages2(terminal.io.language)["setup.connector.nativeStatePrepared"]);
861
867
  terminal.finishQuestions();
862
868
  return config;
863
869
  }
@@ -869,9 +875,8 @@ async function repairConnector(config, terminal) {
869
875
  throw new CliError2("configuration_required");
870
876
  }
871
877
  const old = loadCredential2(credentialPath);
872
- terminal.show("This Connector is paired as " + old.machine_id + ". Re-pairing replaces only its Gateway credential; native setup is kept. Stop any other running Connector for this device first. / \u6B64 Connector \u5DF2\u914D\u5BF9\u4E3A " + old.machine_id + `\u3002\u91CD\u65B0\u914D\u5BF9\u53EA\u66F4\u6362\u4E0E Gateway \u7684\u51ED\u636E\uFF0C\u4FDD\u7559\u539F\u751F\u8BBE\u7F6E\uFF1B\u8BF7\u5148\u505C\u6B62\u8BE5\u8BBE\u5907\u4E0A\u5176\u5B83\u6B63\u5728\u8FD0\u884C\u7684 Connector\u3002
873
- `);
874
- const value = await terminal.ask("One-time re-pairing code from rovloom gateway --repair-device / \u6765\u81EA rovloom gateway --repair-device \u7684\u4E00\u6B21\u6027\u7801: ", true);
878
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.repair.alreadyPaired"], { machine: old.machine_id }));
879
+ const value = await terminal.ask(cliMessages2(terminal.io.language)["setup.repair.codePrompt"], true);
875
880
  const parts = value.split(".");
876
881
  if (parts.length !== 3) {
877
882
  throw new CliError2("configuration_required");
@@ -882,7 +887,7 @@ async function repairConnector(config, terminal) {
882
887
  if (machine !== old.machine_id || !enrollment || !/^[A-Za-z0-9_-]{32,256}$/.test(enrollment)) {
883
888
  throw new CliError2("configuration_required");
884
889
  }
885
- if (!await terminal.confirm("Trust this Gateway address and replace this device credential with a new one? / \u4FE1\u4EFB\u6B64 Gateway \u5730\u5740\u5E76\u7528\u65B0\u51ED\u636E\u66FF\u6362\u672C\u673A\u8BBE\u5907\u51ED\u636E\uFF1F")) {
890
+ if (!await terminal.confirm(cliMessages2(terminal.io.language)["setup.repair.trustConfirm"])) {
886
891
  throw new CliError2("configuration_required");
887
892
  }
888
893
  const delivery = await post(config, "/pairing/redeem", { attempt_id: attempt, machine_id: machine, enrollment }, terminal.io.signal);
@@ -921,14 +926,12 @@ async function repairConnector(config, terminal) {
921
926
  }
922
927
  } catch {
923
928
  terminal.assertOwner();
924
- terminal.show(`Gateway confirmation unavailable; if this Connector cannot authenticate, run the re-pairing again. / \u672A\u6536\u5230 Gateway \u7684\u786E\u8BA4\uFF1B\u82E5 Connector \u4E4B\u540E\u65E0\u6CD5\u8BA4\u8BC1\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C\u91CD\u65B0\u914D\u5BF9\u3002
925
- `);
929
+ terminal.show(cliMessages2(terminal.io.language)["setup.repair.confirmationUnavailable"]);
926
930
  }
927
931
  } finally {
928
932
  sink.close();
929
933
  }
930
- terminal.show(`device_repaired / \u8BBE\u5907\u51ED\u636E\u5DF2\u66F4\u6362\uFF1B\u6B63\u5728\u542F\u52A8 Connector\u3002
931
- `);
934
+ terminal.show(cliMessages2(terminal.io.language)["setup.repair.deviceRepaired"]);
932
935
  }
933
936
 
934
937
  // src/cli/online-claim.ts
@@ -958,7 +961,7 @@ async function claimConnector(config, terminal) {
958
961
  if (boundedJson2(configDocument2(readConfig2(terminal.configPath))) !== boundedJson2(configDocument2(config))) {
959
962
  throw new CliError2("config_changed");
960
963
  }
961
- const address = await terminal.ask("Gateway address (ws://host:port or wss://host) / Gateway \u5730\u5740: ");
964
+ const address = await terminal.ask(cliMessages2(terminal.io.language)["setup.gatewayAddressPrompt"]);
962
965
  config = parseConfig2({ ...configDocument2(config), gateway: { url: address } });
963
966
  writeSetupFile(terminal.configPath, JSON.stringify(configDocument2(config), null, 2) + `
964
967
  `, true, previous);
@@ -976,8 +979,7 @@ async function claimConnector(config, terminal) {
976
979
  throw new CliError2("configuration_required");
977
980
  }
978
981
  if (current.phase === "configured") {
979
- terminal.show(`pairing_completed / \u8BBE\u5907\u8EAB\u4EFD\u5DF2\u6301\u4E45\u914D\u5BF9\uFF0C\u539F\u751F\u6FC0\u6D3B\u5DF2\u5B8C\u6210\u3002
980
- `);
982
+ terminal.show(cliMessages2(terminal.io.language)["setup.claim.pairingCompletedActivated"]);
981
983
  return config;
982
984
  }
983
985
  if (current.phase === "paired") {
@@ -1005,8 +1007,7 @@ async function claimConnector(config, terminal) {
1005
1007
  continue;
1006
1008
  }
1007
1009
  if (Date.now() > deadline) {
1008
- terminal.show(`Approval wait ended; rerun the same command to resume this claim. / \u7B49\u5F85\u6279\u51C6\u5DF2\u7ED3\u675F\uFF1B\u91CD\u65B0\u8FD0\u884C\u540C\u4E00\u547D\u4EE4\u53EF\u7EE7\u7EED\u539F\u8BA4\u9886\u3002
1009
- `);
1010
+ terminal.show(cliMessages2(terminal.io.language)["setup.claim.waitEnded"]);
1010
1011
  throw new CliError2("configuration_required");
1011
1012
  }
1012
1013
  let status;
@@ -1064,15 +1065,15 @@ function matchesSavedCredential(current, path) {
1064
1065
  return credential.machine_id === current.machine_id && credential.recovery_generation === current.recovery_generation && digest2(credential.credential) === current.credential_hash;
1065
1066
  }
1066
1067
  async function restartClaim(config, terminal, path, credentialPath) {
1067
- if (terminal.io.signal.aborted || existsSync2(credentialPath) || !await terminal.confirm("Start a new claim with a new pairing application? / \u539F\u8BA4\u9886\u5DF2\u7EC8\u6B62\u6216\u8FC7\u671F\uFF0C\u662F\u5426\u4F7F\u7528\u65B0\u7684\u914D\u5BF9\u7533\u8BF7\u91CD\u65B0\u8BA4\u9886\uFF1F")) {
1068
+ if (terminal.io.signal.aborted || existsSync2(credentialPath) || !await terminal.confirm(cliMessages2(terminal.io.language)["setup.claim.restartConfirm"])) {
1068
1069
  throw new CliError2("configuration_required");
1069
1070
  }
1070
1071
  return startClaim(config, terminal, path, true);
1071
1072
  }
1072
1073
  async function startClaim(config, terminal, path, replace) {
1073
- const pairing_id = await terminal.ask("Pairing application ID / \u914D\u5BF9\u7533\u8BF7 ID: ");
1074
+ const pairing_id = await terminal.ask(cliMessages2(terminal.io.language)["setup.claim.pairingIdPrompt"]);
1074
1075
  id2(pairing_id);
1075
- if (!await terminal.confirm("Trust this Gateway address for online device enrollment? / \u4FE1\u4EFB\u6B64 Gateway \u5730\u5740\u5E76\u5728\u7EBF\u8BA4\u9886\u8BBE\u5907\u914D\u5BF9\uFF1F")) {
1076
+ if (!await terminal.confirm(cliMessages2(terminal.io.language)["setup.claim.trustConfirm"])) {
1076
1077
  throw new CliError2("configuration_required");
1077
1078
  }
1078
1079
  const secret = randomBytes(32).toString("base64url");
@@ -1094,9 +1095,10 @@ async function startClaim(config, terminal, path, replace) {
1094
1095
  phase: "waiting"
1095
1096
  };
1096
1097
  writeSetupJson(path, current, replace);
1097
- terminal.show("Claim code / \u6838\u5BF9\u7801: " + created.user_code + `
1098
- Approve this exact code for machine ` + current.machine_id + ` in the management page. / \u8BF7\u5728\u7BA1\u7406\u9875\u4E3A\u8BE5\u8BBE\u5907\u6838\u5BF9\u5E76\u6279\u51C6\u6B64\u7801\u3002
1099
- `);
1098
+ terminal.show(formatMessage2(cliMessages2(terminal.io.language)["setup.claim.claimCodeBlock"], {
1099
+ code: created.user_code,
1100
+ machine: current.machine_id
1101
+ }));
1100
1102
  return current;
1101
1103
  }
1102
1104
  async function acknowledge(config, terminal, current) {
@@ -1115,8 +1117,7 @@ async function acknowledge(config, terminal, current) {
1115
1117
  const { secret: _secret, ...rest } = current;
1116
1118
  const paired = { ...rest, phase: "paired" };
1117
1119
  writeSetupJson(runtimeReference(config.state_dir, "pairing-claim"), paired, true);
1118
- terminal.show(`pairing_completed / \u8BBE\u5907\u8EAB\u4EFD\u5DF2\u6301\u4E45\u914D\u5BF9\uFF1B\u63A5\u4E0B\u6765\u53D1\u73B0\u6240\u9009\u539F\u751F\u5B89\u88C5\u3002
1119
- `);
1120
+ terminal.show(cliMessages2(terminal.io.language)["setup.connector.pairingCompleted"]);
1120
1121
  return paired;
1121
1122
  }
1122
1123
  async function activate(config, terminal, current) {
@@ -1163,8 +1164,7 @@ async function activate(config, terminal, current) {
1163
1164
  throw new CliError2("configuration_required");
1164
1165
  }
1165
1166
  } catch {
1166
- terminal.show(`Native activation response unavailable; rerun the same command to check the original claim. / \u539F\u751F\u6FC0\u6D3B\u54CD\u5E94\u4E0D\u53EF\u7528\uFF1B\u91CD\u65B0\u8FD0\u884C\u540C\u4E00\u547D\u4EE4\u6309\u539F\u8BA4\u9886\u6838\u5BF9\u3002
1167
- `);
1167
+ terminal.show(cliMessages2(terminal.io.language)["setup.claim.activationResponseUnavailable"]);
1168
1168
  throw new CliError2("configuration_required");
1169
1169
  }
1170
1170
  const providersPath = runtimeReference(config.state_dir, "providers");
@@ -1194,8 +1194,7 @@ async function activate(config, terminal, current) {
1194
1194
  throw new CliError2("configuration_required");
1195
1195
  }
1196
1196
  writeSetupJson(runtimeReference(config.state_dir, "pairing-claim"), { ...current, phase: "configured" }, true);
1197
- terminal.show(`Native state prepared; starting the real Connector. / \u539F\u751F\u72B6\u6001\u5DF2\u51C6\u5907\uFF1B\u542F\u52A8\u6B63\u5F0F Connector\u3002
1198
- `);
1197
+ terminal.show(cliMessages2(terminal.io.language)["setup.claim.nativeStatePrepared"]);
1199
1198
  terminal.finishQuestions();
1200
1199
  return config;
1201
1200
  }
@@ -1297,4 +1296,4 @@ export {
1297
1296
  CliSetup
1298
1297
  };
1299
1298
 
1300
- //# debugId=C698A949CB8251CE64756E2164756E21
1299
+ //# debugId=DAEB8FB1ABC451BE64756E2164756E21