All checks were successful
deploy / deploy (push) Successful in 58s
Proof: quote-response execution now uses verifierSaltCache.getCachedFreshSalt() only, rejects verifier_salt_unavailable before signing or relay when no fresh cached salt exists, preserves stale command expiry before salt lookup, exposes salt cache and executor queue-delay state in health/sentinel/dashboard surfaces, and is covered by targeted regression tests plus full npm test and dashboard build. Assumptions: the existing verifier salt freshness policy remains safe for cached signing use; fixed in-code queue-delay warning thresholds are sufficient for operator alerts without new latency env vars; no strategy selection, pricing, edge, notional, inventory, arming, signer, or pair enablement behavior changed. Still fake: venue-native terminal fill ids and fee-complete realized PnL remain unavailable; executor concurrency remains single-consumer; live post-deploy evidence still needs collection after repo-workflow deployment.
156 lines
4 KiB
JavaScript
156 lines
4 KiB
JavaScript
const COMMON_STATE_FIELDS = [
|
|
'paused',
|
|
'armed',
|
|
'last_error',
|
|
'last_event_at',
|
|
'last_runtime_eval_at',
|
|
'last_published_at',
|
|
'last_publish_error',
|
|
'publish_count',
|
|
'last_sync_at',
|
|
'last_write_at',
|
|
'last_metrics_at',
|
|
'last_quote_outcomes_at',
|
|
'database_connectivity',
|
|
'source_error_count',
|
|
'last_source_error_at',
|
|
'last_bootstrap_at',
|
|
];
|
|
|
|
export function summarizeServiceSnapshotForSentinel(snapshot = {}) {
|
|
return {
|
|
service: snapshot.service,
|
|
label: snapshot.label,
|
|
base_url: snapshot.base_url,
|
|
reachable: snapshot.reachable,
|
|
state: summarizeServiceState(snapshot.service, snapshot.state),
|
|
health: snapshot.health || null,
|
|
error: snapshot.error || null,
|
|
};
|
|
}
|
|
|
|
export function summarizeServiceState(service, state) {
|
|
if (!state || typeof state !== 'object') return state || null;
|
|
|
|
switch (service) {
|
|
case 'near-intents-ingest':
|
|
return pickFields(state, ['service', 'namespace', 'pair_filter', 'ingest']);
|
|
case 'market-reference-ingest':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'paused',
|
|
'kraken',
|
|
'coingecko',
|
|
'last_published_at',
|
|
'last_publish_error',
|
|
'publish_count',
|
|
'error_count',
|
|
'active_pair',
|
|
]);
|
|
case 'inventory-sync':
|
|
return {
|
|
...pickFields(state, ['service', 'namespace', 'account_id', 'paused', 'last_sync_at', 'last_error', 'publish_count']),
|
|
last_snapshot: summarizeInventorySnapshot(state.last_snapshot),
|
|
};
|
|
case 'liquidity-manager':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'account_id',
|
|
'paused',
|
|
'funding_observer_paused',
|
|
'withdrawals_frozen',
|
|
'deposit_addresses',
|
|
'supported_tokens_error',
|
|
'withdrawal_defaults',
|
|
'latest_funding_observation_at',
|
|
'last_refresh_at',
|
|
'last_error',
|
|
]);
|
|
case 'history-writer':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'database_connectivity',
|
|
'last_write_at',
|
|
'last_alert_write_at',
|
|
'last_funding_observation_write_at',
|
|
'last_environment_status_write_at',
|
|
'last_environment_status_seen_at',
|
|
'last_environment_status_duplicate_at',
|
|
'last_metrics_at',
|
|
'last_quote_outcomes_at',
|
|
'latest_portfolio_metrics',
|
|
'latest_quote_outcomes',
|
|
'offsets',
|
|
'metrics_error',
|
|
'quote_outcomes_error',
|
|
]);
|
|
case 'strategy-engine':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'paused',
|
|
'armed',
|
|
'last_error',
|
|
'latest_decision',
|
|
'latest_inventory_event',
|
|
'decision_count',
|
|
'command_count',
|
|
'reject_count',
|
|
'block_count',
|
|
]);
|
|
case 'trade-executor':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'paused',
|
|
'armed',
|
|
'last_error',
|
|
'relay',
|
|
'verifier_salt_cache',
|
|
'executor_queue_delay',
|
|
'last_result',
|
|
'last_quote_status',
|
|
'submitted_count',
|
|
'salt_unavailable_reject_count',
|
|
'last_salt_unavailable_at',
|
|
'last_salt_unavailable_state',
|
|
'failed_count',
|
|
'blocked_count',
|
|
]);
|
|
case 'operator-dashboard':
|
|
return pickFields(state, [
|
|
'service',
|
|
'namespace',
|
|
'source_error_count',
|
|
'last_source_error_at',
|
|
'last_bootstrap_at',
|
|
'source_errors',
|
|
]);
|
|
default:
|
|
return pickFields(state, COMMON_STATE_FIELDS);
|
|
}
|
|
}
|
|
|
|
function summarizeInventorySnapshot(snapshot) {
|
|
if (!snapshot || typeof snapshot !== 'object') return snapshot || null;
|
|
return pickFields(snapshot, [
|
|
'inventory_id',
|
|
'account_id',
|
|
'synced_at',
|
|
'spendable',
|
|
'pending_inbound',
|
|
'pending_outbound',
|
|
'reconciliation_status',
|
|
]);
|
|
}
|
|
|
|
function pickFields(source, fields) {
|
|
const picked = {};
|
|
for (const field of fields) {
|
|
if (source[field] !== undefined) picked[field] = source[field];
|
|
}
|
|
return picked;
|
|
}
|