@hermespilot/link 0.1.7 → 0.1.8
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
CHANGED
|
@@ -40,6 +40,8 @@ hermes update
|
|
|
40
40
|
hermes gateway run
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
If Hermes Agent is configured through `~/.hermes/.env`, Link follows the same `API_SERVER_*` overrides when calling the local API Server.
|
|
44
|
+
|
|
43
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`.
|
|
44
46
|
|
|
45
47
|
## Runtime data
|
|
@@ -4,7 +4,7 @@ import Router from "@koa/router";
|
|
|
4
4
|
import { Readable } from "stream";
|
|
5
5
|
|
|
6
6
|
// src/constants.ts
|
|
7
|
-
var LINK_VERSION = "0.1.
|
|
7
|
+
var LINK_VERSION = "0.1.8";
|
|
8
8
|
var LINK_COMMAND = "hermeslink";
|
|
9
9
|
var LINK_DEFAULT_PORT = 52379;
|
|
10
10
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -146,12 +146,16 @@ async function readHermesApiServerConfig(profileName = "default", configPath = r
|
|
|
146
146
|
throw error;
|
|
147
147
|
});
|
|
148
148
|
if (!existingRaw) {
|
|
149
|
-
return {};
|
|
149
|
+
return applyEnvOverrides({}, await readHermesApiServerEnvOverrides(profileName), false);
|
|
150
150
|
}
|
|
151
151
|
const config = toRecord(YAML.parse(existingRaw));
|
|
152
152
|
const platforms = toRecord(config.platforms);
|
|
153
153
|
const apiServer = toRecord(platforms.api_server);
|
|
154
|
-
return
|
|
154
|
+
return applyEnvOverrides(
|
|
155
|
+
readApiServerConfig(apiServer, true),
|
|
156
|
+
await readHermesApiServerEnvOverrides(profileName),
|
|
157
|
+
true
|
|
158
|
+
);
|
|
155
159
|
}
|
|
156
160
|
async function ensureHermesApiServerKey(profileName = "default", configPath = resolveHermesConfigPath(profileName)) {
|
|
157
161
|
return ensureHermesApiServerConfig(profileName, configPath);
|
|
@@ -168,10 +172,12 @@ async function ensureHermesApiServerConfig(profileName = "default", configPath =
|
|
|
168
172
|
const platforms = ensureRecord(config, "platforms");
|
|
169
173
|
const apiServer = ensureRecord(platforms, "api_server");
|
|
170
174
|
const extra = ensureRecord(apiServer, "extra");
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
+
const envOverrides = await readHermesApiServerEnvOverrides(profileName);
|
|
176
|
+
const before = applyEnvOverrides(readApiServerConfig(apiServer), envOverrides, false);
|
|
177
|
+
const beforeKey = before.key?.trim() ? before.key : null;
|
|
178
|
+
const beforeEnabled = before.enabled === true;
|
|
179
|
+
const beforeHost = before.host?.trim() ? before.host : null;
|
|
180
|
+
const beforePort = typeof before.port === "number" && Number.isFinite(before.port) ? before.port : null;
|
|
175
181
|
let changed = false;
|
|
176
182
|
let enabledAdded = false;
|
|
177
183
|
let hostAdded = false;
|
|
@@ -198,7 +204,7 @@ async function ensureHermesApiServerConfig(profileName = "default", configPath =
|
|
|
198
204
|
if (!changed) {
|
|
199
205
|
return {
|
|
200
206
|
configPath,
|
|
201
|
-
apiServer: readApiServerConfig(apiServer, true),
|
|
207
|
+
apiServer: applyEnvOverrides(readApiServerConfig(apiServer, true), envOverrides, true),
|
|
202
208
|
changed: false,
|
|
203
209
|
keyAdded: false,
|
|
204
210
|
enabledAdded: false,
|
|
@@ -217,7 +223,7 @@ async function ensureHermesApiServerConfig(profileName = "default", configPath =
|
|
|
217
223
|
await writeFile(configPath, document.toString(), { mode: 384 });
|
|
218
224
|
return {
|
|
219
225
|
configPath,
|
|
220
|
-
apiServer: readApiServerConfig(apiServer, true),
|
|
226
|
+
apiServer: applyEnvOverrides(readApiServerConfig(apiServer, true), envOverrides, true),
|
|
221
227
|
changed: true,
|
|
222
228
|
keyAdded: !beforeKey,
|
|
223
229
|
enabledAdded,
|
|
@@ -239,6 +245,73 @@ function readApiServerConfig(apiServerOrExtra, withDefaults = false) {
|
|
|
239
245
|
key: typeof extra.key === "string" ? extra.key : void 0
|
|
240
246
|
};
|
|
241
247
|
}
|
|
248
|
+
async function readHermesApiServerEnvOverrides(profileName) {
|
|
249
|
+
const values = await readHermesEnvFile(profileName);
|
|
250
|
+
for (const key of ["API_SERVER_ENABLED", "API_SERVER_HOST", "API_SERVER_PORT", "API_SERVER_KEY"]) {
|
|
251
|
+
const value = process.env[key];
|
|
252
|
+
if (typeof value === "string" && value.trim()) {
|
|
253
|
+
values[key] = value;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const port = Number.parseInt(values.API_SERVER_PORT ?? "", 10);
|
|
257
|
+
return {
|
|
258
|
+
enabled: parseEnvBoolean(values.API_SERVER_ENABLED),
|
|
259
|
+
host: values.API_SERVER_HOST?.trim() || void 0,
|
|
260
|
+
port: Number.isFinite(port) ? port : void 0,
|
|
261
|
+
key: values.API_SERVER_KEY?.trim() || void 0
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function readHermesEnvFile(profileName) {
|
|
265
|
+
const envPath = path3.join(resolveHermesProfileDir(profileName), ".env");
|
|
266
|
+
const raw = await readFile2(envPath, "utf8").catch((error) => {
|
|
267
|
+
if (isNodeError2(error, "ENOENT")) {
|
|
268
|
+
return "";
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
});
|
|
272
|
+
const values = {};
|
|
273
|
+
for (const line of raw.split(/\r?\n/u)) {
|
|
274
|
+
const trimmed = line.trim();
|
|
275
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const match = /^(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)=(?<value>.*)$/u.exec(trimmed);
|
|
279
|
+
if (!match?.groups) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
values[match.groups.key] = unquoteEnvValue(match.groups.value.trim());
|
|
283
|
+
}
|
|
284
|
+
return values;
|
|
285
|
+
}
|
|
286
|
+
function applyEnvOverrides(config, env, withDefaults) {
|
|
287
|
+
const host = env.host ?? config.host;
|
|
288
|
+
const port = env.port ?? config.port;
|
|
289
|
+
return {
|
|
290
|
+
enabled: env.enabled ?? config.enabled,
|
|
291
|
+
host: withDefaults ? host ?? DEFAULT_HERMES_API_SERVER_HOST : host,
|
|
292
|
+
port: withDefaults ? port ?? DEFAULT_HERMES_API_SERVER_PORT : port,
|
|
293
|
+
key: env.key ?? config.key
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function parseEnvBoolean(value) {
|
|
297
|
+
if (!value) {
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
const normalized = value.trim().toLowerCase();
|
|
301
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
function unquoteEnvValue(value) {
|
|
310
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
311
|
+
return value.slice(1, -1);
|
|
312
|
+
}
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
242
315
|
function buildNotice(flags) {
|
|
243
316
|
const fields = [];
|
|
244
317
|
if (flags.enabledAdded) {
|
|
@@ -305,7 +378,7 @@ var gatewayStartInFlight = null;
|
|
|
305
378
|
async function ensureHermesApiServerAvailable(options = {}) {
|
|
306
379
|
const configResult = await ensureHermesApiServerConfig();
|
|
307
380
|
const fetcher = options.fetchImpl ?? fetch;
|
|
308
|
-
if (await isHermesApiHealthy(configResult.apiServer, fetcher)) {
|
|
381
|
+
if (!options.forceRestart && await isHermesApiHealthy(configResult.apiServer, fetcher)) {
|
|
309
382
|
return { available: true, configResult, started: false };
|
|
310
383
|
}
|
|
311
384
|
if (!shouldAutoStart(options.autoStart)) {
|
|
@@ -520,6 +593,15 @@ async function callHermesApi(path9, init, options) {
|
|
|
520
593
|
const availability = await ensureHermesApiServerAvailable({ fetchImpl: options.fetchImpl });
|
|
521
594
|
const config = availability.configResult.apiServer;
|
|
522
595
|
const fetcher = options.fetchImpl ?? fetch;
|
|
596
|
+
const request = () => fetchHermesApi(fetcher, config, path9, init);
|
|
597
|
+
const response = await request();
|
|
598
|
+
if (response.status !== 401) {
|
|
599
|
+
return response;
|
|
600
|
+
}
|
|
601
|
+
await ensureHermesApiServerAvailable({ fetchImpl: options.fetchImpl, forceRestart: true });
|
|
602
|
+
return await request();
|
|
603
|
+
}
|
|
604
|
+
async function fetchHermesApi(fetcher, config, path9, init) {
|
|
523
605
|
const headers = new Headers(init.headers);
|
|
524
606
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
525
607
|
if (config.key) {
|
|
@@ -534,12 +616,37 @@ async function callHermesApi(path9, init, options) {
|
|
|
534
616
|
});
|
|
535
617
|
}
|
|
536
618
|
async function readJsonResponse(response) {
|
|
537
|
-
const
|
|
619
|
+
const raw = await response.text().catch(() => "");
|
|
620
|
+
const payload = parseJsonObject(raw);
|
|
538
621
|
if (!response.ok || typeof payload !== "object" || payload === null) {
|
|
539
|
-
throw new LinkHttpError(
|
|
622
|
+
throw new LinkHttpError(
|
|
623
|
+
502,
|
|
624
|
+
"hermes_response_invalid",
|
|
625
|
+
`Hermes API Server returned HTTP ${response.status}: ${readUpstreamMessage(payload, raw)}`
|
|
626
|
+
);
|
|
540
627
|
}
|
|
541
628
|
return payload;
|
|
542
629
|
}
|
|
630
|
+
function parseJsonObject(raw) {
|
|
631
|
+
if (!raw.trim()) {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
try {
|
|
635
|
+
const parsed = JSON.parse(raw);
|
|
636
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
637
|
+
} catch {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function readUpstreamMessage(payload, raw) {
|
|
642
|
+
const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
|
|
643
|
+
const message = readString(error ?? {}, "message") ?? readString(payload ?? {}, "message");
|
|
644
|
+
if (message) {
|
|
645
|
+
return message;
|
|
646
|
+
}
|
|
647
|
+
const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
648
|
+
return body || "empty response body";
|
|
649
|
+
}
|
|
543
650
|
function readString(payload, key) {
|
|
544
651
|
const value = payload[key];
|
|
545
652
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -2989,4 +3096,4 @@ export {
|
|
|
2989
3096
|
getLinkLogFile,
|
|
2990
3097
|
createApp
|
|
2991
3098
|
};
|
|
2992
|
-
//# sourceMappingURL=chunk-
|
|
3099
|
+
//# sourceMappingURL=chunk-L2NM2XMX.js.map
|