@hermespilot/link 0.7.9 → 0.8.0
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/dist/{chunk-LUGNDJU3.js → chunk-GHZLIFQF.js} +639 -330
- package/dist/cli/index.js +1 -1
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import Router from "@koa/router";
|
|
|
5
5
|
// src/conversations/conversation-service.ts
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { createHash as createHash7, randomUUID as randomUUID12 } from "crypto";
|
|
8
|
-
import
|
|
8
|
+
import path26 from "path";
|
|
9
9
|
|
|
10
10
|
// src/database/link-database.ts
|
|
11
11
|
import { mkdir } from "fs/promises";
|
|
@@ -2068,6 +2068,22 @@ function parseLanguage(value) {
|
|
|
2068
2068
|
}
|
|
2069
2069
|
|
|
2070
2070
|
// src/hermes/config.ts
|
|
2071
|
+
function filterHermesModelConfigsByProviderKeys(result, hiddenProviderKeys) {
|
|
2072
|
+
if (hiddenProviderKeys.size === 0) {
|
|
2073
|
+
return result;
|
|
2074
|
+
}
|
|
2075
|
+
const models = result.models.filter(
|
|
2076
|
+
(model) => !hiddenProviderKeys.has(model.provider)
|
|
2077
|
+
);
|
|
2078
|
+
const compressionModel = result.compressionModel && !hiddenProviderKeys.has(result.compressionModel.provider) ? result.compressionModel : null;
|
|
2079
|
+
return {
|
|
2080
|
+
...result,
|
|
2081
|
+
defaultModel: models.some((model) => model.isDefault) ? result.defaultModel : null,
|
|
2082
|
+
compressionModelId: compressionModel ? result.compressionModelId : null,
|
|
2083
|
+
compressionModel,
|
|
2084
|
+
models
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2071
2087
|
var DEFAULT_HERMES_API_SERVER_HOST = "127.0.0.1";
|
|
2072
2088
|
var DEFAULT_HERMES_API_SERVER_PORT = 8642;
|
|
2073
2089
|
var PROFILE_API_SERVER_PORT_START = DEFAULT_HERMES_API_SERVER_PORT + 1;
|
|
@@ -2628,7 +2644,12 @@ async function importHermesModelConfig(input, targetProfileName = "default", tar
|
|
|
2628
2644
|
const sourceApiKey = sourceKeyEnv ? source.env[sourceKeyEnv]?.trim() : source.inlineApiKey;
|
|
2629
2645
|
let keyEnv = existingKeyEnv;
|
|
2630
2646
|
if (!keyEnv && !existingInlineApiKey) {
|
|
2631
|
-
keyEnv = sourceKeyEnv
|
|
2647
|
+
keyEnv = (sourceKeyEnv && !reservedHermesEnvKeys(targetEnv, config).has(sourceKeyEnv) ? sourceKeyEnv : void 0) ?? (sourceApiKey ? await buildUniqueApiKeyEnvName(targetProfileName, config, targetEnv, {
|
|
2648
|
+
providerName: source.model.providerName,
|
|
2649
|
+
modelId: source.model.id,
|
|
2650
|
+
providerKey: targetProviderKey,
|
|
2651
|
+
apiKey: sourceApiKey
|
|
2652
|
+
}) : void 0);
|
|
2632
2653
|
}
|
|
2633
2654
|
if (sourceApiKey && keyEnv && !existingInlineApiKey && (!existingKeyEnv || !targetEnv[keyEnv]?.trim())) {
|
|
2634
2655
|
await writeHermesEnvValue(targetProfileName, keyEnv, sourceApiKey);
|
|
@@ -2771,7 +2792,12 @@ async function saveHermesModelConfig(input, profileName = "default", configPath
|
|
|
2771
2792
|
);
|
|
2772
2793
|
const entry = toRecord(providers[providerKey]);
|
|
2773
2794
|
const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
2774
|
-
const keyEnv = normalized.keyEnv ?? existingKeyEnv ?? (normalized.apiKey ?
|
|
2795
|
+
const keyEnv = normalized.keyEnv ?? existingKeyEnv ?? (normalized.apiKey ? await buildUniqueApiKeyEnvName(profileName, config, void 0, {
|
|
2796
|
+
providerName: normalized.providerName,
|
|
2797
|
+
modelId: normalized.id,
|
|
2798
|
+
providerKey,
|
|
2799
|
+
apiKey: normalized.apiKey
|
|
2800
|
+
}) : void 0);
|
|
2775
2801
|
if (normalized.apiKey && keyEnv) {
|
|
2776
2802
|
await writeHermesEnvValue(profileName, keyEnv, normalized.apiKey);
|
|
2777
2803
|
}
|
|
@@ -2963,22 +2989,28 @@ async function saveHermesModelProviderConfig(input, profileName = "default", con
|
|
|
2963
2989
|
const { document, config, existingRaw } = await readHermesConfigDocument(configPath);
|
|
2964
2990
|
const providers = ensureProvidersRecordWithLegacyMigration(config);
|
|
2965
2991
|
const requestedProviderKey = input.providerKey?.trim();
|
|
2992
|
+
const apiKey = input.apiKey?.trim();
|
|
2966
2993
|
if (requestedProviderKey && !Object.prototype.hasOwnProperty.call(providers, requestedProviderKey)) {
|
|
2967
2994
|
throw new Error(`provider "${requestedProviderKey}" is not configured`);
|
|
2968
2995
|
}
|
|
2969
|
-
const
|
|
2996
|
+
const canReuseVisibleEndpoint = Boolean(requestedProviderKey) || !apiKey || Boolean(input.keyEnv?.trim());
|
|
2997
|
+
const providerKey = requestedProviderKey ?? (canReuseVisibleEndpoint ? findProviderConfigKeyByVisibleEndpoint(providers, {
|
|
2970
2998
|
providerName,
|
|
2971
2999
|
baseUrl,
|
|
2972
3000
|
apiMode,
|
|
2973
3001
|
keyEnv: input.keyEnv
|
|
2974
|
-
}) ?? uniqueProviderConfigKey(
|
|
3002
|
+
}) : null) ?? uniqueProviderConfigKey(
|
|
2975
3003
|
providerConfigKeyBase(void 0, providerName, baseUrl),
|
|
2976
3004
|
providers
|
|
2977
3005
|
);
|
|
2978
3006
|
const entry = toRecord(providers[providerKey]);
|
|
2979
3007
|
const existingKeyEnv = readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
2980
|
-
const
|
|
2981
|
-
|
|
3008
|
+
const keyEnv = input.keyEnv?.trim() || existingKeyEnv || (apiKey ? await buildUniqueApiKeyEnvName(profileName, config, void 0, {
|
|
3009
|
+
providerName,
|
|
3010
|
+
modelId: "PROVIDER",
|
|
3011
|
+
providerKey,
|
|
3012
|
+
apiKey
|
|
3013
|
+
}) : void 0);
|
|
2982
3014
|
if (apiKey && keyEnv) {
|
|
2983
3015
|
await writeHermesEnvValue(profileName, keyEnv, apiKey);
|
|
2984
3016
|
}
|
|
@@ -3020,6 +3052,9 @@ async function deleteHermesModelProviderConfig(providerKey, profileName = "defau
|
|
|
3020
3052
|
if (!Object.prototype.hasOwnProperty.call(providers, normalizedProviderKey)) {
|
|
3021
3053
|
throw new Error(`provider "${normalizedProviderKey}" is not configured`);
|
|
3022
3054
|
}
|
|
3055
|
+
const removedKeyEnv = providerCredentialEnvKey(
|
|
3056
|
+
toRecord(providers[normalizedProviderKey])
|
|
3057
|
+
);
|
|
3023
3058
|
delete providers[normalizedProviderKey];
|
|
3024
3059
|
const backupPath = await writeHermesConfigDocument({
|
|
3025
3060
|
configPath,
|
|
@@ -3027,6 +3062,7 @@ async function deleteHermesModelProviderConfig(providerKey, profileName = "defau
|
|
|
3027
3062
|
config,
|
|
3028
3063
|
existingRaw
|
|
3029
3064
|
});
|
|
3065
|
+
await deleteHermesEnvValueIfUnreferenced(profileName, config, removedKeyEnv);
|
|
3030
3066
|
return {
|
|
3031
3067
|
...await listHermesModelConfigs(profileName, configPath),
|
|
3032
3068
|
backupPath,
|
|
@@ -3146,6 +3182,7 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3146
3182
|
);
|
|
3147
3183
|
}
|
|
3148
3184
|
const providers = ensureProvidersRecordWithLegacyMigration(config);
|
|
3185
|
+
const removedProviderKeyEnvs = /* @__PURE__ */ new Set();
|
|
3149
3186
|
for (const [key, rawEntry] of Object.entries(providers)) {
|
|
3150
3187
|
const entry = providerConfigToLegacyEntry(key, toRecord(rawEntry));
|
|
3151
3188
|
if (!entry || !providerEntryMatchesManagedModel(entry, deletingModel)) {
|
|
@@ -3155,6 +3192,10 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3155
3192
|
if (nextEntry) {
|
|
3156
3193
|
providers[key] = nextEntry;
|
|
3157
3194
|
} else {
|
|
3195
|
+
const keyEnv = providerCredentialEnvKey(toRecord(rawEntry));
|
|
3196
|
+
if (keyEnv) {
|
|
3197
|
+
removedProviderKeyEnvs.add(keyEnv);
|
|
3198
|
+
}
|
|
3158
3199
|
delete providers[key];
|
|
3159
3200
|
}
|
|
3160
3201
|
}
|
|
@@ -3200,6 +3241,9 @@ async function deleteHermesModelConfig(input, profileName = "default", configPat
|
|
|
3200
3241
|
config,
|
|
3201
3242
|
existingRaw
|
|
3202
3243
|
});
|
|
3244
|
+
for (const keyEnv of removedProviderKeyEnvs) {
|
|
3245
|
+
await deleteHermesEnvValueIfUnreferenced(profileName, config, keyEnv);
|
|
3246
|
+
}
|
|
3203
3247
|
const listed = await listHermesModelConfigs(profileName, configPath);
|
|
3204
3248
|
return {
|
|
3205
3249
|
...listed,
|
|
@@ -6694,10 +6738,72 @@ function parseEnvReference(value) {
|
|
|
6694
6738
|
const match = /^\$\{(?<key>[A-Za-z_][A-Za-z0-9_]*)\}$/u.exec(value.trim());
|
|
6695
6739
|
return match?.groups?.key;
|
|
6696
6740
|
}
|
|
6741
|
+
function providerCredentialEnvKey(entry) {
|
|
6742
|
+
return readString2(entry.key_env) ?? parseEnvReference(readString2(entry.api_key));
|
|
6743
|
+
}
|
|
6697
6744
|
function buildApiKeyEnvName(providerName, modelId) {
|
|
6698
6745
|
const base = `${providerName || modelId}`.trim().toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
|
|
6699
6746
|
return `HERMES_${base || "MODEL"}_API_KEY`;
|
|
6700
6747
|
}
|
|
6748
|
+
async function buildUniqueApiKeyEnvName(profileName, config, env, input) {
|
|
6749
|
+
const envValues = env ?? await readHermesEnvFile(profileName);
|
|
6750
|
+
const configReferences = collectEnvReferenceKeys(config);
|
|
6751
|
+
const reserved = /* @__PURE__ */ new Set([...Object.keys(envValues), ...configReferences]);
|
|
6752
|
+
const candidates = [
|
|
6753
|
+
buildApiKeyEnvName(input.providerName, input.modelId),
|
|
6754
|
+
buildApiKeyEnvName(input.providerKey, input.modelId)
|
|
6755
|
+
];
|
|
6756
|
+
for (const candidate of uniqueStrings(candidates)) {
|
|
6757
|
+
if (input.apiKey && !configReferences.has(candidate) && envValues[candidate]?.trim() === input.apiKey.trim()) {
|
|
6758
|
+
return candidate;
|
|
6759
|
+
}
|
|
6760
|
+
if (!reserved.has(candidate)) {
|
|
6761
|
+
return candidate;
|
|
6762
|
+
}
|
|
6763
|
+
}
|
|
6764
|
+
const base = buildApiKeyEnvName(input.providerKey, input.modelId);
|
|
6765
|
+
let index = 2;
|
|
6766
|
+
while (reserved.has(apiKeyEnvNameWithSuffix(base, index))) {
|
|
6767
|
+
index += 1;
|
|
6768
|
+
}
|
|
6769
|
+
return apiKeyEnvNameWithSuffix(base, index);
|
|
6770
|
+
}
|
|
6771
|
+
function reservedHermesEnvKeys(env, config) {
|
|
6772
|
+
return /* @__PURE__ */ new Set([...Object.keys(env), ...collectEnvReferenceKeys(config)]);
|
|
6773
|
+
}
|
|
6774
|
+
function collectEnvReferenceKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
6775
|
+
if (typeof value === "string") {
|
|
6776
|
+
const key = parseEnvReference(value);
|
|
6777
|
+
if (key) {
|
|
6778
|
+
keys.add(key);
|
|
6779
|
+
}
|
|
6780
|
+
return keys;
|
|
6781
|
+
}
|
|
6782
|
+
if (Array.isArray(value)) {
|
|
6783
|
+
for (const item of value) {
|
|
6784
|
+
collectEnvReferenceKeys(item, keys);
|
|
6785
|
+
}
|
|
6786
|
+
return keys;
|
|
6787
|
+
}
|
|
6788
|
+
if (value && typeof value === "object") {
|
|
6789
|
+
for (const item of Object.values(value)) {
|
|
6790
|
+
collectEnvReferenceKeys(item, keys);
|
|
6791
|
+
}
|
|
6792
|
+
}
|
|
6793
|
+
return keys;
|
|
6794
|
+
}
|
|
6795
|
+
function uniqueStrings(values) {
|
|
6796
|
+
return [...new Set(values.filter((value) => value.trim()))];
|
|
6797
|
+
}
|
|
6798
|
+
function apiKeyEnvNameWithSuffix(base, index) {
|
|
6799
|
+
return base.endsWith("_API_KEY") ? `${base.slice(0, -"_API_KEY".length)}_${index}_API_KEY` : `${base}_${index}`;
|
|
6800
|
+
}
|
|
6801
|
+
async function deleteHermesEnvValueIfUnreferenced(profileName, config, key) {
|
|
6802
|
+
if (!key || collectEnvReferenceKeys(config).has(key)) {
|
|
6803
|
+
return;
|
|
6804
|
+
}
|
|
6805
|
+
await deleteHermesEnvValue(profileName, key);
|
|
6806
|
+
}
|
|
6701
6807
|
function formatEnvValue(value) {
|
|
6702
6808
|
return `"${value.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"').replace(/\n/gu, "\\n")}"`;
|
|
6703
6809
|
}
|
|
@@ -7109,7 +7215,7 @@ function isConversationMissingError(error) {
|
|
|
7109
7215
|
}
|
|
7110
7216
|
|
|
7111
7217
|
// src/constants.ts
|
|
7112
|
-
var LINK_VERSION = "0.
|
|
7218
|
+
var LINK_VERSION = "0.8.0";
|
|
7113
7219
|
var LINK_COMMAND = "hermeslink";
|
|
7114
7220
|
var LINK_DEFAULT_PORT = 52379;
|
|
7115
7221
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -12697,9 +12803,115 @@ function isNodeError6(error, code) {
|
|
|
12697
12803
|
}
|
|
12698
12804
|
|
|
12699
12805
|
// src/conversations/profile-runtime.ts
|
|
12806
|
+
import { stat as stat8 } from "fs/promises";
|
|
12807
|
+
|
|
12808
|
+
// src/hermes/model-provider-disconnects.ts
|
|
12700
12809
|
import { stat as stat7 } from "fs/promises";
|
|
12701
|
-
|
|
12810
|
+
import os5 from "os";
|
|
12811
|
+
import path12 from "path";
|
|
12812
|
+
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
12813
|
+
const profileKey = normalizeProfileName3(profileName);
|
|
12814
|
+
const provider = providerKey.trim();
|
|
12815
|
+
if (!provider) {
|
|
12816
|
+
return;
|
|
12817
|
+
}
|
|
12818
|
+
const credentialMtimeMs = await readProviderCredentialMtimeMs(
|
|
12819
|
+
profileKey,
|
|
12820
|
+
provider
|
|
12821
|
+
);
|
|
12822
|
+
await updateJsonFile(
|
|
12823
|
+
disconnectedProvidersPath(paths),
|
|
12824
|
+
(current) => {
|
|
12825
|
+
const store = normalizeStore(current);
|
|
12826
|
+
const profiles = { ...store.profiles ?? {} };
|
|
12827
|
+
const profile = profiles[profileKey] ?? {};
|
|
12828
|
+
profiles[profileKey] = {
|
|
12829
|
+
...profile,
|
|
12830
|
+
providers: {
|
|
12831
|
+
...profile.providers ?? {},
|
|
12832
|
+
[provider]: {
|
|
12833
|
+
disconnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12834
|
+
credentialMtimeMs
|
|
12835
|
+
}
|
|
12836
|
+
}
|
|
12837
|
+
};
|
|
12838
|
+
return { ...store, profiles };
|
|
12839
|
+
}
|
|
12840
|
+
);
|
|
12841
|
+
}
|
|
12842
|
+
async function readDisconnectedAuthBackedModelProviderKeys(paths, profileName) {
|
|
12843
|
+
const store = normalizeStore(
|
|
12844
|
+
await readJsonFile(
|
|
12845
|
+
disconnectedProvidersPath(paths)
|
|
12846
|
+
).catch(() => null)
|
|
12847
|
+
);
|
|
12848
|
+
const profile = store.profiles?.[normalizeProfileName3(profileName)];
|
|
12849
|
+
const providers = profile?.providers ?? {};
|
|
12850
|
+
const active = /* @__PURE__ */ new Set();
|
|
12851
|
+
for (const [provider, entry] of Object.entries(providers)) {
|
|
12852
|
+
if (await isDisconnectMarkerActive(profileName, provider, entry)) {
|
|
12853
|
+
active.add(provider);
|
|
12854
|
+
}
|
|
12855
|
+
}
|
|
12856
|
+
return active;
|
|
12857
|
+
}
|
|
12858
|
+
async function isDisconnectMarkerActive(profileName, providerKey, entry) {
|
|
12859
|
+
const currentMtimeMs = await readProviderCredentialMtimeMs(
|
|
12860
|
+
profileName,
|
|
12861
|
+
providerKey
|
|
12862
|
+
);
|
|
12863
|
+
if (entry.credentialMtimeMs === null || !Number.isFinite(entry.credentialMtimeMs)) {
|
|
12864
|
+
return currentMtimeMs === null;
|
|
12865
|
+
}
|
|
12866
|
+
if (currentMtimeMs === null || !Number.isFinite(currentMtimeMs)) {
|
|
12867
|
+
return true;
|
|
12868
|
+
}
|
|
12869
|
+
return currentMtimeMs <= entry.credentialMtimeMs + 1e3;
|
|
12870
|
+
}
|
|
12871
|
+
async function readProviderCredentialMtimeMs(profileName, providerKey) {
|
|
12872
|
+
for (const filePath of credentialFilesForProvider(profileName, providerKey)) {
|
|
12873
|
+
const stats = await stat7(filePath).catch((error) => {
|
|
12874
|
+
if (isNodeError7(error, "ENOENT")) {
|
|
12875
|
+
return null;
|
|
12876
|
+
}
|
|
12877
|
+
throw error;
|
|
12878
|
+
});
|
|
12879
|
+
if (stats) {
|
|
12880
|
+
return stats.mtimeMs;
|
|
12881
|
+
}
|
|
12882
|
+
}
|
|
12883
|
+
return null;
|
|
12884
|
+
}
|
|
12885
|
+
function credentialFilesForProvider(profileName, providerKey) {
|
|
12886
|
+
const profileDir = resolveHermesProfileDir(normalizeProfileName3(profileName));
|
|
12887
|
+
switch (providerKey) {
|
|
12888
|
+
case "qwen-oauth":
|
|
12889
|
+
return [path12.join(os5.homedir(), ".qwen", "oauth_creds.json")];
|
|
12890
|
+
case "google-gemini-cli":
|
|
12891
|
+
return [path12.join(profileDir, "auth", "google_oauth.json")];
|
|
12892
|
+
default:
|
|
12893
|
+
return [path12.join(profileDir, "auth.json")];
|
|
12894
|
+
}
|
|
12895
|
+
}
|
|
12896
|
+
function disconnectedProvidersPath(paths) {
|
|
12897
|
+
return path12.join(paths.homeDir, "model-provider-disconnects.json");
|
|
12898
|
+
}
|
|
12899
|
+
function normalizeStore(value) {
|
|
12900
|
+
return {
|
|
12901
|
+
version: 1,
|
|
12902
|
+
profiles: value?.profiles && typeof value.profiles === "object" ? value.profiles : {}
|
|
12903
|
+
};
|
|
12904
|
+
}
|
|
12702
12905
|
function normalizeProfileName3(profileName) {
|
|
12906
|
+
return profileName.trim() || "default";
|
|
12907
|
+
}
|
|
12908
|
+
function isNodeError7(error, code) {
|
|
12909
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
12910
|
+
}
|
|
12911
|
+
|
|
12912
|
+
// src/conversations/profile-runtime.ts
|
|
12913
|
+
var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/u;
|
|
12914
|
+
function normalizeProfileName4(profileName) {
|
|
12703
12915
|
const value = profileName?.trim() || "default";
|
|
12704
12916
|
if (!PROFILE_NAME_PATTERN3.test(value)) {
|
|
12705
12917
|
throw new Error("invalid profile name");
|
|
@@ -12710,7 +12922,7 @@ function fallbackProfileDisplayName(profileName) {
|
|
|
12710
12922
|
return profileName === "default" ? "Default" : profileName;
|
|
12711
12923
|
}
|
|
12712
12924
|
async function resolveConversationProfileTarget(paths, profileName) {
|
|
12713
|
-
const normalized =
|
|
12925
|
+
const normalized = normalizeProfileName4(profileName);
|
|
12714
12926
|
const identity = await ensureProfileIdentityForRuntime(paths, normalized);
|
|
12715
12927
|
return {
|
|
12716
12928
|
profileUid: identity.profileUid,
|
|
@@ -12720,7 +12932,7 @@ async function resolveConversationProfileTarget(paths, profileName) {
|
|
|
12720
12932
|
};
|
|
12721
12933
|
}
|
|
12722
12934
|
async function readCurrentConversationRuntime(paths, manifest) {
|
|
12723
|
-
const profileName =
|
|
12935
|
+
const profileName = normalizeProfileName4(
|
|
12724
12936
|
manifest?.profile_name_snapshot ?? manifest?.profile
|
|
12725
12937
|
);
|
|
12726
12938
|
const identity = await ensureProfileIdentityForRuntime(paths, profileName);
|
|
@@ -12732,7 +12944,7 @@ async function readCurrentConversationRuntime(paths, manifest) {
|
|
|
12732
12944
|
contextLength: void 0,
|
|
12733
12945
|
reasoningEffort: void 0
|
|
12734
12946
|
}));
|
|
12735
|
-
const configuredDefaultModel = modelConfig.model ? await findConfiguredModel(profileName, modelConfig.model) : void 0;
|
|
12947
|
+
const configuredDefaultModel = modelConfig.model ? await findConfiguredModel(profileName, modelConfig.model, {}, paths) : void 0;
|
|
12736
12948
|
const override = manifest?.model_override;
|
|
12737
12949
|
const overrideModel = override?.id?.trim();
|
|
12738
12950
|
const overrideProvider = override?.provider_name?.trim() || override?.provider?.trim();
|
|
@@ -12760,7 +12972,7 @@ async function readProfilePresentation(paths, profileName) {
|
|
|
12760
12972
|
};
|
|
12761
12973
|
}
|
|
12762
12974
|
async function readConversationProfileSummary(paths, manifest) {
|
|
12763
|
-
const profileName =
|
|
12975
|
+
const profileName = normalizeProfileName4(
|
|
12764
12976
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
12765
12977
|
);
|
|
12766
12978
|
const presentation = await readProfilePresentation(paths, profileName).catch(
|
|
@@ -12831,8 +13043,8 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
|
|
|
12831
13043
|
async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
12832
13044
|
const profilePath = resolveHermesProfileDir(profileName);
|
|
12833
13045
|
if (profileName !== "default") {
|
|
12834
|
-
const exists = await
|
|
12835
|
-
if (
|
|
13046
|
+
const exists = await stat8(profilePath).then((value) => value.isDirectory()).catch((error) => {
|
|
13047
|
+
if (isNodeError8(error, "ENOENT")) {
|
|
12836
13048
|
return false;
|
|
12837
13049
|
}
|
|
12838
13050
|
throw error;
|
|
@@ -12850,12 +13062,18 @@ async function ensureProfileIdentityForRuntime(paths, profileName) {
|
|
|
12850
13062
|
profilePath
|
|
12851
13063
|
});
|
|
12852
13064
|
}
|
|
12853
|
-
async function findConfiguredModel(profileName, modelId, selector = {}) {
|
|
13065
|
+
async function findConfiguredModel(profileName, modelId, selector = {}, paths) {
|
|
12854
13066
|
const result = await listHermesModelConfigs(profileName).catch(() => null);
|
|
13067
|
+
const disconnectedProviders = paths ? await readDisconnectedAuthBackedModelProviderKeys(paths, profileName).catch(
|
|
13068
|
+
() => /* @__PURE__ */ new Set()
|
|
13069
|
+
) : /* @__PURE__ */ new Set();
|
|
12855
13070
|
return result?.models.find((model) => {
|
|
12856
13071
|
if (model.id !== modelId) {
|
|
12857
13072
|
return false;
|
|
12858
13073
|
}
|
|
13074
|
+
if (disconnectedProviders.has(model.provider)) {
|
|
13075
|
+
return false;
|
|
13076
|
+
}
|
|
12859
13077
|
if (selector.provider && model.provider !== selector.provider) {
|
|
12860
13078
|
return false;
|
|
12861
13079
|
}
|
|
@@ -12875,7 +13093,7 @@ function displayNameForProfile(profile) {
|
|
|
12875
13093
|
const custom = profile.displayName?.trim();
|
|
12876
13094
|
return custom || fallbackProfileDisplayName(profile.profileName);
|
|
12877
13095
|
}
|
|
12878
|
-
function
|
|
13096
|
+
function isNodeError8(error, code) {
|
|
12879
13097
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
12880
13098
|
}
|
|
12881
13099
|
|
|
@@ -13295,9 +13513,15 @@ var ConversationCommandHandlers = class {
|
|
|
13295
13513
|
}
|
|
13296
13514
|
async modelStatusMessage(manifest, language = "zh-CN") {
|
|
13297
13515
|
const runtime = await readCurrentConversationRuntime(this.deps.paths, manifest);
|
|
13298
|
-
const configured = await listHermesModelConfigs(runtime.profileName).
|
|
13299
|
-
() =>
|
|
13300
|
-
|
|
13516
|
+
const configured = await listHermesModelConfigs(runtime.profileName).then(
|
|
13517
|
+
async (result) => filterHermesModelConfigsByProviderKeys(
|
|
13518
|
+
result,
|
|
13519
|
+
await readDisconnectedAuthBackedModelProviderKeys(
|
|
13520
|
+
this.deps.paths,
|
|
13521
|
+
runtime.profileName
|
|
13522
|
+
)
|
|
13523
|
+
)
|
|
13524
|
+
).catch(() => null);
|
|
13301
13525
|
const lines = [
|
|
13302
13526
|
commandText(
|
|
13303
13527
|
language,
|
|
@@ -13408,7 +13632,12 @@ var ConversationCommandHandlers = class {
|
|
|
13408
13632
|
response: await this.modelStatusMessage(input.manifest, input.language)
|
|
13409
13633
|
});
|
|
13410
13634
|
}
|
|
13411
|
-
const configuredModel = await findConfiguredModel(
|
|
13635
|
+
const configuredModel = await findConfiguredModel(
|
|
13636
|
+
runtime.profileName,
|
|
13637
|
+
model,
|
|
13638
|
+
{},
|
|
13639
|
+
this.deps.paths
|
|
13640
|
+
);
|
|
13412
13641
|
if (!configuredModel) {
|
|
13413
13642
|
return this.deps.appendCommandResult({
|
|
13414
13643
|
manifest: input.manifest,
|
|
@@ -13606,7 +13835,9 @@ var ConversationCommandHandlers = class {
|
|
|
13606
13835
|
const reasoningEffort = arg;
|
|
13607
13836
|
const configuredModel = await findConfiguredModel(
|
|
13608
13837
|
runtime.profileName,
|
|
13609
|
-
runtime.model
|
|
13838
|
+
runtime.model,
|
|
13839
|
+
{},
|
|
13840
|
+
this.deps.paths
|
|
13610
13841
|
);
|
|
13611
13842
|
if (!isReasoningEffortSupported(configuredModel, reasoningEffort)) {
|
|
13612
13843
|
return this.deps.appendCommandResult({
|
|
@@ -13794,7 +14025,7 @@ function commandText(language, zh, en) {
|
|
|
13794
14025
|
|
|
13795
14026
|
// src/conversations/delivery-staging.ts
|
|
13796
14027
|
import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
|
|
13797
|
-
import
|
|
14028
|
+
import path13 from "path";
|
|
13798
14029
|
async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
|
|
13799
14030
|
const directory = deliveryStagingRunDir(paths, conversationId, runId);
|
|
13800
14031
|
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
@@ -13807,14 +14038,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
|
|
|
13807
14038
|
});
|
|
13808
14039
|
}
|
|
13809
14040
|
function deliveryStagingRunDir(paths, conversationId, runId) {
|
|
13810
|
-
return
|
|
14041
|
+
return path13.join(
|
|
13811
14042
|
deliveryStagingConversationDir(paths, conversationId),
|
|
13812
14043
|
safePathSegment(runId, "run")
|
|
13813
14044
|
);
|
|
13814
14045
|
}
|
|
13815
14046
|
function deliveryStagingConversationDir(paths, conversationId) {
|
|
13816
14047
|
assertValidConversationId(conversationId);
|
|
13817
|
-
return
|
|
14048
|
+
return path13.join(paths.conversationsDir, conversationId, "delivery-staging");
|
|
13818
14049
|
}
|
|
13819
14050
|
function safePathSegment(value, fallback) {
|
|
13820
14051
|
const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
|
|
@@ -13824,7 +14055,7 @@ function safePathSegment(value, fallback) {
|
|
|
13824
14055
|
// src/conversations/conversation-archive-plans.ts
|
|
13825
14056
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
13826
14057
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
13827
|
-
import
|
|
14058
|
+
import path14 from "path";
|
|
13828
14059
|
var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
|
|
13829
14060
|
var ConversationArchivePlanStore = class {
|
|
13830
14061
|
constructor(paths) {
|
|
@@ -13867,10 +14098,10 @@ var ConversationArchivePlanStore = class {
|
|
|
13867
14098
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
13868
14099
|
}
|
|
13869
14100
|
plansDir() {
|
|
13870
|
-
return
|
|
14101
|
+
return path14.join(this.paths.indexesDir, "conversation-archive-plans");
|
|
13871
14102
|
}
|
|
13872
14103
|
planPath(planId) {
|
|
13873
|
-
return
|
|
14104
|
+
return path14.join(this.plansDir(), `${planId}.json`);
|
|
13874
14105
|
}
|
|
13875
14106
|
};
|
|
13876
14107
|
function normalizePlanId(planId) {
|
|
@@ -13888,7 +14119,7 @@ function normalizePlanId(planId) {
|
|
|
13888
14119
|
// src/conversations/conversation-clear-plans.ts
|
|
13889
14120
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
13890
14121
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
13891
|
-
import
|
|
14122
|
+
import path15 from "path";
|
|
13892
14123
|
var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
|
|
13893
14124
|
var ConversationClearPlanStore = class {
|
|
13894
14125
|
constructor(paths) {
|
|
@@ -13932,10 +14163,10 @@ var ConversationClearPlanStore = class {
|
|
|
13932
14163
|
await writeJsonFile(this.planPath(normalizedPlanId), plan);
|
|
13933
14164
|
}
|
|
13934
14165
|
plansDir() {
|
|
13935
|
-
return
|
|
14166
|
+
return path15.join(this.paths.indexesDir, "conversation-clear-plans");
|
|
13936
14167
|
}
|
|
13937
14168
|
planPath(planId) {
|
|
13938
|
-
return
|
|
14169
|
+
return path15.join(this.plansDir(), `${planId}.json`);
|
|
13939
14170
|
}
|
|
13940
14171
|
};
|
|
13941
14172
|
function normalizePlanId2(planId) {
|
|
@@ -14789,20 +15020,20 @@ function readAttachmentWaveform(attachment) {
|
|
|
14789
15020
|
}
|
|
14790
15021
|
|
|
14791
15022
|
// src/hermes/session-title.ts
|
|
14792
|
-
import { stat as
|
|
14793
|
-
import
|
|
15023
|
+
import { stat as stat9 } from "fs/promises";
|
|
15024
|
+
import path16 from "path";
|
|
14794
15025
|
async function readHermesSessionTitle(sessionId, paths, profileName) {
|
|
14795
15026
|
const trimmedSessionId = sessionId.trim();
|
|
14796
15027
|
if (!trimmedSessionId) {
|
|
14797
15028
|
return void 0;
|
|
14798
15029
|
}
|
|
14799
15030
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
14800
|
-
const dbPath =
|
|
15031
|
+
const dbPath = path16.join(
|
|
14801
15032
|
resolveHermesProfileDir(resolvedProfileName),
|
|
14802
15033
|
"state.db"
|
|
14803
15034
|
);
|
|
14804
|
-
const exists = await
|
|
14805
|
-
if (
|
|
15035
|
+
const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
15036
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
14806
15037
|
return false;
|
|
14807
15038
|
}
|
|
14808
15039
|
throw error;
|
|
@@ -14818,12 +15049,12 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
|
|
|
14818
15049
|
return void 0;
|
|
14819
15050
|
}
|
|
14820
15051
|
const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
|
|
14821
|
-
const dbPath =
|
|
15052
|
+
const dbPath = path16.join(
|
|
14822
15053
|
resolveHermesProfileDir(resolvedProfileName),
|
|
14823
15054
|
"state.db"
|
|
14824
15055
|
);
|
|
14825
|
-
const exists = await
|
|
14826
|
-
if (
|
|
15056
|
+
const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
15057
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
14827
15058
|
return false;
|
|
14828
15059
|
}
|
|
14829
15060
|
throw error;
|
|
@@ -14886,7 +15117,7 @@ function readCompressionTipFromStateDb(dbPath, sessionId) {
|
|
|
14886
15117
|
db?.close();
|
|
14887
15118
|
}
|
|
14888
15119
|
}
|
|
14889
|
-
function
|
|
15120
|
+
function isNodeError9(error, code) {
|
|
14890
15121
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
14891
15122
|
}
|
|
14892
15123
|
function isValidProfileName(value) {
|
|
@@ -15250,8 +15481,8 @@ function stripCompressionTitleSuffix(value) {
|
|
|
15250
15481
|
}
|
|
15251
15482
|
|
|
15252
15483
|
// src/conversations/history-builder.ts
|
|
15253
|
-
import { readFile as readFile8, stat as
|
|
15254
|
-
import
|
|
15484
|
+
import { readFile as readFile8, stat as stat10 } from "fs/promises";
|
|
15485
|
+
import path17 from "path";
|
|
15255
15486
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
15256
15487
|
var HERMES_HISTORY_COLUMNS = [
|
|
15257
15488
|
"role",
|
|
@@ -15323,13 +15554,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
15323
15554
|
}
|
|
15324
15555
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
15325
15556
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
15326
|
-
const dbPath =
|
|
15557
|
+
const dbPath = path17.join(profileDir, "state.db");
|
|
15327
15558
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
15328
15559
|
sessionsDir: value.sessionsDir,
|
|
15329
15560
|
configured: value.configured,
|
|
15330
15561
|
configError: false
|
|
15331
15562
|
})).catch(() => ({
|
|
15332
|
-
sessionsDir:
|
|
15563
|
+
sessionsDir: path17.join(profileDir, "sessions"),
|
|
15333
15564
|
configured: false,
|
|
15334
15565
|
configError: true
|
|
15335
15566
|
}));
|
|
@@ -15374,8 +15605,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
15374
15605
|
};
|
|
15375
15606
|
}
|
|
15376
15607
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
15377
|
-
const exists = await
|
|
15378
|
-
if (
|
|
15608
|
+
const exists = await stat10(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
15609
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
15379
15610
|
return false;
|
|
15380
15611
|
}
|
|
15381
15612
|
throw error;
|
|
@@ -15492,7 +15723,7 @@ async function readHermesTranscriptFilesHistory(sessionsDir, sessionId) {
|
|
|
15492
15723
|
async function readFirstExistingFile(paths) {
|
|
15493
15724
|
for (const filePath of paths) {
|
|
15494
15725
|
const raw = await readFile8(filePath, "utf8").catch((error) => {
|
|
15495
|
-
if (
|
|
15726
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
15496
15727
|
return null;
|
|
15497
15728
|
}
|
|
15498
15729
|
throw error;
|
|
@@ -15505,8 +15736,8 @@ async function readFirstExistingFile(paths) {
|
|
|
15505
15736
|
}
|
|
15506
15737
|
function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
|
|
15507
15738
|
return [
|
|
15508
|
-
|
|
15509
|
-
|
|
15739
|
+
path17.join(sessionsDir, `session_${sessionId}.${extension}`),
|
|
15740
|
+
path17.join(sessionsDir, `${sessionId}.${extension}`)
|
|
15510
15741
|
];
|
|
15511
15742
|
}
|
|
15512
15743
|
function readHistoryRows(dbPath, sessionId) {
|
|
@@ -15706,7 +15937,7 @@ function readTableColumns(db, table) {
|
|
|
15706
15937
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
15707
15938
|
);
|
|
15708
15939
|
}
|
|
15709
|
-
function
|
|
15940
|
+
function isNodeError10(error, code) {
|
|
15710
15941
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
15711
15942
|
}
|
|
15712
15943
|
function isValidProfileName2(value) {
|
|
@@ -17361,13 +17592,13 @@ var ConversationOrchestrationCoordinator = class {
|
|
|
17361
17592
|
};
|
|
17362
17593
|
}
|
|
17363
17594
|
async ensureManifestProfile(manifest, requestedProfileName) {
|
|
17364
|
-
const existingProfileName =
|
|
17595
|
+
const existingProfileName = normalizeProfileName4(
|
|
17365
17596
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
17366
17597
|
);
|
|
17367
17598
|
const hasExistingProfile = Boolean(
|
|
17368
17599
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
17369
17600
|
);
|
|
17370
|
-
const nextProfileName = requestedProfileName ?
|
|
17601
|
+
const nextProfileName = requestedProfileName ? normalizeProfileName4(requestedProfileName) : existingProfileName;
|
|
17371
17602
|
if ((!requestedProfileName || nextProfileName === existingProfileName) && hasExistingProfile && manifest.profile_uid) {
|
|
17372
17603
|
return manifest;
|
|
17373
17604
|
}
|
|
@@ -18697,7 +18928,7 @@ var ContextCompressionCoordinator = class {
|
|
|
18697
18928
|
continue;
|
|
18698
18929
|
}
|
|
18699
18930
|
if (snapshot.runs.some(
|
|
18700
|
-
(run) => run.status === "running" && run.kind !== "command" &&
|
|
18931
|
+
(run) => run.status === "running" && run.kind !== "command" && normalizeProfileName5(run.profile_name_snapshot ?? run.profile) === profileName
|
|
18701
18932
|
)) {
|
|
18702
18933
|
return true;
|
|
18703
18934
|
}
|
|
@@ -18707,7 +18938,7 @@ var ContextCompressionCoordinator = class {
|
|
|
18707
18938
|
async profileNameForRun(conversationId, runId) {
|
|
18708
18939
|
const snapshot = await this.deps.store.readSnapshot(conversationId).catch(() => null);
|
|
18709
18940
|
const run = snapshot?.runs.find((item) => item.id === runId);
|
|
18710
|
-
return
|
|
18941
|
+
return normalizeProfileName5(run?.profile_name_snapshot ?? run?.profile);
|
|
18711
18942
|
}
|
|
18712
18943
|
};
|
|
18713
18944
|
function createContextCompressionMarker(input) {
|
|
@@ -18908,7 +19139,7 @@ function normalizeFocus(value) {
|
|
|
18908
19139
|
function normalizeLanguage2(value) {
|
|
18909
19140
|
return value === "en" ? "en" : "zh-CN";
|
|
18910
19141
|
}
|
|
18911
|
-
function
|
|
19142
|
+
function normalizeProfileName5(value) {
|
|
18912
19143
|
return value?.trim() || "default";
|
|
18913
19144
|
}
|
|
18914
19145
|
function localizedText(language, zh, en) {
|
|
@@ -18936,7 +19167,7 @@ import {
|
|
|
18936
19167
|
rm as rm5,
|
|
18937
19168
|
writeFile as writeFile2
|
|
18938
19169
|
} from "fs/promises";
|
|
18939
|
-
import
|
|
19170
|
+
import path18 from "path";
|
|
18940
19171
|
var ConversationStore = class {
|
|
18941
19172
|
constructor(paths) {
|
|
18942
19173
|
this.paths = paths;
|
|
@@ -18950,7 +19181,7 @@ var ConversationStore = class {
|
|
|
18950
19181
|
const entries = await readdir5(this.paths.conversationsDir, {
|
|
18951
19182
|
withFileTypes: true
|
|
18952
19183
|
}).catch((error) => {
|
|
18953
|
-
if (
|
|
19184
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
18954
19185
|
return [];
|
|
18955
19186
|
}
|
|
18956
19187
|
throw error;
|
|
@@ -19019,7 +19250,7 @@ var ConversationStore = class {
|
|
|
19019
19250
|
await this.readManifest(conversationId);
|
|
19020
19251
|
const raw = await readFile9(this.eventsPath(conversationId), "utf8").catch(
|
|
19021
19252
|
(error) => {
|
|
19022
|
-
if (
|
|
19253
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
19023
19254
|
return "";
|
|
19024
19255
|
}
|
|
19025
19256
|
throw error;
|
|
@@ -19067,40 +19298,40 @@ var ConversationStore = class {
|
|
|
19067
19298
|
return manifest != null && manifest.status !== "deleted_soft";
|
|
19068
19299
|
}
|
|
19069
19300
|
removeConversationAttachments(conversationId) {
|
|
19070
|
-
return rm5(
|
|
19301
|
+
return rm5(path18.join(this.conversationDir(conversationId), "attachments"), {
|
|
19071
19302
|
recursive: true,
|
|
19072
19303
|
force: true
|
|
19073
19304
|
});
|
|
19074
19305
|
}
|
|
19075
19306
|
conversationDir(conversationId) {
|
|
19076
19307
|
assertValidConversationId(conversationId);
|
|
19077
|
-
return
|
|
19308
|
+
return path18.join(this.paths.conversationsDir, conversationId);
|
|
19078
19309
|
}
|
|
19079
19310
|
manifestPath(conversationId) {
|
|
19080
|
-
return
|
|
19311
|
+
return path18.join(this.conversationDir(conversationId), "manifest.json");
|
|
19081
19312
|
}
|
|
19082
19313
|
snapshotPath(conversationId) {
|
|
19083
|
-
return
|
|
19314
|
+
return path18.join(this.conversationDir(conversationId), "snapshot.json");
|
|
19084
19315
|
}
|
|
19085
19316
|
eventsPath(conversationId) {
|
|
19086
|
-
return
|
|
19317
|
+
return path18.join(this.conversationDir(conversationId), "events.ndjson");
|
|
19087
19318
|
}
|
|
19088
19319
|
};
|
|
19089
19320
|
function createEmptySnapshot2() {
|
|
19090
19321
|
return { schema_version: 1, messages: [], runs: [] };
|
|
19091
19322
|
}
|
|
19092
|
-
function
|
|
19323
|
+
function isNodeError11(error, code) {
|
|
19093
19324
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
19094
19325
|
}
|
|
19095
19326
|
|
|
19096
19327
|
// src/conversations/hermes-session-sync.ts
|
|
19097
19328
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
19098
|
-
import { readdir as readdir7, readFile as readFile11, stat as
|
|
19099
|
-
import
|
|
19329
|
+
import { readdir as readdir7, readFile as readFile11, stat as stat12 } from "fs/promises";
|
|
19330
|
+
import path20 from "path";
|
|
19100
19331
|
|
|
19101
19332
|
// src/conversations/delivery-import.ts
|
|
19102
|
-
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as
|
|
19103
|
-
import
|
|
19333
|
+
import { lstat as lstat2, readFile as readFile10, readdir as readdir6, stat as stat11 } from "fs/promises";
|
|
19334
|
+
import path19 from "path";
|
|
19104
19335
|
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
19105
19336
|
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
19106
19337
|
var MAX_DELIVERY_FILES = 50;
|
|
@@ -19171,16 +19402,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
19171
19402
|
".m4a"
|
|
19172
19403
|
]);
|
|
19173
19404
|
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
19174
|
-
const resolvedDir =
|
|
19175
|
-
const relative =
|
|
19176
|
-
if (!relative || relative.startsWith("..") ||
|
|
19405
|
+
const resolvedDir = path19.resolve(stagingDir);
|
|
19406
|
+
const relative = path19.relative(path19.resolve(paths.conversationsDir), resolvedDir);
|
|
19407
|
+
if (!relative || relative.startsWith("..") || path19.isAbsolute(relative)) {
|
|
19177
19408
|
throw new LinkHttpError(
|
|
19178
19409
|
400,
|
|
19179
19410
|
"delivery_staging_invalid",
|
|
19180
19411
|
"delivery staging directory must be inside Hermes Link conversations"
|
|
19181
19412
|
);
|
|
19182
19413
|
}
|
|
19183
|
-
const segments = relative.split(
|
|
19414
|
+
const segments = relative.split(path19.sep);
|
|
19184
19415
|
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
19185
19416
|
throw new LinkHttpError(
|
|
19186
19417
|
400,
|
|
@@ -19196,7 +19427,7 @@ function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
|
19196
19427
|
}
|
|
19197
19428
|
async function collectStagedDeliveryReferences(stagingDir) {
|
|
19198
19429
|
const directoryStat = await lstat2(stagingDir).catch((error) => {
|
|
19199
|
-
if (
|
|
19430
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
19200
19431
|
throw new LinkHttpError(
|
|
19201
19432
|
404,
|
|
19202
19433
|
"delivery_staging_not_found",
|
|
@@ -19216,7 +19447,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
|
|
|
19216
19447
|
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
19217
19448
|
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
19218
19449
|
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
19219
|
-
const sourcePath =
|
|
19450
|
+
const sourcePath = path19.join(stagingDir, entry.name);
|
|
19220
19451
|
const mime = inferMimeType(sourcePath);
|
|
19221
19452
|
return {
|
|
19222
19453
|
path: sourcePath,
|
|
@@ -19376,8 +19607,8 @@ function emptyImportResult(input) {
|
|
|
19376
19607
|
}
|
|
19377
19608
|
async function writeBlobFromFile(deps, conversationId, source) {
|
|
19378
19609
|
const sourcePath = resolveMediaSourcePath(source.path);
|
|
19379
|
-
const fileStat = await
|
|
19380
|
-
if (
|
|
19610
|
+
const fileStat = await stat11(sourcePath).catch((error) => {
|
|
19611
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
19381
19612
|
throw new LinkHttpError(
|
|
19382
19613
|
404,
|
|
19383
19614
|
"media_source_not_found",
|
|
@@ -19402,7 +19633,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
|
|
|
19402
19633
|
}
|
|
19403
19634
|
return deps.writeBlob(conversationId, {
|
|
19404
19635
|
bytes: await readFile10(sourcePath),
|
|
19405
|
-
filename:
|
|
19636
|
+
filename: path19.basename(sourcePath),
|
|
19406
19637
|
mime: source.mime ?? inferMimeType(sourcePath)
|
|
19407
19638
|
});
|
|
19408
19639
|
}
|
|
@@ -19411,11 +19642,11 @@ function describeMediaImportFailure(reference, sourceKey, error) {
|
|
|
19411
19642
|
key: sourceKey,
|
|
19412
19643
|
filename: sanitizeFilename(reference.path, "attachment"),
|
|
19413
19644
|
reason: error instanceof Error ? error.message : String(error),
|
|
19414
|
-
...
|
|
19645
|
+
...isNodeError12(error) && error.code ? { code: error.code } : {}
|
|
19415
19646
|
};
|
|
19416
19647
|
}
|
|
19417
19648
|
function isSupportedDeliveryFilename(filename) {
|
|
19418
|
-
return SUPPORTED_DELIVERY_EXTENSIONS.has(
|
|
19649
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path19.extname(filename).toLowerCase());
|
|
19419
19650
|
}
|
|
19420
19651
|
function readString10(payload, key) {
|
|
19421
19652
|
const value = payload[key];
|
|
@@ -19424,7 +19655,7 @@ function readString10(payload, key) {
|
|
|
19424
19655
|
function toRecord10(value) {
|
|
19425
19656
|
return typeof value === "object" && value !== null ? value : {};
|
|
19426
19657
|
}
|
|
19427
|
-
function
|
|
19658
|
+
function isNodeError12(error, code) {
|
|
19428
19659
|
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
19429
19660
|
}
|
|
19430
19661
|
|
|
@@ -19473,7 +19704,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
|
|
|
19473
19704
|
const candidates = [];
|
|
19474
19705
|
for (const profileName of profileNames) {
|
|
19475
19706
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
19476
|
-
const dbPath =
|
|
19707
|
+
const dbPath = path20.join(profileDir, "state.db");
|
|
19477
19708
|
const sessions = await listProfileSessions(dbPath).catch((error) => {
|
|
19478
19709
|
result.errors.push({
|
|
19479
19710
|
profile: profileName,
|
|
@@ -19589,7 +19820,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
|
|
|
19589
19820
|
const knownHermesSessions = await readKnownHermesSessions(store);
|
|
19590
19821
|
const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
|
|
19591
19822
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
19592
|
-
const dbPath =
|
|
19823
|
+
const dbPath = path20.join(profileDir, "state.db");
|
|
19593
19824
|
const sessions = await listProfileSessionsByIdPrefix(
|
|
19594
19825
|
dbPath,
|
|
19595
19826
|
`cron_${jobId}_`
|
|
@@ -20604,7 +20835,7 @@ async function discoverHermesProfileNames() {
|
|
|
20604
20835
|
const profilesDir = resolveHermesProfilesDir();
|
|
20605
20836
|
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
20606
20837
|
(error) => {
|
|
20607
|
-
if (
|
|
20838
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
20608
20839
|
return [];
|
|
20609
20840
|
}
|
|
20610
20841
|
throw error;
|
|
@@ -20900,10 +21131,10 @@ async function readJsonlMessages(profileName, sessionId) {
|
|
|
20900
21131
|
return [];
|
|
20901
21132
|
}
|
|
20902
21133
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
20903
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
20904
|
-
const transcriptPath =
|
|
21134
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path20.join(profileDir, "sessions"));
|
|
21135
|
+
const transcriptPath = path20.join(sessionsDir, `${sessionId}.jsonl`);
|
|
20905
21136
|
const raw = await readFile11(transcriptPath, "utf8").catch((error) => {
|
|
20906
|
-
if (
|
|
21137
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
20907
21138
|
return "";
|
|
20908
21139
|
}
|
|
20909
21140
|
throw error;
|
|
@@ -21231,8 +21462,8 @@ function quoteIdentifier(value) {
|
|
|
21231
21462
|
return `"${value.replaceAll('"', '""')}"`;
|
|
21232
21463
|
}
|
|
21233
21464
|
async function isFile(filePath) {
|
|
21234
|
-
return
|
|
21235
|
-
if (
|
|
21465
|
+
return stat12(filePath).then((value) => value.isFile()).catch((error) => {
|
|
21466
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
21236
21467
|
return false;
|
|
21237
21468
|
}
|
|
21238
21469
|
throw error;
|
|
@@ -21265,13 +21496,13 @@ function readBoolean(value) {
|
|
|
21265
21496
|
}
|
|
21266
21497
|
return false;
|
|
21267
21498
|
}
|
|
21268
|
-
function
|
|
21499
|
+
function isNodeError13(error, code) {
|
|
21269
21500
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
21270
21501
|
}
|
|
21271
21502
|
|
|
21272
21503
|
// src/conversations/delivery-context.ts
|
|
21273
|
-
import { copyFile, mkdir as mkdir11, stat as
|
|
21274
|
-
import
|
|
21504
|
+
import { copyFile, mkdir as mkdir11, stat as stat13 } from "fs/promises";
|
|
21505
|
+
import path21 from "path";
|
|
21275
21506
|
var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
|
|
21276
21507
|
var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
|
|
21277
21508
|
var MAX_DELIVERY_TOOL_FILES = 50;
|
|
@@ -21343,10 +21574,10 @@ function findDeliveryContext(input) {
|
|
|
21343
21574
|
);
|
|
21344
21575
|
}
|
|
21345
21576
|
async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
21346
|
-
const stagingDir =
|
|
21347
|
-
const conversationsRoot =
|
|
21348
|
-
const relative =
|
|
21349
|
-
if (!relative || relative.startsWith("..") ||
|
|
21577
|
+
const stagingDir = path21.resolve(context.stagingDir);
|
|
21578
|
+
const conversationsRoot = path21.resolve(paths.conversationsDir);
|
|
21579
|
+
const relative = path21.relative(conversationsRoot, stagingDir);
|
|
21580
|
+
if (!relative || relative.startsWith("..") || path21.isAbsolute(relative)) {
|
|
21350
21581
|
throw new LinkHttpError(
|
|
21351
21582
|
400,
|
|
21352
21583
|
"delivery_staging_invalid",
|
|
@@ -21357,13 +21588,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21357
21588
|
const result = { staged: [], skipped: [] };
|
|
21358
21589
|
const usedNames = /* @__PURE__ */ new Set();
|
|
21359
21590
|
for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
|
|
21360
|
-
const sourcePath =
|
|
21591
|
+
const sourcePath = path21.resolve(file.path);
|
|
21361
21592
|
let baseName = sanitizeFilename(
|
|
21362
|
-
file.caption ||
|
|
21593
|
+
file.caption || path21.basename(sourcePath),
|
|
21363
21594
|
`attachment-${index + 1}`
|
|
21364
21595
|
);
|
|
21365
|
-
const sourceExtension =
|
|
21366
|
-
if (!
|
|
21596
|
+
const sourceExtension = path21.extname(sourcePath);
|
|
21597
|
+
if (!path21.extname(baseName) && sourceExtension) {
|
|
21367
21598
|
baseName = `${baseName}${sourceExtension}`;
|
|
21368
21599
|
}
|
|
21369
21600
|
const filename = uniqueStagingFilename(
|
|
@@ -21377,8 +21608,8 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21377
21608
|
});
|
|
21378
21609
|
continue;
|
|
21379
21610
|
}
|
|
21380
|
-
const sourceStat = await
|
|
21381
|
-
if (
|
|
21611
|
+
const sourceStat = await stat13(sourcePath).catch((error) => {
|
|
21612
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
21382
21613
|
return null;
|
|
21383
21614
|
}
|
|
21384
21615
|
throw error;
|
|
@@ -21390,7 +21621,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
|
|
|
21390
21621
|
});
|
|
21391
21622
|
continue;
|
|
21392
21623
|
}
|
|
21393
|
-
const targetPath =
|
|
21624
|
+
const targetPath = path21.join(stagingDir, filename);
|
|
21394
21625
|
await copyFile(sourcePath, targetPath);
|
|
21395
21626
|
result.staged.push({
|
|
21396
21627
|
source_path: sourcePath,
|
|
@@ -21433,7 +21664,7 @@ function normalizeDeliveryFileInputs(value) {
|
|
|
21433
21664
|
});
|
|
21434
21665
|
}
|
|
21435
21666
|
function uniqueStagingFilename(filename, usedNames) {
|
|
21436
|
-
const extension =
|
|
21667
|
+
const extension = path21.extname(filename);
|
|
21437
21668
|
const stem = filename.slice(0, filename.length - extension.length);
|
|
21438
21669
|
let candidate = filename;
|
|
21439
21670
|
let suffix = 2;
|
|
@@ -21469,7 +21700,7 @@ function readString12(payload, key) {
|
|
|
21469
21700
|
const value = payload[key];
|
|
21470
21701
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
21471
21702
|
}
|
|
21472
|
-
function
|
|
21703
|
+
function isNodeError14(error, code) {
|
|
21473
21704
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
21474
21705
|
}
|
|
21475
21706
|
|
|
@@ -22175,9 +22406,9 @@ function readBoolean2(payload, key) {
|
|
|
22175
22406
|
|
|
22176
22407
|
// src/hermes/stt.ts
|
|
22177
22408
|
import { execFile as execFile3 } from "child_process";
|
|
22178
|
-
import { access as access2, readFile as readFile12, stat as
|
|
22179
|
-
import
|
|
22180
|
-
import
|
|
22409
|
+
import { access as access2, readFile as readFile12, stat as stat14 } from "fs/promises";
|
|
22410
|
+
import os6 from "os";
|
|
22411
|
+
import path22 from "path";
|
|
22181
22412
|
import { promisify as promisify3 } from "util";
|
|
22182
22413
|
var execFileAsync3 = promisify3(execFile3);
|
|
22183
22414
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -22272,7 +22503,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
|
|
|
22272
22503
|
};
|
|
22273
22504
|
const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
|
|
22274
22505
|
if (sourceRoot) {
|
|
22275
|
-
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(
|
|
22506
|
+
env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path22.delimiter);
|
|
22276
22507
|
}
|
|
22277
22508
|
return env;
|
|
22278
22509
|
}
|
|
@@ -22367,14 +22598,14 @@ async function resolveHermesPythonRuntime() {
|
|
|
22367
22598
|
};
|
|
22368
22599
|
}
|
|
22369
22600
|
async function resolveExecutablePath(command) {
|
|
22370
|
-
if (
|
|
22601
|
+
if (path22.isAbsolute(command)) {
|
|
22371
22602
|
return await isExecutableFile2(command) ? command : null;
|
|
22372
22603
|
}
|
|
22373
22604
|
const pathEnv = process.env.PATH ?? "";
|
|
22374
22605
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
22375
|
-
for (const dir of pathEnv.split(
|
|
22606
|
+
for (const dir of pathEnv.split(path22.delimiter)) {
|
|
22376
22607
|
for (const extension of extensions) {
|
|
22377
|
-
const candidate =
|
|
22608
|
+
const candidate = path22.join(dir, `${command}${extension}`);
|
|
22378
22609
|
if (await isExecutableFile2(candidate)) {
|
|
22379
22610
|
return candidate;
|
|
22380
22611
|
}
|
|
@@ -22384,7 +22615,7 @@ async function resolveExecutablePath(command) {
|
|
|
22384
22615
|
}
|
|
22385
22616
|
async function isExecutableFile2(filePath) {
|
|
22386
22617
|
try {
|
|
22387
|
-
const info = await
|
|
22618
|
+
const info = await stat14(filePath);
|
|
22388
22619
|
if (!info.isFile()) {
|
|
22389
22620
|
return false;
|
|
22390
22621
|
}
|
|
@@ -22396,11 +22627,11 @@ async function isExecutableFile2(filePath) {
|
|
|
22396
22627
|
}
|
|
22397
22628
|
async function findHermesVenvPython(sourceRoot) {
|
|
22398
22629
|
const candidates = process.platform === "win32" ? [
|
|
22399
|
-
|
|
22400
|
-
|
|
22630
|
+
path22.join(sourceRoot, "venv", "Scripts", "python.exe"),
|
|
22631
|
+
path22.join(sourceRoot, ".venv", "Scripts", "python.exe")
|
|
22401
22632
|
] : [
|
|
22402
|
-
|
|
22403
|
-
|
|
22633
|
+
path22.join(sourceRoot, "venv", "bin", "python"),
|
|
22634
|
+
path22.join(sourceRoot, ".venv", "bin", "python")
|
|
22404
22635
|
];
|
|
22405
22636
|
for (const candidate of candidates) {
|
|
22406
22637
|
if (await isExecutableFile2(candidate)) {
|
|
@@ -22429,8 +22660,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
22429
22660
|
}
|
|
22430
22661
|
async function findDevHermesAgentSource() {
|
|
22431
22662
|
const candidates = [
|
|
22432
|
-
|
|
22433
|
-
|
|
22663
|
+
path22.resolve(process.cwd(), "reference/hermes-agent"),
|
|
22664
|
+
path22.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
22434
22665
|
];
|
|
22435
22666
|
for (const candidate of candidates) {
|
|
22436
22667
|
if (await isHermesAgentSourceRoot(candidate)) {
|
|
@@ -22446,7 +22677,7 @@ async function readHermesLauncherTarget(filePath) {
|
|
|
22446
22677
|
line
|
|
22447
22678
|
);
|
|
22448
22679
|
const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
|
|
22449
|
-
if (!rawTarget || !
|
|
22680
|
+
if (!rawTarget || !path22.isAbsolute(rawTarget)) {
|
|
22450
22681
|
continue;
|
|
22451
22682
|
}
|
|
22452
22683
|
if (await isExecutableFile2(rawTarget)) {
|
|
@@ -22476,12 +22707,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
|
|
|
22476
22707
|
return null;
|
|
22477
22708
|
}
|
|
22478
22709
|
async function findHermesSourceRoot(executablePath) {
|
|
22479
|
-
let cursor =
|
|
22710
|
+
let cursor = path22.dirname(path22.resolve(executablePath));
|
|
22480
22711
|
for (let index = 0; index < 6; index += 1) {
|
|
22481
22712
|
if (await isHermesAgentSourceRoot(cursor)) {
|
|
22482
22713
|
return cursor;
|
|
22483
22714
|
}
|
|
22484
|
-
const parent =
|
|
22715
|
+
const parent = path22.dirname(cursor);
|
|
22485
22716
|
if (parent === cursor) {
|
|
22486
22717
|
break;
|
|
22487
22718
|
}
|
|
@@ -22492,14 +22723,14 @@ async function findHermesSourceRoot(executablePath) {
|
|
|
22492
22723
|
function hermesSourceRootCandidates() {
|
|
22493
22724
|
const candidates = [
|
|
22494
22725
|
process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
|
|
22495
|
-
|
|
22726
|
+
path22.join(os6.homedir(), ".hermes", "hermes-agent"),
|
|
22496
22727
|
"/usr/local/lib/hermes-agent",
|
|
22497
22728
|
"/opt/hermes"
|
|
22498
22729
|
].filter((candidate) => Boolean(candidate));
|
|
22499
22730
|
if (process.platform === "win32") {
|
|
22500
22731
|
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
22501
22732
|
if (localAppData) {
|
|
22502
|
-
candidates.unshift(
|
|
22733
|
+
candidates.unshift(path22.join(localAppData, "hermes", "hermes-agent"));
|
|
22503
22734
|
}
|
|
22504
22735
|
}
|
|
22505
22736
|
return candidates;
|
|
@@ -22509,10 +22740,10 @@ async function findExplicitHermesSourceRoot() {
|
|
|
22509
22740
|
return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
|
|
22510
22741
|
}
|
|
22511
22742
|
async function isHermesAgentSourceRoot(candidate) {
|
|
22512
|
-
return await isDirectory(candidate) && await isDirectory(
|
|
22743
|
+
return await isDirectory(candidate) && await isDirectory(path22.join(candidate, "tools")) && await isDirectory(path22.join(candidate, "hermes_cli"));
|
|
22513
22744
|
}
|
|
22514
22745
|
async function isDirectory(candidate) {
|
|
22515
|
-
return
|
|
22746
|
+
return stat14(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
22516
22747
|
}
|
|
22517
22748
|
function compactProcessOutput(value) {
|
|
22518
22749
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -22520,14 +22751,14 @@ function compactProcessOutput(value) {
|
|
|
22520
22751
|
}
|
|
22521
22752
|
|
|
22522
22753
|
// src/hermes/usage-probe.ts
|
|
22523
|
-
import { open as open3, readFile as readFile14, rm as rm6, stat as
|
|
22524
|
-
import
|
|
22754
|
+
import { open as open3, readFile as readFile14, rm as rm6, stat as stat16 } from "fs/promises";
|
|
22755
|
+
import path24 from "path";
|
|
22525
22756
|
import YAML3 from "yaml";
|
|
22526
22757
|
|
|
22527
22758
|
// src/hermes/profiles.ts
|
|
22528
22759
|
import { execFile as execFile4 } from "child_process";
|
|
22529
|
-
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as
|
|
22530
|
-
import
|
|
22760
|
+
import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat15 } from "fs/promises";
|
|
22761
|
+
import path23 from "path";
|
|
22531
22762
|
import { setTimeout as delay4 } from "timers/promises";
|
|
22532
22763
|
import { promisify as promisify4 } from "util";
|
|
22533
22764
|
import YAML2 from "yaml";
|
|
@@ -22546,7 +22777,7 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
22546
22777
|
const profilesDir = resolveHermesProfilesDir();
|
|
22547
22778
|
const entries = await readdir8(profilesDir, { withFileTypes: true }).catch(
|
|
22548
22779
|
(error) => {
|
|
22549
|
-
if (
|
|
22780
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22550
22781
|
return [];
|
|
22551
22782
|
}
|
|
22552
22783
|
throw error;
|
|
@@ -22600,8 +22831,8 @@ async function prepareHermesProfilesForUse(paths = resolveRuntimePaths()) {
|
|
|
22600
22831
|
async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
22601
22832
|
assertProfileName(name);
|
|
22602
22833
|
const profile = await profileInfo(name, paths);
|
|
22603
|
-
const exists = await
|
|
22604
|
-
if (
|
|
22834
|
+
const exists = await stat15(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
22835
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22605
22836
|
return false;
|
|
22606
22837
|
}
|
|
22607
22838
|
throw error;
|
|
@@ -22670,7 +22901,7 @@ async function readHermesProfileCapabilities(name) {
|
|
|
22670
22901
|
return {
|
|
22671
22902
|
defaultModel: listedModels?.defaultModel ?? null,
|
|
22672
22903
|
modelCount: listedModels?.models.length ?? 0,
|
|
22673
|
-
skillCount: await countSkills(
|
|
22904
|
+
skillCount: await countSkills(path23.join(profileDir, "skills")).catch(
|
|
22674
22905
|
() => 0
|
|
22675
22906
|
),
|
|
22676
22907
|
toolCount: await countConfiguredTools(name).catch(() => 0)
|
|
@@ -22713,8 +22944,8 @@ function assertProfileName(name) {
|
|
|
22713
22944
|
}
|
|
22714
22945
|
}
|
|
22715
22946
|
async function pathExists(targetPath) {
|
|
22716
|
-
return await
|
|
22717
|
-
if (
|
|
22947
|
+
return await stat15(targetPath).then(() => true).catch((error) => {
|
|
22948
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22718
22949
|
return false;
|
|
22719
22950
|
}
|
|
22720
22951
|
throw error;
|
|
@@ -22722,8 +22953,8 @@ async function pathExists(targetPath) {
|
|
|
22722
22953
|
}
|
|
22723
22954
|
async function hasDefaultProfileConfigSource() {
|
|
22724
22955
|
const profilePath = resolveHermesProfileDir(DEFAULT_PROFILE);
|
|
22725
|
-
const profileStat = await
|
|
22726
|
-
if (
|
|
22956
|
+
const profileStat = await stat15(profilePath).catch((error) => {
|
|
22957
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22727
22958
|
return null;
|
|
22728
22959
|
}
|
|
22729
22960
|
throw error;
|
|
@@ -22731,7 +22962,7 @@ async function hasDefaultProfileConfigSource() {
|
|
|
22731
22962
|
if (!profileStat?.isDirectory()) {
|
|
22732
22963
|
return false;
|
|
22733
22964
|
}
|
|
22734
|
-
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(
|
|
22965
|
+
return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path23.join(profilePath, ".env"));
|
|
22735
22966
|
}
|
|
22736
22967
|
async function deleteHermesProfileWithCli(name) {
|
|
22737
22968
|
try {
|
|
@@ -22844,7 +23075,7 @@ function isProcessRunning(pid) {
|
|
|
22844
23075
|
process.kill(pid, 0);
|
|
22845
23076
|
return true;
|
|
22846
23077
|
} catch (error) {
|
|
22847
|
-
return
|
|
23078
|
+
return isNodeError15(error, "EPERM");
|
|
22848
23079
|
}
|
|
22849
23080
|
}
|
|
22850
23081
|
async function waitForProfilePathToRemainAbsent(profilePath) {
|
|
@@ -22881,7 +23112,7 @@ function readExecErrorOutput2(error) {
|
|
|
22881
23112
|
}
|
|
22882
23113
|
return parts.join("\n");
|
|
22883
23114
|
}
|
|
22884
|
-
function
|
|
23115
|
+
function isNodeError15(error, code) {
|
|
22885
23116
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
22886
23117
|
}
|
|
22887
23118
|
function errorMessage(error) {
|
|
@@ -22893,7 +23124,7 @@ function escapeRegExp2(value) {
|
|
|
22893
23124
|
async function countSkills(root) {
|
|
22894
23125
|
const entries = await readdir8(root, { withFileTypes: true }).catch(
|
|
22895
23126
|
(error) => {
|
|
22896
|
-
if (
|
|
23127
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22897
23128
|
return [];
|
|
22898
23129
|
}
|
|
22899
23130
|
throw error;
|
|
@@ -22901,7 +23132,7 @@ async function countSkills(root) {
|
|
|
22901
23132
|
);
|
|
22902
23133
|
let count = 0;
|
|
22903
23134
|
for (const entry of entries) {
|
|
22904
|
-
const entryPath =
|
|
23135
|
+
const entryPath = path23.join(root, entry.name);
|
|
22905
23136
|
if (entry.name === ".git" || entry.name === ".hub") {
|
|
22906
23137
|
continue;
|
|
22907
23138
|
}
|
|
@@ -22920,7 +23151,7 @@ async function countConfiguredTools(profileName) {
|
|
|
22920
23151
|
resolveHermesConfigPath(profileName),
|
|
22921
23152
|
"utf8"
|
|
22922
23153
|
).catch((error) => {
|
|
22923
|
-
if (
|
|
23154
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
22924
23155
|
return "";
|
|
22925
23156
|
}
|
|
22926
23157
|
throw error;
|
|
@@ -22991,10 +23222,10 @@ async function ensureHermesUsageProbeForProfiles(options = {}) {
|
|
|
22991
23222
|
}
|
|
22992
23223
|
async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
22993
23224
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
22994
|
-
const normalizedProfile =
|
|
23225
|
+
const normalizedProfile = normalizeProfileName6(profileName);
|
|
22995
23226
|
const profilePath = resolveHermesProfileDir(normalizedProfile);
|
|
22996
23227
|
const configPath = resolveHermesConfigPath(normalizedProfile);
|
|
22997
|
-
const pluginPath =
|
|
23228
|
+
const pluginPath = path24.join(
|
|
22998
23229
|
profilePath,
|
|
22999
23230
|
"plugins",
|
|
23000
23231
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
@@ -23020,7 +23251,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23020
23251
|
return { ...base(), skipped: true };
|
|
23021
23252
|
}
|
|
23022
23253
|
try {
|
|
23023
|
-
await ensureDirectoryWithInheritedMetadata(
|
|
23254
|
+
await ensureDirectoryWithInheritedMetadata(path24.dirname(eventsPath), 448);
|
|
23024
23255
|
const state = await readUsageProbeState(paths);
|
|
23025
23256
|
const pluginState = state.profiles[normalizedProfile];
|
|
23026
23257
|
const currentConfig = await readUsageProbeConfigStatus({
|
|
@@ -23116,11 +23347,11 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
|
|
|
23116
23347
|
}
|
|
23117
23348
|
async function findHermesUsageProbeEventForRun(input) {
|
|
23118
23349
|
const paths = input.paths ?? resolveRuntimePaths();
|
|
23119
|
-
const profileName =
|
|
23350
|
+
const profileName = normalizeProfileName6(input.profileName);
|
|
23120
23351
|
const eventsPath = resolveUsageProbeEventsPath(paths, profileName);
|
|
23121
23352
|
const raw = await readTail2(eventsPath, MAX_EVENT_READ_BYTES).catch(
|
|
23122
23353
|
(error) => {
|
|
23123
|
-
if (
|
|
23354
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23124
23355
|
return "";
|
|
23125
23356
|
}
|
|
23126
23357
|
throw error;
|
|
@@ -23153,7 +23384,7 @@ async function findHermesUsageProbeEventForRun(input) {
|
|
|
23153
23384
|
return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
|
|
23154
23385
|
}
|
|
23155
23386
|
function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
|
|
23156
|
-
return
|
|
23387
|
+
return path24.join(
|
|
23157
23388
|
paths.homeDir,
|
|
23158
23389
|
USAGE_PROBE_DIR,
|
|
23159
23390
|
safeProfileSegment(profileName),
|
|
@@ -23177,8 +23408,8 @@ function summarizeUsageProbeEnsure(result) {
|
|
|
23177
23408
|
};
|
|
23178
23409
|
}
|
|
23179
23410
|
async function writeUsageProbePlugin(input) {
|
|
23180
|
-
const manifestPath =
|
|
23181
|
-
const initPath =
|
|
23411
|
+
const manifestPath = path24.join(input.pluginPath, "plugin.yaml");
|
|
23412
|
+
const initPath = path24.join(input.pluginPath, "__init__.py");
|
|
23182
23413
|
const manifest = usageProbeManifest();
|
|
23183
23414
|
const source = usageProbePythonSource(
|
|
23184
23415
|
input.profileName,
|
|
@@ -23191,7 +23422,7 @@ async function writeUsageProbePlugin(input) {
|
|
|
23191
23422
|
}
|
|
23192
23423
|
async function writeManagedFile(filePath, content) {
|
|
23193
23424
|
const existing = await readFile14(filePath, "utf8").catch((error) => {
|
|
23194
|
-
if (
|
|
23425
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23195
23426
|
return null;
|
|
23196
23427
|
}
|
|
23197
23428
|
throw error;
|
|
@@ -23294,13 +23525,13 @@ function hasManagedPluginEntry(entries) {
|
|
|
23294
23525
|
return entries.includes(HERMES_USAGE_PROBE_PLUGIN_KEY) || entries.includes(LEGACY_PLUGIN_KEY);
|
|
23295
23526
|
}
|
|
23296
23527
|
function normalizeEnabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
23297
|
-
const nextEnabled =
|
|
23528
|
+
const nextEnabled = uniqueStrings2([
|
|
23298
23529
|
...enabledEntries.filter(
|
|
23299
23530
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23300
23531
|
),
|
|
23301
23532
|
HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23302
23533
|
]);
|
|
23303
|
-
const nextDisabled =
|
|
23534
|
+
const nextDisabled = uniqueStrings2(
|
|
23304
23535
|
disabledEntries.filter(
|
|
23305
23536
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23306
23537
|
)
|
|
@@ -23315,12 +23546,12 @@ function normalizeEnabledPluginConfig(plugins, enabledEntries, disabledEntries)
|
|
|
23315
23546
|
return changed;
|
|
23316
23547
|
}
|
|
23317
23548
|
function normalizeDisabledPluginConfig(plugins, enabledEntries, disabledEntries) {
|
|
23318
|
-
const nextEnabled =
|
|
23549
|
+
const nextEnabled = uniqueStrings2(
|
|
23319
23550
|
enabledEntries.filter(
|
|
23320
23551
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23321
23552
|
)
|
|
23322
23553
|
);
|
|
23323
|
-
const nextDisabled =
|
|
23554
|
+
const nextDisabled = uniqueStrings2([
|
|
23324
23555
|
...disabledEntries.filter(
|
|
23325
23556
|
(entry) => entry !== LEGACY_PLUGIN_KEY && entry !== HERMES_USAGE_PROBE_PLUGIN_KEY
|
|
23326
23557
|
),
|
|
@@ -23341,11 +23572,11 @@ function ensureApiServerToolsetEnabled(config) {
|
|
|
23341
23572
|
if (apiServerToolsets.length === 0 && !Array.isArray(platformToolsets?.api_server) && !knownApiServerToolsets.includes(HERMESPILOT_LINK_TOOLSET)) {
|
|
23342
23573
|
return false;
|
|
23343
23574
|
}
|
|
23344
|
-
const nextToolsets =
|
|
23575
|
+
const nextToolsets = uniqueStrings2([
|
|
23345
23576
|
...Array.isArray(platformToolsets?.api_server) ? apiServerToolsets : ["hermes-api-server"],
|
|
23346
23577
|
HERMESPILOT_LINK_TOOLSET
|
|
23347
23578
|
]);
|
|
23348
|
-
const nextKnownToolsets =
|
|
23579
|
+
const nextKnownToolsets = uniqueStrings2([
|
|
23349
23580
|
...knownApiServerToolsets,
|
|
23350
23581
|
HERMESPILOT_LINK_TOOLSET
|
|
23351
23582
|
]);
|
|
@@ -23525,19 +23756,19 @@ async function disableUsageProbeAfterActivationFailure(input) {
|
|
|
23525
23756
|
});
|
|
23526
23757
|
}
|
|
23527
23758
|
async function cleanupLegacyUsageProbePlugin(profilePath) {
|
|
23528
|
-
const legacyPath =
|
|
23759
|
+
const legacyPath = path24.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
|
|
23529
23760
|
const [manifest, init] = await Promise.all([
|
|
23530
|
-
readFile14(
|
|
23761
|
+
readFile14(path24.join(legacyPath, "plugin.yaml"), "utf8").catch(
|
|
23531
23762
|
(error) => {
|
|
23532
|
-
if (
|
|
23763
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23533
23764
|
return null;
|
|
23534
23765
|
}
|
|
23535
23766
|
throw error;
|
|
23536
23767
|
}
|
|
23537
23768
|
),
|
|
23538
|
-
readFile14(
|
|
23769
|
+
readFile14(path24.join(legacyPath, "__init__.py"), "utf8").catch(
|
|
23539
23770
|
(error) => {
|
|
23540
|
-
if (
|
|
23771
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23541
23772
|
return null;
|
|
23542
23773
|
}
|
|
23543
23774
|
throw error;
|
|
@@ -23584,12 +23815,12 @@ async function rememberUsageProbeState(paths, profileName, patch) {
|
|
|
23584
23815
|
}));
|
|
23585
23816
|
}
|
|
23586
23817
|
function usageProbeStatePath(paths) {
|
|
23587
|
-
return
|
|
23818
|
+
return path24.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
|
|
23588
23819
|
}
|
|
23589
23820
|
async function readHermesConfigDocument2(configPath, language) {
|
|
23590
23821
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
23591
23822
|
(error) => {
|
|
23592
|
-
if (
|
|
23823
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
23593
23824
|
return null;
|
|
23594
23825
|
}
|
|
23595
23826
|
throw error;
|
|
@@ -23620,7 +23851,7 @@ async function writeHermesConfigDocument2(input) {
|
|
|
23620
23851
|
);
|
|
23621
23852
|
}
|
|
23622
23853
|
async function readTail2(filePath, maxBytes) {
|
|
23623
|
-
const info = await
|
|
23854
|
+
const info = await stat16(filePath);
|
|
23624
23855
|
const length = Math.min(info.size, maxBytes);
|
|
23625
23856
|
if (length <= 0) {
|
|
23626
23857
|
return "";
|
|
@@ -23996,7 +24227,7 @@ function withDefaultProfilePlaceholder(profiles) {
|
|
|
23996
24227
|
...profiles
|
|
23997
24228
|
];
|
|
23998
24229
|
}
|
|
23999
|
-
function
|
|
24230
|
+
function normalizeProfileName6(profileName) {
|
|
24000
24231
|
const value = profileName?.trim() || "default";
|
|
24001
24232
|
if (!/^[a-zA-Z0-9._-]{1,64}$/u.test(value)) {
|
|
24002
24233
|
throw new Error("invalid profile name");
|
|
@@ -24004,7 +24235,7 @@ function normalizeProfileName5(profileName) {
|
|
|
24004
24235
|
return value;
|
|
24005
24236
|
}
|
|
24006
24237
|
function safeProfileSegment(profileName) {
|
|
24007
|
-
return encodeURIComponent(
|
|
24238
|
+
return encodeURIComponent(normalizeProfileName6(profileName));
|
|
24008
24239
|
}
|
|
24009
24240
|
function ensureRecord2(target, key) {
|
|
24010
24241
|
const existing = target[key];
|
|
@@ -24021,7 +24252,7 @@ function readStringList5(value) {
|
|
|
24021
24252
|
}
|
|
24022
24253
|
return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
|
|
24023
24254
|
}
|
|
24024
|
-
function
|
|
24255
|
+
function uniqueStrings2(entries) {
|
|
24025
24256
|
return [...new Set(entries.map((entry) => entry.trim()).filter(Boolean))];
|
|
24026
24257
|
}
|
|
24027
24258
|
function sameStringList(left, right) {
|
|
@@ -24047,8 +24278,8 @@ function isRecord3(value) {
|
|
|
24047
24278
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24048
24279
|
}
|
|
24049
24280
|
async function pathIsDirectory(filePath) {
|
|
24050
|
-
return
|
|
24051
|
-
if (
|
|
24281
|
+
return stat16(filePath).then((value) => value.isDirectory()).catch((error) => {
|
|
24282
|
+
if (isNodeError16(error, "ENOENT")) {
|
|
24052
24283
|
return false;
|
|
24053
24284
|
}
|
|
24054
24285
|
throw error;
|
|
@@ -24071,7 +24302,7 @@ function logEnsureResult(logger, source, profiles) {
|
|
|
24071
24302
|
function errorMessage2(error) {
|
|
24072
24303
|
return error instanceof Error ? error.message : String(error);
|
|
24073
24304
|
}
|
|
24074
|
-
function
|
|
24305
|
+
function isNodeError16(error, code) {
|
|
24075
24306
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
24076
24307
|
}
|
|
24077
24308
|
|
|
@@ -24151,8 +24382,8 @@ function toRecord14(value) {
|
|
|
24151
24382
|
}
|
|
24152
24383
|
|
|
24153
24384
|
// src/conversations/run-transcript-enrichment.ts
|
|
24154
|
-
import { readFile as readFile15, stat as
|
|
24155
|
-
import
|
|
24385
|
+
import { readFile as readFile15, stat as stat17 } from "fs/promises";
|
|
24386
|
+
import path25 from "path";
|
|
24156
24387
|
var MESSAGE_COLUMNS2 = [
|
|
24157
24388
|
"id",
|
|
24158
24389
|
"session_id",
|
|
@@ -24228,8 +24459,8 @@ async function readRunFinalAssistantText(input) {
|
|
|
24228
24459
|
}
|
|
24229
24460
|
async function readHermesTranscriptRows(profileName, sessionId) {
|
|
24230
24461
|
const profileDir = resolveHermesProfileDir(profileName);
|
|
24231
|
-
const dbPath =
|
|
24232
|
-
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() =>
|
|
24462
|
+
const dbPath = path25.join(profileDir, "state.db");
|
|
24463
|
+
const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path25.join(profileDir, "sessions"));
|
|
24233
24464
|
const [dbRows, jsonlRows] = await Promise.all([
|
|
24234
24465
|
readStateDbMessages2(dbPath, sessionId),
|
|
24235
24466
|
readJsonlMessages2(sessionsDir, sessionId)
|
|
@@ -24292,9 +24523,9 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
|
|
|
24292
24523
|
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
|
|
24293
24524
|
return [];
|
|
24294
24525
|
}
|
|
24295
|
-
const transcriptPath =
|
|
24526
|
+
const transcriptPath = path25.join(sessionsDir, `${sessionId}.jsonl`);
|
|
24296
24527
|
const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
|
|
24297
|
-
if (
|
|
24528
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
24298
24529
|
return "";
|
|
24299
24530
|
}
|
|
24300
24531
|
throw error;
|
|
@@ -24508,8 +24739,8 @@ function quoteIdentifier2(value) {
|
|
|
24508
24739
|
return `"${value.replaceAll('"', '""')}"`;
|
|
24509
24740
|
}
|
|
24510
24741
|
async function isFile2(filePath) {
|
|
24511
|
-
return
|
|
24512
|
-
if (
|
|
24742
|
+
return stat17(filePath).then((value) => value.isFile()).catch((error) => {
|
|
24743
|
+
if (isNodeError17(error, "ENOENT")) {
|
|
24513
24744
|
return false;
|
|
24514
24745
|
}
|
|
24515
24746
|
throw error;
|
|
@@ -24539,7 +24770,7 @@ function readNumber4(value) {
|
|
|
24539
24770
|
function toRecord15(value) {
|
|
24540
24771
|
return typeof value === "object" && value !== null ? value : {};
|
|
24541
24772
|
}
|
|
24542
|
-
function
|
|
24773
|
+
function isNodeError17(error, code) {
|
|
24543
24774
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
24544
24775
|
}
|
|
24545
24776
|
|
|
@@ -27514,7 +27745,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27514
27745
|
async readHermesCronJobIds(profileName) {
|
|
27515
27746
|
const jobs = await listHermesCronJobs({
|
|
27516
27747
|
logger: this.deps.logger,
|
|
27517
|
-
profileName:
|
|
27748
|
+
profileName: normalizeProfileName4(profileName),
|
|
27518
27749
|
includeDisabled: true
|
|
27519
27750
|
});
|
|
27520
27751
|
return new Set(
|
|
@@ -27522,7 +27753,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
|
|
|
27522
27753
|
);
|
|
27523
27754
|
}
|
|
27524
27755
|
async bindNewCronJobsCreatedByRun(input) {
|
|
27525
|
-
const profileName =
|
|
27756
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
27526
27757
|
const jobs = await listHermesCronJobs({
|
|
27527
27758
|
logger: this.deps.logger,
|
|
27528
27759
|
profileName,
|
|
@@ -27612,7 +27843,7 @@ function formatFilenameList(filenames, language = "zh-CN") {
|
|
|
27612
27843
|
}
|
|
27613
27844
|
async function readdirWithDirs(directory) {
|
|
27614
27845
|
return readdir9(directory, { withFileTypes: true }).catch((error) => {
|
|
27615
|
-
if (
|
|
27846
|
+
if (isNodeError18(error, "ENOENT")) {
|
|
27616
27847
|
return [];
|
|
27617
27848
|
}
|
|
27618
27849
|
throw error;
|
|
@@ -28014,7 +28245,7 @@ function findPreviousHermesResponseId(snapshot, run) {
|
|
|
28014
28245
|
}
|
|
28015
28246
|
function normalizeRunProfileForCompare(profileName) {
|
|
28016
28247
|
try {
|
|
28017
|
-
return
|
|
28248
|
+
return normalizeProfileName4(profileName);
|
|
28018
28249
|
} catch {
|
|
28019
28250
|
return null;
|
|
28020
28251
|
}
|
|
@@ -28147,7 +28378,7 @@ async function sleep(ms, signal) {
|
|
|
28147
28378
|
);
|
|
28148
28379
|
});
|
|
28149
28380
|
}
|
|
28150
|
-
function
|
|
28381
|
+
function isNodeError18(error, code) {
|
|
28151
28382
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
28152
28383
|
return false;
|
|
28153
28384
|
}
|
|
@@ -28381,11 +28612,16 @@ var ConversationService = class {
|
|
|
28381
28612
|
"model_id must be a valid model id"
|
|
28382
28613
|
);
|
|
28383
28614
|
}
|
|
28384
|
-
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
28385
|
-
|
|
28386
|
-
|
|
28387
|
-
|
|
28388
|
-
|
|
28615
|
+
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
28616
|
+
profile.profileName,
|
|
28617
|
+
normalizedModelId,
|
|
28618
|
+
{
|
|
28619
|
+
provider: input.modelProvider,
|
|
28620
|
+
baseUrl: input.modelBaseUrl,
|
|
28621
|
+
apiMode: input.modelApiMode
|
|
28622
|
+
},
|
|
28623
|
+
this.paths
|
|
28624
|
+
) : null;
|
|
28389
28625
|
if (normalizedModelId && !configuredModel) {
|
|
28390
28626
|
throw new LinkHttpError(
|
|
28391
28627
|
404,
|
|
@@ -28475,10 +28711,10 @@ var ConversationService = class {
|
|
|
28475
28711
|
};
|
|
28476
28712
|
}
|
|
28477
28713
|
async ensureCronInboxConversation(input = {}) {
|
|
28478
|
-
const profileName =
|
|
28714
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
28479
28715
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
28480
28716
|
const manifest = await this.store.readManifest(conversationId).catch(() => null);
|
|
28481
|
-
if (manifest?.status === "active" && manifest.title === "HermesLink \u5B9A\u65F6\u4EFB\u52A1" &&
|
|
28717
|
+
if (manifest?.status === "active" && manifest.title === "HermesLink \u5B9A\u65F6\u4EFB\u52A1" && normalizeProfileName4(
|
|
28482
28718
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
28483
28719
|
) === profileName) {
|
|
28484
28720
|
return manifest.id;
|
|
@@ -28589,7 +28825,7 @@ var ConversationService = class {
|
|
|
28589
28825
|
}
|
|
28590
28826
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28591
28827
|
const createdAt = input.runAt ?? now;
|
|
28592
|
-
const profileName =
|
|
28828
|
+
const profileName = normalizeProfileName4(
|
|
28593
28829
|
manifest.profile_name_snapshot ?? manifest.profile ?? input.profileName
|
|
28594
28830
|
);
|
|
28595
28831
|
const ownerAccountId = input.accountId ?? manifest.owner_account_id;
|
|
@@ -28656,7 +28892,7 @@ var ConversationService = class {
|
|
|
28656
28892
|
});
|
|
28657
28893
|
}
|
|
28658
28894
|
async findImportedCronConversation(input) {
|
|
28659
|
-
const profileName =
|
|
28895
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
28660
28896
|
const outputAt = Date.parse(input.runAt ?? "");
|
|
28661
28897
|
const candidates = [];
|
|
28662
28898
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
@@ -28664,7 +28900,7 @@ var ConversationService = class {
|
|
|
28664
28900
|
if (!manifest || manifest.status !== "active") {
|
|
28665
28901
|
continue;
|
|
28666
28902
|
}
|
|
28667
|
-
if (
|
|
28903
|
+
if (normalizeProfileName4(
|
|
28668
28904
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
28669
28905
|
) !== profileName) {
|
|
28670
28906
|
continue;
|
|
@@ -28912,7 +29148,8 @@ var ConversationService = class {
|
|
|
28912
29148
|
const configuredModel = normalizedModelId ? await findConfiguredModel(
|
|
28913
29149
|
currentRuntime.profile.name,
|
|
28914
29150
|
normalizedModelId,
|
|
28915
|
-
modelSelector
|
|
29151
|
+
modelSelector,
|
|
29152
|
+
this.paths
|
|
28916
29153
|
) : null;
|
|
28917
29154
|
if (normalizedModelId && !configuredModel) {
|
|
28918
29155
|
throw new LinkHttpError(
|
|
@@ -29016,7 +29253,7 @@ var ConversationService = class {
|
|
|
29016
29253
|
this.paths,
|
|
29017
29254
|
profileName
|
|
29018
29255
|
);
|
|
29019
|
-
const currentProfile =
|
|
29256
|
+
const currentProfile = normalizeProfileName4(
|
|
29020
29257
|
manifest.profile_name_snapshot ?? manifest.profile
|
|
29021
29258
|
);
|
|
29022
29259
|
if (currentProfile === profile.profileName && (!manifest.profile_uid || manifest.profile_uid === profile.profileUid)) {
|
|
@@ -29306,7 +29543,7 @@ var ConversationService = class {
|
|
|
29306
29543
|
"This approval request cannot be permanently allowed because Hermes did not return a pattern key."
|
|
29307
29544
|
);
|
|
29308
29545
|
}
|
|
29309
|
-
const profileName =
|
|
29546
|
+
const profileName = normalizeProfileName4(
|
|
29310
29547
|
run?.profile ?? match.message.sender.profile ?? manifest.profile_name_snapshot ?? manifest.profile
|
|
29311
29548
|
);
|
|
29312
29549
|
const result = await addHermesCommandAllowlistEntry(
|
|
@@ -29759,7 +29996,7 @@ var ConversationService = class {
|
|
|
29759
29996
|
}
|
|
29760
29997
|
}
|
|
29761
29998
|
hermesArchiveStateSyncMarkerPath() {
|
|
29762
|
-
return
|
|
29999
|
+
return path26.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
|
|
29763
30000
|
}
|
|
29764
30001
|
prepareClearAllConversationPlan(targetStatus) {
|
|
29765
30002
|
return this.maintenance.prepareClearAllConversationPlan(targetStatus);
|
|
@@ -29789,7 +30026,7 @@ var ConversationService = class {
|
|
|
29789
30026
|
return this.maintenance.deleteConversations(conversationIds);
|
|
29790
30027
|
}
|
|
29791
30028
|
async deleteLocalConversationsForProfile(input) {
|
|
29792
|
-
const profileName =
|
|
30029
|
+
const profileName = normalizeProfileName4(input.profileName);
|
|
29793
30030
|
const profileUid = input.profileUid?.trim() || null;
|
|
29794
30031
|
const deletedConversationIds = [];
|
|
29795
30032
|
for (const conversationId of await this.store.listConversationIds()) {
|
|
@@ -29874,7 +30111,7 @@ function conversationMatchesProfile(manifest, profileName, profileUid) {
|
|
|
29874
30111
|
if (profileUid && manifest.profile_uid === profileUid) {
|
|
29875
30112
|
return true;
|
|
29876
30113
|
}
|
|
29877
|
-
return
|
|
30114
|
+
return normalizeProfileName4(manifest.profile_name_snapshot ?? manifest.profile) === profileName;
|
|
29878
30115
|
}
|
|
29879
30116
|
function findApproval(snapshot, approvalId) {
|
|
29880
30117
|
for (const message of snapshot.messages) {
|
|
@@ -32256,110 +32493,6 @@ function assertCronJobId(jobId) {
|
|
|
32256
32493
|
// src/http/routes/model-configs.ts
|
|
32257
32494
|
import { createHash as createHash9 } from "crypto";
|
|
32258
32495
|
|
|
32259
|
-
// src/hermes/model-provider-disconnects.ts
|
|
32260
|
-
import { stat as stat17 } from "fs/promises";
|
|
32261
|
-
import os6 from "os";
|
|
32262
|
-
import path26 from "path";
|
|
32263
|
-
async function markAuthBackedModelProviderDisconnected(paths, profileName, providerKey) {
|
|
32264
|
-
const profileKey = normalizeProfileName6(profileName);
|
|
32265
|
-
const provider = providerKey.trim();
|
|
32266
|
-
if (!provider) {
|
|
32267
|
-
return;
|
|
32268
|
-
}
|
|
32269
|
-
const credentialMtimeMs = await readProviderCredentialMtimeMs(
|
|
32270
|
-
profileKey,
|
|
32271
|
-
provider
|
|
32272
|
-
);
|
|
32273
|
-
await updateJsonFile(
|
|
32274
|
-
disconnectedProvidersPath(paths),
|
|
32275
|
-
(current) => {
|
|
32276
|
-
const store = normalizeStore(current);
|
|
32277
|
-
const profiles = { ...store.profiles ?? {} };
|
|
32278
|
-
const profile = profiles[profileKey] ?? {};
|
|
32279
|
-
profiles[profileKey] = {
|
|
32280
|
-
...profile,
|
|
32281
|
-
providers: {
|
|
32282
|
-
...profile.providers ?? {},
|
|
32283
|
-
[provider]: {
|
|
32284
|
-
disconnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32285
|
-
credentialMtimeMs
|
|
32286
|
-
}
|
|
32287
|
-
}
|
|
32288
|
-
};
|
|
32289
|
-
return { ...store, profiles };
|
|
32290
|
-
}
|
|
32291
|
-
);
|
|
32292
|
-
}
|
|
32293
|
-
async function readDisconnectedAuthBackedModelProviderKeys(paths, profileName) {
|
|
32294
|
-
const store = normalizeStore(
|
|
32295
|
-
await readJsonFile(
|
|
32296
|
-
disconnectedProvidersPath(paths)
|
|
32297
|
-
).catch(() => null)
|
|
32298
|
-
);
|
|
32299
|
-
const profile = store.profiles?.[normalizeProfileName6(profileName)];
|
|
32300
|
-
const providers = profile?.providers ?? {};
|
|
32301
|
-
const active = /* @__PURE__ */ new Set();
|
|
32302
|
-
for (const [provider, entry] of Object.entries(providers)) {
|
|
32303
|
-
if (await isDisconnectMarkerActive(profileName, provider, entry)) {
|
|
32304
|
-
active.add(provider);
|
|
32305
|
-
}
|
|
32306
|
-
}
|
|
32307
|
-
return active;
|
|
32308
|
-
}
|
|
32309
|
-
async function isDisconnectMarkerActive(profileName, providerKey, entry) {
|
|
32310
|
-
const currentMtimeMs = await readProviderCredentialMtimeMs(
|
|
32311
|
-
profileName,
|
|
32312
|
-
providerKey
|
|
32313
|
-
);
|
|
32314
|
-
if (entry.credentialMtimeMs === null || !Number.isFinite(entry.credentialMtimeMs)) {
|
|
32315
|
-
return currentMtimeMs === null;
|
|
32316
|
-
}
|
|
32317
|
-
if (currentMtimeMs === null || !Number.isFinite(currentMtimeMs)) {
|
|
32318
|
-
return true;
|
|
32319
|
-
}
|
|
32320
|
-
return currentMtimeMs <= entry.credentialMtimeMs + 1e3;
|
|
32321
|
-
}
|
|
32322
|
-
async function readProviderCredentialMtimeMs(profileName, providerKey) {
|
|
32323
|
-
for (const filePath of credentialFilesForProvider(profileName, providerKey)) {
|
|
32324
|
-
const stats = await stat17(filePath).catch((error) => {
|
|
32325
|
-
if (isNodeError18(error, "ENOENT")) {
|
|
32326
|
-
return null;
|
|
32327
|
-
}
|
|
32328
|
-
throw error;
|
|
32329
|
-
});
|
|
32330
|
-
if (stats) {
|
|
32331
|
-
return stats.mtimeMs;
|
|
32332
|
-
}
|
|
32333
|
-
}
|
|
32334
|
-
return null;
|
|
32335
|
-
}
|
|
32336
|
-
function credentialFilesForProvider(profileName, providerKey) {
|
|
32337
|
-
const profileDir = resolveHermesProfileDir(normalizeProfileName6(profileName));
|
|
32338
|
-
switch (providerKey) {
|
|
32339
|
-
case "qwen-oauth":
|
|
32340
|
-
return [path26.join(os6.homedir(), ".qwen", "oauth_creds.json")];
|
|
32341
|
-
case "google-gemini-cli":
|
|
32342
|
-
return [path26.join(profileDir, "auth", "google_oauth.json")];
|
|
32343
|
-
default:
|
|
32344
|
-
return [path26.join(profileDir, "auth.json")];
|
|
32345
|
-
}
|
|
32346
|
-
}
|
|
32347
|
-
function disconnectedProvidersPath(paths) {
|
|
32348
|
-
return path26.join(paths.homeDir, "model-provider-disconnects.json");
|
|
32349
|
-
}
|
|
32350
|
-
function normalizeStore(value) {
|
|
32351
|
-
return {
|
|
32352
|
-
version: 1,
|
|
32353
|
-
profiles: value?.profiles && typeof value.profiles === "object" ? value.profiles : {}
|
|
32354
|
-
};
|
|
32355
|
-
}
|
|
32356
|
-
function normalizeProfileName6(profileName) {
|
|
32357
|
-
return profileName.trim() || "default";
|
|
32358
|
-
}
|
|
32359
|
-
function isNodeError18(error, code) {
|
|
32360
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
32361
|
-
}
|
|
32362
|
-
|
|
32363
32496
|
// src/model-catalog/catalog.ts
|
|
32364
32497
|
import { randomInt } from "crypto";
|
|
32365
32498
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
@@ -32747,6 +32880,43 @@ async function reloadGatewayAfterProfileModelConfigChange(result, options) {
|
|
|
32747
32880
|
label: "\u6A21\u578B\u914D\u7F6E"
|
|
32748
32881
|
});
|
|
32749
32882
|
}
|
|
32883
|
+
async function refreshRuntimeAfterModelConfigChange(result, options) {
|
|
32884
|
+
const profileName = options.profileName?.trim() || "default";
|
|
32885
|
+
const label = options.label ?? "\u6A21\u578B\u914D\u7F6E";
|
|
32886
|
+
if (resolveConversationRunBackend() !== "tui_gateway") {
|
|
32887
|
+
return profileName === "default" ? reloadGatewayAfterModelConfigChange(result, options) : reloadGatewayAfterProfileModelConfigChange(result, {
|
|
32888
|
+
...options,
|
|
32889
|
+
profileName
|
|
32890
|
+
});
|
|
32891
|
+
}
|
|
32892
|
+
try {
|
|
32893
|
+
await restartTuiGatewayBackend({
|
|
32894
|
+
profileName,
|
|
32895
|
+
reason: "model_config_changed",
|
|
32896
|
+
logger: options.logger,
|
|
32897
|
+
paths: options.paths
|
|
32898
|
+
});
|
|
32899
|
+
return {
|
|
32900
|
+
...result,
|
|
32901
|
+
tuiGatewayRestarted: true,
|
|
32902
|
+
requiresGatewayReload: false,
|
|
32903
|
+
restartHint: `${label}\u5DF2\u4FDD\u5B58\uFF0C${profileName} Profile \u7684 tui_gateway \u5DF2\u91CD\u542F\u3002\u65B0\u7684 Run \u4F1A\u8BFB\u53D6\u6700\u65B0\u914D\u7F6E\u3002`
|
|
32904
|
+
};
|
|
32905
|
+
} catch (error) {
|
|
32906
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32907
|
+
await options.logger.warn("tui_gateway_restart_after_model_config_failed", {
|
|
32908
|
+
profile: profileName,
|
|
32909
|
+
error: message
|
|
32910
|
+
});
|
|
32911
|
+
return {
|
|
32912
|
+
...result,
|
|
32913
|
+
tuiGatewayRestarted: false,
|
|
32914
|
+
reloadError: message,
|
|
32915
|
+
requiresGatewayReload: true,
|
|
32916
|
+
restartHint: `${label}\u5DF2\u4FDD\u5B58\uFF0C\u4F46 ${profileName} Profile \u7684 tui_gateway \u81EA\u52A8\u91CD\u542F\u5931\u8D25\uFF1A${message}`
|
|
32917
|
+
};
|
|
32918
|
+
}
|
|
32919
|
+
}
|
|
32750
32920
|
async function reloadGatewayAfterProfileConfigChange(result, options) {
|
|
32751
32921
|
try {
|
|
32752
32922
|
await reloadHermesGateway({
|
|
@@ -32793,7 +32963,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32793
32963
|
router.get("/api/v1/model-configs", async (ctx) => {
|
|
32794
32964
|
await authenticateRequest(ctx, paths);
|
|
32795
32965
|
ctx.set("cache-control", "no-store");
|
|
32796
|
-
ctx.body = await
|
|
32966
|
+
ctx.body = await listVisibleHermesModelConfigs("default", paths);
|
|
32797
32967
|
});
|
|
32798
32968
|
router.get("/api/v1/model-configs/catalog", async (ctx) => {
|
|
32799
32969
|
await authenticateRequest(ctx, paths);
|
|
@@ -32831,8 +33001,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32831
33001
|
readModelProviderConfigInput(body),
|
|
32832
33002
|
ctx.params.name
|
|
32833
33003
|
);
|
|
33004
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33005
|
+
defaultReload: true
|
|
33006
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33007
|
+
paths,
|
|
33008
|
+
logger,
|
|
33009
|
+
profileName: ctx.params.name,
|
|
33010
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
33011
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32834
33012
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32835
|
-
|
|
33013
|
+
applied,
|
|
32836
33014
|
ctx.params.name,
|
|
32837
33015
|
paths
|
|
32838
33016
|
);
|
|
@@ -32865,8 +33043,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32865
33043
|
body,
|
|
32866
33044
|
paths
|
|
32867
33045
|
);
|
|
33046
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33047
|
+
defaultReload: true
|
|
33048
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33049
|
+
paths,
|
|
33050
|
+
logger,
|
|
33051
|
+
profileName: ctx.params.name,
|
|
33052
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
33053
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32868
33054
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32869
|
-
|
|
33055
|
+
applied,
|
|
32870
33056
|
ctx.params.name,
|
|
32871
33057
|
paths
|
|
32872
33058
|
);
|
|
@@ -32947,8 +33133,16 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32947
33133
|
},
|
|
32948
33134
|
ctx.params.name
|
|
32949
33135
|
);
|
|
33136
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33137
|
+
defaultReload: true
|
|
33138
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33139
|
+
paths,
|
|
33140
|
+
logger,
|
|
33141
|
+
profileName: ctx.params.name,
|
|
33142
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
33143
|
+
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
32950
33144
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32951
|
-
|
|
33145
|
+
applied,
|
|
32952
33146
|
ctx.params.name,
|
|
32953
33147
|
paths
|
|
32954
33148
|
);
|
|
@@ -32980,8 +33174,14 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32980
33174
|
provider.providerKey,
|
|
32981
33175
|
ctx.params.name
|
|
32982
33176
|
);
|
|
33177
|
+
const applied = await refreshRuntimeAfterModelConfigChange(result, {
|
|
33178
|
+
paths,
|
|
33179
|
+
logger,
|
|
33180
|
+
profileName: ctx.params.name,
|
|
33181
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
33182
|
+
});
|
|
32983
33183
|
ctx.body = await enrichProviderModelConfigResult(
|
|
32984
|
-
|
|
33184
|
+
applied,
|
|
32985
33185
|
ctx.params.name,
|
|
32986
33186
|
paths
|
|
32987
33187
|
);
|
|
@@ -32999,7 +33199,12 @@ function registerModelConfigRoutes(router, options) {
|
|
|
32999
33199
|
);
|
|
33000
33200
|
}
|
|
33001
33201
|
ctx.body = latest ? await enrichProviderModelConfigResult(
|
|
33002
|
-
|
|
33202
|
+
await refreshRuntimeAfterModelConfigChange(latest, {
|
|
33203
|
+
paths,
|
|
33204
|
+
logger,
|
|
33205
|
+
profileName: ctx.params.name,
|
|
33206
|
+
label: "\u6A21\u578B Provider \u914D\u7F6E"
|
|
33207
|
+
}),
|
|
33003
33208
|
ctx.params.name,
|
|
33004
33209
|
paths
|
|
33005
33210
|
) : await listModelProviderResponse(ctx.params.name, paths);
|
|
@@ -33012,7 +33217,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33012
33217
|
const result = await saveHermesModelConfig(readModelConfigInput(body));
|
|
33013
33218
|
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33014
33219
|
defaultReload: true
|
|
33015
|
-
}) ? await
|
|
33220
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33016
33221
|
paths,
|
|
33017
33222
|
logger
|
|
33018
33223
|
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
@@ -33028,8 +33233,10 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33028
33233
|
router.patch("/api/v1/model-configs/defaults", async (ctx) => {
|
|
33029
33234
|
await authenticateRequest(ctx, paths);
|
|
33030
33235
|
const body = await readJsonBody(ctx.req);
|
|
33236
|
+
const input = readModelDefaultsInput(body);
|
|
33031
33237
|
try {
|
|
33032
|
-
|
|
33238
|
+
await assertModelDefaultsSelectable(input, "default", paths);
|
|
33239
|
+
const result = await saveHermesModelDefaults(input);
|
|
33033
33240
|
ctx.body = await enrichProviderModelConfigResult(result, "default", paths);
|
|
33034
33241
|
} catch (error) {
|
|
33035
33242
|
throw toModelConfigHttpError(error);
|
|
@@ -33041,7 +33248,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33041
33248
|
const input = readModelDeleteInput(body);
|
|
33042
33249
|
try {
|
|
33043
33250
|
const result = await deleteHermesModelConfig(input);
|
|
33044
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
33251
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33252
|
+
defaultReload: true
|
|
33253
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33045
33254
|
paths,
|
|
33046
33255
|
logger
|
|
33047
33256
|
}) : markModelConfigAppliedWithoutGatewayReload(result);
|
|
@@ -33058,7 +33267,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33058
33267
|
await authenticateRequest(ctx, paths);
|
|
33059
33268
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
33060
33269
|
ctx.set("cache-control", "no-store");
|
|
33061
|
-
ctx.body = await
|
|
33270
|
+
ctx.body = await listVisibleHermesModelConfigs(ctx.params.name, paths);
|
|
33062
33271
|
});
|
|
33063
33272
|
router.post("/api/v1/profiles/:name/model-configs", async (ctx) => {
|
|
33064
33273
|
await authenticateRequest(ctx, paths);
|
|
@@ -33071,7 +33280,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33071
33280
|
);
|
|
33072
33281
|
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33073
33282
|
defaultReload: true
|
|
33074
|
-
}) ? await
|
|
33283
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33075
33284
|
paths,
|
|
33076
33285
|
logger,
|
|
33077
33286
|
profileName: ctx.params.name
|
|
@@ -33093,7 +33302,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33093
33302
|
await getHermesProfileStatus(input.sourceProfileName, paths);
|
|
33094
33303
|
try {
|
|
33095
33304
|
const result = await importHermesModelConfig(input, ctx.params.name);
|
|
33096
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
33305
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33306
|
+
defaultReload: true
|
|
33307
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33097
33308
|
paths,
|
|
33098
33309
|
logger,
|
|
33099
33310
|
profileName: ctx.params.name
|
|
@@ -33111,11 +33322,10 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33111
33322
|
await authenticateRequest(ctx, paths);
|
|
33112
33323
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
33113
33324
|
const body = await readJsonBody(ctx.req);
|
|
33325
|
+
const input = readModelDefaultsInput(body);
|
|
33114
33326
|
try {
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
ctx.params.name
|
|
33118
|
-
);
|
|
33327
|
+
await assertModelDefaultsSelectable(input, ctx.params.name, paths);
|
|
33328
|
+
const result = await saveHermesModelDefaults(input, ctx.params.name);
|
|
33119
33329
|
ctx.body = await enrichProviderModelConfigResult(
|
|
33120
33330
|
result,
|
|
33121
33331
|
ctx.params.name,
|
|
@@ -33132,7 +33342,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33132
33342
|
const input = readModelDeleteInput(body);
|
|
33133
33343
|
try {
|
|
33134
33344
|
const result = await deleteHermesModelConfig(input, ctx.params.name);
|
|
33135
|
-
const applied = shouldReloadGatewayAfterModelConfigChange(body
|
|
33345
|
+
const applied = shouldReloadGatewayAfterModelConfigChange(body, {
|
|
33346
|
+
defaultReload: true
|
|
33347
|
+
}) ? await refreshRuntimeAfterModelConfigChange(result, {
|
|
33136
33348
|
paths,
|
|
33137
33349
|
logger,
|
|
33138
33350
|
profileName: ctx.params.name
|
|
@@ -33148,7 +33360,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
33148
33360
|
});
|
|
33149
33361
|
}
|
|
33150
33362
|
async function listModelProviderResponse(profileName, paths, options = {}) {
|
|
33151
|
-
const configs = await listHermesModelConfigs(profileName);
|
|
33363
|
+
const configs = options.includeDisconnected ? await listHermesModelConfigs(profileName) : await listVisibleHermesModelConfigs(profileName, paths);
|
|
33152
33364
|
const providerConfigs = await listHermesModelProviderConfigs(profileName);
|
|
33153
33365
|
const catalog = await readModelCapabilityCatalog(paths);
|
|
33154
33366
|
const providers = groupManagedModelsByProvider(
|
|
@@ -33172,6 +33384,92 @@ async function listModelProviderResponse(profileName, paths, options = {}) {
|
|
|
33172
33384
|
providers: visibleProviders
|
|
33173
33385
|
};
|
|
33174
33386
|
}
|
|
33387
|
+
async function listVisibleHermesModelConfigs(profileName, paths) {
|
|
33388
|
+
const configs = await listHermesModelConfigs(profileName);
|
|
33389
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
33390
|
+
paths,
|
|
33391
|
+
profileName
|
|
33392
|
+
);
|
|
33393
|
+
return filterHermesModelConfigsByProviderKeys(configs, disconnectedProviders);
|
|
33394
|
+
}
|
|
33395
|
+
async function assertModelDefaultsSelectable(input, profileName, paths) {
|
|
33396
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
33397
|
+
paths,
|
|
33398
|
+
profileName
|
|
33399
|
+
);
|
|
33400
|
+
assertProviderNotDisconnected(
|
|
33401
|
+
disconnectedProviders,
|
|
33402
|
+
input.taskModelProvider,
|
|
33403
|
+
"task model"
|
|
33404
|
+
);
|
|
33405
|
+
assertProviderNotDisconnected(
|
|
33406
|
+
disconnectedProviders,
|
|
33407
|
+
input.compressionModelProvider,
|
|
33408
|
+
"compression model"
|
|
33409
|
+
);
|
|
33410
|
+
if (disconnectedProviders.size === 0 || !input.taskModelId && !input.compressionModelId) {
|
|
33411
|
+
return;
|
|
33412
|
+
}
|
|
33413
|
+
const allConfigs = await listHermesModelConfigs(profileName);
|
|
33414
|
+
assertModelIdNotHiddenByDisconnect(allConfigs.models, disconnectedProviders, {
|
|
33415
|
+
id: input.taskModelId,
|
|
33416
|
+
provider: input.taskModelProvider,
|
|
33417
|
+
baseUrl: input.taskModelBaseUrl,
|
|
33418
|
+
apiMode: input.taskModelApiMode,
|
|
33419
|
+
label: "task model"
|
|
33420
|
+
});
|
|
33421
|
+
assertModelIdNotHiddenByDisconnect(allConfigs.models, disconnectedProviders, {
|
|
33422
|
+
id: input.compressionModelId,
|
|
33423
|
+
provider: input.compressionModelProvider,
|
|
33424
|
+
baseUrl: input.compressionModelBaseUrl,
|
|
33425
|
+
apiMode: input.compressionModelApiMode,
|
|
33426
|
+
label: "compression model"
|
|
33427
|
+
});
|
|
33428
|
+
}
|
|
33429
|
+
function assertProviderNotDisconnected(disconnectedProviders, provider, label) {
|
|
33430
|
+
if (provider && disconnectedProviders.has(provider)) {
|
|
33431
|
+
throw new LinkHttpError(
|
|
33432
|
+
409,
|
|
33433
|
+
"model_provider_disconnected",
|
|
33434
|
+
`The ${label} provider "${provider}" has been disconnected from this Profile.`
|
|
33435
|
+
);
|
|
33436
|
+
}
|
|
33437
|
+
}
|
|
33438
|
+
function assertModelIdNotHiddenByDisconnect(models, disconnectedProviders, selector) {
|
|
33439
|
+
const id = selector.id?.trim();
|
|
33440
|
+
if (!id) {
|
|
33441
|
+
return;
|
|
33442
|
+
}
|
|
33443
|
+
const matchingModels = models.filter((model) => {
|
|
33444
|
+
if (model.id !== id) {
|
|
33445
|
+
return false;
|
|
33446
|
+
}
|
|
33447
|
+
if (selector.provider && model.provider !== selector.provider) {
|
|
33448
|
+
return false;
|
|
33449
|
+
}
|
|
33450
|
+
if (selector.baseUrl !== void 0 && normalizeUrlForIdentity(model.baseUrl) !== normalizeUrlForIdentity(selector.baseUrl)) {
|
|
33451
|
+
return false;
|
|
33452
|
+
}
|
|
33453
|
+
if (selector.apiMode && model.apiMode !== selector.apiMode) {
|
|
33454
|
+
return false;
|
|
33455
|
+
}
|
|
33456
|
+
return true;
|
|
33457
|
+
});
|
|
33458
|
+
if (matchingModels.some((model) => !disconnectedProviders.has(model.provider))) {
|
|
33459
|
+
return;
|
|
33460
|
+
}
|
|
33461
|
+
const hiddenMatch = matchingModels.find(
|
|
33462
|
+
(model) => disconnectedProviders.has(model.provider)
|
|
33463
|
+
);
|
|
33464
|
+
if (!hiddenMatch) {
|
|
33465
|
+
return;
|
|
33466
|
+
}
|
|
33467
|
+
throw new LinkHttpError(
|
|
33468
|
+
409,
|
|
33469
|
+
"model_provider_disconnected",
|
|
33470
|
+
`The ${selector.label} "${id}" belongs to disconnected provider "${hiddenMatch.provider}".`
|
|
33471
|
+
);
|
|
33472
|
+
}
|
|
33175
33473
|
async function enrichProviderModelConfigResult(result, profileName, paths) {
|
|
33176
33474
|
const providerResponse = await listModelProviderResponse(profileName, paths);
|
|
33177
33475
|
return {
|
|
@@ -33830,12 +34128,12 @@ function registerProfileCatalogRoutes(router, options) {
|
|
|
33830
34128
|
ok: true,
|
|
33831
34129
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33832
34130
|
profiles: await Promise.all(
|
|
33833
|
-
profiles.map((profile) => readProfileCatalogItem(profile))
|
|
34131
|
+
profiles.map((profile) => readProfileCatalogItem(profile, paths))
|
|
33834
34132
|
)
|
|
33835
34133
|
};
|
|
33836
34134
|
});
|
|
33837
34135
|
}
|
|
33838
|
-
async function readProfileCatalogItem(profile) {
|
|
34136
|
+
async function readProfileCatalogItem(profile, paths) {
|
|
33839
34137
|
const [apiServer, capabilities, permissions, modelConfigs] = await Promise.all([
|
|
33840
34138
|
readCatalogField(
|
|
33841
34139
|
"apiServer",
|
|
@@ -33849,7 +34147,10 @@ async function readProfileCatalogItem(profile) {
|
|
|
33849
34147
|
"permissions",
|
|
33850
34148
|
() => readHermesProfilePermissions(profile.name)
|
|
33851
34149
|
),
|
|
33852
|
-
readCatalogField(
|
|
34150
|
+
readCatalogField(
|
|
34151
|
+
"modelConfigs",
|
|
34152
|
+
() => listVisibleHermesModelConfigs2(profile.name, paths)
|
|
34153
|
+
)
|
|
33853
34154
|
]);
|
|
33854
34155
|
return {
|
|
33855
34156
|
profile,
|
|
@@ -33870,6 +34171,14 @@ async function readProfileCatalogItem(profile) {
|
|
|
33870
34171
|
]
|
|
33871
34172
|
};
|
|
33872
34173
|
}
|
|
34174
|
+
async function listVisibleHermesModelConfigs2(profileName, paths) {
|
|
34175
|
+
const configs = await listHermesModelConfigs(profileName);
|
|
34176
|
+
const disconnectedProviders = await readDisconnectedAuthBackedModelProviderKeys(
|
|
34177
|
+
paths,
|
|
34178
|
+
profileName
|
|
34179
|
+
);
|
|
34180
|
+
return filterHermesModelConfigsByProviderKeys(configs, disconnectedProviders);
|
|
34181
|
+
}
|
|
33873
34182
|
async function readCatalogField(field, load) {
|
|
33874
34183
|
try {
|
|
33875
34184
|
return { value: await load(), errors: [] };
|
|
@@ -39698,11 +40007,11 @@ async function mergeLastReportedPublicRoutes(paths, snapshotInput) {
|
|
|
39698
40007
|
const state = await readNetworkReportState(paths);
|
|
39699
40008
|
return {
|
|
39700
40009
|
...snapshotInput,
|
|
39701
|
-
publicIpv4s:
|
|
40010
|
+
publicIpv4s: uniqueStrings3([
|
|
39702
40011
|
...snapshotInput.publicIpv4s,
|
|
39703
40012
|
...state.lastReportedPublicIpv4s
|
|
39704
40013
|
]).slice(0, 2),
|
|
39705
|
-
publicIpv6s:
|
|
40014
|
+
publicIpv6s: uniqueStrings3([
|
|
39706
40015
|
...snapshotInput.publicIpv6s,
|
|
39707
40016
|
...state.lastReportedPublicIpv6s
|
|
39708
40017
|
]).slice(0, 2)
|
|
@@ -39794,7 +40103,7 @@ function sameStringList2(left, right) {
|
|
|
39794
40103
|
}
|
|
39795
40104
|
return left.every((value, index) => value === right[index]);
|
|
39796
40105
|
}
|
|
39797
|
-
function
|
|
40106
|
+
function uniqueStrings3(values) {
|
|
39798
40107
|
return [...new Set(values)];
|
|
39799
40108
|
}
|
|
39800
40109
|
function formatUtcDay(date) {
|