@soyeht/soyeht 0.2.3 → 0.2.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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/qr.ts +12 -4
- package/src/service.ts +40 -4
- package/src/version.ts +1 -1
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/qr.ts
CHANGED
|
@@ -426,11 +426,19 @@ export function renderQrTerminal(text: string): string | null {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// ▄ (lower half block): foreground = lower half, background = upper half
|
|
429
|
+
// Use 24-bit true color (ESC[48;2;R;G;Bm) for pure black/white contrast.
|
|
430
|
+
// Generic ANSI colors (40/47) map to theme colors which may not be pure B&W.
|
|
431
|
+
const BG_BLACK = "\x1b[48;2;0;0;0m";
|
|
432
|
+
const BG_WHITE = "\x1b[48;2;255;255;255m";
|
|
433
|
+
const FG_BLACK = "\x1b[38;2;0;0;0m";
|
|
434
|
+
const FG_WHITE = "\x1b[38;2;255;255;255m";
|
|
435
|
+
const RST = "\x1b[0m";
|
|
436
|
+
|
|
429
437
|
function cell(top: number, bottom: number): string {
|
|
430
|
-
if (top === 1 && bottom === 1) return
|
|
431
|
-
if (top === 0 && bottom === 0) return
|
|
432
|
-
if (top === 1 && bottom === 0) return
|
|
433
|
-
return
|
|
438
|
+
if (top === 1 && bottom === 1) return `${BG_BLACK} ${RST}`;
|
|
439
|
+
if (top === 0 && bottom === 0) return `${BG_WHITE} ${RST}`;
|
|
440
|
+
if (top === 1 && bottom === 0) return `${FG_WHITE}${BG_BLACK}\u2584${RST}`;
|
|
441
|
+
return `${FG_BLACK}${BG_WHITE}\u2584${RST}`;
|
|
434
442
|
}
|
|
435
443
|
|
|
436
444
|
const lines: string[] = [];
|
package/src/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { readFile, writeFile, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { networkInterfaces } from "node:os";
|
|
4
5
|
import type { OpenClawPluginApi, OpenClawPluginService, OpenClawPluginServiceContext } from "openclaw/plugin-sdk";
|
|
5
6
|
import {
|
|
6
7
|
createNonceCache,
|
|
@@ -73,6 +74,26 @@ export function createSecurityV2Deps(): SecurityV2Deps {
|
|
|
73
74
|
// ---------------------------------------------------------------------------
|
|
74
75
|
|
|
75
76
|
const AUTO_PAIRING_TTL_MS = 90_000;
|
|
77
|
+
const DEFAULT_GATEWAY_PORT = 18789;
|
|
78
|
+
|
|
79
|
+
function detectLanIp(): string | null {
|
|
80
|
+
const nets = networkInterfaces();
|
|
81
|
+
for (const ifaces of Object.values(nets)) {
|
|
82
|
+
if (!ifaces) continue;
|
|
83
|
+
for (const iface of ifaces) {
|
|
84
|
+
// Skip loopback and internal, only IPv4
|
|
85
|
+
if (iface.internal || iface.family !== "IPv4") continue;
|
|
86
|
+
return iface.address;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function detectGatewayPort(cfg: Record<string, unknown>): number {
|
|
93
|
+
const gw = (cfg as Record<string, unknown>)["gateway"] as Record<string, unknown> | undefined;
|
|
94
|
+
const port = gw?.["port"];
|
|
95
|
+
return typeof port === "number" && port > 0 ? port : DEFAULT_GATEWAY_PORT;
|
|
96
|
+
}
|
|
76
97
|
|
|
77
98
|
async function showPairingQr(api: OpenClawPluginApi, v2deps: SecurityV2Deps): Promise<void> {
|
|
78
99
|
const identity = v2deps.identity;
|
|
@@ -91,10 +112,24 @@ async function showPairingQr(api: OpenClawPluginApi, v2deps: SecurityV2Deps): Pr
|
|
|
91
112
|
const expiresAt = Date.now() + AUTO_PAIRING_TTL_MS;
|
|
92
113
|
const fingerprint = computeFingerprint(identity);
|
|
93
114
|
|
|
94
|
-
// Resolve gatewayUrl
|
|
115
|
+
// Resolve gatewayUrl: config > runtime > auto-detect LAN IP
|
|
95
116
|
const cfg = await api.runtime.config.loadConfig();
|
|
96
117
|
const account = resolveSoyehtAccount(cfg, accountId);
|
|
97
|
-
|
|
118
|
+
let gatewayUrl = account.gatewayUrl;
|
|
119
|
+
if (!gatewayUrl) {
|
|
120
|
+
const runtime = api.runtime as Record<string, unknown>;
|
|
121
|
+
if (typeof runtime["gatewayUrl"] === "string" && runtime["gatewayUrl"]) {
|
|
122
|
+
gatewayUrl = runtime["gatewayUrl"];
|
|
123
|
+
} else if (typeof runtime["baseUrl"] === "string" && runtime["baseUrl"]) {
|
|
124
|
+
gatewayUrl = runtime["baseUrl"];
|
|
125
|
+
} else {
|
|
126
|
+
const ip = detectLanIp();
|
|
127
|
+
if (ip) {
|
|
128
|
+
const port = detectGatewayPort(cfg);
|
|
129
|
+
gatewayUrl = `http://${ip}:${port}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
98
133
|
|
|
99
134
|
v2deps.pairingSessions.set(pairingToken, {
|
|
100
135
|
token: pairingToken,
|
|
@@ -104,8 +139,9 @@ async function showPairingQr(api: OpenClawPluginApi, v2deps: SecurityV2Deps): Pr
|
|
|
104
139
|
});
|
|
105
140
|
|
|
106
141
|
// Compact QR: soyeht://pair?g=<gatewayUrl>&t=<token>&fp=<fingerprint>
|
|
107
|
-
//
|
|
108
|
-
|
|
142
|
+
// No encodeURIComponent — custom scheme, no escaping needed.
|
|
143
|
+
// App fetches full key material via HTTP GET /soyeht/pairing/info
|
|
144
|
+
const qrText = `soyeht://pair?g=${gatewayUrl}&t=${pairingToken}&fp=${fingerprint}`;
|
|
109
145
|
const rendered = renderQrTerminal(qrText);
|
|
110
146
|
|
|
111
147
|
if (rendered) {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PLUGIN_VERSION = "0.2.
|
|
1
|
+
export const PLUGIN_VERSION = "0.2.5";
|