rovloom 0.6.0 → 0.6.1

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 (62) hide show
  1. package/README.md +2 -2
  2. package/dist/src/admin/index.js +1 -1
  3. package/dist/src/{chunk-g27b2gmx.js → chunk-0e9gg4sg.js} +2 -2
  4. package/dist/src/chunk-1xer1ent.js +102 -0
  5. package/dist/src/{chunk-863tk45b.js → chunk-41fny9qm.js} +5 -5
  6. package/dist/src/{chunk-zjzrcc59.js → chunk-59h200cr.js} +139 -7
  7. package/dist/src/chunk-5w5srj3b.js +93 -0
  8. package/dist/src/{chunk-d4ejrtjq.js → chunk-6n6b1xpy.js} +1 -1
  9. package/dist/src/{chunk-ez7d5dac.js → chunk-6w4g9fw8.js} +4 -1
  10. package/dist/src/{chunk-0dqx2mmz.js → chunk-73aq2qkz.js} +1 -1
  11. package/dist/src/{chunk-n6wtjyfb.js → chunk-8c9yc0ge.js} +5 -5
  12. package/dist/src/{chunk-57ayxqnb.js → chunk-8fy8w7ds.js} +2 -23
  13. package/dist/src/{chunk-2tn0r64b.js → chunk-9h9x79bx.js} +5 -5
  14. package/dist/src/{chunk-qjh9x872.js → chunk-av31bx1e.js} +1 -1
  15. package/dist/src/{chunk-0xgpxdgc.js → chunk-bbdtsg4b.js} +7 -5
  16. package/dist/src/{chunk-q5pgbwb3.js → chunk-e7jgpxkf.js} +2 -2
  17. package/dist/src/{chunk-zj48jvfe.js → chunk-efty77sb.js} +3 -40
  18. package/dist/src/{chunk-gyvqqnjb.js → chunk-enj7s8cv.js} +49 -29
  19. package/dist/src/{chunk-jka7v3wb.js → chunk-ftpy8xx3.js} +311 -23
  20. package/dist/src/{chunk-zdxww7ms.js → chunk-h3xyq7zk.js} +1 -1
  21. package/dist/src/{chunk-ttsrctxg.js → chunk-hxmq28pg.js} +9 -7
  22. package/dist/src/{chunk-s3ydr65k.js → chunk-j59ac0xs.js} +2 -2
  23. package/dist/src/{chunk-5s2emvww.js → chunk-j6svdqr8.js} +10 -302
  24. package/dist/src/{chunk-9zkq0kc8.js → chunk-jkzfck98.js} +3 -3
  25. package/dist/src/{chunk-ssp57p43.js → chunk-m76cmkfy.js} +7 -5
  26. package/dist/src/{chunk-d2kz25f2.js → chunk-m96mbzcw.js} +674 -308
  27. package/dist/src/{chunk-yv9px0f5.js → chunk-n3z3jaxy.js} +20 -14
  28. package/dist/src/{chunk-jf4r0r44.js → chunk-ndj0ay2p.js} +19 -18
  29. package/dist/src/{chunk-5b8g62kw.js → chunk-pgs9jp6t.js} +61 -179
  30. package/dist/src/chunk-pksaqt9a.js +25 -0
  31. package/dist/src/{chunk-wxqhscsw.js → chunk-pnep7yhj.js} +7 -99
  32. package/dist/src/chunk-qn5xae7g.js +66 -0
  33. package/dist/src/{chunk-0pewfj89.js → chunk-qp8tgms2.js} +1 -1
  34. package/dist/src/{chunk-mg0fd6ph.js → chunk-rmgw8h27.js} +10 -8
  35. package/dist/src/{chunk-gbsbwssz.js → chunk-sf4ztxba.js} +3 -3
  36. package/dist/src/{chunk-x0td5wn4.js → chunk-shez7hkc.js} +1 -1
  37. package/dist/src/chunk-sqz614bh.js +30 -0
  38. package/dist/src/{chunk-09f9q26d.js → chunk-vdwzb63d.js} +2 -2
  39. package/dist/src/chunk-xtvgm6kf.js +48 -0
  40. package/dist/src/chunk-y8zm75jt.js +157 -0
  41. package/dist/src/chunk-yasqw2vj.js +756 -0
  42. package/dist/src/{chunk-srfbjwtk.js → chunk-z0qmfqvd.js} +3 -3
  43. package/dist/src/chunk-zm705snh.js +53 -0
  44. package/dist/src/cli/index.js +29 -28
  45. package/dist/src/cli/main.js +5 -5
  46. package/dist/src/connector/herdr/installation.js +4 -4
  47. package/dist/src/connector/herdr/provider.js +9 -9
  48. package/dist/src/connector/herdr/runtime.js +7 -7
  49. package/dist/src/connector/luvus/index.js +7 -6
  50. package/dist/src/mcp/index.js +16 -15
  51. package/dist/src/runtime/index.js +16 -15
  52. package/dist/src/service/index.js +2 -2
  53. package/dist/src/store.js +4 -4
  54. package/dist/src/transport/index.js +9 -9
  55. package/dist/src/webui/index.js +141 -939
  56. package/package.json +1 -1
  57. package/dist/src/chunk-0qgk7mhz.js +0 -52
  58. package/dist/src/chunk-3dvn33c4.js +0 -1299
  59. package/dist/src/chunk-5j0gj6w2.js +0 -7
  60. package/dist/src/chunk-vwcntax3.js +0 -228
  61. package/dist/src/chunk-ytpfmcp1.js +0 -25
  62. package/dist/src/chunk-zjkjjm81.js +0 -1114
@@ -1,7 +1,4 @@
1
1
  // @bun
