nanobazaar-cli 1.0.11 → 1.0.12
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/nanobazaar +131 -97
- package/package.json +1 -1
package/bin/nanobazaar
CHANGED
|
@@ -667,14 +667,10 @@ Commands:
|
|
|
667
667
|
poll [--since-event-id <id>] [--limit <n>] [--types a,b] [--no-ack]
|
|
668
668
|
Poll events and optionally ack
|
|
669
669
|
watch [--streams a,b] [--stream-path /v0/stream] [--safety-poll-interval <seconds>]
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
watch-all [--streams a,b] [--stream-path /v0/stream] [--safety-poll-interval <seconds>]
|
|
675
|
-
[--state-path <path>] [--openclaw-bin <bin>] [--fswatch-bin <bin>]
|
|
676
|
-
[--event-text <text>] [--mode now|next] [--debounce-ms <ms>]
|
|
677
|
-
Run relay watch + state watcher together (recommended)
|
|
670
|
+
[--state-path <path>] [--openclaw-bin <bin>] [--fswatch-bin <bin>]
|
|
671
|
+
[--event-text <text>] [--mode now|next] [--debounce-ms <ms>]
|
|
672
|
+
Maintain SSE connection; poll on wake + on safety interval.
|
|
673
|
+
If fswatch is available, also watch local state and trigger OpenClaw wakeups.
|
|
678
674
|
|
|
679
675
|
Global flags:
|
|
680
676
|
--help Show this help
|
|
@@ -1635,12 +1631,116 @@ function deriveDefaultStreams({keys, state}) {
|
|
|
1635
1631
|
return uniqStrings(streams);
|
|
1636
1632
|
}
|
|
1637
1633
|
|
|
1634
|
+
function startStateWatcher(options) {
|
|
1635
|
+
const opts = options || {};
|
|
1636
|
+
const statePath = String(opts.statePath || '');
|
|
1637
|
+
const fswatchBin = String(opts.fswatchBin || 'fswatch');
|
|
1638
|
+
const openclawBin = String(opts.openclawBin || 'openclaw');
|
|
1639
|
+
const mode = String(opts.mode || 'now');
|
|
1640
|
+
const eventText = String(opts.eventText || 'NanoBazaar state changed');
|
|
1641
|
+
const debounceMs = typeof opts.debounceMs === 'number' ? opts.debounceMs : 250;
|
|
1642
|
+
const signal = opts.signal;
|
|
1643
|
+
|
|
1644
|
+
if (!statePath) {
|
|
1645
|
+
return {stop: () => {}};
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
if (!fs.existsSync(statePath)) {
|
|
1649
|
+
console.error(`[watch] state file not found at ${statePath}. Waiting for it to appear...`);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
let watcher = null;
|
|
1653
|
+
let buffer = '';
|
|
1654
|
+
let lastWake = 0;
|
|
1655
|
+
let openclawMissing = false;
|
|
1656
|
+
let disabled = false;
|
|
1657
|
+
|
|
1658
|
+
function disable(message) {
|
|
1659
|
+
if (disabled) {
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
disabled = true;
|
|
1663
|
+
if (message) {
|
|
1664
|
+
console.error(`[watch] ${message}`);
|
|
1665
|
+
}
|
|
1666
|
+
if (watcher && !watcher.killed) {
|
|
1667
|
+
watcher.kill('SIGTERM');
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function triggerWake() {
|
|
1672
|
+
if (openclawMissing) {
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
const now = Date.now();
|
|
1676
|
+
if (debounceMs && now - lastWake < debounceMs) {
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
lastWake = now;
|
|
1680
|
+
const result = spawnSync(openclawBin, ['system', 'event', '--text', eventText, '--mode', mode], {stdio: 'inherit'});
|
|
1681
|
+
if (result.error) {
|
|
1682
|
+
openclawMissing = true;
|
|
1683
|
+
console.error(`[watch] Failed to run ${openclawBin}: ${result.error.message}. Local wakeups disabled.`);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
try {
|
|
1688
|
+
watcher = spawn(fswatchBin, ['-0', '-o', statePath], {stdio: ['ignore', 'pipe', 'inherit']});
|
|
1689
|
+
} catch (err) {
|
|
1690
|
+
disable(`Failed to start fswatch: ${err && err.message ? err.message : String(err)}. Running SSE polling only.`);
|
|
1691
|
+
return {stop: () => {}};
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
watcher.on('error', (err) => {
|
|
1695
|
+
if (disabled) {
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
if (err && err.code === 'ENOENT') {
|
|
1699
|
+
disable('fswatch not found. Install it to enable local wakeups (macOS: brew install fswatch, Ubuntu: apt install fswatch).');
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
disable(`Failed to start fswatch: ${err && err.message ? err.message : String(err)}. Running SSE polling only.`);
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
watcher.stdout.on('data', (chunk) => {
|
|
1706
|
+
buffer += chunk.toString('utf8');
|
|
1707
|
+
const parts = buffer.split('\0');
|
|
1708
|
+
buffer = parts.pop();
|
|
1709
|
+
if (parts.length === 0) {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
triggerWake();
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
watcher.on('close', (code) => {
|
|
1716
|
+
if (disabled || (signal && signal.aborted)) {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
if (code === 0) {
|
|
1720
|
+
disable();
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
disable(`fswatch exited with code ${code}. Running SSE polling only.`);
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
if (signal) {
|
|
1727
|
+
if (signal.aborted) {
|
|
1728
|
+
disable();
|
|
1729
|
+
} else {
|
|
1730
|
+
signal.addEventListener('abort', () => disable());
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
return {stop: () => disable()};
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1638
1737
|
async function runWatch(argv) {
|
|
1639
1738
|
requireFetch();
|
|
1640
1739
|
|
|
1641
1740
|
const {flags} = parseArgs(argv);
|
|
1642
1741
|
const config = buildConfig();
|
|
1643
|
-
const
|
|
1742
|
+
const statePath = expandHomePath(String(flags.statePath || config.state_path));
|
|
1743
|
+
const state = loadState(statePath);
|
|
1644
1744
|
const {keys} = requireKeys(state);
|
|
1645
1745
|
const identity = deriveIdentity(keys);
|
|
1646
1746
|
|
|
@@ -1660,6 +1760,14 @@ async function runWatch(argv) {
|
|
|
1660
1760
|
const pollLimit = parseOptionalPositiveInt(flags.limit, '--limit')
|
|
1661
1761
|
?? parseOptionalPositiveInt(config.poll_limit, 'NBR_POLL_LIMIT');
|
|
1662
1762
|
|
|
1763
|
+
const fswatchBin = String(flags.fswatchBin || 'fswatch');
|
|
1764
|
+
const openclawBin = String(flags.openclawBin || 'openclaw');
|
|
1765
|
+
const mode = String(flags.mode || 'now');
|
|
1766
|
+
const eventText = String(flags.eventText || 'NanoBazaar state changed');
|
|
1767
|
+
const debounceMs = flags.debounceMs
|
|
1768
|
+
? parsePositiveInt(flags.debounceMs, '--debounce-ms')
|
|
1769
|
+
: 250;
|
|
1770
|
+
|
|
1663
1771
|
ensureStreamCursorMap(state);
|
|
1664
1772
|
const streamSet = new Set(streams);
|
|
1665
1773
|
|
|
@@ -1731,7 +1839,7 @@ async function runWatch(argv) {
|
|
|
1731
1839
|
|
|
1732
1840
|
const addedEvents = appendEvents(state, allEvents);
|
|
1733
1841
|
if (addedEvents > 0) {
|
|
1734
|
-
saveStateMerged(
|
|
1842
|
+
saveStateMerged(statePath, state);
|
|
1735
1843
|
}
|
|
1736
1844
|
|
|
1737
1845
|
let ackedStreams = 0;
|
|
@@ -1761,7 +1869,7 @@ async function runWatch(argv) {
|
|
|
1761
1869
|
}
|
|
1762
1870
|
|
|
1763
1871
|
setStreamCursor(state, entry.stream, next);
|
|
1764
|
-
saveStateMerged(
|
|
1872
|
+
saveStateMerged(statePath, state);
|
|
1765
1873
|
ackedStreams += 1;
|
|
1766
1874
|
}
|
|
1767
1875
|
}
|
|
@@ -1816,11 +1924,21 @@ async function runWatch(argv) {
|
|
|
1816
1924
|
process.on('SIGTERM', stop);
|
|
1817
1925
|
|
|
1818
1926
|
console.error(`[watch] relay=${config.relay_url}`);
|
|
1819
|
-
console.error(`[watch] state_path=${
|
|
1927
|
+
console.error(`[watch] state_path=${statePath}`);
|
|
1820
1928
|
console.error(`[watch] stream_path=${streamPath}`);
|
|
1821
1929
|
console.error(`[watch] streams=${streams.join(',')}`);
|
|
1822
1930
|
console.error(`[watch] safety_poll_interval_seconds=${safetyIntervalSeconds}`);
|
|
1823
1931
|
|
|
1932
|
+
const stateWatcher = startStateWatcher({
|
|
1933
|
+
statePath,
|
|
1934
|
+
fswatchBin,
|
|
1935
|
+
openclawBin,
|
|
1936
|
+
mode,
|
|
1937
|
+
eventText,
|
|
1938
|
+
debounceMs,
|
|
1939
|
+
signal,
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1824
1942
|
const safetyTimer = setInterval(() => {
|
|
1825
1943
|
if (signal.aborted) {
|
|
1826
1944
|
return;
|
|
@@ -1913,85 +2031,7 @@ async function runWatch(argv) {
|
|
|
1913
2031
|
}
|
|
1914
2032
|
|
|
1915
2033
|
clearInterval(safetyTimer);
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
async function runWatchState(argv) {
|
|
1919
|
-
const {flags} = parseArgs(argv);
|
|
1920
|
-
const config = buildConfig();
|
|
1921
|
-
const statePath = expandHomePath(String(flags.statePath || config.state_path));
|
|
1922
|
-
const fswatchBin = String(flags.fswatchBin || 'fswatch');
|
|
1923
|
-
const openclawBin = String(flags.openclawBin || 'openclaw');
|
|
1924
|
-
const mode = String(flags.mode || 'now');
|
|
1925
|
-
const eventText = String(flags.eventText || 'NanoBazaar state changed');
|
|
1926
|
-
const debounceMs = flags.debounceMs
|
|
1927
|
-
? parsePositiveInt(flags.debounceMs, '--debounce-ms')
|
|
1928
|
-
: 250;
|
|
1929
|
-
|
|
1930
|
-
if (!fs.existsSync(statePath)) {
|
|
1931
|
-
console.error(`State file not found at ${statePath}. Waiting for it to appear...`);
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
let buffer = '';
|
|
1935
|
-
let lastWake = 0;
|
|
1936
|
-
let openclawMissing = false;
|
|
1937
|
-
|
|
1938
|
-
function triggerWake() {
|
|
1939
|
-
if (openclawMissing) {
|
|
1940
|
-
return;
|
|
1941
|
-
}
|
|
1942
|
-
const now = Date.now();
|
|
1943
|
-
if (debounceMs && now - lastWake < debounceMs) {
|
|
1944
|
-
return;
|
|
1945
|
-
}
|
|
1946
|
-
lastWake = now;
|
|
1947
|
-
const result = spawnSync(openclawBin, ['system', 'event', '--text', eventText, '--mode', mode], {stdio: 'inherit'});
|
|
1948
|
-
if (result.error) {
|
|
1949
|
-
openclawMissing = true;
|
|
1950
|
-
console.error(`Failed to run ${openclawBin}: ${result.error.message}`);
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
return new Promise((resolve, reject) => {
|
|
1955
|
-
const watcher = spawn(fswatchBin, ['-0', '-o', statePath], {stdio: ['ignore', 'pipe', 'inherit']});
|
|
1956
|
-
|
|
1957
|
-
watcher.on('error', (err) => {
|
|
1958
|
-
if (err && err.code === 'ENOENT') {
|
|
1959
|
-
reject(new Error(`fswatch not found. Install it (macOS: brew install fswatch).`));
|
|
1960
|
-
return;
|
|
1961
|
-
}
|
|
1962
|
-
reject(new Error(`Failed to start fswatch: ${err && err.message ? err.message : String(err)}`));
|
|
1963
|
-
});
|
|
1964
|
-
|
|
1965
|
-
watcher.stdout.on('data', (chunk) => {
|
|
1966
|
-
buffer += chunk.toString('utf8');
|
|
1967
|
-
const parts = buffer.split('\0');
|
|
1968
|
-
buffer = parts.pop();
|
|
1969
|
-
if (parts.length === 0) {
|
|
1970
|
-
return;
|
|
1971
|
-
}
|
|
1972
|
-
triggerWake();
|
|
1973
|
-
});
|
|
1974
|
-
|
|
1975
|
-
const forwardSignal = (signal) => {
|
|
1976
|
-
if (!watcher.killed) {
|
|
1977
|
-
watcher.kill(signal);
|
|
1978
|
-
}
|
|
1979
|
-
};
|
|
1980
|
-
process.on('SIGINT', () => forwardSignal('SIGINT'));
|
|
1981
|
-
process.on('SIGTERM', () => forwardSignal('SIGTERM'));
|
|
1982
|
-
|
|
1983
|
-
watcher.on('close', (code) => {
|
|
1984
|
-
if (code === 0) {
|
|
1985
|
-
resolve();
|
|
1986
|
-
return;
|
|
1987
|
-
}
|
|
1988
|
-
reject(new Error(`fswatch exited with code ${code}`));
|
|
1989
|
-
});
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
async function runWatchAll(argv) {
|
|
1994
|
-
await Promise.all([runWatch(argv), runWatchState(argv)]);
|
|
2034
|
+
stateWatcher.stop();
|
|
1995
2035
|
}
|
|
1996
2036
|
|
|
1997
2037
|
function loadVersion() {
|
|
@@ -2093,12 +2133,6 @@ async function main() {
|
|
|
2093
2133
|
case 'watch':
|
|
2094
2134
|
await runWatch(rest);
|
|
2095
2135
|
return;
|
|
2096
|
-
case 'watch-state':
|
|
2097
|
-
await runWatchState(rest);
|
|
2098
|
-
return;
|
|
2099
|
-
case 'watch-all':
|
|
2100
|
-
await runWatchAll(rest);
|
|
2101
|
-
return;
|
|
2102
2136
|
default:
|
|
2103
2137
|
throw new Error(`Unknown command: ${command}`);
|
|
2104
2138
|
}
|