@hermespilot/link 0.7.4 → 0.7.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.
|
@@ -2618,16 +2618,20 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
|
|
|
2618
2618
|
const shouldUpdateReasoningEffort = input.reasoningEffort !== void 0;
|
|
2619
2619
|
const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
|
|
2620
2620
|
const providers = ensureProvidersRecordWithLegacyMigration(config);
|
|
2621
|
-
const originalModelId = input.originalModelId?.trim()
|
|
2621
|
+
const originalModelId = input.originalModelId?.trim();
|
|
2622
2622
|
const originalProvider = input.originalProvider?.trim();
|
|
2623
2623
|
const originalBaseUrl = input.originalBaseUrl?.trim();
|
|
2624
2624
|
const originalApiMode = input.originalApiMode?.trim();
|
|
2625
|
-
const
|
|
2626
|
-
id: originalModelId,
|
|
2625
|
+
const originalIdentity = {
|
|
2626
|
+
id: originalModelId || normalized.id,
|
|
2627
2627
|
provider: originalProvider,
|
|
2628
2628
|
baseUrl: originalBaseUrl,
|
|
2629
2629
|
apiMode: originalApiMode
|
|
2630
|
-
}
|
|
2630
|
+
};
|
|
2631
|
+
const hasOriginalIdentity = Boolean(
|
|
2632
|
+
originalModelId || originalProvider || originalBaseUrl || originalApiMode
|
|
2633
|
+
);
|
|
2634
|
+
const existingProviderKey = (hasOriginalIdentity ? findProviderConfigKeyByModelIdentity(providers, originalIdentity) : null) ?? (originalProvider && originalBaseUrl ? findProviderConfigKeyByEndpoint(providers, {
|
|
2631
2635
|
provider: originalProvider,
|
|
2632
2636
|
baseUrl: originalBaseUrl,
|
|
2633
2637
|
apiMode: originalApiMode
|
|
@@ -2647,7 +2651,7 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
|
|
|
2647
2651
|
await writeHermesEnvValue(profileName, keyEnv, normalized.apiKey);
|
|
2648
2652
|
}
|
|
2649
2653
|
writeProviderEndpointConfig(entry, normalized, keyEnv);
|
|
2650
|
-
ensureEntryModelConfig(entry,
|
|
2654
|
+
ensureEntryModelConfig(entry, originalIdentity.id, normalized.id);
|
|
2651
2655
|
writeEntryModelContextLength(entry, normalized.id, normalized.contextLength);
|
|
2652
2656
|
writeEntryModelSupportsVision(entry, normalized.id, input.supportsVision);
|
|
2653
2657
|
if (shouldUpdateReasoningEffort) {
|
|
@@ -2662,15 +2666,29 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
|
|
|
2662
2666
|
const currentDefaultConfig = readModelConfig(modelConfig);
|
|
2663
2667
|
const currentDefault = currentDefaultConfig.model;
|
|
2664
2668
|
const currentDefaultReasoningEffort = readProfileReasoningEffort(config);
|
|
2665
|
-
|
|
2666
|
-
|
|
2669
|
+
const currentDefaultMatchesOriginal = hasOriginalIdentity && modelConfigMatchesModelIdentity(currentDefaultConfig, originalIdentity);
|
|
2670
|
+
const currentDefaultMatchesNext = modelConfigMatchesModelIdentity(
|
|
2671
|
+
currentDefaultConfig,
|
|
2672
|
+
{
|
|
2673
|
+
id: normalized.id,
|
|
2674
|
+
provider: providerKey,
|
|
2675
|
+
baseUrl: normalized.baseUrl,
|
|
2676
|
+
apiMode: inferApiMode(
|
|
2677
|
+
providerKey,
|
|
2678
|
+
normalized.baseUrl,
|
|
2679
|
+
normalized.apiMode
|
|
2680
|
+
)
|
|
2681
|
+
}
|
|
2682
|
+
);
|
|
2683
|
+
if (normalized.setDefault || !currentDefault || currentDefaultMatchesOriginal) {
|
|
2684
|
+
if (normalized.setDefault && currentDefault && !currentDefaultMatchesNext && !currentDefaultMatchesOriginal) {
|
|
2667
2685
|
retainModelDefaultAsProvider(providers, {
|
|
2668
2686
|
...currentDefaultConfig,
|
|
2669
2687
|
...currentDefaultReasoningEffort ? { reasoningEffort: currentDefaultReasoningEffort } : {}
|
|
2670
2688
|
});
|
|
2671
2689
|
}
|
|
2672
|
-
const defaultKeyEnv = keyEnv ?? (
|
|
2673
|
-
const defaultApiKey = normalized.apiKey ?? (!defaultKeyEnv &&
|
|
2690
|
+
const defaultKeyEnv = keyEnv ?? (currentDefaultMatchesOriginal ? currentDefaultConfig.keyEnv : void 0);
|
|
2691
|
+
const defaultApiKey = normalized.apiKey ?? (!defaultKeyEnv && currentDefaultMatchesOriginal ? currentDefaultConfig.apiKey : void 0);
|
|
2674
2692
|
writeDefaultModelConfig(modelConfig, {
|
|
2675
2693
|
...normalized,
|
|
2676
2694
|
provider: providerKey,
|
|
@@ -2858,7 +2876,12 @@ async function saveHermesModelDefaults(input, profileName = "default", configPat
|
|
|
2858
2876
|
const modelConfig = ensureRecord(config, "model");
|
|
2859
2877
|
const currentDefaultConfig = readModelConfig(modelConfig);
|
|
2860
2878
|
const currentDefaultReasoningEffort = readProfileReasoningEffort(config);
|
|
2861
|
-
if (currentDefaultConfig.model && currentDefaultConfig
|
|
2879
|
+
if (currentDefaultConfig.model && !modelConfigMatchesModelIdentity(currentDefaultConfig, {
|
|
2880
|
+
id: selected.id,
|
|
2881
|
+
provider: selected.provider,
|
|
2882
|
+
baseUrl: selected.baseUrl,
|
|
2883
|
+
apiMode: selected.apiMode
|
|
2884
|
+
})) {
|
|
2862
2885
|
retainModelDefaultAsProvider(providers, {
|
|
2863
2886
|
...currentDefaultConfig,
|
|
2864
2887
|
...currentDefaultReasoningEffort ? { reasoningEffort: currentDefaultReasoningEffort } : {}
|
|
@@ -3472,6 +3495,11 @@ function providerConfigToLegacyEntry(providerKey, entry) {
|
|
|
3472
3495
|
}
|
|
3473
3496
|
function ensureProvidersRecordWithLegacyMigration(config) {
|
|
3474
3497
|
const providers = ensureRecord(config, "providers");
|
|
3498
|
+
for (const [key, rawEntry] of Object.entries(providers)) {
|
|
3499
|
+
const entry = toRecord(rawEntry);
|
|
3500
|
+
normalizeProviderCredentialReference(entry);
|
|
3501
|
+
providers[key] = entry;
|
|
3502
|
+
}
|
|
3475
3503
|
const customProviders = Array.isArray(config.custom_providers) ? config.custom_providers : [];
|
|
3476
3504
|
for (const rawEntry of customProviders) {
|
|
3477
3505
|
const entry = toRecord(rawEntry);
|
|
@@ -3609,7 +3637,7 @@ function legacyEntryToProviderConfig(entry, baseUrl) {
|
|
|
3609
3637
|
const apiKey = readString2(entry.api_key);
|
|
3610
3638
|
const keyEnv = readString2(entry.key_env) ?? parseEnvReference(apiKey);
|
|
3611
3639
|
if (keyEnv) {
|
|
3612
|
-
next.
|
|
3640
|
+
next.api_key = `\${${keyEnv}}`;
|
|
3613
3641
|
} else if (apiKey) {
|
|
3614
3642
|
next.api_key = apiKey;
|
|
3615
3643
|
}
|
|
@@ -3643,6 +3671,15 @@ function legacyEntryToProviderConfig(entry, baseUrl) {
|
|
|
3643
3671
|
}
|
|
3644
3672
|
return next;
|
|
3645
3673
|
}
|
|
3674
|
+
function normalizeProviderCredentialReference(entry) {
|
|
3675
|
+
const apiKey = readString2(entry.api_key);
|
|
3676
|
+
const keyEnv = readString2(entry.key_env) ?? parseEnvReference(apiKey);
|
|
3677
|
+
if (!keyEnv) {
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3680
|
+
entry.api_key = `\${${keyEnv}}`;
|
|
3681
|
+
delete entry.key_env;
|
|
3682
|
+
}
|
|
3646
3683
|
function normalizeEntryModelsMap(entry) {
|
|
3647
3684
|
const models = entry.models;
|
|
3648
3685
|
if (typeof models === "object" && models !== null && !Array.isArray(models)) {
|
|
@@ -3780,8 +3817,8 @@ function writeProviderEndpointConfig(entry, input, keyEnv) {
|
|
|
3780
3817
|
delete entry.contextLength;
|
|
3781
3818
|
}
|
|
3782
3819
|
if (keyEnv) {
|
|
3783
|
-
entry.
|
|
3784
|
-
delete entry.
|
|
3820
|
+
entry.api_key = `\${${keyEnv}}`;
|
|
3821
|
+
delete entry.key_env;
|
|
3785
3822
|
} else {
|
|
3786
3823
|
delete entry.key_env;
|
|
3787
3824
|
}
|
|
@@ -4041,22 +4078,18 @@ function managedModelPreferenceScore(model) {
|
|
|
4041
4078
|
return (model.isDefault ? 1e3 : 0) + (model.credentialState === "configured" ? 200 : 0) + (model.credentialState === "missing" ? 50 : 0) + (model.source === "auth_store" ? 100 : 0) + (model.credentialSource !== "unknown" ? 40 : 0) + (model.isReadOnly ? 10 : 0);
|
|
4042
4079
|
}
|
|
4043
4080
|
function matchesDefaultModelConfig(input) {
|
|
4044
|
-
if (!input.defaultModel
|
|
4045
|
-
return false;
|
|
4046
|
-
}
|
|
4047
|
-
const defaultApiMode = input.modelConfig.apiMode?.trim();
|
|
4048
|
-
if (defaultApiMode && input.apiMode !== defaultApiMode) {
|
|
4081
|
+
if (!input.defaultModel) {
|
|
4049
4082
|
return false;
|
|
4050
4083
|
}
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4084
|
+
return modelConfigMatchesModelIdentity(
|
|
4085
|
+
{ ...input.modelConfig, model: input.defaultModel },
|
|
4086
|
+
{
|
|
4087
|
+
id: input.id,
|
|
4088
|
+
provider: input.provider,
|
|
4089
|
+
baseUrl: input.baseUrl,
|
|
4090
|
+
apiMode: input.apiMode
|
|
4091
|
+
}
|
|
4092
|
+
);
|
|
4060
4093
|
}
|
|
4061
4094
|
function providerEntryMatchesManagedModel(entry, model) {
|
|
4062
4095
|
const providerName = readString2(entry.name) ?? readString2(entry.provider_name) ?? readString2(entry.provider_key) ?? "Custom Provider";
|
|
@@ -4266,6 +4299,29 @@ function resolveCompressionModel(config, models) {
|
|
|
4266
4299
|
function findManagedModelById(models, id) {
|
|
4267
4300
|
return models.find((model) => model.id === id);
|
|
4268
4301
|
}
|
|
4302
|
+
function modelConfigMatchesModelIdentity(modelConfig, input) {
|
|
4303
|
+
if (modelConfig.model !== input.id) {
|
|
4304
|
+
return false;
|
|
4305
|
+
}
|
|
4306
|
+
const provider = input.provider?.trim();
|
|
4307
|
+
const configProvider = modelConfig.provider?.trim();
|
|
4308
|
+
if (provider && configProvider && configProvider !== provider) {
|
|
4309
|
+
return false;
|
|
4310
|
+
}
|
|
4311
|
+
const baseUrl = input.baseUrl?.trim();
|
|
4312
|
+
if (baseUrl !== void 0 && normalizeBaseUrl(modelConfig.baseUrl ?? "") !== normalizeBaseUrl(baseUrl)) {
|
|
4313
|
+
return false;
|
|
4314
|
+
}
|
|
4315
|
+
const apiMode = input.apiMode?.trim();
|
|
4316
|
+
if (apiMode) {
|
|
4317
|
+
const providerForApiMode = configProvider ?? provider ?? "default";
|
|
4318
|
+
const configBaseUrl = modelConfig.baseUrl ?? baseUrl ?? "";
|
|
4319
|
+
if (inferApiMode(providerForApiMode, configBaseUrl, modelConfig.apiMode) !== apiMode) {
|
|
4320
|
+
return false;
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
return true;
|
|
4324
|
+
}
|
|
4269
4325
|
function findManagedModel(models, input) {
|
|
4270
4326
|
const provider = input.provider?.trim();
|
|
4271
4327
|
const baseUrl = input.baseUrl?.trim();
|
|
@@ -4430,7 +4486,7 @@ function retainModelDefaultAsProvider(providers, model) {
|
|
|
4430
4486
|
writeEntryModelSupportsVision(entry, id, model.supportsVision);
|
|
4431
4487
|
}
|
|
4432
4488
|
if (model.keyEnv) {
|
|
4433
|
-
entry.
|
|
4489
|
+
entry.api_key = `\${${model.keyEnv}}`;
|
|
4434
4490
|
} else if (model.apiKey) {
|
|
4435
4491
|
entry.api_key = model.apiKey;
|
|
4436
4492
|
}
|
|
@@ -6454,7 +6510,7 @@ function isConversationMissingError(error) {
|
|
|
6454
6510
|
}
|
|
6455
6511
|
|
|
6456
6512
|
// src/constants.ts
|
|
6457
|
-
var LINK_VERSION = "0.7.
|
|
6513
|
+
var LINK_VERSION = "0.7.5";
|
|
6458
6514
|
var LINK_COMMAND = "hermeslink";
|
|
6459
6515
|
var LINK_DEFAULT_PORT = 52379;
|
|
6460
6516
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -24497,11 +24553,15 @@ function registerConversationRoutes(router, options) {
|
|
|
24497
24553
|
router.get("/api/v1/conversations", async (ctx) => {
|
|
24498
24554
|
const auth = await authenticateRequest(ctx, paths);
|
|
24499
24555
|
const language = readPreferredLanguage(ctx);
|
|
24500
|
-
|
|
24556
|
+
const cursor = readConversationListCursor(ctx.query);
|
|
24557
|
+
const forceSync = readConversationListForce(ctx.query);
|
|
24558
|
+
await prepareConversationListRead(conversations, logger, auth, {
|
|
24559
|
+
syncHermesSessions: forceSync || !cursor
|
|
24560
|
+
});
|
|
24501
24561
|
ctx.set("cache-control", "no-store");
|
|
24502
24562
|
const result = await conversations.listConversationPage({
|
|
24503
24563
|
limit: readLimit(ctx.query.limit),
|
|
24504
|
-
cursor
|
|
24564
|
+
cursor
|
|
24505
24565
|
});
|
|
24506
24566
|
const localized = localizeConversationListPage(result, language);
|
|
24507
24567
|
ctx.body = {
|
|
@@ -25014,7 +25074,15 @@ function registerConversationRoutes(router, options) {
|
|
|
25014
25074
|
}
|
|
25015
25075
|
);
|
|
25016
25076
|
}
|
|
25017
|
-
async function prepareConversationListRead(conversations, logger, auth) {
|
|
25077
|
+
async function prepareConversationListRead(conversations, logger, auth, options = {}) {
|
|
25078
|
+
if (options.syncHermesSessions) {
|
|
25079
|
+
await conversations.syncHermesSessions().catch((error) => {
|
|
25080
|
+
void logger.warn("hermes_session_sync_failed", {
|
|
25081
|
+
source: "conversation_list_read",
|
|
25082
|
+
error: error instanceof Error ? error.message : String(error)
|
|
25083
|
+
});
|
|
25084
|
+
});
|
|
25085
|
+
}
|
|
25018
25086
|
await conversations.backfillCronOwnership({
|
|
25019
25087
|
accountId: auth.accountId,
|
|
25020
25088
|
appInstanceId: auth.appInstanceId
|
|
@@ -25030,6 +25098,13 @@ async function prepareConversationListRead(conversations, logger, auth) {
|
|
|
25030
25098
|
});
|
|
25031
25099
|
});
|
|
25032
25100
|
}
|
|
25101
|
+
function readConversationListCursor(query) {
|
|
25102
|
+
return readQueryString(query.cursor) ?? readQueryString(query.after) ?? readQueryString(query.page_cursor);
|
|
25103
|
+
}
|
|
25104
|
+
function readConversationListForce(query) {
|
|
25105
|
+
const raw = Array.isArray(query.force) ? query.force[0] : query.force;
|
|
25106
|
+
return readBoolean3(raw) === true;
|
|
25107
|
+
}
|
|
25033
25108
|
function localizeConversationListPage(page, language) {
|
|
25034
25109
|
return {
|
|
25035
25110
|
...page,
|
|
@@ -25783,7 +25858,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
25783
25858
|
const body = await readJsonBody(ctx.req);
|
|
25784
25859
|
try {
|
|
25785
25860
|
const result = await saveHermesModelConfig(readModelConfigInput(body));
|
|
25786
|
-
ctx.body = shouldReloadGatewayAfterModelConfigChange(body
|
|
25861
|
+
ctx.body = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
25862
|
+
defaultReload: true
|
|
25863
|
+
}) ? await reloadGatewayAfterModelConfigChange(result, {
|
|
25787
25864
|
paths,
|
|
25788
25865
|
logger
|
|
25789
25866
|
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
@@ -25829,7 +25906,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
25829
25906
|
readModelConfigInput(body),
|
|
25830
25907
|
ctx.params.name
|
|
25831
25908
|
);
|
|
25832
|
-
ctx.body = shouldReloadGatewayAfterModelConfigChange(body
|
|
25909
|
+
ctx.body = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
25910
|
+
defaultReload: true
|
|
25911
|
+
}) ? await reloadGatewayAfterProfileModelConfigChange(result, {
|
|
25833
25912
|
paths,
|
|
25834
25913
|
logger,
|
|
25835
25914
|
profileName: ctx.params.name
|
|
@@ -25982,15 +26061,20 @@ function readModelConfigImportInput(body) {
|
|
|
25982
26061
|
setDefault: readBoolean3(body.set_default ?? body.setDefault)
|
|
25983
26062
|
};
|
|
25984
26063
|
}
|
|
25985
|
-
function shouldReloadGatewayAfterModelConfigChange(body) {
|
|
25986
|
-
|
|
25987
|
-
|
|
26064
|
+
function shouldReloadGatewayAfterModelConfigChange(body, options = {}) {
|
|
26065
|
+
if (readBoolean3(body.skip_gateway_reload ?? body.skipGatewayReload) === true) {
|
|
26066
|
+
return false;
|
|
26067
|
+
}
|
|
26068
|
+
if (readBoolean3(body.reload_gateway ?? body.reloadGateway) === true) {
|
|
26069
|
+
return true;
|
|
26070
|
+
}
|
|
26071
|
+
return options.defaultReload === true;
|
|
25988
26072
|
}
|
|
25989
26073
|
function markModelConfigAppliedWithoutGatewayReload(result) {
|
|
25990
26074
|
return {
|
|
25991
26075
|
...result,
|
|
25992
26076
|
requiresGatewayReload: false,
|
|
25993
|
-
restartHint: "\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\
|
|
26077
|
+
restartHint: "\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u5DF2\u6309\u8BF7\u6C42\u8DF3\u8FC7 Hermes Gateway \u91CD\u8F7D\u3002\u82E5\u521A\u4FEE\u6539 API Key\u3001Base URL \u6216 API Mode\uFF0C\u8BF7\u624B\u52A8\u91CD\u8F7D Gateway \u540E\u518D\u5F00\u59CB\u65B0\u7684 Run\u3002"
|
|
25994
26078
|
};
|
|
25995
26079
|
}
|
|
25996
26080
|
function toModelConfigHttpError(error) {
|
package/dist/cli/index.js
CHANGED
package/dist/http/app.js
CHANGED