2
- import {
3
- DEFAULT_PAIRING_POLICY_REF
4
- } from "../chunk-5j0gj6w2.js";
5
2
  import {
6
3
  ReadError2,
7
4
  createSnapshotReader2,
@@ -13,10 +10,17 @@ import {
13
10
 
14
11
  // src/webui/i18n.ts
15
12
  var chinese = Object.freeze({
16
- "resources.previous": "\u4E0A\u4E00\u9875",
17
- "resources.next": "\u4E0B\u4E00\u9875",
18
13
  "resources.tokens": "MCP Token",
19
- "resources.pairings": "\u914D\u5BF9\u8BB0\u5F55",
14
+ "resources.pairingCodes": "\u914D\u5BF9\u7801",
15
+ "resources.createCode": "\u751F\u6210\u914D\u5BF9\u7801",
16
+ "resources.codesNote": "\u4E00\u673A\u4E00\u7801\uFF1A\u751F\u6210\u540E\u628A\u52A0\u5165\u547D\u4EE4\u590D\u5236\u5230\u76EE\u6807\u673A\u5668\u6267\u884C\uFF0C\u5373\u5B8C\u6210\u914D\u5BF9\u3002\u672A\u4F7F\u7528\u7684\u7801\u53EF\u968F\u65F6\u4F5C\u5E9F\u3002",
17
+ "resources.codeCreatedNote": "\u5DF2\u751F\u6210\u3002\u590D\u5236\u4E0B\u9762\u7684\u547D\u4EE4\u5230 Connector \u673A\u5668\u8FD0\u884C\u5373\u53EF\uFF08\u7801\u53EA\u663E\u793A\u4E00\u6B21\u547D\u4EE4\u5F62\u6001\uFF0C\u5217\u8868\u4E2D\u53EF\u518D\u6B21\u590D\u5236\uFF09\u3002",
18
+ "resources.code_unused": "\u672A\u4F7F\u7528",
19
+ "resources.code_used": "\u5DF2\u4F7F\u7528",
20
+ "resources.code_revoked": "\u5DF2\u4F5C\u5E9F",
21
+ "resources.code_failed": "\u5931\u8D25",
22
+ "resources.revokeCode": "\u4F5C\u5E9F",
23
+ "resources.confirmRevokeCode": "\u4F5C\u5E9F\u8FD9\u5F20\u914D\u5BF9\u7801\uFF1F\u76EE\u6807\u8BBE\u5907\u9700\u751F\u6210\u65B0\u7801\u3002",
20
24
  "resources.name": "\u540D\u79F0",
21
25
  "resources.expiry": "\u6709\u6548\u671F",
22
26
  "resources.days": "{count} \u5929",
@@ -37,17 +41,12 @@ var chinese = Object.freeze({
37
41
  "resources.deleteDeviceNote": "\u5220\u9664\u8BBE\u5907\u53CA\u5176\u8FD0\u884C\u914D\u7F6E\u3001\u914D\u5BF9\u8BB0\u5F55\u548C\u8C03\u7528\u5185\u5BB9\uFF0C\u5F53\u524D\u8FDE\u63A5\u4F1A\u65AD\u5F00\u3002",
38
42
  "resources.confirmDevice": "\u5220\u9664\u8BE5\u8BBE\u5907\u53CA\u5176\u914D\u5BF9\u3001\u8C03\u7528\u8BB0\u5F55\uFF1F\u5982\u9700\u518D\u6B21\u8FDE\u63A5\uFF0C\u8BF7\u91CD\u65B0\u914D\u5BF9\u3002",
39
43
  "resources.confirmToken": "\u5220\u9664 Token\u300C{name}\u300D\uFF1F\u4F7F\u7528\u5B83\u7684\u5BA2\u6237\u7AEF\u5C06\u65E0\u6CD5\u7EE7\u7EED\u8BBF\u95EE\u3002",
40
- "resources.confirmPairing": "\u5220\u9664\u8FD9\u6761\u914D\u5BF9\u8BB0\u5F55\uFF1F\u672A\u5B8C\u6210\u7684\u914D\u5BF9\u4F1A\u7EC8\u6B62\uFF0C\u5DF2\u914D\u5BF9\u8BBE\u5907\u4ECD\u4FDD\u7559\u3002",
41
44
  "resources.emptyTokens": "\u8FD8\u6CA1\u6709 MCP Token\u3002\u5728\u4E0A\u65B9\u521B\u5EFA\u4E00\u4E2A\uFF0C\u5373\u53EF\u8FDE\u63A5\u4F60\u7684 AI \u5BA2\u6237\u7AEF\u3002",
42
- "resources.emptyPairings": "\u6682\u65E0\u914D\u5BF9\u8BB0\u5F55\u3002",
43
45
  "resources.loading": "\u6B63\u5728\u52A0\u8F7D\u2026",
44
46
  "resources.saving": "\u6B63\u5728\u4FDD\u5B58\u2026",
45
47
  "resources.saved": "\u5DF2\u4FDD\u5B58",
46
48
  "resources.failed": "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002",
47
49
  "resources.invalid": "\u8BF7\u4F7F\u7528\u4E0D\u91CD\u590D\u7684\u540D\u79F0\uFF08\u5B57\u6BCD\u3001\u6570\u5B57\u3001- \u6216 _\uFF09\uFF0C\u5E76\u9009\u62E9\u6709\u6548\u671F\u3002",
48
- "resources.pending": "\u7B49\u5F85\u914D\u5BF9",
49
- "resources.paired": "\u5DF2\u914D\u5BF9",
50
- "resources.cancelled": "\u5DF2\u53D6\u6D88",
51
50
  "nav.workspace": "\u5DE5\u4F5C\u7A7A\u95F4",
52
51
  "nav.overview": "\u6982\u89C8",
53
52
  "nav.devices": "\u8BBE\u5907\u7BA1\u7406",
@@ -66,11 +65,6 @@ var chinese = Object.freeze({
66
65
  "overview.attention": "\u9700\u8981\u5173\u6CE8",
67
66
  "overview.attentionNote": "\u672C\u9875\u5C1A\u672A\u9A8C\u8BC1\u8FDE\u63A5\u7684\u8BBE\u5907",
68
67
  "overview.scope": "\u8BBE\u5907\u7EDF\u8BA1\u4EC5\u8986\u76D6\u5F53\u524D\u9875\uFF1B\u8FC7\u671F\u89C2\u6D4B\u4E0D\u4EE3\u8868\u5F53\u524D\u72B6\u6001\u3002",
69
- "overview.gateway": "\u7F51\u5173\u8FD0\u884C\u72B6\u6001",
70
- "overview.available": "\u53EF\u7528",
71
- "overview.degraded": "\u53D7\u9650",
72
- "overview.unavailable": "\u4E0D\u53EF\u7528",
73
- "overview.unverified": "\u672A\u6838\u5B9E",
74
68
  "overview.store": "\u6570\u636E\u5B58\u50A8",
75
69
  "overview.transport": "\u8BBE\u5907\u901A\u9053",
76
70
  "overview.native": "\u539F\u751F\u8FD0\u884C\u73AF\u5883",
@@ -85,23 +79,16 @@ var chinese = Object.freeze({
85
79
  "devices.results": "\u5F53\u524D\u9875\u663E\u793A {shown} / {count} \u53F0\u8BBE\u5907",
86
80
  "devices.noMatches": "\u6CA1\u6709\u5339\u914D\u7684\u8BBE\u5907",
87
81
  "devices.noMatchesHint": "\u8BD5\u8BD5\u5176\u4ED6\u540D\u79F0\u3001ID \u6216\u6807\u7B7E\uFF0C\u6216\u5207\u6362\u72B6\u6001\u7B5B\u9009\u3002",
88
- "devices.inspect": "\u67E5\u770B\u8BE6\u60C5",
89
82
  "devices.record": "\u89C2\u6D4B\u8BE6\u60C5",
90
83
  "devices.open": "\u67E5\u770B {name} \u7684\u8BE6\u60C5",
84
+ "overview.available": "\u53EF\u7528",
85
+ "overview.degraded": "\u964D\u7EA7",
86
+ "overview.unavailable": "\u4E0D\u53EF\u7528",
87
+ "overview.unverified": "\u672A\u6838\u9A8C",
91
88
  "pairing.pageTitle": "\u8FDE\u63A5\u4E00\u53F0\u65B0\u8BBE\u5907",
92
- "pairing.pageDescription": "\u521B\u5EFA\u7533\u8BF7\u3001\u8FD0\u884C Connector\u3001\u6838\u5BF9\u4EE3\u7801\u3002\u4E09\u6B65\u628A\u5DE5\u4F5C\u7AD9\u52A0\u5165\u4F60\u7684\u5DE5\u4F5C\u7A7A\u95F4\u3002",
93
- "pairing.stepCreate": "\u521B\u5EFA\u914D\u5BF9\u7533\u8BF7",
94
- "pairing.stepRun": "\u8FD0\u884C Connector",
95
- "pairing.stepApprove": "\u6838\u5BF9\u5E76\u6279\u51C6",
96
- "pairing.formTitle": "\u6DFB\u52A0\u4F60\u7684\u5DE5\u4F5C\u7AD9",
97
- "pairing.namePlaceholder": "\u4F8B\u5982 office-workstation",
98
- "pairing.nameHint": "\u4F7F\u7528 1\u201363 \u4F4D\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u6216\u8FDE\u5B57\u7B26\u3002",
99
- "pairing.advanced": "\u9AD8\u7EA7\uFF1A\u914D\u5BF9\u7B56\u7565",
100
- "pairing.guideTitle": "\u7136\u540E\uFF0C\u5728\u5DE5\u4F5C\u7AD9\u4E0A\u8FD0\u884C",
101
- "pairing.guideDescription": "\u5728\u5B89\u88C5\u4E86 Luvus \u6216 Herdr \u7684\u8BBE\u5907\u7EC8\u7AEF\u4E2D\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\uFF0C\u6309\u63D0\u793A\u8F93\u5165 Gateway \u5730\u5740\u548C\u7533\u8BF7\u7F16\u53F7\u3002",
102
- "pairing.guideNote": "\u8BA4\u9886\u540E\uFF0C\u6838\u5BF9\u7F51\u9875\u548C\u7EC8\u7AEF\u4E0A\u7684\u4EE3\u7801\uFF0C\u518D\u6279\u51C6\u672C\u673A\u539F\u751F\u5B89\u88C5\u8BBF\u95EE\u3002",
89
+ "pairing.pageDescription": "\u751F\u6210\u4E00\u673A\u4E00\u7801\uFF0C\u628A\u52A0\u5165\u547D\u4EE4\u590D\u5236\u5230\u76EE\u6807\u673A\u5668\u8FD0\u884C\u5373\u53EF\uFF1B\u6CA1\u6709\u7533\u8BF7\u3001\u6279\u51C6\u6216\u6838\u5BF9\u7801\u3002",
103
90
  "pairing.loginRequired": "\u914D\u5BF9\u9700\u8981\u7BA1\u7406\u4F1A\u8BDD",
104
- "pairing.loginHint": "\u4ECE Gateway \u7684\u7BA1\u7406\u5165\u53E3\u767B\u5F55\u540E\uFF0C\u5373\u53EF\u521B\u5EFA\u7533\u8BF7\u5E76\u6279\u51C6\u65B0\u8BBE\u5907\u3002",
91
+ "pairing.loginHint": "\u4ECE Gateway \u7684\u7BA1\u7406\u5165\u53E3\u767B\u5F55\u540E\uFF0C\u5373\u53EF\u751F\u6210\u914D\u5BF9\u7801\u3002",
105
92
  "connect.title": "\u628A\u5DE5\u4F5C\u7A7A\u95F4\u8FDE\u63A5\u5230 AI",
106
93
  "connect.description": "\u8BBE\u5907\u5B8C\u6210\u914D\u5BF9\u540E\uFF0C\u4E3A AI \u5BA2\u6237\u7AEF\u7B7E\u53D1\u4E13\u7528 MCP Bearer\u3002",
107
94
  "connect.stepOne": "\u627E\u5230\u5DF2\u6709\u8C03\u7528\u65B9",
@@ -113,7 +100,6 @@ var chinese = Object.freeze({
113
100
  "connect.docker": "Docker \u90E8\u7F72\u547D\u4EE4",
114
101
  "connect.docs": "\u67E5\u770B\u5B8C\u6574\u8FDE\u63A5\u6307\u5357",
115
102
  "copy.action": "\u590D\u5236\u547D\u4EE4",
116
- "copy.id": "\u590D\u5236\u7533\u8BF7\u7F16\u53F7",
117
103
  "copy.done": "\u5DF2\u590D\u5236",
118
104
  "copy.failed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u6587\u672C",
119
105
  "empty.title": "\u4F60\u7684\u7B2C\u4E00\u53F0\u8BBE\u5907\uFF0C\u4ECE\u8FD9\u91CC\u5F00\u59CB",
@@ -130,11 +116,9 @@ var chinese = Object.freeze({
130
116
  "app.footer": "Rovloom \xB7 Gateway \u63A7\u5236\u53F0",
131
117
  "preference.switch": "\u5207\u6362\u4E3A {value}",
132
118
  "language.label": "\u8BED\u8A00",
133
- "language.system": "\u8DDF\u968F\u6D4F\u89C8\u5668",
134
119
  "language.chinese": "\u7B80\u4F53\u4E2D\u6587",
135
120
  "language.english": "English",
136
121
  "theme.label": "\u4E3B\u9898",
137
- "theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
138
122
  "theme.light": "\u6D45\u8272",
139
123
  "theme.dark": "\u6DF1\u8272",
140
124
  "manage.heading": "\u8BBE\u5907\u7BA1\u7406\u64CD\u4F5C",
@@ -165,28 +149,6 @@ var chinese = Object.freeze({
165
149
  "manage.revokeEffect": "\u6301\u4E45\u64A4\u9500\u6210\u529F\u540E\uFF0C\u65E7\u8EAB\u4EFD\u5C06\u4E0D\u80FD\u91CD\u65B0\u63A5\u5165\uFF1B\u7ED3\u679C\u4EE5\u5F53\u524D\u6301\u4E45\u56DE\u6267\u4E3A\u51C6\uFF0C\u539F\u751F\u5DE5\u4F5C\u53EF\u80FD\u7EE7\u7EED\u3002",
166
150
  "manage.nativeEffect": "\u6B64\u64CD\u4F5C\u4E0D\u4FDD\u8BC1\u505C\u6B62\u6216\u56DE\u6EDA\u539F\u751F\u5DE5\u4F5C\u3002",
167
151
  "manage.action": "\u64CD\u4F5C",
168
- "pairing.title": "\u5728\u7EBF\u6DFB\u52A0\u8BBE\u5907",
169
- "pairing.intro": "\u4E3A\u5DE5\u4F5C\u7AD9\u8D77\u4E00\u4E2A\u540D\u79F0\u3002\u521B\u5EFA\u914D\u5BF9\u7533\u8BF7\u540E\uFF0C\u6309\u53F3\u4FA7\u6307\u5F15\u5B8C\u6210\u8FDE\u63A5\u3002",
170
- "pairing.name": "\u65B0\u8BBE\u5907\u540D\u79F0",
171
- "pairing.policy": "\u6388\u6743\u653F\u7B56\u5F15\u7528",
172
- "pairing.create": "\u65B0\u5EFA\u914D\u5BF9\u7533\u8BF7",
173
- "pairing.preparing": "\u6B63\u5728\u51C6\u5907\u540E\u53F0\u786E\u8BA4\u2026",
174
- "pairing.review": "\u8BF7\u6838\u5BF9\u4EE5\u4E0B\u540E\u53F0\u6458\u8981\uFF0C\u786E\u8BA4\u540E\u624D\u4F1A\u63D0\u4EA4\u3002",
175
- "pairing.confirm": "\u786E\u8BA4\u6267\u884C",
176
- "pairing.cancel": "\u53D6\u6D88",
177
- "pairing.created": "\u7533\u8BF7\u5DF2\u521B\u5EFA\u3002\u628A\u7533\u8BF7\u7F16\u53F7\u4EA4\u7ED9\u65B0\u8BBE\u5907\uFF1A",
178
- "pairing.waitingClaim": "\u7B49\u5F85\u8BBE\u5907\u63D0\u4EA4\u8BA4\u9886\u2026",
179
- "pairing.claimArrived": "\u8BBE\u5907\u5DF2\u53D1\u8D77\u8FDE\u63A5\u3002\u786E\u8BA4\u4E0B\u65B9\u4EE3\u7801\u4E0E\u5DE5\u4F5C\u7AD9\u7EC8\u7AEF\u4E00\u81F4\u540E\uFF0C\u518D\u6279\u51C6\u914D\u5BF9\uFF1A",
180
- "pairing.userCode": "\u6838\u5BF9\u7801",
181
- "pairing.approve": "\u6279\u51C6\u7B7E\u53D1\u51ED\u636E",
182
- "pairing.approved": "\u5DF2\u6279\u51C6\uFF0C\u51ED\u636E\u5DF2\u6258\u7BA1\u4EA4\u4ED8\uFF0C\u7B49\u5F85\u8BBE\u5907\u53D6\u56DE\u2026",
183
- "pairing.paired": "\u8BBE\u5907\u5DF2\u53D6\u56DE\u51ED\u636E\uFF0C\u914D\u5BF9\u5B8C\u6210\u3002",
184
- "pairing.activated": "\u8BBE\u5907\u5DF2\u53D6\u56DE\u51ED\u636E\u5E76\u5B8C\u6210\u539F\u751F\u6FC0\u6D3B\uFF0C\u53EF\u6B63\u5F0F\u4F7F\u7528\u3002",
185
- "pairing.statusPending": "\u7533\u8BF7\u5F85\u8BA4\u9886",
186
- "pairing.expired": "\u7533\u8BF7\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u65B0\u5EFA\u3002",
187
- "pairing.failed": "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u65B0\u51C6\u5907\u3002",
188
- "pairing.application": "\u7533\u8BF7\u7F16\u53F7",
189
- "pairing.machine": "\u8BBE\u5907",
190
152
  "manage.machine": "\u76EE\u6807\u8BBE\u5907",
191
153
  "manage.epoch": "\u8FDE\u63A5\u4EE3\u6B21",
192
154
  "manage.revision": "\u8D44\u6E90\u7248\u672C",
@@ -219,6 +181,15 @@ var chinese = Object.freeze({
219
181
  "error.timeout": "\u8BFB\u53D6\u8D85\u8FC7 5 \u79D2\u3002\u5F53\u524D\u663E\u793A\u4E0D\u662F\u5B9E\u65F6\u72B6\u6001\u3002",
220
182
  "error.cancelled": "\u5DF2\u53D6\u6D88\u672C\u6B21\u8BFB\u53D6\u3002",
221
183
  "time.noObservation": "\u5C1A\u65E0\u53EF\u4FE1\u89C2\u6D4B",
184
+ "reason.auth_failed": "\u8BA4\u8BC1\u5931\u8D25",
185
+ "reason.backend_unavailable": "\u540E\u7AEF\u4E0D\u53EF\u7528",
186
+ "reason.binding_mismatch": "\u7ED1\u5B9A\u4E0D\u4E00\u81F4",
187
+ "reason.connection_closed": "\u8FDE\u63A5\u5DF2\u5173\u95ED",
188
+ "reason.credential_expired": "\u51ED\u636E\u5DF2\u8FC7\u671F",
189
+ "reason.heartbeat_timeout": "\u5FC3\u8DF3\u8D85\u65F6",
190
+ "reason.not_recorded": "\u672A\u8BB0\u5F55",
191
+ "reason.protocol_error": "\u534F\u8BAE\u9519\u8BEF",
192
+ "reason.stale": "\u72B6\u6001\u8FC7\u671F",
222
193
  "time.noHeartbeat": "\u5C1A\u65E0\u53EF\u4FE1\u5FC3\u8DF3",
223
194
  "time.observed": "{time} \xB7 {age}{expired}",
224
195
  "time.expiredSuffix": " \xB7 \u5DF2\u8FC7\u671F",
@@ -250,15 +221,6 @@ var chinese = Object.freeze({
250
221
  "persisted.offline": "\u79BB\u7EBF\u767B\u8BB0 (offline)",
251
222
  "persisted.online": "\u5728\u7EBF\u767B\u8BB0 (online)",
252
223
  "persisted.revoked": "\u8EAB\u4EFD\u5DF2\u64A4\u9500 (revoked)",
253
- "reason.backend_unavailable": "\u540E\u7AEF\u4E0D\u53EF\u7528",
254
- "reason.not_recorded": "\u672A\u8BB0\u5F55",
255
- "reason.stale": "\u89C2\u6D4B\u5DF2\u8FC7\u671F",
256
- "reason.auth_failed": "\u8BA4\u8BC1\u5931\u8D25",
257
- "reason.credential_expired": "\u51ED\u636E\u8FC7\u671F",
258
- "reason.heartbeat_timeout": "\u5FC3\u8DF3\u8D85\u65F6",
259
- "reason.protocol_error": "\u534F\u8BAE\u9519\u8BEF",
260
- "reason.connection_closed": "\u8FDE\u63A5\u5DF2\u5173\u95ED",
261
- "reason.binding_mismatch": "\u914D\u7F6E\u4E0D\u4E00\u81F4",
262
224
  "device.expired": "\u8BBE\u5907\u89C2\u6D4B\u5DF2\u8FC7\u671F",
263
225
  "device.revokedHistory": "\u4E0A\u6B21\u6301\u4E45\u767B\u8BB0\uFF1A\u8EAB\u4EFD\u5DF2\u64A4\u9500\uFF08\u5DF2\u8FC7\u671F\uFF09",
264
226
  "device.oldRecords": "\u65E7\u8FDE\u63A5\u4E0E\u8BA4\u8BC1\u8BB0\u5F55\u4E0D\u80FD\u6062\u590D\u6388\u6743",
@@ -287,10 +249,6 @@ var chinese = Object.freeze({
287
249
  "devices.health": "\u8FDE\u63A5\u4E0E\u8BA4\u8BC1",
288
250
  "devices.activity": "\u6700\u8FD1\u5FC3\u8DF3",
289
251
  "devices.received": "Gateway \u63A5\u6536 {time}",
290
- "devices.observations": "\u89C2\u6D4B\u65F6\u95F4",
291
- "devices.emptyHeading": "\u5C1A\u65E0\u53EF\u5C55\u793A\u7684\u8BBE\u5907",
292
- "devices.empty": "\u6B64\u8303\u56F4\u5C1A\u65E0\u8BBE\u5907\u3002\u7531\u53D7\u4FDD\u62A4\u7684\u672C\u673A\u7BA1\u7406\u6E20\u9053\u5EFA\u7ACB\u914D\u5BF9\u7533\u8BF7\u540E\u518D\u5237\u65B0\u3002",
293
- "devices.heartbeat": "\u6700\u8FD1\u5FC3\u8DF3\uFF1A{age} \xB7 Gateway \u63A5\u6536 {time}",
294
252
  "devices.observed": "\u8BBE\u5907\u72B6\u6001\u89C2\u6D4B\uFF1A{time}",
295
253
  "devices.connectionObserved": "\u8FDE\u63A5\u89C2\u6D4B\uFF1A{time}",
296
254
  "devices.authObserved": "\u8BA4\u8BC1\u89C2\u6D4B\uFF1A{time}",
@@ -351,10 +309,17 @@ var chinese = Object.freeze({
351
309
  "gateway.degradedNote": "\u5F53\u524D\u7981\u6B62\u7BA1\u7406\u5199\u5165\uFF1B\u6301\u4E45\u64A4\u9500\u7B49\u64CD\u4F5C\u53EF\u80FD\u5C1A\u672A\u751F\u6548\uFF0C\u8BF7\u4F7F\u7528\u53D7\u4FDD\u62A4\u672C\u673A\u8BCA\u65AD\u3002"
352
310
  });
353
311
  var english = Object.freeze({
354
- "resources.previous": "Previous",
355
- "resources.next": "Next",
356
312
  "resources.tokens": "MCP tokens",
357
- "resources.pairings": "Pairing records",
313
+ "resources.pairingCodes": "Pairing codes",
314
+ "resources.createCode": "Generate code",
315
+ "resources.codesNote": "One code per device: run the displayed join command on the target machine to pair. Unused codes can be revoked anytime.",
316
+ "resources.codeCreatedNote": "Code created. Copy the command below and run it on the Connector machine.",
317
+ "resources.code_unused": "Unused",
318
+ "resources.code_used": "Used",
319
+ "resources.code_revoked": "Revoked",
320
+ "resources.code_failed": "Failed",
321
+ "resources.revokeCode": "Revoke",
322
+ "resources.confirmRevokeCode": "Revoke this pairing code? Generate a new one for the device.",
358
323
  "resources.name": "Name",
359
324
  "resources.expiry": "Valid for",
360
325
  "resources.days": "{count} days",
@@ -375,17 +340,12 @@ var english = Object.freeze({
375
340
  "resources.deleteDeviceNote": "Remove this device, runtime configuration, pairing records and call content, and disconnect it.",
376
341
  "resources.confirmDevice": "Delete this device and its pairing and call records? Pair it again to reconnect.",
377
342
  "resources.confirmToken": "Delete token \u201C{name}\u201D? Clients using it will lose access.",
378
- "resources.confirmPairing": "Delete this pairing record? Unfinished pairing will stop. Paired devices are kept.",
379
343
  "resources.emptyTokens": "No MCP tokens yet. Create one above to connect your AI client.",
380
- "resources.emptyPairings": "No pairing records.",
381
344
  "resources.loading": "Loading\u2026",
382
345
  "resources.saving": "Saving\u2026",
383
346
  "resources.saved": "Saved",
384
347
  "resources.failed": "The operation did not complete. Refresh and try again.",
385
348
  "resources.invalid": "Use a unique name (letters, numbers, - or _) and a valid expiry.",
386
- "resources.pending": "Pending",
387
- "resources.paired": "Paired",
388
- "resources.cancelled": "Cancelled",
389
349
  "nav.workspace": "WORKSPACE",
390
350
  "nav.overview": "Overview",
391
351
  "nav.devices": "Devices",
@@ -404,11 +364,6 @@ var english = Object.freeze({
404
364
  "overview.attention": "Needs attention",
405
365
  "overview.attentionNote": "Devices without a verified connection",
406
366
  "overview.scope": "Device counts cover this page only. Expired observations do not establish current state.",
407
- "overview.gateway": "Gateway health",
408
- "overview.available": "Available",
409
- "overview.degraded": "Degraded",
410
- "overview.unavailable": "Unavailable",
411
- "overview.unverified": "Unverified",
412
367
  "overview.store": "Storage",
413
368
  "overview.transport": "Device transport",
414
369
  "overview.native": "Native runtime",
@@ -423,23 +378,16 @@ var english = Object.freeze({
423
378
  "devices.results": "Showing {shown} of {count} devices on this page",
424
379
  "devices.noMatches": "No matching devices",
425
380
  "devices.noMatchesHint": "Try another name, ID or label, or change the status filter.",
426
- "devices.inspect": "View details",
427
381
  "devices.record": "Observation details",
428
382
  "devices.open": "View details for {name}",
383
+ "overview.available": "Available",
384
+ "overview.degraded": "Degraded",
385
+ "overview.unavailable": "Unavailable",
386
+ "overview.unverified": "Unverified",
429
387
  "pairing.pageTitle": "Bring a new device on board",
430
- "pairing.pageDescription": "Create an application, run Connector, and verify the code. Your workspace is three steps away.",
431
- "pairing.stepCreate": "Create an application",
432
- "pairing.stepRun": "Run Connector",
433
- "pairing.stepApprove": "Verify and approve",
434
- "pairing.formTitle": "Add your workstation",
435
- "pairing.namePlaceholder": "e.g. office-workstation",
436
- "pairing.nameHint": "Use 1\u201363 lowercase letters, digits, dots, underscores or hyphens.",
437
- "pairing.advanced": "Advanced: pairing policy",
438
- "pairing.guideTitle": "Then, on your workstation",
439
- "pairing.guideDescription": "Run this command on the device with Luvus or Herdr installed. Enter the Gateway address and application ID when prompted.",
440
- "pairing.guideNote": "Match the code on this page with the terminal, then approve local native installation access.",
388
+ "pairing.pageDescription": "Generate a per-device code and run the join command on the target machine. No applications, approvals or verification codes.",
441
389
  "pairing.loginRequired": "A management session is required",
442
- "pairing.loginHint": "Sign in through the Gateway management entry to create applications and approve new devices.",
390
+ "pairing.loginHint": "Sign in through the Gateway management entry to generate pairing codes.",
443
391
  "connect.title": "Connect your workspace to AI",
444
392
  "connect.description": "Once a device is paired, issue a dedicated MCP Bearer for your AI client.",
445
393
  "connect.stepOne": "Find an existing caller",
@@ -451,7 +399,6 @@ var english = Object.freeze({
451
399
  "connect.docker": "Docker deployment commands",
452
400
  "connect.docs": "Read the connection guide",
453
401
  "copy.action": "Copy command",
454
- "copy.id": "Copy application ID",
455
402
  "copy.done": "Copied",
456
403
  "copy.failed": "Copy unavailable; select the text manually",
457
404
  "empty.title": "Start with your first device",
@@ -468,11 +415,9 @@ var english = Object.freeze({
468
415
  "app.footer": "Rovloom \xB7 Gateway console",
469
416
  "preference.switch": "Switch to {value}",
470
417
  "language.label": "Language",
471
- "language.system": "Browser language",
472
418
  "language.chinese": "\u7B80\u4F53\u4E2D\u6587",
473
419
  "language.english": "English",
474
420
  "theme.label": "Theme",
475
- "theme.system": "System theme",
476
421
  "theme.light": "Light",
477
422
  "theme.dark": "Dark",
478
423
  "manage.heading": "Device management",
@@ -503,28 +448,6 @@ var english = Object.freeze({
503
448
  "manage.revokeEffect": "After durable revocation, that identity cannot reconnect. The current durable receipt determines the outcome; native work may continue.",
504
449
  "manage.nativeEffect": "This operation does not guarantee stopping or rolling back native work.",
505
450
  "manage.action": "Action",
506
- "pairing.title": "Add a device online",
507
- "pairing.intro": "Give your workstation a name. Create an application, then follow the steps on the right.",
508
- "pairing.name": "New device name",
509
- "pairing.policy": "Authorization policy reference",
510
- "pairing.create": "Create pairing application",
511
- "pairing.preparing": "Preparing the background confirmation\u2026",
512
- "pairing.review": "Review the background summary below; nothing is submitted until you confirm.",
513
- "pairing.confirm": "Confirm",
514
- "pairing.cancel": "Cancel",
515
- "pairing.created": "Application created. Give this pairing ID to the new device:",
516
- "pairing.waitingClaim": "Waiting for the device to submit its claim\u2026",
517
- "pairing.claimArrived": "The device submitted a claim. Approve issuance only if the check code matches the device screen:",
518
- "pairing.userCode": "Check code",
519
- "pairing.approve": "Approve credential issuance",
520
- "pairing.approved": "Approved. The credential is held for delivery; waiting for the device to collect it\u2026",
521
- "pairing.paired": "The device collected its credential. Pairing complete.",
522
- "pairing.activated": "Credential collected and native activation complete. Ready to use.",
523
- "pairing.statusPending": "Awaiting claim",
524
- "pairing.expired": "This application expired; create a new one.",
525
- "pairing.failed": "The operation did not complete; prepare it again.",
526
- "pairing.application": "Pairing ID",
527
- "pairing.machine": "Device",
528
451
  "manage.machine": "Target device",
529
452
  "manage.epoch": "Connection epoch",
530
453
  "manage.revision": "Resource revision",
@@ -557,6 +480,15 @@ var english = Object.freeze({
557
480
  "error.timeout": "The read exceeded 5 seconds. The displayed state is not live.",
558
481
  "error.cancelled": "This read was cancelled.",
559
482
  "time.noObservation": "No trusted observation",
483
+ "reason.auth_failed": "Authentication failed",
484
+ "reason.backend_unavailable": "Backend unavailable",
485
+ "reason.binding_mismatch": "Binding mismatch",
486
+ "reason.connection_closed": "Connection closed",
487
+ "reason.credential_expired": "Credential expired",
488
+ "reason.heartbeat_timeout": "Heartbeat timed out",
489
+ "reason.not_recorded": "Not recorded",
490
+ "reason.protocol_error": "Protocol error",
491
+ "reason.stale": "Stale",
560
492
  "time.noHeartbeat": "No trusted heartbeat",
561
493
  "time.observed": "{time} \xB7 {age}{expired}",
562
494
  "time.expiredSuffix": " \xB7 expired",
@@ -588,15 +520,6 @@ var english = Object.freeze({
588
520
  "persisted.offline": "Registered offline (offline)",
589
521
  "persisted.online": "Registered online (online)",
590
522
  "persisted.revoked": "Identity revoked (revoked)",
591
- "reason.backend_unavailable": "Backend unavailable",
592
- "reason.not_recorded": "Not recorded",
593
- "reason.stale": "Observation expired",
594
- "reason.auth_failed": "Authentication failed",
595
- "reason.credential_expired": "Credential expired",
596
- "reason.heartbeat_timeout": "Heartbeat timed out",
597
- "reason.protocol_error": "Protocol error",
598
- "reason.connection_closed": "Connection closed",
599
- "reason.binding_mismatch": "Binding mismatch",
600
523
  "device.expired": "Device observation expired",
601
524
  "device.revokedHistory": "Last stored registration: identity revoked (expired)",
602
525
  "device.oldRecords": "Old connection and authentication records cannot restore authorization",
@@ -625,10 +548,6 @@ var english = Object.freeze({
625
548
  "devices.health": "Connection & authentication",
626
549
  "devices.activity": "Last heartbeat",
627
550
  "devices.received": "Gateway received {time}",
628
- "devices.observations": "Observations",
629
- "devices.emptyHeading": "No devices to display",
630
- "devices.empty": "There are no devices in this scope. Create a pairing request through the protected local management channel, then refresh.",
631
- "devices.heartbeat": "Last heartbeat: {age} \xB7 Gateway received {time}",
632
551
  "devices.observed": "Device state observed: {time}",
633
552
  "devices.connectionObserved": "Connection observed: {time}",
634
553
  "devices.authObserved": "Authentication observed: {time}",
@@ -1728,49 +1647,6 @@ function checkDeviceIntent(value) {
1728
1647
  summary({ ...operation, recovery_generation: "validation", expected_revision: 0, reason: "administrator_request" }, "invalid_request");
1729
1648
  return JSON.parse(JSON.stringify(value));
1730
1649
  }
1731
- function checkPairingIntent(value) {
1732
- record(value, ["action"], ["machine_id", "name", "policy_ref", "labels", "pairing_id", "claim_id", "user_code"], "invalid_request");
1733
- const action = value.action;
1734
- if (action === "pairing_create") {
1735
- const hasName = Object.hasOwn(value, "name");
1736
- const keys = hasName ? ["action", "name", "policy_ref", "labels"] : ["action", "machine_id", "policy_ref"];
1737
- record(value, keys, [], "invalid_request");
1738
- const intent = value;
1739
- const operation = intent.action === "pairing_create" && "name" in intent ? {
1740
- action: "pairing_create",
1741
- target: { name: intent.name, policy_ref: intent.policy_ref, labels: intent.labels }
1742
- } : {
1743
- action: "pairing_create",
1744
- target: {
1745
- machine_id: intent.machine_id,
1746
- policy_ref: intent.policy_ref
1747
- }
1748
- };
1749
- summary({ ...operation, recovery_generation: "validation", expected_revision: 0, reason: "administrator_request" }, "invalid_request");
1750
- return JSON.parse(JSON.stringify(value));
1751
- }
1752
- if (action === "pairing_claim_approve") {
1753
- record(value, ["action", "machine_id", "pairing_id", "claim_id", "user_code"], [], "invalid_request");
1754
- const intent = value;
1755
- if (intent.action !== "pairing_claim_approve") {
1756
- invalid("invalid_request");
1757
- }
1758
- summary({
1759
- action: "pairing_claim_approve",
1760
- target: {
1761
- machine_id: intent.machine_id,
1762
- pairing_id: intent.pairing_id,
1763
- claim_id: intent.claim_id,
1764
- user_code: intent.user_code
1765
- },
1766
- recovery_generation: "validation",
1767
- expected_revision: 0,
1768
- reason: "administrator_request"
1769
- }, "invalid_request");
1770
- return JSON.parse(JSON.stringify(value));
1771
- }
1772
- invalid("invalid_request");
1773
- }
1774
1650
  function pairingSummary(v, problem) {
1775
1651
  const target = v.target;
1776
1652
  if (v.action === "pairing_create") {
@@ -2720,683 +2596,6 @@ function createDeviceActions(options) {
2720
2596
  };
2721
2597
  }
2722
2598
 
2723
- // src/webui/pairing-flow.ts
2724
- function snapshot2(value) {
2725
- const copy = JSON.parse(JSON.stringify(value));
2726
- function freeze(item) {
2727
- if (item && typeof item === "object") {
2728
- for (const child of Object.values(item)) {
2729
- freeze(child);
2730
- }
2731
- Object.freeze(item);
2732
- }
2733
- }
2734
- freeze(copy);
2735
- return copy;
2736
- }
2737
- function failure2(error) {
2738
- return error instanceof ManagementClientError ? error.problem : "unavailable";
2739
- }
2740
- function operationOf(intent) {
2741
- if (intent.action === "pairing_claim_approve") {
2742
- return {
2743
- action: "pairing_claim_approve",
2744
- target: {
2745
- machine_id: intent.machine_id,
2746
- pairing_id: intent.pairing_id,
2747
- claim_id: intent.claim_id,
2748
- user_code: intent.user_code
2749
- }
2750
- };
2751
- }
2752
- return "name" in intent ? { action: "pairing_create", target: { name: intent.name, policy_ref: intent.policy_ref, labels: intent.labels } } : { action: "pairing_create", target: { machine_id: intent.machine_id, policy_ref: intent.policy_ref } };
2753
- }
2754
- var operationName = {
2755
- pairing_create: "pairings_create",
2756
- pairing_claim_approve: "pairings_claim_approve"
2757
- };
2758
- function createPairingManagementFlow(options) {
2759
- let state = { phase: "idle" };
2760
- let intent;
2761
- let write;
2762
- let review;
2763
- let original;
2764
- let active;
2765
- let generation = 0;
2766
- let disposed = false;
2767
- const requestId = options.requestId ?? managementRequestId;
2768
- async function execute(operation, args, signal) {
2769
- if (signal.aborted) {
2770
- throw new ManagementClientError("aborted");
2771
- }
2772
- managementWriteBody(operation, args);
2773
- const reply = await options.transport.execute(operation, args, signal);
2774
- return checkManagementWriteReply(operation, reply, args);
2775
- }
2776
- function begin(phase, signal) {
2777
- if (disposed || active || state.phase !== phase || signal?.aborted) {
2778
- throw new ManagementClientError("invalid_request");
2779
- }
2780
- const controller = new AbortController;
2781
- active = controller;
2782
- const current = ++generation;
2783
- const abort = () => cancel();
2784
- signal?.addEventListener("abort", abort, { once: true });
2785
- return {
2786
- controller,
2787
- current: () => !disposed && generation === current && !controller.signal.aborted,
2788
- done() {
2789
- signal?.removeEventListener("abort", abort);
2790
- if (active === controller) {
2791
- active = undefined;
2792
- }
2793
- }
2794
- };
2795
- }
2796
- function unconfirmed(problem) {
2797
- if (!original) {
2798
- state = { phase: "failed", problem: problem ?? "unavailable" };
2799
- return;
2800
- }
2801
- state = { phase: "unconfirmed", operation: original, ...problem ? { problem } : {} };
2802
- }
2803
- function accept(receipt) {
2804
- if (!original || !intent || !review || receipt.request_id !== original.request_id || receipt.history_epoch !== original.history_epoch || receipt.recovery_generation !== original.recovery_generation || receipt.action !== intent.action || receipt.operation_digest !== review.operation_digest) {
2805
- throw new ManagementClientError("invalid_response", true);
2806
- }
2807
- if (intent.action === "pairing_claim_approve" && receipt.target.machine_id !== intent.machine_id) {
2808
- throw new ManagementClientError("invalid_response", true);
2809
- }
2810
- if (receipt.evidence !== "current_durable" || receipt.operation_state === "outcome_unknown") {
2811
- unconfirmed("outcome_unconfirmed");
2812
- } else {
2813
- state = receipt.operation_state === "committed" ? { phase: "committed", receipt } : { phase: "not_committed", receipt };
2814
- }
2815
- }
2816
- function cancel() {
2817
- if (!active) {
2818
- if (state.phase === "idle" || state.phase === "awaiting_confirmation") {
2819
- generation++;
2820
- state = { phase: "cancelled" };
2821
- }
2822
- return;
2823
- }
2824
- generation++;
2825
- const controller = active;
2826
- active = undefined;
2827
- const uncertain = state.phase === "submitting" || state.phase === "checking";
2828
- controller.abort();
2829
- if (uncertain) {
2830
- unconfirmed("aborted");
2831
- } else {
2832
- state = { phase: "cancelled" };
2833
- }
2834
- }
2835
- return {
2836
- state() {
2837
- return snapshot2(state);
2838
- },
2839
- async prepare(value, signal) {
2840
- const pending = begin("idle", signal);
2841
- try {
2842
- intent = checkPairingIntent(value);
2843
- state = { phase: "preparing", intent };
2844
- const id = requestId();
2845
- const history = await execute("history_open", {}, pending.controller.signal);
2846
- if (!pending.current()) {
2847
- return snapshot2(state);
2848
- }
2849
- const resource = "machine_id" in intent ? await execute("write_state_get", { machine_id: intent.machine_id }, pending.controller.signal) : {
2850
- resource_revision: 0,
2851
- recovery_generation: history.recovery_generation,
2852
- connection_epoch: null,
2853
- binding_revision: null
2854
- };
2855
- if (!pending.current()) {
2856
- return snapshot2(state);
2857
- }
2858
- if (history.recovery_generation !== resource.recovery_generation) {
2859
- throw new ManagementClientError("conflict");
2860
- }
2861
- const draft = {
2862
- ...operationOf(intent),
2863
- request_id: id,
2864
- history_epoch: history.history_epoch,
2865
- recovery_generation: history.recovery_generation,
2866
- expected_revision: resource.resource_revision,
2867
- reason: "administrator_request"
2868
- };
2869
- const confirmation = await execute("confirmations_create", draft, pending.controller.signal);
2870
- if (!pending.current()) {
2871
- return snapshot2(state);
2872
- }
2873
- review = snapshot2(confirmation);
2874
- write = {
2875
- request_id: id,
2876
- history_epoch: history.history_epoch,
2877
- recovery_generation: history.recovery_generation,
2878
- expected_revision: resource.resource_revision,
2879
- reason: draft.reason,
2880
- confirmation_id: confirmation.confirmation_id
2881
- };
2882
- original = {
2883
- request_id: id,
2884
- history_epoch: history.history_epoch,
2885
- recovery_generation: history.recovery_generation
2886
- };
2887
- if (confirmation.state === "expired") {
2888
- throw new ManagementClientError("conflict");
2889
- }
2890
- if (confirmation.state === "consumed") {
2891
- unconfirmed("outcome_unconfirmed");
2892
- } else {
2893
- state = { phase: "awaiting_confirmation", review };
2894
- }
2895
- } catch (error) {
2896
- if (pending.current()) {
2897
- state = { phase: "failed", problem: failure2(error) };
2898
- }
2899
- } finally {
2900
- pending.done();
2901
- }
2902
- return snapshot2(state);
2903
- },
2904
- async confirm(signal) {
2905
- const pending = begin("awaiting_confirmation", signal);
2906
- try {
2907
- if (!write || !review || !intent) {
2908
- throw new ManagementClientError("invalid_request");
2909
- }
2910
- state = { phase: "confirming", review };
2911
- const confirmed = await execute("confirmations_confirm", { ...write, operation_digest: review.operation_digest }, pending.controller.signal);
2912
- if (!pending.current()) {
2913
- return snapshot2(state);
2914
- }
2915
- if (managementConfirmationKey(confirmed) !== managementConfirmationKey(review)) {
2916
- throw new ManagementClientError("invalid_response");
2917
- }
2918
- if (confirmed.state === "consumed") {
2919
- unconfirmed("outcome_unconfirmed");
2920
- return snapshot2(state);
2921
- }
2922
- if (confirmed.state !== "confirmed") {
2923
- throw new ManagementClientError("conflict");
2924
- }
2925
- const operation = operationName[intent.action];
2926
- const target = intent.action === "pairing_claim_approve" ? {
2927
- machine_id: intent.machine_id,
2928
- pairing_id: intent.pairing_id,
2929
- claim_id: intent.claim_id,
2930
- user_code: intent.user_code
2931
- } : ("name" in intent) ? { name: intent.name, policy_ref: intent.policy_ref, labels: intent.labels } : { machine_id: intent.machine_id, policy_ref: intent.policy_ref };
2932
- state = { phase: "submitting", review };
2933
- const receipt = await execute(operation, { ...write, ...target }, pending.controller.signal);
2934
- if (pending.current()) {
2935
- accept(receipt);
2936
- }
2937
- } catch (error) {
2938
- if (pending.current()) {
2939
- if (state.phase === "submitting") {
2940
- unconfirmed(failure2(error));
2941
- } else {
2942
- state = { phase: "failed", problem: failure2(error) };
2943
- }
2944
- }
2945
- } finally {
2946
- pending.done();
2947
- }
2948
- return snapshot2(state);
2949
- },
2950
- async checkReceipt(signal) {
2951
- const pending = begin("unconfirmed", signal);
2952
- try {
2953
- if (!original) {
2954
- throw new ManagementClientError("invalid_request");
2955
- }
2956
- state = { phase: "checking", operation: original };
2957
- const receipt = await execute("operations_get", original, pending.controller.signal);
2958
- if (pending.current()) {
2959
- accept(receipt);
2960
- }
2961
- } catch (error) {
2962
- if (pending.current()) {
2963
- unconfirmed(failure2(error));
2964
- }
2965
- } finally {
2966
- pending.done();
2967
- }
2968
- return snapshot2(state);
2969
- },
2970
- cancel,
2971
- dispose() {
2972
- cancel();
2973
- disposed = true;
2974
- }
2975
- };
2976
- }
2977
-
2978
- // src/webui/pairing-actions.ts
2979
- var errors2 = {
2980
- invalid_request: "manage.invalid",
2981
- invalid_response: "manage.invalid",
2982
- unauthenticated: "manage.unauthenticated",
2983
- forbidden: "manage.forbidden",
2984
- conflict: "manage.conflict",
2985
- busy: "manage.busy",
2986
- unavailable: "manage.unavailable",
2987
- outcome_unconfirmed: "manage.unknown",
2988
- aborted: "manage.cancelled"
2989
- };
2990
- function entryRenderSignature(language, entry) {
2991
- return [
2992
- language,
2993
- entry.name,
2994
- entry.machine_id,
2995
- entry.pairing_id,
2996
- entry.status,
2997
- entry.claim?.status ?? "",
2998
- entry.claim?.user_code ?? "",
2999
- entry.claim?.activation ?? ""
3000
- ].join("|");
3001
- }
3002
- function node3(tag, className = "") {
3003
- const element = document.createElement(tag);
3004
- element.className = className;
3005
- return element;
3006
- }
3007
- function createPairingActions(options) {
3008
- const region = node3("section", "pairing-section");
3009
- const steps = node3("ol", "pairing-steps");
3010
- const stepItems = ["pairing.stepCreate", "pairing.stepRun", "pairing.stepApprove"].map((key, index) => {
3011
- const item = node3("li");
3012
- const number = node3("span", "pairing-step-number");
3013
- number.textContent = String(index + 1).padStart(2, "0");
3014
- const label = node3("span");
3015
- item.append(number, label);
3016
- steps.append(item);
3017
- return { item, label, key };
3018
- });
3019
- const layout = node3("div", "pairing-layout");
3020
- const setup = node3("div", "pairing-setup");
3021
- const heading = node3("h2");
3022
- const intro = node3("p", "muted");
3023
- const form = node3("form", "pairing-form");
3024
- const nameLabel = node3("label");
3025
- const nameCaption = node3("span");
3026
- const nameInput = node3("input");
3027
- nameInput.name = "pairing-name";
3028
- nameInput.required = true;
3029
- nameInput.maxLength = 63;
3030
- nameInput.pattern = "[a-z0-9][a-z0-9._-]{0,62}";
3031
- nameInput.autocomplete = "off";
3032
- const nameHint = node3("small", "field-hint");
3033
- nameHint.id = "pairing-name-hint";
3034
- nameInput.setAttribute("aria-describedby", nameHint.id);
3035
- nameLabel.append(nameCaption, nameInput, nameHint);
3036
- const policyLabel = node3("label");
3037
- const policyCaption = node3("span");
3038
- const policyInput = node3("input");
3039
- policyInput.name = "pairing-policy";
3040
- policyInput.required = true;
3041
- policyInput.maxLength = 128;
3042
- policyInput.autocomplete = "off";
3043
- policyInput.value = DEFAULT_PAIRING_POLICY_REF;
3044
- policyLabel.append(policyCaption, policyInput);
3045
- const create = node3("button", "pairing-confirm");
3046
- create.type = "submit";
3047
- const advanced = node3("details", "pairing-advanced");
3048
- const advancedSummary = node3("summary");
3049
- advanced.append(advancedSummary, policyLabel);
3050
- form.append(nameLabel, advanced, create);
3051
- const reviewBox = node3("div", "pairing-review");
3052
- reviewBox.hidden = true;
3053
- const reviewText = node3("p", "pairing-status");
3054
- const reviewFacts = node3("dl", "facts pairing-facts");
3055
- const controls = node3("div", "pairing-controls");
3056
- const confirm = node3("button", "pairing-confirm");
3057
- const check = node3("button");
3058
- const cancel = node3("button");
3059
- for (const button of [confirm, check, cancel]) {
3060
- button.type = "button";
3061
- }
3062
- controls.append(confirm, check, cancel);
3063
- reviewBox.append(reviewText, reviewFacts, controls);
3064
- const message = node3("p", "pairing-status");
3065
- message.setAttribute("role", "status");
3066
- message.setAttribute("aria-live", "polite");
3067
- const list = node3("ul", "pairing-entries");
3068
- setup.append(heading, intro, form, reviewBox, message);
3069
- const guide = node3("aside", "pairing-guide");
3070
- const guideIcon = node3("span", "pairing-guide-icon");
3071
- guideIcon.append(uiIcon("connect"));
3072
- const guideHeading = node3("h2");
3073
- const guideDescription = node3("p", "muted");
3074
- const command = node3("div", "command-snippet");
3075
- const commandCode = node3("code");
3076
- commandCode.textContent = "rovloom connector --claim";
3077
- const commandCopy = copyControl("rovloom connector --claim", "");
3078
- command.append(commandCode, commandCopy);
3079
- const guideNote = node3("p", "pairing-guide-note");
3080
- guide.append(guideIcon, guideHeading, guideDescription, command, guideNote);
3081
- layout.append(setup, guide);
3082
- region.append(steps, layout, list);
3083
- let i18n;
3084
- let available = false;
3085
- let disposed = false;
3086
- let flow;
3087
- let flowIntent;
3088
- let timer;
3089
- const entries = new Map;
3090
- const pollMs = options.pollMs ?? 4000;
3091
- function fact(term, value) {
3092
- const dt = node3("dt");
3093
- dt.textContent = term;
3094
- const dd = node3("dd");
3095
- dd.textContent = value;
3096
- reviewFacts.append(dt, dd);
3097
- }
3098
- function paintFlow(state) {
3099
- if (!i18n) {
3100
- return;
3101
- }
3102
- const t = i18n.t.bind(i18n);
3103
- check.hidden = true;
3104
- confirm.hidden = true;
3105
- if (state.phase === "idle" || state.phase === "cancelled") {
3106
- reviewBox.hidden = true;
3107
- return;
3108
- }
3109
- reviewBox.hidden = false;
3110
- if (state.phase === "preparing" || state.phase === "confirming" || state.phase === "submitting") {
3111
- reviewText.textContent = t("pairing.preparing");
3112
- return;
3113
- }
3114
- if (state.phase === "awaiting_confirmation") {
3115
- reviewText.textContent = t("pairing.review");
3116
- reviewFacts.replaceChildren();
3117
- const summary = state.review.summary;
3118
- fact(t("manage.action"), t(summary.action === "pairing_create" ? "pairing.create" : "pairing.approve"));
3119
- if (summary.action === "pairing_create") {
3120
- fact(t("pairing.name"), "name" in summary.target ? summary.target.name : summary.target.machine_id);
3121
- fact(t("pairing.policy"), summary.target.policy_ref);
3122
- } else if (summary.action === "pairing_claim_approve") {
3123
- fact(t("pairing.machine"), summary.target.machine_id);
3124
- fact(t("pairing.userCode"), summary.target.user_code);
3125
- }
3126
- fact(t("manage.expires"), new Date(state.review.expires_at).toISOString());
3127
- confirm.hidden = false;
3128
- confirm.textContent = t("pairing.confirm");
3129
- return;
3130
- }
3131
- if (state.phase === "committed") {
3132
- reviewBox.hidden = true;
3133
- const receipt = state.receipt;
3134
- if (state.receipt.action === "pairing_create" && receipt.pairing_id && receipt.machine_id) {
3135
- const name = flowIntent?.action === "pairing_create" && "name" in flowIntent ? flowIntent.name : entries.get(receipt.pairing_id)?.name ?? receipt.machine_id;
3136
- addEntry({
3137
- name,
3138
- machine_id: receipt.machine_id,
3139
- pairing_id: receipt.pairing_id,
3140
- status: "pending",
3141
- item: node3("li", "pairing-entry")
3142
- });
3143
- message.textContent = t("pairing.created");
3144
- } else if (state.receipt.action === "pairing_claim_approve") {
3145
- message.textContent = t("pairing.approved");
3146
- }
3147
- flow = undefined;
3148
- flowIntent = undefined;
3149
- ensurePolling();
3150
- return;
3151
- }
3152
- if (state.phase === "not_committed") {
3153
- reviewText.textContent = t("manage.notCommitted");
3154
- flow = undefined;
3155
- flowIntent = undefined;
3156
- return;
3157
- }
3158
- if (state.phase === "unconfirmed") {
3159
- reviewText.textContent = t("manage.unknown");
3160
- check.hidden = false;
3161
- check.textContent = t("manage.check");
3162
- return;
3163
- }
3164
- if (state.phase === "failed") {
3165
- reviewText.textContent = t(errors2[state.problem]);
3166
- if (state.problem === "unauthenticated" || state.problem === "forbidden") {
3167
- options.authorizationLost(state.problem);
3168
- }
3169
- flow = undefined;
3170
- flowIntent = undefined;
3171
- return;
3172
- }
3173
- reviewBox.hidden = true;
3174
- }
3175
- async function start(intent) {
3176
- if (disposed || !available || flow) {
3177
- return;
3178
- }
3179
- flowIntent = intent;
3180
- flow = createPairingManagementFlow({ transport: options.transport });
3181
- paintFlow(await flow.prepare(intent));
3182
- }
3183
- function renderEntry(entry) {
3184
- if (!i18n) {
3185
- return;
3186
- }
3187
- const signature = entryRenderSignature(i18n.language, entry);
3188
- if (entry.rendered === signature) {
3189
- return;
3190
- }
3191
- entry.rendered = signature;
3192
- const t = i18n.t.bind(i18n);
3193
- entry.item.replaceChildren();
3194
- const head = node3("div", "pairing-entry-head");
3195
- const title = node3("strong");
3196
- title.textContent = entry.name;
3197
- const status = node3("span", "muted");
3198
- status.textContent = entry.claim?.activation === "configured" ? t("pairing.activated") : entry.status === "paired" ? t("pairing.paired") : entry.status === "failed" ? t("pairing.failed") : entry.status === "expired" || entry.status === "cancelled" ? t("pairing.expired") : entry.claim?.status === "approved" ? t("pairing.approved") : entry.claim ? t("pairing.claimArrived") : t("pairing.waitingClaim");
3199
- head.append(title, status);
3200
- entry.item.append(head);
3201
- status.classList.add("badge");
3202
- const facts = node3("dl", "facts");
3203
- const idTerm = node3("dt");
3204
- idTerm.textContent = t("pairing.application");
3205
- const idValue = node3("dd");
3206
- const idCode = node3("code");
3207
- idCode.textContent = entry.pairing_id;
3208
- idValue.append(idCode, copyControl(entry.pairing_id, t("copy.id")));
3209
- const machineTerm = node3("dt");
3210
- machineTerm.textContent = t("pairing.machine");
3211
- const machineValue = node3("dd");
3212
- machineValue.textContent = entry.machine_id;
3213
- facts.append(idTerm, idValue, machineTerm, machineValue);
3214
- entry.item.append(facts);
3215
- updateSteps();
3216
- if (entry.status === "pending" && entry.claim?.status === "pending") {
3217
- const codeLabel = node3("p", "muted");
3218
- codeLabel.textContent = t("pairing.userCode");
3219
- const code = node3("div", "pairing-code");
3220
- code.textContent = entry.claim.user_code;
3221
- const approve = node3("button", "pairing-confirm");
3222
- approve.type = "button";
3223
- approve.textContent = t("pairing.approve");
3224
- const claim = entry.claim;
3225
- approve.addEventListener("click", () => {
3226
- start({
3227
- action: "pairing_claim_approve",
3228
- machine_id: entry.machine_id,
3229
- pairing_id: entry.pairing_id,
3230
- claim_id: claim.claim_id,
3231
- user_code: claim.user_code
3232
- });
3233
- });
3234
- entry.item.append(codeLabel, code, approve);
3235
- }
3236
- }
3237
- function updateSteps() {
3238
- const entry = [...entries.values()].at(-1);
3239
- const finished = entry?.claim?.activation === "configured" || entry?.status === "paired" && !entry.claim;
3240
- const stage = !entry || ["expired", "failed", "cancelled"].includes(entry.status) ? 0 : finished ? 3 : entry.claim ? 2 : 1;
3241
- for (const [index, step] of stepItems.entries()) {
3242
- step.item.dataset.state = index < stage ? "complete" : index === stage ? "current" : "upcoming";
3243
- if (index === stage) {
3244
- step.item.setAttribute("aria-current", "step");
3245
- } else {
3246
- step.item.removeAttribute("aria-current");
3247
- }
3248
- }
3249
- }
3250
- function addEntry(entry) {
3251
- const existing = entries.get(entry.pairing_id);
3252
- if (existing) {
3253
- existing.status = entry.status;
3254
- renderEntry(existing);
3255
- return;
3256
- }
3257
- entries.set(entry.pairing_id, entry);
3258
- options.committed?.();
3259
- list.append(entry.item);
3260
- renderEntry(entry);
3261
- }
3262
- function unfinished(entry) {
3263
- return entry.status === "pending" || entry.status === "paired" && entry.claim?.status === "delivered";
3264
- }
3265
- async function poll() {
3266
- for (const entry of entries.values()) {
3267
- if (disposed || !unfinished(entry)) {
3268
- continue;
3269
- }
3270
- try {
3271
- const args = { machine_id: entry.machine_id, pairing_id: entry.pairing_id };
3272
- managementWriteBody("pairings_get", args);
3273
- const reply = await options.transport.execute("pairings_get", args, new AbortController().signal);
3274
- const status = checkManagementWriteReply("pairings_get", reply, args);
3275
- entry.status = status.status;
3276
- entry.claim = status.claim;
3277
- renderEntry(entry);
3278
- } catch (error) {
3279
- if (error instanceof ManagementClientError && (error.problem === "unauthenticated" || error.problem === "forbidden")) {
3280
- stopPolling();
3281
- options.authorizationLost(error.problem);
3282
- return;
3283
- }
3284
- }
3285
- }
3286
- if (![...entries.values()].some(unfinished)) {
3287
- stopPolling();
3288
- }
3289
- }
3290
- function ensurePolling() {
3291
- if (disposed || timer || ![...entries.values()].some(unfinished)) {
3292
- return;
3293
- }
3294
- timer = setInterval(() => {
3295
- poll();
3296
- }, pollMs);
3297
- }
3298
- function stopPolling() {
3299
- if (timer) {
3300
- clearInterval(timer);
3301
- timer = undefined;
3302
- }
3303
- }
3304
- form.addEventListener("submit", (event) => {
3305
- event.preventDefault();
3306
- const name = nameInput.value.trim();
3307
- const policy = policyInput.value.trim();
3308
- if (!name || !policy) {
3309
- return;
3310
- }
3311
- start({ action: "pairing_create", name, policy_ref: policy, labels: [] });
3312
- });
3313
- confirm.addEventListener("click", () => {
3314
- if (flow) {
3315
- flow.confirm().then(paintFlow);
3316
- }
3317
- });
3318
- check.addEventListener("click", () => {
3319
- if (flow) {
3320
- flow.checkReceipt().then(paintFlow);
3321
- }
3322
- });
3323
- cancel.addEventListener("click", () => {
3324
- flow?.cancel();
3325
- if (flow) {
3326
- paintFlow(flow.state());
3327
- }
3328
- if (flow?.state().phase === "cancelled") {
3329
- flow = undefined;
3330
- flowIntent = undefined;
3331
- }
3332
- });
3333
- return {
3334
- region,
3335
- update(localizer, isAvailable) {
3336
- i18n = localizer;
3337
- available = isAvailable;
3338
- region.hidden = !isAvailable;
3339
- const t = localizer.t.bind(localizer);
3340
- heading.textContent = t("pairing.formTitle");
3341
- for (const step of stepItems) {
3342
- step.label.textContent = t(step.key);
3343
- }
3344
- updateSteps();
3345
- nameInput.placeholder = t("pairing.namePlaceholder");
3346
- nameHint.textContent = t("pairing.nameHint");
3347
- advancedSummary.textContent = t("pairing.advanced");
3348
- guideHeading.textContent = t("pairing.guideTitle");
3349
- guideDescription.textContent = t("pairing.guideDescription");
3350
- guideNote.textContent = t("pairing.guideNote");
3351
- commandCopy.setAttribute("aria-label", t("copy.action"));
3352
- commandCopy.title = t("copy.action");
3353
- intro.textContent = t("pairing.intro");
3354
- nameCaption.textContent = t("pairing.name");
3355
- policyCaption.textContent = t("pairing.policy");
3356
- create.textContent = t("pairing.create");
3357
- cancel.textContent = t("pairing.cancel");
3358
- for (const entry of entries.values()) {
3359
- renderEntry(entry);
3360
- }
3361
- if (flow) {
3362
- paintFlow(flow.state());
3363
- }
3364
- if (isAvailable) {
3365
- ensurePolling();
3366
- }
3367
- },
3368
- suspend() {
3369
- stopPolling();
3370
- },
3371
- remove(pairingId) {
3372
- entries.get(pairingId)?.item.remove();
3373
- entries.delete(pairingId);
3374
- if (flowIntent && "pairing_id" in flowIntent && flowIntent.pairing_id === pairingId) {
3375
- flow?.dispose();
3376
- flow = undefined;
3377
- flowIntent = undefined;
3378
- reviewBox.hidden = true;
3379
- }
3380
- updateSteps();
3381
- },
3382
- clear() {
3383
- stopPolling();
3384
- flow?.dispose();
3385
- flow = undefined;
3386
- flowIntent = undefined;
3387
- entries.clear();
3388
- list.replaceChildren();
3389
- reviewBox.hidden = true;
3390
- message.textContent = "";
3391
- },
3392
- dispose() {
3393
- disposed = true;
3394
- stopPolling();
3395
- flow?.dispose();
3396
- }
3397
- };
3398
- }
3399
-
3400
2599
  // src/webui/resource-client.ts
3401
2600
  class ResourceError extends Error {
3402
2601
  code;
@@ -3407,15 +2606,15 @@ class ResourceError extends Error {
3407
2606
  }
3408
2607
 
3409
2608
  // src/webui/resource-panel.ts
3410
- function node4(tag, text = "", className = "") {
2609
+ function node3(tag, text = "", className = "") {
3411
2610
  const element = document.createElement(tag);
3412
2611
  element.textContent = text;
3413
2612
  element.className = className;
3414
2613
  return element;
3415
2614
  }
3416
2615
  function createResourcePanel(options) {
3417
- const region = node4("section", "", "resource-panel");
3418
- const feedback = node4("p", "", "resource-feedback");
2616
+ const region = node3("section", "", "resource-panel");
2617
+ const feedback = node3("p", "", "resource-feedback");
3419
2618
  feedback.setAttribute("role", "status");
3420
2619
  let i18n;
3421
2620
  let key = "";
@@ -3423,14 +2622,15 @@ function createResourcePanel(options) {
3423
2622
  let pending = false;
3424
2623
  let controller = new AbortController;
3425
2624
  let data;
3426
- let cursor = 0;
3427
2625
  let tokenName = "";
3428
2626
  let tokenDays = "365";
2627
+ let codeName = "";
2628
+ let created;
3429
2629
  let version = 0;
3430
2630
  let route;
3431
2631
  let language = "";
3432
2632
  function button(label, action, danger = false) {
3433
- const result = node4("button", label, danger ? "danger-button" : "");
2633
+ const result = node3("button", label, danger ? "danger-button" : "");
3434
2634
  result.type = "button";
3435
2635
  result.disabled = pending;
3436
2636
  result.addEventListener("click", action);
@@ -3454,7 +2654,7 @@ function createResourcePanel(options) {
3454
2654
  pending = true;
3455
2655
  feedback.textContent = i18n.t("resources.loading");
3456
2656
  try {
3457
- const value = await request(key === "connect" ? "tokens_list" : "pairings_list", key === "connect" ? {} : { cursor });
2657
+ const value = await request(key === "connect" ? "tokens_list" : "pairing_codes_list", key === "connect" ? {} : {});
3458
2658
  if (generation !== version || !active) {
3459
2659
  return;
3460
2660
  }
@@ -3483,7 +2683,10 @@ function createResourcePanel(options) {
3483
2683
  feedback.textContent = i18n.t("resources.saving");
3484
2684
  render();
3485
2685
  try {
3486
- await request(operation, args);
2686
+ const reply = operation === "pairing_codes_create" ? await request(operation, args) : await request(operation, args);
2687
+ if (operation === "pairing_codes_create" && reply && typeof reply.code === "string") {
2688
+ created = reply;
2689
+ }
3487
2690
  if (generation !== version || !active) {
3488
2691
  return;
3489
2692
  }
@@ -3492,6 +2695,9 @@ function createResourcePanel(options) {
3492
2695
  tokenName = "";
3493
2696
  tokenDays = "365";
3494
2697
  }
2698
+ if (operation === "pairing_codes_create") {
2699
+ codeName = "";
2700
+ }
3495
2701
  if (operation === "devices_delete") {
3496
2702
  options.committed(route.machineId ?? undefined);
3497
2703
  } else {
@@ -3521,21 +2727,21 @@ function createResourcePanel(options) {
3521
2727
  return;
3522
2728
  }
3523
2729
  const { t } = i18n;
3524
- const body = node4("div", "", "resource-body");
2730
+ const body = node3("div", "", "resource-body");
3525
2731
  if (key === "device") {
3526
- const card = node4("article", "", "resource-delete-card");
3527
- card.append(node4("div", t("resources.deleteDeviceNote"), "muted"), button(t("resources.deleteDevice"), () => remove("devices_delete", { machine_id: route.machineId }, t("resources.confirmDevice")), true));
2732
+ const card = node3("article", "", "resource-delete-card");
2733
+ card.append(node3("div", t("resources.deleteDeviceNote"), "muted"), button(t("resources.deleteDevice"), () => remove("devices_delete", { machine_id: route.machineId }, t("resources.confirmDevice")), true));
3528
2734
  body.append(card);
3529
2735
  } else {
3530
- const heading = node4("div", "", "section-heading");
3531
- heading.append(node4("h2", t(key === "connect" ? "resources.tokens" : "resources.pairings")), button(t("action.refresh"), () => {
2736
+ const heading = node3("div", "", "section-heading");
2737
+ heading.append(node3("h2", t(key === "connect" ? "resources.tokens" : "resources.pairingCodes")), button(t("action.refresh"), () => {
3532
2738
  refresh();
3533
2739
  }));
3534
2740
  body.append(heading);
3535
2741
  if (key === "connect") {
3536
- const form = node4("form", "", "token-create-form");
3537
- const nameLabel = node4("label", t("resources.name"));
3538
- const name = node4("input");
2742
+ const form = node3("form", "", "token-create-form");
2743
+ const nameLabel = node3("label", t("resources.name"));
2744
+ const name = node3("input");
3539
2745
  name.name = "token-name";
3540
2746
  name.required = true;
3541
2747
  name.pattern = "[A-Za-z0-9_-]{1,128}";
@@ -3547,12 +2753,12 @@ function createResourcePanel(options) {
3547
2753
  tokenName = name.value;
3548
2754
  });
3549
2755
  nameLabel.append(name);
3550
- const daysLabel = node4("label", t("resources.expiry"));
3551
- const days = node4("select");
2756
+ const daysLabel = node3("label", t("resources.expiry"));
2757
+ const days = node3("select");
3552
2758
  days.name = "token-days";
3553
2759
  days.disabled = pending;
3554
2760
  for (const value of [365, 90, 30, 7]) {
3555
- const option = node4("option", t("resources.days", { count: value }));
2761
+ const option = node3("option", t("resources.days", { count: value }));
3556
2762
  option.value = String(value);
3557
2763
  days.append(option);
3558
2764
  }
@@ -3561,7 +2767,7 @@ function createResourcePanel(options) {
3561
2767
  tokenDays = days.value;
3562
2768
  });
3563
2769
  daysLabel.append(days);
3564
- const submit = node4("button", t("resources.create"), "primary-button");
2770
+ const submit = node3("button", t("resources.create"), "primary-button");
3565
2771
  submit.type = "submit";
3566
2772
  submit.disabled = pending;
3567
2773
  form.append(nameLabel, daysLabel, submit);
@@ -3569,21 +2775,21 @@ function createResourcePanel(options) {
3569
2775
  event.preventDefault();
3570
2776
  mutate("tokens_create", { principal: name.value.trim(), days: Number(days.value) });
3571
2777
  });
3572
- body.append(node4("p", t("resources.tokenNote"), "muted"), form);
2778
+ body.append(node3("p", t("resources.tokenNote"), "muted"), form);
3573
2779
  if (data?.resource) {
3574
- const endpoint = node4("div", "", "mcp-endpoint");
3575
- endpoint.append(node4("span", t("resources.endpoint")), node4("code", data.resource), copyControl(data.resource, t("copy.action")));
3576
- body.append(endpoint, node4("p", t("resources.endpointNote"), "muted"));
2780
+ const endpoint = node3("div", "", "mcp-endpoint");
2781
+ endpoint.append(node3("span", t("resources.endpoint")), node3("code", data.resource), copyControl(data.resource, t("copy.action")));
2782
+ body.append(endpoint, node3("p", t("resources.endpointNote"), "muted"));
3577
2783
  }
3578
2784
  for (const item of data?.items ?? []) {
3579
2785
  const token = item;
3580
- const card = node4("article", "", "token-card");
2786
+ const card = node3("article", "", "token-card");
3581
2787
  card.dataset.tokenPrincipal = token.principal;
3582
- const summary = node4("div", "", "token-summary");
3583
- summary.append(node4("h3", token.principal), node4("span", t(token.expires_at > Date.now() ? "resources.active" : "resources.expired"), "badge"), node4("span", t("resources.expiresAt", { time: i18n.timestamp(token.expires_at) }), "muted"));
3584
- const controls = node4("div", "", "token-controls");
2788
+ const summary = node3("div", "", "token-summary");
2789
+ summary.append(node3("h3", token.principal), node3("span", t(token.expires_at > Date.now() ? "resources.active" : "resources.expired"), "badge"), node3("span", t("resources.expiresAt", { time: i18n.timestamp(token.expires_at) }), "muted"));
2790
+ const controls = node3("div", "", "token-controls");
3585
2791
  if (token.token) {
3586
- const value = node4("input");
2792
+ const value = node3("input");
3587
2793
  value.type = "password";
3588
2794
  value.value = token.token;
3589
2795
  value.readOnly = true;
@@ -3597,7 +2803,7 @@ function createResourcePanel(options) {
3597
2803
  });
3598
2804
  controls.append(value, toggle, copyControl(token.token, t("copy.action")));
3599
2805
  } else {
3600
- controls.append(node4("span", t("resources.legacy"), "muted"), button(t("resources.reissue"), () => {
2806
+ controls.append(node3("span", t("resources.legacy"), "muted"), button(t("resources.reissue"), () => {
3601
2807
  if (window.confirm(t("resources.confirmReissue"))) {
3602
2808
  mutate("tokens_rotate", { principal: token.principal });
3603
2809
  }
@@ -3608,34 +2814,60 @@ function createResourcePanel(options) {
3608
2814
  body.append(card);
3609
2815
  }
3610
2816
  } else {
3611
- for (const item of data?.items ?? []) {
3612
- const pairing = item;
3613
- const card = node4("article", "", "pairing-record");
3614
- const details = node4("div");
3615
- const label = ["pending", "paired", "cancelled", "expired"].includes(pairing.status) ? t("resources." + pairing.status) : pairing.status;
3616
- details.append(node4("h3", pairing.name), node4("code", pairing.pairing_id), node4("p", label + " \xB7 " + i18n.timestamp(pairing.expires), "muted"));
3617
- card.append(details, button(t("resources.delete"), () => remove("pairings_delete", { pairing_id: pairing.pairing_id }, t("resources.confirmPairing")), true));
2817
+ const host = typeof location === "undefined" ? "127.0.0.1" : location.hostname;
2818
+ const form = node3("form", "", "token-create-form");
2819
+ const nameLabel = node3("label", t("resources.name"));
2820
+ const name = node3("input");
2821
+ name.name = "code-name";
2822
+ name.pattern = "[A-Za-z0-9._-]{0,63}";
2823
+ name.maxLength = 63;
2824
+ name.placeholder = "workstation";
2825
+ name.disabled = pending;
2826
+ name.value = codeName;
2827
+ name.addEventListener("input", () => {
2828
+ codeName = name.value;
2829
+ });
2830
+ nameLabel.append(name);
2831
+ const submit = node3("button", t("resources.createCode"), "primary-button");
2832
+ submit.type = "submit";
2833
+ submit.disabled = pending;
2834
+ form.append(nameLabel, submit);
2835
+ form.addEventListener("submit", (event) => {
2836
+ event.preventDefault();
2837
+ mutate("pairing_codes_create", name.value.trim() ? { name: name.value.trim() } : {});
2838
+ });
2839
+ body.append(node3("p", t("resources.codesNote"), "muted"), form);
2840
+ if (created) {
2841
+ const command = "rovloom connector --join ws://" + host + ":" + created.device_port + "#" + created.code;
2842
+ const card = node3("article", "", "pairing-record code-created");
2843
+ const details = node3("div");
2844
+ details.append(node3("h3", created.name), node3("p", t("resources.codeCreatedNote"), "muted"));
2845
+ const block = node3("div", "", "command-snippet");
2846
+ block.append(node3("code", command), copyControl(command, t("copy.action")));
2847
+ card.append(details, block);
3618
2848
  body.append(card);
2849
+ created = undefined;
3619
2850
  }
3620
- }
3621
- if (key === "pairing" && (cursor > 0 || typeof data?.next_cursor === "number")) {
3622
- const pages = node4("div", "", "token-controls");
3623
- if (cursor > 0) {
3624
- pages.append(button(t("resources.previous"), () => {
3625
- cursor = Math.max(0, cursor - 50);
3626
- refresh();
3627
- }));
3628
- }
3629
- if (typeof data?.next_cursor === "number") {
3630
- pages.append(button(t("resources.next"), () => {
3631
- cursor = data.next_cursor;
3632
- refresh();
3633
- }));
2851
+ const port = data?.device_port ?? 9443;
2852
+ for (const item of data?.items ?? []) {
2853
+ const entry = item;
2854
+ const card = node3("article", "", "pairing-record");
2855
+ const details = node3("div");
2856
+ const label = t("resources.code_" + entry.status);
2857
+ details.append(node3("h3", entry.name), node3("code", entry.machine_id), node3("p", label + " \xB7 " + i18n.timestamp(entry.created_at) + (entry.used_at ? " \xB7 " + i18n.timestamp(entry.used_at) : ""), "muted"));
2858
+ card.append(details);
2859
+ if (entry.status === "unused" && entry.code) {
2860
+ const command = "rovloom connector --join ws://" + host + ":" + port + "#" + entry.code;
2861
+ const block = node3("div", "", "command-snippet");
2862
+ block.append(node3("code", command), copyControl(command, t("copy.action")));
2863
+ card.append(block);
2864
+ card.append(button(t("resources.revokeCode"), () => remove("pairing_codes_revoke", { code_id: entry.code_id }, t("resources.confirmRevokeCode")), true));
2865
+ }
2866
+ body.append(card);
3634
2867
  }
3635
- body.append(pages);
3636
2868
  }
3637
- if (data?.items.length === 0) {
3638
- body.append(node4("p", t(key === "connect" ? "resources.emptyTokens" : "resources.emptyPairings"), "empty"));
2869
+ if (data?.items.length === 0 && key === "connect") {
2870
+ body.append(node3("p", t("resources.emptyTokens"), "empty"));
3639
2871
  }
3640
2872
  }
3641
2873
  region.replaceChildren(body, feedback);
@@ -3644,9 +2876,10 @@ function createResourcePanel(options) {
3644
2876
  active = false;
3645
2877
  key = "";
3646
2878
  data = undefined;
3647
- cursor = 0;
3648
2879
  tokenName = "";
3649
2880
  tokenDays = "365";
2881
+ codeName = "";
2882
+ created = undefined;
3650
2883
  version++;
3651
2884
  controller.abort();
3652
2885
  pending = false;
@@ -3677,8 +2910,8 @@ function createResourcePanel(options) {
3677
2910
  region.hidden = false;
3678
2911
  if (changed) {
3679
2912
  key = nextKey;
3680
- cursor = 0;
3681
2913
  data = undefined;
2914
+ created = undefined;
3682
2915
  version++;
3683
2916
  controller.abort();
3684
2917
  controller = new AbortController;
@@ -3803,37 +3036,16 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
3803
3036
  if (actions) {
3804
3037
  footer.before(actions.region);
3805
3038
  }
3806
- const pairing = management ? createPairingActions({
3807
- transport: management,
3808
- committed() {
3809
- resourcePanel?.refresh();
3810
- },
3811
- authorizationLost(problem) {
3812
- if (disposed) {
3813
- return;
3814
- }
3815
- reader.dispose();
3816
- actions?.clear();
3817
- pairing?.clear();
3818
- paint({ snapshot: null, pending: false, stale: true, ageMs: 0, problem }, route);
3819
- }
3820
- }) : undefined;
3821
- if (pairing) {
3822
- footer.before(pairing.region);
3823
- }
3824
3039
  const resourcePanel = resources ? createResourcePanel({
3825
3040
  transport: resources,
3826
- committed(machine, pairingId) {
3041
+ committed(machine) {
3827
3042
  if (machine) {
3828
3043
  location.hash = "#view=devices";
3829
- } else if (pairingId) {
3830
- pairing?.remove(pairingId);
3831
3044
  }
3832
3045
  },
3833
3046
  authorizationLost(problem) {
3834
3047
  reader.dispose();
3835
3048
  actions?.clear();
3836
- pairing?.clear();
3837
3049
  paint({ snapshot: null, pending: false, stale: true, ageMs: 0, problem }, route);
3838
3050
  }
3839
3051
  }) : undefined;
@@ -3848,9 +3060,7 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
3848
3060
  indicator.dataset.state = state.pending ? "pending" : state.stale ? "stale" : state.snapshot ? "current" : "empty";
3849
3061
  if (["unauthenticated", "forbidden", "not_found"].includes(state.problem ?? "")) {
3850
3062
  actions?.clear();
3851
- pairing?.clear();
3852
3063
  }
3853
- pairing?.update(i18n, !!state.snapshot && !state.stale && state.snapshot.value.status.mode === "active");
3854
3064
  actions?.update(i18n, !!state.snapshot && !state.stale && state.snapshot.value.status.mode === "active", state.snapshot ? state.snapshot.observedAt + state.ageMs : undefined);
3855
3065
  const section = current.machineId ? "devices" : current.section ?? "overview";
3856
3066
  content.dataset.section = section;
@@ -3865,9 +3075,6 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
3865
3075
  const active = !!state.snapshot && !state.stale && state.snapshot.value.status.mode === "active";
3866
3076
  sidebarStatus.dataset.state = active ? "current" : "unknown";
3867
3077
  statusValue.textContent = i18n.t(active ? "gateway.active" : state.snapshot ? state.stale ? "reader.stale" : "gateway.degraded" : "reader.empty");
3868
- if (pairing) {
3869
- pairing.region.hidden = section !== "pairing" || !active;
3870
- }
3871
3078
  renderPage(content, state, current, i18n, !!actions, filter);
3872
3079
  const authorized = active && !["unauthenticated", "forbidden", "not_found"].includes(state.problem ?? "");
3873
3080
  resourcePanel?.update(current, i18n, authorized);
@@ -4001,7 +3208,6 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
4001
3208
  function navigate() {
4002
3209
  filter = { query: "", status: "all" };
4003
3210
  actions?.suspend();
4004
- pairing?.suspend();
4005
3211
  if (location.hash === "#management-content") {
4006
3212
  content.focus();
4007
3213
  return;
@@ -4014,7 +3220,6 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
4014
3220
  function visibility() {
4015
3221
  if (document.visibilityState === "hidden") {
4016
3222
  actions?.suspend();
4017
- pairing?.suspend();
4018
3223
  }
4019
3224
  reader.visible(document.visibilityState !== "hidden");
4020
3225
  }
@@ -4028,7 +3233,6 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
4028
3233
  }
4029
3234
  function hide() {
4030
3235
  actions?.suspend();
4031
- pairing?.suspend();
4032
3236
  reader.dispose();
4033
3237
  resourcePanel?.clear();
4034
3238
  content.replaceChildren();
@@ -4041,7 +3245,6 @@ function mountWebUi(root, read = unavailableRead, clock, management, resources)
4041
3245
  reader.dispose();
4042
3246
  resourcePanel?.dispose();
4043
3247
  actions?.dispose();
4044
- pairing?.dispose();
4045
3248
  content.removeEventListener("click", deviceAction);
4046
3249
  content.removeEventListener("click", filterDevices);
4047
3250
  content.removeEventListener("input", searchDevices);
@@ -4448,7 +3651,6 @@ export {
4448
3651
  createManagementPageCsrf,
4449
3652
  createManagementPageRead,
4450
3653
  createManagementWriteClient,
4451
- createPairingManagementFlow,
4452
3654
  createSnapshotReader2 as createSnapshotReader,
4453
3655
  mountWebUi,
4454
3656
  parseRoute,
@@ -4456,4 +3658,4 @@ export {
4456
3658
  unavailableRead
4457
3659
  };
4458
3660
 
4459
- //# debugId=9FAF58405B4DF80264756E2164756E21
3661
+ //# debugId=57232E8032FB7D7964756E2164756E21