oomi-ai 0.2.45 → 0.2.48
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/bin/oomi-ai.js +182 -23
- package/lib/personaApiClient.js +13 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/bin/oomi-ai.js
CHANGED
|
@@ -924,8 +924,98 @@ function collectManagedPersonaRefreshTargets({
|
|
|
924
924
|
|
|
925
925
|
return targets.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
926
926
|
}
|
|
927
|
-
|
|
928
|
-
|
|
927
|
+
|
|
928
|
+
function normalizeBackendPersonaRefreshRecord(rawPersona) {
|
|
929
|
+
if (!rawPersona || typeof rawPersona !== 'object') {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const slug = String(
|
|
934
|
+
rawPersona.slug ||
|
|
935
|
+
rawPersona.id ||
|
|
936
|
+
rawPersona.personaId ||
|
|
937
|
+
''
|
|
938
|
+
).trim();
|
|
939
|
+
if (!slug) {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
return {
|
|
944
|
+
slug,
|
|
945
|
+
name: String(rawPersona.name || slug).trim() || slug,
|
|
946
|
+
description: String(rawPersona.description || rawPersona.summary || rawPersona.name || slug).trim() || slug,
|
|
947
|
+
templateVersion: String(rawPersona.promptTemplateVersion || 'v1').trim() || 'v1',
|
|
948
|
+
templateType: String(rawPersona.templateType || '').trim(),
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function resolveExistingWorkspacePathForSlug(slug, workspaceRoot = defaultPersonaWorkspaceRoot()) {
|
|
953
|
+
for (const root of listPersonaWorkspaceRoots(workspaceRoot)) {
|
|
954
|
+
const workspacePath = resolvePersonaWorkspacePath({
|
|
955
|
+
workspaceRoot: root,
|
|
956
|
+
slug,
|
|
957
|
+
});
|
|
958
|
+
if (fs.existsSync(workspacePath)) {
|
|
959
|
+
return workspacePath;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return '';
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
async function discoverBackendLinkedPersonaRefreshTargets({
|
|
966
|
+
client,
|
|
967
|
+
workspaceRoot = defaultPersonaWorkspaceRoot(),
|
|
968
|
+
existingTargets = [],
|
|
969
|
+
logger = null,
|
|
970
|
+
} = {}) {
|
|
971
|
+
if (!client || typeof client.listPersonas !== 'function') {
|
|
972
|
+
return [];
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const targetsBySlug = new Map(
|
|
976
|
+
Array.isArray(existingTargets)
|
|
977
|
+
? existingTargets.map((target) => [String(target?.slug || '').trim(), target]).filter(([slug]) => slug)
|
|
978
|
+
: [],
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
const payload = await client.listPersonas();
|
|
982
|
+
const backendPersonas = Array.isArray(payload?.personas) ? payload.personas : [];
|
|
983
|
+
const discoveredTargets = [];
|
|
984
|
+
|
|
985
|
+
for (const rawPersona of backendPersonas) {
|
|
986
|
+
const backendPersona = normalizeBackendPersonaRefreshRecord(rawPersona);
|
|
987
|
+
if (!backendPersona || targetsBySlug.has(backendPersona.slug)) {
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const workspacePath = resolveExistingWorkspacePathForSlug(backendPersona.slug, workspaceRoot);
|
|
991
|
+
if (!workspacePath) {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const state = readPersonaRuntimeState(workspacePath);
|
|
995
|
+
const target = {
|
|
996
|
+
slug: backendPersona.slug,
|
|
997
|
+
workspacePath,
|
|
998
|
+
state: {
|
|
999
|
+
...state,
|
|
1000
|
+
slug: backendPersona.slug,
|
|
1001
|
+
name: backendPersona.name,
|
|
1002
|
+
description: backendPersona.description,
|
|
1003
|
+
templateVersion: backendPersona.templateVersion,
|
|
1004
|
+
},
|
|
1005
|
+
processRunning: false,
|
|
1006
|
+
};
|
|
1007
|
+
discoveredTargets.push(target);
|
|
1008
|
+
targetsBySlug.set(backendPersona.slug, target);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (discoveredTargets.length > 0) {
|
|
1012
|
+
logger?.(`Added ${discoveredTargets.length} backend-linked persona runtime target${discoveredTargets.length === 1 ? '' : 's'} from workspace discovery.`);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return discoveredTargets;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
async function findExistingManagedPersona(client, slug) {
|
|
929
1019
|
try {
|
|
930
1020
|
return await client.getPersona({ slug });
|
|
931
1021
|
} catch (error) {
|
|
@@ -1314,6 +1404,7 @@ function printStructuredResult(result, asJson = false) {
|
|
|
1314
1404
|
|
|
1315
1405
|
async function handlePersonaRuntimeRegisterCommand(slug, flags = {}) {
|
|
1316
1406
|
const client = createCliPersonaApiClient(flags);
|
|
1407
|
+
const jsonOutput = isTruthyFlag(flags.json);
|
|
1317
1408
|
const runtime = resolvePersonaRuntimeInput(
|
|
1318
1409
|
flags,
|
|
1319
1410
|
{},
|
|
@@ -1322,19 +1413,27 @@ async function handlePersonaRuntimeRegisterCommand(slug, flags = {}) {
|
|
|
1322
1413
|
workspaceRoot: resolvePersonaWorkspaceRoot(flags),
|
|
1323
1414
|
},
|
|
1324
1415
|
);
|
|
1416
|
+
if (!jsonOutput) {
|
|
1417
|
+
console.log(`[personas] Registering runtime for ${slug}.`);
|
|
1418
|
+
console.log(`[personas] Backend: ${resolvePersonaBackendUrl(flags)}`);
|
|
1419
|
+
console.log(`[personas] Device: ${resolvePersonaDeviceId(flags)}`);
|
|
1420
|
+
console.log(`[personas] Endpoint: ${runtime.endpoint}`);
|
|
1421
|
+
console.log(`[personas] Healthcheck: ${runtime.healthcheckUrl}`);
|
|
1422
|
+
}
|
|
1325
1423
|
const payload = await client.registerRuntime({
|
|
1326
1424
|
slug,
|
|
1327
1425
|
endpoint: runtime.endpoint,
|
|
1328
|
-
healthcheckUrl: runtime.healthcheckUrl,
|
|
1329
|
-
localPort: runtime.localPort,
|
|
1330
|
-
transport: runtime.transport,
|
|
1331
|
-
startedAt: parseIsoTimestamp(flags['started-at'], 'started-at'),
|
|
1332
|
-
});
|
|
1333
|
-
printStructuredResult(payload,
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1426
|
+
healthcheckUrl: runtime.healthcheckUrl,
|
|
1427
|
+
localPort: runtime.localPort,
|
|
1428
|
+
transport: runtime.transport,
|
|
1429
|
+
startedAt: parseIsoTimestamp(flags['started-at'], 'started-at'),
|
|
1430
|
+
});
|
|
1431
|
+
printStructuredResult(payload, jsonOutput);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1336
1434
|
async function handlePersonaHeartbeatCommand(slug, flags = {}) {
|
|
1337
1435
|
const client = createCliPersonaApiClient(flags);
|
|
1436
|
+
const jsonOutput = isTruthyFlag(flags.json);
|
|
1338
1437
|
const runtime = resolvePersonaRuntimeInput(
|
|
1339
1438
|
flags,
|
|
1340
1439
|
{},
|
|
@@ -1343,16 +1442,23 @@ async function handlePersonaHeartbeatCommand(slug, flags = {}) {
|
|
|
1343
1442
|
workspaceRoot: resolvePersonaWorkspaceRoot(flags),
|
|
1344
1443
|
},
|
|
1345
1444
|
);
|
|
1445
|
+
if (!jsonOutput) {
|
|
1446
|
+
console.log(`[personas] Sending heartbeat for ${slug}.`);
|
|
1447
|
+
console.log(`[personas] Backend: ${resolvePersonaBackendUrl(flags)}`);
|
|
1448
|
+
console.log(`[personas] Device: ${resolvePersonaDeviceId(flags)}`);
|
|
1449
|
+
console.log(`[personas] Endpoint: ${runtime.endpoint}`);
|
|
1450
|
+
console.log(`[personas] Healthcheck: ${runtime.healthcheckUrl}`);
|
|
1451
|
+
}
|
|
1346
1452
|
const payload = await client.heartbeatRuntime({
|
|
1347
1453
|
slug,
|
|
1348
1454
|
endpoint: runtime.endpoint,
|
|
1349
|
-
healthcheckUrl: runtime.healthcheckUrl,
|
|
1350
|
-
localPort: runtime.localPort,
|
|
1351
|
-
transport: runtime.transport,
|
|
1352
|
-
observedAt: parseIsoTimestamp(flags['observed-at'], 'observed-at'),
|
|
1353
|
-
});
|
|
1354
|
-
printStructuredResult(payload,
|
|
1355
|
-
}
|
|
1455
|
+
healthcheckUrl: runtime.healthcheckUrl,
|
|
1456
|
+
localPort: runtime.localPort,
|
|
1457
|
+
transport: runtime.transport,
|
|
1458
|
+
observedAt: parseIsoTimestamp(flags['observed-at'], 'observed-at'),
|
|
1459
|
+
});
|
|
1460
|
+
printStructuredResult(payload, jsonOutput);
|
|
1461
|
+
}
|
|
1356
1462
|
|
|
1357
1463
|
async function handlePersonaRuntimeFailCommand(slug, flags = {}) {
|
|
1358
1464
|
const code = String(flags.code || '').trim();
|
|
@@ -1566,13 +1672,16 @@ async function handlePersonaDeleteCommand(slug, flags = {}) {
|
|
|
1566
1672
|
printStructuredResult(result, isTruthyFlag(flags.json));
|
|
1567
1673
|
}
|
|
1568
1674
|
|
|
1569
|
-
async function restartManagedPersonaRefreshTargets(flags = {}) {
|
|
1675
|
+
async function restartManagedPersonaRefreshTargets(flags = {}, options = {}) {
|
|
1570
1676
|
const workspaceRoot = resolvePersonaWorkspaceRoot(flags);
|
|
1571
|
-
const
|
|
1677
|
+
const localTargets = collectManagedPersonaRefreshTargets({
|
|
1572
1678
|
workspaceRoot,
|
|
1573
1679
|
includeStopped: isTruthyFlag(flags['include-stopped']),
|
|
1574
1680
|
});
|
|
1681
|
+
const targetsBySlug = new Map(localTargets.map((target) => [target.slug, target]));
|
|
1682
|
+
const targets = [...localTargets];
|
|
1575
1683
|
const results = [];
|
|
1684
|
+
const logger = options.logger || null;
|
|
1576
1685
|
|
|
1577
1686
|
let client = null;
|
|
1578
1687
|
let registrationError = '';
|
|
@@ -1580,10 +1689,35 @@ async function restartManagedPersonaRefreshTargets(flags = {}) {
|
|
|
1580
1689
|
client = createCliPersonaApiClient(flags);
|
|
1581
1690
|
} catch (error) {
|
|
1582
1691
|
registrationError = error instanceof Error ? error.message : String(error);
|
|
1692
|
+
logger?.(`Persona backend registration unavailable: ${registrationError}`);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
if (client) {
|
|
1696
|
+
try {
|
|
1697
|
+
const backendTargets = await discoverBackendLinkedPersonaRefreshTargets({
|
|
1698
|
+
client,
|
|
1699
|
+
workspaceRoot,
|
|
1700
|
+
existingTargets: targets,
|
|
1701
|
+
logger,
|
|
1702
|
+
});
|
|
1703
|
+
for (const target of backendTargets) {
|
|
1704
|
+
targetsBySlug.set(target.slug, target);
|
|
1705
|
+
targets.push(target);
|
|
1706
|
+
}
|
|
1707
|
+
} catch (error) {
|
|
1708
|
+
logger?.(`Backend persona discovery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1709
|
+
}
|
|
1583
1710
|
}
|
|
1584
1711
|
|
|
1712
|
+
logger?.(
|
|
1713
|
+
`Discovered ${targets.length} managed persona runtime${targets.length == 1 ? '' : 's'} to refresh.`,
|
|
1714
|
+
);
|
|
1715
|
+
|
|
1585
1716
|
for (const target of targets) {
|
|
1586
1717
|
const state = target.state && typeof target.state === 'object' ? target.state : {};
|
|
1718
|
+
logger?.(
|
|
1719
|
+
`Refreshing persona ${target.slug} (previous port: ${String(state.localPort || 'unknown')}).`,
|
|
1720
|
+
);
|
|
1587
1721
|
const launchResult = await launchManagedPersonaRuntime({
|
|
1588
1722
|
slug: target.slug,
|
|
1589
1723
|
name: String(state.name || target.slug).trim() || target.slug,
|
|
@@ -1600,6 +1734,7 @@ async function restartManagedPersonaRefreshTargets(flags = {}) {
|
|
|
1600
1734
|
let registration = null;
|
|
1601
1735
|
if (client) {
|
|
1602
1736
|
try {
|
|
1737
|
+
logger?.(`Re-registering runtime for ${launchResult.slug} at ${launchResult.runtime.endpoint}.`);
|
|
1603
1738
|
registration = await client.registerRuntime({
|
|
1604
1739
|
slug: launchResult.slug,
|
|
1605
1740
|
endpoint: launchResult.runtime.endpoint,
|
|
@@ -1613,9 +1748,14 @@ async function restartManagedPersonaRefreshTargets(flags = {}) {
|
|
|
1613
1748
|
ok: false,
|
|
1614
1749
|
error: error instanceof Error ? error.message : String(error),
|
|
1615
1750
|
};
|
|
1751
|
+
logger?.(`Runtime registration failed for ${launchResult.slug}: ${registration.error}`);
|
|
1616
1752
|
}
|
|
1617
1753
|
}
|
|
1618
1754
|
|
|
1755
|
+
logger?.(
|
|
1756
|
+
`Persona ${launchResult.slug} refreshed on port ${launchResult.runtime.localPort} (${launchResult.runtime.endpoint}).`,
|
|
1757
|
+
);
|
|
1758
|
+
|
|
1619
1759
|
results.push({
|
|
1620
1760
|
slug: launchResult.slug,
|
|
1621
1761
|
workspacePath: launchResult.workspacePath,
|
|
@@ -1634,10 +1774,12 @@ async function restartManagedPersonaRefreshTargets(flags = {}) {
|
|
|
1634
1774
|
};
|
|
1635
1775
|
}
|
|
1636
1776
|
|
|
1637
|
-
async function refreshBridgeForUpdate(flags = {}) {
|
|
1777
|
+
async function refreshBridgeForUpdate(flags = {}, options = {}) {
|
|
1778
|
+
const logger = options.logger || null;
|
|
1638
1779
|
if (process.platform === 'darwin') {
|
|
1639
1780
|
const launchdStatus = readBridgeLaunchdStatus();
|
|
1640
1781
|
if (launchdStatus.installed) {
|
|
1782
|
+
logger?.(`Restarting launchd-managed bridge ${launchdStatus.target}.`);
|
|
1641
1783
|
await stopBridgeLaunchdService();
|
|
1642
1784
|
startBridgeLaunchdService();
|
|
1643
1785
|
incrementBridgeMetric('bridge_restart_count');
|
|
@@ -1651,12 +1793,14 @@ async function refreshBridgeForUpdate(flags = {}) {
|
|
|
1651
1793
|
|
|
1652
1794
|
const running = findRunningBridgeProcess();
|
|
1653
1795
|
if (!running) {
|
|
1796
|
+
logger?.('No bridge process is currently running; skipping bridge restart.');
|
|
1654
1797
|
return {
|
|
1655
1798
|
restarted: false,
|
|
1656
1799
|
mode: 'process',
|
|
1657
1800
|
};
|
|
1658
1801
|
}
|
|
1659
1802
|
|
|
1803
|
+
logger?.(`Restarting bridge process ${running.pid}.`);
|
|
1660
1804
|
const stopResult = await stopBridgeProcesses();
|
|
1661
1805
|
if (Array.isArray(stopResult.stillAlive) && stopResult.stillAlive.length > 0) {
|
|
1662
1806
|
throw new Error(`Failed to stop bridge processes: ${stopResult.stillAlive.join(', ')}`);
|
|
@@ -1673,19 +1817,31 @@ async function refreshBridgeForUpdate(flags = {}) {
|
|
|
1673
1817
|
}
|
|
1674
1818
|
|
|
1675
1819
|
async function handleOpenclawRefreshCommand(flags = {}) {
|
|
1820
|
+
const jsonOutput = isTruthyFlag(flags.json);
|
|
1821
|
+
const logProgress = jsonOutput ? null : (message) => console.log(`[refresh] ${message}`);
|
|
1676
1822
|
const currentVersion = currentPackageVersion();
|
|
1677
1823
|
let latestVersion = '';
|
|
1678
1824
|
if (!isTruthyFlag(flags['skip-version-check'])) {
|
|
1825
|
+
logProgress?.(`Checking npm for the latest oomi-ai version (installed: ${currentVersion || 'unknown'}).`);
|
|
1679
1826
|
latestVersion = await fetchLatestPublishedVersion('oomi-ai');
|
|
1680
1827
|
if (latestVersion && compareVersions(currentVersion, latestVersion) < 0) {
|
|
1681
1828
|
throw new Error(
|
|
1682
1829
|
`Installed oomi-ai ${currentVersion} is behind npm ${latestVersion}. Update first, then rerun: oomi openclaw refresh`
|
|
1683
1830
|
);
|
|
1684
1831
|
}
|
|
1832
|
+
if (latestVersion) {
|
|
1833
|
+
logProgress?.(`Latest published version is ${latestVersion}.`);
|
|
1834
|
+
}
|
|
1685
1835
|
}
|
|
1686
1836
|
|
|
1687
|
-
|
|
1688
|
-
const
|
|
1837
|
+
logProgress?.('Refreshing managed persona runtimes.');
|
|
1838
|
+
const personaRefresh = await restartManagedPersonaRefreshTargets(flags, {
|
|
1839
|
+
logger: logProgress,
|
|
1840
|
+
});
|
|
1841
|
+
logProgress?.('Refreshing bridge process.');
|
|
1842
|
+
const bridgeRefresh = await refreshBridgeForUpdate(flags, {
|
|
1843
|
+
logger: logProgress,
|
|
1844
|
+
});
|
|
1689
1845
|
const payload = {
|
|
1690
1846
|
ok: true,
|
|
1691
1847
|
currentVersion,
|
|
@@ -1699,7 +1855,7 @@ async function handleOpenclawRefreshCommand(flags = {}) {
|
|
|
1699
1855
|
bridge: bridgeRefresh,
|
|
1700
1856
|
};
|
|
1701
1857
|
|
|
1702
|
-
if (
|
|
1858
|
+
if (jsonOutput) {
|
|
1703
1859
|
console.log(JSON.stringify(payload, null, 2));
|
|
1704
1860
|
return;
|
|
1705
1861
|
}
|
|
@@ -5827,5 +5983,8 @@ export {
|
|
|
5827
5983
|
isBridgeWorkerCommand,
|
|
5828
5984
|
parsePositiveInteger,
|
|
5829
5985
|
collectManagedPersonaRefreshTargets,
|
|
5986
|
+
discoverBackendLinkedPersonaRefreshTargets,
|
|
5987
|
+
normalizeBackendPersonaRefreshRecord,
|
|
5830
5988
|
resolvePersonaRuntimeInput,
|
|
5989
|
+
resolveExistingWorkspacePathForSlug,
|
|
5831
5990
|
};
|
package/lib/personaApiClient.js
CHANGED
|
@@ -100,10 +100,19 @@ export function createPersonaApiClient({
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
return {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
return {
|
|
104
|
+
listPersonas() {
|
|
105
|
+
return getJson({
|
|
106
|
+
fetchImpl,
|
|
107
|
+
backendUrl: resolvedBackendUrl,
|
|
108
|
+
deviceToken: resolvedDeviceToken,
|
|
109
|
+
path: '/v1/personas',
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
getPersona({
|
|
114
|
+
slug,
|
|
115
|
+
}) {
|
|
107
116
|
const safeSlug = trimString(slug);
|
|
108
117
|
if (!safeSlug) {
|
|
109
118
|
throw new Error('Persona slug is required.');
|
package/openclaw.plugin.json
CHANGED