@tunnelbox/hermes 0.1.4 → 0.1.5
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.
- package/README.md +17 -22
- package/README.zh-CN.md +15 -22
- package/dist/index.mjs +613 -554
- package/package.json +7 -5
package/dist/index.mjs
CHANGED
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
import { resolve as resolve2 } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
-
// src/bridge.ts
|
|
8
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
9
|
-
import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
|
|
10
|
-
import { hostname } from "node:os";
|
|
11
|
-
import { resolve } from "node:path";
|
|
12
|
-
|
|
13
7
|
// ../core/src/types.ts
|
|
14
8
|
function envelope(type, payload, id) {
|
|
15
9
|
return { id, type, payload, ts: Date.now() };
|
|
@@ -140,30 +134,28 @@ var zh_CN_default = {
|
|
|
140
134
|
"install.failConnect": "\uFF08\u672A\u80FD\u5373\u65F6\u8FDE\u63A5\u4E2D\u7EE7\u6253\u5370\u914D\u5BF9\u4FE1\u606F\u2014\u2014\u4E2D\u7EE7\u4E0D\u53EF\u8FBE\u6216\u5F53\u524D Node \u8FC7\u65E7\u3002",
|
|
141
135
|
"install.failConnectHint": "\u91CD\u542F opencode \u540E\u53EF\u7528 --print-logs \u67E5\u770B\u7EC8\u7AEF\u4E8C\u7EF4\u7801\uFF0C\u6216\u5728 opencode \u5185\u8F93\u5165\u201C\u663E\u793A\u624B\u673A\u914D\u5BF9\u7801\u201D\u6216 /pair\u3002\uFF09",
|
|
142
136
|
"install.hintFresh": "\u63D0\u793A\uFF1A\u914D\u5BF9\u7801\u4E3A\u4E00\u6B21\u6027\uFF0C\u7EA6 10 \u5206\u949F\u6709\u6548\uFF1B\u82E5\u7A0D\u540E\u624D\u542F\u52A8 opencode\uFF0C\u4EE5\u542F\u52A8\u65F6\u7EC8\u7AEF\u6253\u5370\u6216 /pair \u53D6\u5F97\u7684\u6700\u65B0\u914D\u5BF9\u4FE1\u606F\u4E3A\u51C6\u3002",
|
|
143
|
-
"install.linkLine": "\u914D\u5BF9\u94FE\u63A5: {link}",
|
|
144
137
|
"install.pluginInstalled": "\u5DF2\u5B89\u88C5\u63D2\u4EF6: {path}",
|
|
145
|
-
"install.qrUnavailable": "\uFF08\u4E8C\u7EF4\u7801\u6E32\u67D3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4F7F\u7528\u4E0A\u65B9\u914D\u5BF9\
|
|
138
|
+
"install.qrUnavailable": "\uFF08\u4E8C\u7EF4\u7801\u6E32\u67D3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u4F7F\u7528\u4E0A\u65B9\u914D\u5BF9\u7801\uFF09",
|
|
146
139
|
"install.qrcodeEnsure": "\u5B89\u88C5 qrcode \u5230 opencode \u914D\u7F6E\u76EE\u5F55\u2026",
|
|
147
|
-
"install.qrcodeFail": "qrcode \u5B89\u88C5\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4F7F\u7528\uFF0C\
|
|
140
|
+
"install.qrcodeFail": "qrcode \u5B89\u88C5\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4F7F\u7528\uFF0C\u4EC5\u663E\u793A\u914D\u5BF9\u7801\uFF09: {err}",
|
|
148
141
|
"install.relayError": "\u4E2D\u7EE7\u8FD4\u56DE\u9519\u8BEF: {msg}",
|
|
149
|
-
"install.scanHint": "\u624B\u673A\u6253\u5F00 tunnelbox App\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\
|
|
142
|
+
"install.scanHint": "\u624B\u673A\u6253\u5F00 tunnelbox App\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\uFF0C\u626B\u63CF\u4E0A\u65B9\u4E8C\u7EF4\u7801\u5373\u53EF\u8FDE\u63A5\uFF1A",
|
|
150
143
|
"pair.heading": "tunnelbox \u624B\u673A\u914D\u5BF9",
|
|
151
|
-
"pair.howTo": "\u5728\u624B\u673A\
|
|
144
|
+
"pair.howTo": "\u5728\u624B\u673A\u4E0A\u6253\u5F00 tunnelbox App\uFF0C\u7528\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\u626B\u63CF\u4E0A\u65B9\u4E8C\u7EF4\u7801\uFF0C\u6216\u624B\u52A8\u8F93\u5165\u914D\u5BF9\u7801\u3002",
|
|
152
145
|
"pair.oneTime": "\u914D\u5BF9\u7801\u4E3A\u4E00\u6B21\u6027\uFF0C\u4F7F\u7528\u540E\u5373\u5931\u6548\uFF1B\u5982\u9700\u65B0\u7801\uFF0C\u53EF\u8981\u6C42\u201C\u5237\u65B0\u914D\u5BF9\u7801\u201D\u3002",
|
|
153
|
-
"pair.promptBody": "\u8BF7\u5E2E\u6211\u67E5\u770B\u672C\u673A\u624B\u673A\u8FDC\u7A0B\u914D\u5BF9\u4FE1\u606F\uFF1A\u663E\u793A\u914D\u5BF9\u7801\
|
|
154
|
-
"pair.promptDescription": "\u663E\u793A\u624B\u673A\u8FDC\u7A0B\u914D\u5BF9\u7684\u914D\u5BF9\u7801\
|
|
155
|
-
"pair.scanIntro": "**\u626B\u4E00\u626B\u914D\u5BF9**\uFF1A\u624B\u673A\u6253\u5F00 tunnelbox App\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\
|
|
146
|
+
"pair.promptBody": "\u8BF7\u5E2E\u6211\u67E5\u770B\u672C\u673A\u624B\u673A\u8FDC\u7A0B\u914D\u5BF9\u4FE1\u606F\uFF1A\u663E\u793A\u914D\u5BF9\u7801\u4E0E\u53EF\u4F9B\u626B\u7801\u7684\u4E8C\u7EF4\u7801\u3002\u4E8C\u7EF4\u7801\u8BF7\u653E\u5728\u7B49\u5BBD\u4EE3\u7801\u5757\u91CC\u5E76\u9010\u5B57\u539F\u6837\u4FDD\u7559\uFF0C\u65B9\u4FBF\u6211\u7528\u624B\u673A\u626B\u7801\u3002",
|
|
147
|
+
"pair.promptDescription": "\u663E\u793A\u624B\u673A\u8FDC\u7A0B\u914D\u5BF9\u7684\u914D\u5BF9\u7801\u4E0E\u626B\u7801\u4E8C\u7EF4\u7801",
|
|
148
|
+
"pair.scanIntro": "**\u626B\u4E00\u626B\u914D\u5BF9**\uFF1A\u624B\u673A\u6253\u5F00 tunnelbox App\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\uFF0C\u626B\u63CF\u4E0B\u65B9\u4E8C\u7EF4\u7801\u5373\u53EF\u81EA\u52A8\u8FDE\u63A5\u3002",
|
|
156
149
|
"perm.promptAuthorize": "opencode \u8BF7\u6C42\u6388\u6743",
|
|
157
150
|
"perm.promptPatterns": "opencode \u8BF7\u6C42\u8BBF\u95EE\uFF1A{list}",
|
|
158
151
|
"perm.promptResource": "opencode \u8BF7\u6C42\u8BBF\u95EE\u8D44\u6E90\uFF1A{list}",
|
|
159
152
|
"perm.promptTool": "opencode \u8BF7\u6C42\u6267\u884C {tool}",
|
|
160
153
|
"qr.codeLabel": "\u914D\u5BF9\u7801",
|
|
161
154
|
"qr.fileWritten": "\u914D\u5BF9\u4FE1\u606F\u5DF2\u5199\u5165: {path}",
|
|
162
|
-
"qr.
|
|
163
|
-
"qr.openHint": "\u624B\u673A\u6D4F\u89C8\u5668\u6253\u5F00\u94FE\u63A5\uFF0C\u6216\u6253\u5F00 App \u540E\u624B\u52A8\u8F93\u5165\u914D\u5BF9\u7801\u3002",
|
|
155
|
+
"qr.openHint": "\u6253\u5F00 tunnelbox App\uFF0C\u7528\u300C\u626B\u4E00\u626B\u914D\u5BF9\u300D\u626B\u63CF\u4E0A\u65B9\u4E8C\u7EF4\u7801\uFF0C\u6216\u624B\u52A8\u8F93\u5165\u914D\u5BF9\u7801\u3002",
|
|
164
156
|
"qr.titleNoType": "tunnelbox \u624B\u673A\u914D\u5BF9",
|
|
165
157
|
"qr.titleWithType": "tunnelbox {type} \u624B\u673A\u914D\u5BF9",
|
|
166
|
-
"qr.unavailable": "\uFF08\u672A\u5B89\u88C5 qrcode\uFF0C\u8BF7\u4F7F\u7528\u4E0A\u65B9\
|
|
158
|
+
"qr.unavailable": "\uFF08\u672A\u5B89\u88C5 qrcode\uFF0C\u8BF7\u4F7F\u7528\u4E0A\u65B9\u914D\u5BF9\u7801\uFF09",
|
|
167
159
|
"claude.errBusy": "\u8BE5\u4F1A\u8BDD\u6B63\u5728\u751F\u6210\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u6216\u5148\u505C\u6B62",
|
|
168
160
|
"claude.errTurn": "\u751F\u6210\u51FA\u9519",
|
|
169
161
|
"claude.pairingStillClaimed": "\u5DF2\u7ED1\u5B9A\u624B\u673A\u8D26\u53F7\uFF1A\u6B64\u914D\u5BF9\u7801\u4EC5\u7528\u4E8E\u540C\u8D26\u53F7\u65B0\u8BBE\u5907\u91CD\u65B0\u914D\u5BF9\uFF0C\u5F53\u524D\u8BBE\u5907\u53EF\u76F4\u63A5\u4ECE\u624B\u673A\u300C\u6211\u7684\u7535\u8111\u300D\u8FDE\u63A5",
|
|
@@ -231,7 +223,41 @@ var zh_CN_default = {
|
|
|
231
223
|
"claude.errReadHistory": "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25({id})",
|
|
232
224
|
"setup.langSet": "\u5DF2\u8BBE\u7F6E\u8FD0\u884C\u8BED\u8A00",
|
|
233
225
|
"setup.langHint": "\u91CD\u542F\u9002\u914D\u5668\u540E\u6309\u8BE5\u8BED\u8A00\u8F93\u51FA\uFF08\u8FD0\u884C\u65F6\u53EF\u88AB TUNNELBOX_LANG \u8986\u76D6\uFF09\u3002\u72B6\u6001\u6587\u4EF6\uFF1A",
|
|
234
|
-
"setup.langCurrent": "\u5F53\u524D\u8FD0\u884C\u8BED\u8A00"
|
|
226
|
+
"setup.langCurrent": "\u5F53\u524D\u8FD0\u884C\u8BED\u8A00",
|
|
227
|
+
"hermes.errNoRelay": "\u672A\u914D\u7F6E\u4E2D\u7EE7\u5730\u5740\uFF1A\u6784\u5EFA\u8BF7\u7528 --env \u6CE8\u5165\u9ED8\u8BA4\uFF0C\u8FD0\u884C\u53EF\u7528 TUNNELBOX_RELAY_URL \u8986\u76D6",
|
|
228
|
+
"hermes.logConnected": "\u5DF2\u8FDE\u63A5\u4E2D\u7EE7 {url}",
|
|
229
|
+
"hermes.logMsgUncaught": "\u6D88\u606F\u5904\u7406\u672A\u6355\u83B7\u5F02\u5E38: {message}",
|
|
230
|
+
"hermes.logDisconnected": "\u4E0E\u4E2D\u7EE7\u65AD\u5F00\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u8FDE\u2026",
|
|
231
|
+
"hermes.logPairCode": "\u751F\u6210\u914D\u5BF9\u7801: {code}",
|
|
232
|
+
"hermes.logUnhandledType": "\u672A\u5904\u7406\u7684\u6D88\u606F\u7C7B\u578B: {type}",
|
|
233
|
+
"hermes.logHandleFailed": "\u5904\u7406 {type} \u5931\u8D25: {message}",
|
|
234
|
+
"hermes.logClaimed": "\u5DF2\u7ED1\u5B9A\u5230\u624B\u673A\u8D26\u53F7\uFF0C\u4E4B\u540E\u624B\u673A\u4ECE\u300C\u6211\u7684\u7535\u8111\u300D\u76F4\u63A5\u8FDE\u63A5",
|
|
235
|
+
"hermes.logResetIdentity": "\u88AB\u8D26\u53F7\u89E3\u7ED1\uFF0C\u6B63\u5728\u91CD\u7F6E\u8EAB\u4EFD\u5E76\u91CD\u65B0\u751F\u6210\u914D\u5BF9\u7801\u2026",
|
|
236
|
+
"hermes.cmdNew": "\u65B0\u5EFA\u4F1A\u8BDD",
|
|
237
|
+
"hermes.cmdHelp": "\u663E\u793A\u5E2E\u52A9",
|
|
238
|
+
"hermes.newSessionTitle": "\u65B0\u4F1A\u8BDD",
|
|
239
|
+
"hermes.helpText": "\u652F\u6301\u7684\u547D\u4EE4\uFF1A\n- /new \u65B0\u5EFA\u4F1A\u8BDD\n- /help \u663E\u793A\u5E2E\u52A9",
|
|
240
|
+
"hermes.errBusy": "\u8BE5\u4F1A\u8BDD\u6B63\u5728\u751F\u6210\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u6216\u5148\u505C\u6B62",
|
|
241
|
+
"hermes.errBadWorkspace": "\u8BE5\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528",
|
|
242
|
+
"hermes.logWorkspaceSwitched": "\u5207\u6362\u5DE5\u4F5C\u533A: {path}",
|
|
243
|
+
"hermes.errWorkspaceMissing": "\u5DE5\u4F5C\u533A\u4E0D\u5B58\u5728: {dir}",
|
|
244
|
+
"hermes.errStartFailed": "\u542F\u52A8\u5931\u8D25: {message}",
|
|
245
|
+
"hermes.msgStarted": "Hermes \u9002\u914D\u5668\u5DF2\u542F\u52A8\uFF08Ctrl+C \u9000\u51FA\uFF09",
|
|
246
|
+
"hermes.logAbortSent": "\u5DF2\u53D1\u9001\u4E2D\u6B62 (session={session})",
|
|
247
|
+
"hermes.logPromptFailed": "session.prompt \u5931\u8D25: {message}",
|
|
248
|
+
"hermes.logTurnError": "\u56DE\u5408\u51FA\u9519 (session={session}): {message}",
|
|
249
|
+
"hermes.logPairForced": "--pair\uFF1A\u5DF2\u5F3A\u5236\u7533\u8BF7\u914D\u5BF9\u7801\uFF08\u4EC5\u672C\u6B21\uFF09",
|
|
250
|
+
"hermes.pairingStillClaimed": "\u5DF2\u7ED1\u5B9A\u624B\u673A\u8D26\u53F7\uFF1A\u6B64\u914D\u5BF9\u7801\u4EC5\u7528\u4E8E\u540C\u8D26\u53F7\u65B0\u8BBE\u5907\u91CD\u65B0\u914D\u5BF9\uFF0C\u5F53\u524D\u8BBE\u5907\u53EF\u76F4\u63A5\u4ECE\u624B\u673A\u300C\u6211\u7684\u7535\u8111\u300D\u8FDE\u63A5",
|
|
251
|
+
"hermes.logPairNoRelay": "\u672A\u914D\u7F6E\u4E2D\u7EE7\u5730\u5740\uFF0C\u65E0\u6CD5\u7533\u8BF7\u914D\u5BF9\u7801",
|
|
252
|
+
"hermes.errSpawnHermes": "\u65E0\u6CD5\u542F\u52A8 Hermes\uFF08{bin}\uFF09\uFF1A\u8BF7\u786E\u8BA4\u5DF2\u5B89\u88C5\uFF08{err}\uFF09",
|
|
253
|
+
"hermes.errHermesExit": "hermes \u9000\u51FA({code}): {err}",
|
|
254
|
+
"hermes.logSessionUpdated": "\u4F1A\u8BDD\u5DF2\u66F4\u65B0 (session={session})",
|
|
255
|
+
"hermes.errNoInject": "\u624B\u673A\u6D88\u606F\u6CE8\u5165\u4E0D\u53EF\u7528\uFF08\u5F53\u524D\u4F1A\u8BDD\u975E\u4EA4\u4E92\u5F0F\u6216\u8FDB\u7A0B\u672A\u5C31\u7EEA\uFF09",
|
|
256
|
+
"hermes.pairNoRelay": "\u65E0\u6CD5\u8FDE\u63A5\u4E2D\u7EE7\uFF08relay \u4E0D\u53EF\u8FBE\u6216 websockets \u7F3A\u5931\uFF09",
|
|
257
|
+
"hermes.pairTimeout": "\u7533\u8BF7\u914D\u5BF9\u7801\u8D85\u65F6\uFF08\u4E2D\u7EE7\u672A\u54CD\u5E94\uFF09",
|
|
258
|
+
"hermes.errToolPair": "tunnelbox_pair \u5931\u8D25: {err}",
|
|
259
|
+
"hermes.errToolStatus": "tunnelbox_status \u5931\u8D25: {err}",
|
|
260
|
+
"hermes.cliUsage": "\u7528\u6CD5: /tunnelbox <pair|status>\n pair \u5411\u4E2D\u7EE7\u7533\u8BF7\u624B\u673A\u914D\u5BF9\u7801\u5E76\u6253\u5370\n status \u663E\u793A\u4E2D\u7EE7/\u7ED1\u5B9A/\u8FDE\u63A5\u72B6\u6001"
|
|
235
261
|
};
|
|
236
262
|
|
|
237
263
|
// ../core/locales/zh-TW.json
|
|
@@ -269,30 +295,28 @@ var zh_TW_default = {
|
|
|
269
295
|
"install.failConnect": "\uFF08\u672A\u80FD\u5373\u6642\u9023\u63A5\u4E2D\u7E7C\u5217\u5370\u914D\u5C0D\u8CC7\u8A0A\u2014\u2014\u4E2D\u7E7C\u7121\u6CD5\u9023\u7DDA\u6216\u76EE\u524D Node \u904E\u820A\u3002",
|
|
270
296
|
"install.failConnectHint": "\u91CD\u555F opencode \u5F8C\u53EF\u7528 --print-logs \u67E5\u770B\u7D42\u7AEF QR Code\uFF0C\u6216\u662F\u5728 opencode \u5167\u8F38\u5165\u300C\u986F\u793A\u624B\u6A5F\u914D\u5C0D\u78BC\u300D\u6216 /pair\u3002\uFF09",
|
|
271
297
|
"install.hintFresh": "\u63D0\u793A\uFF1A\u914D\u5C0D\u78BC\u70BA\u4E00\u6B21\u6027\uFF0C\u7D04 10 \u5206\u9418\u6709\u6548\uFF1B\u82E5\u7A0D\u5F8C\u624D\u555F\u52D5 opencode\uFF0C\u4EE5\u555F\u52D5\u6642\u7D42\u7AEF\u5217\u5370\u6216 /pair \u53D6\u5F97\u7684\u6700\u65B0\u914D\u5C0D\u8CC7\u8A0A\u70BA\u6E96\u3002",
|
|
272
|
-
"install.linkLine": "\u914D\u5C0D\u9023\u7D50: {link}",
|
|
273
298
|
"install.pluginInstalled": "\u5DF2\u5B89\u88DD\u5916\u639B: {path}",
|
|
274
|
-
"install.qrUnavailable": "\uFF08QR Code \u7121\u6CD5\u986F\u793A\uFF0C\u8ACB\u4F7F\u7528\u4E0A\u65B9\u914D\u5C0D\
|
|
299
|
+
"install.qrUnavailable": "\uFF08QR Code \u7121\u6CD5\u986F\u793A\uFF0C\u8ACB\u4F7F\u7528\u4E0A\u65B9\u914D\u5C0D\u78BC\uFF09",
|
|
275
300
|
"install.qrcodeEnsure": "\u6B63\u5728\u5C07 qrcode \u5B89\u88DD\u5230 opencode \u8A2D\u5B9A\u76EE\u9304\u2026",
|
|
276
|
-
"install.qrcodeFail": "qrcode \u5B89\u88DD\u5931\u6557\uFF08\u4E0D\u5F71\u97FF\u4F7F\u7528\
|
|
301
|
+
"install.qrcodeFail": "qrcode \u5B89\u88DD\u5931\u6557\uFF08\u4E0D\u5F71\u97FF\u4F7F\u7528\uFF0C\u50C5\u986F\u793A\u914D\u5C0D\u78BC\uFF09: {err}",
|
|
277
302
|
"install.relayError": "\u4E2D\u7E7C\u50B3\u56DE\u932F\u8AA4: {msg}",
|
|
278
|
-
"install.scanHint": "\u624B\u6A5F\u958B\u555F tunnelbox App\u300C\u6383\u63CF\u914D\u5C0D\u300D\uFF0C\
|
|
303
|
+
"install.scanHint": "\u624B\u6A5F\u958B\u555F tunnelbox App\u300C\u6383\u63CF\u914D\u5C0D\u300D\uFF0C\u6383\u63CF\u4E0A\u65B9 QR Code \u5373\u53EF\u9023\u63A5\uFF1A",
|
|
279
304
|
"pair.heading": "tunnelbox \u624B\u6A5F\u914D\u5C0D",
|
|
280
|
-
"pair.howTo": "\u5728\u624B\u6A5F\
|
|
305
|
+
"pair.howTo": "\u5728\u624B\u6A5F\u4E0A\u958B\u555F tunnelbox App\uFF0C\u7528\u300C\u6383\u63CF\u914D\u5C0D\u300D\u6383\u63CF\u4E0A\u65B9 QR Code\uFF0C\u6216\u624B\u52D5\u8F38\u5165\u914D\u5C0D\u78BC\u3002",
|
|
281
306
|
"pair.oneTime": "\u914D\u5C0D\u78BC\u70BA\u4E00\u6B21\u6027\uFF0C\u4F7F\u7528\u5F8C\u5373\u5931\u6548\uFF1B\u5982\u9700\u65B0\u78BC\uFF0C\u53EF\u8981\u6C42\u300C\u91CD\u65B0\u6574\u7406\u914D\u5C0D\u78BC\u300D\u3002",
|
|
282
|
-
"pair.promptBody": "\u8ACB\u5E6B\u6211\u67E5\u770B\u672C\u6A5F\u624B\u6A5F\u9060\u7AEF\u914D\u5C0D\u8CC7\u8A0A\uFF1A\u986F\u793A\u914D\u5C0D\u78BC\
|
|
283
|
-
"pair.promptDescription": "\u986F\u793A\u624B\u6A5F\u9060\u7AEF\u914D\u5C0D\u7684\u914D\u5C0D\u78BC\
|
|
284
|
-
"pair.scanIntro": "**\u6383\u78BC\u914D\u5C0D**\uFF1A\u624B\u6A5F\u958B\u555F tunnelbox App\u300C\u6383\u63CF\u914D\u5C0D\u300D\uFF0C\
|
|
307
|
+
"pair.promptBody": "\u8ACB\u5E6B\u6211\u67E5\u770B\u672C\u6A5F\u624B\u6A5F\u9060\u7AEF\u914D\u5C0D\u8CC7\u8A0A\uFF1A\u986F\u793A\u914D\u5C0D\u78BC\u8207\u53EF\u4F9B\u6383\u78BC\u7684 QR Code\u3002QR Code \u8ACB\u653E\u5728\u7B49\u5BEC\u7A0B\u5F0F\u78BC\u5340\u584A\u5167\u4E26\u9010\u5B57\u539F\u6A23\u4FDD\u7559\uFF0C\u65B9\u4FBF\u6211\u7528\u624B\u6A5F\u6383\u63CF\u3002",
|
|
308
|
+
"pair.promptDescription": "\u986F\u793A\u624B\u6A5F\u9060\u7AEF\u914D\u5C0D\u7684\u914D\u5C0D\u78BC\u8207\u6383\u78BC QR Code",
|
|
309
|
+
"pair.scanIntro": "**\u6383\u78BC\u914D\u5C0D**\uFF1A\u624B\u6A5F\u958B\u555F tunnelbox App\u300C\u6383\u63CF\u914D\u5C0D\u300D\uFF0C\u6383\u63CF\u4E0B\u65B9 QR Code \u5373\u53EF\u81EA\u52D5\u9023\u63A5\u3002",
|
|
285
310
|
"perm.promptAuthorize": "opencode \u8ACB\u6C42\u6388\u6B0A",
|
|
286
311
|
"perm.promptPatterns": "opencode \u8ACB\u6C42\u5B58\u53D6\uFF1A{list}",
|
|
287
312
|
"perm.promptResource": "opencode \u8ACB\u6C42\u5B58\u53D6\u8CC7\u6E90\uFF1A{list}",
|
|
288
313
|
"perm.promptTool": "opencode \u8ACB\u6C42\u57F7\u884C {tool}",
|
|
289
314
|
"qr.codeLabel": "\u914D\u5C0D\u78BC",
|
|
290
315
|
"qr.fileWritten": "\u914D\u5C0D\u8CC7\u8A0A\u5DF2\u5BEB\u5165: {path}",
|
|
291
|
-
"qr.
|
|
292
|
-
"qr.openHint": "\u5728\u624B\u6A5F\u700F\u89BD\u5668\u958B\u555F\u9023\u7D50\uFF0C\u6216\u5728 App \u4E2D\u624B\u52D5\u8F38\u5165\u914D\u5C0D\u78BC\u3002",
|
|
316
|
+
"qr.openHint": "\u958B\u555F tunnelbox App\uFF0C\u7528\u300C\u6383\u63CF\u914D\u5C0D\u300D\u6383\u63CF\u4E0A\u65B9 QR Code\uFF0C\u6216\u624B\u52D5\u8F38\u5165\u914D\u5C0D\u78BC\u3002",
|
|
293
317
|
"qr.titleNoType": "tunnelbox \u624B\u6A5F\u914D\u5C0D",
|
|
294
318
|
"qr.titleWithType": "tunnelbox {type} \u624B\u6A5F\u914D\u5C0D",
|
|
295
|
-
"qr.unavailable": "\uFF08\u672A\u5B89\u88DD qrcode\uFF0C\u8ACB\u4F7F\u7528\u4E0A\u65B9\
|
|
319
|
+
"qr.unavailable": "\uFF08\u672A\u5B89\u88DD qrcode\uFF0C\u8ACB\u4F7F\u7528\u4E0A\u65B9\u914D\u5C0D\u78BC\uFF09",
|
|
296
320
|
"claude.errBusy": "\u8A72\u6703\u8A71\u6B63\u5728\u7522\u751F\u4E2D\uFF0C\u8ACB\u7B49\u5F85\u5B8C\u6210\u6216\u5148\u505C\u6B62",
|
|
297
321
|
"claude.errTurn": "\u7522\u751F\u51FA\u932F",
|
|
298
322
|
"claude.pairingStillClaimed": "\u5DF2\u7D81\u5B9A\u624B\u6A5F\u5E33\u865F\uFF1A\u6B64\u914D\u5C0D\u78BC\u50C5\u4F9B\u540C\u5E33\u865F\u65B0\u88DD\u7F6E\u91CD\u65B0\u914D\u5C0D\uFF0C\u76EE\u524D\u88DD\u7F6E\u53EF\u76F4\u63A5\u5F9E\u624B\u6A5F\u300C\u6211\u7684\u96FB\u8166\u300D\u9023\u63A5",
|
|
@@ -360,7 +384,41 @@ var zh_TW_default = {
|
|
|
360
384
|
"claude.errReadHistory": "\u8B80\u53D6\u6703\u8A71\u6B77\u53F2\u5931\u6557({id})",
|
|
361
385
|
"setup.langSet": "\u5DF2\u8A2D\u5B9A\u57F7\u884C\u8A9E\u8A00",
|
|
362
386
|
"setup.langHint": "\u91CD\u65B0\u555F\u52D5\u4ECB\u9762\u5361\u5F8C\u5C07\u4EE5\u8A72\u8A9E\u8A00\u8F38\u51FA\uFF08\u57F7\u884C\u6642\u53EF\u88AB TUNNELBOX_LANG \u8986\u5BEB\uFF09\u3002\u72C0\u614B\u6A94\uFF1A",
|
|
363
|
-
"setup.langCurrent": "\u76EE\u524D\u57F7\u884C\u8A9E\u8A00"
|
|
387
|
+
"setup.langCurrent": "\u76EE\u524D\u57F7\u884C\u8A9E\u8A00",
|
|
388
|
+
"hermes.errNoRelay": "\u672A\u8A2D\u5B9A\u4E2D\u7E7C\u4F4D\u5740\uFF1A\u5EFA\u7F6E\u8ACB\u7528 --env \u6CE8\u5165\u9810\u8A2D\uFF0C\u57F7\u884C\u53EF\u7528 TUNNELBOX_RELAY_URL \u8986\u5BEB",
|
|
389
|
+
"hermes.logConnected": "\u5DF2\u9023\u7DDA\u4E2D\u7E7C {url}",
|
|
390
|
+
"hermes.logMsgUncaught": "\u8A0A\u606F\u8655\u7406\u672A\u6355\u7372\u4F8B\u5916: {message}",
|
|
391
|
+
"hermes.logDisconnected": "\u8207\u4E2D\u7E7C\u65B7\u7DDA\uFF0C\u6B63\u5728\u81EA\u52D5\u91CD\u65B0\u9023\u7DDA\u2026",
|
|
392
|
+
"hermes.logPairCode": "\u7522\u751F\u914D\u5C0D\u78BC: {code}",
|
|
393
|
+
"hermes.logUnhandledType": "\u672A\u8655\u7406\u7684\u8A0A\u606F\u985E\u578B: {type}",
|
|
394
|
+
"hermes.logHandleFailed": "\u8655\u7406 {type} \u5931\u6557: {message}",
|
|
395
|
+
"hermes.logClaimed": "\u5DF2\u7D81\u5B9A\u5230\u624B\u6A5F\u5E33\u865F\uFF0C\u4E4B\u5F8C\u624B\u6A5F\u5F9E\u300C\u6211\u7684\u96FB\u8166\u300D\u76F4\u63A5\u9023\u7DDA",
|
|
396
|
+
"hermes.logResetIdentity": "\u88AB\u5E33\u865F\u89E3\u7D81\uFF0C\u6B63\u5728\u91CD\u8A2D\u8EAB\u5206\u4E26\u91CD\u65B0\u7522\u751F\u914D\u5C0D\u78BC\u2026",
|
|
397
|
+
"hermes.cmdNew": "\u65B0\u589E\u6703\u8A71",
|
|
398
|
+
"hermes.cmdHelp": "\u986F\u793A\u8AAA\u660E",
|
|
399
|
+
"hermes.newSessionTitle": "\u65B0\u6703\u8A71",
|
|
400
|
+
"hermes.helpText": "\u652F\u63F4\u7684\u547D\u4EE4\uFF1A\n- /new \u65B0\u589E\u6703\u8A71\n- /help \u986F\u793A\u8AAA\u660E",
|
|
401
|
+
"hermes.errBusy": "\u6B64\u6703\u8A71\u6B63\u5728\u7522\u751F\u4E2D\uFF0C\u8ACB\u7B49\u5F85\u5B8C\u6210\u6216\u5148\u505C\u6B62",
|
|
402
|
+
"hermes.errBadWorkspace": "\u6B64\u76EE\u9304\u4E0D\u5B58\u5728\u6216\u7121\u6CD5\u4F7F\u7528",
|
|
403
|
+
"hermes.logWorkspaceSwitched": "\u5207\u63DB\u5DE5\u4F5C\u5340: {path}",
|
|
404
|
+
"hermes.errWorkspaceMissing": "\u5DE5\u4F5C\u5340\u4E0D\u5B58\u5728: {dir}",
|
|
405
|
+
"hermes.errStartFailed": "\u555F\u52D5\u5931\u6557: {message}",
|
|
406
|
+
"hermes.msgStarted": "Hermes \u4ECB\u63A5\u5668\u5DF2\u555F\u52D5\uFF08Ctrl+C \u9000\u51FA\uFF09",
|
|
407
|
+
"hermes.logAbortSent": "\u5DF2\u50B3\u9001\u4E2D\u6B62 (session={session})",
|
|
408
|
+
"hermes.logPromptFailed": "session.prompt \u5931\u6557: {message}",
|
|
409
|
+
"hermes.logTurnError": "\u56DE\u5408\u51FA\u932F (session={session}): {message}",
|
|
410
|
+
"hermes.logPairForced": "--pair\uFF1A\u5DF2\u5F37\u5236\u7533\u8ACB\u914D\u5C0D\u78BC\uFF08\u50C5\u672C\u6B21\uFF09",
|
|
411
|
+
"hermes.pairingStillClaimed": "\u5DF2\u7D81\u5B9A\u624B\u6A5F\u5E33\u865F\uFF1A\u6B64\u914D\u5C0D\u78BC\u50C5\u4F9B\u540C\u5E33\u865F\u65B0\u88DD\u7F6E\u91CD\u65B0\u914D\u5C0D\uFF0C\u76EE\u524D\u88DD\u7F6E\u53EF\u76F4\u63A5\u5F9E\u624B\u6A5F\u300C\u6211\u7684\u96FB\u8166\u300D\u9023\u7DDA",
|
|
412
|
+
"hermes.logPairNoRelay": "\u672A\u8A2D\u5B9A\u4E2D\u7E7C\u4F4D\u5740\uFF0C\u7121\u6CD5\u7533\u8ACB\u914D\u5C0D\u78BC",
|
|
413
|
+
"hermes.errSpawnHermes": "\u7121\u6CD5\u555F\u52D5 Hermes\uFF08{bin}\uFF09\uFF1A\u8ACB\u78BA\u8A8D\u5DF2\u5B89\u88DD\uFF08{err}\uFF09",
|
|
414
|
+
"hermes.errHermesExit": "hermes \u9000\u51FA({code}): {err}",
|
|
415
|
+
"hermes.logSessionUpdated": "\u6703\u8A71\u5DF2\u66F4\u65B0 (session={session})",
|
|
416
|
+
"hermes.errNoInject": "\u624B\u6A5F\u8A0A\u606F\u6CE8\u5165\u4E0D\u53EF\u7528\uFF08\u76EE\u524D\u5DE5\u4F5C\u968E\u6BB5\u975E\u4E92\u52D5\u5F0F\u6216\u8655\u7406\u7A0B\u5E8F\u672A\u5C31\u7DD2\uFF09",
|
|
417
|
+
"hermes.pairNoRelay": "\u7121\u6CD5\u9023\u7DDA\u4E2D\u7E7C\uFF08relay \u4E0D\u53EF\u9054\u6216 websockets \u7F3A\u5931\uFF09",
|
|
418
|
+
"hermes.pairTimeout": "\u7533\u8ACB\u914D\u5C0D\u78BC\u903E\u6642\uFF08\u4E2D\u7E7C\u672A\u56DE\u61C9\uFF09",
|
|
419
|
+
"hermes.errToolPair": "tunnelbox_pair \u5931\u6557: {err}",
|
|
420
|
+
"hermes.errToolStatus": "tunnelbox_status \u5931\u6557: {err}",
|
|
421
|
+
"hermes.cliUsage": "\u7528\u6CD5: /tunnelbox <pair|status>\n pair \u5411\u4E2D\u7E7C\u7533\u8ACB\u624B\u6A5F\u914D\u5C0D\u78BC\u4E26\u5217\u5370\n status \u986F\u793A\u4E2D\u7E7C/\u7D81\u5B9A/\u9023\u7DDA\u72C0\u614B"
|
|
364
422
|
};
|
|
365
423
|
|
|
366
424
|
// ../core/locales/en-US.json
|
|
@@ -398,30 +456,28 @@ var en_US_default = {
|
|
|
398
456
|
"install.failConnect": "(Could not connect to the relay to print pairing info \u2014 the relay may be unreachable or the Node version is too old.",
|
|
399
457
|
"install.failConnectHint": "After restarting opencode, check the terminal QR code with --print-logs, or type \u201Cshow phone pairing code\u201D / /pair inside opencode.)",
|
|
400
458
|
"install.hintFresh": "Note: the pairing code is single-use and valid for about 10 minutes. If you start opencode later, use the latest pairing info printed at startup or obtained via /pair.",
|
|
401
|
-
"install.linkLine": "Pairing link: {link}",
|
|
402
459
|
"install.pluginInstalled": "Installed plugin: {path}",
|
|
403
|
-
"install.qrUnavailable": "(QR rendering unavailable \u2014 use the pairing
|
|
460
|
+
"install.qrUnavailable": "(QR rendering unavailable \u2014 use the pairing code above)",
|
|
404
461
|
"install.qrcodeEnsure": "Installing qrcode into the opencode config directory\u2026",
|
|
405
|
-
"install.qrcodeFail": "qrcode installation failed (not critical \u2014 the
|
|
462
|
+
"install.qrcodeFail": "qrcode installation failed (not critical \u2014 only the pairing code is shown): {err}",
|
|
406
463
|
"install.relayError": "Relay returned an error: {msg}",
|
|
407
|
-
"install.scanHint": "Open tunnelbox on your phone, tap \u201CScan to pair\u201D,
|
|
464
|
+
"install.scanHint": "Open tunnelbox on your phone, tap \u201CScan to pair\u201D, and scan the QR code above to connect:",
|
|
408
465
|
"pair.heading": "tunnelbox phone pairing",
|
|
409
|
-
"pair.howTo": "Open the
|
|
466
|
+
"pair.howTo": "Open the tunnelbox app on your phone and scan the QR code above with \u201CScan to pair\u201D, or enter the pairing code manually.",
|
|
410
467
|
"pair.oneTime": "The pairing code is single-use and invalidates after it is claimed; ask to \u201Crefresh the pairing code\u201D for a new one.",
|
|
411
|
-
"pair.promptBody": "Please show this computer's phone pairing info: the pairing code
|
|
412
|
-
"pair.promptDescription": "Show the phone pairing code
|
|
413
|
-
"pair.scanIntro": "**Pair by scanning**: open the tunnelbox app on your phone
|
|
468
|
+
"pair.promptBody": "Please show this computer's phone pairing info: the pairing code and a QR code I can scan. Keep the QR code inside a monospaced code block and reproduce it character-for-character so my phone can scan it.",
|
|
469
|
+
"pair.promptDescription": "Show the phone pairing code and scan-able QR code",
|
|
470
|
+
"pair.scanIntro": "**Pair by scanning**: open the tunnelbox app on your phone, tap \u201CScan to pair\u201D, and scan the QR code below to connect automatically.",
|
|
414
471
|
"perm.promptAuthorize": "opencode requests authorization",
|
|
415
472
|
"perm.promptPatterns": "opencode requests access to: {list}",
|
|
416
473
|
"perm.promptResource": "opencode requests access to resources: {list}",
|
|
417
474
|
"perm.promptTool": "opencode requests to run {tool}",
|
|
418
475
|
"qr.codeLabel": "Pairing code",
|
|
419
476
|
"qr.fileWritten": "Pairing info written to: {path}",
|
|
420
|
-
"qr.
|
|
421
|
-
"qr.openHint": "Open the link in your phone browser, or enter the pairing code manually in the app.",
|
|
477
|
+
"qr.openHint": "Open the tunnelbox app on your phone and scan the QR code above, or enter the pairing code manually in the app.",
|
|
422
478
|
"qr.titleNoType": "tunnelbox phone pairing",
|
|
423
479
|
"qr.titleWithType": "tunnelbox {type} phone pairing",
|
|
424
|
-
"qr.unavailable": "(qrcode not installed \u2014 use the
|
|
480
|
+
"qr.unavailable": "(qrcode not installed \u2014 use the pairing code above)",
|
|
425
481
|
"claude.errBusy": "This session is still running; wait for it to finish or stop it first",
|
|
426
482
|
"claude.errTurn": "generation error",
|
|
427
483
|
"claude.pairingStillClaimed": "Already bound to a phone account: this code is only for pairing a new device on the same account; the current device connects directly from My Computers",
|
|
@@ -489,7 +545,41 @@ var en_US_default = {
|
|
|
489
545
|
"claude.errReadHistory": "Failed to read session history ({id})",
|
|
490
546
|
"setup.langSet": "Runtime language set to",
|
|
491
547
|
"setup.langHint": "The adapter will output in this language after restart (runtime TUNNELBOX_LANG can override). State file:",
|
|
492
|
-
"setup.langCurrent": "Current runtime language"
|
|
548
|
+
"setup.langCurrent": "Current runtime language",
|
|
549
|
+
"hermes.errNoRelay": "No relay URL configured: build with --env to inject a default, or override at runtime with TUNNELBOX_RELAY_URL",
|
|
550
|
+
"hermes.logConnected": "Connected to relay {url}",
|
|
551
|
+
"hermes.logMsgUncaught": "Uncaught exception while handling message: {message}",
|
|
552
|
+
"hermes.logDisconnected": "Disconnected from relay, reconnecting automatically\u2026",
|
|
553
|
+
"hermes.logPairCode": "Generated pairing code: {code}",
|
|
554
|
+
"hermes.logUnhandledType": "Unhandled message type: {type}",
|
|
555
|
+
"hermes.logHandleFailed": "Failed to handle {type}: {message}",
|
|
556
|
+
"hermes.logClaimed": "Bound to your phone account. From now on the phone connects directly from My Computers",
|
|
557
|
+
"hermes.logResetIdentity": "Unbound from the account; resetting identity and generating a new pairing code\u2026",
|
|
558
|
+
"hermes.cmdNew": "New session",
|
|
559
|
+
"hermes.cmdHelp": "Show help",
|
|
560
|
+
"hermes.newSessionTitle": "New session",
|
|
561
|
+
"hermes.helpText": "Supported commands:\n- /new new session\n- /help show help",
|
|
562
|
+
"hermes.errBusy": "This session is still running; wait for it to finish or stop it first",
|
|
563
|
+
"hermes.errBadWorkspace": "Directory does not exist or is unusable",
|
|
564
|
+
"hermes.logWorkspaceSwitched": "Switched workspace: {path}",
|
|
565
|
+
"hermes.errWorkspaceMissing": "Workspace does not exist: {dir}",
|
|
566
|
+
"hermes.errStartFailed": "Failed to start: {message}",
|
|
567
|
+
"hermes.msgStarted": "Hermes adapter started (Ctrl+C to quit)",
|
|
568
|
+
"hermes.logAbortSent": "Abort sent (session={session})",
|
|
569
|
+
"hermes.logPromptFailed": "session.prompt failed: {message}",
|
|
570
|
+
"hermes.logTurnError": "Turn error (session={session}): {message}",
|
|
571
|
+
"hermes.logPairForced": "--pair: forcing a pairing code request (this run only)",
|
|
572
|
+
"hermes.pairingStillClaimed": "Already bound to a phone account: this code is only for pairing a new device on the same account; the current device connects directly from My Computers",
|
|
573
|
+
"hermes.logPairNoRelay": "Cannot request a pairing code: no relay URL configured",
|
|
574
|
+
"hermes.errSpawnHermes": "Cannot start Hermes ({bin}): please confirm it is installed ({err})",
|
|
575
|
+
"hermes.errHermesExit": "hermes exited ({code}): {err}",
|
|
576
|
+
"hermes.logSessionUpdated": "Session updated (session={session})",
|
|
577
|
+
"hermes.errNoInject": "Phone message injection unavailable (session not interactive or process not ready)",
|
|
578
|
+
"hermes.pairNoRelay": "Cannot reach the relay (relay unreachable or websockets missing)",
|
|
579
|
+
"hermes.pairTimeout": "Timed out requesting a pairing code (relay did not respond)",
|
|
580
|
+
"hermes.errToolPair": "tunnelbox_pair failed: {err}",
|
|
581
|
+
"hermes.errToolStatus": "tunnelbox_status failed: {err}",
|
|
582
|
+
"hermes.cliUsage": "Usage: /tunnelbox <pair|status>\n pair request and print a phone pairing code\n status show relay/binding/connection status"
|
|
493
583
|
};
|
|
494
584
|
|
|
495
585
|
// ../core/locales/ja-JP.json
|
|
@@ -527,30 +617,28 @@ var ja_JP_default = {
|
|
|
527
617
|
"install.failConnect": "\uFF08\u30DA\u30A2\u30EA\u30F3\u30B0\u60C5\u5831\u3092\u51FA\u529B\u3059\u308B\u305F\u3081\u30EA\u30EC\u30FC\u306B\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F \u2014 \u30EA\u30EC\u30FC\u306B\u5230\u9054\u3067\u304D\u306A\u3044\u304B Node \u304C\u53E4\u3059\u304E\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
528
618
|
"install.failConnectHint": "opencode \u3092\u518D\u8D77\u52D5\u5F8C\u3001--print-logs \u3067\u30BF\u30FC\u30DF\u30CA\u30EB\u306E QR \u30B3\u30FC\u30C9\u3092\u78BA\u8A8D\u3059\u308B\u304B\u3001opencode \u5185\u3067\u300C\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u8868\u793A\u300D\u307E\u305F\u306F /pair \u3068\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\uFF09",
|
|
529
619
|
"install.hintFresh": "\u6CE8\u610F\uFF1A\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u306F\u4E00\u5EA6\u304D\u308A\u3067\u3001\u7D04 10 \u5206\u9593\u6709\u52B9\u3067\u3059\u3002\u5F8C\u304B\u3089 opencode \u3092\u8D77\u52D5\u3059\u308B\u5834\u5408\u306F\u3001\u8D77\u52D5\u6642\u306B\u8868\u793A\u3055\u308C\u308B\u304B /pair \u3067\u53D6\u5F97\u3057\u305F\u6700\u65B0\u60C5\u5831\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
530
|
-
"install.linkLine": "\u30DA\u30A2\u30EA\u30F3\u30B0\u30EA\u30F3\u30AF: {link}",
|
|
531
620
|
"install.pluginInstalled": "\u30D7\u30E9\u30B0\u30A4\u30F3\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB: {path}",
|
|
532
|
-
"install.qrUnavailable": "\uFF08QR \u30B3\u30FC\u30C9\u3092\u8868\u793A\u3067\u304D\u307E\u305B\u3093 \u2014 \u4E0A\u8A18\u306E\
|
|
621
|
+
"install.qrUnavailable": "\uFF08QR \u30B3\u30FC\u30C9\u3092\u8868\u793A\u3067\u304D\u307E\u305B\u3093 \u2014 \u4E0A\u8A18\u306E\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
|
|
533
622
|
"install.qrcodeEnsure": "qrcode \u3092 opencode \u8A2D\u5B9A\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u3044\u307E\u3059\u2026",
|
|
534
|
-
"install.qrcodeFail": "qrcode \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u4F7F\u7528\u306B\u306F\u5F71\u97FF\u305B\u305A\
|
|
623
|
+
"install.qrcodeFail": "qrcode \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u4F7F\u7528\u306B\u306F\u5F71\u97FF\u305B\u305A\u3001\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u306E\u307F\u8868\u793A\u3055\u308C\u307E\u3059\uFF09: {err}",
|
|
535
624
|
"install.relayError": "\u30EA\u30EC\u30FC\u304C\u30A8\u30E9\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F: {msg}",
|
|
536
|
-
"install.scanHint": "\u30B9\u30DE\u30DB\u3067 tunnelbox App\u300C\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30DA\u30A2\u30EA\u30F3\u30B0\u300D\u3092\u958B\
|
|
625
|
+
"install.scanHint": "\u30B9\u30DE\u30DB\u3067 tunnelbox App\u300C\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30DA\u30A2\u30EA\u30F3\u30B0\u300D\u3092\u958B\u304D\u3001\u4E0A\u306E QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u63A5\u7D9A\uFF1A",
|
|
537
626
|
"pair.heading": "tunnelbox \u30B9\u30DE\u30DB\u30DA\u30A2\u30EA\u30F3\u30B0",
|
|
538
|
-
"pair.howTo": "\u30B9\u30DE\u30DB\
|
|
627
|
+
"pair.howTo": "\u30B9\u30DE\u30DB\u3067 tunnelbox App \u3092\u958B\u304D\u3001\u300C\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30DA\u30A2\u30EA\u30F3\u30B0\u300D\u3067\u4E0A\u306E QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3059\u308B\u304B\u3001\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u624B\u52D5\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
539
628
|
"pair.oneTime": "\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u306F\u4E00\u5EA6\u304D\u308A\u3067\u3001\u4F7F\u7528\u5F8C\u306F\u7121\u52B9\u306B\u306A\u308A\u307E\u3059\u3002\u65B0\u3057\u3044\u30B3\u30FC\u30C9\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u300C\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u66F4\u65B0\u300D\u3068\u4F9D\u983C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
540
|
-
"pair.promptBody": "\u3053\u306E PC \u306E\u30B9\u30DE\u30DB\u9060\u9694\u30DA\u30A2\u30EA\u30F3\u30B0\u60C5\u5831\u3092\u8868\u793A\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\
|
|
541
|
-
"pair.promptDescription": "\u30B9\u30DE\u30DB\u9060\u9694\u30DA\u30A2\u30EA\u30F3\u30B0\u306E\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u30FB\
|
|
542
|
-
"pair.scanIntro": "**\u30B9\u30AD\u30E3\u30F3\u3067\u30DA\u30A2\u30EA\u30F3\u30B0**\uFF1A\u30B9\u30DE\u30DB\u3067 tunnelbox App\u300C\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30DA\u30A2\u30EA\u30F3\u30B0\u300D\u3092\u958B\
|
|
629
|
+
"pair.promptBody": "\u3053\u306E PC \u306E\u30B9\u30DE\u30DB\u9060\u9694\u30DA\u30A2\u30EA\u30F3\u30B0\u60C5\u5831\u3092\u8868\u793A\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3068\u3001\u30B9\u30AD\u30E3\u30F3\u3067\u304D\u308B QR \u30B3\u30FC\u30C9\u3002QR \u30B3\u30FC\u30C9\u306F\u7B49\u5E45\u30B3\u30FC\u30C9\u30D6\u30ED\u30C3\u30AF\u306B\u5165\u308C\u30011 \u6587\u5B57\u3082\u5909\u3048\u305A\u305D\u306E\u307E\u307E\u51FA\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30B9\u30DE\u30DB\u3067\u30B9\u30AD\u30E3\u30F3\u3067\u304D\u308B\u3088\u3046\u306B\u3059\u308B\u305F\u3081\u3067\u3059\u3002",
|
|
630
|
+
"pair.promptDescription": "\u30B9\u30DE\u30DB\u9060\u9694\u30DA\u30A2\u30EA\u30F3\u30B0\u306E\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u30FB\u30B9\u30AD\u30E3\u30F3\u7528 QR \u30B3\u30FC\u30C9\u3092\u8868\u793A",
|
|
631
|
+
"pair.scanIntro": "**\u30B9\u30AD\u30E3\u30F3\u3067\u30DA\u30A2\u30EA\u30F3\u30B0**\uFF1A\u30B9\u30DE\u30DB\u3067 tunnelbox App\u300C\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30DA\u30A2\u30EA\u30F3\u30B0\u300D\u3092\u958B\u304D\u3001\u4E0B\u306E QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3059\u308B\u3068\u81EA\u52D5\u7684\u306B\u63A5\u7D9A\u3055\u308C\u307E\u3059\u3002",
|
|
543
632
|
"perm.promptAuthorize": "opencode \u304C\u8A8D\u53EF\u3092\u8981\u6C42\u3057\u3066\u3044\u307E\u3059",
|
|
544
633
|
"perm.promptPatterns": "opencode \u304C\u30A2\u30AF\u30BB\u30B9\u3092\u8981\u6C42: {list}",
|
|
545
634
|
"perm.promptResource": "opencode \u304C\u30EA\u30BD\u30FC\u30B9\u3078\u306E\u30A2\u30AF\u30BB\u30B9\u3092\u8981\u6C42: {list}",
|
|
546
635
|
"perm.promptTool": "opencode \u304C {tool} \u306E\u5B9F\u884C\u3092\u8981\u6C42",
|
|
547
636
|
"qr.codeLabel": "\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9",
|
|
548
637
|
"qr.fileWritten": "\u30DA\u30A2\u30EA\u30F3\u30B0\u60C5\u5831\u3092\u66F8\u304D\u8FBC\u307F\u307E\u3057\u305F: {path}",
|
|
549
|
-
"qr.
|
|
550
|
-
"qr.openHint": "\u30B9\u30DE\u30DB\u306E\u30D6\u30E9\u30A6\u30B6\u3067\u30EA\u30F3\u30AF\u3092\u958B\u304F\u304B\u3001App \u3067\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u624B\u52D5\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
638
|
+
"qr.openHint": "tunnelbox App \u3092\u958B\u304D\u3001\u4E0A\u306E QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3059\u308B\u304B\u3001App \u3067\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u624B\u52D5\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
551
639
|
"qr.titleNoType": "tunnelbox \u30B9\u30DE\u30DB\u30DA\u30A2\u30EA\u30F3\u30B0",
|
|
552
640
|
"qr.titleWithType": "tunnelbox {type} \u30B9\u30DE\u30DB\u30DA\u30A2\u30EA\u30F3\u30B0",
|
|
553
|
-
"qr.unavailable": "\uFF08qrcode \u304C\u672A\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB \u2014 \u4E0A\u8A18\u306E\
|
|
641
|
+
"qr.unavailable": "\uFF08qrcode \u304C\u672A\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB \u2014 \u4E0A\u8A18\u306E\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
|
|
554
642
|
"claude.errBusy": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u5B9F\u884C\u4E2D\u3067\u3059\u3002\u5B8C\u4E86\u3059\u308B\u307E\u3067\u304A\u5F85\u3061\u3044\u305F\u3060\u304F\u304B\u3001\u5148\u306B\u505C\u6B62\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
555
643
|
"claude.errTurn": "\u751F\u6210\u30A8\u30E9\u30FC",
|
|
556
644
|
"claude.pairingStillClaimed": "\u3059\u3067\u306B\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u7D10\u4ED8\u3051\u6E08\u307F\u3067\u3059\u3002\u3053\u306E\u30B3\u30FC\u30C9\u306F\u540C\u3058\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u65B0\u3057\u3044\u7AEF\u672B\u306E\u30DA\u30A2\u30EA\u30F3\u30B0\u5C02\u7528\u3067\u3059\u3002\u73FE\u5728\u306E\u7AEF\u672B\u306F\u300C\u30DE\u30A4\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u304B\u3089\u76F4\u63A5\u63A5\u7D9A\u3067\u304D\u307E\u3059",
|
|
@@ -618,7 +706,41 @@ var ja_JP_default = {
|
|
|
618
706
|
"claude.errReadHistory": "\u30BB\u30C3\u30B7\u30E7\u30F3\u5C65\u6B74\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\uFF08{id}\uFF09",
|
|
619
707
|
"setup.langSet": "\u5B9F\u884C\u8A00\u8A9E\u3092\u8A2D\u5B9A\u3057\u307E\u3057\u305F",
|
|
620
708
|
"setup.langHint": "\u518D\u8D77\u52D5\u5F8C\u306B\u3053\u306E\u8A00\u8A9E\u3067\u51FA\u529B\u3057\u307E\u3059\uFF08\u5B9F\u884C\u6642\u306F TUNNELBOX_LANG \u3067\u4E0A\u66F8\u304D\u53EF\u80FD\uFF09\u3002\u72B6\u614B\u30D5\u30A1\u30A4\u30EB\uFF1A",
|
|
621
|
-
"setup.langCurrent": "\u73FE\u5728\u306E\u5B9F\u884C\u8A00\u8A9E"
|
|
709
|
+
"setup.langCurrent": "\u73FE\u5728\u306E\u5B9F\u884C\u8A00\u8A9E",
|
|
710
|
+
"hermes.errNoRelay": "\u30EA\u30EC\u30FCURL\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\uFF1A--env \u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u3092\u6CE8\u5165\u3001\u5B9F\u884C\u6642\u306F TUNNELBOX_RELAY_URL \u3067\u4E0A\u66F8\u304D\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
711
|
+
"hermes.logConnected": "\u30EA\u30EC\u30FC\u306B\u63A5\u7D9A\u3057\u307E\u3057\u305F {url}",
|
|
712
|
+
"hermes.logMsgUncaught": "\u30E1\u30C3\u30BB\u30FC\u30B8\u51E6\u7406\u4E2D\u306E\u672A\u6355\u6349\u4F8B\u5916: {message}",
|
|
713
|
+
"hermes.logDisconnected": "\u30EA\u30EC\u30FC\u304B\u3089\u5207\u65AD\u3001\u81EA\u52D5\u518D\u63A5\u7D9A\u4E2D\u2026",
|
|
714
|
+
"hermes.logPairCode": "\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u751F\u6210: {code}",
|
|
715
|
+
"hermes.logUnhandledType": "\u672A\u51E6\u7406\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u30BF\u30A4\u30D7: {type}",
|
|
716
|
+
"hermes.logHandleFailed": "{type} \u306E\u51E6\u7406\u306B\u5931\u6557: {message}",
|
|
717
|
+
"hermes.logClaimed": "\u30B9\u30DE\u30DB\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u30D0\u30A4\u30F3\u30C9\u3057\u307E\u3057\u305F\u3002\u4EE5\u5F8C\u306F\u30B9\u30DE\u30DB\u306E\u300C\u30DE\u30A4\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u304B\u3089\u76F4\u63A5\u63A5\u7D9A\u3057\u307E\u3059",
|
|
718
|
+
"hermes.logResetIdentity": "\u30A2\u30AB\u30A6\u30F3\u30C8\u304B\u3089\u30D0\u30A4\u30F3\u30C9\u89E3\u9664\u3001ID\u3092\u30EA\u30BB\u30C3\u30C8\u3057\u3066\u65B0\u3057\u3044\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u751F\u6210\u4E2D\u2026",
|
|
719
|
+
"hermes.cmdNew": "\u65B0\u898F\u30BB\u30C3\u30B7\u30E7\u30F3",
|
|
720
|
+
"hermes.cmdHelp": "\u30D8\u30EB\u30D7\u3092\u8868\u793A",
|
|
721
|
+
"hermes.newSessionTitle": "\u65B0\u898F\u30BB\u30C3\u30B7\u30E7\u30F3",
|
|
722
|
+
"hermes.helpText": "\u30B5\u30DD\u30FC\u30C8\u30B3\u30DE\u30F3\u30C9\uFF1A\n- /new \u65B0\u898F\u30BB\u30C3\u30B7\u30E7\u30F3\n- /help \u30D8\u30EB\u30D7\u3092\u8868\u793A",
|
|
723
|
+
"hermes.errBusy": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u751F\u6210\u4E2D\u3067\u3059\u3002\u5B8C\u4E86\u3092\u5F85\u3064\u304B\u3001\u5148\u306B\u505C\u6B62\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
724
|
+
"hermes.errBadWorkspace": "\u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u5B58\u5728\u3057\u306A\u3044\u304B\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
725
|
+
"hermes.logWorkspaceSwitched": "\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u5207\u66FF: {path}",
|
|
726
|
+
"hermes.errWorkspaceMissing": "\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u304C\u5B58\u5728\u3057\u307E\u305B\u3093: {dir}",
|
|
727
|
+
"hermes.errStartFailed": "\u8D77\u52D5\u306B\u5931\u6557: {message}",
|
|
728
|
+
"hermes.msgStarted": "Hermes \u30A2\u30C0\u30D7\u30BF\u3092\u8D77\u52D5\u3057\u307E\u3057\u305F\uFF08Ctrl+C \u3067\u7D42\u4E86\uFF09",
|
|
729
|
+
"hermes.logAbortSent": "\u4E2D\u6B62\u3092\u9001\u4FE1 (session={session})",
|
|
730
|
+
"hermes.logPromptFailed": "session.prompt \u306B\u5931\u6557: {message}",
|
|
731
|
+
"hermes.logTurnError": "\u30BF\u30FC\u30F3\u30A8\u30E9\u30FC (session={session}): {message}",
|
|
732
|
+
"hermes.logPairForced": "--pair\uFF1A\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u5F37\u5236\u53D6\u5F97\uFF08\u4ECA\u56DE\u306E\u307F\uFF09",
|
|
733
|
+
"hermes.pairingStillClaimed": "\u30B9\u30DE\u30DB\u30A2\u30AB\u30A6\u30F3\u30C8\u306B\u30D0\u30A4\u30F3\u30C9\u6E08\u307F\uFF1A\u3053\u306E\u30B3\u30FC\u30C9\u306F\u540C\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u65B0\u30C7\u30D0\u30A4\u30B9\u518D\u30DA\u30A2\u30EA\u30F3\u30B0\u7528\u3067\u3059\u3002\u73FE\u5728\u306E\u30C7\u30D0\u30A4\u30B9\u306F\u30B9\u30DE\u30DB\u306E\u300C\u30DE\u30A4\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u304B\u3089\u76F4\u63A5\u63A5\u7D9A\u3067\u304D\u307E\u3059",
|
|
734
|
+
"hermes.logPairNoRelay": "\u30EA\u30EC\u30FCURL\u304C\u672A\u8A2D\u5B9A\u306E\u305F\u3081\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093",
|
|
735
|
+
"hermes.errSpawnHermes": "Hermes\uFF08{bin}\uFF09\u3092\u8D77\u52D5\u3067\u304D\u307E\u305B\u3093\uFF1A\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\uFF08{err}\uFF09",
|
|
736
|
+
"hermes.errHermesExit": "hermes \u7D42\u4E86({code}): {err}",
|
|
737
|
+
"hermes.logSessionUpdated": "\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u66F4\u65B0 (session={session})",
|
|
738
|
+
"hermes.errNoInject": "\u30B9\u30DE\u30DB\u304B\u3089\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u633F\u5165\u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093\uFF08\u975E\u5BFE\u8A71\u30BB\u30C3\u30B7\u30E7\u30F3\u307E\u305F\u306F\u30D7\u30ED\u30BB\u30B9\u672A\u6E96\u5099\uFF09",
|
|
739
|
+
"hermes.pairNoRelay": "\u30EA\u30EC\u30FC\u306B\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\uFF08\u30EA\u30EC\u30FC\u5230\u9054\u4E0D\u80FD\u307E\u305F\u306F websockets \u6B20\u843D\uFF09",
|
|
740
|
+
"hermes.pairTimeout": "\u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u8981\u6C42\u304C\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\uFF08\u30EA\u30EC\u30FC\u672A\u5FDC\u7B54\uFF09",
|
|
741
|
+
"hermes.errToolPair": "tunnelbox_pair \u5931\u6557: {err}",
|
|
742
|
+
"hermes.errToolStatus": "tunnelbox_status \u5931\u6557: {err}",
|
|
743
|
+
"hermes.cliUsage": "\u4F7F\u7528\u65B9\u6CD5: /tunnelbox <pair|status>\n pair \u30DA\u30A2\u30EA\u30F3\u30B0\u30B3\u30FC\u30C9\u3092\u53D6\u5F97\u3057\u3066\u8868\u793A\n status \u30EA\u30EC\u30FC/\u30D0\u30A4\u30F3\u30C9/\u63A5\u7D9A\u72B6\u614B\u3092\u8868\u793A"
|
|
622
744
|
};
|
|
623
745
|
|
|
624
746
|
// ../core/locales/ko-KR.json
|
|
@@ -656,30 +778,28 @@ var ko_KR_default = {
|
|
|
656
778
|
"install.failConnect": "(\uD398\uC5B4\uB9C1 \uC815\uBCF4\uB97C \uCD9C\uB825\uD558\uB824\uACE0 \uB9B4\uB808\uC774\uC5D0 \uC989\uC2DC \uC5F0\uACB0\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uB9B4\uB808\uC774\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uAC70\uB098 Node \uBC84\uC804\uC774 \uB108\uBB34 \uC624\uB798\uB410\uC2B5\uB2C8\uB2E4.",
|
|
657
779
|
"install.failConnectHint": "opencode\uB97C \uB2E4\uC2DC \uC2DC\uC791\uD55C \uB4A4 --print-logs\uB85C \uD130\uBBF8\uB110 QR \uCF54\uB4DC\uB97C \uD655\uC778\uD558\uAC70\uB098, opencode \uC548\uC5D0\uC11C \u201C\uD734\uB300\uD3F0 \uD398\uC5B4\uB9C1 \uCF54\uB4DC \uD45C\uC2DC\u201D \uB610\uB294 /pair\uB97C \uC785\uB825\uD558\uC138\uC694.)",
|
|
658
780
|
"install.hintFresh": "\uCC38\uACE0: \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB294 \uC77C\uD68C\uC6A9\uC774\uBA70 \uC57D 10\uBD84\uAC04 \uC720\uD6A8\uD569\uB2C8\uB2E4. \uB098\uC911\uC5D0 opencode\uB97C \uC2DC\uC791\uD55C\uB2E4\uBA74 \uC2DC\uC791 \uC2DC \uCD9C\uB825\uB418\uAC70\uB098 /pair\uB85C \uBC1B\uC740 \uCD5C\uC2E0 \uC815\uBCF4\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
|
|
659
|
-
"install.linkLine": "\uD398\uC5B4\uB9C1 \uB9C1\uD06C: {link}",
|
|
660
781
|
"install.pluginInstalled": "\uD50C\uB7EC\uADF8\uC778 \uC124\uCE58\uB428: {path}",
|
|
661
|
-
"install.qrUnavailable": "(QR \uCF54\uB4DC\uB97C \uD45C\uC2DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC704\uC758 \
|
|
782
|
+
"install.qrUnavailable": "(QR \uCF54\uB4DC\uB97C \uD45C\uC2DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC704\uC758 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC0AC\uC6A9\uD558\uC138\uC694)",
|
|
662
783
|
"install.qrcodeEnsure": "qrcode\uB97C opencode \uC124\uC815 \uB514\uB809\uD130\uB9AC\uC5D0 \uC124\uCE58\uD558\uB294 \uC911\u2026",
|
|
663
|
-
"install.qrcodeFail": "qrcode \uC124\uCE58 \uC2E4\uD328(\uC0AC\uC6A9\uC5D0\uB294 \uC601\uD5A5\uC774 \uC5C6\uC73C\uBA70
|
|
784
|
+
"install.qrcodeFail": "qrcode \uC124\uCE58 \uC2E4\uD328(\uC0AC\uC6A9\uC5D0\uB294 \uC601\uD5A5\uC774 \uC5C6\uC73C\uBA70 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB9CC \uD45C\uC2DC\uB429\uB2C8\uB2E4): {err}",
|
|
664
785
|
"install.relayError": "\uB9B4\uB808\uC774 \uC624\uB958 \uC751\uB2F5: {msg}",
|
|
665
|
-
"install.scanHint": "\uD734\uB300\uD3F0\uC5D0\uC11C tunnelbox \uC571 \u201C\uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\u201D\uC744 \uC5F4\
|
|
786
|
+
"install.scanHint": "\uD734\uB300\uD3F0\uC5D0\uC11C tunnelbox \uC571 \u201C\uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\u201D\uC744 \uC5F4\uACE0 \uC704 QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\uD558\uC138\uC694:",
|
|
666
787
|
"pair.heading": "tunnelbox \uD734\uB300\uD3F0 \uD398\uC5B4\uB9C1",
|
|
667
|
-
"pair.howTo": "\uD734\uB300\uD3F0 \
|
|
788
|
+
"pair.howTo": "\uD734\uB300\uD3F0\uC5D0\uC11C tunnelbox \uC571\uC744 \uC5F4\uACE0 \u201C\uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\u201D\uB85C \uC704 QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uAC70\uB098 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC9C1\uC811 \uC785\uB825\uD558\uC138\uC694.",
|
|
668
789
|
"pair.oneTime": "\uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB294 \uC77C\uD68C\uC6A9\uC774\uBA70 \uC0AC\uC6A9 \uD6C4\uC5D0\uB294 \uB9CC\uB8CC\uB429\uB2C8\uB2E4. \uC0C8 \uCF54\uB4DC\uAC00 \uD544\uC694\uD558\uBA74 \u201C\uD398\uC5B4\uB9C1 \uCF54\uB4DC \uC0C8\uB85C\uACE0\uCE68\u201D\uC744 \uC694\uCCAD\uD558\uC138\uC694.",
|
|
669
|
-
"pair.promptBody": "\uC774 PC\uC758 \uD734\uB300\uD3F0 \uC6D0\uACA9 \uD398\uC5B4\uB9C1 \uC815\uBCF4\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694: \uD398\uC5B4\uB9C1 \uCF54\uB4DC
|
|
670
|
-
"pair.promptDescription": "\uD734\uB300\uD3F0 \uC6D0\uACA9 \uD398\uC5B4\uB9C1\uC758 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\xB7\
|
|
671
|
-
"pair.scanIntro": "**\uC2A4\uCE94\uC73C\uB85C \uC5F0\uACB0**: \uD734\uB300\uD3F0\uC5D0\uC11C tunnelbox \uC571 \u201C\uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\u201D\uC744 \uC5F4\
|
|
790
|
+
"pair.promptBody": "\uC774 PC\uC758 \uD734\uB300\uD3F0 \uC6D0\uACA9 \uD398\uC5B4\uB9C1 \uC815\uBCF4\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694: \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uC640 \uC2A4\uCE94\uD560 \uC218 \uC788\uB294 QR \uCF54\uB4DC. QR \uCF54\uB4DC\uB294 \uACE0\uC815\uD3ED \uCF54\uB4DC \uBE14\uB85D\uC5D0 \uB123\uACE0 \uD55C \uAE00\uC790\uB3C4 \uBC14\uAFB8\uC9C0 \uB9D0\uACE0 \uADF8\uB300\uB85C \uD45C\uC2DC\uD574 \uC8FC\uC138\uC694. \uD734\uB300\uD3F0\uC73C\uB85C \uC2A4\uCE94\uD560 \uC218 \uC788\uB3C4\uB85D \uD558\uAE30 \uC704\uD568\uC785\uB2C8\uB2E4.",
|
|
791
|
+
"pair.promptDescription": "\uD734\uB300\uD3F0 \uC6D0\uACA9 \uD398\uC5B4\uB9C1\uC758 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\xB7\uC2A4\uCE94\uC6A9 QR \uCF54\uB4DC \uD45C\uC2DC",
|
|
792
|
+
"pair.scanIntro": "**\uC2A4\uCE94\uC73C\uB85C \uC5F0\uACB0**: \uD734\uB300\uD3F0\uC5D0\uC11C tunnelbox \uC571 \u201C\uC2A4\uCE94\uD558\uC5EC \uC5F0\uACB0\u201D\uC744 \uC5F4\uACE0 \uC544\uB798 QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uC5F0\uACB0\uB429\uB2C8\uB2E4.",
|
|
672
793
|
"perm.promptAuthorize": "opencode\uAC00 \uAD8C\uD55C\uC744 \uC694\uCCAD\uD569\uB2C8\uB2E4",
|
|
673
794
|
"perm.promptPatterns": "opencode\uAC00 \uC811\uADFC\uC744 \uC694\uCCAD: {list}",
|
|
674
795
|
"perm.promptResource": "opencode\uAC00 \uB9AC\uC18C\uC2A4 \uC811\uADFC\uC744 \uC694\uCCAD: {list}",
|
|
675
796
|
"perm.promptTool": "opencode\uAC00 {tool} \uC2E4\uD589\uC744 \uC694\uCCAD",
|
|
676
797
|
"qr.codeLabel": "\uD398\uC5B4\uB9C1 \uCF54\uB4DC",
|
|
677
798
|
"qr.fileWritten": "\uD398\uC5B4\uB9C1 \uC815\uBCF4\uAC00 \uAE30\uB85D\uB428: {path}",
|
|
678
|
-
"qr.
|
|
679
|
-
"qr.openHint": "\uD734\uB300\uD3F0 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB9C1\uD06C\uB97C \uC5F4\uAC70\uB098 \uC571\uC5D0\uC11C \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC9C1\uC811 \uC785\uB825\uD558\uC138\uC694.",
|
|
799
|
+
"qr.openHint": "tunnelbox \uC571\uC744 \uC5F4\uACE0 \uC704 QR \uCF54\uB4DC\uB97C \uC2A4\uCE94\uD558\uAC70\uB098, \uC571\uC5D0\uC11C \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC9C1\uC811 \uC785\uB825\uD558\uC138\uC694.",
|
|
680
800
|
"qr.titleNoType": "tunnelbox \uD734\uB300\uD3F0 \uD398\uC5B4\uB9C1",
|
|
681
801
|
"qr.titleWithType": "tunnelbox {type} \uD734\uB300\uD3F0 \uD398\uC5B4\uB9C1",
|
|
682
|
-
"qr.unavailable": "(qrcode\uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC74C \u2014 \uC704\uC758 \
|
|
802
|
+
"qr.unavailable": "(qrcode\uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC74C \u2014 \uC704\uC758 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC0AC\uC6A9\uD558\uC138\uC694)",
|
|
683
803
|
"claude.errBusy": "\uC774 \uC138\uC158\uC774 \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4. \uC644\uB8CC\uB420 \uB54C\uAE4C\uC9C0 \uAE30\uB2E4\uB9AC\uAC70\uB098 \uBA3C\uC800 \uC911\uC9C0\uD558\uC138\uC694",
|
|
684
804
|
"claude.errTurn": "\uC0DD\uC131 \uC624\uB958",
|
|
685
805
|
"claude.pairingStillClaimed": "\uC774\uBBF8 \uACC4\uC815\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uC774 \uCF54\uB4DC\uB294 \uAC19\uC740 \uACC4\uC815\uC758 \uC0C8 \uAE30\uAE30 \uC5F0\uACB0 \uC804\uC6A9\uC774\uBA70, \uD604\uC7AC \uAE30\uAE30\uB294 '\uB0B4 \uCEF4\uD4E8\uD130'\uC5D0\uC11C \uC9C1\uC811 \uC5F0\uACB0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4",
|
|
@@ -747,7 +867,41 @@ var ko_KR_default = {
|
|
|
747
867
|
"claude.errReadHistory": "\uC138\uC158 \uAE30\uB85D\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4({id})",
|
|
748
868
|
"setup.langSet": "\uC2E4\uD589 \uC5B8\uC5B4\uB97C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4",
|
|
749
869
|
"setup.langHint": "\uB2E4\uC2DC \uC2DC\uC791\uD558\uBA74 \uC774 \uC5B8\uC5B4\uB85C \uCD9C\uB825\uB429\uB2C8\uB2E4(\uC2E4\uD589 \uC2DC TUNNELBOX_LANG\uC73C\uB85C \uC7AC\uC815\uC758 \uAC00\uB2A5). \uC0C1\uD0DC \uD30C\uC77C:",
|
|
750
|
-
"setup.langCurrent": "\uD604\uC7AC \uC2E4\uD589 \uC5B8\uC5B4"
|
|
870
|
+
"setup.langCurrent": "\uD604\uC7AC \uC2E4\uD589 \uC5B8\uC5B4",
|
|
871
|
+
"hermes.errNoRelay": "\uB9B4\uB808\uC774 URL\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: --env\uB85C \uAE30\uBCF8\uAC12\uC744 \uC8FC\uC785\uD558\uAC70\uB098 \uC2E4\uD589 \uC2DC TUNNELBOX_RELAY_URL\uB85C \uC7AC\uC815\uC758\uD558\uC138\uC694",
|
|
872
|
+
"hermes.logConnected": "\uB9B4\uB808\uC774\uC5D0 \uC5F0\uACB0\uB428 {url}",
|
|
873
|
+
"hermes.logMsgUncaught": "\uBA54\uC2DC\uC9C0 \uCC98\uB9AC \uC911 \uCC98\uB9AC\uB418\uC9C0 \uC54A\uC740 \uC608\uC678: {message}",
|
|
874
|
+
"hermes.logDisconnected": "\uB9B4\uB808\uC774\uC640 \uC5F0\uACB0 \uB04A\uAE40, \uC790\uB3D9 \uC7AC\uC5F0\uACB0 \uC911\u2026",
|
|
875
|
+
"hermes.logPairCode": "\uD398\uC5B4\uB9C1 \uCF54\uB4DC \uC0DD\uC131: {code}",
|
|
876
|
+
"hermes.logUnhandledType": "\uCC98\uB9AC\uB418\uC9C0 \uC54A\uC740 \uBA54\uC2DC\uC9C0 \uC720\uD615: {type}",
|
|
877
|
+
"hermes.logHandleFailed": "{type} \uCC98\uB9AC \uC2E4\uD328: {message}",
|
|
878
|
+
"hermes.logClaimed": "\uD734\uB300\uD3F0 \uACC4\uC815\uC5D0 \uBC14\uC778\uB529\uB428. \uC774\uC81C \uD734\uB300\uD3F0\uC758 '\uB0B4 \uCEF4\uD4E8\uD130'\uC5D0\uC11C \uC9C1\uC811 \uC5F0\uACB0\uD569\uB2C8\uB2E4",
|
|
879
|
+
"hermes.logResetIdentity": "\uACC4\uC815\uC5D0\uC11C \uBC14\uC778\uB529 \uD574\uC81C\uB428, ID \uC7AC\uC124\uC815 \uBC0F \uC0C8 \uD398\uC5B4\uB9C1 \uCF54\uB4DC \uC0DD\uC131 \uC911\u2026",
|
|
880
|
+
"hermes.cmdNew": "\uC0C8 \uC138\uC158",
|
|
881
|
+
"hermes.cmdHelp": "\uB3C4\uC6C0\uB9D0 \uD45C\uC2DC",
|
|
882
|
+
"hermes.newSessionTitle": "\uC0C8 \uC138\uC158",
|
|
883
|
+
"hermes.helpText": "\uC9C0\uC6D0 \uBA85\uB839:\n- /new \uC0C8 \uC138\uC158\n- /help \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC",
|
|
884
|
+
"hermes.errBusy": "\uC774 \uC138\uC158\uC774 \uC0DD\uC131 \uC911\uC785\uB2C8\uB2E4. \uC644\uB8CC\uB97C \uAE30\uB2E4\uB9AC\uAC70\uB098 \uBA3C\uC800 \uC911\uC9C0\uD558\uC138\uC694",
|
|
885
|
+
"hermes.errBadWorkspace": "\uC774 \uB514\uB809\uD1A0\uB9AC\uB294 \uC874\uC7AC\uD558\uC9C0 \uC54A\uAC70\uB098 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
886
|
+
"hermes.logWorkspaceSwitched": "\uC791\uC5C5\uACF5\uAC04 \uC804\uD658: {path}",
|
|
887
|
+
"hermes.errWorkspaceMissing": "\uC791\uC5C5\uACF5\uAC04\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {dir}",
|
|
888
|
+
"hermes.errStartFailed": "\uC2DC\uC791 \uC2E4\uD328: {message}",
|
|
889
|
+
"hermes.msgStarted": "Hermes \uC5B4\uB311\uD130 \uC2DC\uC791\uB428 (Ctrl+C\uB85C \uC885\uB8CC)",
|
|
890
|
+
"hermes.logAbortSent": "\uC911\uC9C0 \uC804\uC1A1\uB428 (session={session})",
|
|
891
|
+
"hermes.logPromptFailed": "session.prompt \uC2E4\uD328: {message}",
|
|
892
|
+
"hermes.logTurnError": "\uD134 \uC624\uB958 (session={session}): {message}",
|
|
893
|
+
"hermes.logPairForced": "--pair: \uD398\uC5B4\uB9C1 \uCF54\uB4DC \uAC15\uC81C \uC694\uCCAD (\uC774\uBC88\uC5D0\uB9CC)",
|
|
894
|
+
"hermes.pairingStillClaimed": "\uD734\uB300\uD3F0 \uACC4\uC815\uC5D0 \uC774\uBBF8 \uBC14\uC778\uB529\uB428: \uC774 \uCF54\uB4DC\uB294 \uAC19\uC740 \uACC4\uC815\uC758 \uC0C8 \uAE30\uAE30 \uC7AC\uD398\uC5B4\uB9C1\uC6A9\uC785\uB2C8\uB2E4. \uD604\uC7AC \uAE30\uAE30\uB294 \uD734\uB300\uD3F0\uC758 '\uB0B4 \uCEF4\uD4E8\uD130'\uC5D0\uC11C \uC9C1\uC811 \uC5F0\uACB0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4",
|
|
895
|
+
"hermes.logPairNoRelay": "\uB9B4\uB808\uC774 URL\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC544 \uD398\uC5B4\uB9C1 \uCF54\uB4DC\uB97C \uC694\uCCAD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
896
|
+
"hermes.errSpawnHermes": "Hermes({bin})\uB97C \uC2DC\uC791\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: \uC124\uCE58 \uC5EC\uBD80\uB97C \uD655\uC778\uD558\uC138\uC694 ({err})",
|
|
897
|
+
"hermes.errHermesExit": "hermes \uC885\uB8CC({code}): {err}",
|
|
898
|
+
"hermes.logSessionUpdated": "\uC138\uC158 \uC5C5\uB370\uC774\uD2B8\uB428 (session={session})",
|
|
899
|
+
"hermes.errNoInject": "\uD734\uB300\uD3F0 \uBA54\uC2DC\uC9C0 \uC8FC\uC785\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uBE44\uB300\uD654\uD615 \uC138\uC158 \uB610\uB294 \uD504\uB85C\uC138\uC2A4 \uBBF8\uC900\uBE44)",
|
|
900
|
+
"hermes.pairNoRelay": "\uB9B4\uB808\uC774\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uB9B4\uB808\uC774 \uB3C4\uB2EC \uBD88\uAC00 \uB610\uB294 websockets \uB204\uB77D)",
|
|
901
|
+
"hermes.pairTimeout": "\uD398\uC5B4\uB9C1 \uCF54\uB4DC \uC694\uCCAD \uC2DC\uAC04 \uCD08\uACFC(\uB9B4\uB808\uC774 \uBBF8\uC751\uB2F5)",
|
|
902
|
+
"hermes.errToolPair": "tunnelbox_pair \uC2E4\uD328: {err}",
|
|
903
|
+
"hermes.errToolStatus": "tunnelbox_status \uC2E4\uD328: {err}",
|
|
904
|
+
"hermes.cliUsage": "\uC0AC\uC6A9\uBC95: /tunnelbox <pair|status>\n pair \uD734\uB300\uD3F0 \uD398\uC5B4\uB9C1 \uCF54\uB4DC \uC694\uCCAD/\uCD9C\uB825\n status \uB9B4\uB808\uC774/\uBC14\uC778\uB529/\uC5F0\uACB0 \uC0C1\uD0DC \uD45C\uC2DC"
|
|
751
905
|
};
|
|
752
906
|
|
|
753
907
|
// ../core/locales/fr-FR.json
|
|
@@ -785,30 +939,28 @@ var fr_FR_default = {
|
|
|
785
939
|
"install.failConnect": "(Impossible de se connecter au relais pour afficher l'appairage \u2014 relais injoignable ou version de Node trop ancienne.",
|
|
786
940
|
"install.failConnectHint": "Apr\xE8s le red\xE9marrage d'opencode, consultez le QR code du terminal avec --print-logs, ou tapez \u201Cafficher le code d'appairage\u201D / /pair dans opencode.)",
|
|
787
941
|
"install.hintFresh": "Remarque : le code d'appairage est \xE0 usage unique et valide environ 10 minutes. Si vous d\xE9marrez opencode plus tard, utilisez les informations imprim\xE9es au d\xE9marrage ou obtenues via /pair.",
|
|
788
|
-
"install.linkLine": "Lien d'appairage : {link}",
|
|
789
942
|
"install.pluginInstalled": "Plugin install\xE9 : {path}",
|
|
790
|
-
"install.qrUnavailable": "(Rendu du QR indisponible \u2014 utilisez le
|
|
943
|
+
"install.qrUnavailable": "(Rendu du QR indisponible \u2014 utilisez le code ci-dessus)",
|
|
791
944
|
"install.qrcodeEnsure": "Installation de qrcode dans le r\xE9pertoire de configuration d'opencode\u2026",
|
|
792
|
-
"install.qrcodeFail": "\xC9chec de l'installation de qrcode (sans cons\xE9quence \u2014 le
|
|
945
|
+
"install.qrcodeFail": "\xC9chec de l'installation de qrcode (sans cons\xE9quence \u2014 seul le code d'appairage est affich\xE9) : {err}",
|
|
793
946
|
"install.relayError": "Le relais a renvoy\xE9 une erreur : {msg}",
|
|
794
|
-
"install.scanHint": "Ouvrez tunnelbox sur votre t\xE9l\xE9phone, touchez \u201CScanner pour appairer\u201D,
|
|
947
|
+
"install.scanHint": "Ouvrez tunnelbox sur votre t\xE9l\xE9phone, touchez \u201CScanner pour appairer\u201D, puis scannez le QR ci-dessus pour connecter :",
|
|
795
948
|
"pair.heading": "Appairage t\xE9l\xE9phone tunnelbox",
|
|
796
|
-
"pair.howTo": "Ouvrez
|
|
949
|
+
"pair.howTo": "Ouvrez l'application tunnelbox sur votre t\xE9l\xE9phone, scannez le QR ci-dessus avec \u201CScanner pour appairer\u201D, ou saisissez le code d'appairage manuellement.",
|
|
797
950
|
"pair.oneTime": "Le code d'appairage est \xE0 usage unique et expire apr\xE8s utilisation ; demandez \xE0 \u201Craffra\xEEchir le code d'appairage\u201D pour en obtenir un nouveau.",
|
|
798
|
-
"pair.promptBody": "Veuillez afficher les informations d'appairage t\xE9l\xE9phone de cet ordinateur : le code d'appairage
|
|
799
|
-
"pair.promptDescription": "Afficher le code d'appairage
|
|
800
|
-
"pair.scanIntro": "**Appairer par scan** : ouvrez l'application tunnelbox sur votre t\xE9l\xE9phone
|
|
951
|
+
"pair.promptBody": "Veuillez afficher les informations d'appairage t\xE9l\xE9phone de cet ordinateur : le code d'appairage et un QR code scannable. Gardez le QR code dans un bloc de code \xE0 largeur fixe et reproduisez-le caract\xE8re par caract\xE8re pour que mon t\xE9l\xE9phone puisse le scanner.",
|
|
952
|
+
"pair.promptDescription": "Afficher le code d'appairage et le QR code scannable",
|
|
953
|
+
"pair.scanIntro": "**Appairer par scan** : ouvrez l'application tunnelbox sur votre t\xE9l\xE9phone, touchez \u201CScanner pour appairer\u201D, puis scannez le QR ci-dessous pour vous connecter automatiquement.",
|
|
801
954
|
"perm.promptAuthorize": "opencode demande une autorisation",
|
|
802
955
|
"perm.promptPatterns": "opencode demande l'acc\xE8s \xE0 : {list}",
|
|
803
956
|
"perm.promptResource": "opencode demande l'acc\xE8s aux ressources : {list}",
|
|
804
957
|
"perm.promptTool": "opencode demande d'ex\xE9cuter {tool}",
|
|
805
958
|
"qr.codeLabel": "Code d'appairage",
|
|
806
959
|
"qr.fileWritten": "Informations d'appairage \xE9crites dans : {path}",
|
|
807
|
-
"qr.
|
|
808
|
-
"qr.openHint": "Ouvrez le lien dans le navigateur du t\xE9l\xE9phone, ou saisissez le code d'appairage dans l'application.",
|
|
960
|
+
"qr.openHint": "Ouvrez l'application tunnelbox sur votre t\xE9l\xE9phone et scannez le QR ci-dessus, ou saisissez le code d'appairage manuellement dans l'application.",
|
|
809
961
|
"qr.titleNoType": "appairage t\xE9l\xE9phone tunnelbox",
|
|
810
962
|
"qr.titleWithType": "appairage t\xE9l\xE9phone tunnelbox {type}",
|
|
811
|
-
"qr.unavailable": "(qrcode non install\xE9 \u2014 utilisez le
|
|
963
|
+
"qr.unavailable": "(qrcode non install\xE9 \u2014 utilisez le code d'appairage ci-dessus)",
|
|
812
964
|
"claude.errBusy": "Cette session est en cours d'ex\xE9cution ; attendez la fin ou arr\xEAtez-la d'abord",
|
|
813
965
|
"claude.errTurn": "erreur de g\xE9n\xE9ration",
|
|
814
966
|
"claude.pairingStillClaimed": "D\xE9j\xE0 li\xE9 \xE0 un compte t\xE9l\xE9phone : ce code sert uniquement \xE0 appairer un nouvel appareil sur le m\xEAme compte ; l'appareil actuel se connecte directement depuis Mes ordinateurs",
|
|
@@ -876,7 +1028,41 @@ var fr_FR_default = {
|
|
|
876
1028
|
"claude.errReadHistory": "\xC9chec de la lecture de l'historique de session ({id})",
|
|
877
1029
|
"setup.langSet": "Langue d'ex\xE9cution d\xE9finie sur",
|
|
878
1030
|
"setup.langHint": "L'adaptateur utilisera cette langue au prochain d\xE9marrage (TUNNELBOX_LANG peut la remplacer). Fichier d'\xE9tat :",
|
|
879
|
-
"setup.langCurrent": "Langue d'ex\xE9cution actuelle"
|
|
1031
|
+
"setup.langCurrent": "Langue d'ex\xE9cution actuelle",
|
|
1032
|
+
"hermes.errNoRelay": "URL du relais non configur\xE9e : utilisez --env pour injecter une valeur par d\xE9faut, ou remplacez \xE0 l'ex\xE9cution avec TUNNELBOX_RELAY_URL",
|
|
1033
|
+
"hermes.logConnected": "Connect\xE9 au relais {url}",
|
|
1034
|
+
"hermes.logMsgUncaught": "Exception non intercept\xE9e lors du traitement du message : {message}",
|
|
1035
|
+
"hermes.logDisconnected": "D\xE9connect\xE9 du relais, reconnexion automatique\u2026",
|
|
1036
|
+
"hermes.logPairCode": "Code d'appairage g\xE9n\xE9r\xE9 : {code}",
|
|
1037
|
+
"hermes.logUnhandledType": "Type de message non trait\xE9 : {type}",
|
|
1038
|
+
"hermes.logHandleFailed": "\xC9chec du traitement de {type} : {message}",
|
|
1039
|
+
"hermes.logClaimed": "Li\xE9 \xE0 votre compte t\xE9l\xE9phone. Le t\xE9l\xE9phone se connecte d\xE9sormais directement depuis Mes ordinateurs",
|
|
1040
|
+
"hermes.logResetIdentity": "Dissoci\xE9 du compte ; r\xE9initialisation de l'identit\xE9 et g\xE9n\xE9ration d'un nouveau code d'appairage\u2026",
|
|
1041
|
+
"hermes.cmdNew": "Nouvelle session",
|
|
1042
|
+
"hermes.cmdHelp": "Afficher l'aide",
|
|
1043
|
+
"hermes.newSessionTitle": "Nouvelle session",
|
|
1044
|
+
"hermes.helpText": "Commandes prises en charge :\n- /new nouvelle session\n- /help afficher l'aide",
|
|
1045
|
+
"hermes.errBusy": "Cette session est en cours de g\xE9n\xE9ration ; attendez la fin ou arr\xEAtez-la d'abord",
|
|
1046
|
+
"hermes.errBadWorkspace": "Ce r\xE9pertoire n'existe pas ou est inutilisable",
|
|
1047
|
+
"hermes.logWorkspaceSwitched": "Espace de travail chang\xE9 : {path}",
|
|
1048
|
+
"hermes.errWorkspaceMissing": "L'espace de travail n'existe pas : {dir}",
|
|
1049
|
+
"hermes.errStartFailed": "\xC9chec du d\xE9marrage : {message}",
|
|
1050
|
+
"hermes.msgStarted": "Adaptateur Hermes d\xE9marr\xE9 (Ctrl+C pour quitter)",
|
|
1051
|
+
"hermes.logAbortSent": "Interruption envoy\xE9e (session={session})",
|
|
1052
|
+
"hermes.logPromptFailed": "session.prompt a \xE9chou\xE9 : {message}",
|
|
1053
|
+
"hermes.logTurnError": "Erreur de tour (session={session}) : {message}",
|
|
1054
|
+
"hermes.logPairForced": "--pair : demande forc\xE9e du code d'appairage (cet ex\xE9cution uniquement)",
|
|
1055
|
+
"hermes.pairingStillClaimed": "D\xE9j\xE0 li\xE9 \xE0 un compte t\xE9l\xE9phone : ce code ne sert qu'\xE0 r\xE9appairer un nouvel appareil sur le m\xEAme compte ; l'appareil actuel se connecte directement depuis Mes ordinateurs",
|
|
1056
|
+
"hermes.logPairNoRelay": "Impossible de demander un code d'appairage : URL du relais non configur\xE9e",
|
|
1057
|
+
"hermes.errSpawnHermes": "Impossible de d\xE9marrer Hermes ({bin}) : v\xE9rifiez qu'il est install\xE9 ({err})",
|
|
1058
|
+
"hermes.errHermesExit": "hermes a quitt\xE9 ({code}) : {err}",
|
|
1059
|
+
"hermes.logSessionUpdated": "Session mise \xE0 jour (session={session})",
|
|
1060
|
+
"hermes.errNoInject": "Injection du message t\xE9l\xE9phone indisponible (session non interactive ou processus non pr\xEAt)",
|
|
1061
|
+
"hermes.pairNoRelay": "Impossible de joindre le relais (relais injoignable ou websockets manquant)",
|
|
1062
|
+
"hermes.pairTimeout": "D\xE9lai d\xE9pass\xE9 pour le code d'appairage (relais sans r\xE9ponse)",
|
|
1063
|
+
"hermes.errToolPair": "\xE9chec de tunnelbox_pair : {err}",
|
|
1064
|
+
"hermes.errToolStatus": "\xE9chec de tunnelbox_status : {err}",
|
|
1065
|
+
"hermes.cliUsage": "Utilisation : /tunnelbox <pair|status>\n pair demander et afficher un code d'appairage\n status afficher l'\xE9tat relais/liaison/connexion"
|
|
880
1066
|
};
|
|
881
1067
|
|
|
882
1068
|
// ../core/locales/de-DE.json
|
|
@@ -914,30 +1100,28 @@ var de_DE_default = {
|
|
|
914
1100
|
"install.failConnect": "(Keine sofortige Verbindung zum Relay zum Drucken der Pairing-Informationen m\xF6glich \u2014 Relay nicht erreichbar oder Node zu alt.",
|
|
915
1101
|
"install.failConnectHint": "Nach dem Neustart von opencode den Terminal-QR-Code mit --print-logs anzeigen oder im opencode \u201CTelefon-Pairing-Code anzeigen\u201D bzw. /pair eingeben.)",
|
|
916
1102
|
"install.hintFresh": "Hinweis: Der Pairing-Code ist einmalig und etwa 10 Minuten g\xFCltig. Wenn Sie opencode sp\xE4ter starten, nutzen Sie die beim Start gedruckten oder per /pair abgerufenen aktuellen Informationen.",
|
|
917
|
-
"install.linkLine": "Pairing-Link: {link}",
|
|
918
1103
|
"install.pluginInstalled": "Plugin installiert: {path}",
|
|
919
|
-
"install.qrUnavailable": "(QR-Darstellung nicht verf\xFCgbar \u2014 nutzen Sie den
|
|
1104
|
+
"install.qrUnavailable": "(QR-Darstellung nicht verf\xFCgbar \u2014 nutzen Sie den Code oben)",
|
|
920
1105
|
"install.qrcodeEnsure": "Installiere qrcode in das opencode-Konfigurationsverzeichnis\u2026",
|
|
921
|
-
"install.qrcodeFail": "qrcode-Installation fehlgeschlagen (nicht kritisch \u2014
|
|
1106
|
+
"install.qrcodeFail": "qrcode-Installation fehlgeschlagen (nicht kritisch \u2014 nur der Pairing-Code wird angezeigt): {err}",
|
|
922
1107
|
"install.relayError": "Relay hat einen Fehler zur\xFCckgegeben: {msg}",
|
|
923
|
-
"install.scanHint": "\xD6ffnen Sie tunnelbox auf dem Telefon, tippen Sie auf \u201CZum Pairing scannen\u201D
|
|
1108
|
+
"install.scanHint": "\xD6ffnen Sie tunnelbox auf dem Telefon, tippen Sie auf \u201CZum Pairing scannen\u201D und scannen Sie den QR-Code oben zum Verbinden:",
|
|
924
1109
|
"pair.heading": "tunnelbox Telefon-Pairing",
|
|
925
|
-
"pair.howTo": "\xD6ffnen Sie
|
|
1110
|
+
"pair.howTo": "\xD6ffnen Sie die tunnelbox-App auf dem Telefon und scannen Sie den QR-Code oben mit \u201CZum Pairing scannen\u201D, oder geben Sie den Pairing-Code manuell ein.",
|
|
926
1111
|
"pair.oneTime": "Der Pairing-Code ist einmalig und verf\xE4llt nach der Nutzung; f\xFCr einen neuen Code \u201CPairing-Code aktualisieren\u201D anfordern.",
|
|
927
|
-
"pair.promptBody": "Zeigen Sie bitte die Telefon-Pairing-Informationen dieses Computers an: den Pairing-Code
|
|
928
|
-
"pair.promptDescription": "Pairing-Code
|
|
929
|
-
"pair.scanIntro": "**Per Scan pairen**: \xD6ffnen Sie die tunnelbox-App auf dem Telefon
|
|
1112
|
+
"pair.promptBody": "Zeigen Sie bitte die Telefon-Pairing-Informationen dieses Computers an: den Pairing-Code und einen scannbaren QR-Code. Behalten Sie den QR-Code in einem Codeblock mit fester Breite und geben Sie ihn zeichengenau wieder, damit mein Telefon ihn scannen kann.",
|
|
1113
|
+
"pair.promptDescription": "Pairing-Code und scannbaren QR-Code anzeigen",
|
|
1114
|
+
"pair.scanIntro": "**Per Scan pairen**: \xD6ffnen Sie die tunnelbox-App auf dem Telefon, tippen Sie auf \u201CZum Pairing scannen\u201D und scannen Sie den QR-Code unten, um sich automatisch zu verbinden.",
|
|
930
1115
|
"perm.promptAuthorize": "opencode fordert eine Genehmigung an",
|
|
931
1116
|
"perm.promptPatterns": "opencode fordert Zugriff auf: {list}",
|
|
932
1117
|
"perm.promptResource": "opencode fordert Zugriff auf Ressourcen: {list}",
|
|
933
1118
|
"perm.promptTool": "opencode fordert die Ausf\xFChrung von {tool} an",
|
|
934
1119
|
"qr.codeLabel": "Pairing-Code",
|
|
935
1120
|
"qr.fileWritten": "Pairing-Informationen geschrieben in: {path}",
|
|
936
|
-
"qr.
|
|
937
|
-
"qr.openHint": "\xD6ffnen Sie den Link im Telefon-Browser oder geben Sie den Pairing-Code manuell in der App ein.",
|
|
1121
|
+
"qr.openHint": "\xD6ffnen Sie die tunnelbox-App auf dem Telefon und scannen Sie den QR-Code oben, oder geben Sie den Pairing-Code manuell in der App ein.",
|
|
938
1122
|
"qr.titleNoType": "tunnelbox Telefon-Pairing",
|
|
939
1123
|
"qr.titleWithType": "tunnelbox {type} Telefon-Pairing",
|
|
940
|
-
"qr.unavailable": "(qrcode nicht installiert \u2014 nutzen Sie den
|
|
1124
|
+
"qr.unavailable": "(qrcode nicht installiert \u2014 nutzen Sie den Pairing-Code oben)",
|
|
941
1125
|
"claude.errBusy": "Diese Sitzung l\xE4uft noch; warten Sie auf das Ende oder stoppen Sie sie zuerst",
|
|
942
1126
|
"claude.errTurn": "Generierungsfehler",
|
|
943
1127
|
"claude.pairingStillClaimed": "Bereits an ein Telefonkonto gebunden: Dieser Code dient nur zum Koppeln eines neuen Ger\xE4ts auf demselben Konto; das aktuelle Ger\xE4t verbindet sich direkt \xFCber \u201EMeine Computer\u201C",
|
|
@@ -1005,7 +1189,41 @@ var de_DE_default = {
|
|
|
1005
1189
|
"claude.errReadHistory": "Sitzungsverlauf konnte nicht gelesen werden ({id})",
|
|
1006
1190
|
"setup.langSet": "Laufzeitsprache gesetzt auf",
|
|
1007
1191
|
"setup.langHint": "Der Adapter gibt nach dem Neustart in dieser Sprache aus (TUNNELBOX_LANG kann zur Laufzeit \xFCberschreiben). Statusdatei:",
|
|
1008
|
-
"setup.langCurrent": "Aktuelle Laufzeitsprache"
|
|
1192
|
+
"setup.langCurrent": "Aktuelle Laufzeitsprache",
|
|
1193
|
+
"hermes.errNoRelay": "Keine Relay-URL konfiguriert: mit --env einen Standardwert einf\xFCgen oder zur Laufzeit mit TUNNELBOX_RELAY_URL \xFCberschreiben",
|
|
1194
|
+
"hermes.logConnected": "Mit Relay verbunden {url}",
|
|
1195
|
+
"hermes.logMsgUncaught": "Nicht abgefangene Ausnahme bei der Nachrichtenverarbeitung: {message}",
|
|
1196
|
+
"hermes.logDisconnected": "Vom Relay getrennt, automatische Neuverbindung\u2026",
|
|
1197
|
+
"hermes.logPairCode": "Pairing-Code generiert: {code}",
|
|
1198
|
+
"hermes.logUnhandledType": "Nicht behandelter Nachrichtentyp: {type}",
|
|
1199
|
+
"hermes.logHandleFailed": "Fehler beim Verarbeiten von {type}: {message}",
|
|
1200
|
+
"hermes.logClaimed": "An Ihr Telefonkonto gebunden. Das Telefon verbindet sich k\xFCnftig direkt \xFCber Meine Computer",
|
|
1201
|
+
"hermes.logResetIdentity": "Vom Konto entbunden; Identit\xE4t wird zur\xFCckgesetzt und ein neuer Pairing-Code generiert\u2026",
|
|
1202
|
+
"hermes.cmdNew": "Neue Sitzung",
|
|
1203
|
+
"hermes.cmdHelp": "Hilfe anzeigen",
|
|
1204
|
+
"hermes.newSessionTitle": "Neue Sitzung",
|
|
1205
|
+
"hermes.helpText": "Unterst\xFCtzte Befehle:\n- /new neue Sitzung\n- /help Hilfe anzeigen",
|
|
1206
|
+
"hermes.errBusy": "Diese Sitzung wird gerade generiert; warten Sie auf den Abschluss oder stoppen Sie sie zuerst",
|
|
1207
|
+
"hermes.errBadWorkspace": "Dieses Verzeichnis existiert nicht oder ist unbrauchbar",
|
|
1208
|
+
"hermes.logWorkspaceSwitched": "Arbeitsbereich gewechselt: {path}",
|
|
1209
|
+
"hermes.errWorkspaceMissing": "Arbeitsbereich existiert nicht: {dir}",
|
|
1210
|
+
"hermes.errStartFailed": "Start fehlgeschlagen: {message}",
|
|
1211
|
+
"hermes.msgStarted": "Hermes-Adapter gestartet (Strg+C zum Beenden)",
|
|
1212
|
+
"hermes.logAbortSent": "Abbruch gesendet (session={session})",
|
|
1213
|
+
"hermes.logPromptFailed": "session.prompt fehlgeschlagen: {message}",
|
|
1214
|
+
"hermes.logTurnError": "Fehler bei Runde (session={session}): {message}",
|
|
1215
|
+
"hermes.logPairForced": "--pair: Pairing-Code erzwungen (nur dieser Lauf)",
|
|
1216
|
+
"hermes.pairingStillClaimed": "Bereits an ein Telefonkonto gebunden: Dieser Code dient nur zum erneuten Pairing eines neuen Ger\xE4ts desselben Kontos; das aktuelle Ger\xE4t verbindet sich direkt \xFCber Meine Computer",
|
|
1217
|
+
"hermes.logPairNoRelay": "Kein Pairing-Code anforderbar: keine Relay-URL konfiguriert",
|
|
1218
|
+
"hermes.errSpawnHermes": "Hermes ({bin}) kann nicht gestartet werden: bitte Installation pr\xFCfen ({err})",
|
|
1219
|
+
"hermes.errHermesExit": "hermes beendet ({code}): {err}",
|
|
1220
|
+
"hermes.logSessionUpdated": "Sitzung aktualisiert (session={session})",
|
|
1221
|
+
"hermes.errNoInject": "Telefon-Nachrichteneinspeisung nicht verf\xFCgbar (Sitzung nicht interaktiv oder Prozess nicht bereit)",
|
|
1222
|
+
"hermes.pairNoRelay": "Relay nicht erreichbar (Relay nicht erreichbar oder websockets fehlt)",
|
|
1223
|
+
"hermes.pairTimeout": "Zeit\xFCberschreitung bei der Pairing-Code-Anfrage (Relay antwortete nicht)",
|
|
1224
|
+
"hermes.errToolPair": "tunnelbox_pair fehlgeschlagen: {err}",
|
|
1225
|
+
"hermes.errToolStatus": "tunnelbox_status fehlgeschlagen: {err}",
|
|
1226
|
+
"hermes.cliUsage": "Verwendung: /tunnelbox <pair|status>\n pair Pairing-Code anfordern und anzeigen\n status Relay/Bindung/Verbindung anzeigen"
|
|
1009
1227
|
};
|
|
1010
1228
|
|
|
1011
1229
|
// ../core/locales/es-ES.json
|
|
@@ -1043,30 +1261,28 @@ var es_ES_default = {
|
|
|
1043
1261
|
"install.failConnect": "(No se pudo conectar de inmediato con el relay para imprimir el emparejamiento \u2014 relay inaccesible o versi\xF3n de Node demasiado antigua.",
|
|
1044
1262
|
"install.failConnectHint": "Tras reiniciar opencode, consulte el c\xF3digo QR del terminal con --print-logs, o escriba \u201Cmostrar c\xF3digo de emparejamiento del tel\xE9fono\u201D o /pair en opencode.)",
|
|
1045
1263
|
"install.hintFresh": "Nota: el c\xF3digo de emparejamiento es de un solo uso y v\xE1lido durante unos 10 minutos. Si inicia opencode m\xE1s tarde, use la informaci\xF3n m\xE1s reciente impresa al iniciar u obtenida con /pair.",
|
|
1046
|
-
"install.linkLine": "Enlace de emparejamiento: {link}",
|
|
1047
1264
|
"install.pluginInstalled": "Complemento instalado: {path}",
|
|
1048
|
-
"install.qrUnavailable": "(Representaci\xF3n del QR no disponible: use el
|
|
1265
|
+
"install.qrUnavailable": "(Representaci\xF3n del QR no disponible: use el c\xF3digo anterior)",
|
|
1049
1266
|
"install.qrcodeEnsure": "Instalando qrcode en el directorio de configuraci\xF3n de opencode\u2026",
|
|
1050
|
-
"install.qrcodeFail": "Error al instalar qrcode (no es cr\xEDtico;
|
|
1267
|
+
"install.qrcodeFail": "Error al instalar qrcode (no es cr\xEDtico; solo se muestra el c\xF3digo de emparejamiento): {err}",
|
|
1051
1268
|
"install.relayError": "El relay devolvi\xF3 un error: {msg}",
|
|
1052
|
-
"install.scanHint": "Abra tunnelbox en el tel\xE9fono, pulse \u201CEscanear para emparejar\u201D
|
|
1269
|
+
"install.scanHint": "Abra tunnelbox en el tel\xE9fono, pulse \u201CEscanear para emparejar\u201D y escanee el c\xF3digo QR superior para conectar:",
|
|
1053
1270
|
"pair.heading": "Emparejamiento de tel\xE9fono tunnelbox",
|
|
1054
|
-
"pair.howTo": "Abra
|
|
1271
|
+
"pair.howTo": "Abra la aplicaci\xF3n tunnelbox en el tel\xE9fono y escanee el c\xF3digo QR superior con \u201CEscanear para emparejar\u201D, o introduzca el c\xF3digo de emparejamiento manualmente.",
|
|
1055
1272
|
"pair.oneTime": "El c\xF3digo de emparejamiento es de un solo uso y caduca al utilizarse; pida \u201Cactualizar el c\xF3digo de emparejamiento\u201D para obtener uno nuevo.",
|
|
1056
|
-
"pair.promptBody": "Mu\xE9strame la informaci\xF3n de emparejamiento con el tel\xE9fono de este ordenador: el c\xF3digo de emparejamiento
|
|
1057
|
-
"pair.promptDescription": "Mostrar el c\xF3digo de emparejamiento
|
|
1058
|
-
"pair.scanIntro": "**Emparejar escaneando**: abra la aplicaci\xF3n tunnelbox en el tel\xE9fono
|
|
1273
|
+
"pair.promptBody": "Mu\xE9strame la informaci\xF3n de emparejamiento con el tel\xE9fono de este ordenador: el c\xF3digo de emparejamiento y un c\xF3digo QR escaneable. Mant\xE9n el c\xF3digo QR dentro de un bloque de c\xF3digo de ancho fijo y reprod\xFAcelo car\xE1cter por car\xE1cter para que mi tel\xE9fono pueda escanearlo.",
|
|
1274
|
+
"pair.promptDescription": "Mostrar el c\xF3digo de emparejamiento y el QR escaneable",
|
|
1275
|
+
"pair.scanIntro": "**Emparejar escaneando**: abra la aplicaci\xF3n tunnelbox en el tel\xE9fono, pulse \u201CEscanear para emparejar\u201D y escanee el c\xF3digo QR inferior para conectar autom\xE1ticamente.",
|
|
1059
1276
|
"perm.promptAuthorize": "opencode solicita autorizaci\xF3n",
|
|
1060
1277
|
"perm.promptPatterns": "opencode solicita acceso a: {list}",
|
|
1061
1278
|
"perm.promptResource": "opencode solicita acceso a los recursos: {list}",
|
|
1062
1279
|
"perm.promptTool": "opencode solicita ejecutar {tool}",
|
|
1063
1280
|
"qr.codeLabel": "C\xF3digo de emparejamiento",
|
|
1064
1281
|
"qr.fileWritten": "Informaci\xF3n de emparejamiento escrita en: {path}",
|
|
1065
|
-
"qr.
|
|
1066
|
-
"qr.openHint": "Abra el enlace en el navegador del tel\xE9fono o introduzca el c\xF3digo de emparejamiento manualmente en la aplicaci\xF3n.",
|
|
1282
|
+
"qr.openHint": "Abra la aplicaci\xF3n tunnelbox en el tel\xE9fono y escanee el c\xF3digo QR superior, o introduzca el c\xF3digo de emparejamiento manualmente en la aplicaci\xF3n.",
|
|
1067
1283
|
"qr.titleNoType": "emparejamiento de tel\xE9fono tunnelbox",
|
|
1068
1284
|
"qr.titleWithType": "emparejamiento de tel\xE9fono tunnelbox {type}",
|
|
1069
|
-
"qr.unavailable": "(qrcode no instalado: use el
|
|
1285
|
+
"qr.unavailable": "(qrcode no instalado: use el c\xF3digo de emparejamiento anterior)",
|
|
1070
1286
|
"claude.errBusy": "Esta sesi\xF3n est\xE1 en ejecuci\xF3n; espere a que termine o det\xE9ngala primero",
|
|
1071
1287
|
"claude.errTurn": "error de generaci\xF3n",
|
|
1072
1288
|
"claude.pairingStillClaimed": "Ya vinculado a una cuenta de tel\xE9fono: este c\xF3digo solo sirve para emparejar un nuevo dispositivo en la misma cuenta; el dispositivo actual se conecta directamente desde Mis computadoras",
|
|
@@ -1134,7 +1350,41 @@ var es_ES_default = {
|
|
|
1134
1350
|
"claude.errReadHistory": "No se pudo leer el historial de la sesi\xF3n ({id})",
|
|
1135
1351
|
"setup.langSet": "Idioma de ejecuci\xF3n establecido en",
|
|
1136
1352
|
"setup.langHint": "El adaptador emitir\xE1 en este idioma tras reiniciar (TUNNELBOX_LANG puede sobrescribirlo en tiempo de ejecuci\xF3n). Archivo de estado:",
|
|
1137
|
-
"setup.langCurrent": "Idioma de ejecuci\xF3n actual"
|
|
1353
|
+
"setup.langCurrent": "Idioma de ejecuci\xF3n actual",
|
|
1354
|
+
"hermes.errNoRelay": "URL del rel\xE9 no configurada: use --env para inyectar un valor predeterminado o sobrescr\xEDbalo en tiempo de ejecuci\xF3n con TUNNELBOX_RELAY_URL",
|
|
1355
|
+
"hermes.logConnected": "Conectado al rel\xE9 {url}",
|
|
1356
|
+
"hermes.logMsgUncaught": "Excepci\xF3n no capturada al procesar el mensaje: {message}",
|
|
1357
|
+
"hermes.logDisconnected": "Desconectado del rel\xE9, reconectando autom\xE1ticamente\u2026",
|
|
1358
|
+
"hermes.logPairCode": "C\xF3digo de emparejamiento generado: {code}",
|
|
1359
|
+
"hermes.logUnhandledType": "Tipo de mensaje no procesado: {type}",
|
|
1360
|
+
"hermes.logHandleFailed": "Error al procesar {type}: {message}",
|
|
1361
|
+
"hermes.logClaimed": "Vinculado a su cuenta de tel\xE9fono. El tel\xE9fono se conecta ahora directamente desde Mis computadoras",
|
|
1362
|
+
"hermes.logResetIdentity": "Desvinculado de la cuenta; restableciendo identidad y generando un nuevo c\xF3digo de emparejamiento\u2026",
|
|
1363
|
+
"hermes.cmdNew": "Nueva sesi\xF3n",
|
|
1364
|
+
"hermes.cmdHelp": "Mostrar ayuda",
|
|
1365
|
+
"hermes.newSessionTitle": "Nueva sesi\xF3n",
|
|
1366
|
+
"hermes.helpText": "Comandos admitidos:\n- /new nueva sesi\xF3n\n- /help mostrar ayuda",
|
|
1367
|
+
"hermes.errBusy": "Esta sesi\xF3n se est\xE1 generando; espere a que termine o det\xE9ngala primero",
|
|
1368
|
+
"hermes.errBadWorkspace": "Este directorio no existe o no se puede usar",
|
|
1369
|
+
"hermes.logWorkspaceSwitched": "Espacio de trabajo cambiado: {path}",
|
|
1370
|
+
"hermes.errWorkspaceMissing": "El espacio de trabajo no existe: {dir}",
|
|
1371
|
+
"hermes.errStartFailed": "Error al iniciar: {message}",
|
|
1372
|
+
"hermes.msgStarted": "Adaptador de Hermes iniciado (Ctrl+C para salir)",
|
|
1373
|
+
"hermes.logAbortSent": "Interrupci\xF3n enviada (session={session})",
|
|
1374
|
+
"hermes.logPromptFailed": "session.prompt fall\xF3: {message}",
|
|
1375
|
+
"hermes.logTurnError": "Error de turno (session={session}): {message}",
|
|
1376
|
+
"hermes.logPairForced": "--pair: solicitud forzada del c\xF3digo de emparejamiento (solo esta ejecuci\xF3n)",
|
|
1377
|
+
"hermes.pairingStillClaimed": "Ya vinculado a una cuenta de tel\xE9fono: este c\xF3digo solo sirve para reemparejar un nuevo dispositivo en la misma cuenta; el dispositivo actual se conecta directamente desde Mis computadoras",
|
|
1378
|
+
"hermes.logPairNoRelay": "No se puede solicitar un c\xF3digo de emparejamiento: no hay URL de rel\xE9 configurada",
|
|
1379
|
+
"hermes.errSpawnHermes": "No se puede iniciar Hermes ({bin}): confirme que est\xE1 instalado ({err})",
|
|
1380
|
+
"hermes.errHermesExit": "hermes sali\xF3 ({code}): {err}",
|
|
1381
|
+
"hermes.logSessionUpdated": "Sesi\xF3n actualizada (session={session})",
|
|
1382
|
+
"hermes.errNoInject": "Inyecci\xF3n de mensaje del tel\xE9fono no disponible (sesi\xF3n no interactiva o proceso no listo)",
|
|
1383
|
+
"hermes.pairNoRelay": "No se puede alcanzar el rel\xE9 (rel\xE9 inaccesible o websockets ausente)",
|
|
1384
|
+
"hermes.pairTimeout": "Tiempo de espera agotado al solicitar el c\xF3digo de emparejamiento (rel\xE9 sin respuesta)",
|
|
1385
|
+
"hermes.errToolPair": "fallo de tunnelbox_pair: {err}",
|
|
1386
|
+
"hermes.errToolStatus": "fallo de tunnelbox_status: {err}",
|
|
1387
|
+
"hermes.cliUsage": "Uso: /tunnelbox <pair|status>\n pair solicitar e imprimir un c\xF3digo de emparejamiento\n status mostrar estado de rel\xE9/enlace/conexi\xF3n"
|
|
1138
1388
|
};
|
|
1139
1389
|
|
|
1140
1390
|
// ../core/src/i18n.ts
|
|
@@ -1192,6 +1442,9 @@ function t(lang, key, vars) {
|
|
|
1192
1442
|
}
|
|
1193
1443
|
return text;
|
|
1194
1444
|
}
|
|
1445
|
+
function makeT(lang) {
|
|
1446
|
+
return (key, vars) => t(lang, key, vars);
|
|
1447
|
+
}
|
|
1195
1448
|
|
|
1196
1449
|
// ../core/src/state.ts
|
|
1197
1450
|
function baseName() {
|
|
@@ -1237,12 +1490,11 @@ function loadState(type) {
|
|
|
1237
1490
|
return fallback;
|
|
1238
1491
|
}
|
|
1239
1492
|
}
|
|
1240
|
-
function writePairingFile(
|
|
1493
|
+
function writePairingFile(code, type) {
|
|
1241
1494
|
const p = join(baseName(), `remote-pairing${suffix(type)}.txt`);
|
|
1242
1495
|
try {
|
|
1243
1496
|
mkdirSync(dirname(p), { recursive: true });
|
|
1244
1497
|
writeFileSync(p, `\u914D\u5BF9\u7801: ${code}
|
|
1245
|
-
\u914D\u5BF9\u94FE\u63A5: ${link}
|
|
1246
1498
|
`, "utf8");
|
|
1247
1499
|
} catch {
|
|
1248
1500
|
}
|
|
@@ -1279,19 +1531,14 @@ function padVisual(s, width, align = "left") {
|
|
|
1279
1531
|
}
|
|
1280
1532
|
return s + " ".repeat(pad);
|
|
1281
1533
|
}
|
|
1282
|
-
async function printPairing(
|
|
1534
|
+
async function printPairing(code, type, name, lang) {
|
|
1283
1535
|
const resolved = lang ? normalizeLang(lang) : resolveLang();
|
|
1284
|
-
const
|
|
1285
|
-
if (type) p.set("type", type);
|
|
1286
|
-
if (name) p.set("name", name);
|
|
1287
|
-
const link = `${relayHttpBase}/app/#/pages/index/index?${p.toString()}`;
|
|
1288
|
-
const file = writePairingFile(link, code, type);
|
|
1536
|
+
const file = writePairingFile(code, type);
|
|
1289
1537
|
const tt = (key, vars) => t(resolved, key, vars);
|
|
1290
1538
|
const title = type ? tt("qr.titleWithType", { type }) : tt("qr.titleNoType");
|
|
1291
1539
|
const codeLine = `${tt("qr.codeLabel")}: ${code}`;
|
|
1292
|
-
const linkLine = `${tt("qr.linkLabel")}: ${link}`;
|
|
1293
1540
|
const hint = tt("qr.openHint");
|
|
1294
|
-
const inner = Math.max(40, dispWidth(codeLine), dispWidth(
|
|
1541
|
+
const inner = Math.max(40, dispWidth(codeLine), dispWidth(title));
|
|
1295
1542
|
const W = Math.min(76, inner + 4);
|
|
1296
1543
|
const bar = "\u2500".repeat(W);
|
|
1297
1544
|
console.log("");
|
|
@@ -1299,7 +1546,6 @@ async function printPairing(relayHttpBase, code, type, name, lang) {
|
|
|
1299
1546
|
console.log("\u2502 " + padVisual(title, W - 2, "center") + " \u2502");
|
|
1300
1547
|
console.log("\u251C" + bar + "\u2524");
|
|
1301
1548
|
console.log("\u2502 " + padVisual(codeLine, W - 2) + " \u2502");
|
|
1302
|
-
console.log("\u2502 " + padVisual(linkLine, W - 2) + " \u2502");
|
|
1303
1549
|
console.log("\u2514" + bar + "\u2518");
|
|
1304
1550
|
console.log(" " + hint);
|
|
1305
1551
|
try {
|
|
@@ -1307,7 +1553,7 @@ async function printPairing(relayHttpBase, code, type, name, lang) {
|
|
|
1307
1553
|
const render = mod.default ?? mod;
|
|
1308
1554
|
const toString = render.toString;
|
|
1309
1555
|
if (toString) {
|
|
1310
|
-
const qr = await toString(
|
|
1556
|
+
const qr = await toString(code, { type: "terminal", small: true });
|
|
1311
1557
|
if (qr) console.log(qr);
|
|
1312
1558
|
}
|
|
1313
1559
|
} catch {
|
|
@@ -1317,8 +1563,72 @@ async function printPairing(relayHttpBase, code, type, name, lang) {
|
|
|
1317
1563
|
console.log("");
|
|
1318
1564
|
}
|
|
1319
1565
|
|
|
1566
|
+
// src/bridge.ts
|
|
1567
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1568
|
+
import { existsSync as existsSync4, statSync as statSync2 } from "node:fs";
|
|
1569
|
+
import { hostname } from "node:os";
|
|
1570
|
+
import { resolve } from "node:path";
|
|
1571
|
+
|
|
1320
1572
|
// src/runner.ts
|
|
1321
1573
|
import { spawn } from "node:child_process";
|
|
1574
|
+
|
|
1575
|
+
// src/sessions.ts
|
|
1576
|
+
import { spawnSync } from "node:child_process";
|
|
1577
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1578
|
+
import { join as join2 } from "node:path";
|
|
1579
|
+
function needsCmdQuote(s) {
|
|
1580
|
+
return s === "" || /[ \t&|<>^"%]/.test(s);
|
|
1581
|
+
}
|
|
1582
|
+
function cmdLineFor(shimPath, args) {
|
|
1583
|
+
const parts = [`"${shimPath}"`, ...args.map((a) => needsCmdQuote(a) ? `"${a}"` : a)];
|
|
1584
|
+
return parts.join(" ");
|
|
1585
|
+
}
|
|
1586
|
+
function findWinShim(name) {
|
|
1587
|
+
if (name.includes("/") || name.includes("\\")) return null;
|
|
1588
|
+
const dirs = (process.env.PATH || "").split(";").filter(Boolean);
|
|
1589
|
+
for (const dir of dirs) {
|
|
1590
|
+
for (const ext of [".cmd", ".bat"]) {
|
|
1591
|
+
const p = join2(dir, name + ext);
|
|
1592
|
+
if (existsSync2(p)) return p;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
function runHermesSync(binRaw, args, cwd) {
|
|
1598
|
+
const parts = binRaw.trim().split(/\s+/);
|
|
1599
|
+
const execBin = parts[0];
|
|
1600
|
+
const preArgs = parts.slice(1);
|
|
1601
|
+
const base = { cwd, env: process.env, encoding: "utf8", timeout: 6e4 };
|
|
1602
|
+
if (process.platform === "win32") {
|
|
1603
|
+
const shim = findWinShim(execBin);
|
|
1604
|
+
if (shim) {
|
|
1605
|
+
return spawnSync(cmdLineFor(shim, [...preArgs, ...args]), [], { ...base, shell: true });
|
|
1606
|
+
}
|
|
1607
|
+
const res = spawnSync(execBin, [...preArgs, ...args], base);
|
|
1608
|
+
if (res.error) {
|
|
1609
|
+
const shim2 = findWinShim(execBin);
|
|
1610
|
+
if (shim2) return spawnSync(cmdLineFor(shim2, [...preArgs, ...args]), [], { ...base, shell: true });
|
|
1611
|
+
}
|
|
1612
|
+
return res;
|
|
1613
|
+
}
|
|
1614
|
+
return spawnSync(execBin, [...preArgs, ...args], base);
|
|
1615
|
+
}
|
|
1616
|
+
var SESSION_ID_RE = /^\d{8}_\d{6}_[0-9a-f]{6}$/i;
|
|
1617
|
+
function latestHermesId(binRaw, dir) {
|
|
1618
|
+
try {
|
|
1619
|
+
const res = runHermesSync(binRaw, ["sessions", "list", "--workspace", dir, "--limit", "1"], dir);
|
|
1620
|
+
if (res.status !== 0) return null;
|
|
1621
|
+
const lines = (res.stdout || "").split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
1622
|
+
const row = lines[2];
|
|
1623
|
+
if (!row) return null;
|
|
1624
|
+
const id = row.trim().split(/\s+/).pop() || "";
|
|
1625
|
+
return SESSION_ID_RE.test(id) ? id : null;
|
|
1626
|
+
} catch {
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// src/runner.ts
|
|
1322
1632
|
var HermesProcess = class {
|
|
1323
1633
|
constructor(displaySessionID, hooks) {
|
|
1324
1634
|
this.displaySessionID = displaySessionID;
|
|
@@ -1327,18 +1637,11 @@ var HermesProcess = class {
|
|
|
1327
1637
|
child = null;
|
|
1328
1638
|
killed = false;
|
|
1329
1639
|
seq = 0;
|
|
1330
|
-
|
|
1331
|
-
emitCount = 0;
|
|
1332
|
-
capturedId = null;
|
|
1333
|
-
get resumeId() {
|
|
1334
|
-
return this.capturedId;
|
|
1335
|
-
}
|
|
1336
|
-
get produced() {
|
|
1337
|
-
return this.emitCount > 0;
|
|
1338
|
-
}
|
|
1339
|
-
start(opts, text, resumeId) {
|
|
1640
|
+
start(opts, text, hermesId) {
|
|
1340
1641
|
return new Promise((resolve3, reject) => {
|
|
1642
|
+
const t2 = makeT(opts.lang ?? "en-US");
|
|
1341
1643
|
let stderrBuf = "";
|
|
1644
|
+
let outBuf = "";
|
|
1342
1645
|
let settled = false;
|
|
1343
1646
|
const done = (err) => {
|
|
1344
1647
|
if (settled) return;
|
|
@@ -1347,47 +1650,106 @@ var HermesProcess = class {
|
|
|
1347
1650
|
else resolve3();
|
|
1348
1651
|
};
|
|
1349
1652
|
const bin = opts.bin || "hermes";
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1653
|
+
const binParts = bin.trim().split(/\s+/);
|
|
1654
|
+
const execBin = binParts[0];
|
|
1655
|
+
const preArgs = binParts.slice(1);
|
|
1656
|
+
const args = [...preArgs, "chat"];
|
|
1657
|
+
if (hermesId) args.push("--resume", hermesId);
|
|
1658
|
+
args.push("-Q", "-q", text);
|
|
1352
1659
|
if (opts.provider) args.push("--provider", opts.provider);
|
|
1353
1660
|
if (opts.model) args.push("--model", opts.model);
|
|
1354
1661
|
if (opts.toolsets) args.push("--toolsets", opts.toolsets);
|
|
1355
1662
|
if (opts.verbose) args.push("--verbose");
|
|
1356
|
-
|
|
1663
|
+
const winShim = process.platform === "win32" ? findWinShim(execBin) : null;
|
|
1664
|
+
const emitFull = () => {
|
|
1665
|
+
const textOut = stripAnsi(outBuf).trim();
|
|
1666
|
+
if (!textOut) return;
|
|
1667
|
+
this.hooks.emit(this.displaySessionID, `m-${Date.now()}-${++this.seq}`, {
|
|
1668
|
+
id: "t",
|
|
1669
|
+
type: "text",
|
|
1670
|
+
text: textOut,
|
|
1671
|
+
complete: true
|
|
1672
|
+
});
|
|
1673
|
+
};
|
|
1674
|
+
const wire = (c) => {
|
|
1675
|
+
c.stdout?.setEncoding("utf8");
|
|
1676
|
+
c.stdout?.on("data", (chunk) => {
|
|
1677
|
+
outBuf += chunk;
|
|
1678
|
+
});
|
|
1679
|
+
c.stderr?.setEncoding("utf8");
|
|
1680
|
+
c.stderr?.on("data", (chunk) => {
|
|
1681
|
+
stderrBuf += chunk;
|
|
1682
|
+
for (const l of chunk.split("\n")) {
|
|
1683
|
+
const s = stripAnsi(l).trim();
|
|
1684
|
+
if (s && /error|login|auth|key|not (found|installed)|denied|failed/i.test(s)) {
|
|
1685
|
+
this.hooks.log(`hermes: ${s.slice(0, 300)}`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
c.on("close", (code, signal) => {
|
|
1690
|
+
if (this.killed || signal) {
|
|
1691
|
+
done();
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
if (c !== this.child) {
|
|
1695
|
+
done();
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
if (code !== 0) {
|
|
1699
|
+
const err = stderrBuf.trim().split("\n").pop() || String(code);
|
|
1700
|
+
this.error(t2("hermes.errHermesExit", { code: String(code), err: stripAnsi(err).slice(0, 500) }));
|
|
1701
|
+
} else {
|
|
1702
|
+
emitFull();
|
|
1703
|
+
}
|
|
1704
|
+
done();
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1357
1707
|
let child;
|
|
1358
1708
|
try {
|
|
1359
|
-
|
|
1709
|
+
if (winShim) {
|
|
1710
|
+
child = spawn(cmdLineFor(winShim, args), [], {
|
|
1711
|
+
cwd: opts.cwd,
|
|
1712
|
+
env: process.env,
|
|
1713
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1714
|
+
shell: true
|
|
1715
|
+
});
|
|
1716
|
+
} else {
|
|
1717
|
+
child = spawn(execBin, args, { cwd: opts.cwd, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
1718
|
+
}
|
|
1360
1719
|
} catch (e) {
|
|
1361
|
-
done(new Error(
|
|
1720
|
+
done(new Error(t2("hermes.errSpawnHermes", { bin: execBin, err: e.message })));
|
|
1362
1721
|
return;
|
|
1363
1722
|
}
|
|
1364
1723
|
this.child = child;
|
|
1365
|
-
child
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1724
|
+
wire(child);
|
|
1725
|
+
if (!winShim) {
|
|
1726
|
+
child.on("error", (e) => {
|
|
1727
|
+
const errno = e.code;
|
|
1728
|
+
if (process.platform === "win32" && errno === "ENOENT") {
|
|
1729
|
+
const shim = findWinShim(execBin);
|
|
1730
|
+
if (shim) {
|
|
1731
|
+
try {
|
|
1732
|
+
const c2 = spawn(cmdLineFor(shim, args), [], {
|
|
1733
|
+
cwd: opts.cwd,
|
|
1734
|
+
env: process.env,
|
|
1735
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1736
|
+
shell: true
|
|
1737
|
+
});
|
|
1738
|
+
this.child = c2;
|
|
1739
|
+
c2.on("error", (e2) => {
|
|
1740
|
+
if (!this.killed) done(new Error(`Hermes: ${e2.message}`));
|
|
1741
|
+
});
|
|
1742
|
+
wire(c2);
|
|
1743
|
+
return;
|
|
1744
|
+
} catch (e2) {
|
|
1745
|
+
done(new Error(t2("hermes.errSpawnHermes", { bin, err: e2.message })));
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1374
1749
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
if (!this.killed) done(new Error(`Hermes \u542F\u52A8\u5931\u8D25: ${e.message}`));
|
|
1379
|
-
});
|
|
1380
|
-
child.on("close", (code, signal) => {
|
|
1381
|
-
if (this.killed || signal) {
|
|
1382
|
-
done();
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
if (code !== 0) {
|
|
1386
|
-
const err = stderrBuf.trim().split("\n").pop() || `hermes \u9000\u51FA\u7801 ${code}`;
|
|
1387
|
-
this.error(`hermes \u9000\u51FA(${code}): ${stripAnsi(err).slice(0, 500)}`);
|
|
1388
|
-
}
|
|
1389
|
-
done();
|
|
1390
|
-
});
|
|
1750
|
+
if (!this.killed) done(new Error(`Hermes: ${e.message}`));
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1391
1753
|
});
|
|
1392
1754
|
}
|
|
1393
1755
|
kill() {
|
|
@@ -1414,282 +1776,26 @@ var HermesProcess = class {
|
|
|
1414
1776
|
});
|
|
1415
1777
|
this.hooks.status(this.displaySessionID, "error");
|
|
1416
1778
|
}
|
|
1417
|
-
onStdout(chunk) {
|
|
1418
|
-
const clean = stripAnsi(chunk);
|
|
1419
|
-
if (!clean) return;
|
|
1420
|
-
if (!this.capturedId) {
|
|
1421
|
-
const m = clean.match(/(?:--resume\s+)?\b(\d{8}_\d{6}_[0-9a-fA-F]{3,})\b/);
|
|
1422
|
-
if (m) {
|
|
1423
|
-
this.capturedId = m[1];
|
|
1424
|
-
this.hooks.onResumeId?.(m[1]);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
const meaningful = clean.replace(/^\s*[\n\r]+/, "").replace(/[\n\r]+$/, "");
|
|
1428
|
-
if (!meaningful) return;
|
|
1429
|
-
if (!this.msgId) this.msgId = `m-${Date.now()}-${++this.seq}`;
|
|
1430
|
-
this.emitCount++;
|
|
1431
|
-
this.hooks.emit(this.displaySessionID, this.msgId, { id: "t", type: "text", delta: meaningful, complete: false });
|
|
1432
|
-
}
|
|
1433
1779
|
};
|
|
1434
1780
|
var ANSI = /\u001B\[[0-9;?]*[ -\/]*[@-~]/g;
|
|
1435
1781
|
function stripAnsi(s) {
|
|
1436
1782
|
return s.replace(ANSI, "");
|
|
1437
1783
|
}
|
|
1438
1784
|
|
|
1439
|
-
// src/approval.ts
|
|
1440
|
-
import { createServer } from "node:http";
|
|
1441
|
-
import { copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1442
|
-
import { homedir as homedir2 } from "node:os";
|
|
1443
|
-
import { join as join2 } from "node:path";
|
|
1444
|
-
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
1445
|
-
var DECISION_TIMEOUT_MS = 12e4;
|
|
1446
|
-
var HOOK_MARKER = "--hook-pre-tool-call";
|
|
1447
|
-
var ApprovalBridge = class {
|
|
1448
|
-
constructor(cfg) {
|
|
1449
|
-
this.cfg = cfg;
|
|
1450
|
-
}
|
|
1451
|
-
server = null;
|
|
1452
|
-
pending = /* @__PURE__ */ new Map();
|
|
1453
|
-
seq = 0;
|
|
1454
|
-
get baseUrl() {
|
|
1455
|
-
return `http://127.0.0.1:${this.cfg.port}`;
|
|
1456
|
-
}
|
|
1457
|
-
start() {
|
|
1458
|
-
if (!this.cfg.enabled) return;
|
|
1459
|
-
this.server = createServer((req, res) => void this.handle(req, res).catch((e) => this.fail(res, 500, e)));
|
|
1460
|
-
this.server.listen(this.cfg.port, "127.0.0.1", () => {
|
|
1461
|
-
console.log(`[tunnelbox:hermes] \u5BA1\u6279 HTTP \u670D\u52A1\u5DF2\u76D1\u542C ${this.baseUrl}`);
|
|
1462
|
-
this.installHook();
|
|
1463
|
-
});
|
|
1464
|
-
this.server.on("error", (e) => {
|
|
1465
|
-
console.error(`[tunnelbox:hermes] \u5BA1\u6279 HTTP \u670D\u52A1\u9519\u8BEF: ${e.message}`);
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
stop() {
|
|
1469
|
-
this.flush("\u9002\u914D\u5668\u5173\u95ED\uFF0C\u81EA\u52A8\u62D2\u7EDD");
|
|
1470
|
-
this.server?.close();
|
|
1471
|
-
this.server = null;
|
|
1472
|
-
}
|
|
1473
|
-
/** 断线/关闭时把所有待审批 resolve 为 deny(fail-closed)。 */
|
|
1474
|
-
flush(reason) {
|
|
1475
|
-
for (const p of this.pending.values()) {
|
|
1476
|
-
clearTimeout(p.timer);
|
|
1477
|
-
this.resolveOne(p, { decision: "deny", reason });
|
|
1478
|
-
}
|
|
1479
|
-
this.pending.clear();
|
|
1480
|
-
}
|
|
1481
|
-
/** 手机 permission.reply → 回填决定。 */
|
|
1482
|
-
resolve(id, d) {
|
|
1483
|
-
const p = this.pending.get(id);
|
|
1484
|
-
if (!p) return;
|
|
1485
|
-
clearTimeout(p.timer);
|
|
1486
|
-
this.pending.delete(id);
|
|
1487
|
-
this.resolveOne(p, d);
|
|
1488
|
-
}
|
|
1489
|
-
/** 由 bridge 注入:把 permission.request 经中继推到手机;返回是否推送成功。 */
|
|
1490
|
-
onPermissionRequest = () => true;
|
|
1491
|
-
resolveOne(p, d) {
|
|
1492
|
-
p.resolved = d;
|
|
1493
|
-
for (const r of p.resolvers) r(d);
|
|
1494
|
-
p.resolvers.clear();
|
|
1495
|
-
}
|
|
1496
|
-
async handle(req, res) {
|
|
1497
|
-
const url = req.url || "/";
|
|
1498
|
-
const method = req.method || "GET";
|
|
1499
|
-
if (method === "POST" && url === "/v1/permission") {
|
|
1500
|
-
await this.onPermissionPost(req, res);
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
|
-
const m = url.match(/^\/v1\/permission\/decision\/([^/]+)$/);
|
|
1504
|
-
if (method === "GET" && m) {
|
|
1505
|
-
this.onDecisionGet(m[1], res);
|
|
1506
|
-
return;
|
|
1507
|
-
}
|
|
1508
|
-
this.fail(res, 404, new Error("not found"));
|
|
1509
|
-
}
|
|
1510
|
-
readJson(req) {
|
|
1511
|
-
return new Promise((resolve3, reject) => {
|
|
1512
|
-
let body = "";
|
|
1513
|
-
req.on("data", (c) => body += c);
|
|
1514
|
-
req.on("end", () => {
|
|
1515
|
-
try {
|
|
1516
|
-
resolve3(body.trim() ? JSON.parse(body) : {});
|
|
1517
|
-
} catch (e) {
|
|
1518
|
-
reject(new Error("invalid json"));
|
|
1519
|
-
}
|
|
1520
|
-
});
|
|
1521
|
-
req.on("error", reject);
|
|
1522
|
-
});
|
|
1523
|
-
}
|
|
1524
|
-
async onPermissionPost(req, res) {
|
|
1525
|
-
const body = await this.readJson(req);
|
|
1526
|
-
const tool = String(body.tool || "tool");
|
|
1527
|
-
const args = body.args && typeof body.args === "object" ? body.args : {};
|
|
1528
|
-
const sessionID = String(body.sessionID || "hermes");
|
|
1529
|
-
const prompt = typeof body.prompt === "string" ? body.prompt : `Hermes \u8BF7\u6C42\u6267\u884C ${tool}`;
|
|
1530
|
-
const id = `p-${++this.seq}-${Date.now()}`;
|
|
1531
|
-
const ok = this.onPermissionRequest({ id, sessionID, tool, args, prompt, createdAt: Date.now(), kind: "approval" });
|
|
1532
|
-
if (!ok) {
|
|
1533
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
1534
|
-
res.end(JSON.stringify({ requestId: id, decision: "deny", reason: "\u4E2D\u7EE7\u79BB\u7EBF\uFF0C\u81EA\u52A8\u62D2\u7EDD\uFF08fail-closed\uFF09" }));
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
const timer = setTimeout(() => {
|
|
1538
|
-
this.resolve(id, { decision: "deny", reason: "\u5BA1\u6279\u8D85\u65F6\uFF0C\u81EA\u52A8\u62D2\u7EDD\uFF08fail-closed\uFF09" });
|
|
1539
|
-
}, DECISION_TIMEOUT_MS);
|
|
1540
|
-
this.pending.set(id, { timer, resolvers: /* @__PURE__ */ new Set() });
|
|
1541
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
1542
|
-
res.end(JSON.stringify({ requestId: id }));
|
|
1543
|
-
}
|
|
1544
|
-
onDecisionGet(id, res) {
|
|
1545
|
-
const p = this.pending.get(id);
|
|
1546
|
-
if (!p) {
|
|
1547
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
1548
|
-
res.end(JSON.stringify({ decision: "deny", reason: "\u672A\u77E5\u5BA1\u6279\u8BF7\u6C42" }));
|
|
1549
|
-
return;
|
|
1550
|
-
}
|
|
1551
|
-
if (p.resolved) {
|
|
1552
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
1553
|
-
res.end(JSON.stringify(p.resolved));
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
p.resolvers.add((d) => {
|
|
1557
|
-
if (res.headersSent) return;
|
|
1558
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
1559
|
-
res.end(JSON.stringify(d));
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
/** 把本适配器的 pre_tool_call hook 注入 Hermes config.yaml(幂等,写前备份)。 */
|
|
1563
|
-
installHook() {
|
|
1564
|
-
try {
|
|
1565
|
-
const hermesHome = process.env.HERMES_HOME || join2(homedir2(), ".hermes");
|
|
1566
|
-
const configPath = join2(hermesHome, "config.yaml");
|
|
1567
|
-
let root2 = {};
|
|
1568
|
-
let existed = false;
|
|
1569
|
-
if (existsSync2(configPath)) {
|
|
1570
|
-
existed = true;
|
|
1571
|
-
const raw = readFileSync2(configPath, "utf8");
|
|
1572
|
-
const parsed = parseYaml(raw);
|
|
1573
|
-
if (parsed && typeof parsed === "object") root2 = parsed;
|
|
1574
|
-
else throw new Error("config.yaml \u89E3\u6790\u5931\u8D25\uFF08\u4FDD\u7559\u539F\u6837\uFF0C\u4E0D\u8986\u76D6\uFF09");
|
|
1575
|
-
}
|
|
1576
|
-
const hooks = root2.hooks = root2.hooks || {};
|
|
1577
|
-
const list = hooks.pre_tool_call = Array.isArray(hooks.pre_tool_call) ? hooks.pre_tool_call : [];
|
|
1578
|
-
const already = list.some((g) => JSON.stringify(g).includes(HOOK_MARKER));
|
|
1579
|
-
if (!already) {
|
|
1580
|
-
list.push({
|
|
1581
|
-
matcher: ".*",
|
|
1582
|
-
command: `node "${this.cfg.entryPath}" ${HOOK_MARKER}`,
|
|
1583
|
-
timeout: DECISION_TIMEOUT_MS / 1e3
|
|
1584
|
-
});
|
|
1585
|
-
if (existed) {
|
|
1586
|
-
try {
|
|
1587
|
-
copyFileSync(configPath, `${configPath}.tunnelbox.bak`);
|
|
1588
|
-
} catch {
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
mkdirSync2(hermesHome, { recursive: true });
|
|
1592
|
-
writeFileSync2(configPath, stringifyYaml(root2), "utf8");
|
|
1593
|
-
console.log(`[tunnelbox:hermes] \u5DF2\u6CE8\u5165 hooks.pre_tool_call \u5230 ${configPath}\uFF08${HOOK_MARKER} \u2192 \u624B\u673A\u5BA1\u6279\uFF09`);
|
|
1594
|
-
}
|
|
1595
|
-
} catch (e) {
|
|
1596
|
-
console.warn(`[tunnelbox:hermes] \u6CE8\u5165 Hermes hook \u5931\u8D25\uFF08\u5C06\u65E0\u6CD5\u624B\u673A\u5BA1\u6279\uFF09: ${e.message}`);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
fail(res, code, e) {
|
|
1600
|
-
if (res.headersSent) return;
|
|
1601
|
-
res.writeHead(code, { "content-type": "application/json" });
|
|
1602
|
-
res.end(JSON.stringify({ error: e.message }));
|
|
1603
|
-
}
|
|
1604
|
-
};
|
|
1605
|
-
function readStdin() {
|
|
1606
|
-
return new Promise((resolve3, reject) => {
|
|
1607
|
-
let buf = "";
|
|
1608
|
-
process.stdin.setEncoding("utf8");
|
|
1609
|
-
process.stdin.on("data", (c) => buf += c);
|
|
1610
|
-
process.stdin.on("end", () => {
|
|
1611
|
-
try {
|
|
1612
|
-
resolve3(buf.trim() ? JSON.parse(buf) : {});
|
|
1613
|
-
} catch (e) {
|
|
1614
|
-
reject(new Error("invalid hook stdin json"));
|
|
1615
|
-
}
|
|
1616
|
-
});
|
|
1617
|
-
process.stdin.on("error", reject);
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
async function postPermission(port, body) {
|
|
1621
|
-
const res = await fetch(`http://127.0.0.1:${port}/v1/permission`, {
|
|
1622
|
-
method: "POST",
|
|
1623
|
-
headers: { "content-type": "application/json" },
|
|
1624
|
-
body: JSON.stringify(body),
|
|
1625
|
-
signal: AbortSignal.timeout(1e4)
|
|
1626
|
-
});
|
|
1627
|
-
return await res.json();
|
|
1628
|
-
}
|
|
1629
|
-
async function pollDecision(port, requestId) {
|
|
1630
|
-
const res = await fetch(`http://127.0.0.1:${port}/v1/permission/decision/${encodeURIComponent(requestId)}`, {
|
|
1631
|
-
signal: AbortSignal.timeout(DECISION_TIMEOUT_MS + 1e4)
|
|
1632
|
-
});
|
|
1633
|
-
return await res.json();
|
|
1634
|
-
}
|
|
1635
|
-
async function approvalHookRun(port) {
|
|
1636
|
-
let input;
|
|
1637
|
-
try {
|
|
1638
|
-
input = await readStdin();
|
|
1639
|
-
} catch (e) {
|
|
1640
|
-
allowNoop();
|
|
1641
|
-
return;
|
|
1642
|
-
}
|
|
1643
|
-
const tool = String(input.tool_name || input.tool || input.toolName || "tool");
|
|
1644
|
-
const args = input.tool_input && typeof input.tool_input === "object" ? input.tool_input : input.arguments && typeof input.arguments === "object" ? input.arguments : {};
|
|
1645
|
-
const prompt = `Hermes \u8BF7\u6C42\u6267\u884C ${tool}`;
|
|
1646
|
-
const sessionID = String(input.session_key || input.session_id || "hermes");
|
|
1647
|
-
let created;
|
|
1648
|
-
try {
|
|
1649
|
-
created = await postPermission(port, { sessionID, tool, args, prompt });
|
|
1650
|
-
} catch (e) {
|
|
1651
|
-
allowNoop();
|
|
1652
|
-
return;
|
|
1653
|
-
}
|
|
1654
|
-
if (created.decision === "deny" || created.reason) {
|
|
1655
|
-
denyBlock(created.reason || "\u5DF2\u62D2\u7EDD");
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
try {
|
|
1659
|
-
const d = await pollDecision(port, created.requestId);
|
|
1660
|
-
if (d.decision === "allow") {
|
|
1661
|
-
allowNoop();
|
|
1662
|
-
return;
|
|
1663
|
-
}
|
|
1664
|
-
denyBlock(d.reason || "\u7528\u6237\u62D2\u7EDD");
|
|
1665
|
-
} catch (e) {
|
|
1666
|
-
denyBlock(`\u5BA1\u6279\u901A\u9053\u5F02\u5E38\uFF0C\u81EA\u52A8\u62D2\u7EDD\uFF08fail-closed\uFF09: ${e.message}`);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
function allowNoop() {
|
|
1670
|
-
process.stdout.write("{}\n");
|
|
1671
|
-
process.exit(0);
|
|
1672
|
-
}
|
|
1673
|
-
function denyBlock(reason) {
|
|
1674
|
-
process.stdout.write(JSON.stringify({ action: "block", message: reason }) + "\n");
|
|
1675
|
-
process.stderr.write(reason + "\n");
|
|
1676
|
-
process.exit(2);
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
1785
|
// src/mirror.ts
|
|
1680
1786
|
import {
|
|
1681
1787
|
existsSync as existsSync3,
|
|
1682
|
-
mkdirSync as
|
|
1683
|
-
readFileSync as
|
|
1788
|
+
mkdirSync as mkdirSync2,
|
|
1789
|
+
readFileSync as readFileSync2,
|
|
1684
1790
|
readdirSync,
|
|
1685
1791
|
rmSync,
|
|
1686
1792
|
statSync,
|
|
1687
|
-
writeFileSync as
|
|
1793
|
+
writeFileSync as writeFileSync2
|
|
1688
1794
|
} from "node:fs";
|
|
1689
|
-
import { homedir as
|
|
1795
|
+
import { homedir as homedir2 } from "node:os";
|
|
1690
1796
|
import { join as join3 } from "node:path";
|
|
1691
1797
|
function root() {
|
|
1692
|
-
return join3(
|
|
1798
|
+
return join3(homedir2(), ".tunnelbox", "hermes-sessions");
|
|
1693
1799
|
}
|
|
1694
1800
|
function dirOf(sessionID) {
|
|
1695
1801
|
return join3(root(), sessionID);
|
|
@@ -1699,7 +1805,7 @@ var META = "meta.json";
|
|
|
1699
1805
|
function readMeta(sessionID) {
|
|
1700
1806
|
try {
|
|
1701
1807
|
const p = join3(dirOf(sessionID), META);
|
|
1702
|
-
if (existsSync3(p)) return JSON.parse(
|
|
1808
|
+
if (existsSync3(p)) return JSON.parse(readFileSync2(p, "utf8"));
|
|
1703
1809
|
} catch {
|
|
1704
1810
|
}
|
|
1705
1811
|
return null;
|
|
@@ -1709,8 +1815,8 @@ function getMeta(sessionID) {
|
|
|
1709
1815
|
}
|
|
1710
1816
|
function writeMeta(sessionID, meta) {
|
|
1711
1817
|
try {
|
|
1712
|
-
|
|
1713
|
-
|
|
1818
|
+
mkdirSync2(dirOf(sessionID), { recursive: true });
|
|
1819
|
+
writeFileSync2(join3(dirOf(sessionID), META), JSON.stringify(meta, null, 2), "utf8");
|
|
1714
1820
|
} catch {
|
|
1715
1821
|
}
|
|
1716
1822
|
}
|
|
@@ -1718,26 +1824,26 @@ function newSessionId() {
|
|
|
1718
1824
|
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
1719
1825
|
return `s-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1720
1826
|
}
|
|
1721
|
-
function saveMeta(sessionID, patch) {
|
|
1722
|
-
const prev = readMeta(sessionID) || { title:
|
|
1827
|
+
function saveMeta(sessionID, patch, defaultTitle = "New session") {
|
|
1828
|
+
const prev = readMeta(sessionID) || { title: defaultTitle, created: Date.now(), updated: Date.now() };
|
|
1723
1829
|
const meta = { ...prev, ...patch, updated: Date.now() };
|
|
1724
1830
|
writeMeta(sessionID, meta);
|
|
1725
1831
|
return meta;
|
|
1726
1832
|
}
|
|
1727
|
-
function appendPart(sessionID, msgId, role, part, created = Date.now()) {
|
|
1833
|
+
function appendPart(sessionID, msgId, role, part, created = Date.now(), defaultTitle = "New session") {
|
|
1728
1834
|
try {
|
|
1729
1835
|
const d = dirOf(sessionID);
|
|
1730
|
-
|
|
1836
|
+
mkdirSync2(d, { recursive: true });
|
|
1731
1837
|
const line = JSON.stringify({ id: msgId, role, parts: [part], created });
|
|
1732
|
-
|
|
1838
|
+
writeFileSync2(join3(d, FILE), line + "\n", { encoding: "utf8", flag: "a" });
|
|
1733
1839
|
if (role === "user" && part.type === "text" && part.text) {
|
|
1734
1840
|
const meta = readMeta(sessionID);
|
|
1735
|
-
if (!meta || meta.title ===
|
|
1841
|
+
if (!meta || meta.title === defaultTitle) {
|
|
1736
1842
|
const t2 = part.text.trim();
|
|
1737
|
-
saveMeta(sessionID, { title: t2.length > 30 ? `${t2.slice(0, 30)}\u2026` : t2 });
|
|
1843
|
+
saveMeta(sessionID, { title: t2.length > 30 ? `${t2.slice(0, 30)}\u2026` : t2 }, defaultTitle);
|
|
1738
1844
|
}
|
|
1739
1845
|
}
|
|
1740
|
-
saveMeta(sessionID, {});
|
|
1846
|
+
saveMeta(sessionID, {}, defaultTitle);
|
|
1741
1847
|
} catch {
|
|
1742
1848
|
}
|
|
1743
1849
|
}
|
|
@@ -1763,7 +1869,7 @@ async function listSessions() {
|
|
|
1763
1869
|
async function readHistory(sessionID) {
|
|
1764
1870
|
let lines;
|
|
1765
1871
|
try {
|
|
1766
|
-
lines =
|
|
1872
|
+
lines = readFileSync2(join3(dirOf(sessionID), FILE), "utf8").split("\n");
|
|
1767
1873
|
} catch {
|
|
1768
1874
|
return [];
|
|
1769
1875
|
}
|
|
@@ -1797,52 +1903,32 @@ async function removeSession(sessionID) {
|
|
|
1797
1903
|
// src/bridge.ts
|
|
1798
1904
|
var VERSION = "0.1.0";
|
|
1799
1905
|
var AGENT_TYPE = "hermes";
|
|
1800
|
-
var ADAPTER_COMMANDS = [
|
|
1801
|
-
{ name: "new", description: "\u65B0\u5EFA\u4F1A\u8BDD" },
|
|
1802
|
-
{ name: "help", description: "\u663E\u793A\u5E2E\u52A9" }
|
|
1803
|
-
];
|
|
1804
1906
|
var HermesBridge = class {
|
|
1805
1907
|
constructor(cfg) {
|
|
1806
1908
|
this.cfg = cfg;
|
|
1807
1909
|
this.currentWorkspace = resolve(cfg.cwd || process.cwd());
|
|
1910
|
+
this.t = makeT(cfg.lang ?? "en-US");
|
|
1808
1911
|
}
|
|
1809
1912
|
ws = new RelayClient();
|
|
1810
1913
|
state = loadState(AGENT_TYPE);
|
|
1811
1914
|
claimed = !!this.state.claimed;
|
|
1812
1915
|
everConnected = false;
|
|
1813
1916
|
relayBase = "";
|
|
1917
|
+
t;
|
|
1814
1918
|
currentWorkspace;
|
|
1815
1919
|
pendingWorkspaces = /* @__PURE__ */ new Map();
|
|
1816
1920
|
sessionCwd = /* @__PURE__ */ new Map();
|
|
1817
1921
|
activeRuns = /* @__PURE__ */ new Map();
|
|
1818
|
-
/** 手机审批桥(Hermes pre_tool_call hook → 手机权限卡)。 */
|
|
1819
|
-
approval = null;
|
|
1820
|
-
/** permissionID → 未决审批请求(session.pending 补发;hook 会话 id 与 tunnelbox 会话非一一对应,按全量在途请求重发,手机端幂等去重)。 */
|
|
1821
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
1822
|
-
get relayHttpBase() {
|
|
1823
|
-
return this.relayBase.replace(/^ws/, "http").replace(/\/+$/, "");
|
|
1824
|
-
}
|
|
1825
1922
|
async start() {
|
|
1826
1923
|
const url = (this.cfg.relayUrl || injectedRelayUrl() || "").replace(/\/+$/, "");
|
|
1827
1924
|
this.relayBase = url;
|
|
1828
1925
|
if (!url) {
|
|
1829
|
-
this.log("
|
|
1926
|
+
this.log(this.t("hermes.errNoRelay"));
|
|
1830
1927
|
} else {
|
|
1831
1928
|
this.connect(url);
|
|
1832
1929
|
}
|
|
1833
|
-
if (this.cfg.approval?.enabled) {
|
|
1834
|
-
const approval = new ApprovalBridge(this.cfg.approval);
|
|
1835
|
-
approval.onPermissionRequest = (req) => {
|
|
1836
|
-
this.registerPending(req);
|
|
1837
|
-
return this.send(envelope("permission.request", req));
|
|
1838
|
-
};
|
|
1839
|
-
approval.start();
|
|
1840
|
-
this.approval = approval;
|
|
1841
|
-
}
|
|
1842
1930
|
}
|
|
1843
1931
|
dispose() {
|
|
1844
|
-
this.approval?.stop();
|
|
1845
|
-
this.approval = null;
|
|
1846
1932
|
this.ws.close();
|
|
1847
1933
|
for (const p of this.activeRuns.values()) p.kill();
|
|
1848
1934
|
this.activeRuns.clear();
|
|
@@ -1858,10 +1944,6 @@ var HermesBridge = class {
|
|
|
1858
1944
|
msg.clientID = src.clientID;
|
|
1859
1945
|
this.send(msg);
|
|
1860
1946
|
}
|
|
1861
|
-
pairingLink(code) {
|
|
1862
|
-
const p = new URLSearchParams({ code, type: AGENT_TYPE, name: hostname() });
|
|
1863
|
-
return `${this.relayHttpBase}/app/#/pages/index/index?${p.toString()}`;
|
|
1864
|
-
}
|
|
1865
1947
|
connect(url) {
|
|
1866
1948
|
const agentID = this.state.agentId;
|
|
1867
1949
|
const wsUrl = `${url}/ws/agent?token=${encodeURIComponent(agentID)}`;
|
|
@@ -1871,17 +1953,18 @@ var HermesBridge = class {
|
|
|
1871
1953
|
{
|
|
1872
1954
|
onOpen: () => {
|
|
1873
1955
|
this.everConnected = true;
|
|
1874
|
-
this.log("
|
|
1956
|
+
this.log(this.t("hermes.logConnected", { url }));
|
|
1875
1957
|
this.announce();
|
|
1876
1958
|
},
|
|
1877
1959
|
onMessage: (env) => {
|
|
1878
|
-
void this.onMessage(env).catch(
|
|
1960
|
+
void this.onMessage(env).catch(
|
|
1961
|
+
(e) => this.log(this.t("hermes.logMsgUncaught", { message: e.message }))
|
|
1962
|
+
);
|
|
1879
1963
|
},
|
|
1880
1964
|
onClose: () => {
|
|
1881
1965
|
if (this.everConnected) {
|
|
1882
1966
|
this.everConnected = false;
|
|
1883
|
-
this.log("
|
|
1884
|
-
this.approval?.flush("\u4E2D\u7EE7\u65AD\u5F00\uFF0C\u81EA\u52A8\u62D2\u7EDD\uFF08fail-closed\uFF09");
|
|
1967
|
+
this.log(this.t("hermes.logDisconnected"));
|
|
1885
1968
|
}
|
|
1886
1969
|
},
|
|
1887
1970
|
onError: () => {
|
|
@@ -1896,11 +1979,11 @@ var HermesBridge = class {
|
|
|
1896
1979
|
type: AGENT_TYPE,
|
|
1897
1980
|
capabilities: {
|
|
1898
1981
|
streaming: true,
|
|
1899
|
-
//
|
|
1982
|
+
// 回合级整段回传(chat -Q -q 一次性输出,非逐 token;协议 text+complete 语义)
|
|
1900
1983
|
thinking: false,
|
|
1901
|
-
// hermes
|
|
1902
|
-
permission:
|
|
1903
|
-
//
|
|
1984
|
+
// hermes oneshot 无结构化推理部件
|
|
1985
|
+
permission: false,
|
|
1986
|
+
// headless 无官方审批通道 → Hermes 自身策略/沙箱 fail-closed 兜底
|
|
1904
1987
|
commands: true,
|
|
1905
1988
|
abort: true
|
|
1906
1989
|
// kill 子进程
|
|
@@ -1910,24 +1993,31 @@ var HermesBridge = class {
|
|
|
1910
1993
|
timezone: localTimezone()
|
|
1911
1994
|
};
|
|
1912
1995
|
this.send(envelope("agent.info", info));
|
|
1913
|
-
if (
|
|
1996
|
+
if (this.cfg.forcePair) {
|
|
1997
|
+
this.log(this.t("hermes.logPairForced"));
|
|
1998
|
+
this.send(envelope("pair.create", {}));
|
|
1999
|
+
} else if (!this.claimed) {
|
|
2000
|
+
this.send(envelope("pair.create", {}));
|
|
2001
|
+
}
|
|
1914
2002
|
}
|
|
1915
2003
|
async onMessage(env) {
|
|
1916
2004
|
try {
|
|
1917
2005
|
switch (env.type) {
|
|
1918
2006
|
case "pair.created": {
|
|
1919
2007
|
const code = env.payload.code;
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
this.
|
|
1923
|
-
|
|
2008
|
+
writePairingJson({ code, relayUrl: this.relayBase, at: Date.now() }, AGENT_TYPE);
|
|
2009
|
+
this.log(this.t("hermes.logPairCode", { code }));
|
|
2010
|
+
if (this.claimed) {
|
|
2011
|
+
this.log(this.t("hermes.pairingStillClaimed"));
|
|
2012
|
+
}
|
|
2013
|
+
void printPairing(code, AGENT_TYPE, hostname(), this.cfg.lang ?? "en-US");
|
|
1924
2014
|
return;
|
|
1925
2015
|
}
|
|
1926
2016
|
case "agent.claimed":
|
|
1927
2017
|
this.claimed = true;
|
|
1928
2018
|
this.state.claimed = true;
|
|
1929
2019
|
saveState(this.state, AGENT_TYPE);
|
|
1930
|
-
this.log("
|
|
2020
|
+
this.log(this.t("hermes.logClaimed"));
|
|
1931
2021
|
return;
|
|
1932
2022
|
case "agent.revoked":
|
|
1933
2023
|
this.resetIdentity();
|
|
@@ -1946,10 +2036,6 @@ var HermesBridge = class {
|
|
|
1946
2036
|
return this.sessionDelete(env);
|
|
1947
2037
|
case "command.list":
|
|
1948
2038
|
return this.commandList(env);
|
|
1949
|
-
case "permission.reply":
|
|
1950
|
-
return this.permissionReply(env);
|
|
1951
|
-
case "session.pending":
|
|
1952
|
-
return this.sessionPending(env);
|
|
1953
2039
|
case "workspace.list":
|
|
1954
2040
|
return this.workspaceList(env);
|
|
1955
2041
|
case "workspace.set":
|
|
@@ -1957,10 +2043,10 @@ var HermesBridge = class {
|
|
|
1957
2043
|
case "error":
|
|
1958
2044
|
return;
|
|
1959
2045
|
default:
|
|
1960
|
-
this.log("
|
|
2046
|
+
this.log(this.t("hermes.logUnhandledType", { type: env.type }));
|
|
1961
2047
|
}
|
|
1962
2048
|
} catch (e) {
|
|
1963
|
-
this.log(
|
|
2049
|
+
this.log(this.t("hermes.logHandleFailed", { type: env.type, message: e.message }));
|
|
1964
2050
|
this.reply(env, "error", { code: "HANDLER_ERROR", message: e.message, id: env.id });
|
|
1965
2051
|
}
|
|
1966
2052
|
}
|
|
@@ -1976,10 +2062,10 @@ var HermesBridge = class {
|
|
|
1976
2062
|
const workspace = payload?.workspace || this.currentWorkspace;
|
|
1977
2063
|
const id = newSessionId();
|
|
1978
2064
|
const now = Date.now();
|
|
1979
|
-
saveMeta(id, { title: "
|
|
2065
|
+
saveMeta(id, { title: this.t("hermes.newSessionTitle"), cwd: workspace, created: now, updated: now });
|
|
1980
2066
|
this.pendingWorkspaces.set(id, workspace);
|
|
1981
2067
|
this.reply(env, "session.created", {
|
|
1982
|
-
session: { id, title: "
|
|
2068
|
+
session: { id, title: this.t("hermes.newSessionTitle"), created: now, updated: now, status: "idle", workspace }
|
|
1983
2069
|
});
|
|
1984
2070
|
}
|
|
1985
2071
|
async sessionMessages(env) {
|
|
@@ -1996,19 +2082,19 @@ var HermesBridge = class {
|
|
|
1996
2082
|
this.reply(env, "message.part", {
|
|
1997
2083
|
sessionID: payload.sessionID,
|
|
1998
2084
|
messageID: `m-${Date.now()}`,
|
|
1999
|
-
part: { id: "t", type: "text", text: "
|
|
2085
|
+
part: { id: "t", type: "text", text: this.t("hermes.helpText"), complete: true }
|
|
2000
2086
|
});
|
|
2001
2087
|
return;
|
|
2002
2088
|
}
|
|
2003
2089
|
if (this.activeRuns.has(payload.sessionID)) {
|
|
2004
|
-
this.reply(env, "error", { code: "BUSY", message: "
|
|
2090
|
+
this.reply(env, "error", { code: "BUSY", message: this.t("hermes.errBusy"), id: env.id });
|
|
2005
2091
|
return;
|
|
2006
2092
|
}
|
|
2007
2093
|
try {
|
|
2008
2094
|
await this.runTurn(payload.sessionID, text);
|
|
2009
2095
|
} catch (e) {
|
|
2010
2096
|
const msg = e.message || String(e);
|
|
2011
|
-
this.log(
|
|
2097
|
+
this.log(this.t("hermes.logPromptFailed", { message: msg }));
|
|
2012
2098
|
this.reply(env, "error", { code: "PROMPT_ERROR", message: msg, id: env.id });
|
|
2013
2099
|
}
|
|
2014
2100
|
}
|
|
@@ -2017,7 +2103,7 @@ var HermesBridge = class {
|
|
|
2017
2103
|
const p = this.activeRuns.get(sessionID);
|
|
2018
2104
|
if (p) {
|
|
2019
2105
|
p.kill();
|
|
2020
|
-
this.log(
|
|
2106
|
+
this.log(this.t("hermes.logAbortSent", { session: sessionID }));
|
|
2021
2107
|
}
|
|
2022
2108
|
this.send(envelope("session.status", { sessionID, status: "idle" }));
|
|
2023
2109
|
}
|
|
@@ -2032,43 +2118,23 @@ var HermesBridge = class {
|
|
|
2032
2118
|
this.reply(env, "session.deleted", { sessionID });
|
|
2033
2119
|
}
|
|
2034
2120
|
async commandList(env) {
|
|
2035
|
-
this.reply(env, "commands", {
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
this.pendingRequests.delete(p.permissionID);
|
|
2041
|
-
this.approval?.resolve(p.permissionID, {
|
|
2042
|
-
decision: p.status === "allow" ? "allow" : "deny",
|
|
2043
|
-
reason: p.status === "deny" ? "\u7528\u6237\u62D2\u7EDD" : void 0
|
|
2121
|
+
this.reply(env, "commands", {
|
|
2122
|
+
commands: [
|
|
2123
|
+
{ name: "new", description: this.t("hermes.cmdNew") },
|
|
2124
|
+
{ name: "help", description: this.t("hermes.cmdHelp") }
|
|
2125
|
+
]
|
|
2044
2126
|
});
|
|
2045
|
-
this.send(envelope("permission.replied", { permissionID: p.permissionID, status: p.status }));
|
|
2046
|
-
}
|
|
2047
|
-
registerPending(req) {
|
|
2048
|
-
const id = String(req?.id ?? "");
|
|
2049
|
-
if (!id) return;
|
|
2050
|
-
this.pendingRequests.set(id, { at: Date.now(), req });
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* session.pending:补发仍未决(未超时)的审批请求。hermes hook 上报的会话 id 与
|
|
2054
|
-
* tunnelbox 会话不保证一一对应,故全量补发在途请求;手机端按 permissionID 幂等去重。
|
|
2055
|
-
*/
|
|
2056
|
-
async sessionPending(_env) {
|
|
2057
|
-
const MAX_AGE = 12e4;
|
|
2058
|
-
const now = Date.now();
|
|
2059
|
-
let n = 0;
|
|
2060
|
-
for (const [id, pe] of this.pendingRequests) {
|
|
2061
|
-
if (now - pe.at >= MAX_AGE) {
|
|
2062
|
-
this.pendingRequests.delete(id);
|
|
2063
|
-
continue;
|
|
2064
|
-
}
|
|
2065
|
-
if (this.send(envelope("permission.request", pe.req))) n++;
|
|
2066
|
-
}
|
|
2067
|
-
if (n) this.log(`session.pending \u8865\u53D1\u5F85\u5BA1\u6279 ${n} \u6761`);
|
|
2068
2127
|
}
|
|
2069
2128
|
async workspaceList(env) {
|
|
2070
2129
|
this.reply(env, "workspaces", {
|
|
2071
|
-
workspaces: [
|
|
2130
|
+
workspaces: [
|
|
2131
|
+
{
|
|
2132
|
+
path: this.currentWorkspace,
|
|
2133
|
+
name: this.currentWorkspace.split(/[\\/]/).pop() || this.currentWorkspace,
|
|
2134
|
+
sessionCount: 0,
|
|
2135
|
+
updated: 0
|
|
2136
|
+
}
|
|
2137
|
+
],
|
|
2072
2138
|
current: this.currentWorkspace
|
|
2073
2139
|
});
|
|
2074
2140
|
}
|
|
@@ -2076,32 +2142,32 @@ var HermesBridge = class {
|
|
|
2076
2142
|
const raw = (env.payload?.path || "").trim();
|
|
2077
2143
|
const path = raw ? resolve(raw) : "";
|
|
2078
2144
|
if (!path || !existsSync4(path) || !statSync2(path).isDirectory()) {
|
|
2079
|
-
this.reply(env, "error", { code: "BAD_WORKSPACE", message: "
|
|
2145
|
+
this.reply(env, "error", { code: "BAD_WORKSPACE", message: this.t("hermes.errBadWorkspace"), id: env.id });
|
|
2080
2146
|
return;
|
|
2081
2147
|
}
|
|
2082
2148
|
this.currentWorkspace = path;
|
|
2083
|
-
this.log("
|
|
2149
|
+
this.log(this.t("hermes.logWorkspaceSwitched", { path }));
|
|
2084
2150
|
this.announce();
|
|
2085
2151
|
await this.workspaceList(env);
|
|
2086
2152
|
}
|
|
2087
2153
|
async runTurn(sessionID, text) {
|
|
2088
2154
|
const dir = this.pendingWorkspaces.get(sessionID) || this.sessionCwd.get(sessionID) || this.currentWorkspace;
|
|
2089
2155
|
if (!existsSync4(dir) || !statSync2(dir).isDirectory()) {
|
|
2090
|
-
throw new Error(
|
|
2156
|
+
throw new Error(this.t("hermes.errWorkspaceMissing", { dir }));
|
|
2091
2157
|
}
|
|
2092
2158
|
this.pendingWorkspaces.delete(sessionID);
|
|
2093
2159
|
const now = Date.now();
|
|
2094
|
-
|
|
2160
|
+
const defaultTitle = this.t("hermes.newSessionTitle");
|
|
2161
|
+
appendPart(sessionID, `u-${now}`, "user", { id: "u", type: "text", text, complete: true }, now, defaultTitle);
|
|
2095
2162
|
this.send(envelope("session.status", { sessionID, status: "running" }));
|
|
2096
|
-
const
|
|
2163
|
+
const hermesId = getMeta(sessionID)?.hermesId;
|
|
2097
2164
|
const proc = new HermesProcess(sessionID, {
|
|
2098
2165
|
emit: (s, messageID, part) => {
|
|
2099
2166
|
this.send(envelope("message.part", { sessionID: s, messageID, part }));
|
|
2100
|
-
appendPart(sessionID, messageID, "assistant", part);
|
|
2167
|
+
appendPart(sessionID, messageID, "assistant", part, Date.now(), defaultTitle);
|
|
2101
2168
|
},
|
|
2102
2169
|
status: (s, status) => this.send(envelope("session.status", { sessionID: s, status })),
|
|
2103
|
-
log: (m) => this.log(m)
|
|
2104
|
-
onResumeId: (id) => saveMeta(sessionID, { resumeId: id, cwd: dir })
|
|
2170
|
+
log: (m) => this.log(m)
|
|
2105
2171
|
});
|
|
2106
2172
|
this.activeRuns.set(sessionID, proc);
|
|
2107
2173
|
try {
|
|
@@ -2112,16 +2178,21 @@ var HermesBridge = class {
|
|
|
2112
2178
|
provider: this.cfg.provider,
|
|
2113
2179
|
toolsets: this.cfg.toolsets,
|
|
2114
2180
|
verbose: this.cfg.verbose,
|
|
2115
|
-
bin: this.cfg.bin
|
|
2181
|
+
bin: this.cfg.bin,
|
|
2182
|
+
lang: this.cfg.lang
|
|
2116
2183
|
},
|
|
2117
2184
|
text,
|
|
2118
|
-
|
|
2185
|
+
hermesId
|
|
2119
2186
|
);
|
|
2120
2187
|
this.sessionCwd.set(sessionID, dir);
|
|
2121
|
-
if (!getMeta(sessionID)) saveMeta(sessionID, { title:
|
|
2188
|
+
if (!getMeta(sessionID)) saveMeta(sessionID, { title: defaultTitle, cwd: dir }, defaultTitle);
|
|
2189
|
+
if (!hermesId) {
|
|
2190
|
+
const discovered = latestHermesId(this.cfg.bin || "hermes", dir);
|
|
2191
|
+
if (discovered) saveMeta(sessionID, { hermesId: discovered, cwd: dir }, defaultTitle);
|
|
2192
|
+
}
|
|
2122
2193
|
} catch (e) {
|
|
2123
2194
|
const msg = e.message || String(e);
|
|
2124
|
-
this.log(
|
|
2195
|
+
this.log(this.t("hermes.logTurnError", { session: sessionID, message: msg }));
|
|
2125
2196
|
const id = `m-err-${Date.now()}`;
|
|
2126
2197
|
const part = { id: "err", type: "text", text: `[hermes] ${msg}`, complete: true };
|
|
2127
2198
|
this.send(envelope("message.part", { sessionID, messageID: id, part }));
|
|
@@ -2142,7 +2213,7 @@ var HermesBridge = class {
|
|
|
2142
2213
|
}
|
|
2143
2214
|
}
|
|
2144
2215
|
resetIdentity() {
|
|
2145
|
-
this.log("
|
|
2216
|
+
this.log(this.t("hermes.logResetIdentity"));
|
|
2146
2217
|
this.claimed = false;
|
|
2147
2218
|
this.state.agentId = randomBytes2(16).toString("hex");
|
|
2148
2219
|
this.state.claimed = false;
|
|
@@ -2161,22 +2232,13 @@ function localTimezone() {
|
|
|
2161
2232
|
}
|
|
2162
2233
|
|
|
2163
2234
|
// src/index.ts
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
const v = (raw || "").trim().toLowerCase();
|
|
2167
|
-
if (v === "on" || v === "1" || v === "true") return true;
|
|
2168
|
-
if (v === "off" || v === "0" || v === "false") return false;
|
|
2169
|
-
return fallback ?? true;
|
|
2235
|
+
function resolveLangForAdapter(saved) {
|
|
2236
|
+
return resolveLang({ saved });
|
|
2170
2237
|
}
|
|
2171
2238
|
function start(opts = {}) {
|
|
2239
|
+
const lang = resolveLangForAdapter(opts.lang ?? null);
|
|
2240
|
+
const t2 = makeT(lang);
|
|
2172
2241
|
const envOn = (name) => /^(1|true|yes|on)$/i.test(process.env[name] || "");
|
|
2173
|
-
const entryPath = fileURLToPath(import.meta.url);
|
|
2174
|
-
const approvalEnabled = parseApproval(process.env.TUNNELBOX_HERMES_APPROVAL, opts.approval);
|
|
2175
|
-
const approval = {
|
|
2176
|
-
enabled: approvalEnabled,
|
|
2177
|
-
port: parseInt(process.env.TUNNELBOX_HERMES_HOOK_PORT || String(opts.hookPort || DEFAULT_HOOK_PORT), 10) || DEFAULT_HOOK_PORT,
|
|
2178
|
-
entryPath
|
|
2179
|
-
};
|
|
2180
2242
|
const cfg = {
|
|
2181
2243
|
relayUrl: (process.env.TUNNELBOX_RELAY_URL || opts.relayUrl || "").trim(),
|
|
2182
2244
|
cwd: process.env.TUNNELBOX_CWD || opts.cwd || process.cwd(),
|
|
@@ -2185,11 +2247,12 @@ function start(opts = {}) {
|
|
|
2185
2247
|
toolsets: process.env.TUNNELBOX_HERMES_TOOLSETS?.trim() || opts.toolsets || void 0,
|
|
2186
2248
|
verbose: envOn("TUNNELBOX_HERMES_VERBOSE") || !!opts.verbose,
|
|
2187
2249
|
bin: process.env.TUNNELBOX_HERMES_BIN?.trim() || opts.bin || "hermes",
|
|
2188
|
-
|
|
2250
|
+
lang,
|
|
2251
|
+
forcePair: !!opts.forcePair
|
|
2189
2252
|
};
|
|
2190
2253
|
const bridge = new HermesBridge(cfg);
|
|
2191
2254
|
void bridge.start().catch((e) => {
|
|
2192
|
-
console.error(
|
|
2255
|
+
console.error(`[tunnelbox:hermes] ${t2("hermes.errStartFailed", { message: e.message })}`);
|
|
2193
2256
|
});
|
|
2194
2257
|
let stopped = false;
|
|
2195
2258
|
return {
|
|
@@ -2208,20 +2271,16 @@ var isEntry = (() => {
|
|
|
2208
2271
|
}
|
|
2209
2272
|
})();
|
|
2210
2273
|
if (isEntry) {
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
};
|
|
2220
|
-
process.once("SIGINT", onSignal);
|
|
2221
|
-
process.once("SIGTERM", onSignal);
|
|
2222
|
-
console.log("[tunnelbox:hermes] Hermes \u9002\u914D\u5668\u5DF2\u542F\u52A8\uFF08Ctrl+C \u9000\u51FA\uFF09");
|
|
2223
|
-
}
|
|
2274
|
+
const { stop } = start({ forcePair: process.argv.includes("--pair") });
|
|
2275
|
+
const onSignal = () => {
|
|
2276
|
+
stop();
|
|
2277
|
+
process.exit(0);
|
|
2278
|
+
};
|
|
2279
|
+
process.once("SIGINT", onSignal);
|
|
2280
|
+
process.once("SIGTERM", onSignal);
|
|
2281
|
+
console.log(`[tunnelbox:hermes] ${makeT(resolveLangForAdapter(null))("hermes.msgStarted")}`);
|
|
2224
2282
|
}
|
|
2225
2283
|
export {
|
|
2284
|
+
resolveLangForAdapter,
|
|
2226
2285
|
start
|
|
2227
2286
|
};
|