@hermespilot/link 0.4.1 → 0.4.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 +2 -0
- package/dist/{chunk-476X63MC.js → chunk-MFRSQUSE.js} +285 -80
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.js +56 -17
- package/dist/http/app.d.ts +2 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ If Hermes Agent is configured through `~/.hermes/.env`, Link follows the same `A
|
|
|
44
44
|
|
|
45
45
|
CLI output follows the current system language when it is Chinese or English. You can override it for a single command with `HERMESLINK_LANG=zh-CN` or `HERMESLINK_LANG=en`.
|
|
46
46
|
|
|
47
|
+
Set `HERMESLINK_LOG_LEVEL=warn` to suppress `debug` and `info` logs in published builds; `warn` is the default. You can also persist it with `hermeslink config set log-level warn`.
|
|
48
|
+
|
|
47
49
|
## Runtime data
|
|
48
50
|
|
|
49
51
|
Hermes Link keeps its local identity and runtime state under:
|
|
@@ -1102,6 +1102,20 @@ function readProfileAvatarType(row) {
|
|
|
1102
1102
|
import { mkdir as mkdir3, readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
1103
1103
|
import path4 from "path";
|
|
1104
1104
|
|
|
1105
|
+
// src/core/errors.ts
|
|
1106
|
+
var LinkHttpError = class extends Error {
|
|
1107
|
+
constructor(status, code, message) {
|
|
1108
|
+
super(message);
|
|
1109
|
+
this.status = status;
|
|
1110
|
+
this.code = code;
|
|
1111
|
+
}
|
|
1112
|
+
status;
|
|
1113
|
+
code;
|
|
1114
|
+
};
|
|
1115
|
+
function isLinkHttpError(error) {
|
|
1116
|
+
return error instanceof LinkHttpError;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1105
1119
|
// src/storage/atomic-json.ts
|
|
1106
1120
|
import { readFile } from "fs/promises";
|
|
1107
1121
|
|
|
@@ -3824,6 +3838,7 @@ async function decorateHermesLinkCronJob(paths, profileName, job) {
|
|
|
3824
3838
|
async function syncHermesLinkCronDeliveries(paths, runtime, logger) {
|
|
3825
3839
|
const registry = await readRegistry(paths);
|
|
3826
3840
|
let touched = false;
|
|
3841
|
+
const staleBindings = [];
|
|
3827
3842
|
for (const binding of registry.bindings) {
|
|
3828
3843
|
const delivered = new Set(binding.deliveredOutputPaths ?? []);
|
|
3829
3844
|
const outputs = await listCronOutputFiles(
|
|
@@ -3848,6 +3863,13 @@ async function syncHermesLinkCronDeliveries(paths, runtime, logger) {
|
|
|
3848
3863
|
delivered.add(output.path);
|
|
3849
3864
|
touched = true;
|
|
3850
3865
|
} catch (error) {
|
|
3866
|
+
if (isConversationMissingError(error)) {
|
|
3867
|
+
staleBindings.push({
|
|
3868
|
+
profileName: binding.profileName,
|
|
3869
|
+
jobId: binding.jobId
|
|
3870
|
+
});
|
|
3871
|
+
break;
|
|
3872
|
+
}
|
|
3851
3873
|
void logger.warn("cron_link_delivery_failed", {
|
|
3852
3874
|
profile: binding.profileName,
|
|
3853
3875
|
job_id: binding.jobId,
|
|
@@ -3858,6 +3880,14 @@ async function syncHermesLinkCronDeliveries(paths, runtime, logger) {
|
|
|
3858
3880
|
}
|
|
3859
3881
|
binding.deliveredOutputPaths = [...delivered];
|
|
3860
3882
|
}
|
|
3883
|
+
if (staleBindings.length > 0) {
|
|
3884
|
+
registry.bindings = registry.bindings.filter(
|
|
3885
|
+
(binding) => !staleBindings.some(
|
|
3886
|
+
(staleBinding) => staleBinding.profileName === binding.profileName && staleBinding.jobId === binding.jobId
|
|
3887
|
+
)
|
|
3888
|
+
);
|
|
3889
|
+
touched = true;
|
|
3890
|
+
}
|
|
3861
3891
|
if (touched) {
|
|
3862
3892
|
await writeRegistry(paths, registry);
|
|
3863
3893
|
}
|
|
@@ -3945,6 +3975,9 @@ function readString3(record, ...keys) {
|
|
|
3945
3975
|
function isNodeError4(error, code) {
|
|
3946
3976
|
return error instanceof Error && error.code === code;
|
|
3947
3977
|
}
|
|
3978
|
+
function isConversationMissingError(error) {
|
|
3979
|
+
return isLinkHttpError(error) && error.status === 404 && error.code === "conversation_not_found";
|
|
3980
|
+
}
|
|
3948
3981
|
|
|
3949
3982
|
// src/hermes/gateway.ts
|
|
3950
3983
|
import { execFile as execFile2, spawn } from "child_process";
|
|
@@ -3953,20 +3986,6 @@ import path7 from "path";
|
|
|
3953
3986
|
import { setTimeout as delay } from "timers/promises";
|
|
3954
3987
|
import { promisify as promisify2 } from "util";
|
|
3955
3988
|
|
|
3956
|
-
// src/core/errors.ts
|
|
3957
|
-
var LinkHttpError = class extends Error {
|
|
3958
|
-
constructor(status, code, message) {
|
|
3959
|
-
super(message);
|
|
3960
|
-
this.status = status;
|
|
3961
|
-
this.code = code;
|
|
3962
|
-
}
|
|
3963
|
-
status;
|
|
3964
|
-
code;
|
|
3965
|
-
};
|
|
3966
|
-
function isLinkHttpError(error) {
|
|
3967
|
-
return error instanceof LinkHttpError;
|
|
3968
|
-
}
|
|
3969
|
-
|
|
3970
3989
|
// src/runtime/logger.ts
|
|
3971
3990
|
import { appendFile, mkdir as mkdir4, open as open2, readFile as readFile4, rename as rename2, rm as rm2, stat as stat3 } from "fs/promises";
|
|
3972
3991
|
import os3 from "os";
|
|
@@ -3977,7 +3996,7 @@ import os2 from "os";
|
|
|
3977
3996
|
import path5 from "path";
|
|
3978
3997
|
|
|
3979
3998
|
// src/constants.ts
|
|
3980
|
-
var LINK_VERSION = "0.4.
|
|
3999
|
+
var LINK_VERSION = "0.4.3";
|
|
3981
4000
|
var LINK_COMMAND = "hermeslink";
|
|
3982
4001
|
var LINK_DEFAULT_PORT = 52379;
|
|
3983
4002
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -4012,11 +4031,18 @@ var MAX_READ_LIMIT = 1e3;
|
|
|
4012
4031
|
var DEFAULT_MAX_BYTES_PER_FILE = 512 * 1024;
|
|
4013
4032
|
var GATEWAY_LOG_FILE = "hermes-gateway.log";
|
|
4014
4033
|
var DAEMON_LOG_FILE = "daemon.log";
|
|
4034
|
+
var LOG_LEVEL_PRIORITY = {
|
|
4035
|
+
debug: 10,
|
|
4036
|
+
info: 20,
|
|
4037
|
+
warn: 30,
|
|
4038
|
+
error: 40
|
|
4039
|
+
};
|
|
4015
4040
|
var FileLogger = class {
|
|
4016
4041
|
filePath;
|
|
4017
4042
|
paths;
|
|
4018
4043
|
maxFileBytes;
|
|
4019
4044
|
maxFiles;
|
|
4045
|
+
minLevel;
|
|
4020
4046
|
now;
|
|
4021
4047
|
queue = Promise.resolve();
|
|
4022
4048
|
constructor(options = {}) {
|
|
@@ -4024,6 +4050,7 @@ var FileLogger = class {
|
|
|
4024
4050
|
this.filePath = getLinkLogFile(this.paths, options.fileName);
|
|
4025
4051
|
this.maxFileBytes = Math.max(256, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
4026
4052
|
this.maxFiles = Math.max(0, Math.floor(options.maxFiles ?? DEFAULT_MAX_FILES));
|
|
4053
|
+
this.minLevel = options.minLevel ?? "warn";
|
|
4027
4054
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
4028
4055
|
}
|
|
4029
4056
|
debug(message, fields) {
|
|
@@ -4039,6 +4066,9 @@ var FileLogger = class {
|
|
|
4039
4066
|
return this.write("error", message, fields);
|
|
4040
4067
|
}
|
|
4041
4068
|
write(level, message, fields) {
|
|
4069
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.minLevel]) {
|
|
4070
|
+
return Promise.resolve();
|
|
4071
|
+
}
|
|
4042
4072
|
const entry = {
|
|
4043
4073
|
ts: this.now().toISOString(),
|
|
4044
4074
|
level,
|
|
@@ -4464,8 +4494,11 @@ var MIN_API_SERVER_VERSION = "0.4.0";
|
|
|
4464
4494
|
var PROFILE_NAME_PATTERN = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
4465
4495
|
var DASHBOARD_STATUS_URL = "http://127.0.0.1:9119/api/status";
|
|
4466
4496
|
var DASHBOARD_STATUS_TIMEOUT_MS = 1500;
|
|
4497
|
+
var DEFAULT_VERSION_CACHE_TTL_MS = 6e4;
|
|
4467
4498
|
var MAX_VERSION_LOG_OUTPUT_LENGTH = 1200;
|
|
4468
4499
|
var gatewayStartInFlightByProfile = /* @__PURE__ */ new Map();
|
|
4500
|
+
var hermesVersionCache = /* @__PURE__ */ new Map();
|
|
4501
|
+
var hermesVersionInFlight = /* @__PURE__ */ new Map();
|
|
4469
4502
|
async function ensureHermesApiServerAvailable(options = {}) {
|
|
4470
4503
|
const profileName = normalizeProfileName(options.profileName);
|
|
4471
4504
|
await assertProfileExists(profileName);
|
|
@@ -4582,41 +4615,38 @@ async function reloadHermesGateway(options = {}) {
|
|
|
4582
4615
|
return ensureHermesApiServerAvailable({ ...options, forceRestart: true });
|
|
4583
4616
|
}
|
|
4584
4617
|
async function readHermesVersion(options = {}) {
|
|
4618
|
+
const hermesBin = resolveHermesBin();
|
|
4619
|
+
const cacheTtlMs = Math.max(
|
|
4620
|
+
0,
|
|
4621
|
+
Math.floor(options.cacheTtlMs ?? DEFAULT_VERSION_CACHE_TTL_MS)
|
|
4622
|
+
);
|
|
4623
|
+
if (!options.forceRefresh && cacheTtlMs > 0) {
|
|
4624
|
+
const cached = hermesVersionCache.get(hermesBin);
|
|
4625
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
4626
|
+
return cached.value;
|
|
4627
|
+
}
|
|
4628
|
+
hermesVersionCache.delete(hermesBin);
|
|
4629
|
+
}
|
|
4630
|
+
const inFlight = hermesVersionInFlight.get(hermesBin);
|
|
4631
|
+
if (inFlight) {
|
|
4632
|
+
return await inFlight;
|
|
4633
|
+
}
|
|
4634
|
+
const probe = probeHermesVersion(hermesBin, options);
|
|
4635
|
+
hermesVersionInFlight.set(hermesBin, probe);
|
|
4585
4636
|
try {
|
|
4586
|
-
const
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
const dashboardStatusUrl = options.dashboardStatusUrl ?? DASHBOARD_STATUS_URL;
|
|
4592
|
-
void options.logger?.warn("hermes_version_dashboard_fallback_requested", {
|
|
4593
|
-
dashboard_status_url: dashboardStatusUrl,
|
|
4594
|
-
reason: cliError instanceof Error ? cliError.message : String(cliError)
|
|
4595
|
-
});
|
|
4596
|
-
try {
|
|
4597
|
-
const fallback = await readHermesDashboardVersion({
|
|
4598
|
-
fetchImpl: options.fetchImpl,
|
|
4599
|
-
statusUrl: dashboardStatusUrl,
|
|
4600
|
-
timeoutMs: options.dashboardTimeoutMs
|
|
4601
|
-
});
|
|
4602
|
-
void options.logger?.info("hermes_version_dashboard_fallback_succeeded", {
|
|
4603
|
-
dashboard_status_url: dashboardStatusUrl,
|
|
4604
|
-
hermes_version: fallback.version
|
|
4605
|
-
});
|
|
4606
|
-
return fallback;
|
|
4607
|
-
} catch (dashboardError) {
|
|
4608
|
-
void options.logger?.warn("hermes_version_dashboard_fallback_failed", {
|
|
4609
|
-
dashboard_status_url: dashboardStatusUrl,
|
|
4610
|
-
error: dashboardError instanceof Error ? dashboardError.message : String(dashboardError)
|
|
4637
|
+
const version = await probe;
|
|
4638
|
+
if (cacheTtlMs > 0) {
|
|
4639
|
+
hermesVersionCache.set(hermesBin, {
|
|
4640
|
+
value: version,
|
|
4641
|
+
expiresAt: Date.now() + cacheTtlMs
|
|
4611
4642
|
});
|
|
4612
|
-
throw new Error(
|
|
4613
|
-
`Hermes version detection failed. CLI: ${cliError instanceof Error ? cliError.message : String(cliError)}; dashboard fallback: ${dashboardError instanceof Error ? dashboardError.message : String(dashboardError)}`
|
|
4614
|
-
);
|
|
4615
4643
|
}
|
|
4644
|
+
return version;
|
|
4645
|
+
} finally {
|
|
4646
|
+
hermesVersionInFlight.delete(hermesBin);
|
|
4616
4647
|
}
|
|
4617
4648
|
}
|
|
4618
|
-
async function execHermesVersion(logger) {
|
|
4619
|
-
const hermesBin = resolveHermesBin();
|
|
4649
|
+
async function execHermesVersion(hermesBin, logger) {
|
|
4620
4650
|
const failures = [];
|
|
4621
4651
|
for (const args of [["version"], ["--version"]]) {
|
|
4622
4652
|
try {
|
|
@@ -4626,11 +4656,18 @@ async function execHermesVersion(logger) {
|
|
|
4626
4656
|
});
|
|
4627
4657
|
} catch (error) {
|
|
4628
4658
|
const failure = describeVersionCommandFailure(hermesBin, args, error);
|
|
4629
|
-
failures.push(failure
|
|
4630
|
-
void logger?.
|
|
4659
|
+
failures.push(failure);
|
|
4660
|
+
void logger?.debug("hermes_version_cli_command_attempt_failed", failure.fields);
|
|
4631
4661
|
}
|
|
4632
4662
|
}
|
|
4633
|
-
|
|
4663
|
+
const summary = failures.map((failure) => failure.summary).join("; ");
|
|
4664
|
+
void logger?.warn("hermes_version_cli_command_failed", {
|
|
4665
|
+
hermes_bin: hermesBin,
|
|
4666
|
+
failure_count: failures.length,
|
|
4667
|
+
error: summary,
|
|
4668
|
+
attempts: failures.map((failure) => failure.fields)
|
|
4669
|
+
});
|
|
4670
|
+
throw new Error(summary);
|
|
4634
4671
|
}
|
|
4635
4672
|
function assertHermesRunsApiSupported(version, status) {
|
|
4636
4673
|
if (status !== 404) {
|
|
@@ -5051,27 +5088,85 @@ async function readHermesDashboardVersion(options = {}) {
|
|
|
5051
5088
|
clearTimeout(timer);
|
|
5052
5089
|
}
|
|
5053
5090
|
}
|
|
5091
|
+
async function probeHermesVersion(hermesBin, options) {
|
|
5092
|
+
try {
|
|
5093
|
+
const { stdout } = await execHermesVersion(hermesBin, options.logger);
|
|
5094
|
+
const raw = stdout.trim();
|
|
5095
|
+
const version = parseHermesVersion(raw);
|
|
5096
|
+
return buildHermesVersionInfo(raw, version);
|
|
5097
|
+
} catch (cliError) {
|
|
5098
|
+
const dashboardStatusUrl = options.dashboardStatusUrl ?? DASHBOARD_STATUS_URL;
|
|
5099
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_requested", {
|
|
5100
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
5101
|
+
reason: cliError instanceof Error ? cliError.message : String(cliError)
|
|
5102
|
+
});
|
|
5103
|
+
try {
|
|
5104
|
+
const fallback = await readHermesDashboardVersion({
|
|
5105
|
+
fetchImpl: options.fetchImpl,
|
|
5106
|
+
statusUrl: dashboardStatusUrl,
|
|
5107
|
+
timeoutMs: options.dashboardTimeoutMs
|
|
5108
|
+
});
|
|
5109
|
+
void options.logger?.info("hermes_version_dashboard_fallback_succeeded", {
|
|
5110
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
5111
|
+
hermes_version: fallback.version
|
|
5112
|
+
});
|
|
5113
|
+
return fallback;
|
|
5114
|
+
} catch (dashboardError) {
|
|
5115
|
+
void options.logger?.warn("hermes_version_dashboard_fallback_failed", {
|
|
5116
|
+
dashboard_status_url: dashboardStatusUrl,
|
|
5117
|
+
error: dashboardError instanceof Error ? dashboardError.message : String(dashboardError)
|
|
5118
|
+
});
|
|
5119
|
+
throw new Error(
|
|
5120
|
+
`Hermes version detection failed. CLI: ${cliError instanceof Error ? cliError.message : String(cliError)}; dashboard fallback: ${dashboardError instanceof Error ? dashboardError.message : String(dashboardError)}`
|
|
5121
|
+
);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5054
5125
|
function describeVersionCommandFailure(hermesBin, args, error) {
|
|
5055
5126
|
const message = error instanceof Error ? error.message : String(error);
|
|
5056
|
-
const
|
|
5127
|
+
const details = readExecErrorDetails(error, message);
|
|
5057
5128
|
return {
|
|
5058
5129
|
summary: `${hermesBin} ${args.join(" ")} failed: ${message}`,
|
|
5059
5130
|
fields: {
|
|
5060
5131
|
hermes_bin: hermesBin,
|
|
5061
5132
|
command: args.join(" "),
|
|
5133
|
+
cwd: process.cwd(),
|
|
5062
5134
|
error: message,
|
|
5063
|
-
...
|
|
5135
|
+
...details
|
|
5064
5136
|
}
|
|
5065
5137
|
};
|
|
5066
5138
|
}
|
|
5067
|
-
function
|
|
5139
|
+
function readExecErrorDetails(error, message) {
|
|
5068
5140
|
if (typeof error !== "object" || error === null) {
|
|
5069
|
-
return "";
|
|
5141
|
+
return message.includes("timed out") ? { timed_out: true } : {};
|
|
5070
5142
|
}
|
|
5143
|
+
const details = {};
|
|
5071
5144
|
const stdout = "stdout" in error && error.stdout != null ? String(error.stdout) : "";
|
|
5072
5145
|
const stderr = "stderr" in error && error.stderr != null ? String(error.stderr) : "";
|
|
5073
|
-
|
|
5074
|
-
|
|
5146
|
+
if ("code" in error) {
|
|
5147
|
+
const code = error.code;
|
|
5148
|
+
if (typeof code === "number") {
|
|
5149
|
+
details.exit_code = code;
|
|
5150
|
+
} else if (typeof code === "string" && code.trim()) {
|
|
5151
|
+
details.error_code = code;
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
if ("signal" in error && typeof error.signal === "string") {
|
|
5155
|
+
details.signal = error.signal;
|
|
5156
|
+
}
|
|
5157
|
+
if ("killed" in error && typeof error.killed === "boolean") {
|
|
5158
|
+
details.killed = error.killed;
|
|
5159
|
+
}
|
|
5160
|
+
if (message.includes("timed out")) {
|
|
5161
|
+
details.timed_out = true;
|
|
5162
|
+
}
|
|
5163
|
+
if (stdout.trim()) {
|
|
5164
|
+
details.stdout = truncateVersionLogOutput(stdout.trim());
|
|
5165
|
+
}
|
|
5166
|
+
if (stderr.trim()) {
|
|
5167
|
+
details.stderr = truncateVersionLogOutput(stderr.trim());
|
|
5168
|
+
}
|
|
5169
|
+
return details;
|
|
5075
5170
|
}
|
|
5076
5171
|
function truncateVersionLogOutput(value) {
|
|
5077
5172
|
return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
|
|
@@ -7244,22 +7339,31 @@ var defaultLinkConfig = {
|
|
|
7244
7339
|
relayBaseUrl: "https://hermes-relay.clawpilot.me",
|
|
7245
7340
|
appConnectTokenIssuer: "https://hermes-server.clawpilot.me",
|
|
7246
7341
|
appConnectTokenAudience: "hermes-link",
|
|
7247
|
-
language: "auto"
|
|
7342
|
+
language: "auto",
|
|
7343
|
+
logLevel: "warn"
|
|
7248
7344
|
};
|
|
7249
7345
|
async function loadConfig(paths = resolveRuntimePaths()) {
|
|
7250
7346
|
const existing = await readJsonFile(paths.configFile);
|
|
7251
7347
|
const language = normalizeConfiguredLanguage(existing?.language);
|
|
7252
7348
|
const lanHost = normalizeLanHost(existing?.lanHost);
|
|
7349
|
+
const logLevel = normalizeLogLevel(
|
|
7350
|
+
existing?.logLevel ?? process.env.HERMESLINK_LOG_LEVEL
|
|
7351
|
+
);
|
|
7253
7352
|
return {
|
|
7254
7353
|
...defaultLinkConfig,
|
|
7255
7354
|
...existing ?? {},
|
|
7256
7355
|
language,
|
|
7257
|
-
lanHost
|
|
7356
|
+
lanHost,
|
|
7357
|
+
logLevel
|
|
7258
7358
|
};
|
|
7259
7359
|
}
|
|
7260
7360
|
async function saveConfig(patch, paths = resolveRuntimePaths()) {
|
|
7261
7361
|
const current = await loadConfig(paths);
|
|
7262
|
-
const next = {
|
|
7362
|
+
const next = {
|
|
7363
|
+
...current,
|
|
7364
|
+
...patch,
|
|
7365
|
+
logLevel: patch.logLevel === void 0 ? current.logLevel : normalizeLogLevel(patch.logLevel)
|
|
7366
|
+
};
|
|
7263
7367
|
await writeJsonFile(paths.configFile, next);
|
|
7264
7368
|
return next;
|
|
7265
7369
|
}
|
|
@@ -7269,6 +7373,18 @@ function normalizeConfiguredLanguage(language) {
|
|
|
7269
7373
|
}
|
|
7270
7374
|
return defaultLinkConfig.language;
|
|
7271
7375
|
}
|
|
7376
|
+
function normalizeLogLevel(level) {
|
|
7377
|
+
if (level === "debug" || level === "info" || level === "warn" || level === "error") {
|
|
7378
|
+
return level;
|
|
7379
|
+
}
|
|
7380
|
+
return defaultLinkConfig.logLevel;
|
|
7381
|
+
}
|
|
7382
|
+
function parseLogLevel(value) {
|
|
7383
|
+
if (value === "debug" || value === "info" || value === "warn" || value === "error") {
|
|
7384
|
+
return value;
|
|
7385
|
+
}
|
|
7386
|
+
return null;
|
|
7387
|
+
}
|
|
7272
7388
|
function normalizeLanHost(value) {
|
|
7273
7389
|
if (value === null || value === void 0) {
|
|
7274
7390
|
return null;
|
|
@@ -8037,13 +8153,23 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
8037
8153
|
return this.appendCommandResultLocked(input);
|
|
8038
8154
|
}
|
|
8039
8155
|
startRunWorkerAndDrain(conversationId, runId, input) {
|
|
8040
|
-
void this.deps.runLifecycle.startRunWorker(conversationId, runId, input).catch(
|
|
8041
|
-
(error)
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8156
|
+
void this.deps.runLifecycle.startRunWorker(conversationId, runId, input).catch(async (error) => {
|
|
8157
|
+
if (isConversationNotFoundError(error)) {
|
|
8158
|
+
return;
|
|
8159
|
+
}
|
|
8160
|
+
try {
|
|
8161
|
+
await this.deps.runLifecycle.failRun(
|
|
8162
|
+
conversationId,
|
|
8163
|
+
runId,
|
|
8164
|
+
error instanceof Error ? error.message : String(error)
|
|
8165
|
+
);
|
|
8166
|
+
} catch (failError) {
|
|
8167
|
+
if (isConversationNotFoundError(failError)) {
|
|
8168
|
+
return;
|
|
8169
|
+
}
|
|
8170
|
+
throw failError;
|
|
8171
|
+
}
|
|
8172
|
+
}).finally(() => {
|
|
8047
8173
|
void this.startNextQueuedRun(conversationId);
|
|
8048
8174
|
});
|
|
8049
8175
|
}
|
|
@@ -8575,6 +8701,9 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
8575
8701
|
${attachmentLines.join("\n")}`;
|
|
8576
8702
|
}
|
|
8577
8703
|
};
|
|
8704
|
+
function isConversationNotFoundError(error) {
|
|
8705
|
+
return error instanceof LinkHttpError && error.code === "conversation_not_found";
|
|
8706
|
+
}
|
|
8578
8707
|
|
|
8579
8708
|
// src/conversations/agent-events.ts
|
|
8580
8709
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -11483,16 +11612,32 @@ async function callHermesApi(path26, init, options) {
|
|
|
11483
11612
|
try {
|
|
11484
11613
|
response = await request();
|
|
11485
11614
|
} catch (error) {
|
|
11486
|
-
logHermesApiError(
|
|
11615
|
+
logHermesApiError(
|
|
11616
|
+
options.logger,
|
|
11617
|
+
method,
|
|
11618
|
+
path26,
|
|
11619
|
+
options.profileName,
|
|
11620
|
+
startedAt,
|
|
11621
|
+
error
|
|
11622
|
+
);
|
|
11487
11623
|
throw error;
|
|
11488
11624
|
}
|
|
11489
11625
|
if (response.status !== 401) {
|
|
11490
|
-
logHermesApiResponse(
|
|
11626
|
+
logHermesApiResponse(
|
|
11627
|
+
options.logger,
|
|
11628
|
+
method,
|
|
11629
|
+
path26,
|
|
11630
|
+
options.profileName,
|
|
11631
|
+
startedAt,
|
|
11632
|
+
response
|
|
11633
|
+
);
|
|
11491
11634
|
return response;
|
|
11492
11635
|
}
|
|
11493
11636
|
void options.logger?.warn("hermes_api_request_retrying_after_401", {
|
|
11494
11637
|
method,
|
|
11495
11638
|
path: path26,
|
|
11639
|
+
profile: options.profileName ?? "default",
|
|
11640
|
+
port: config.port ?? null,
|
|
11496
11641
|
duration_ms: Date.now() - startedAt
|
|
11497
11642
|
});
|
|
11498
11643
|
const refreshedAvailability = await ensureHermesApiServerAvailable({
|
|
@@ -11505,10 +11650,24 @@ async function callHermesApi(path26, init, options) {
|
|
|
11505
11650
|
try {
|
|
11506
11651
|
response = await request();
|
|
11507
11652
|
} catch (error) {
|
|
11508
|
-
logHermesApiError(
|
|
11653
|
+
logHermesApiError(
|
|
11654
|
+
options.logger,
|
|
11655
|
+
method,
|
|
11656
|
+
path26,
|
|
11657
|
+
options.profileName,
|
|
11658
|
+
startedAt,
|
|
11659
|
+
error
|
|
11660
|
+
);
|
|
11509
11661
|
throw error;
|
|
11510
11662
|
}
|
|
11511
|
-
logHermesApiResponse(
|
|
11663
|
+
logHermesApiResponse(
|
|
11664
|
+
options.logger,
|
|
11665
|
+
method,
|
|
11666
|
+
path26,
|
|
11667
|
+
options.profileName,
|
|
11668
|
+
startedAt,
|
|
11669
|
+
response
|
|
11670
|
+
);
|
|
11512
11671
|
return response;
|
|
11513
11672
|
}
|
|
11514
11673
|
async function fetchHermesApi(fetcher, config, path26, init, options) {
|
|
@@ -11526,8 +11685,11 @@ async function fetchHermesApi(fetcher, config, path26, init, options) {
|
|
|
11526
11685
|
throw error;
|
|
11527
11686
|
}
|
|
11528
11687
|
void options.logger?.warn("hermes_api_server_connect_failed", {
|
|
11688
|
+
method: String(init.method ?? "GET").toUpperCase(),
|
|
11529
11689
|
path: path26,
|
|
11690
|
+
profile: options.profileName ?? "default",
|
|
11530
11691
|
port: config.port ?? null,
|
|
11692
|
+
url: `http://127.0.0.1:${config.port}${path26}`,
|
|
11531
11693
|
error: error instanceof Error ? error.message : String(error)
|
|
11532
11694
|
});
|
|
11533
11695
|
throw new LinkHttpError(
|
|
@@ -11537,10 +11699,11 @@ async function fetchHermesApi(fetcher, config, path26, init, options) {
|
|
|
11537
11699
|
);
|
|
11538
11700
|
});
|
|
11539
11701
|
}
|
|
11540
|
-
function logHermesApiResponse(logger, method, path26, startedAt, response) {
|
|
11702
|
+
function logHermesApiResponse(logger, method, path26, profileName, startedAt, response) {
|
|
11541
11703
|
const fields = {
|
|
11542
11704
|
method,
|
|
11543
11705
|
path: path26,
|
|
11706
|
+
profile: profileName ?? "default",
|
|
11544
11707
|
status: response.status,
|
|
11545
11708
|
duration_ms: Date.now() - startedAt
|
|
11546
11709
|
};
|
|
@@ -11560,10 +11723,11 @@ async function logHermesApiFailureResponse(logger, fields, response) {
|
|
|
11560
11723
|
...upstreamError ? { upstream_error: upstreamError } : {}
|
|
11561
11724
|
});
|
|
11562
11725
|
}
|
|
11563
|
-
function logHermesApiError(logger, method, path26, startedAt, error) {
|
|
11726
|
+
function logHermesApiError(logger, method, path26, profileName, startedAt, error) {
|
|
11564
11727
|
void logger?.warn("hermes_api_request_failed", {
|
|
11565
11728
|
method,
|
|
11566
11729
|
path: path26,
|
|
11730
|
+
profile: profileName ?? "default",
|
|
11567
11731
|
duration_ms: Date.now() - startedAt,
|
|
11568
11732
|
...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
|
|
11569
11733
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -15748,6 +15912,7 @@ function createHttpErrorMiddleware(logger) {
|
|
|
15748
15912
|
{
|
|
15749
15913
|
method: ctx.method,
|
|
15750
15914
|
path: ctx.path,
|
|
15915
|
+
query: ctx.querystring || null,
|
|
15751
15916
|
status,
|
|
15752
15917
|
code,
|
|
15753
15918
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -20254,6 +20419,9 @@ function subscribeHermesUpdateStatus(listener) {
|
|
|
20254
20419
|
return () => updateEvents.off("status", listener);
|
|
20255
20420
|
}
|
|
20256
20421
|
async function readRemoteRelease(options, now) {
|
|
20422
|
+
const context = await readHermesReleaseCheckContext(options.paths).catch(
|
|
20423
|
+
() => null
|
|
20424
|
+
);
|
|
20257
20425
|
const cached = await readReleaseCache(options.paths);
|
|
20258
20426
|
const cacheFresh = cached ? now().getTime() - Date.parse(cached.fetched_at) < RELEASE_CACHE_TTL_MS : false;
|
|
20259
20427
|
if (!options.refreshRemote || cacheFresh) {
|
|
@@ -20267,6 +20435,8 @@ async function readRemoteRelease(options, now) {
|
|
|
20267
20435
|
const snapshot = normalizeServerReleaseSnapshot(await response.json());
|
|
20268
20436
|
if (snapshot.issue) {
|
|
20269
20437
|
void options.logger?.warn("hermes_release_server_cache_issue", {
|
|
20438
|
+
server_base_url: context?.serverBaseUrl ?? null,
|
|
20439
|
+
release_check_url: context?.releaseCheckUrl ?? null,
|
|
20270
20440
|
error: snapshot.issue
|
|
20271
20441
|
});
|
|
20272
20442
|
}
|
|
@@ -20296,6 +20466,10 @@ async function readRemoteRelease(options, now) {
|
|
|
20296
20466
|
} catch (error) {
|
|
20297
20467
|
const issue = error instanceof Error ? error.message : String(error);
|
|
20298
20468
|
void options.logger?.warn("hermes_release_server_check_failed", {
|
|
20469
|
+
server_base_url: context?.serverBaseUrl ?? null,
|
|
20470
|
+
release_check_url: context?.releaseCheckUrl ?? null,
|
|
20471
|
+
cached: cached !== null,
|
|
20472
|
+
cache_fresh: cacheFresh,
|
|
20299
20473
|
error: issue
|
|
20300
20474
|
});
|
|
20301
20475
|
return cached ? { remote: fromCache(cached), state: "cached", issue } : { remote: null, state: "unavailable", issue };
|
|
@@ -20419,6 +20593,14 @@ async function fetchLatestReleaseFromServer(options, fetcher) {
|
|
|
20419
20593
|
clearTimeout(timer);
|
|
20420
20594
|
}
|
|
20421
20595
|
}
|
|
20596
|
+
async function readHermesReleaseCheckContext(paths) {
|
|
20597
|
+
const config = await loadConfig(paths);
|
|
20598
|
+
const url = new URL(SERVER_HERMES_RELEASES_LATEST_PATH, config.serverBaseUrl);
|
|
20599
|
+
return {
|
|
20600
|
+
serverBaseUrl: config.serverBaseUrl,
|
|
20601
|
+
releaseCheckUrl: url.toString()
|
|
20602
|
+
};
|
|
20603
|
+
}
|
|
20422
20604
|
function isProcessAlive2(pid) {
|
|
20423
20605
|
if (!pid || pid <= 0) {
|
|
20424
20606
|
return false;
|
|
@@ -21239,11 +21421,7 @@ async function checkLanIpChange(options, context = {}) {
|
|
|
21239
21421
|
public_ipv6s: routes.publicIpv6s,
|
|
21240
21422
|
reason: reservation.reason
|
|
21241
21423
|
};
|
|
21242
|
-
|
|
21243
|
-
void options.logger.warn("lan_ip_report_skipped", logFields);
|
|
21244
|
-
} else {
|
|
21245
|
-
void options.logger.debug("lan_ip_report_skipped", logFields);
|
|
21246
|
-
}
|
|
21424
|
+
void options.logger.debug("lan_ip_report_skipped", logFields);
|
|
21247
21425
|
return;
|
|
21248
21426
|
}
|
|
21249
21427
|
try {
|
|
@@ -21286,6 +21464,7 @@ function startCronDeliveryScheduler(options) {
|
|
|
21286
21464
|
);
|
|
21287
21465
|
} catch (error) {
|
|
21288
21466
|
void options.logger.warn("cron_link_delivery_sync_failed", {
|
|
21467
|
+
source: "daemon_scheduler",
|
|
21289
21468
|
error: error instanceof Error ? error.message : String(error)
|
|
21290
21469
|
});
|
|
21291
21470
|
} finally {
|
|
@@ -21315,6 +21494,7 @@ function startHermesSessionSyncScheduler(options) {
|
|
|
21315
21494
|
await options.conversations.syncHermesSessions();
|
|
21316
21495
|
} catch (error) {
|
|
21317
21496
|
void options.logger.warn("hermes_session_sync_failed", {
|
|
21497
|
+
source: "daemon_scheduler",
|
|
21318
21498
|
error: error instanceof Error ? error.message : String(error)
|
|
21319
21499
|
});
|
|
21320
21500
|
} finally {
|
|
@@ -21337,8 +21517,8 @@ function startHermesSessionSyncScheduler(options) {
|
|
|
21337
21517
|
var DEFAULT_RELAY_READY_TIMEOUT_MS = 2e3;
|
|
21338
21518
|
async function startLinkService(options = {}) {
|
|
21339
21519
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
21340
|
-
const logger = createFileLogger({ paths });
|
|
21341
21520
|
const [identity, config] = await Promise.all([loadIdentity(paths), loadConfig(paths)]);
|
|
21521
|
+
const logger = createFileLogger({ paths, minLevel: config.logLevel });
|
|
21342
21522
|
await logger.info("service_starting", {
|
|
21343
21523
|
port: config.port,
|
|
21344
21524
|
mode: identity?.link_id ? "paired" : "local-only"
|
|
@@ -21357,6 +21537,7 @@ async function startLinkService(options = {}) {
|
|
|
21357
21537
|
const triggerHermesSessionSync = () => {
|
|
21358
21538
|
hermesSessionSync = Promise.resolve().then(() => conversations.syncHermesSessions()).then(() => void 0).catch((error) => {
|
|
21359
21539
|
void logger.warn("hermes_session_sync_failed", {
|
|
21540
|
+
source: "service_startup",
|
|
21360
21541
|
error: error instanceof Error ? error.message : String(error)
|
|
21361
21542
|
});
|
|
21362
21543
|
});
|
|
@@ -21376,13 +21557,18 @@ async function startLinkService(options = {}) {
|
|
|
21376
21557
|
} catch (error) {
|
|
21377
21558
|
await logger.error("service_start_failed", {
|
|
21378
21559
|
port: config.port,
|
|
21560
|
+
link_id: identity?.link_id ?? null,
|
|
21379
21561
|
error: error instanceof Error ? error.message : String(error)
|
|
21380
21562
|
});
|
|
21381
21563
|
await logger.flush();
|
|
21382
21564
|
throw error;
|
|
21383
21565
|
}
|
|
21384
21566
|
server.on("error", (error) => {
|
|
21385
|
-
void logger.error("service_error", {
|
|
21567
|
+
void logger.error("service_error", {
|
|
21568
|
+
port: config.port,
|
|
21569
|
+
link_id: identity?.link_id ?? null,
|
|
21570
|
+
error: error.message
|
|
21571
|
+
});
|
|
21386
21572
|
});
|
|
21387
21573
|
void logger.info("service_started", {
|
|
21388
21574
|
port: config.port,
|
|
@@ -21998,6 +22184,9 @@ async function writeFailedStartState(options, error, targetVersion = null) {
|
|
|
21998
22184
|
return readLinkUpdateStatus(options.paths);
|
|
21999
22185
|
}
|
|
22000
22186
|
async function readRemoteLinkPolicy(options) {
|
|
22187
|
+
const context = await readLinkReleaseCheckContext(options.paths).catch(
|
|
22188
|
+
() => null
|
|
22189
|
+
);
|
|
22001
22190
|
try {
|
|
22002
22191
|
const response = await fetchCurrentLinkReleaseFromServer(
|
|
22003
22192
|
options,
|
|
@@ -22022,6 +22211,8 @@ async function readRemoteLinkPolicy(options) {
|
|
|
22022
22211
|
} catch (error) {
|
|
22023
22212
|
const issue = error instanceof Error ? error.message : String(error);
|
|
22024
22213
|
void options.logger?.warn("link_release_server_check_failed", {
|
|
22214
|
+
server_base_url: context?.serverBaseUrl ?? null,
|
|
22215
|
+
release_check_url: context?.releaseCheckUrl ?? null,
|
|
22025
22216
|
error: issue
|
|
22026
22217
|
});
|
|
22027
22218
|
return { remote: null, state: "unavailable", issue };
|
|
@@ -22080,6 +22271,16 @@ async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
|
|
|
22080
22271
|
clearTimeout(timer);
|
|
22081
22272
|
}
|
|
22082
22273
|
}
|
|
22274
|
+
async function readLinkReleaseCheckContext(paths) {
|
|
22275
|
+
const config = await loadConfig(paths);
|
|
22276
|
+
const url = new URL(SERVER_LINK_CURRENT_RELEASE_PATH, config.serverBaseUrl);
|
|
22277
|
+
url.searchParams.set("channel", "stable");
|
|
22278
|
+
url.searchParams.set("lang", "en");
|
|
22279
|
+
return {
|
|
22280
|
+
serverBaseUrl: config.serverBaseUrl,
|
|
22281
|
+
releaseCheckUrl: url.toString()
|
|
22282
|
+
};
|
|
22283
|
+
}
|
|
22083
22284
|
function computeLinkUpdateState(localVersion, remote) {
|
|
22084
22285
|
if (!remote?.current_version) {
|
|
22085
22286
|
return "unknown";
|
|
@@ -22879,7 +23080,7 @@ function registerSystemRoutes(router, options) {
|
|
|
22879
23080
|
})),
|
|
22880
23081
|
readDeviceSummary(paths),
|
|
22881
23082
|
listHermesProfiles(paths).catch(() => []),
|
|
22882
|
-
readHermesUpdateCheck({ paths, logger
|
|
23083
|
+
readHermesUpdateCheck({ paths, logger }).catch(
|
|
22883
23084
|
(error) => ({
|
|
22884
23085
|
ok: true,
|
|
22885
23086
|
local: {
|
|
@@ -23744,7 +23945,8 @@ function assertLoopbackRequest(request) {
|
|
|
23744
23945
|
// src/http/app.ts
|
|
23745
23946
|
async function createApp(options = {}) {
|
|
23746
23947
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
23747
|
-
const
|
|
23948
|
+
const config = await loadConfig(paths).catch(() => null);
|
|
23949
|
+
const logger = options.logger ?? createFileLogger({ paths, minLevel: config?.logLevel ?? "warn" });
|
|
23748
23950
|
const conversations = options.conversations ?? new ConversationService(paths, logger);
|
|
23749
23951
|
let cronDeliverySyncRunning = false;
|
|
23750
23952
|
const syncCronDeliveries = async () => {
|
|
@@ -23756,6 +23958,7 @@ async function createApp(options = {}) {
|
|
|
23756
23958
|
await syncHermesLinkCronDeliveries(paths, conversations, logger);
|
|
23757
23959
|
} catch (error) {
|
|
23758
23960
|
void logger.warn("cron_link_delivery_sync_failed", {
|
|
23961
|
+
source: "http_app_bootstrap",
|
|
23759
23962
|
error: error instanceof Error ? error.message : String(error)
|
|
23760
23963
|
});
|
|
23761
23964
|
} finally {
|
|
@@ -23794,18 +23997,20 @@ async function createApp(options = {}) {
|
|
|
23794
23997
|
export {
|
|
23795
23998
|
LINK_VERSION,
|
|
23796
23999
|
LINK_COMMAND,
|
|
24000
|
+
LinkHttpError,
|
|
23797
24001
|
resolveHermesProfileDir,
|
|
23798
24002
|
resolveHermesConfigPath,
|
|
23799
24003
|
readHermesApiServerConfig,
|
|
23800
24004
|
ensureHermesApiServerConfig,
|
|
23801
|
-
LinkHttpError,
|
|
23802
24005
|
resolveRuntimePaths,
|
|
23803
24006
|
createFileLogger,
|
|
23804
24007
|
getLinkLogFile,
|
|
23805
24008
|
ensureHermesApiServerAvailable,
|
|
23806
24009
|
readHermesVersion,
|
|
24010
|
+
defaultLinkConfig,
|
|
23807
24011
|
loadConfig,
|
|
23808
24012
|
saveConfig,
|
|
24013
|
+
parseLogLevel,
|
|
23809
24014
|
normalizeLanHost,
|
|
23810
24015
|
ConversationService,
|
|
23811
24016
|
loadIdentity,
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
import { R as RuntimePaths } from '../paths-CmAiZsna.js';
|
|
3
3
|
|
|
4
4
|
declare function waitForPairingOrShutdown(sessionId: string, expiresAt: string, paths: RuntimePaths): Promise<'claimed' | 'expired' | 'shutdown'>;
|
|
5
|
+
declare function isCliEntrypoint(entry?: string, moduleUrl?: string): boolean;
|
|
5
6
|
|
|
6
|
-
export { waitForPairingOrShutdown };
|
|
7
|
+
export { isCliEntrypoint, waitForPairingOrShutdown };
|
package/dist/cli/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
createFileLogger,
|
|
10
10
|
currentCliScriptPath,
|
|
11
11
|
daemonLogFile,
|
|
12
|
+
defaultLinkConfig,
|
|
12
13
|
detectRuntimeEnvironment,
|
|
13
14
|
ensureHermesApiServerAvailable,
|
|
14
15
|
ensureHermesApiServerConfig,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
loadConfig,
|
|
21
22
|
loadIdentity,
|
|
22
23
|
normalizeLanHost,
|
|
24
|
+
parseLogLevel,
|
|
23
25
|
preparePairing,
|
|
24
26
|
probeLocalLinkService,
|
|
25
27
|
readHermesApiServerConfig,
|
|
@@ -34,12 +36,14 @@ import {
|
|
|
34
36
|
startDaemonProcess,
|
|
35
37
|
startLinkService,
|
|
36
38
|
stopDaemonProcess
|
|
37
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-MFRSQUSE.js";
|
|
38
40
|
|
|
39
41
|
// src/cli/index.ts
|
|
40
42
|
import { Command } from "commander";
|
|
43
|
+
import { realpathSync } from "fs";
|
|
41
44
|
import path3 from "path";
|
|
42
45
|
import { createInterface } from "readline/promises";
|
|
46
|
+
import { pathToFileURL } from "url";
|
|
43
47
|
import qrcode from "qrcode-terminal";
|
|
44
48
|
|
|
45
49
|
// src/autostart/autostart.ts
|
|
@@ -257,6 +261,9 @@ var messages = {
|
|
|
257
261
|
"config.lanHostInvalid": "lan-host must be a private LAN IPv4 address, such as 192.168.1.23.",
|
|
258
262
|
"config.lanHostSet": "Configured LAN host: {value}",
|
|
259
263
|
"config.lanHostUnset": "Configured LAN host cleared.",
|
|
264
|
+
"config.logLevelInvalid": "log-level must be one of: debug, info, warn, error.",
|
|
265
|
+
"config.logLevelSet": "Configured log level: {value}",
|
|
266
|
+
"config.logLevelUnset": "Configured log level reset to the default: {value}.",
|
|
260
267
|
"config.reported": "Updated HermesPilot Server with the latest LAN address.",
|
|
261
268
|
"config.reportSkippedUnpaired": "Hermes Link is not paired yet. The LAN address will be reported after pairing.",
|
|
262
269
|
"daemon.description": "Run Hermes Link in the foreground",
|
|
@@ -352,6 +359,9 @@ var messages = {
|
|
|
352
359
|
"config.lanHostInvalid": "lan-host \u5FC5\u987B\u662F\u5C40\u57DF\u7F51 IPv4 \u5730\u5740\uFF0C\u4F8B\u5982 192.168.1.23\u3002",
|
|
353
360
|
"config.lanHostSet": "\u5DF2\u914D\u7F6E\u5C40\u57DF\u7F51\u4E3B\u673A\uFF1A{value}",
|
|
354
361
|
"config.lanHostUnset": "\u5DF2\u6E05\u9664\u5C40\u57DF\u7F51\u4E3B\u673A\u914D\u7F6E\u3002",
|
|
362
|
+
"config.logLevelInvalid": "log-level \u53EA\u80FD\u662F\u4EE5\u4E0B\u503C\u4E4B\u4E00\uFF1Adebug\u3001info\u3001warn\u3001error\u3002",
|
|
363
|
+
"config.logLevelSet": "\u5DF2\u914D\u7F6E\u65E5\u5FD7\u7EA7\u522B\uFF1A{value}",
|
|
364
|
+
"config.logLevelUnset": "\u5DF2\u5C06\u65E5\u5FD7\u7EA7\u522B\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C\uFF1A{value}\u3002",
|
|
355
365
|
"config.reported": "\u5DF2\u628A\u6700\u65B0\u5C40\u57DF\u7F51\u5730\u5740\u66F4\u65B0\u5230 HermesPilot Server\u3002",
|
|
356
366
|
"config.reportSkippedUnpaired": "Hermes Link \u8FD8\u6CA1\u6709\u914D\u5BF9\uFF0C\u5C40\u57DF\u7F51\u5730\u5740\u4F1A\u5728\u914D\u5BF9\u540E\u4E0A\u62A5\u3002",
|
|
357
367
|
"daemon.description": "\u4EE5\u524D\u53F0\u65B9\u5F0F\u8FD0\u884C Hermes Link",
|
|
@@ -736,16 +746,28 @@ configCommand.command("set").argument("<key>").argument("<value>").description(h
|
|
|
736
746
|
const language = resolveLanguage(current.language);
|
|
737
747
|
const t = translate.bind(null, language);
|
|
738
748
|
const normalizedKey = key.trim().toLowerCase();
|
|
739
|
-
if (normalizedKey
|
|
740
|
-
|
|
749
|
+
if (normalizedKey === "lan-host") {
|
|
750
|
+
const lanHost = normalizeLanHost(value);
|
|
751
|
+
if (!lanHost) {
|
|
752
|
+
throw new Error(t("config.lanHostInvalid"));
|
|
753
|
+
}
|
|
754
|
+
const next = await saveConfig({ lanHost }, paths);
|
|
755
|
+
console.log(t("config.lanHostSet", { value: next.lanHost ?? lanHost }));
|
|
756
|
+
await reportConfigNetworkUpdate(paths, t);
|
|
757
|
+
return;
|
|
741
758
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
759
|
+
if (normalizedKey === "log-level") {
|
|
760
|
+
const logLevel = parseLogLevel(value.trim().toLowerCase());
|
|
761
|
+
if (!logLevel) {
|
|
762
|
+
throw new Error(t("config.logLevelInvalid"));
|
|
763
|
+
}
|
|
764
|
+
const next = await saveConfig({ logLevel }, paths);
|
|
765
|
+
console.log(t("config.logLevelSet", { value: next.logLevel }));
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
{
|
|
769
|
+
throw new Error(t("config.unknownKey", { key }));
|
|
745
770
|
}
|
|
746
|
-
const next = await saveConfig({ lanHost }, paths);
|
|
747
|
-
console.log(t("config.lanHostSet", { value: next.lanHost ?? lanHost }));
|
|
748
|
-
await reportConfigNetworkUpdate(paths, t);
|
|
749
771
|
});
|
|
750
772
|
configCommand.command("unset").argument("<key>").description(helpText("config.unset.description")).action(async (key) => {
|
|
751
773
|
const paths = resolveRuntimePaths();
|
|
@@ -753,12 +775,22 @@ configCommand.command("unset").argument("<key>").description(helpText("config.un
|
|
|
753
775
|
const language = resolveLanguage(current.language);
|
|
754
776
|
const t = translate.bind(null, language);
|
|
755
777
|
const normalizedKey = key.trim().toLowerCase();
|
|
756
|
-
if (normalizedKey
|
|
778
|
+
if (normalizedKey === "lan-host") {
|
|
779
|
+
await saveConfig({ lanHost: null }, paths);
|
|
780
|
+
console.log(t("config.lanHostUnset"));
|
|
781
|
+
await reportConfigNetworkUpdate(paths, t);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (normalizedKey === "log-level") {
|
|
785
|
+
await saveConfig({ logLevel: defaultLinkConfig.logLevel }, paths);
|
|
786
|
+
console.log(
|
|
787
|
+
t("config.logLevelUnset", { value: defaultLinkConfig.logLevel })
|
|
788
|
+
);
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
{
|
|
757
792
|
throw new Error(t("config.unknownKey", { key }));
|
|
758
793
|
}
|
|
759
|
-
await saveConfig({ lanHost: null }, paths);
|
|
760
|
-
console.log(t("config.lanHostUnset"));
|
|
761
|
-
await reportConfigNetworkUpdate(paths, t);
|
|
762
794
|
});
|
|
763
795
|
program.command("start").description(helpText("start.description")).action(async () => {
|
|
764
796
|
const [config, status] = await Promise.all([loadConfig(), getDaemonStatus()]);
|
|
@@ -1031,7 +1063,7 @@ async function deliverStagedFilesFromCli(stagingDir, paths, config) {
|
|
|
1031
1063
|
throw error;
|
|
1032
1064
|
}
|
|
1033
1065
|
}
|
|
1034
|
-
const logger = createFileLogger({ paths });
|
|
1066
|
+
const logger = createFileLogger({ paths, minLevel: config.logLevel });
|
|
1035
1067
|
try {
|
|
1036
1068
|
const conversations = new ConversationService(paths, logger);
|
|
1037
1069
|
return await conversations.deliverStagedFiles(stagingDir);
|
|
@@ -1156,10 +1188,17 @@ function printPostPairingNetworkNotice(environment, config, t) {
|
|
|
1156
1188
|
console.log(t("pair.relayOnlyLanHostHint"));
|
|
1157
1189
|
console.log(t("pair.relayOnlySafetyHint"));
|
|
1158
1190
|
}
|
|
1159
|
-
function isCliEntrypoint() {
|
|
1160
|
-
|
|
1161
|
-
|
|
1191
|
+
function isCliEntrypoint(entry = process.argv[1], moduleUrl = import.meta.url) {
|
|
1192
|
+
if (!entry) {
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
try {
|
|
1196
|
+
return moduleUrl === pathToFileURL(realpathSync(path3.resolve(entry))).href;
|
|
1197
|
+
} catch {
|
|
1198
|
+
return moduleUrl === pathToFileURL(path3.resolve(entry)).href;
|
|
1199
|
+
}
|
|
1162
1200
|
}
|
|
1163
1201
|
export {
|
|
1202
|
+
isCliEntrypoint,
|
|
1164
1203
|
waitForPairingOrShutdown
|
|
1165
1204
|
};
|
package/dist/http/app.d.ts
CHANGED
|
@@ -310,6 +310,7 @@ interface FileLoggerOptions {
|
|
|
310
310
|
fileName?: string;
|
|
311
311
|
maxFileBytes?: number;
|
|
312
312
|
maxFiles?: number;
|
|
313
|
+
minLevel?: LogLevel;
|
|
313
314
|
now?: () => Date;
|
|
314
315
|
}
|
|
315
316
|
declare class FileLogger {
|
|
@@ -317,6 +318,7 @@ declare class FileLogger {
|
|
|
317
318
|
private readonly paths;
|
|
318
319
|
private readonly maxFileBytes;
|
|
319
320
|
private readonly maxFiles;
|
|
321
|
+
private readonly minLevel;
|
|
320
322
|
private readonly now;
|
|
321
323
|
private queue;
|
|
322
324
|
constructor(options?: FileLoggerOptions);
|
package/dist/http/app.js
CHANGED