openclaw-clawtown-plugin 1.1.12 → 1.1.14
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/reporter.ts +355 -4
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-clawtown-plugin",
|
|
3
3
|
"name": "OpenClaw Clawtown Plugin",
|
|
4
4
|
"description": "Connects an OpenClaw agent to OpenClaw Forum and reports forum actions",
|
|
5
|
-
"version": "1.1.
|
|
5
|
+
"version": "1.1.14",
|
|
6
6
|
"main": "./index.ts",
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED
package/reporter.ts
CHANGED
|
@@ -124,6 +124,20 @@ interface ReporterRuntimeInfo {
|
|
|
124
124
|
pluginHash: string;
|
|
125
125
|
syncedAt: number;
|
|
126
126
|
pluginDir: string;
|
|
127
|
+
openClawHome?: string | null;
|
|
128
|
+
isolationMode?: string;
|
|
129
|
+
isolationActive?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface AuthHealthCheck {
|
|
133
|
+
ok: boolean;
|
|
134
|
+
storePath: string;
|
|
135
|
+
modelsPath: string;
|
|
136
|
+
requiredProviders: string[];
|
|
137
|
+
availableProviders: string[];
|
|
138
|
+
invalidProfileIds: string[];
|
|
139
|
+
missingProviders: string[];
|
|
140
|
+
reason: string;
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
class Reporter {
|
|
@@ -158,8 +172,17 @@ class Reporter {
|
|
|
158
172
|
private reporterRuntime: ReporterRuntimeInfo = readReporterRuntimeInfo();
|
|
159
173
|
private forcedOpenClawHome = "";
|
|
160
174
|
private openClawIsolationMode = "unknown";
|
|
175
|
+
private authHealth: AuthHealthCheck | null = null;
|
|
161
176
|
|
|
162
177
|
constructor() {
|
|
178
|
+
const legacyRuntime = handleLegacyRuntimeConflict(this.reporterRuntime);
|
|
179
|
+
if (legacyRuntime.summary) {
|
|
180
|
+
console.warn(`[forum-reporter-v2] ${legacyRuntime.summary}`);
|
|
181
|
+
}
|
|
182
|
+
if (legacyRuntime.disableCurrentRuntime) {
|
|
183
|
+
this.bridgeDisabled = true;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
163
186
|
const initialLocal = readLocalReporterConfig();
|
|
164
187
|
const forumHomeBootstrap = ensureForumIsolatedHome(this.reporterRuntime, initialLocal);
|
|
165
188
|
if (forumHomeBootstrap.summary) {
|
|
@@ -191,6 +214,11 @@ class Reporter {
|
|
|
191
214
|
isolationMode: this.openClawIsolationMode,
|
|
192
215
|
isolationActive: Boolean(this.forcedOpenClawHome),
|
|
193
216
|
};
|
|
217
|
+
const authHealth = inspectAuthHealthForStateDir(resolveStateDirForConfiguredHome(this.forcedOpenClawHome));
|
|
218
|
+
this.authHealth = authHealth;
|
|
219
|
+
if (!authHealth.ok) {
|
|
220
|
+
console.error(`[forum-reporter-v2] ${formatAuthHealthFailure(authHealth)}`);
|
|
221
|
+
}
|
|
194
222
|
if (!this.userId || !this.apiKey) {
|
|
195
223
|
console.warn("[forum-reporter-v2] 未配置 userId/apiKey,插件已禁用");
|
|
196
224
|
}
|
|
@@ -667,9 +695,17 @@ class Reporter {
|
|
|
667
695
|
const pairCode = String(payload?.pairCode ?? "").trim();
|
|
668
696
|
if (/^\d{6}$/.test(pairCode) && pairCode !== this.lastPairCodeShown) {
|
|
669
697
|
this.lastPairCodeShown = pairCode;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
698
|
+
const displayName = String(payload?.displayName ?? "").trim() || this.userId;
|
|
699
|
+
if (this.authHealth?.ok === false) {
|
|
700
|
+
console.error(`❌ 机器人「${displayName}」已连上共答社区 V2,但本地模型鉴权无效,任务暂时不会执行`);
|
|
701
|
+
console.error(`🔑 配对码:${pairCode}`);
|
|
702
|
+
console.error(`[forum-reporter-v2] ${formatAuthHealthFailure(this.authHealth)}`);
|
|
703
|
+
console.error("请先修复主 OpenClaw 的模型鉴权,再重启本地 OpenClaw 网关");
|
|
704
|
+
} else {
|
|
705
|
+
console.log(`✅ 你的机器人「${displayName}」已接入共答社区 V2`);
|
|
706
|
+
console.log(`🔑 配对码:${pairCode}`);
|
|
707
|
+
console.log("请在论坛首页「我的机器人」中输入此配对码进行绑定");
|
|
708
|
+
}
|
|
673
709
|
}
|
|
674
710
|
} catch (error) {
|
|
675
711
|
console.warn("[forum-reporter-v2] profile sync failed", error);
|
|
@@ -1575,6 +1611,86 @@ function looksLikeOpenClawHome(homePath: string) {
|
|
|
1575
1611
|
}
|
|
1576
1612
|
}
|
|
1577
1613
|
|
|
1614
|
+
function handleLegacyRuntimeConflict(runtimeInfo: ReporterRuntimeInfo) {
|
|
1615
|
+
const runtimeDir = path.resolve(String(runtimeInfo?.pluginDir ?? "").trim() || ".");
|
|
1616
|
+
if (path.basename(runtimeDir) !== LEGACY_PLUGIN_ID) {
|
|
1617
|
+
return { disableCurrentRuntime: false, summary: "" };
|
|
1618
|
+
}
|
|
1619
|
+
const canonicalCandidates = [
|
|
1620
|
+
path.join(path.dirname(runtimeDir), PLUGIN_ID),
|
|
1621
|
+
path.join(os.homedir(), DEFAULT_OPENCLAW_HOME_DIRNAME, "extensions", PLUGIN_ID),
|
|
1622
|
+
path.join(os.homedir(), FORUM_ISOLATED_HOME_DIRNAME, DEFAULT_OPENCLAW_HOME_DIRNAME, "extensions", PLUGIN_ID),
|
|
1623
|
+
];
|
|
1624
|
+
const hasCanonical = canonicalCandidates.some((candidate) => {
|
|
1625
|
+
const resolved = path.resolve(candidate);
|
|
1626
|
+
return resolved !== runtimeDir && hasUsablePluginDir(resolved);
|
|
1627
|
+
});
|
|
1628
|
+
if (!hasCanonical) {
|
|
1629
|
+
return { disableCurrentRuntime: false, summary: "" };
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
const changedParts = new Set<string>();
|
|
1633
|
+
const defaultStateDir = path.join(os.homedir(), DEFAULT_OPENCLAW_HOME_DIRNAME);
|
|
1634
|
+
const forumStateDir = path.join(os.homedir(), FORUM_ISOLATED_HOME_DIRNAME, DEFAULT_OPENCLAW_HOME_DIRNAME);
|
|
1635
|
+
if (stripLegacyPluginState(defaultStateDir, runtimeDir)) changedParts.add("default legacy state cleaned");
|
|
1636
|
+
if (stripLegacyPluginState(forumStateDir, runtimeDir)) changedParts.add("forum legacy state cleaned");
|
|
1637
|
+
if (quarantineLegacyRuntimeDir(runtimeDir)) changedParts.add("legacy dir quarantined");
|
|
1638
|
+
|
|
1639
|
+
const detail = changedParts.size ? ` (${Array.from(changedParts).join(", ")})` : "";
|
|
1640
|
+
return {
|
|
1641
|
+
disableCurrentRuntime: true,
|
|
1642
|
+
summary: `legacy forum-reporter runtime detected at ${runtimeDir}; disabling this instance to avoid mixed execution${detail}`,
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
function stripLegacyPluginState(stateDir: string, activeLegacyDir = "") {
|
|
1647
|
+
if (!stateDir || !fs.existsSync(stateDir)) return false;
|
|
1648
|
+
let changed = false;
|
|
1649
|
+
const configInfo = readOpenClawConfigInfo(stateDir);
|
|
1650
|
+
if (configInfo.path && configInfo.config) {
|
|
1651
|
+
const nextConfig = cloneJson(configInfo.config);
|
|
1652
|
+
const plugins = (nextConfig.plugins ??= {});
|
|
1653
|
+
const allow = Array.isArray(plugins.allow) ? plugins.allow : [];
|
|
1654
|
+
const nextAllow = allow.filter((item: any) => String(item) !== LEGACY_PLUGIN_ID);
|
|
1655
|
+
if (nextAllow.length !== allow.length) {
|
|
1656
|
+
plugins.allow = nextAllow;
|
|
1657
|
+
changed = true;
|
|
1658
|
+
}
|
|
1659
|
+
if (plugins.entries && Object.prototype.hasOwnProperty.call(plugins.entries, LEGACY_PLUGIN_ID)) {
|
|
1660
|
+
delete plugins.entries[LEGACY_PLUGIN_ID];
|
|
1661
|
+
changed = true;
|
|
1662
|
+
}
|
|
1663
|
+
if (plugins.installs && Object.prototype.hasOwnProperty.call(plugins.installs, LEGACY_PLUGIN_ID)) {
|
|
1664
|
+
delete plugins.installs[LEGACY_PLUGIN_ID];
|
|
1665
|
+
changed = true;
|
|
1666
|
+
}
|
|
1667
|
+
if (changed) {
|
|
1668
|
+
writeOpenClawConfig(configInfo.path, nextConfig);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
const legacyDir = path.join(stateDir, "extensions", LEGACY_PLUGIN_ID);
|
|
1672
|
+
if (path.resolve(legacyDir) !== path.resolve(activeLegacyDir || "")) {
|
|
1673
|
+
changed = removeDirectoryIfExists(legacyDir) || changed;
|
|
1674
|
+
}
|
|
1675
|
+
return changed;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
function quarantineLegacyRuntimeDir(runtimeDir: string) {
|
|
1679
|
+
if (!runtimeDir || !fs.existsSync(runtimeDir)) return false;
|
|
1680
|
+
const parent = path.dirname(runtimeDir);
|
|
1681
|
+
const targetDir = path.join(parent, `${LEGACY_PLUGIN_ID}-disabled`);
|
|
1682
|
+
if (path.resolve(targetDir) === path.resolve(runtimeDir)) return false;
|
|
1683
|
+
try {
|
|
1684
|
+
if (fs.existsSync(targetDir)) {
|
|
1685
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
1686
|
+
}
|
|
1687
|
+
fs.renameSync(runtimeDir, targetDir);
|
|
1688
|
+
return true;
|
|
1689
|
+
} catch {
|
|
1690
|
+
return false;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1578
1694
|
function ensureForumIsolatedHome(
|
|
1579
1695
|
runtimeInfo: ReporterRuntimeInfo,
|
|
1580
1696
|
sourceLocalConfig: ReturnType<typeof readLocalReporterConfig>,
|
|
@@ -1664,6 +1780,237 @@ function hashDirectory(dirPath: string) {
|
|
|
1664
1780
|
}
|
|
1665
1781
|
}
|
|
1666
1782
|
|
|
1783
|
+
function resolveStateDirForConfiguredHome(openclawHome: string) {
|
|
1784
|
+
if (openclawHome) {
|
|
1785
|
+
const resolved = path.resolve(openclawHome);
|
|
1786
|
+
if (path.basename(resolved) === DEFAULT_OPENCLAW_HOME_DIRNAME) return resolved;
|
|
1787
|
+
return path.join(resolved, DEFAULT_OPENCLAW_HOME_DIRNAME);
|
|
1788
|
+
}
|
|
1789
|
+
return path.join(os.homedir(), DEFAULT_OPENCLAW_HOME_DIRNAME);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
function inspectAuthHealthForStateDir(stateDir: string): AuthHealthCheck {
|
|
1793
|
+
const configInfo = readOpenClawConfigInfo(stateDir);
|
|
1794
|
+
const requiredProviders = resolveConfiguredProviders(configInfo.config);
|
|
1795
|
+
const storePath = path.join(stateDir, "agents", "main", "agent", "auth-profiles.json");
|
|
1796
|
+
const modelsPath = path.join(stateDir, "agents", "main", "agent", "models.json");
|
|
1797
|
+
const availableProviders = new Set<string>(readInlineCredentialProviders(stateDir, configInfo.config));
|
|
1798
|
+
const invalidProfileIds: string[] = [];
|
|
1799
|
+
if (!fs.existsSync(storePath)) {
|
|
1800
|
+
const missingProviders = requiredProviders.filter((provider) => !availableProviders.has(provider));
|
|
1801
|
+
return {
|
|
1802
|
+
ok: missingProviders.length === 0,
|
|
1803
|
+
storePath,
|
|
1804
|
+
modelsPath,
|
|
1805
|
+
requiredProviders,
|
|
1806
|
+
availableProviders: Array.from(availableProviders),
|
|
1807
|
+
invalidProfileIds,
|
|
1808
|
+
missingProviders,
|
|
1809
|
+
reason: missingProviders.length ? "missing_store" : "ok",
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
let parsed: Record<string, any> | null = null;
|
|
1813
|
+
try {
|
|
1814
|
+
parsed = JSON.parse(fs.readFileSync(storePath, "utf-8"));
|
|
1815
|
+
} catch {
|
|
1816
|
+
return {
|
|
1817
|
+
ok: false,
|
|
1818
|
+
storePath,
|
|
1819
|
+
modelsPath,
|
|
1820
|
+
requiredProviders,
|
|
1821
|
+
availableProviders: Array.from(availableProviders),
|
|
1822
|
+
invalidProfileIds,
|
|
1823
|
+
missingProviders: requiredProviders.filter((provider) => !availableProviders.has(provider)),
|
|
1824
|
+
reason: "invalid_json",
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
const profiles = parsed?.profiles && typeof parsed.profiles === "object"
|
|
1828
|
+
? parsed.profiles as Record<string, any>
|
|
1829
|
+
: null;
|
|
1830
|
+
if (!profiles) {
|
|
1831
|
+
return {
|
|
1832
|
+
ok: false,
|
|
1833
|
+
storePath,
|
|
1834
|
+
modelsPath,
|
|
1835
|
+
requiredProviders,
|
|
1836
|
+
availableProviders: Array.from(availableProviders),
|
|
1837
|
+
invalidProfileIds,
|
|
1838
|
+
missingProviders: requiredProviders.filter((provider) => !availableProviders.has(provider)),
|
|
1839
|
+
reason: "invalid_store",
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
for (const [profileId, profile] of Object.entries(profiles)) {
|
|
1843
|
+
const normalized = normalizeAuthProfile(profile);
|
|
1844
|
+
if (!normalized.valid || !normalized.provider) {
|
|
1845
|
+
invalidProfileIds.push(profileId);
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
availableProviders.add(normalized.provider);
|
|
1849
|
+
}
|
|
1850
|
+
const missingProviders = requiredProviders.filter((provider) => !availableProviders.has(provider));
|
|
1851
|
+
return {
|
|
1852
|
+
ok: missingProviders.length === 0,
|
|
1853
|
+
storePath,
|
|
1854
|
+
modelsPath,
|
|
1855
|
+
requiredProviders,
|
|
1856
|
+
availableProviders: Array.from(availableProviders),
|
|
1857
|
+
invalidProfileIds,
|
|
1858
|
+
missingProviders,
|
|
1859
|
+
reason: missingProviders.length ? "missing_provider" : "ok",
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
function normalizeAuthProfile(profile: any) {
|
|
1864
|
+
if (!profile || typeof profile !== "object") {
|
|
1865
|
+
return { valid: false, provider: "" };
|
|
1866
|
+
}
|
|
1867
|
+
const type = String(profile.type ?? "").trim().toLowerCase();
|
|
1868
|
+
const provider = String(profile.provider ?? "").trim().toLowerCase();
|
|
1869
|
+
if (!type || !provider) {
|
|
1870
|
+
return { valid: false, provider };
|
|
1871
|
+
}
|
|
1872
|
+
if (type === "api_key") {
|
|
1873
|
+
return {
|
|
1874
|
+
valid: hasNonEmptyString(profile.key) || hasNonEmptyString(profile.keyRef),
|
|
1875
|
+
provider,
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
if (type === "token") {
|
|
1879
|
+
return {
|
|
1880
|
+
valid: hasNonEmptyString(profile.token) || hasNonEmptyString(profile.tokenRef),
|
|
1881
|
+
provider,
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
if (type === "oauth") {
|
|
1885
|
+
return {
|
|
1886
|
+
valid: hasNonEmptyString(profile.access) || hasNonEmptyString(profile.refresh),
|
|
1887
|
+
provider,
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
return { valid: false, provider };
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function hasNonEmptyString(value: unknown) {
|
|
1894
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function resolveConfiguredProviders(config: Record<string, any> | null) {
|
|
1898
|
+
const out = new Set<string>();
|
|
1899
|
+
const pushModelRef = (value: unknown) => {
|
|
1900
|
+
const raw = String(value ?? "").trim();
|
|
1901
|
+
if (!raw) return;
|
|
1902
|
+
const provider = raw.includes("/") ? raw.slice(0, raw.indexOf("/")).trim().toLowerCase() : raw.toLowerCase();
|
|
1903
|
+
if (provider) out.add(provider);
|
|
1904
|
+
};
|
|
1905
|
+
const visitModelNode = (value: unknown) => {
|
|
1906
|
+
if (typeof value === "string") {
|
|
1907
|
+
if (value.includes("/")) pushModelRef(value);
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
if (Array.isArray(value)) {
|
|
1911
|
+
for (const item of value) visitModelNode(item);
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
if (!value || typeof value !== "object") return;
|
|
1915
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1916
|
+
if (key === "providers" || key === "mode") continue;
|
|
1917
|
+
if (typeof nested === "string") {
|
|
1918
|
+
if (key === "primary" || key === "fallback" || key === "model" || nested.includes("/")) {
|
|
1919
|
+
pushModelRef(nested);
|
|
1920
|
+
}
|
|
1921
|
+
continue;
|
|
1922
|
+
}
|
|
1923
|
+
visitModelNode(nested);
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
pushModelRef(config?.agent?.model?.primary);
|
|
1927
|
+
pushModelRef(config?.agents?.defaults?.model?.primary);
|
|
1928
|
+
pushModelRef(config?.model?.primary);
|
|
1929
|
+
const modelMaps = [
|
|
1930
|
+
config?.agent?.models,
|
|
1931
|
+
config?.agents?.defaults?.models,
|
|
1932
|
+
config?.models,
|
|
1933
|
+
];
|
|
1934
|
+
for (const map of modelMaps) {
|
|
1935
|
+
visitModelNode(map);
|
|
1936
|
+
}
|
|
1937
|
+
const providerMaps = [
|
|
1938
|
+
config?.agent?.models?.providers,
|
|
1939
|
+
config?.agents?.defaults?.models?.providers,
|
|
1940
|
+
config?.models?.providers,
|
|
1941
|
+
];
|
|
1942
|
+
for (const map of providerMaps) {
|
|
1943
|
+
if (!map || typeof map !== "object") continue;
|
|
1944
|
+
for (const key of Object.keys(map)) pushModelRef(key);
|
|
1945
|
+
}
|
|
1946
|
+
return Array.from(out);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
function readInlineCredentialProviders(stateDir: string, config: Record<string, any> | null) {
|
|
1950
|
+
const providers = new Set<string>();
|
|
1951
|
+
const mergeProviderMap = (map: Record<string, any> | null | undefined) => {
|
|
1952
|
+
if (!map || typeof map !== "object") return;
|
|
1953
|
+
for (const [providerName, value] of Object.entries(map)) {
|
|
1954
|
+
if (!hasInlineProviderCredential(value)) continue;
|
|
1955
|
+
const normalized = String(providerName ?? "").trim().toLowerCase();
|
|
1956
|
+
if (normalized) providers.add(normalized);
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
|
|
1960
|
+
mergeProviderMap(config?.agent?.models?.providers);
|
|
1961
|
+
mergeProviderMap(config?.agents?.defaults?.models?.providers);
|
|
1962
|
+
mergeProviderMap(config?.models?.providers);
|
|
1963
|
+
|
|
1964
|
+
try {
|
|
1965
|
+
const modelsPath = path.join(stateDir, "agents", "main", "agent", "models.json");
|
|
1966
|
+
if (fs.existsSync(modelsPath)) {
|
|
1967
|
+
const parsed = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
|
|
1968
|
+
mergeProviderMap(parsed?.providers);
|
|
1969
|
+
}
|
|
1970
|
+
} catch {}
|
|
1971
|
+
|
|
1972
|
+
return Array.from(providers);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
function hasInlineProviderCredential(value: any) {
|
|
1976
|
+
if (!value || typeof value !== "object") return false;
|
|
1977
|
+
const headers = value.headers && typeof value.headers === "object" ? value.headers : {};
|
|
1978
|
+
return hasNonEmptyString(value.apiKey)
|
|
1979
|
+
|| hasNonEmptyString(value.api_key)
|
|
1980
|
+
|| hasNonEmptyString(value.key)
|
|
1981
|
+
|| hasNonEmptyString(value.token)
|
|
1982
|
+
|| hasNonEmptyString(value.accessToken)
|
|
1983
|
+
|| hasNonEmptyString(value.access_token)
|
|
1984
|
+
|| hasNonEmptyString(value.authToken)
|
|
1985
|
+
|| hasNonEmptyString(value.auth_token)
|
|
1986
|
+
|| hasNonEmptyString(headers.Authorization)
|
|
1987
|
+
|| hasNonEmptyString(headers.authorization)
|
|
1988
|
+
|| hasNonEmptyString(headers["x-api-key"])
|
|
1989
|
+
|| hasNonEmptyString(headers["X-API-Key"]);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
function formatAuthHealthFailure(health: AuthHealthCheck) {
|
|
1993
|
+
const parts: string[] = [`forum auth check failed: ${health.storePath}`];
|
|
1994
|
+
if (health.reason === "missing_store") {
|
|
1995
|
+
parts.push("auth-profiles.json not found");
|
|
1996
|
+
} else if (health.reason === "invalid_json") {
|
|
1997
|
+
parts.push("auth-profiles.json is not valid JSON");
|
|
1998
|
+
} else if (health.reason === "invalid_store") {
|
|
1999
|
+
parts.push("auth-profiles.json missing required profiles object");
|
|
2000
|
+
}
|
|
2001
|
+
if (health.invalidProfileIds.length) {
|
|
2002
|
+
parts.push(`invalid profiles: ${health.invalidProfileIds.slice(0, 5).join(", ")}`);
|
|
2003
|
+
}
|
|
2004
|
+
if (health.modelsPath) {
|
|
2005
|
+
parts.push(`models: ${health.modelsPath}`);
|
|
2006
|
+
}
|
|
2007
|
+
if (health.requiredProviders.length) {
|
|
2008
|
+
const available = health.availableProviders.length ? health.availableProviders.join(", ") : "none";
|
|
2009
|
+
parts.push(`required providers: ${health.requiredProviders.join(", ")}; available: ${available}`);
|
|
2010
|
+
}
|
|
2011
|
+
return parts.join(" | ");
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1667
2014
|
function syncReporterConfigToForum(
|
|
1668
2015
|
sourceLocalConfig: ReturnType<typeof readLocalReporterConfig>,
|
|
1669
2016
|
forumStateDir: string,
|
|
@@ -1685,7 +2032,10 @@ function syncAgentAuthProfiles(sourceStateDir: string, targetStateDir: string) {
|
|
|
1685
2032
|
if (!fs.existsSync(sourceAgentsDir)) return false;
|
|
1686
2033
|
let changed = false;
|
|
1687
2034
|
const relFiles = listFilesRecursive(sourceAgentsDir)
|
|
1688
|
-
.filter((rel) =>
|
|
2035
|
+
.filter((rel) => {
|
|
2036
|
+
const base = path.basename(rel);
|
|
2037
|
+
return base === "auth-profiles.json" || base === "models.json" || base === "auth.json";
|
|
2038
|
+
});
|
|
1689
2039
|
for (const rel of relFiles) {
|
|
1690
2040
|
const sourcePath = path.join(sourceAgentsDir, rel);
|
|
1691
2041
|
const targetPath = path.join(targetStateDir, "agents", rel);
|
|
@@ -1759,6 +2109,7 @@ function normalizeForumHomeConfig(
|
|
|
1759
2109
|
forumConfig: Record<string, any> | null,
|
|
1760
2110
|
) {
|
|
1761
2111
|
const config = cloneJson(inputConfig);
|
|
2112
|
+
delete config.channels;
|
|
1762
2113
|
const gateway = (config.gateway ??= {});
|
|
1763
2114
|
const auth = (gateway.auth ??= {});
|
|
1764
2115
|
const authToken = typeof auth.token === "string" ? auth.token.trim() : "";
|