@hermespilot/link 0.5.1 → 0.5.3
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 +8 -2
- package/dist/{chunk-PULX22HX.js → chunk-5JBXQ3VC.js} +1144 -53
- package/dist/cli/index.js +301 -11
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +6 -0
package/dist/cli/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
ensureHermesApiServerAvailable,
|
|
15
15
|
ensureHermesApiServerConfig,
|
|
16
16
|
ensureIdentity,
|
|
17
|
+
fetchRelayStreamBatchPolicy,
|
|
17
18
|
getDaemonStatus,
|
|
18
19
|
getIdentityStatus,
|
|
19
20
|
getLinkLogFile,
|
|
@@ -36,12 +37,12 @@ import {
|
|
|
36
37
|
startDaemonProcess,
|
|
37
38
|
startLinkService,
|
|
38
39
|
stopDaemonProcess
|
|
39
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-5JBXQ3VC.js";
|
|
40
41
|
|
|
41
42
|
// src/cli/index.ts
|
|
42
43
|
import { Command } from "commander";
|
|
43
|
-
import { realpathSync } from "fs";
|
|
44
|
-
import
|
|
44
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
45
|
+
import path4 from "path";
|
|
45
46
|
import { createInterface } from "readline/promises";
|
|
46
47
|
import { pathToFileURL } from "url";
|
|
47
48
|
import qrcode from "qrcode-terminal";
|
|
@@ -139,6 +140,7 @@ async function hasSystemctlUser() {
|
|
|
139
140
|
}
|
|
140
141
|
function launchdDefinition() {
|
|
141
142
|
const filePath = path.join(os.homedir(), "Library", "LaunchAgents", `${MACOS_LABEL}.plist`);
|
|
143
|
+
const environment = autostartEnvironment();
|
|
142
144
|
return {
|
|
143
145
|
method: "launchd",
|
|
144
146
|
filePath,
|
|
@@ -154,6 +156,10 @@ function launchdDefinition() {
|
|
|
154
156
|
<string>${xmlEscape(currentCliScriptPath())}</string>
|
|
155
157
|
<string>daemon-supervisor</string>
|
|
156
158
|
</array>
|
|
159
|
+
<key>EnvironmentVariables</key>
|
|
160
|
+
<dict>
|
|
161
|
+
${plistEnvironmentEntries(environment)}
|
|
162
|
+
</dict>
|
|
157
163
|
<key>RunAtLoad</key>
|
|
158
164
|
<true/>
|
|
159
165
|
<key>KeepAlive</key>
|
|
@@ -165,6 +171,7 @@ function launchdDefinition() {
|
|
|
165
171
|
}
|
|
166
172
|
function systemdUserDefinition() {
|
|
167
173
|
const filePath = path.join(os.homedir(), ".config", "systemd", "user", "hermeslink.service");
|
|
174
|
+
const environment = autostartEnvironment();
|
|
168
175
|
return {
|
|
169
176
|
method: "systemd-user",
|
|
170
177
|
filePath,
|
|
@@ -174,6 +181,7 @@ After=network-online.target
|
|
|
174
181
|
|
|
175
182
|
[Service]
|
|
176
183
|
Type=simple
|
|
184
|
+
${systemdEnvironmentLines(environment)}
|
|
177
185
|
ExecStart=${systemdQuote(process.execPath)} ${systemdQuote(currentCliScriptPath())} daemon-supervisor
|
|
178
186
|
Restart=no
|
|
179
187
|
|
|
@@ -184,13 +192,14 @@ WantedBy=default.target
|
|
|
184
192
|
}
|
|
185
193
|
function xdgAutostartDefinition() {
|
|
186
194
|
const filePath = path.join(os.homedir(), ".config", "autostart", "hermeslink.desktop");
|
|
195
|
+
const environment = autostartEnvironment();
|
|
187
196
|
return {
|
|
188
197
|
method: "xdg-autostart",
|
|
189
198
|
filePath,
|
|
190
199
|
content: `[Desktop Entry]
|
|
191
200
|
Type=Application
|
|
192
201
|
Name=Hermes Link
|
|
193
|
-
Exec=${desktopQuote(process.execPath)} ${desktopQuote(currentCliScriptPath())} daemon-supervisor
|
|
202
|
+
Exec=${desktopQuote("/usr/bin/env")} ${desktopEnvironmentArgs(environment)} ${desktopQuote(process.execPath)} ${desktopQuote(currentCliScriptPath())} daemon-supervisor
|
|
194
203
|
Terminal=false
|
|
195
204
|
X-GNOME-Autostart-enabled=true
|
|
196
205
|
`
|
|
@@ -199,12 +208,16 @@ X-GNOME-Autostart-enabled=true
|
|
|
199
208
|
function windowsStartupDefinition() {
|
|
200
209
|
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
201
210
|
const filePath = path.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup", "HermesLink.cmd");
|
|
211
|
+
const environment = autostartEnvironment();
|
|
202
212
|
return {
|
|
203
213
|
method: "windows-startup",
|
|
204
214
|
filePath,
|
|
205
|
-
content:
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
content: [
|
|
216
|
+
"@echo off",
|
|
217
|
+
...cmdEnvironmentLines(environment),
|
|
218
|
+
`start "" /min "${process.execPath}" "${currentCliScriptPath()}" daemon-supervisor`,
|
|
219
|
+
""
|
|
220
|
+
].join("\r\n")
|
|
208
221
|
};
|
|
209
222
|
}
|
|
210
223
|
function unsupportedStatus() {
|
|
@@ -224,6 +237,57 @@ function systemdQuote(value) {
|
|
|
224
237
|
function desktopQuote(value) {
|
|
225
238
|
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
226
239
|
}
|
|
240
|
+
function autostartEnvironment() {
|
|
241
|
+
const environment = {};
|
|
242
|
+
const pathValue = buildAutostartPath();
|
|
243
|
+
if (pathValue) {
|
|
244
|
+
environment.PATH = pathValue;
|
|
245
|
+
}
|
|
246
|
+
const hermesBin = process.env.HERMES_BIN?.trim();
|
|
247
|
+
if (hermesBin) {
|
|
248
|
+
environment.HERMES_BIN = hermesBin;
|
|
249
|
+
}
|
|
250
|
+
return environment;
|
|
251
|
+
}
|
|
252
|
+
function buildAutostartPath() {
|
|
253
|
+
const separator = process.platform === "win32" ? ";" : ":";
|
|
254
|
+
const candidates = [
|
|
255
|
+
path.dirname(process.execPath),
|
|
256
|
+
...(process.env.PATH ?? "").split(separator),
|
|
257
|
+
path.join(os.homedir(), ".local", "bin"),
|
|
258
|
+
...process.platform === "win32" ? [] : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"]
|
|
259
|
+
];
|
|
260
|
+
const seen = /* @__PURE__ */ new Set();
|
|
261
|
+
const entries = [];
|
|
262
|
+
for (const candidate of candidates) {
|
|
263
|
+
const normalized = candidate.trim();
|
|
264
|
+
if (!normalized) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const key = process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
268
|
+
if (seen.has(key)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
seen.add(key);
|
|
272
|
+
entries.push(normalized);
|
|
273
|
+
}
|
|
274
|
+
return entries.join(separator);
|
|
275
|
+
}
|
|
276
|
+
function plistEnvironmentEntries(environment) {
|
|
277
|
+
return Object.entries(environment).map(([key, value]) => ` <key>${xmlEscape(key)}</key>
|
|
278
|
+
<string>${xmlEscape(value)}</string>`).join("\n");
|
|
279
|
+
}
|
|
280
|
+
function systemdEnvironmentLines(environment) {
|
|
281
|
+
return Object.entries(environment).map(([key, value]) => `Environment=${systemdQuote(`${key}=${value}`)}`).join("\n");
|
|
282
|
+
}
|
|
283
|
+
function desktopEnvironmentArgs(environment) {
|
|
284
|
+
return Object.entries(environment).map(([key, value]) => desktopQuote(`${key}=${value}`)).join(" ");
|
|
285
|
+
}
|
|
286
|
+
function cmdEnvironmentLines(environment) {
|
|
287
|
+
return Object.entries(environment).map(
|
|
288
|
+
([key, value]) => `set "${key}=${value.replaceAll('"', "")}"`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
227
291
|
|
|
228
292
|
// src/i18n.ts
|
|
229
293
|
var messages = {
|
|
@@ -303,6 +367,25 @@ var messages = {
|
|
|
303
367
|
"pair.autostartUnchanged": "Existing paired devices found. Boot autostart settings were left unchanged.",
|
|
304
368
|
"pair.autostartFailed": "Pairing succeeded, but boot autostart could not be enabled: {message}",
|
|
305
369
|
"doctor.description": "Run local diagnostics",
|
|
370
|
+
"doctor.installOnly": "only check npm global command and PATH setup",
|
|
371
|
+
"doctor.installHeader": "Install/PATH diagnostics:",
|
|
372
|
+
"doctor.installNpmPrefix": "npm global prefix: {value}",
|
|
373
|
+
"doctor.installGlobalBin": "npm global bin directory: {value}",
|
|
374
|
+
"doctor.installExpectedCommand": "expected hermeslink command: {value} ({state})",
|
|
375
|
+
"doctor.installCommandOnPath": "hermeslink found on PATH: {value}",
|
|
376
|
+
"doctor.installUnknown": "unknown",
|
|
377
|
+
"doctor.installOk": "ok",
|
|
378
|
+
"doctor.installMissing": "missing",
|
|
379
|
+
"doctor.installReady": "The global hermeslink command is visible to this shell.",
|
|
380
|
+
"doctor.installPrefixUnavailable": "Could not read npm global prefix with `{command} prefix -g`. Check that npm is available in this shell.",
|
|
381
|
+
"doctor.installExpectedMissing": "npm does not appear to have created the global hermeslink shim. Re-run `npm install -g @hermespilot/link` in the same system environment where Hermes runs.",
|
|
382
|
+
"doctor.installPathMissing": "npm installed hermeslink, but this shell cannot see npm's global bin directory on PATH.",
|
|
383
|
+
"doctor.installShadowed": "A different hermeslink command appears earlier on PATH. Remove the old command or move npm's global bin directory earlier on PATH.",
|
|
384
|
+
"doctor.installWslWindowsNpm": "This looks like Windows Node/npm being used from WSL (`/mnt/c/...`). Prefer installing Linux Node.js inside WSL, or run both Hermes Agent and Hermes Link on the Windows host.",
|
|
385
|
+
"doctor.installDirectRun": "Direct run without changing PATH: {command}",
|
|
386
|
+
"doctor.installUnixPathHint": 'Temporary PATH fix: export PATH="{path}:$PATH"',
|
|
387
|
+
"doctor.installWindowsPathHint": "Add this directory to your user Path, then open a new terminal: {path}",
|
|
388
|
+
"doctor.installNpxFallback": "PATH-independent fallback: npx --yes @hermespilot/link doctor --install",
|
|
306
389
|
"doctor.identityOk": "Runtime identity: OK",
|
|
307
390
|
"doctor.installId": "Install ID: {value}",
|
|
308
391
|
"doctor.linkId": "Link ID: {value}",
|
|
@@ -401,6 +484,25 @@ var messages = {
|
|
|
401
484
|
"pair.autostartUnchanged": "\u68C0\u6D4B\u5230\u5DF2\u6709\u914D\u5BF9\u8BBE\u5907\uFF0C\u5F00\u673A\u81EA\u542F\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002",
|
|
402
485
|
"pair.autostartFailed": "\u914D\u5BF9\u5DF2\u6210\u529F\uFF0C\u4F46\u542F\u7528\u5F00\u673A\u81EA\u542F\u5931\u8D25\uFF1A{message}",
|
|
403
486
|
"doctor.description": "\u8FD0\u884C\u672C\u673A\u8BCA\u65AD",
|
|
487
|
+
"doctor.installOnly": "\u53EA\u68C0\u67E5 npm \u5168\u5C40\u547D\u4EE4\u548C PATH \u8BBE\u7F6E",
|
|
488
|
+
"doctor.installHeader": "\u5B89\u88C5 / PATH \u8BCA\u65AD\uFF1A",
|
|
489
|
+
"doctor.installNpmPrefix": "npm \u5168\u5C40 prefix\uFF1A{value}",
|
|
490
|
+
"doctor.installGlobalBin": "npm \u5168\u5C40 bin \u76EE\u5F55\uFF1A{value}",
|
|
491
|
+
"doctor.installExpectedCommand": "\u9884\u671F\u7684 hermeslink \u547D\u4EE4\uFF1A{value}\uFF08{state}\uFF09",
|
|
492
|
+
"doctor.installCommandOnPath": "\u5F53\u524D PATH \u627E\u5230\u7684 hermeslink\uFF1A{value}",
|
|
493
|
+
"doctor.installUnknown": "\u672A\u77E5",
|
|
494
|
+
"doctor.installOk": "\u5B58\u5728",
|
|
495
|
+
"doctor.installMissing": "\u672A\u627E\u5230",
|
|
496
|
+
"doctor.installReady": "\u5F53\u524D shell \u53EF\u4EE5\u76F4\u63A5\u627E\u5230\u5168\u5C40 hermeslink \u547D\u4EE4\u3002",
|
|
497
|
+
"doctor.installPrefixUnavailable": "\u65E0\u6CD5\u901A\u8FC7 `{command} prefix -g` \u8BFB\u53D6 npm \u5168\u5C40 prefix\u3002\u8BF7\u5148\u786E\u8BA4\u5F53\u524D shell \u80FD\u8FD0\u884C npm\u3002",
|
|
498
|
+
"doctor.installExpectedMissing": "npm \u4F3C\u4E4E\u6CA1\u6709\u521B\u5EFA\u5168\u5C40 hermeslink shim\u3002\u8BF7\u5728 Hermes \u6240\u5728\u7684\u540C\u4E00\u7CFB\u7EDF\u73AF\u5883\u91CC\u91CD\u65B0\u6267\u884C `npm install -g @hermespilot/link`\u3002",
|
|
499
|
+
"doctor.installPathMissing": "npm \u5DF2\u5B89\u88C5 hermeslink\uFF0C\u4F46\u5F53\u524D shell \u7684 PATH \u91CC\u6CA1\u6709 npm \u5168\u5C40 bin \u76EE\u5F55\u3002",
|
|
500
|
+
"doctor.installShadowed": "PATH \u524D\u9762\u5B58\u5728\u53E6\u4E00\u4E2A hermeslink \u547D\u4EE4\u3002\u8BF7\u5220\u9664\u65E7\u547D\u4EE4\uFF0C\u6216\u628A npm \u5168\u5C40 bin \u76EE\u5F55\u653E\u5230 PATH \u66F4\u524D\u9762\u3002",
|
|
501
|
+
"doctor.installWslWindowsNpm": "\u5F53\u524D\u770B\u8D77\u6765\u662F\u5728 WSL \u4E2D\u8C03\u7528\u4E86 Windows Node/npm\uFF08`/mnt/c/...`\uFF09\u3002\u5EFA\u8BAE\u5728 WSL \u5185\u5B89\u88C5 Linux \u7248 Node.js\uFF0C\u6216\u628A Hermes Agent \u4E0E Hermes Link \u90FD\u653E\u5728 Windows \u5BBF\u4E3B\u673A\u8FD0\u884C\u3002",
|
|
502
|
+
"doctor.installDirectRun": "\u4E0D\u6539 PATH \u4E5F\u53EF\u4EE5\u76F4\u63A5\u8FD0\u884C\uFF1A{command}",
|
|
503
|
+
"doctor.installUnixPathHint": '\u4E34\u65F6\u8865 PATH\uFF1Aexport PATH="{path}:$PATH"',
|
|
504
|
+
"doctor.installWindowsPathHint": "\u628A\u8FD9\u4E2A\u76EE\u5F55\u52A0\u5165\u5F53\u524D\u7528\u6237\u7684 Path\uFF0C\u7136\u540E\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\uFF1A{path}",
|
|
505
|
+
"doctor.installNpxFallback": "\u4E0D\u4F9D\u8D56 PATH \u7684\u515C\u5E95\u547D\u4EE4\uFF1Anpx --yes @hermespilot/link doctor --install",
|
|
404
506
|
"doctor.identityOk": "\u8FD0\u884C\u8EAB\u4EFD\uFF1A\u6B63\u5E38",
|
|
405
507
|
"doctor.installId": "Install ID\uFF1A{value}",
|
|
406
508
|
"doctor.linkId": "Link ID\uFF1A{value}",
|
|
@@ -701,6 +803,135 @@ async function spawnDetached(command, args) {
|
|
|
701
803
|
});
|
|
702
804
|
}
|
|
703
805
|
|
|
806
|
+
// src/runtime/install-info.ts
|
|
807
|
+
import { execFileSync } from "child_process";
|
|
808
|
+
import { existsSync, realpathSync } from "fs";
|
|
809
|
+
import path3 from "path";
|
|
810
|
+
function readInstallPathInfo() {
|
|
811
|
+
return inspectInstallPath({
|
|
812
|
+
npmPrefix: resolveGlobalPrefix()
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
function inspectInstallPath(options = {}) {
|
|
816
|
+
const platform = options.platform ?? process.platform;
|
|
817
|
+
const env = options.env ?? process.env;
|
|
818
|
+
const exists = options.exists ?? existsSync;
|
|
819
|
+
const realpath = options.realpath ?? realpathSync;
|
|
820
|
+
const npmPrefix = options.npmPrefix === void 0 ? resolveGlobalPrefix(platform, env) : options.npmPrefix;
|
|
821
|
+
const globalBinDir = npmPrefix ? resolveGlobalBinDir(npmPrefix, platform) : null;
|
|
822
|
+
const expectedCommandPath = globalBinDir ? resolveGlobalCommandPath(globalBinDir, platform) : null;
|
|
823
|
+
const expectedCommandExists = expectedCommandPath ? exists(expectedCommandPath) : false;
|
|
824
|
+
const pathIncludesGlobalBin = globalBinDir ? pathIncludes(globalBinDir, env.PATH ?? "", platform) : false;
|
|
825
|
+
const commandOnPath = findCommandOnPath(LINK_COMMAND, env, platform, exists);
|
|
826
|
+
const commandOnPathMatchesExpected = commandOnPath && expectedCommandPath ? samePath(commandOnPath, expectedCommandPath, platform, realpath) : null;
|
|
827
|
+
return {
|
|
828
|
+
platform,
|
|
829
|
+
npmCommand: resolveNpmCommand(platform),
|
|
830
|
+
npmPrefix,
|
|
831
|
+
globalBinDir,
|
|
832
|
+
expectedCommandPath,
|
|
833
|
+
expectedCommandExists,
|
|
834
|
+
pathIncludesGlobalBin,
|
|
835
|
+
commandOnPath,
|
|
836
|
+
commandOnPathMatchesExpected,
|
|
837
|
+
wslWindowsNpmLikely: platform === "linux" && Boolean(npmPrefix?.startsWith("/mnt/c/"))
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function hasInstallPathIssue(info) {
|
|
841
|
+
return !info.npmPrefix || !info.globalBinDir || !info.expectedCommandExists || !info.pathIncludesGlobalBin || !info.commandOnPath || info.commandOnPathMatchesExpected === false || info.wslWindowsNpmLikely;
|
|
842
|
+
}
|
|
843
|
+
function resolveGlobalBinDir(prefix, platform) {
|
|
844
|
+
return platform === "win32" ? prefix : pathForPlatform(platform).join(prefix, "bin");
|
|
845
|
+
}
|
|
846
|
+
function resolveGlobalCommandPath(binDir, platform) {
|
|
847
|
+
return pathForPlatform(platform).join(
|
|
848
|
+
binDir,
|
|
849
|
+
platform === "win32" ? `${LINK_COMMAND}.cmd` : LINK_COMMAND
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
function formatShellCommand(commandPath, args, platform) {
|
|
853
|
+
return [quoteShellToken(commandPath, platform), ...args.map((arg) => quoteShellToken(arg, platform))].join(" ");
|
|
854
|
+
}
|
|
855
|
+
function resolveGlobalPrefix(platform = process.platform, env = process.env) {
|
|
856
|
+
const envPrefix = env.npm_config_prefix?.trim() ?? env.npm_config_global_prefix?.trim() ?? "";
|
|
857
|
+
if (envPrefix) {
|
|
858
|
+
return envPrefix;
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
const output = execFileSync(resolveNpmCommand(platform), ["prefix", "-g"], {
|
|
862
|
+
encoding: "utf8",
|
|
863
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
864
|
+
timeout: 2e3
|
|
865
|
+
});
|
|
866
|
+
return output.trim() || null;
|
|
867
|
+
} catch {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function resolveNpmCommand(platform) {
|
|
872
|
+
return platform === "win32" ? "npm.cmd" : "npm";
|
|
873
|
+
}
|
|
874
|
+
function pathIncludes(target, pathValue, platform) {
|
|
875
|
+
const normalizedTarget = normalizePath(target, platform);
|
|
876
|
+
return splitPath(pathValue, platform).some(
|
|
877
|
+
(entry) => normalizePath(entry, platform) === normalizedTarget
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
function findCommandOnPath(command, env, platform, exists) {
|
|
881
|
+
for (const entry of splitPath(env.PATH ?? "", platform)) {
|
|
882
|
+
for (const fileName of commandFileNames(command, env, platform)) {
|
|
883
|
+
const candidate = pathForPlatform(platform).join(entry, fileName);
|
|
884
|
+
if (exists(candidate)) {
|
|
885
|
+
return candidate;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
function commandFileNames(command, env, platform) {
|
|
892
|
+
if (platform !== "win32") {
|
|
893
|
+
return [command];
|
|
894
|
+
}
|
|
895
|
+
const extensions = (env.PATHEXT || ".COM;.EXE;.BAT;.CMD;.PS1").split(";").map((value) => value.trim()).filter(Boolean);
|
|
896
|
+
return [command, ...extensions.map((extension) => `${command}${extension.toLowerCase()}`)];
|
|
897
|
+
}
|
|
898
|
+
function samePath(left, right, platform, realpath) {
|
|
899
|
+
try {
|
|
900
|
+
return normalizeComparablePath(realpath(left), platform) === normalizeComparablePath(realpath(right), platform);
|
|
901
|
+
} catch {
|
|
902
|
+
return normalizeComparablePath(left, platform) === normalizeComparablePath(right, platform);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function splitPath(pathValue, platform) {
|
|
906
|
+
const delimiter = platform === "win32" ? ";" : ":";
|
|
907
|
+
return pathValue.split(delimiter).map((entry) => stripPathQuotes(entry.trim())).filter(Boolean);
|
|
908
|
+
}
|
|
909
|
+
function stripPathQuotes(value) {
|
|
910
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
911
|
+
return value.slice(1, -1);
|
|
912
|
+
}
|
|
913
|
+
return value;
|
|
914
|
+
}
|
|
915
|
+
function normalizePath(value, platform) {
|
|
916
|
+
return normalizeComparablePath(pathForPlatform(platform).resolve(value), platform);
|
|
917
|
+
}
|
|
918
|
+
function normalizeComparablePath(value, platform) {
|
|
919
|
+
const normalized = pathForPlatform(platform).normalize(value);
|
|
920
|
+
return platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
921
|
+
}
|
|
922
|
+
function pathForPlatform(platform) {
|
|
923
|
+
return platform === "win32" ? path3.win32 : path3.posix;
|
|
924
|
+
}
|
|
925
|
+
function quoteShellToken(value, platform) {
|
|
926
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/u.test(value)) {
|
|
927
|
+
return value;
|
|
928
|
+
}
|
|
929
|
+
if (platform === "win32") {
|
|
930
|
+
return `"${value.replaceAll('"', '\\"')}"`;
|
|
931
|
+
}
|
|
932
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
933
|
+
}
|
|
934
|
+
|
|
704
935
|
// src/cli/index.ts
|
|
705
936
|
var program = new Command();
|
|
706
937
|
var helpLanguage = detectSystemLanguage();
|
|
@@ -874,6 +1105,7 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
874
1105
|
const hadActiveDevices = await hasActiveDevices(paths);
|
|
875
1106
|
const probeBeforePair = await probeLocalLinkService({ port: config.port });
|
|
876
1107
|
const prepared = await preparePairing(paths);
|
|
1108
|
+
const streamBatchPolicy = await fetchRelayStreamBatchPolicy(config.serverBaseUrl);
|
|
877
1109
|
await clearPairingClaim(prepared.sessionId, paths);
|
|
878
1110
|
const probe = await probeLocalLinkService({ port: config.port, linkId: prepared.linkId });
|
|
879
1111
|
if (probe.reachable && !probe.reusable) {
|
|
@@ -892,7 +1124,8 @@ program.command("pair").description(helpText("pair.description")).action(async (
|
|
|
892
1124
|
pairingRelayBridge = connectRelayControl({
|
|
893
1125
|
relayBaseUrl: prepared.relayBaseUrl,
|
|
894
1126
|
linkId: prepared.linkId,
|
|
895
|
-
localPort: config.port
|
|
1127
|
+
localPort: config.port,
|
|
1128
|
+
initialStreamBatchPolicy: streamBatchPolicy
|
|
896
1129
|
});
|
|
897
1130
|
pairingRelayBridge.publishNetworkRoutes(prepared.routes);
|
|
898
1131
|
}
|
|
@@ -999,7 +1232,17 @@ program.command("logs").description(helpText("logs.description")).action(async (
|
|
|
999
1232
|
console.log(t("logs.servicePath", { path: getLinkLogFile(paths) }));
|
|
1000
1233
|
console.log(t("logs.daemonPath", { path: daemonLogFile(paths) }));
|
|
1001
1234
|
});
|
|
1002
|
-
program.command("doctor").description(helpText("doctor.description")).action(async () => {
|
|
1235
|
+
program.command("doctor").option("--install", helpText("doctor.installOnly")).description(helpText("doctor.description")).action(async (options) => {
|
|
1236
|
+
const installInfo = readInstallPathInfo();
|
|
1237
|
+
const installLanguage = await loadCliLanguage().catch(() => detectSystemLanguage());
|
|
1238
|
+
const installT = translate.bind(null, installLanguage);
|
|
1239
|
+
if (options.install) {
|
|
1240
|
+
printInstallDiagnostics(installInfo, installT, true);
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (hasInstallPathIssue(installInfo)) {
|
|
1244
|
+
printInstallDiagnostics(installInfo, installT, false);
|
|
1245
|
+
}
|
|
1003
1246
|
const [identity, config] = await Promise.all([ensureIdentity(), loadConfig()]);
|
|
1004
1247
|
const language = resolveLanguage(config.language);
|
|
1005
1248
|
const t = translate.bind(null, language);
|
|
@@ -1045,6 +1288,53 @@ async function loadCliLanguage() {
|
|
|
1045
1288
|
function formatHermesVersion(version) {
|
|
1046
1289
|
return version.version ?? version.raw;
|
|
1047
1290
|
}
|
|
1291
|
+
function printInstallDiagnostics(info, t, verbose) {
|
|
1292
|
+
if (!verbose && !hasInstallPathIssue(info)) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
console.log(t("doctor.installHeader"));
|
|
1296
|
+
console.log(t("doctor.installNpmPrefix", { value: info.npmPrefix ?? t("doctor.installUnknown") }));
|
|
1297
|
+
console.log(t("doctor.installGlobalBin", { value: info.globalBinDir ?? t("doctor.installUnknown") }));
|
|
1298
|
+
console.log(
|
|
1299
|
+
t("doctor.installExpectedCommand", {
|
|
1300
|
+
value: info.expectedCommandPath ?? t("doctor.installUnknown"),
|
|
1301
|
+
state: info.expectedCommandExists ? t("doctor.installOk") : t("doctor.installMissing")
|
|
1302
|
+
})
|
|
1303
|
+
);
|
|
1304
|
+
console.log(
|
|
1305
|
+
t("doctor.installCommandOnPath", {
|
|
1306
|
+
value: info.commandOnPath ?? t("doctor.installMissing")
|
|
1307
|
+
})
|
|
1308
|
+
);
|
|
1309
|
+
if (!info.npmPrefix) {
|
|
1310
|
+
console.log(t("doctor.installPrefixUnavailable", { command: info.npmCommand }));
|
|
1311
|
+
} else if (!info.expectedCommandExists) {
|
|
1312
|
+
console.log(t("doctor.installExpectedMissing"));
|
|
1313
|
+
} else if (!info.pathIncludesGlobalBin || !info.commandOnPath) {
|
|
1314
|
+
console.log(t("doctor.installPathMissing"));
|
|
1315
|
+
} else if (info.commandOnPathMatchesExpected === false) {
|
|
1316
|
+
console.log(t("doctor.installShadowed"));
|
|
1317
|
+
} else {
|
|
1318
|
+
console.log(t("doctor.installReady"));
|
|
1319
|
+
}
|
|
1320
|
+
if (info.wslWindowsNpmLikely) {
|
|
1321
|
+
console.log(t("doctor.installWslWindowsNpm"));
|
|
1322
|
+
}
|
|
1323
|
+
if (info.expectedCommandPath && info.expectedCommandExists) {
|
|
1324
|
+
console.log(
|
|
1325
|
+
t("doctor.installDirectRun", {
|
|
1326
|
+
command: formatShellCommand(info.expectedCommandPath, ["doctor"], info.platform)
|
|
1327
|
+
})
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
if (info.globalBinDir && info.platform !== "win32") {
|
|
1331
|
+
console.log(t("doctor.installUnixPathHint", { path: info.globalBinDir }));
|
|
1332
|
+
}
|
|
1333
|
+
if (info.globalBinDir && info.platform === "win32") {
|
|
1334
|
+
console.log(t("doctor.installWindowsPathHint", { path: info.globalBinDir }));
|
|
1335
|
+
}
|
|
1336
|
+
console.log(t("doctor.installNpxFallback"));
|
|
1337
|
+
}
|
|
1048
1338
|
function pairingPreflightProgressKey(stage) {
|
|
1049
1339
|
switch (stage) {
|
|
1050
1340
|
case "hermes_files":
|
|
@@ -1193,9 +1483,9 @@ function isCliEntrypoint(entry = process.argv[1], moduleUrl = import.meta.url) {
|
|
|
1193
1483
|
return false;
|
|
1194
1484
|
}
|
|
1195
1485
|
try {
|
|
1196
|
-
return moduleUrl === pathToFileURL(
|
|
1486
|
+
return moduleUrl === pathToFileURL(realpathSync2(path4.resolve(entry))).href;
|
|
1197
1487
|
} catch {
|
|
1198
|
-
return moduleUrl === pathToFileURL(
|
|
1488
|
+
return moduleUrl === pathToFileURL(path4.resolve(entry)).href;
|
|
1199
1489
|
}
|
|
1200
1490
|
}
|
|
1201
1491
|
export {
|
package/dist/http/app.js
CHANGED
package/package.json
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -99,9 +99,12 @@ async function main() {
|
|
|
99
99
|
console.log(
|
|
100
100
|
`如果当前 shell 找不到 \`hermeslink\`,说明 npm 全局 bin 目录没有进 PATH:${globalBinDir}`,
|
|
101
101
|
);
|
|
102
|
+
console.log("你也可以先运行不依赖 PATH 的诊断:npx --yes @hermespilot/link doctor --install");
|
|
102
103
|
console.log(`你可以直接运行:${commandPath} pair`);
|
|
103
104
|
if (process.platform !== "win32") {
|
|
104
105
|
console.log(`或者先补 PATH:export PATH="${globalBinDir}:$PATH"`);
|
|
106
|
+
} else {
|
|
107
|
+
console.log(`或者把这个目录加入当前用户的 Path,然后重新打开终端:${globalBinDir}`);
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
} else {
|
|
@@ -111,9 +114,12 @@ async function main() {
|
|
|
111
114
|
console.log(
|
|
112
115
|
`If your shell cannot find \`hermeslink\`, npm's global bin directory is not on PATH: ${globalBinDir}`,
|
|
113
116
|
);
|
|
117
|
+
console.log("You can run a PATH-independent diagnostic first: npx --yes @hermespilot/link doctor --install");
|
|
114
118
|
console.log(`You can run it directly: ${commandPath} pair`);
|
|
115
119
|
if (process.platform !== "win32") {
|
|
116
120
|
console.log(`Or add it to PATH first: export PATH="${globalBinDir}:$PATH"`);
|
|
121
|
+
} else {
|
|
122
|
+
console.log(`Or add this directory to your user Path, then open a new terminal: ${globalBinDir}`);
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
}
|