dexbot 1.4.20 → 1.4.21
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/CHANGELOG.md +23 -0
- package/README.md +22 -12
- package/analysis/ama_fitting/calibrate_convergence_er.ts +1 -1
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/bot_fitting/shared_utils.ts +1 -1
- package/analysis/bot_key_utils.ts +1 -5
- package/analysis/bot_usage/kibana_bot_queries.ts +1 -256
- package/analysis/chart_css.ts +1 -1
- package/analysis/math_utils.ts +1 -8
- package/analysis/trend_detection/package.json +1 -1
- package/claw/ecosystem.config.cjs +2 -3
- package/claw/examples/memu_integration_example.ts +17 -17
- package/claw/modules/chain_actions.ts +51 -54
- package/claw/modules/chain_broadcast.ts +66 -104
- package/claw/modules/claw_bridge.ts +60 -80
- package/claw/modules/claw_catalog.ts +47 -16
- package/claw/modules/claw_infra.ts +1 -5
- package/claw/modules/claw_launcher.ts +47 -49
- package/claw/modules/claw_skill_md.ts +8 -11
- package/claw/modules/credit_runtime_adapter.ts +2 -18
- package/claw/modules/decision_loop.ts +4 -0
- package/claw/modules/dexbot_bridge.ts +1 -1
- package/claw/modules/dexbot_profiles.ts +5 -21
- package/claw/modules/honest_ecosystem.ts +24 -8
- package/claw/modules/liquidity_pools.ts +10 -3
- package/claw/modules/mcp_utils.ts +109 -0
- package/claw/modules/memu_bridge.ts +76 -54
- package/claw/modules/mpa_utils.ts +26 -3
- package/claw/modules/position_discovery.ts +16 -27
- package/claw/modules/position_manager.ts +32 -41
- package/claw/modules/position_manager_watch.ts +3 -2
- package/claw/modules/short_mpa_strategy.ts +1 -9
- package/claw/modules/skill_utils.ts +5 -3
- package/claw/modules/utils.ts +9 -1
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/index.ts +27 -10
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/scripts/claw_bridge.ts +1 -1
- package/claw/scripts/claw_mcp_server.ts +19 -78
- package/claw/scripts/honest_assets_report.ts +19 -84
- package/claw/scripts/memu_mcp_server.ts +57 -155
- package/claw/tests/test_claw_bridge.ts +6 -2
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/analysis/ama_fitting/calibrate_convergence_er.js +1 -1
- package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -1
- package/dist/analysis/bot_fitting/shared_utils.d.ts +1 -12
- package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -1
- package/dist/analysis/bot_fitting/shared_utils.js +1 -1
- package/dist/analysis/bot_fitting/shared_utils.js.map +1 -1
- package/dist/analysis/bot_key_utils.d.ts +1 -1
- package/dist/analysis/bot_key_utils.d.ts.map +1 -1
- package/dist/analysis/bot_key_utils.js +1 -5
- package/dist/analysis/bot_key_utils.js.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +1 -337
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js +1 -241
- package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
- package/dist/analysis/chart_css.d.ts +1 -15
- package/dist/analysis/chart_css.d.ts.map +1 -1
- package/dist/analysis/chart_css.js +1 -1
- package/dist/analysis/chart_css.js.map +1 -1
- package/dist/analysis/math_utils.d.ts +3 -5
- package/dist/analysis/math_utils.d.ts.map +1 -1
- package/dist/analysis/math_utils.js +3 -5
- package/dist/analysis/math_utils.js.map +1 -1
- package/dist/bot.js +2 -2
- package/dist/bot.js.map +1 -1
- package/dist/credential-daemon.js +1 -1
- package/dist/credential-daemon.js.map +1 -1
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +18 -14
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/ama_signal_runner.js +3 -3
- package/dist/market_adapter/core/kibana_client.d.ts +1 -3
- package/dist/market_adapter/core/kibana_client.d.ts.map +1 -1
- package/dist/market_adapter/core/kibana_client.js +1 -1
- package/dist/market_adapter/core/kibana_client.js.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.d.ts +1 -2
- package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -1
- package/dist/market_adapter/core/market_adapter_service.js +1 -1
- package/dist/market_adapter/core/market_adapter_service.js.map +1 -1
- package/dist/market_adapter/core/strategies/regime_gate.d.ts +1 -2
- package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -1
- package/dist/market_adapter/core/strategies/regime_gate.js +1 -1
- package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +2 -2
- package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +5 -5
- package/dist/market_adapter/inputs/fetch_lp_data.js +5 -5
- package/dist/market_adapter/inputs/kibana_source.d.ts +1 -2
- package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -1
- package/dist/market_adapter/inputs/kibana_source.js +1 -1
- package/dist/market_adapter/inputs/kibana_source.js.map +1 -1
- package/dist/market_adapter/lp_chart_strategy_loader.d.ts +1 -2
- package/dist/market_adapter/lp_chart_strategy_loader.d.ts.map +1 -1
- package/dist/market_adapter/lp_chart_strategy_loader.js +4 -4
- package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -1
- package/dist/market_adapter/market_adapter.d.ts.map +1 -1
- package/dist/market_adapter/market_adapter.js +2 -1
- package/dist/market_adapter/market_adapter.js.map +1 -1
- package/dist/modules/account_bots.d.ts +3 -0
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +156 -46
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/account_orders.d.ts +1 -7
- package/dist/modules/account_orders.d.ts.map +1 -1
- package/dist/modules/account_orders.js +9 -17
- package/dist/modules/account_orders.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +2 -14
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +22 -31
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/bot_settings.js +1 -1
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/chain_keys.js +2 -2
- package/dist/modules/chain_keys.js.map +1 -1
- package/dist/modules/chain_orders.d.ts +2 -3
- package/dist/modules/chain_orders.d.ts.map +1 -1
- package/dist/modules/chain_orders.js +8 -25
- package/dist/modules/chain_orders.js.map +1 -1
- package/dist/modules/config.d.ts.map +1 -1
- package/dist/modules/config.js +4 -1
- package/dist/modules/config.js.map +1 -1
- package/dist/modules/constants.d.ts +0 -1
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +0 -4
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credential_policy.js +1 -1
- package/dist/modules/credential_policy.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +3 -2
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +2 -1
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/fund_registry.d.ts.map +1 -1
- package/dist/modules/fund_registry.js +5 -2
- package/dist/modules/fund_registry.js.map +1 -1
- package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -1
- package/dist/modules/launcher/bot_supervisor.js +6 -7
- package/dist/modules/launcher/bot_supervisor.js.map +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -1
- package/dist/modules/launcher/foreign_cred_daemon.js +6 -0
- package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.d.ts +2 -3
- package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/market_adapter_runtime.js +7 -15
- package/dist/modules/launcher/market_adapter_runtime.js.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -1
- package/dist/modules/launcher/monolithic_runtime.js +1 -0
- package/dist/modules/launcher/monolithic_runtime.js.map +1 -1
- package/dist/modules/launcher/status_reporting.d.ts +1 -2
- package/dist/modules/launcher/status_reporting.d.ts.map +1 -1
- package/dist/modules/launcher/status_reporting.js +1 -4
- package/dist/modules/launcher/status_reporting.js.map +1 -1
- package/dist/modules/order/accounting.js +2 -2
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/format.d.ts +0 -8
- package/dist/modules/order/format.d.ts.map +1 -1
- package/dist/modules/order/format.js +0 -8
- package/dist/modules/order/format.js.map +1 -1
- package/dist/modules/order/logger.js +1 -1
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/logger_state.d.ts +1 -13
- package/dist/modules/order/logger_state.d.ts.map +1 -1
- package/dist/modules/order/logger_state.js +1 -11
- package/dist/modules/order/logger_state.js.map +1 -1
- package/dist/modules/order/processed_fill_store.d.ts +0 -5
- package/dist/modules/order/processed_fill_store.d.ts.map +1 -1
- package/dist/modules/order/processed_fill_store.js +1 -12
- package/dist/modules/order/processed_fill_store.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +10 -3
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +7 -1
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +19 -1
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +10 -2
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/runtime_settings.js +3 -1
- package/dist/modules/runtime_settings.js.map +1 -1
- package/dist/modules/settings_merge.d.ts.map +1 -1
- package/dist/modules/settings_merge.js +3 -1
- package/dist/modules/settings_merge.js.map +1 -1
- package/dist/modules/storage/browser_adapter.d.ts +8 -3
- package/dist/modules/storage/browser_adapter.d.ts.map +1 -1
- package/dist/modules/storage/browser_adapter.js +53 -4
- package/dist/modules/storage/browser_adapter.js.map +1 -1
- package/dist/modules/utils/sanitize_key.d.ts +7 -0
- package/dist/modules/utils/sanitize_key.d.ts.map +1 -0
- package/dist/modules/utils/sanitize_key.js +15 -0
- package/dist/modules/utils/sanitize_key.js.map +1 -0
- package/dist/pm2.js +6 -6
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/analyze-orders.d.ts +2 -2
- package/dist/scripts/analyze-orders.d.ts.map +1 -1
- package/dist/scripts/analyze-orders.js +4 -12
- package/dist/scripts/analyze-orders.js.map +1 -1
- package/dist/scripts/runner.js +1 -1
- package/dist/scripts/runner.js.map +1 -1
- package/dist/scripts/update.js +1 -1
- package/dist/scripts/update.js.map +1 -1
- package/docs/BITSHARES_ONBOARDING.md +63 -18
- package/docs/COPY_ON_WRITE_MASTER_PLAN.md +5 -14
- package/docs/COW_INVARIANTS.md +4 -4
- package/docs/CREDENTIAL_SECURITY.md +0 -11
- package/docs/DEXBOT_COMPARISON.md +11 -11
- package/docs/EVOLUTION.md +11 -10
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +27 -83
- package/docs/GRID_RECALCULATION.md +1 -13
- package/docs/GRID_RECONCILE.md +24 -24
- package/docs/LIFECYCLE.md +4 -4
- package/docs/LOGGING.md +1 -1
- package/docs/MPA_CREDIT_USAGE.md +3 -3
- package/docs/README.md +2 -2
- package/docs/WORKFLOW.md +2 -2
- package/docs/architecture.md +43 -38
- package/docs/developer_guide.md +16 -18
- package/market_adapter/README.md +0 -2
- package/modules/README.md +2 -1
- package/package.json +1 -1
- package/scripts/README.md +1 -1
- package/claw/openclaw.plugin.json +0 -13
- package/dist/market_adapter/merge_lp_data.d.ts +0 -3
- package/dist/market_adapter/merge_lp_data.d.ts.map +0 -1
- package/dist/market_adapter/merge_lp_data.js +0 -125
- package/dist/market_adapter/merge_lp_data.js.map +0 -1
- package/dist/market_adapter/utils/paths.d.ts +0 -3
- package/dist/market_adapter/utils/paths.d.ts.map +0 -1
- package/dist/market_adapter/utils/paths.js +0 -5
- package/dist/market_adapter/utils/paths.js.map +0 -1
|
@@ -3,11 +3,12 @@ import * as client from './bitshares_client.js';
|
|
|
3
3
|
const { BitShares } = client;
|
|
4
4
|
import { executeOperations } from './chain_broadcast.js';
|
|
5
5
|
import { loadDexbotOrderUtils, requireDexbot2Module, loadDexbotOrderConstants } from './dexbot_bridge.js';
|
|
6
|
-
import { requireBtsBackedMpa
|
|
7
|
-
import { getAsset,
|
|
6
|
+
import { requireBtsBackedMpa } from './mpa_utils.js';
|
|
7
|
+
import { getAsset, getFullAccount, readOpenOrders, resolveAccountId, resolveAccountName } from './chain_queries.js';
|
|
8
8
|
import { getErrorMessage } from '../../modules/utils/errors.js';
|
|
9
9
|
|
|
10
|
-
// Graphene protocol constant: operation type 4 =
|
|
10
|
+
// Graphene protocol constant: operation type 4 = fill_order (limit_order_create
|
|
11
|
+
// is type 1). Account history subscriptions emit op[0] = 4 for matched fills.
|
|
11
12
|
const FILL_ORDER_OPERATION_TYPE = 4;
|
|
12
13
|
const accountSubscriptions = new Map();
|
|
13
14
|
|
|
@@ -134,18 +135,7 @@ async function buildBorrowMpaOperation({
|
|
|
134
135
|
throw new Error(`Account not found: ${accountName}`);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
const mpaMeta = await
|
|
138
|
-
if (!mpaMeta.bitasset_data_id) {
|
|
139
|
-
throw new Error(`${mpaAsset} is not a market-issued asset`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const backingAsset = await getBackingAsset(mpaAsset);
|
|
143
|
-
if (!backingAsset) {
|
|
144
|
-
throw new Error(`Could not resolve backing asset for ${mpaAsset}`);
|
|
145
|
-
}
|
|
146
|
-
if (backingAsset.symbol !== CORE_SYMBOL) {
|
|
147
|
-
throw new Error(`${mpaAsset} is backed by ${backingAsset.symbol}, not ${CORE_SYMBOL}; use short_mpa_strategy for non-BTS MPAs`);
|
|
148
|
-
}
|
|
138
|
+
const { backingAsset, mpaAsset: mpaMeta } = await requireBtsBackedMpa(mpaAsset, 'use short_mpa_strategy for non-BTS MPAs');
|
|
149
139
|
|
|
150
140
|
const floatToBlockchainInt = getFloatToBlockchainInt();
|
|
151
141
|
const debtInt = floatToBlockchainInt(debtDelta, mpaMeta.precision);
|
|
@@ -179,18 +169,7 @@ async function buildSettleMpaOperation({ accountName, mpaAsset, amount }: any) {
|
|
|
179
169
|
throw new Error(`Account not found: ${accountName}`);
|
|
180
170
|
}
|
|
181
171
|
|
|
182
|
-
const mpaMeta = await
|
|
183
|
-
if (!mpaMeta.bitasset_data_id) {
|
|
184
|
-
throw new Error(`${mpaAsset} is not a market-issued asset`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const backingAsset = await getBackingAsset(mpaAsset);
|
|
188
|
-
if (!backingAsset) {
|
|
189
|
-
throw new Error(`Could not resolve backing asset for ${mpaAsset}`);
|
|
190
|
-
}
|
|
191
|
-
if (backingAsset.symbol !== CORE_SYMBOL) {
|
|
192
|
-
throw new Error(`${mpaAsset} is backed by ${backingAsset.symbol}, not ${CORE_SYMBOL}`);
|
|
193
|
-
}
|
|
172
|
+
const { mpaAsset: mpaMeta } = await requireBtsBackedMpa(mpaAsset);
|
|
194
173
|
|
|
195
174
|
const floatToBlockchainInt = getFloatToBlockchainInt();
|
|
196
175
|
const settleAmountInt = floatToBlockchainInt(amount, mpaMeta.precision);
|
|
@@ -304,36 +283,19 @@ async function listenForFills(accountNameOrId: any, callback: any) {
|
|
|
304
283
|
|
|
305
284
|
if (!accountSubscriptions.has(accountName)) {
|
|
306
285
|
const callbacks: Set<Function> = new Set();
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
// caught here, and attach a catch for async rejections so a rejected
|
|
316
|
-
// handler cannot become an unhandled promise rejection (crash on
|
|
317
|
-
// Node >= 15).
|
|
318
|
-
try {
|
|
319
|
-
const result = fn(fills);
|
|
320
|
-
if (result && typeof result.then === 'function') {
|
|
321
|
-
result.catch((err: any) => {
|
|
322
|
-
console.error('listenForFills callback error:', getErrorMessage(err));
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
} catch (err: any) {
|
|
326
|
-
console.error('listenForFills callback error:', getErrorMessage(err));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
BitShares.subscribe('account', bsCallback, accountName);
|
|
286
|
+
let bsCallback: Function | null = null;
|
|
287
|
+
try {
|
|
288
|
+
bsCallback = await subscribeAccountFills(accountName, callbacks);
|
|
289
|
+
} catch (err: any) {
|
|
290
|
+
throw new Error(`Failed to subscribe to account updates for ${accountName}: ${getErrorMessage(err)}`);
|
|
291
|
+
}
|
|
292
|
+
// Register only after the remote subscription succeeded so callers never
|
|
293
|
+
// believe they are subscribed when setup actually failed.
|
|
332
294
|
accountSubscriptions.set(accountName, { callbacks, bsCallback });
|
|
333
295
|
}
|
|
334
296
|
|
|
335
297
|
const entry = accountSubscriptions.get(accountName);
|
|
336
|
-
if (!entry) {
|
|
298
|
+
if (!entry || !entry.bsCallback) {
|
|
337
299
|
throw new Error(`Subscription entry not found for ${accountName}`);
|
|
338
300
|
}
|
|
339
301
|
entry.callbacks.add(callback);
|
|
@@ -357,5 +319,40 @@ async function listenForFills(accountNameOrId: any, callback: any) {
|
|
|
357
319
|
};
|
|
358
320
|
}
|
|
359
321
|
|
|
360
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Subscribe to account history fills and fan them out to every registered
|
|
324
|
+
* callback. The underlying subscription call is async and can reject (network
|
|
325
|
+
* error, unknown account); the promise is awaited by listenForFills so a
|
|
326
|
+
* failure surfaces instead of becoming an unhandled rejection.
|
|
327
|
+
*/
|
|
328
|
+
async function subscribeAccountFills(accountName: string, callbacks: Set<Function>) {
|
|
329
|
+
const bsCallback = (updates: any[] = []) => {
|
|
330
|
+
const fills = updates.filter((update) => update?.op && update.op[0] === FILL_ORDER_OPERATION_TYPE);
|
|
331
|
+
if (fills.length === 0) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
for (const fn of Array.from(callbacks)) {
|
|
336
|
+
// The callback may be async. Run it synchronously so sync throws are
|
|
337
|
+
// caught here, and attach a catch for async rejections so a rejected
|
|
338
|
+
// handler cannot become an unhandled promise rejection (crash on
|
|
339
|
+
// Node >= 15).
|
|
340
|
+
try {
|
|
341
|
+
const result = fn(fills);
|
|
342
|
+
if (result && typeof result.then === 'function') {
|
|
343
|
+
result.catch((err: any) => {
|
|
344
|
+
console.error('listenForFills callback error:', getErrorMessage(err));
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
} catch (err: any) {
|
|
348
|
+
console.error('listenForFills callback error:', getErrorMessage(err));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
await BitShares.subscribe('account', bsCallback, accountName);
|
|
354
|
+
return bsCallback;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export { adjustMpaCollateral, borrowMpa, buildBorrowMpaOperation, buildCreateLimitOrderOperation, buildUpdateLimitOrderOperation, buildSettleMpaOperation, cancelLimitOrder, createLimitOrder, executeBatch, getMpaPosition, getOpenOrders, listenForFills, repayMpaDebt, updateLimitOrder, settleMpa }
|
|
361
358
|
|
|
@@ -130,7 +130,7 @@ function resolveAccountName(options: Record<string, any> = {}) {
|
|
|
130
130
|
return options.accountName || Config.BITSHARES_ACCOUNT || null;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
async function createSigningClientFromCredentialDaemon(
|
|
133
|
+
async function createSigningClientFromCredentialDaemon() {
|
|
134
134
|
throw new Error(
|
|
135
135
|
'The credential daemon no longer exports raw private keys. ' +
|
|
136
136
|
'Use broadcastOperationViaCredentialDaemon() or executeOperationsViaCredentialDaemon() ' +
|
|
@@ -152,10 +152,7 @@ async function getSigningClient(options: Record<string, any> = {}) {
|
|
|
152
152
|
throw new Error('Credential daemon is not ready');
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
return createSigningClientFromCredentialDaemon(
|
|
156
|
-
...options,
|
|
157
|
-
accountName
|
|
158
|
-
});
|
|
155
|
+
return createSigningClientFromCredentialDaemon();
|
|
159
156
|
}
|
|
160
157
|
|
|
161
158
|
function normalizeOperations(operations: any) {
|
|
@@ -163,6 +160,53 @@ function normalizeOperations(operations: any) {
|
|
|
163
160
|
return ops.filter(Boolean);
|
|
164
161
|
}
|
|
165
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Shared credential-daemon broadcast plumbing: clamps the request timeout so
|
|
165
|
+
* the daemon's inner deadline can fire first, waits for the daemon, resolves
|
|
166
|
+
* session credentials, and retries once on session/HMAC failures (SIGHUP on
|
|
167
|
+
* source-auth denials). `dispatch` performs the actual daemon call and must
|
|
168
|
+
* be idempotent-safe: it is only re-invoked after credentials are re-resolved.
|
|
169
|
+
*/
|
|
170
|
+
async function viaCredentialDaemon(accountName: string, options: Record<string, any>, dispatch: (creds: { sessionId: string | null; botHmacSecret: string | null }) => Promise<any>) {
|
|
171
|
+
const daemonTimeoutMs = Number.isFinite(Number(options.daemonTimeoutMs))
|
|
172
|
+
? Number(options.daemonTimeoutMs)
|
|
173
|
+
: undefined;
|
|
174
|
+
// Clamp the broadcast request timeout up to the default outer window: the
|
|
175
|
+
// daemon's inner deadline (25s) must be allowed to fire and reply a typed
|
|
176
|
+
// BROADCAST_DEADLINE before this socket timer. A caller-supplied short
|
|
177
|
+
// timeout would time out mid-broadcast — a false uncertain.
|
|
178
|
+
const requestTimeoutMs = Number.isFinite(Number(options.daemonRequestTimeoutMs))
|
|
179
|
+
? Math.max(Number(options.daemonRequestTimeoutMs), DEFAULT_BROADCAST_TIMEOUT_MS)
|
|
180
|
+
: undefined;
|
|
181
|
+
|
|
182
|
+
await waitForCredentialDaemon(daemonTimeoutMs, options);
|
|
183
|
+
|
|
184
|
+
const creds = await resolveSessionCredentials(accountName, options);
|
|
185
|
+
const socketOptions = {
|
|
186
|
+
socketPath: options.socketPath,
|
|
187
|
+
timeoutMs: requestTimeoutMs
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
return await dispatch({ ...socketOptions, ...creds });
|
|
192
|
+
} catch (err: any) {
|
|
193
|
+
const msg = String(getErrorMessage(err) || err);
|
|
194
|
+
if (msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED) || msg.includes(DAEMON_ERRORS.SESSION_EXPIRED)) {
|
|
195
|
+
const isSourceAuthError = msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED);
|
|
196
|
+
console.warn(`[CLAW] Session/HMAC error, re-resolving credentials and retrying: ${msg}`);
|
|
197
|
+
if (isSourceAuthError) {
|
|
198
|
+
_sendSighupToDaemon();
|
|
199
|
+
}
|
|
200
|
+
const retry = await resolveSessionCredentials(accountName, options);
|
|
201
|
+
if (isSourceAuthError) {
|
|
202
|
+
await new Promise(r => setTimeout(r, 500));
|
|
203
|
+
}
|
|
204
|
+
return await dispatch({ ...socketOptions, ...retry });
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
166
210
|
async function executeOperations(operations: any, options: Record<string, any> = {}) {
|
|
167
211
|
const ops = normalizeOperations(operations);
|
|
168
212
|
if (ops.length === 0) {
|
|
@@ -175,66 +219,20 @@ async function executeOperations(operations: any, options: Record<string, any> =
|
|
|
175
219
|
throw new Error('accountName is required');
|
|
176
220
|
}
|
|
177
221
|
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Clamp the broadcast request timeout up to the default outer window: the
|
|
182
|
-
// daemon's inner deadline (25s) must be allowed to fire and reply a typed
|
|
183
|
-
// BROADCAST_DEADLINE before this socket timer. A caller-supplied short
|
|
184
|
-
// timeout would time out mid-broadcast — a false uncertain.
|
|
185
|
-
const requestTimeoutMs = Number.isFinite(Number(options.daemonRequestTimeoutMs))
|
|
186
|
-
? Math.max(Number(options.daemonRequestTimeoutMs), DEFAULT_BROADCAST_TIMEOUT_MS)
|
|
187
|
-
: undefined;
|
|
188
|
-
|
|
189
|
-
await waitForCredentialDaemon(daemonTimeoutMs, options);
|
|
190
|
-
|
|
191
|
-
let { sessionId, botHmacSecret } = await resolveSessionCredentials(accountName, options);
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const result = await executeOperationsViaCredentialDaemon(accountName, ops, {
|
|
195
|
-
socketPath: options.socketPath,
|
|
196
|
-
timeoutMs: requestTimeoutMs,
|
|
197
|
-
sessionId,
|
|
198
|
-
botHmacSecret,
|
|
222
|
+
const result = await viaCredentialDaemon(accountName, options, async (daemonOptions) =>
|
|
223
|
+
executeOperationsViaCredentialDaemon(accountName, ops, {
|
|
224
|
+
...daemonOptions,
|
|
199
225
|
requestType: 'broadcast',
|
|
200
226
|
batchId: options.batchId || null
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
const msg = String(getErrorMessage(err) || err);
|
|
211
|
-
if (msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED) || msg.includes(DAEMON_ERRORS.SESSION_EXPIRED)) {
|
|
212
|
-
const isSourceAuthError = msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED);
|
|
213
|
-
console.warn(`[CLAW] Session/HMAC error, re-resolving credentials and retrying: ${msg}`);
|
|
214
|
-
if (isSourceAuthError) {
|
|
215
|
-
_sendSighupToDaemon();
|
|
216
|
-
}
|
|
217
|
-
const retry = await resolveSessionCredentials(accountName, options);
|
|
218
|
-
if (isSourceAuthError) {
|
|
219
|
-
await new Promise(r => setTimeout(r, 500));
|
|
220
|
-
}
|
|
221
|
-
const result = await executeOperationsViaCredentialDaemon(accountName, ops, {
|
|
222
|
-
socketPath: options.socketPath,
|
|
223
|
-
timeoutMs: requestTimeoutMs,
|
|
224
|
-
sessionId: retry.sessionId,
|
|
225
|
-
botHmacSecret: retry.botHmacSecret,
|
|
226
|
-
requestType: 'broadcast',
|
|
227
|
-
batchId: options.batchId || null
|
|
228
|
-
});
|
|
229
|
-
return {
|
|
230
|
-
...result,
|
|
231
|
-
operation_results: result.operation_results || [],
|
|
232
|
-
raw: result.raw || null,
|
|
233
|
-
success: true
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
throw err;
|
|
237
|
-
}
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
...result,
|
|
232
|
+
operation_results: result.operation_results || [],
|
|
233
|
+
raw: result.raw || null,
|
|
234
|
+
success: true
|
|
235
|
+
};
|
|
238
236
|
}
|
|
239
237
|
|
|
240
238
|
const client = await getSigningClient(options);
|
|
@@ -290,27 +288,10 @@ async function broadcastOperation(operation: any, options: Record<string, any> =
|
|
|
290
288
|
throw new Error('accountName is required');
|
|
291
289
|
}
|
|
292
290
|
|
|
293
|
-
|
|
294
|
-
? Number(options.daemonTimeoutMs)
|
|
295
|
-
: undefined;
|
|
296
|
-
// Clamp the broadcast request timeout up to the default outer window: the
|
|
297
|
-
// daemon's inner deadline (25s) must be allowed to fire and reply a typed
|
|
298
|
-
// BROADCAST_DEADLINE before this socket timer. A caller-supplied short
|
|
299
|
-
// timeout would time out mid-broadcast — a false uncertain.
|
|
300
|
-
const requestTimeoutMs = Number.isFinite(Number(options.daemonRequestTimeoutMs))
|
|
301
|
-
? Math.max(Number(options.daemonRequestTimeoutMs), DEFAULT_BROADCAST_TIMEOUT_MS)
|
|
302
|
-
: undefined;
|
|
303
|
-
|
|
304
|
-
await waitForCredentialDaemon(daemonTimeoutMs, options);
|
|
305
|
-
|
|
306
|
-
let { sessionId, botHmacSecret } = await resolveSessionCredentials(accountName, options);
|
|
307
|
-
|
|
308
|
-
async function doBroadcast(sid: string | null | undefined, secret: string | null) {
|
|
291
|
+
return viaCredentialDaemon(accountName, options, async (daemonOptions) => {
|
|
309
292
|
const result = await broadcastOperationViaCredentialDaemon(accountName, operation, {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
sessionId: sid,
|
|
313
|
-
botHmacSecret: secret
|
|
293
|
+
...daemonOptions,
|
|
294
|
+
batchId: options.batchId || null
|
|
314
295
|
});
|
|
315
296
|
const operationResults =
|
|
316
297
|
(result && Array.isArray(result.operation_results) && result.operation_results) ||
|
|
@@ -318,26 +299,7 @@ async function broadcastOperation(operation: any, options: Record<string, any> =
|
|
|
318
299
|
(Array.isArray(result) && result[0] && result[0].trx && Array.isArray(result[0].trx.operation_results) && result[0].trx.operation_results) ||
|
|
319
300
|
[];
|
|
320
301
|
return { success: true, raw: result, operation_results: operationResults };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
try {
|
|
324
|
-
return await doBroadcast(sessionId, botHmacSecret);
|
|
325
|
-
} catch (err: any) {
|
|
326
|
-
const msg = String(getErrorMessage(err) || err);
|
|
327
|
-
if (msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED) || msg.includes(DAEMON_ERRORS.SESSION_EXPIRED)) {
|
|
328
|
-
const isSourceAuthError = msg.includes(DAEMON_ERRORS.SOURCE_AUTH_DENIED);
|
|
329
|
-
console.warn(`[CLAW] Session/HMAC error, re-resolving credentials and retrying: ${msg}`);
|
|
330
|
-
if (isSourceAuthError) {
|
|
331
|
-
_sendSighupToDaemon();
|
|
332
|
-
}
|
|
333
|
-
const retry = await resolveSessionCredentials(accountName, options);
|
|
334
|
-
if (isSourceAuthError) {
|
|
335
|
-
await new Promise(r => setTimeout(r, 500));
|
|
336
|
-
}
|
|
337
|
-
return await doBroadcast(retry.sessionId, retry.botHmacSecret);
|
|
338
|
-
}
|
|
339
|
-
throw err;
|
|
340
|
-
}
|
|
302
|
+
});
|
|
341
303
|
}
|
|
342
304
|
|
|
343
305
|
const client = await getSigningClient(options);
|
|
@@ -348,5 +310,5 @@ async function broadcastOperation(operation: any, options: Record<string, any> =
|
|
|
348
310
|
}
|
|
349
311
|
}
|
|
350
312
|
|
|
351
|
-
export { broadcastOperation,
|
|
313
|
+
export { broadcastOperation, executeOperations, getSigningClient, resolveAccountName }
|
|
352
314
|
|
|
@@ -11,7 +11,8 @@ import { buildCloseShortPlan, buildOpenShortPlan, buildTakeProfitPlan, closeShor
|
|
|
11
11
|
import { launcherRun, launcherDrystart, launcherReset, launcherDisable, launcherPm2Start, launcherPm2Stop, launcherPm2Delete, launcherPm2Restart } from './claw_launcher.js';
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import type { ClawBridgeOptions
|
|
14
|
+
import type { ClawBridgeOptions } from './types.js';
|
|
15
|
+
import { validateMemuCommandArgs } from './memu_bridge.js';
|
|
15
16
|
|
|
16
17
|
function stripPrivateKey(options: ClawBridgeOptions = {}): ClawBridgeOptions {
|
|
17
18
|
const sanitized = { ...options };
|
|
@@ -21,24 +22,37 @@ function stripPrivateKey(options: ClawBridgeOptions = {}): ClawBridgeOptions {
|
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Split a "BASE/QUOTE" pair string into { baseSymbol, quoteSymbol }.
|
|
24
|
-
* Throws if the
|
|
25
|
+
* Throws if the value is not a two-segment BASE/QUOTE string — extra segments
|
|
26
|
+
* are rejected instead of being silently dropped.
|
|
25
27
|
*/
|
|
26
28
|
function splitPair(pairValue: string) {
|
|
27
29
|
if (typeof pairValue !== 'string' || !pairValue.includes('/')) {
|
|
28
30
|
throw new Error('pair must be provided as BASE/QUOTE');
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
33
|
+
const segments = pairValue.split('/');
|
|
34
|
+
if (segments.length !== 2 || !segments[0] || !segments[1]) {
|
|
33
35
|
throw new Error('pair must be provided as BASE/QUOTE');
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
return {
|
|
37
|
-
baseSymbol:
|
|
38
|
-
quoteSymbol:
|
|
39
|
+
baseSymbol: segments[0].trim(),
|
|
40
|
+
quoteSymbol: segments[1].trim()
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Resolve explicit asset symbols or fall back to the parsed pair option.
|
|
46
|
+
* Returns null when neither is available so callers can raise their own
|
|
47
|
+
* command-specific error.
|
|
48
|
+
*/
|
|
49
|
+
function resolvePairSymbols(safeOptions: ClawBridgeOptions) {
|
|
50
|
+
if (!safeOptions.pair) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return splitPair(safeOptions.pair);
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/**
|
|
43
57
|
* Extract a profile context reference string from command options.
|
|
44
58
|
* Returns the first matching field: botRef, identifier, botId, or pair.
|
|
@@ -47,6 +61,25 @@ function getProfileContextRef(options: ClawBridgeOptions = {}): string | null {
|
|
|
47
61
|
return options.botRef || options.identifier || options.botId || options.pair || null;
|
|
48
62
|
}
|
|
49
63
|
|
|
64
|
+
const MEMU_COMMAND_MAP: Record<string, string> = {
|
|
65
|
+
'memu-memorize': 'memorize',
|
|
66
|
+
'memu-retrieve': 'retrieve',
|
|
67
|
+
'memu-create-item': 'create-item',
|
|
68
|
+
'memu-update-item': 'update-item',
|
|
69
|
+
'memu-delete-item': 'delete-item',
|
|
70
|
+
'memu-memorize-conversation': 'memorize-conversation',
|
|
71
|
+
'memu-memorize-trading-context': 'memorize-trading-context',
|
|
72
|
+
'memu-retrieve-trading-context': 'retrieve-trading-context'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Credit runtime commands address bots by botRef or identifier only (never
|
|
77
|
+
* botId/pair, unlike profile-context).
|
|
78
|
+
*/
|
|
79
|
+
function getCreditRuntimeBotRef(options: ClawBridgeOptions = {}): string | null {
|
|
80
|
+
return options.botRef || options.identifier || null;
|
|
81
|
+
}
|
|
82
|
+
|
|
50
83
|
/**
|
|
51
84
|
* Extract and validate the patch object from command options.
|
|
52
85
|
* Throws if patch is missing or not a plain object.
|
|
@@ -121,12 +154,12 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
121
154
|
|
|
122
155
|
case 'profile-context':
|
|
123
156
|
return bridge.profiles.getClawProfileContext(
|
|
124
|
-
safeOptions
|
|
157
|
+
getProfileContextRef(safeOptions),
|
|
125
158
|
safeOptions
|
|
126
159
|
);
|
|
127
160
|
|
|
128
161
|
case 'market-snapshot': {
|
|
129
|
-
const pair =
|
|
162
|
+
const pair = resolvePairSymbols(safeOptions);
|
|
130
163
|
const baseSymbol = safeOptions.baseSymbol || pair?.baseSymbol;
|
|
131
164
|
const quoteSymbol = safeOptions.quoteSymbol || pair?.quoteSymbol;
|
|
132
165
|
|
|
@@ -173,7 +206,7 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
173
206
|
});
|
|
174
207
|
|
|
175
208
|
case 'honest-pair': {
|
|
176
|
-
const pair =
|
|
209
|
+
const pair = resolvePairSymbols(safeOptions);
|
|
177
210
|
const assetA = safeOptions.assetA || pair?.baseSymbol;
|
|
178
211
|
const assetB = safeOptions.assetB || pair?.quoteSymbol;
|
|
179
212
|
|
|
@@ -185,7 +218,7 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
185
218
|
}
|
|
186
219
|
|
|
187
220
|
case 'honest-price': {
|
|
188
|
-
const pair =
|
|
221
|
+
const pair = resolvePairSymbols(safeOptions);
|
|
189
222
|
const assetA = safeOptions.assetA || pair?.baseSymbol;
|
|
190
223
|
const assetB = safeOptions.assetB || pair?.quoteSymbol;
|
|
191
224
|
|
|
@@ -290,18 +323,14 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
290
323
|
return getMpaPosition(safeOptions.accountName || safeOptions.accountRef || accountName, safeOptions.mpaAsset);
|
|
291
324
|
|
|
292
325
|
case 'credit-runtime-status':
|
|
293
|
-
return bridge.creditRuntime.getStatus(
|
|
294
|
-
safeOptions.botRef || safeOptions.identifier || null
|
|
295
|
-
);
|
|
326
|
+
return bridge.creditRuntime.getStatus(getCreditRuntimeBotRef(safeOptions));
|
|
296
327
|
|
|
297
328
|
case 'credit-runtime-refresh':
|
|
298
|
-
return bridge.creditRuntime.refresh(
|
|
299
|
-
safeOptions.botRef || safeOptions.identifier || null
|
|
300
|
-
);
|
|
329
|
+
return bridge.creditRuntime.refresh(getCreditRuntimeBotRef(safeOptions));
|
|
301
330
|
|
|
302
331
|
case 'credit-runtime-maintenance':
|
|
303
332
|
return bridge.creditRuntime.runMaintenance(
|
|
304
|
-
safeOptions
|
|
333
|
+
getCreditRuntimeBotRef(safeOptions),
|
|
305
334
|
safeOptions.context || 'periodic',
|
|
306
335
|
safeOptions,
|
|
307
336
|
options.privateKey
|
|
@@ -309,13 +338,13 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
309
338
|
|
|
310
339
|
case 'credit-runtime-watchdog':
|
|
311
340
|
return bridge.creditRuntime.runWatchdog(
|
|
312
|
-
safeOptions
|
|
341
|
+
getCreditRuntimeBotRef(safeOptions),
|
|
313
342
|
options.privateKey
|
|
314
343
|
);
|
|
315
344
|
|
|
316
345
|
case 'credit-runtime-reborrows':
|
|
317
346
|
return bridge.creditRuntime.processReborrows(
|
|
318
|
-
safeOptions
|
|
347
|
+
getCreditRuntimeBotRef(safeOptions),
|
|
319
348
|
options.privateKey
|
|
320
349
|
);
|
|
321
350
|
|
|
@@ -346,72 +375,23 @@ async function runClawCommand(command: string, options: ClawBridgeOptions = {}):
|
|
|
346
375
|
case 'memu-manifest':
|
|
347
376
|
return require('./memu_bridge').describeMemuBridge(safeOptions);
|
|
348
377
|
|
|
349
|
-
case 'memu-memorize':
|
|
350
|
-
|
|
351
|
-
throw new Error('memu-memorize requires resourceUrl and modality');
|
|
352
|
-
}
|
|
353
|
-
return runMemuCommand('memorize', safeOptions);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
case 'memu-retrieve': {
|
|
357
|
-
if (!safeOptions.queries) {
|
|
358
|
-
throw new Error('memu-retrieve requires queries');
|
|
359
|
-
}
|
|
360
|
-
return runMemuCommand('retrieve', safeOptions);
|
|
361
|
-
}
|
|
362
|
-
|
|
378
|
+
case 'memu-memorize':
|
|
379
|
+
case 'memu-retrieve':
|
|
363
380
|
case 'memu-list-categories':
|
|
364
|
-
return runMemuCommand('list-categories', safeOptions);
|
|
365
|
-
|
|
366
381
|
case 'memu-list-items':
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
case 'memu-
|
|
370
|
-
if (!(safeOptions.categoryId || safeOptions.categoryName || safeOptions.category) || !safeOptions.summary) {
|
|
371
|
-
throw new Error('memu-create-item requires categoryId or categoryName, plus summary');
|
|
372
|
-
}
|
|
373
|
-
return runMemuCommand('create-item', safeOptions);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
case 'memu-update-item': {
|
|
377
|
-
if (!safeOptions.itemId || !safeOptions.updates) {
|
|
378
|
-
throw new Error('memu-update-item requires itemId and updates');
|
|
379
|
-
}
|
|
380
|
-
return runMemuCommand('update-item', safeOptions);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
case 'memu-delete-item': {
|
|
384
|
-
if (!safeOptions.itemId) {
|
|
385
|
-
throw new Error('memu-delete-item requires itemId');
|
|
386
|
-
}
|
|
387
|
-
return runMemuCommand('delete-item', safeOptions);
|
|
388
|
-
}
|
|
389
|
-
|
|
382
|
+
case 'memu-create-item':
|
|
383
|
+
case 'memu-update-item':
|
|
384
|
+
case 'memu-delete-item':
|
|
390
385
|
case 'memu-clear':
|
|
391
|
-
return runMemuCommand('clear', safeOptions);
|
|
392
|
-
|
|
393
386
|
case 'memu-status':
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
case 'memu-memorize-conversation': {
|
|
397
|
-
if (!safeOptions.messages) {
|
|
398
|
-
throw new Error('memu-memorize-conversation requires messages');
|
|
399
|
-
}
|
|
400
|
-
return runMemuCommand('memorize-conversation', safeOptions);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
case 'memu-memorize-trading-context': {
|
|
404
|
-
if (!safeOptions.context) {
|
|
405
|
-
throw new Error('memu-memorize-trading-context requires context');
|
|
406
|
-
}
|
|
407
|
-
return runMemuCommand('memorize-trading-context', safeOptions);
|
|
408
|
-
}
|
|
409
|
-
|
|
387
|
+
case 'memu-memorize-conversation':
|
|
388
|
+
case 'memu-memorize-trading-context':
|
|
410
389
|
case 'memu-retrieve-trading-context': {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
390
|
+
const memuCommand = MEMU_COMMAND_MAP[command] || command.replace(/^memu-/, '');
|
|
391
|
+
// Shared validation with runMemuCommand/memu_mcp_server so every surface
|
|
392
|
+
// rejects missing args with the same message.
|
|
393
|
+
validateMemuCommandArgs(memuCommand, safeOptions);
|
|
394
|
+
return runMemuCommand(memuCommand, safeOptions);
|
|
415
395
|
}
|
|
416
396
|
|
|
417
397
|
default:
|