@xfxstudio/claworld 0.2.21 → 0.2.23-beta.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/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,
|
|
@@ -1324,6 +1328,30 @@ function createDeliveryReplyDispatcher({
|
|
|
1324
1328
|
runtimeOutputSummary.counts[kind] += 1;
|
|
1325
1329
|
};
|
|
1326
1330
|
|
|
1331
|
+
const submitRelayReply = async (replyText) => {
|
|
1332
|
+
if (typeof relayClient?.submitDeliveryReply !== 'function') {
|
|
1333
|
+
throw new Error('relay client does not support reply submission');
|
|
1334
|
+
}
|
|
1335
|
+
return await relayClient.submitDeliveryReply({
|
|
1336
|
+
deliveryId,
|
|
1337
|
+
sessionKey,
|
|
1338
|
+
replyText,
|
|
1339
|
+
source: 'openclaw-autochain',
|
|
1340
|
+
});
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
const submitRelayKeptSilent = async (reason) => {
|
|
1344
|
+
if (typeof relayClient?.submitDeliveryKeptSilent !== 'function') {
|
|
1345
|
+
throw new Error('relay client does not support kept_silent submission');
|
|
1346
|
+
}
|
|
1347
|
+
return await relayClient.submitDeliveryKeptSilent({
|
|
1348
|
+
deliveryId,
|
|
1349
|
+
sessionKey,
|
|
1350
|
+
reason,
|
|
1351
|
+
source: 'openclaw-autochain',
|
|
1352
|
+
});
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1327
1355
|
const flushReply = async (text) => {
|
|
1328
1356
|
const normalized = String(text || '').trim();
|
|
1329
1357
|
if (!normalized || replied || suppressed) return false;
|
|
@@ -1331,25 +1359,9 @@ function createDeliveryReplyDispatcher({
|
|
|
1331
1359
|
suppressed = true;
|
|
1332
1360
|
return false;
|
|
1333
1361
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
sessionKey,
|
|
1338
|
-
replyText: normalized,
|
|
1339
|
-
source: 'openclaw-autochain',
|
|
1340
|
-
});
|
|
1341
|
-
replyTransport = replyResult?.transport || 'websocket';
|
|
1342
|
-
replyFallbackUsed = replyResult?.fallbackUsed === true;
|
|
1343
|
-
} else {
|
|
1344
|
-
relayClient.sendReply({
|
|
1345
|
-
deliveryId,
|
|
1346
|
-
sessionKey,
|
|
1347
|
-
replyText: normalized,
|
|
1348
|
-
source: 'openclaw-autochain',
|
|
1349
|
-
});
|
|
1350
|
-
replyTransport = 'websocket-fire-and-forget';
|
|
1351
|
-
replyFallbackUsed = false;
|
|
1352
|
-
}
|
|
1362
|
+
const replyResult = await submitRelayReply(normalized);
|
|
1363
|
+
replyTransport = replyResult?.transport || null;
|
|
1364
|
+
replyFallbackUsed = replyResult?.fallbackUsed === true;
|
|
1353
1365
|
replied = true;
|
|
1354
1366
|
return true;
|
|
1355
1367
|
};
|
|
@@ -1360,19 +1372,11 @@ function createDeliveryReplyDispatcher({
|
|
|
1360
1372
|
suppressed = true;
|
|
1361
1373
|
return false;
|
|
1362
1374
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
source: 'openclaw-autochain',
|
|
1369
|
-
});
|
|
1370
|
-
keptSilentTransport = silentResult?.transport || 'websocket';
|
|
1371
|
-
keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
|
|
1372
|
-
} else {
|
|
1373
|
-
keptSilentTransport = 'unsupported';
|
|
1374
|
-
keptSilentFallbackUsed = false;
|
|
1375
|
-
}
|
|
1375
|
+
const silentResult = await submitRelayKeptSilent(
|
|
1376
|
+
normalizePluginOptionalText(reason) || 'no_renderable_reply',
|
|
1377
|
+
);
|
|
1378
|
+
keptSilentTransport = silentResult?.transport || null;
|
|
1379
|
+
keptSilentFallbackUsed = silentResult?.fallbackUsed === true;
|
|
1376
1380
|
keptSilent = true;
|
|
1377
1381
|
return true;
|
|
1378
1382
|
};
|
|
@@ -1705,12 +1709,13 @@ async function maybeBridgeRuntimeDelivery({
|
|
|
1705
1709
|
});
|
|
1706
1710
|
|
|
1707
1711
|
try {
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1712
|
+
const acceptedResult = await relayClient.acceptDeliveryHttp({
|
|
1713
|
+
deliveryId,
|
|
1714
|
+
sessionKey,
|
|
1715
|
+
source: 'runtime_dispatch',
|
|
1716
|
+
});
|
|
1717
|
+
if (acceptedResult.status < 200 || acceptedResult.status >= 300) {
|
|
1718
|
+
throw new Error(`failed to submit relay delivery acceptance: ${acceptedResult.status}`);
|
|
1714
1719
|
}
|
|
1715
1720
|
} catch (error) {
|
|
1716
1721
|
logger.warn?.(`[claworld:${runtimeAccountId}] delivery acceptance acknowledgement failed`, {
|
|
@@ -1895,48 +1900,118 @@ export function createClaworldChannelPlugin({
|
|
|
1895
1900
|
}
|
|
1896
1901
|
|
|
1897
1902
|
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
1903
|
if (!accountId || !appToken) {
|
|
1902
1904
|
return { skipped: true, reason: 'missing_account_or_token' };
|
|
1903
1905
|
}
|
|
1904
1906
|
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1907
|
+
let configPersistResult = { skipped: true, reason: 'missing_runtime_config_io' };
|
|
1908
|
+
if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
|
|
1909
|
+
const currentCfg = await runtime.config.loadConfig();
|
|
1910
|
+
const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
|
|
1911
|
+
nextCfg.channels = nextCfg.channels && typeof nextCfg.channels === 'object' && !Array.isArray(nextCfg.channels)
|
|
1912
|
+
? nextCfg.channels
|
|
1913
|
+
: {};
|
|
1914
|
+
const claworldRoot = nextCfg.channels.claworld && typeof nextCfg.channels.claworld === 'object' && !Array.isArray(nextCfg.channels.claworld)
|
|
1915
|
+
? nextCfg.channels.claworld
|
|
1916
|
+
: {};
|
|
1917
|
+
const accounts = claworldRoot.accounts && typeof claworldRoot.accounts === 'object' && !Array.isArray(claworldRoot.accounts)
|
|
1918
|
+
? claworldRoot.accounts
|
|
1919
|
+
: {};
|
|
1920
|
+
const account = accounts[accountId] && typeof accounts[accountId] === 'object' && !Array.isArray(accounts[accountId])
|
|
1921
|
+
? accounts[accountId]
|
|
1922
|
+
: {};
|
|
1923
|
+
|
|
1924
|
+
const normalizedRelayAgentId = normalizeClaworldText(relayAgentId, normalizeClaworldText(account?.relay?.agentId, null));
|
|
1925
|
+
const currentAppToken = normalizeClaworldText(account.appToken, null);
|
|
1926
|
+
const currentRelayAgentId = normalizeClaworldText(account?.relay?.agentId, null);
|
|
1927
|
+
if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
|
|
1928
|
+
configPersistResult = { skipped: true, reason: 'already_persisted' };
|
|
1929
|
+
} else {
|
|
1930
|
+
accounts[accountId] = {
|
|
1931
|
+
...account,
|
|
1932
|
+
appToken,
|
|
1933
|
+
relay: {
|
|
1934
|
+
...(account?.relay && typeof account.relay === 'object' && !Array.isArray(account.relay) ? account.relay : {}),
|
|
1935
|
+
...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
|
|
1936
|
+
},
|
|
1937
|
+
};
|
|
1938
|
+
delete accounts[accountId].registration;
|
|
1939
|
+
claworldRoot.accounts = accounts;
|
|
1940
|
+
nextCfg.channels.claworld = claworldRoot;
|
|
1941
|
+
await runtime.config.writeConfigFile(nextCfg);
|
|
1942
|
+
configPersistResult = { skipped: false, ok: true };
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
let backupPersistResult = { skipped: true, reason: 'missing_runtime_config_loader' };
|
|
1947
|
+
try {
|
|
1948
|
+
backupPersistResult = await persistClaworldRuntimeBackup({
|
|
1949
|
+
runtime,
|
|
1950
|
+
accountId,
|
|
1951
|
+
});
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
backupPersistResult = {
|
|
1954
|
+
skipped: true,
|
|
1955
|
+
reason: 'backup_persist_failed',
|
|
1956
|
+
error: error?.message || String(error),
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
return {
|
|
1961
|
+
...configPersistResult,
|
|
1962
|
+
backup: backupPersistResult,
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1919
1965
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
|
|
1924
|
-
return { skipped: true, reason: 'already_persisted' };
|
|
1966
|
+
async function maybeRestoreRuntimeAppToken({ runtime, accountId, runtimeConfig }) {
|
|
1967
|
+
if (resolveRuntimeAppToken(runtimeConfig)) {
|
|
1968
|
+
return { restored: false, reason: 'already_configured', runtimeConfig };
|
|
1925
1969
|
}
|
|
1926
1970
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1971
|
+
const backupState = await loadClaworldRuntimeBackup({ accountId });
|
|
1972
|
+
const backup = backupState.backup;
|
|
1973
|
+
const backupToken = normalizeClaworldText(backup?.appToken, null);
|
|
1974
|
+
if (!backupToken) {
|
|
1975
|
+
return {
|
|
1976
|
+
restored: false,
|
|
1977
|
+
reason: 'backup_missing_app_token',
|
|
1978
|
+
runtimeConfig,
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
const backupServerUrl = normalizeClaworldText(backup?.serverUrl, null);
|
|
1983
|
+
const currentServerUrl = normalizeClaworldText(runtimeConfig?.serverUrl, null);
|
|
1984
|
+
if (backupServerUrl && currentServerUrl && normalizeRelayHttpBaseUrl(backupServerUrl) !== normalizeRelayHttpBaseUrl(currentServerUrl)) {
|
|
1985
|
+
return {
|
|
1986
|
+
restored: false,
|
|
1987
|
+
reason: 'backup_server_mismatch',
|
|
1988
|
+
runtimeConfig,
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
const restoredRuntimeConfig = applyRuntimeIdentity(runtimeConfig, {
|
|
1993
|
+
appToken: backupToken,
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
try {
|
|
1997
|
+
await persistRuntimeAppToken({
|
|
1998
|
+
runtime,
|
|
1999
|
+
accountId,
|
|
2000
|
+
appToken: backupToken,
|
|
2001
|
+
});
|
|
2002
|
+
} catch (error) {
|
|
2003
|
+
logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist restored runtime appToken`, {
|
|
2004
|
+
error: error?.message || String(error),
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
return {
|
|
2009
|
+
restored: true,
|
|
2010
|
+
reason: 'installer_state_backup',
|
|
2011
|
+
runtimeConfig: restoredRuntimeConfig,
|
|
2012
|
+
backup,
|
|
2013
|
+
installerStatePath: backupState.installerStatePath,
|
|
1934
2014
|
};
|
|
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
2015
|
}
|
|
1941
2016
|
|
|
1942
2017
|
function resolveConfiguredRuntimeContext(context = {}) {
|
|
@@ -1959,6 +2034,19 @@ export function createClaworldChannelPlugin({
|
|
|
1959
2034
|
const cfg = configuredContext.cfg || {};
|
|
1960
2035
|
const accountId = configuredContext.accountId || null;
|
|
1961
2036
|
let runtimeConfig = configuredContext.runtimeConfig;
|
|
2037
|
+
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2038
|
+
const restoredBinding = await maybeRestoreRuntimeAppToken({
|
|
2039
|
+
runtime: runtimeResolution.runtime,
|
|
2040
|
+
accountId,
|
|
2041
|
+
runtimeConfig,
|
|
2042
|
+
});
|
|
2043
|
+
if (restoredBinding.restored) {
|
|
2044
|
+
runtimeConfig = restoredBinding.runtimeConfig;
|
|
2045
|
+
logger.info?.(`[claworld:${accountId || 'default'}] restored runtime binding from installer state`, {
|
|
2046
|
+
installerStatePath: restoredBinding.installerStatePath || null,
|
|
2047
|
+
relayAgentId: runtimeConfig?.relay?.agentId || null,
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
1962
2050
|
const runtimeContext = accountRuntimeContexts.get(accountId || 'default') || null;
|
|
1963
2051
|
if (runtimeContext?.runtimeConfig && !runtimeContext?.deferredFailure) {
|
|
1964
2052
|
return {
|
|
@@ -2009,6 +2097,20 @@ export function createClaworldChannelPlugin({
|
|
|
2009
2097
|
runtimeConfigShape: summarizeObjectShape(runtimeConfig),
|
|
2010
2098
|
});
|
|
2011
2099
|
|
|
2100
|
+
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2101
|
+
const restoredBinding = await maybeRestoreRuntimeAppToken({
|
|
2102
|
+
runtime: runtimeResolution.runtime,
|
|
2103
|
+
accountId: runtimeAccountId,
|
|
2104
|
+
runtimeConfig,
|
|
2105
|
+
});
|
|
2106
|
+
if (restoredBinding.restored) {
|
|
2107
|
+
runtimeConfig = restoredBinding.runtimeConfig;
|
|
2108
|
+
logger.info?.(`[claworld:${runtimeAccountId}] restored runtime binding from installer state`, {
|
|
2109
|
+
installerStatePath: restoredBinding.installerStatePath || null,
|
|
2110
|
+
relayAgentId: runtimeConfig?.relay?.agentId || null,
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2012
2114
|
const validation = validateClaworldChannelConfig(configSource, context.accountId);
|
|
2013
2115
|
if (!validation.ok && sourceType !== 'root_cfg') {
|
|
2014
2116
|
logger.warn?.(`[claworld:${runtimeAccountId}] non-root runtime source would not validate as full cfg`, {
|
|
@@ -2084,7 +2186,6 @@ export function createClaworldChannelPlugin({
|
|
|
2084
2186
|
return { startedDeferred: true, reason: 'missing_runtime_context', runtimeConfig };
|
|
2085
2187
|
}
|
|
2086
2188
|
|
|
2087
|
-
const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
|
|
2088
2189
|
const pluginRuntime = runtimeResolution.runtime;
|
|
2089
2190
|
const runtimeSource = runtimeResolution.runtimeSource;
|
|
2090
2191
|
|
|
@@ -2092,24 +2193,24 @@ export function createClaworldChannelPlugin({
|
|
|
2092
2193
|
runtimeSource,
|
|
2093
2194
|
});
|
|
2094
2195
|
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2196
|
+
try {
|
|
2197
|
+
const persisted = await persistRuntimeAppToken({
|
|
2198
|
+
runtime: pluginRuntime,
|
|
2199
|
+
accountId: runtimeConfig.accountId,
|
|
2200
|
+
appToken: resolveRuntimeAppToken(runtimeConfig),
|
|
2201
|
+
relayAgentId: runtimeConfig.relay?.agentId || null,
|
|
2202
|
+
});
|
|
2203
|
+
if (!persisted.skipped || persisted.backup?.skipped === false) {
|
|
2204
|
+
logger.info?.(`[claworld:${runtimeAccountId}] persisted runtime binding state`, {
|
|
2099
2205
|
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),
|
|
2206
|
+
configSkipped: persisted.skipped === true,
|
|
2207
|
+
backupSkipped: persisted.backup?.skipped === true,
|
|
2111
2208
|
});
|
|
2112
2209
|
}
|
|
2210
|
+
} catch (error) {
|
|
2211
|
+
logger.warn?.(`[claworld:${runtimeAccountId}] failed to persist runtime appToken`, {
|
|
2212
|
+
error: error?.message || String(error),
|
|
2213
|
+
});
|
|
2113
2214
|
}
|
|
2114
2215
|
|
|
2115
2216
|
accountRuntimeContexts.set(accountKey, {
|
|
@@ -29,6 +29,21 @@ import {
|
|
|
29
29
|
TERMINAL_CLOSE_REASONS,
|
|
30
30
|
} from './relay-client-shared.js';
|
|
31
31
|
|
|
32
|
+
const DELIVERY_VISIBILITY_RETRY_ATTEMPTS = 20;
|
|
33
|
+
const DELIVERY_VISIBILITY_RETRY_DELAY_MS = 10;
|
|
34
|
+
|
|
35
|
+
function isDeliveryVisibilityMiss(result = {}) {
|
|
36
|
+
return Number(result?.status) === 404
|
|
37
|
+
&& normalizeOptionalText(result?.body?.error) === 'delivery_not_found';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function waitForDeliveryVisibilityRetry() {
|
|
41
|
+
await new Promise((resolve) => {
|
|
42
|
+
const timer = setTimeout(resolve, DELIVERY_VISIBILITY_RETRY_DELAY_MS);
|
|
43
|
+
if (typeof timer?.unref === 'function') timer.unref();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
export class ClaworldRelayClient extends EventEmitter {
|
|
33
48
|
constructor({
|
|
34
49
|
logger = console,
|
|
@@ -244,6 +259,18 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
244
259
|
return { status: response.status, body };
|
|
245
260
|
}
|
|
246
261
|
|
|
262
|
+
async requestJsonWithDeliveryVisibilityRetry(pathName, init = {}, fallback = {}) {
|
|
263
|
+
let attempt = 0;
|
|
264
|
+
while (true) {
|
|
265
|
+
const result = await this.requestJson(pathName, init, fallback);
|
|
266
|
+
if (!isDeliveryVisibilityMiss(result) || attempt >= DELIVERY_VISIBILITY_RETRY_ATTEMPTS - 1) {
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
attempt += 1;
|
|
270
|
+
await waitForDeliveryVisibilityRetry();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
247
274
|
async openSocket({
|
|
248
275
|
wsUrl,
|
|
249
276
|
agentId,
|
|
@@ -459,7 +486,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
459
486
|
config,
|
|
460
487
|
agentId,
|
|
461
488
|
credential = null,
|
|
462
|
-
clientVersion = 'claworld-plugin/0.2.
|
|
489
|
+
clientVersion = 'claworld-plugin/0.2.22',
|
|
463
490
|
sessionTarget,
|
|
464
491
|
fallbackTarget,
|
|
465
492
|
} = {}) {
|
|
@@ -879,7 +906,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
879
906
|
replyText,
|
|
880
907
|
source,
|
|
881
908
|
});
|
|
882
|
-
const result = await this.
|
|
909
|
+
const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(envelope.deliveryId)}/reply`, {
|
|
883
910
|
method: 'POST',
|
|
884
911
|
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
885
912
|
body: JSON.stringify({
|
|
@@ -901,7 +928,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
901
928
|
|
|
902
929
|
async acceptDeliveryHttp({ deliveryId, sessionKey = null, source = 'runtime_dispatch' } = {}) {
|
|
903
930
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
904
|
-
const result = await this.
|
|
931
|
+
const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/accepted`, {
|
|
905
932
|
method: 'POST',
|
|
906
933
|
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
907
934
|
body: JSON.stringify({
|
|
@@ -932,6 +959,10 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
932
959
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
933
960
|
httpFallback = true,
|
|
934
961
|
} = {}) {
|
|
962
|
+
const ackPromise = this.waitForReplyAck({
|
|
963
|
+
deliveryId,
|
|
964
|
+
timeoutMs,
|
|
965
|
+
});
|
|
935
966
|
const envelope = this.sendReply({
|
|
936
967
|
deliveryId,
|
|
937
968
|
sessionKey,
|
|
@@ -940,10 +971,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
940
971
|
});
|
|
941
972
|
|
|
942
973
|
try {
|
|
943
|
-
const ack = await
|
|
944
|
-
deliveryId: envelope.deliveryId,
|
|
945
|
-
timeoutMs,
|
|
946
|
-
});
|
|
974
|
+
const ack = await ackPromise;
|
|
947
975
|
return {
|
|
948
976
|
ok: true,
|
|
949
977
|
envelope,
|
|
@@ -1011,6 +1039,24 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1011
1039
|
}
|
|
1012
1040
|
}
|
|
1013
1041
|
|
|
1042
|
+
async submitDeliveryReply({
|
|
1043
|
+
deliveryId,
|
|
1044
|
+
sessionKey,
|
|
1045
|
+
replyText,
|
|
1046
|
+
source = 'subagent',
|
|
1047
|
+
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1048
|
+
httpFallback = true,
|
|
1049
|
+
} = {}) {
|
|
1050
|
+
return await this.sendReplyAndWaitForAck({
|
|
1051
|
+
deliveryId,
|
|
1052
|
+
sessionKey,
|
|
1053
|
+
replyText,
|
|
1054
|
+
source,
|
|
1055
|
+
timeoutMs,
|
|
1056
|
+
httpFallback,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1014
1060
|
async sendAcceptedAndWaitForAck({
|
|
1015
1061
|
deliveryId,
|
|
1016
1062
|
sessionKey,
|
|
@@ -1018,6 +1064,10 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1018
1064
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1019
1065
|
httpFallback = true,
|
|
1020
1066
|
} = {}) {
|
|
1067
|
+
const ackPromise = this.waitForAcceptedAck({
|
|
1068
|
+
deliveryId,
|
|
1069
|
+
timeoutMs,
|
|
1070
|
+
});
|
|
1021
1071
|
const envelope = this.sendAccepted({
|
|
1022
1072
|
deliveryId,
|
|
1023
1073
|
sessionKey,
|
|
@@ -1025,10 +1075,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1025
1075
|
});
|
|
1026
1076
|
|
|
1027
1077
|
try {
|
|
1028
|
-
const ack = await
|
|
1029
|
-
deliveryId: envelope.deliveryId,
|
|
1030
|
-
timeoutMs,
|
|
1031
|
-
});
|
|
1078
|
+
const ack = await ackPromise;
|
|
1032
1079
|
return {
|
|
1033
1080
|
ok: true,
|
|
1034
1081
|
envelope,
|
|
@@ -1082,7 +1129,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1082
1129
|
|
|
1083
1130
|
async keepDeliverySilentHttp({ deliveryId, reason = null, source = 'openclaw-autochain' } = {}) {
|
|
1084
1131
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
1085
|
-
const result = await this.
|
|
1132
|
+
const result = await this.requestJsonWithDeliveryVisibilityRetry(`/v1/deliveries/${encodeURIComponent(normalizedDeliveryId)}/kept-silent`, {
|
|
1086
1133
|
method: 'POST',
|
|
1087
1134
|
headers: buildRuntimeAuthHeaders(this.runtimeConfig, { 'content-type': 'application/json' }),
|
|
1088
1135
|
body: JSON.stringify({
|
|
@@ -1113,6 +1160,10 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1113
1160
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1114
1161
|
httpFallback = true,
|
|
1115
1162
|
} = {}) {
|
|
1163
|
+
const ackPromise = this.waitForKeepSilentAck({
|
|
1164
|
+
deliveryId,
|
|
1165
|
+
timeoutMs,
|
|
1166
|
+
});
|
|
1116
1167
|
const envelope = this.sendKeepSilent({
|
|
1117
1168
|
deliveryId,
|
|
1118
1169
|
sessionKey,
|
|
@@ -1121,10 +1172,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1121
1172
|
});
|
|
1122
1173
|
|
|
1123
1174
|
try {
|
|
1124
|
-
const ack = await
|
|
1125
|
-
deliveryId: envelope.deliveryId,
|
|
1126
|
-
timeoutMs,
|
|
1127
|
-
});
|
|
1175
|
+
const ack = await ackPromise;
|
|
1128
1176
|
return {
|
|
1129
1177
|
ok: true,
|
|
1130
1178
|
envelope,
|
|
@@ -1192,6 +1240,24 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1192
1240
|
}
|
|
1193
1241
|
}
|
|
1194
1242
|
|
|
1243
|
+
async submitDeliveryKeptSilent({
|
|
1244
|
+
deliveryId,
|
|
1245
|
+
sessionKey,
|
|
1246
|
+
reason = null,
|
|
1247
|
+
source = 'openclaw-autochain',
|
|
1248
|
+
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1249
|
+
httpFallback = true,
|
|
1250
|
+
} = {}) {
|
|
1251
|
+
return await this.sendKeepSilentAndWaitForAck({
|
|
1252
|
+
deliveryId,
|
|
1253
|
+
sessionKey,
|
|
1254
|
+
reason,
|
|
1255
|
+
source,
|
|
1256
|
+
timeoutMs,
|
|
1257
|
+
httpFallback,
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1195
1261
|
async createChatRequest({ fromAgentId, displayName, agentCode, requestContext = {} } = {}) {
|
|
1196
1262
|
const normalized = normalizeChatRequestInput({ requestContext, source: 'direct_lookup' });
|
|
1197
1263
|
const normalizedDisplayName = normalizeOptionalText(displayName);
|
|
@@ -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
|
+
}
|