@xfxstudio/claworld 0.2.20 → 0.2.22
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
CHANGED
package/package.json
CHANGED
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
resolveClaworldRuntimeConfig,
|
|
18
18
|
validateClaworldChannelConfig,
|
|
19
19
|
} from './config-schema.js';
|
|
20
|
+
import {
|
|
21
|
+
loadClaworldRuntimeBackup,
|
|
22
|
+
persistClaworldRuntimeBackup,
|
|
23
|
+
} from './runtime-backup.js';
|
|
20
24
|
import {
|
|
21
25
|
claworldOnboardingAdapter,
|
|
22
26
|
claworldSetupAdapter,
|
|
@@ -1895,48 +1899,118 @@ export function createClaworldChannelPlugin({
|
|
|
1895
1899
|
}
|
|
1896
1900
|
|
|
1897
1901
|
async function persistRuntimeAppToken({ runtime, accountId, appToken, relayAgentId = null }) {
|
|
1898
|
-
if (!runtime?.config?.loadConfig || !runtime?.config?.writeConfigFile) {
|
|
1899
|
-
return { skipped: true, reason: 'missing_runtime_config_io' };
|
|
1900
|
-
}
|
|
1901
1902
|
if (!accountId || !appToken) {
|
|
1902
1903
|
return { skipped: true, reason: 'missing_account_or_token' };
|
|
1903
1904
|
}
|
|
1904
1905
|
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1906
|
+
let configPersistResult = { skipped: true, reason: 'missing_runtime_config_io' };
|
|
1907
|
+
if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
|
|
1908
|
+
const currentCfg = await runtime.config.loadConfig();
|
|
1909
|
+
const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
|
|
1910
|
+
nextCfg.channels = nextCfg.channels && typeof nextCfg.channels === 'object' && !Array.isArray(nextCfg.channels)
|
|
1911
|
+
? nextCfg.channels
|
|
1912
|
+
: {};
|
|
1913
|
+
const claworldRoot = nextCfg.channels.claworld && typeof nextCfg.channels.claworld === 'object' && !Array.isArray(nextCfg.channels.claworld)
|
|
1914
|
+
? nextCfg.channels.claworld
|
|
1915
|
+
: {};
|
|
1916
|
+
const accounts = claworldRoot.accounts && typeof claworldRoot.accounts === 'object' && !Array.isArray(claworldRoot.accounts)
|
|
1917
|
+
? claworldRoot.accounts
|
|
1918
|
+
: {};
|
|
1919
|
+
const account = accounts[accountId] && typeof accounts[accountId] === 'object' && !Array.isArray(accounts[accountId])
|
|
1920
|
+
? accounts[accountId]
|
|
1921
|
+
: {};
|
|
1922
|
+
|
|
1923
|
+
const normalizedRelayAgentId = normalizeClaworldText(relayAgentId, normalizeClaworldText(account?.relay?.agentId, null));
|
|
1924
|
+
const currentAppToken = normalizeClaworldText(account.appToken, null);
|
|
1925
|
+
const currentRelayAgentId = normalizeClaworldText(account?.relay?.agentId, null);
|
|
1926
|
+
if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
|
|
1927
|
+
configPersistResult = { skipped: true, reason: 'already_persisted' };
|
|
1928
|
+
} else {
|
|
1929
|
+
accounts[accountId] = {
|
|
1930
|
+
...account,
|
|
1931
|
+
appToken,
|
|
1932
|
+
relay: {
|
|
1933
|
+
...(account?.relay && typeof account.relay === 'object' && !Array.isArray(account.relay) ? account.relay : {}),
|
|
1934
|
+
...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
|
|
1935
|
+
},
|
|
1936
|
+
};
|
|
1937
|
+
delete accounts[accountId].registration;
|
|
1938
|
+
claworldRoot.accounts = accounts;
|
|
1939
|
+
nextCfg.channels.claworld = claworldRoot;
|
|
1940
|
+
await runtime.config.writeConfigFile(nextCfg);
|
|
1941
|
+
configPersistResult = { skipped: false, ok: true };
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
let backupPersistResult = { skipped: true, reason: 'missing_runtime_config_loader' };
|
|
1946
|
+
try {
|
|
1947
|
+
backupPersistResult = await persistClaworldRuntimeBackup({
|
|
1948
|
+
runtime,
|
|
1949
|
+
accountId,
|
|
1950
|
+
});
|
|
1951
|
+
} catch (error) {
|
|
1952
|
+
backupPersistResult = {
|
|
1953
|
+
skipped: true,
|
|
1954
|
+
reason: 'backup_persist_failed',
|
|
1955
|
+
error: error?.message || String(error),
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
return {
|
|
1960
|
+
...configPersistResult,
|
|
1961
|
+
backup: backupPersistResult,
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1919
1964
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
|
|
1924
|
-
return { skipped: true, reason: 'already_persisted' };
|
|
1965
|
+
async function maybeRestoreRuntimeAppToken({ runtime, accountId, runtimeConfig }) {
|
|
1966
|
+
if (resolveRuntimeAppToken(runtimeConfig)) {
|
|
1967
|
+
return { restored: false, reason: 'already_configured', runtimeConfig };
|
|
1925
1968
|
}
|
|
1926
1969
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1970
|
+
const backupState = await loadClaworldRuntimeBackup({ accountId });
|
|
1971
|
+
const backup = backupState.backup;
|
|
1972
|
+
const backupToken = normalizeClaworldText(backup?.appToken, null);
|
|
1973
|
+
if (!backupToken) {
|
|
1974
|
+
return {
|
|
1975
|
+
restored: false,
|
|
1976
|
+
reason: 'backup_missing_app_token',
|
|
1977
|
+
runtimeConfig,
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
const backupServerUrl = normalizeClaworldText(backup?.serverUrl, null);
|
|
1982
|
+
const currentServerUrl = normalizeClaworldText(runtimeConfig?.serverUrl, null);
|
|
1983
|
+
if (backupServerUrl && currentServerUrl && normalizeRelayHttpBaseUrl(backupServerUrl) !== normalizeRelayHttpBaseUrl(currentServerUrl)) {
|
|
1984
|
+
return {
|
|
1985
|
+
restored: false,
|
|
1986
|
+
reason: 'backup_server_mismatch',
|
|
1987
|
+
runtimeConfig,
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
const restoredRuntimeConfig = applyRuntimeIdentity(runtimeConfig, {
|
|
1992
|
+
appToken: backupToken,
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1995
|
+
try {
|
|
1996
|
+
await persistRuntimeAppToken({
|
|
1997
|
+
runtime,
|
|
1998
|
+
accountId,
|
|
1999
|
+
appToken: backupToken,
|
|
2000
|
+
});
|
|
2001
|
+
} catch (error) {
|
|
2002
|
+
logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist restored runtime appToken`, {
|
|
2003
|
+
error: error?.message || String(error),
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
return {
|
|
2008
|
+
restored: true,
|
|
2009
|
+
reason: 'installer_state_backup',
|
|
2010
|
+
runtimeConfig: restoredRuntimeConfig,
|
|
2011
|
+
backup,
|
|
2012
|
+
installerStatePath: backupState.installerStatePath,
|
|
1934
2013
|
};
|
|
1935
|
-
delete accounts[accountId].registration;
|
|
1936
|
-
claworldRoot.accounts = accounts;
|
|
1937
|
-
nextCfg.channels.claworld = claworldRoot;
|
|
1938
|
-
await runtime.config.writeConfigFile(nextCfg);
|
|
1939
|
-
return { skipped: false, ok: true };
|
|
1940
2014
|
}
|
|
1941
2015
|
|
|
1942
2016
|
function resolveConfiguredRuntimeContext(context = {}) {
|
|
@@ -1959,6 +2033,19 @@ export function createClaworldChannelPlugin({
|
|
|
1959
2033
|
const cfg = configuredContext.cfg || {};
|
|
1960
2034
|
const accountId = configuredContext.accountId || null;
|
|
1961
2035
|
let runtimeConfig = configuredContext.runtimeConfig;
|
|
2036
|
+
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2037
|
+
const restoredBinding = await maybeRestoreRuntimeAppToken({
|
|
2038
|
+
runtime: runtimeResolution.runtime,
|
|
2039
|
+
accountId,
|
|
2040
|
+
runtimeConfig,
|
|
2041
|
+
});
|
|
2042
|
+
if (restoredBinding.restored) {
|
|
2043
|
+
runtimeConfig = restoredBinding.runtimeConfig;
|
|
2044
|
+
logger.info?.(`[claworld:${accountId || 'default'}] restored runtime binding from installer state`, {
|
|
2045
|
+
installerStatePath: restoredBinding.installerStatePath || null,
|
|
2046
|
+
relayAgentId: runtimeConfig?.relay?.agentId || null,
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
1962
2049
|
const runtimeContext = accountRuntimeContexts.get(accountId || 'default') || null;
|
|
1963
2050
|
if (runtimeContext?.runtimeConfig && !runtimeContext?.deferredFailure) {
|
|
1964
2051
|
return {
|
|
@@ -2009,6 +2096,20 @@ export function createClaworldChannelPlugin({
|
|
|
2009
2096
|
runtimeConfigShape: summarizeObjectShape(runtimeConfig),
|
|
2010
2097
|
});
|
|
2011
2098
|
|
|
2099
|
+
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2100
|
+
const restoredBinding = await maybeRestoreRuntimeAppToken({
|
|
2101
|
+
runtime: runtimeResolution.runtime,
|
|
2102
|
+
accountId: runtimeAccountId,
|
|
2103
|
+
runtimeConfig,
|
|
2104
|
+
});
|
|
2105
|
+
if (restoredBinding.restored) {
|
|
2106
|
+
runtimeConfig = restoredBinding.runtimeConfig;
|
|
2107
|
+
logger.info?.(`[claworld:${runtimeAccountId}] restored runtime binding from installer state`, {
|
|
2108
|
+
installerStatePath: restoredBinding.installerStatePath || null,
|
|
2109
|
+
relayAgentId: runtimeConfig?.relay?.agentId || null,
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2012
2113
|
const validation = validateClaworldChannelConfig(configSource, context.accountId);
|
|
2013
2114
|
if (!validation.ok && sourceType !== 'root_cfg') {
|
|
2014
2115
|
logger.warn?.(`[claworld:${runtimeAccountId}] non-root runtime source would not validate as full cfg`, {
|
|
@@ -2084,7 +2185,6 @@ export function createClaworldChannelPlugin({
|
|
|
2084
2185
|
return { startedDeferred: true, reason: 'missing_runtime_context', runtimeConfig };
|
|
2085
2186
|
}
|
|
2086
2187
|
|
|
2087
|
-
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2088
2188
|
const pluginRuntime = runtimeResolution.runtime;
|
|
2089
2189
|
const runtimeSource = runtimeResolution.runtimeSource;
|
|
2090
2190
|
|
|
@@ -2092,24 +2192,24 @@ export function createClaworldChannelPlugin({
|
|
|
2092
2192
|
runtimeSource,
|
|
2093
2193
|
});
|
|
2094
2194
|
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2195
|
+
try {
|
|
2196
|
+
const persisted = await persistRuntimeAppToken({
|
|
2197
|
+
runtime: pluginRuntime,
|
|
2198
|
+
accountId: runtimeConfig.accountId,
|
|
2199
|
+
appToken: resolveRuntimeAppToken(runtimeConfig),
|
|
2200
|
+
relayAgentId: runtimeConfig.relay?.agentId || null,
|
|
2201
|
+
});
|
|
2202
|
+
if (!persisted.skipped || persisted.backup?.skipped === false) {
|
|
2203
|
+
logger.info?.(`[claworld:${runtimeAccountId}] persisted runtime binding state`, {
|
|
2099
2204
|
accountId: runtimeConfig.accountId,
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
});
|
|
2103
|
-
if (!persisted.skipped) {
|
|
2104
|
-
logger.info?.(`[claworld:${runtimeAccountId}] persisted runtime appToken`, {
|
|
2105
|
-
accountId: runtimeConfig.accountId,
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
} catch (error) {
|
|
2109
|
-
logger.warn?.(`[claworld:${runtimeAccountId}] failed to persist runtime appToken`, {
|
|
2110
|
-
error: error?.message || String(error),
|
|
2205
|
+
configSkipped: persisted.skipped === true,
|
|
2206
|
+
backupSkipped: persisted.backup?.skipped === true,
|
|
2111
2207
|
});
|
|
2112
2208
|
}
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
logger.warn?.(`[claworld:${runtimeAccountId}] failed to persist runtime appToken`, {
|
|
2211
|
+
error: error?.message || String(error),
|
|
2212
|
+
});
|
|
2113
2213
|
}
|
|
2114
2214
|
|
|
2115
2215
|
accountRuntimeContexts.set(accountKey, {
|
|
@@ -1022,12 +1022,20 @@ function buildRegisteredTools(api, plugin) {
|
|
|
1022
1022
|
});
|
|
1023
1023
|
const pairedAgentId = identityPayload?.agentId || runtimeConfig.relay?.agentId || null;
|
|
1024
1024
|
const relayAgent = pairedAgentId
|
|
1025
|
-
?
|
|
1026
|
-
cfg,
|
|
1027
|
-
accountId,
|
|
1028
|
-
runtimeConfig,
|
|
1025
|
+
? {
|
|
1029
1026
|
agentId: pairedAgentId,
|
|
1030
|
-
|
|
1027
|
+
displayName: normalizeText(
|
|
1028
|
+
identityPayload?.publicIdentity?.displayName,
|
|
1029
|
+
normalizeText(
|
|
1030
|
+
identityPayload?.recommendedDisplayName,
|
|
1031
|
+
normalizeText(runtimeConfig?.name, normalizeText(runtimeConfig?.registration?.displayName, null)),
|
|
1032
|
+
),
|
|
1033
|
+
),
|
|
1034
|
+
discoverable: null,
|
|
1035
|
+
contactable: null,
|
|
1036
|
+
online: null,
|
|
1037
|
+
resolved: null,
|
|
1038
|
+
}
|
|
1031
1039
|
: null;
|
|
1032
1040
|
const hasConfiguredAppToken = Boolean(
|
|
1033
1041
|
runtimeConfig.appToken
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
7
|
+
findClaworldManagedRuntimeBackup,
|
|
8
|
+
setClaworldManagedRuntimeBackupState,
|
|
9
|
+
stripClaworldManagedRuntimeConfig,
|
|
10
|
+
} from './managed-config.js';
|
|
11
|
+
|
|
12
|
+
function normalizeText(value, fallback = null) {
|
|
13
|
+
if (value == null) return fallback;
|
|
14
|
+
const normalized = String(value).trim();
|
|
15
|
+
return normalized || fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveDefaultOpenClawConfigPath() {
|
|
19
|
+
return path.resolve(normalizeText(
|
|
20
|
+
process.env.OPENCLAW_CONFIG_PATH,
|
|
21
|
+
path.join(os.homedir(), '.openclaw', 'openclaw.json'),
|
|
22
|
+
));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveClaworldInstallerStatePath(configPath = resolveDefaultOpenClawConfigPath()) {
|
|
26
|
+
const resolvedConfigPath = path.resolve(String(configPath));
|
|
27
|
+
return path.join(path.dirname(resolvedConfigPath), '.claworld-installer-state.json');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function loadInstallerStateFromDisk(installerStatePath) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await fs.readFile(installerStatePath, 'utf8');
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error?.code === 'ENOENT') return {};
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function writeInstallerStateToDisk(installerStatePath, installerState) {
|
|
42
|
+
const nextState = installerState && typeof installerState === 'object' && !Array.isArray(installerState)
|
|
43
|
+
? installerState
|
|
44
|
+
: {};
|
|
45
|
+
if (Object.keys(nextState).length === 0) {
|
|
46
|
+
await fs.rm(installerStatePath, { force: true });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
await fs.mkdir(path.dirname(installerStatePath), { recursive: true });
|
|
50
|
+
await fs.writeFile(installerStatePath, `${JSON.stringify(nextState, null, 2)}\n`, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function loadClaworldRuntimeBackup({
|
|
54
|
+
accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
55
|
+
configPath = resolveDefaultOpenClawConfigPath(),
|
|
56
|
+
} = {}) {
|
|
57
|
+
const installerStatePath = resolveClaworldInstallerStatePath(configPath);
|
|
58
|
+
const installerState = await loadInstallerStateFromDisk(installerStatePath);
|
|
59
|
+
return {
|
|
60
|
+
installerStatePath,
|
|
61
|
+
installerState,
|
|
62
|
+
backup: findClaworldManagedRuntimeBackup(installerState, accountId),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function persistClaworldRuntimeBackup({
|
|
67
|
+
runtime = null,
|
|
68
|
+
accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
|
|
69
|
+
configPath = resolveDefaultOpenClawConfigPath(),
|
|
70
|
+
} = {}) {
|
|
71
|
+
if (!runtime?.config?.loadConfig) {
|
|
72
|
+
return { skipped: true, reason: 'missing_runtime_config_loader' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const currentConfig = await runtime.config.loadConfig();
|
|
76
|
+
const { backup } = stripClaworldManagedRuntimeConfig(currentConfig, {
|
|
77
|
+
accountId,
|
|
78
|
+
preserveBackup: true,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (!backup?.appToken) {
|
|
82
|
+
return { skipped: true, reason: 'missing_app_token', backup: null };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const installerStatePath = resolveClaworldInstallerStatePath(configPath);
|
|
86
|
+
const installerState = await loadInstallerStateFromDisk(installerStatePath);
|
|
87
|
+
const previousBackup = findClaworldManagedRuntimeBackup(installerState, accountId);
|
|
88
|
+
if (JSON.stringify(previousBackup || null) === JSON.stringify(backup)) {
|
|
89
|
+
return {
|
|
90
|
+
skipped: true,
|
|
91
|
+
reason: 'already_persisted',
|
|
92
|
+
installerStatePath,
|
|
93
|
+
backup,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setClaworldManagedRuntimeBackupState(installerState, accountId, backup);
|
|
98
|
+
await writeInstallerStateToDisk(installerStatePath, installerState);
|
|
99
|
+
return {
|
|
100
|
+
skipped: false,
|
|
101
|
+
ok: true,
|
|
102
|
+
installerStatePath,
|
|
103
|
+
backup,
|
|
104
|
+
};
|
|
105
|
+
}
|