@hyperflow.fun/ghost 0.0.3 → 0.0.5
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/README.md +4 -20
- package/dist/index.js +1328 -716
- package/dist/package.json +1 -1
- package/dist/skills/builtin/ask-user-questions/SKILL.md +48 -0
- package/dist/skills/builtin/event-judge/SKILL.md +52 -7
- package/dist/skills/builtin/trade-executor/SKILL.md +1 -1
- package/dist/web/dist/assets/{Chart-BOyhyITw.js → Chart-cnNd398J.js} +1 -1
- package/dist/web/dist/assets/{Config-CQyc8yqV.js → Config-Bui5kcW0.js} +1 -1
- package/dist/web/dist/assets/{Cost-BoAvnlDi.js → Cost-BDSIn5ub.js} +1 -1
- package/dist/web/dist/assets/{Cron-5mIZVsft.js → Cron-C7gaZhMu.js} +1 -1
- package/dist/web/dist/assets/{Dashboard-BRxSDtCD.js → Dashboard-qwYsK3Gi.js} +1 -1
- package/dist/web/dist/assets/{Logs-BpXqElSx.js → Logs-DyfS6kEe.js} +1 -1
- package/dist/web/dist/assets/{Memory-CcgSgCqJ.js → Memory-BPMmbFbx.js} +1 -1
- package/dist/web/dist/assets/SVN-RethinkSans-Bold-BIisIh3U.otf +0 -0
- package/dist/web/dist/assets/SVN-RethinkSans-Italic-8Q-lLPN5.otf +0 -0
- package/dist/web/dist/assets/SVN-RethinkSans-Medium-yLh4RBRv.otf +0 -0
- package/dist/web/dist/assets/SVN-RethinkSans-Regular-CVuIF6ni.otf +0 -0
- package/dist/web/dist/assets/{Sessions-BEIP55fW.js → Sessions-nANl57gZ.js} +1 -1
- package/dist/web/dist/assets/{Skills-4s4LDpzh.js → Skills-BPXH_m1K.js} +1 -1
- package/dist/web/dist/assets/{Tools-C-NcyONi.js → Tools-CypplonI.js} +1 -1
- package/dist/web/dist/assets/{activity-DJy9m_N9.js → activity-BLl_txfw.js} +1 -1
- package/dist/web/dist/assets/{clock-CFVWqojO.js → clock-CMLb2b5z.js} +1 -1
- package/dist/web/dist/assets/empty-notifications-C88Hm9lC.svg +62 -0
- package/dist/web/dist/assets/empty-token-search-aBFBwedz.svg +5 -0
- package/dist/web/dist/assets/{highlighted-body-OFNGDK62-y5VVuvTf.js → highlighted-body-OFNGDK62-CKxBaGN5.js} +1 -1
- package/dist/web/dist/assets/index-CKAK_IGv.js +59 -0
- package/dist/web/dist/assets/index-PX2emSv9.css +1 -0
- package/dist/web/dist/assets/{mermaid-GHXKKRXX-BvdvTI5r.js → mermaid-GHXKKRXX-BuHBmaw6.js} +3 -3
- package/dist/web/dist/assets/{refresh-cw-DlbQ5OE4.js → refresh-cw-DHCgoaOK.js} +1 -1
- package/dist/web/dist/assets/welcome-globe-analytics-sF4OWku9.svg +201 -0
- package/dist/web/dist/assets/welcome-globe-bars-COA7MJUP.svg +305 -0
- package/dist/web/dist/assets/welcome-news-card-BfM47ZZ3.svg +59 -0
- package/dist/web/dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/dist/assets/index-B1ED3P_d.css +0 -1
- package/dist/web/dist/assets/index-C6QRLmO2.js +0 -54
- package/dist/web/dist/assets/rethink-sans-latin-ext-CuoiHPIp.woff2 +0 -0
- package/dist/web/dist/assets/rethink-sans-latin-v3CgWhBT.woff2 +0 -0
- package/dist/web/dist/assets/welcome-illustration-B8_SJ7In.svg +0 -319
package/dist/index.js
CHANGED
|
@@ -4094,7 +4094,6 @@ var init_schema = __esm(() => {
|
|
|
4094
4094
|
});
|
|
4095
4095
|
cronSchema = exports_external.object({
|
|
4096
4096
|
enableScheduler: exports_external.boolean().default(true),
|
|
4097
|
-
timezone: exports_external.string().default("UTC"),
|
|
4098
4097
|
maxConcurrentJobs: exports_external.coerce.number().int().positive().default(5)
|
|
4099
4098
|
});
|
|
4100
4099
|
telegramChannelSchema = exports_external.object({
|
|
@@ -4360,7 +4359,6 @@ __export(exports_paths, {
|
|
|
4360
4359
|
getEvalConfigPath: () => getEvalConfigPath,
|
|
4361
4360
|
getDbPath: () => getDbPath,
|
|
4362
4361
|
getDaemonPidPath: () => getDaemonPidPath,
|
|
4363
|
-
getCronStorePath: () => getCronStorePath,
|
|
4364
4362
|
getCredentialsPath: () => getCredentialsPath,
|
|
4365
4363
|
getConfigPath: () => getConfigPath,
|
|
4366
4364
|
getCliWorkspacePath: () => getCliWorkspacePath,
|
|
@@ -4405,9 +4403,6 @@ function getModelsConfigPath() {
|
|
|
4405
4403
|
function getEvalConfigPath() {
|
|
4406
4404
|
return join(getGhostDir(), "eval.json");
|
|
4407
4405
|
}
|
|
4408
|
-
function getCronStorePath() {
|
|
4409
|
-
return join(getWorkspaceDir(), "cron", "jobs.json");
|
|
4410
|
-
}
|
|
4411
4406
|
function getCliWorkspacePath() {
|
|
4412
4407
|
return join(getGhostDir(), "cli-workspace");
|
|
4413
4408
|
}
|
|
@@ -4513,7 +4508,6 @@ __export(exports_config, {
|
|
|
4513
4508
|
getEvalConfigPath: () => getEvalConfigPath,
|
|
4514
4509
|
getDbPath: () => getDbPath,
|
|
4515
4510
|
getDaemonPidPath: () => getDaemonPidPath,
|
|
4516
|
-
getCronStorePath: () => getCronStorePath,
|
|
4517
4511
|
getCredentialsPath: () => getCredentialsPath,
|
|
4518
4512
|
getConfigPath: () => getConfigPath,
|
|
4519
4513
|
getCliWorkspacePath: () => getCliWorkspacePath,
|
|
@@ -151269,6 +151263,34 @@ function formatOrder(order) {
|
|
|
151269
151263
|
return `${order.symbol} ${formatSide(order.side)} ${order.type} ${order.size}${priceStr}${triggerStr}`;
|
|
151270
151264
|
}
|
|
151271
151265
|
|
|
151266
|
+
// src/services/wizard-data.ts
|
|
151267
|
+
function composeOpenPositionWizard(input) {
|
|
151268
|
+
const symbol5 = input.symbol?.toUpperCase();
|
|
151269
|
+
if (!symbol5)
|
|
151270
|
+
return;
|
|
151271
|
+
if (typeof input.size !== "number" || !Number.isFinite(input.size))
|
|
151272
|
+
return;
|
|
151273
|
+
const side = input.side === "buy" || input.side === "long" ? "long" : "short";
|
|
151274
|
+
const leverage = input.leverage && input.leverage > 0 ? input.leverage : 1;
|
|
151275
|
+
const orderType = input.orderType === "limit" ? "limit" : "market";
|
|
151276
|
+
const hasEntry = typeof input.entryPrice === "number" && Number.isFinite(input.entryPrice) && input.entryPrice > 0;
|
|
151277
|
+
const entryPrice = hasEntry ? input.entryPrice : undefined;
|
|
151278
|
+
return {
|
|
151279
|
+
kind: "open_position",
|
|
151280
|
+
symbol: symbol5,
|
|
151281
|
+
side,
|
|
151282
|
+
leverage,
|
|
151283
|
+
size: input.size,
|
|
151284
|
+
orderType,
|
|
151285
|
+
entryPrice,
|
|
151286
|
+
stopLoss: input.stopLoss,
|
|
151287
|
+
takeProfit: input.takeProfit
|
|
151288
|
+
};
|
|
151289
|
+
}
|
|
151290
|
+
function composeGenericWizard(groups) {
|
|
151291
|
+
return { kind: "generic", groups };
|
|
151292
|
+
}
|
|
151293
|
+
|
|
151272
151294
|
// src/services/confirm-policy.ts
|
|
151273
151295
|
function isConfirmable(toolName) {
|
|
151274
151296
|
return CONFIRMABLE_TOOLS.has(toolName);
|
|
@@ -151313,15 +151335,25 @@ function describePlaceOrder(params) {
|
|
|
151313
151335
|
const sizeStr = size !== undefined ? `${size}` : "";
|
|
151314
151336
|
const levSuffix = leverage && leverage > 0 ? ` ${leverage}x` : "";
|
|
151315
151337
|
const sideRow = leverage && leverage > 0 ? `Side: ${side} ${leverage}x` : `Side: ${side}`;
|
|
151338
|
+
const wizard = composeOpenPositionWizard({
|
|
151339
|
+
symbol: getString(params, "symbol") ?? "",
|
|
151340
|
+
side: getString(params, "side") ?? "buy",
|
|
151341
|
+
size: getNumber(params, "size") ?? 0,
|
|
151342
|
+
leverage: getNumber(params, "leverage"),
|
|
151343
|
+
orderType: getString(params, "orderType"),
|
|
151344
|
+
entryPrice: price
|
|
151345
|
+
});
|
|
151316
151346
|
if (orderType === "limit" && price !== undefined) {
|
|
151317
151347
|
return {
|
|
151318
151348
|
title: `Place limit order: ${side} ${sizeStr} ${symbol5} @ ${formatUsd(price)}?`,
|
|
151319
|
-
bullets: [sideRow]
|
|
151349
|
+
bullets: [sideRow],
|
|
151350
|
+
wizard
|
|
151320
151351
|
};
|
|
151321
151352
|
}
|
|
151322
151353
|
return {
|
|
151323
151354
|
title: `Place market order: ${side} ${sizeStr} ${symbol5}?`,
|
|
151324
|
-
bullets: [sideRow]
|
|
151355
|
+
bullets: [sideRow],
|
|
151356
|
+
wizard
|
|
151325
151357
|
};
|
|
151326
151358
|
}
|
|
151327
151359
|
function describeBracketOrder(params) {
|
|
@@ -151329,8 +151361,9 @@ function describeBracketOrder(params) {
|
|
|
151329
151361
|
const side = sideLabel(getString(params, "side"));
|
|
151330
151362
|
const leverage = getNumber(params, "leverage");
|
|
151331
151363
|
const size = getNumber(params, "size");
|
|
151332
|
-
const
|
|
151364
|
+
const rawOrderType = getString(params, "orderType")?.toLowerCase();
|
|
151333
151365
|
const entryPrice = getNumber(params, "entryPrice") ?? getNumber(params, "price");
|
|
151366
|
+
const orderType = rawOrderType ?? (entryPrice !== undefined ? "limit" : "market");
|
|
151334
151367
|
const stopLoss = getNumber(params, "stopLoss");
|
|
151335
151368
|
const takeProfit = getNumber(params, "takeProfit");
|
|
151336
151369
|
const sizeStr = size !== undefined ? `${size}` : "";
|
|
@@ -151343,7 +151376,17 @@ function describeBracketOrder(params) {
|
|
|
151343
151376
|
bullets.push(`SL: ${formatUsdCompact(stopLoss)}`);
|
|
151344
151377
|
if (takeProfit !== undefined)
|
|
151345
151378
|
bullets.push(`TP: ${formatUsdCompact(takeProfit)}`);
|
|
151346
|
-
|
|
151379
|
+
const wizard = composeOpenPositionWizard({
|
|
151380
|
+
symbol: getString(params, "symbol") ?? "",
|
|
151381
|
+
side: getString(params, "side") ?? "buy",
|
|
151382
|
+
size: getNumber(params, "size") ?? 0,
|
|
151383
|
+
leverage: getNumber(params, "leverage"),
|
|
151384
|
+
orderType,
|
|
151385
|
+
entryPrice,
|
|
151386
|
+
stopLoss: getNumber(params, "stopLoss"),
|
|
151387
|
+
takeProfit: getNumber(params, "takeProfit")
|
|
151388
|
+
});
|
|
151389
|
+
return { title, bullets, wizard };
|
|
151347
151390
|
}
|
|
151348
151391
|
function describeSetSlTp(params) {
|
|
151349
151392
|
const symbol5 = upper(getString(params, "symbol"));
|
|
@@ -151414,14 +151457,26 @@ function describePartialClose(params) {
|
|
|
151414
151457
|
const symbol5 = upper(getString(params, "symbol"));
|
|
151415
151458
|
const pct = getNumber(params, "percentage");
|
|
151416
151459
|
const size = getNumber(params, "size");
|
|
151460
|
+
const sizeBefore = getNumber(params, "sizeBefore");
|
|
151461
|
+
const rows = [];
|
|
151462
|
+
if (pct !== undefined)
|
|
151463
|
+
rows.push({ label: "Close %", value: `${pct}%` });
|
|
151464
|
+
if (size !== undefined)
|
|
151465
|
+
rows.push({ label: "Size", value: `${size} ${symbol5}` });
|
|
151466
|
+
if (sizeBefore !== undefined && pct !== undefined) {
|
|
151467
|
+
const sizeAfter = sizeBefore * (1 - pct / 100);
|
|
151468
|
+
rows.push({ label: "Size before", value: `${sizeBefore}`, tone: "muted" });
|
|
151469
|
+
rows.push({ label: "Size after", value: `${sizeAfter}` });
|
|
151470
|
+
}
|
|
151471
|
+
const wizard = rows.length > 0 ? composeGenericWizard([{ label: `${symbol5} partial close`, rows }]) : undefined;
|
|
151417
151472
|
if (pct !== undefined) {
|
|
151418
151473
|
const pctStr = Number.isInteger(pct) ? `${pct}` : pct.toFixed(0);
|
|
151419
|
-
return { title: `Close ${pctStr}% of ${symbol5} position?`, bullets: [] };
|
|
151474
|
+
return { title: `Close ${pctStr}% of ${symbol5} position?`, bullets: [], wizard };
|
|
151420
151475
|
}
|
|
151421
151476
|
if (size !== undefined) {
|
|
151422
|
-
return { title: `Close ${size} ${symbol5} position?`, bullets: [] };
|
|
151477
|
+
return { title: `Close ${size} ${symbol5} position?`, bullets: [], wizard };
|
|
151423
151478
|
}
|
|
151424
|
-
return { title: `Close part of ${symbol5} position?`, bullets: [] };
|
|
151479
|
+
return { title: `Close part of ${symbol5} position?`, bullets: [], wizard };
|
|
151425
151480
|
}
|
|
151426
151481
|
function describeAdjustMargin(params) {
|
|
151427
151482
|
const symbol5 = upper(getString(params, "symbol"));
|
|
@@ -151429,17 +151484,26 @@ function describeAdjustMargin(params) {
|
|
|
151429
151484
|
if (amount === undefined) {
|
|
151430
151485
|
return { title: `Adjust margin on ${symbol5}?`, bullets: [] };
|
|
151431
151486
|
}
|
|
151487
|
+
const sign = amount >= 0 ? "+" : "\u2212";
|
|
151488
|
+
const rows = [
|
|
151489
|
+
{
|
|
151490
|
+
label: "Margin change",
|
|
151491
|
+
value: `${sign}${formatUsd(Math.abs(amount))}`,
|
|
151492
|
+
tone: amount >= 0 ? "reward" : "risk"
|
|
151493
|
+
}
|
|
151494
|
+
];
|
|
151495
|
+
const wizard = composeGenericWizard([{ label: `${symbol5} margin adjust`, rows }]);
|
|
151432
151496
|
if (amount >= 0) {
|
|
151433
|
-
return { title: `Add ${formatUsd(amount)} margin to ${symbol5}?`, bullets: [] };
|
|
151497
|
+
return { title: `Add ${formatUsd(amount)} margin to ${symbol5}?`, bullets: [], wizard };
|
|
151434
151498
|
}
|
|
151435
|
-
return { title: `Reduce ${formatUsd(Math.abs(amount))} margin on ${symbol5}?`, bullets: [] };
|
|
151499
|
+
return { title: `Reduce ${formatUsd(Math.abs(amount))} margin on ${symbol5}?`, bullets: [], wizard };
|
|
151436
151500
|
}
|
|
151437
151501
|
function describeConfirm(toolName, params) {
|
|
151438
151502
|
const safeParams = params && typeof params === "object" ? params : {};
|
|
151439
151503
|
const describer = CONFIRM_DESCRIBERS[toolName];
|
|
151440
151504
|
if (describer)
|
|
151441
151505
|
return describer(safeParams);
|
|
151442
|
-
return { title: `
|
|
151506
|
+
return { title: `Run ${toolName}?`, bullets: [] };
|
|
151443
151507
|
}
|
|
151444
151508
|
var CONFIRMABLE_TOOLS, CONFIRM_DESCRIBERS;
|
|
151445
151509
|
var init_confirm_policy = __esm(() => {
|
|
@@ -151487,7 +151551,7 @@ function createGhostSdkMcpServer(deps) {
|
|
|
151487
151551
|
}
|
|
151488
151552
|
if (decision.decision === "rejected") {
|
|
151489
151553
|
const reasonMsg = decision.reason && decision.reason.length > 0 ? `User declined. Reason: ${decision.reason}` : "User declined. Do not retry.";
|
|
151490
|
-
logger.debug({ tool: agentTool.name, reason: decision.reason }, "tool
|
|
151554
|
+
logger.debug({ tool: agentTool.name, reason: decision.reason }, "tool not approved");
|
|
151491
151555
|
return { content: [{ type: "text", text: reasonMsg }], isError: true };
|
|
151492
151556
|
}
|
|
151493
151557
|
}
|
|
@@ -151532,7 +151596,36 @@ var init_constants2 = __esm(() => {
|
|
|
151532
151596
|
"read_file",
|
|
151533
151597
|
"list_dir",
|
|
151534
151598
|
"web_fetch",
|
|
151535
|
-
"web_search"
|
|
151599
|
+
"web_search",
|
|
151600
|
+
"ghost_chat_history",
|
|
151601
|
+
"ghost_get_trade_history",
|
|
151602
|
+
"ghost_get_balance",
|
|
151603
|
+
"ghost_get_positions",
|
|
151604
|
+
"ghost_get_orders",
|
|
151605
|
+
"ghost_list_wallets",
|
|
151606
|
+
"ghost_get_recent_orders",
|
|
151607
|
+
"ghost_get_price",
|
|
151608
|
+
"ghost_get_funding_rates",
|
|
151609
|
+
"ghost_get_orderbook",
|
|
151610
|
+
"ghost_get_klines",
|
|
151611
|
+
"ghost_get_indicators",
|
|
151612
|
+
"ghost_get_levels",
|
|
151613
|
+
"ghost_market_overview",
|
|
151614
|
+
"ghost_pre_trade_check",
|
|
151615
|
+
"ghost_morning_briefing",
|
|
151616
|
+
"ghost_session_info",
|
|
151617
|
+
"ghost_timing_risk",
|
|
151618
|
+
"ghost_cross_exchange_funding",
|
|
151619
|
+
"ghost_liquidation_map",
|
|
151620
|
+
"ghost_get_whale_activity",
|
|
151621
|
+
"ghost_tweets_search",
|
|
151622
|
+
"ghost_news_sources",
|
|
151623
|
+
"ghost_news_search",
|
|
151624
|
+
"ghost_news_discover_rss",
|
|
151625
|
+
"ghost_watchlist_list",
|
|
151626
|
+
"ghost_alert_list",
|
|
151627
|
+
"ghost_alert_history",
|
|
151628
|
+
"ghost_check_alerts"
|
|
151536
151629
|
]);
|
|
151537
151630
|
});
|
|
151538
151631
|
|
|
@@ -156179,14 +156272,6 @@ function createClaudeCliModel(modelId) {
|
|
|
156179
156272
|
maxTokens: known?.maxTokens ?? DEFAULT_SPECS.maxTokens
|
|
156180
156273
|
};
|
|
156181
156274
|
}
|
|
156182
|
-
function getClaudeCliModels() {
|
|
156183
|
-
return [
|
|
156184
|
-
{ id: "claude-opus-4-7", name: "Claude Opus 4.7" },
|
|
156185
|
-
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
|
|
156186
|
-
{ id: "claude-haiku-4-5", name: "Claude Haiku 4.5" },
|
|
156187
|
-
{ id: "claude-opus-4-6", name: "Claude Opus 4.6" }
|
|
156188
|
-
];
|
|
156189
|
-
}
|
|
156190
156275
|
var KNOWN_MODELS, DEFAULT_SPECS;
|
|
156191
156276
|
var init_models6 = __esm(() => {
|
|
156192
156277
|
KNOWN_MODELS = {
|
|
@@ -156485,7 +156570,7 @@ function assertValidVersions(sorted) {
|
|
|
156485
156570
|
}
|
|
156486
156571
|
}
|
|
156487
156572
|
}
|
|
156488
|
-
var baselineDbMigration, proactiveCooldownsMigration, dropProactiveCooldownsMigration, alertsKindPayloadMigration, addSettingsKvMigration, addTweetsAiRelevantMigration, addObserverStateMigration, splitAlertsMigration, addXFollowsEnabledSourceMigration, DB_MIGRATIONS, CONFIG_MIGRATIONS;
|
|
156573
|
+
var baselineDbMigration, proactiveCooldownsMigration, dropProactiveCooldownsMigration, alertsKindPayloadMigration, addSettingsKvMigration, addTweetsAiRelevantMigration, addObserverStateMigration, splitAlertsMigration, addXFollowsEnabledSourceMigration, addCronJobsMigration, DB_MIGRATIONS, CONFIG_MIGRATIONS;
|
|
156489
156574
|
var init_registry2 = __esm(() => {
|
|
156490
156575
|
baselineDbMigration = {
|
|
156491
156576
|
version: 1,
|
|
@@ -156618,6 +156703,42 @@ var init_registry2 = __esm(() => {
|
|
|
156618
156703
|
db2.run(`UPDATE x_follows SET user_disabled = 0 WHERE user_disabled IS NULL`);
|
|
156619
156704
|
}
|
|
156620
156705
|
};
|
|
156706
|
+
addCronJobsMigration = {
|
|
156707
|
+
version: 10,
|
|
156708
|
+
label: "add_cron_jobs",
|
|
156709
|
+
up: (db2) => {
|
|
156710
|
+
db2.run(`
|
|
156711
|
+
CREATE TABLE IF NOT EXISTS cron_jobs (
|
|
156712
|
+
id TEXT PRIMARY KEY,
|
|
156713
|
+
name TEXT NOT NULL,
|
|
156714
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
156715
|
+
schedule_kind TEXT NOT NULL,
|
|
156716
|
+
schedule_at_ms INTEGER,
|
|
156717
|
+
schedule_every_ms INTEGER,
|
|
156718
|
+
schedule_expr TEXT,
|
|
156719
|
+
schedule_tz TEXT,
|
|
156720
|
+
payload_kind TEXT NOT NULL DEFAULT 'agent_turn',
|
|
156721
|
+
payload_message TEXT NOT NULL,
|
|
156722
|
+
payload_deliver INTEGER NOT NULL DEFAULT 1,
|
|
156723
|
+
payload_channel TEXT,
|
|
156724
|
+
payload_to TEXT,
|
|
156725
|
+
next_run_at_ms INTEGER,
|
|
156726
|
+
last_run_at_ms INTEGER,
|
|
156727
|
+
last_status TEXT,
|
|
156728
|
+
last_error TEXT,
|
|
156729
|
+
run_history TEXT NOT NULL DEFAULT '[]',
|
|
156730
|
+
created_at_ms INTEGER NOT NULL,
|
|
156731
|
+
updated_at_ms INTEGER NOT NULL,
|
|
156732
|
+
delete_after_run INTEGER NOT NULL DEFAULT 0
|
|
156733
|
+
);
|
|
156734
|
+
`);
|
|
156735
|
+
db2.run(`CREATE UNIQUE INDEX IF NOT EXISTS idx_cron_jobs_name ON cron_jobs(name);`);
|
|
156736
|
+
db2.run(`
|
|
156737
|
+
CREATE INDEX IF NOT EXISTS idx_cron_jobs_enabled_next
|
|
156738
|
+
ON cron_jobs(enabled, next_run_at_ms) WHERE enabled = 1;
|
|
156739
|
+
`);
|
|
156740
|
+
}
|
|
156741
|
+
};
|
|
156621
156742
|
DB_MIGRATIONS = [
|
|
156622
156743
|
baselineDbMigration,
|
|
156623
156744
|
proactiveCooldownsMigration,
|
|
@@ -156627,7 +156748,8 @@ var init_registry2 = __esm(() => {
|
|
|
156627
156748
|
addTweetsAiRelevantMigration,
|
|
156628
156749
|
addObserverStateMigration,
|
|
156629
156750
|
splitAlertsMigration,
|
|
156630
|
-
addXFollowsEnabledSourceMigration
|
|
156751
|
+
addXFollowsEnabledSourceMigration,
|
|
156752
|
+
addCronJobsMigration
|
|
156631
156753
|
];
|
|
156632
156754
|
CONFIG_MIGRATIONS = [];
|
|
156633
156755
|
});
|
|
@@ -157957,6 +158079,9 @@ class ToolRegistry {
|
|
|
157957
158079
|
all() {
|
|
157958
158080
|
return [...this.tools.values()];
|
|
157959
158081
|
}
|
|
158082
|
+
taskAgentTools() {
|
|
158083
|
+
return [...this.tools.values()].filter((t2) => READ_TOOLS.has(t2.name) || TASK_AGENT_EXTRAS.has(t2.name));
|
|
158084
|
+
}
|
|
157960
158085
|
names() {
|
|
157961
158086
|
return [...this.tools.keys()];
|
|
157962
158087
|
}
|
|
@@ -158003,7 +158128,11 @@ class ToolRegistry {
|
|
|
158003
158128
|
return { ...result, content: truncated };
|
|
158004
158129
|
}
|
|
158005
158130
|
}
|
|
158006
|
-
var MAX_RESULT_CHARS = 16000;
|
|
158131
|
+
var MAX_RESULT_CHARS = 16000, TASK_AGENT_EXTRAS;
|
|
158132
|
+
var init_registry3 = __esm(() => {
|
|
158133
|
+
init_constants2();
|
|
158134
|
+
TASK_AGENT_EXTRAS = new Set(["save_memory", "cron"]);
|
|
158135
|
+
});
|
|
158007
158136
|
|
|
158008
158137
|
// src/tools/read-file.ts
|
|
158009
158138
|
import { readFileSync as readFileSync11, statSync as statSync2 } from "fs";
|
|
@@ -158630,7 +158759,7 @@ var init_web_fetch = __esm(() => {
|
|
|
158630
158759
|
// src/tools/cron.ts
|
|
158631
158760
|
class CronTool {
|
|
158632
158761
|
service;
|
|
158633
|
-
|
|
158762
|
+
tzService;
|
|
158634
158763
|
name = "cron";
|
|
158635
158764
|
label = "Cron";
|
|
158636
158765
|
description = "Schedule reminders and recurring tasks. Actions: add, list, remove.";
|
|
@@ -158638,9 +158767,9 @@ class CronTool {
|
|
|
158638
158767
|
_channel = null;
|
|
158639
158768
|
_chatId = null;
|
|
158640
158769
|
_inCron = false;
|
|
158641
|
-
constructor(service,
|
|
158770
|
+
constructor(service, tzService) {
|
|
158642
158771
|
this.service = service;
|
|
158643
|
-
this.
|
|
158772
|
+
this.tzService = tzService;
|
|
158644
158773
|
}
|
|
158645
158774
|
setOrigin(channel, chatId) {
|
|
158646
158775
|
this._channel = channel || null;
|
|
@@ -158674,7 +158803,7 @@ class CronTool {
|
|
|
158674
158803
|
if (params.every_seconds) {
|
|
158675
158804
|
schedule = { kind: "every", everyMs: params.every_seconds * 1000 };
|
|
158676
158805
|
} else if (params.cron_expr) {
|
|
158677
|
-
schedule = { kind: "cron", expr: params.cron_expr, tz: params.tz ?? this.
|
|
158806
|
+
schedule = { kind: "cron", expr: params.cron_expr, tz: params.tz ?? this.tzService.get() };
|
|
158678
158807
|
} else if (params.at) {
|
|
158679
158808
|
const atMs = new Date(params.at).getTime();
|
|
158680
158809
|
if (isNaN(atMs))
|
|
@@ -158791,11 +158920,12 @@ function createToolRegistry(_security, options) {
|
|
|
158791
158920
|
reg(new ExecTool);
|
|
158792
158921
|
reg(new WebSearchTool(options.webSearchConfig));
|
|
158793
158922
|
reg(new WebFetchTool);
|
|
158794
|
-
reg(new CronTool(options.cronService, options.
|
|
158923
|
+
reg(new CronTool(options.cronService, options.timezoneService));
|
|
158795
158924
|
reg(new SaveMemoryTool(options.memoryStore));
|
|
158796
158925
|
return registry4;
|
|
158797
158926
|
}
|
|
158798
158927
|
var init_tools = __esm(() => {
|
|
158928
|
+
init_registry3();
|
|
158799
158929
|
init_read_file();
|
|
158800
158930
|
init_write_file();
|
|
158801
158931
|
init_edit_file();
|
|
@@ -158805,6 +158935,7 @@ var init_tools = __esm(() => {
|
|
|
158805
158935
|
init_web_fetch();
|
|
158806
158936
|
init_cron();
|
|
158807
158937
|
init_save_memory();
|
|
158938
|
+
init_registry3();
|
|
158808
158939
|
init_read_file();
|
|
158809
158940
|
init_write_file();
|
|
158810
158941
|
init_edit_file();
|
|
@@ -159684,7 +159815,7 @@ class ContextBuilder {
|
|
|
159684
159815
|
return prompt.toString();
|
|
159685
159816
|
}
|
|
159686
159817
|
buildRuntimeContext(channel, chatId) {
|
|
159687
|
-
const tz = this.config.
|
|
159818
|
+
const tz = this.config.getTimezone?.() ?? "UTC";
|
|
159688
159819
|
const now = new Date;
|
|
159689
159820
|
const timeStr = now.toLocaleString("en-US", {
|
|
159690
159821
|
timeZone: tz,
|
|
@@ -168553,45 +168684,63 @@ var require_dist5 = __commonJS((exports) => {
|
|
|
168553
168684
|
exports.default = CronExpressionParser_1.CronExpressionParser;
|
|
168554
168685
|
});
|
|
168555
168686
|
|
|
168556
|
-
// src/scheduler/
|
|
168557
|
-
function
|
|
168558
|
-
|
|
168559
|
-
|
|
168560
|
-
|
|
168561
|
-
|
|
168687
|
+
// src/scheduler/storage.ts
|
|
168688
|
+
function scheduleFromRow(row) {
|
|
168689
|
+
switch (row.schedule_kind) {
|
|
168690
|
+
case "at":
|
|
168691
|
+
return { kind: "at", atMs: row.schedule_at_ms ?? undefined };
|
|
168692
|
+
case "every":
|
|
168693
|
+
return { kind: "every", everyMs: row.schedule_every_ms ?? undefined };
|
|
168694
|
+
case "cron":
|
|
168695
|
+
return {
|
|
168696
|
+
kind: "cron",
|
|
168697
|
+
expr: row.schedule_expr ?? undefined,
|
|
168698
|
+
tz: row.schedule_tz ?? undefined
|
|
168699
|
+
};
|
|
168700
|
+
default:
|
|
168701
|
+
return { kind: "cron" };
|
|
168562
168702
|
}
|
|
168563
168703
|
}
|
|
168564
|
-
|
|
168565
|
-
|
|
168566
|
-
|
|
168567
|
-
|
|
168568
|
-
|
|
168569
|
-
|
|
168570
|
-
|
|
168571
|
-
|
|
168572
|
-
|
|
168573
|
-
|
|
168574
|
-
|
|
168575
|
-
|
|
168576
|
-
message:
|
|
168577
|
-
deliver:
|
|
168704
|
+
function rowToJob(row) {
|
|
168705
|
+
let runHistory = [];
|
|
168706
|
+
try {
|
|
168707
|
+
runHistory = JSON.parse(row.run_history);
|
|
168708
|
+
} catch {}
|
|
168709
|
+
return {
|
|
168710
|
+
id: row.id,
|
|
168711
|
+
name: row.name,
|
|
168712
|
+
enabled: row.enabled === 1,
|
|
168713
|
+
schedule: scheduleFromRow(row),
|
|
168714
|
+
payload: {
|
|
168715
|
+
kind: "agent_turn",
|
|
168716
|
+
message: row.payload_message,
|
|
168717
|
+
deliver: row.payload_deliver === 1,
|
|
168718
|
+
channel: row.payload_channel ?? undefined,
|
|
168719
|
+
to: row.payload_to ?? undefined
|
|
168578
168720
|
},
|
|
168579
|
-
{
|
|
168580
|
-
|
|
168581
|
-
|
|
168582
|
-
|
|
168583
|
-
|
|
168584
|
-
|
|
168585
|
-
|
|
168586
|
-
|
|
168587
|
-
|
|
168588
|
-
|
|
168589
|
-
|
|
168590
|
-
}
|
|
168721
|
+
state: {
|
|
168722
|
+
nextRunAtMs: row.next_run_at_ms,
|
|
168723
|
+
lastRunAtMs: row.last_run_at_ms,
|
|
168724
|
+
lastStatus: row.last_status ?? null,
|
|
168725
|
+
lastError: row.last_error,
|
|
168726
|
+
runHistory
|
|
168727
|
+
},
|
|
168728
|
+
createdAtMs: row.created_at_ms,
|
|
168729
|
+
updatedAtMs: row.updated_at_ms,
|
|
168730
|
+
deleteAfterRun: row.delete_after_run === 1
|
|
168731
|
+
};
|
|
168732
|
+
}
|
|
168733
|
+
function scheduleBindings(s2) {
|
|
168734
|
+
return {
|
|
168735
|
+
schedule_kind: s2.kind,
|
|
168736
|
+
schedule_at_ms: s2.atMs ?? null,
|
|
168737
|
+
schedule_every_ms: s2.everyMs ?? null,
|
|
168738
|
+
schedule_expr: s2.expr ?? null,
|
|
168739
|
+
schedule_tz: s2.tz ?? null
|
|
168740
|
+
};
|
|
168741
|
+
}
|
|
168591
168742
|
|
|
168592
168743
|
// src/scheduler/service.ts
|
|
168593
|
-
import { existsSync as existsSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, statSync as statSync4 } from "fs";
|
|
168594
|
-
import { dirname as dirname8 } from "path";
|
|
168595
168744
|
function computeNextRun(schedule, nowMs) {
|
|
168596
168745
|
switch (schedule.kind) {
|
|
168597
168746
|
case "at":
|
|
@@ -168618,255 +168767,292 @@ function computeNextRun(schedule, nowMs) {
|
|
|
168618
168767
|
return null;
|
|
168619
168768
|
}
|
|
168620
168769
|
}
|
|
168621
|
-
|
|
168622
|
-
|
|
168623
|
-
|
|
168624
|
-
|
|
168625
|
-
|
|
168626
|
-
|
|
168627
|
-
|
|
168628
|
-
|
|
168629
|
-
|
|
168630
|
-
|
|
168631
|
-
|
|
168632
|
-
|
|
168633
|
-
|
|
168634
|
-
|
|
168635
|
-
|
|
168636
|
-
|
|
168637
|
-
|
|
168638
|
-
|
|
168639
|
-
|
|
168640
|
-
|
|
168641
|
-
|
|
168642
|
-
|
|
168770
|
+
var import_cron_parser, MAX_HISTORY = 20, MIN_INTERVAL_MS = 1e4, CronService;
|
|
168771
|
+
var init_service = __esm(() => {
|
|
168772
|
+
import_cron_parser = __toESM(require_dist5(), 1);
|
|
168773
|
+
CronService = class CronService {
|
|
168774
|
+
db;
|
|
168775
|
+
stmts;
|
|
168776
|
+
timerHandle = null;
|
|
168777
|
+
_running = false;
|
|
168778
|
+
onJob;
|
|
168779
|
+
static DEFAULT_JOB_NAMES = ["morning-briefing", "evening-recap"];
|
|
168780
|
+
constructor(db2) {
|
|
168781
|
+
this.db = db2;
|
|
168782
|
+
this.stmts = {
|
|
168783
|
+
selectAll: db2.prepare("SELECT * FROM cron_jobs"),
|
|
168784
|
+
selectById: db2.prepare("SELECT * FROM cron_jobs WHERE id = ?"),
|
|
168785
|
+
selectByName: db2.prepare("SELECT * FROM cron_jobs WHERE name = ?"),
|
|
168786
|
+
insert: db2.prepare(`
|
|
168787
|
+
INSERT INTO cron_jobs (
|
|
168788
|
+
id, name, enabled,
|
|
168789
|
+
schedule_kind, schedule_at_ms, schedule_every_ms, schedule_expr, schedule_tz,
|
|
168790
|
+
payload_kind, payload_message, payload_deliver, payload_channel, payload_to,
|
|
168791
|
+
next_run_at_ms, last_run_at_ms, last_status, last_error,
|
|
168792
|
+
run_history, created_at_ms, updated_at_ms, delete_after_run
|
|
168793
|
+
) VALUES (
|
|
168794
|
+
$id, $name, $enabled,
|
|
168795
|
+
$schedule_kind, $schedule_at_ms, $schedule_every_ms, $schedule_expr, $schedule_tz,
|
|
168796
|
+
$payload_kind, $payload_message, $payload_deliver, $payload_channel, $payload_to,
|
|
168797
|
+
$next_run_at_ms, NULL, NULL, NULL,
|
|
168798
|
+
'[]', $created_at_ms, $updated_at_ms, $delete_after_run
|
|
168799
|
+
)
|
|
168800
|
+
`),
|
|
168801
|
+
updateFull: db2.prepare(`
|
|
168802
|
+
UPDATE cron_jobs SET
|
|
168803
|
+
last_run_at_ms = $last_run_at_ms,
|
|
168804
|
+
last_status = $last_status,
|
|
168805
|
+
last_error = $last_error,
|
|
168806
|
+
next_run_at_ms = $next_run_at_ms,
|
|
168807
|
+
run_history = $run_history,
|
|
168808
|
+
enabled = $enabled,
|
|
168809
|
+
updated_at_ms = $updated_at_ms
|
|
168810
|
+
WHERE id = $id
|
|
168811
|
+
`),
|
|
168812
|
+
updateNext: db2.prepare("UPDATE cron_jobs SET next_run_at_ms = ?, updated_at_ms = ? WHERE id = ?"),
|
|
168813
|
+
updateTzAndNext: db2.prepare("UPDATE cron_jobs SET schedule_tz = ?, next_run_at_ms = ?, updated_at_ms = ? WHERE name = ?"),
|
|
168814
|
+
updateEnabled: db2.prepare("UPDATE cron_jobs SET enabled = ?, next_run_at_ms = ?, updated_at_ms = ? WHERE id = ?"),
|
|
168815
|
+
delete: db2.prepare("DELETE FROM cron_jobs WHERE id = ?")
|
|
168816
|
+
};
|
|
168817
|
+
}
|
|
168818
|
+
setOnJob(fn) {
|
|
168819
|
+
this.onJob = fn;
|
|
168820
|
+
}
|
|
168821
|
+
start(opts = {}) {
|
|
168822
|
+
this._running = true;
|
|
168823
|
+
this.seedDefaultJobs(opts.defaults ?? []);
|
|
168824
|
+
const now = Date.now();
|
|
168825
|
+
const rows = this.stmts.selectAll.all();
|
|
168826
|
+
this.db.transaction(() => {
|
|
168827
|
+
for (const row of rows) {
|
|
168828
|
+
if (row.enabled === 1 && row.next_run_at_ms !== null && row.next_run_at_ms <= now) {
|
|
168829
|
+
const schedule = scheduleFromRow(row);
|
|
168830
|
+
const next = computeNextRun(schedule, now);
|
|
168831
|
+
this.stmts.updateNext.run(next, now, row.id);
|
|
168832
|
+
} else if (row.next_run_at_ms === null && row.enabled) {
|
|
168833
|
+
const schedule = scheduleFromRow(row);
|
|
168834
|
+
const next = computeNextRun(schedule, now);
|
|
168835
|
+
this.stmts.updateNext.run(next, now, row.id);
|
|
168836
|
+
}
|
|
168837
|
+
}
|
|
168838
|
+
})();
|
|
168839
|
+
this.scheduleNextTick();
|
|
168840
|
+
}
|
|
168841
|
+
stop() {
|
|
168842
|
+
this._running = false;
|
|
168843
|
+
if (this.timerHandle) {
|
|
168844
|
+
clearTimeout(this.timerHandle);
|
|
168845
|
+
this.timerHandle = null;
|
|
168643
168846
|
}
|
|
168644
168847
|
}
|
|
168645
|
-
|
|
168646
|
-
|
|
168647
|
-
|
|
168648
|
-
|
|
168649
|
-
this._running = false;
|
|
168650
|
-
if (this.timerHandle) {
|
|
168651
|
-
clearTimeout(this.timerHandle);
|
|
168652
|
-
this.timerHandle = null;
|
|
168848
|
+
listJobs(includeDisabled = false) {
|
|
168849
|
+
const rows = this.stmts.selectAll.all();
|
|
168850
|
+
const jobs = includeDisabled ? rows.map(rowToJob) : rows.filter((r2) => r2.enabled === 1).map(rowToJob);
|
|
168851
|
+
return jobs.sort((a, b5) => (a.state.nextRunAtMs ?? Infinity) - (b5.state.nextRunAtMs ?? Infinity));
|
|
168653
168852
|
}
|
|
168654
|
-
|
|
168655
|
-
|
|
168656
|
-
|
|
168657
|
-
|
|
168658
|
-
|
|
168659
|
-
|
|
168660
|
-
|
|
168661
|
-
|
|
168662
|
-
|
|
168663
|
-
|
|
168664
|
-
|
|
168665
|
-
|
|
168666
|
-
|
|
168667
|
-
|
|
168668
|
-
|
|
168669
|
-
|
|
168670
|
-
|
|
168671
|
-
|
|
168672
|
-
|
|
168673
|
-
|
|
168674
|
-
}
|
|
168675
|
-
|
|
168676
|
-
|
|
168677
|
-
|
|
168678
|
-
|
|
168679
|
-
|
|
168680
|
-
|
|
168681
|
-
|
|
168682
|
-
|
|
168683
|
-
|
|
168684
|
-
deleteAfterRun: opts.deleteAfterRun ?? false
|
|
168685
|
-
};
|
|
168686
|
-
this.store.jobs.push(job);
|
|
168687
|
-
this.saveStore();
|
|
168688
|
-
this.armTimer();
|
|
168689
|
-
return job;
|
|
168690
|
-
}
|
|
168691
|
-
removeJob(jobId) {
|
|
168692
|
-
this.loadStore();
|
|
168693
|
-
const idx = this.store.jobs.findIndex((j9) => j9.id === jobId);
|
|
168694
|
-
if (idx === -1)
|
|
168695
|
-
return false;
|
|
168696
|
-
this.store.jobs.splice(idx, 1);
|
|
168697
|
-
this.saveStore();
|
|
168698
|
-
this.armTimer();
|
|
168699
|
-
return true;
|
|
168700
|
-
}
|
|
168701
|
-
enableJob(jobId, enabled) {
|
|
168702
|
-
this.loadStore();
|
|
168703
|
-
const job = this.store.jobs.find((j9) => j9.id === jobId);
|
|
168704
|
-
if (!job)
|
|
168705
|
-
return;
|
|
168706
|
-
job.enabled = enabled;
|
|
168707
|
-
if (enabled) {
|
|
168708
|
-
job.state.nextRunAtMs = computeNextRun(job.schedule, Date.now());
|
|
168709
|
-
} else {
|
|
168710
|
-
job.state.nextRunAtMs = null;
|
|
168853
|
+
addJob(opts) {
|
|
168854
|
+
const now = Date.now();
|
|
168855
|
+
const id = crypto.randomUUID().slice(0, 12);
|
|
168856
|
+
const sb = scheduleBindings(opts.schedule);
|
|
168857
|
+
try {
|
|
168858
|
+
this.stmts.insert.run({
|
|
168859
|
+
$id: id,
|
|
168860
|
+
$name: opts.name,
|
|
168861
|
+
$enabled: 1,
|
|
168862
|
+
...Object.fromEntries(Object.entries(sb).map(([k, v4]) => [`$${k}`, v4])),
|
|
168863
|
+
$payload_kind: "agent_turn",
|
|
168864
|
+
$payload_message: opts.message,
|
|
168865
|
+
$payload_deliver: opts.deliver ?? true ? 1 : 0,
|
|
168866
|
+
$payload_channel: opts.channel ?? null,
|
|
168867
|
+
$payload_to: opts.to ?? null,
|
|
168868
|
+
$next_run_at_ms: computeNextRun(opts.schedule, now),
|
|
168869
|
+
$created_at_ms: now,
|
|
168870
|
+
$updated_at_ms: now,
|
|
168871
|
+
$delete_after_run: opts.deleteAfterRun ?? false ? 1 : 0
|
|
168872
|
+
});
|
|
168873
|
+
} catch (err) {
|
|
168874
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
168875
|
+
if (msg.includes("UNIQUE constraint failed") || msg.includes("SQLITE_CONSTRAINT")) {
|
|
168876
|
+
throw new Error(`Cron name already exists: ${opts.name}`);
|
|
168877
|
+
}
|
|
168878
|
+
throw err;
|
|
168879
|
+
}
|
|
168880
|
+
this.scheduleNextTick();
|
|
168881
|
+
const row = this.stmts.selectById.get(id);
|
|
168882
|
+
return rowToJob(row);
|
|
168711
168883
|
}
|
|
168712
|
-
|
|
168713
|
-
|
|
168714
|
-
|
|
168715
|
-
|
|
168716
|
-
|
|
168717
|
-
|
|
168718
|
-
|
|
168719
|
-
|
|
168720
|
-
|
|
168721
|
-
|
|
168722
|
-
|
|
168723
|
-
|
|
168724
|
-
|
|
168725
|
-
|
|
168726
|
-
|
|
168727
|
-
|
|
168728
|
-
|
|
168729
|
-
|
|
168730
|
-
|
|
168731
|
-
|
|
168732
|
-
|
|
168733
|
-
|
|
168734
|
-
enabled
|
|
168735
|
-
|
|
168736
|
-
|
|
168737
|
-
}
|
|
168738
|
-
|
|
168739
|
-
|
|
168740
|
-
|
|
168741
|
-
|
|
168742
|
-
|
|
168743
|
-
|
|
168744
|
-
|
|
168745
|
-
|
|
168746
|
-
|
|
168747
|
-
|
|
168748
|
-
|
|
168749
|
-
enabled: true,
|
|
168750
|
-
schedule: spec.schedule,
|
|
168751
|
-
payload: {
|
|
168752
|
-
kind: "agent_turn",
|
|
168753
|
-
message: spec.message,
|
|
168754
|
-
deliver: spec.deliver
|
|
168755
|
-
},
|
|
168756
|
-
state: {
|
|
168757
|
-
nextRunAtMs: computeNextRun(spec.schedule, now),
|
|
168758
|
-
lastRunAtMs: null,
|
|
168759
|
-
lastStatus: null,
|
|
168760
|
-
lastError: null,
|
|
168761
|
-
runHistory: []
|
|
168762
|
-
},
|
|
168763
|
-
createdAtMs: now,
|
|
168764
|
-
updatedAtMs: now,
|
|
168765
|
-
deleteAfterRun: false
|
|
168884
|
+
removeJob(jobId) {
|
|
168885
|
+
const row = this.stmts.selectById.get(jobId);
|
|
168886
|
+
if (!row)
|
|
168887
|
+
return false;
|
|
168888
|
+
this.stmts.delete.run(jobId);
|
|
168889
|
+
this.scheduleNextTick();
|
|
168890
|
+
return true;
|
|
168891
|
+
}
|
|
168892
|
+
enableJob(jobId, enabled) {
|
|
168893
|
+
const now = Date.now();
|
|
168894
|
+
const row = this.stmts.selectById.get(jobId);
|
|
168895
|
+
if (!row)
|
|
168896
|
+
return;
|
|
168897
|
+
const nextRun = enabled ? computeNextRun(scheduleFromRow(row), now) : null;
|
|
168898
|
+
this.stmts.updateEnabled.run(enabled ? 1 : 0, nextRun, now, jobId);
|
|
168899
|
+
this.scheduleNextTick();
|
|
168900
|
+
}
|
|
168901
|
+
async runJob(jobId, force = false) {
|
|
168902
|
+
const row = this.stmts.selectById.get(jobId);
|
|
168903
|
+
if (!row)
|
|
168904
|
+
return;
|
|
168905
|
+
const job = rowToJob(row);
|
|
168906
|
+
if (!job.enabled && !force)
|
|
168907
|
+
return;
|
|
168908
|
+
await this.executeJob(job);
|
|
168909
|
+
}
|
|
168910
|
+
getJob(jobId) {
|
|
168911
|
+
const row = this.stmts.selectById.get(jobId);
|
|
168912
|
+
return row ? rowToJob(row) : undefined;
|
|
168913
|
+
}
|
|
168914
|
+
status() {
|
|
168915
|
+
const rows = this.stmts.selectAll.all();
|
|
168916
|
+
const enabled = rows.filter((r2) => r2.enabled === 1);
|
|
168917
|
+
return {
|
|
168918
|
+
enabled: this._running,
|
|
168919
|
+
jobs: enabled.length,
|
|
168920
|
+
nextTickAtMs: this.getNextTickMs()
|
|
168766
168921
|
};
|
|
168767
|
-
this.store.jobs.push(job);
|
|
168768
|
-
dirty = true;
|
|
168769
168922
|
}
|
|
168770
|
-
|
|
168771
|
-
|
|
168772
|
-
|
|
168773
|
-
|
|
168774
|
-
|
|
168775
|
-
|
|
168776
|
-
|
|
168777
|
-
|
|
168778
|
-
|
|
168779
|
-
|
|
168923
|
+
updateBuiltinJobsTimezone(tz) {
|
|
168924
|
+
const updated = [];
|
|
168925
|
+
const now = Date.now();
|
|
168926
|
+
this.db.transaction(() => {
|
|
168927
|
+
for (const name of CronService.DEFAULT_JOB_NAMES) {
|
|
168928
|
+
const row = this.stmts.selectByName.get(name);
|
|
168929
|
+
if (!row || row.schedule_kind !== "cron")
|
|
168930
|
+
continue;
|
|
168931
|
+
const nextRun = computeNextRun({ kind: "cron", expr: row.schedule_expr ?? undefined, tz }, now);
|
|
168932
|
+
this.stmts.updateTzAndNext.run(tz, nextRun, now, name);
|
|
168933
|
+
updated.push(name);
|
|
168934
|
+
}
|
|
168935
|
+
})();
|
|
168936
|
+
this.scheduleNextTick();
|
|
168937
|
+
return updated;
|
|
168938
|
+
}
|
|
168939
|
+
seedDefaultJobs(specs) {
|
|
168940
|
+
const now = Date.now();
|
|
168941
|
+
this.db.transaction(() => {
|
|
168942
|
+
for (const spec of specs) {
|
|
168943
|
+
const existing = this.stmts.selectByName.get(spec.name);
|
|
168944
|
+
if (existing)
|
|
168945
|
+
continue;
|
|
168946
|
+
const id = crypto.randomUUID().slice(0, 12);
|
|
168947
|
+
const sb = scheduleBindings(spec.schedule);
|
|
168948
|
+
this.stmts.insert.run({
|
|
168949
|
+
$id: id,
|
|
168950
|
+
$name: spec.name,
|
|
168951
|
+
$enabled: 1,
|
|
168952
|
+
...Object.fromEntries(Object.entries(sb).map(([k, v4]) => [`$${k}`, v4])),
|
|
168953
|
+
$payload_kind: "agent_turn",
|
|
168954
|
+
$payload_message: spec.message,
|
|
168955
|
+
$payload_deliver: spec.deliver ? 1 : 0,
|
|
168956
|
+
$payload_channel: null,
|
|
168957
|
+
$payload_to: null,
|
|
168958
|
+
$next_run_at_ms: computeNextRun(spec.schedule, now),
|
|
168959
|
+
$created_at_ms: now,
|
|
168960
|
+
$updated_at_ms: now,
|
|
168961
|
+
$delete_after_run: 0
|
|
168962
|
+
});
|
|
168963
|
+
}
|
|
168964
|
+
})();
|
|
168965
|
+
}
|
|
168966
|
+
async executeJob(job) {
|
|
168967
|
+
const startMs = Date.now();
|
|
168968
|
+
let status = "ok";
|
|
168969
|
+
let error45;
|
|
168970
|
+
try {
|
|
168971
|
+
if (this.onJob) {
|
|
168972
|
+
await this.onJob(job);
|
|
168973
|
+
}
|
|
168974
|
+
} catch (err) {
|
|
168975
|
+
status = "error";
|
|
168976
|
+
error45 = err instanceof Error ? err.message : String(err);
|
|
168977
|
+
}
|
|
168978
|
+
const durationMs = Date.now() - startMs;
|
|
168979
|
+
const record6 = { runAtMs: startMs, status, durationMs, error: error45 };
|
|
168980
|
+
const currentRow = this.stmts.selectById.get(job.id);
|
|
168981
|
+
let runHistory = [];
|
|
168982
|
+
if (currentRow) {
|
|
168983
|
+
try {
|
|
168984
|
+
runHistory = JSON.parse(currentRow.run_history);
|
|
168985
|
+
} catch {}
|
|
168780
168986
|
}
|
|
168781
|
-
|
|
168782
|
-
|
|
168783
|
-
|
|
168784
|
-
}
|
|
168785
|
-
const durationMs = Date.now() - startMs;
|
|
168786
|
-
const record6 = { runAtMs: startMs, status, durationMs, error: error45 };
|
|
168787
|
-
job.state.lastRunAtMs = startMs;
|
|
168788
|
-
job.state.lastStatus = status;
|
|
168789
|
-
job.state.lastError = error45 ?? null;
|
|
168790
|
-
job.state.runHistory.push(record6);
|
|
168791
|
-
if (job.state.runHistory.length > MAX_HISTORY) {
|
|
168792
|
-
job.state.runHistory = job.state.runHistory.slice(-MAX_HISTORY);
|
|
168793
|
-
}
|
|
168794
|
-
if (job.schedule.kind === "at") {
|
|
168795
|
-
if (job.deleteAfterRun) {
|
|
168796
|
-
const idx = this.store.jobs.indexOf(job);
|
|
168797
|
-
if (idx >= 0)
|
|
168798
|
-
this.store.jobs.splice(idx, 1);
|
|
168799
|
-
} else {
|
|
168800
|
-
job.enabled = false;
|
|
168801
|
-
job.state.nextRunAtMs = null;
|
|
168987
|
+
runHistory.push(record6);
|
|
168988
|
+
if (runHistory.length > MAX_HISTORY) {
|
|
168989
|
+
runHistory = runHistory.slice(-MAX_HISTORY);
|
|
168802
168990
|
}
|
|
168803
|
-
|
|
168804
|
-
|
|
168991
|
+
let nextRunAtMs;
|
|
168992
|
+
let enabled = 1;
|
|
168993
|
+
if (job.schedule.kind === "at") {
|
|
168994
|
+
if (job.deleteAfterRun) {
|
|
168995
|
+
this.stmts.delete.run(job.id);
|
|
168996
|
+
this.scheduleNextTick();
|
|
168997
|
+
return;
|
|
168998
|
+
}
|
|
168999
|
+
enabled = 0;
|
|
169000
|
+
nextRunAtMs = null;
|
|
169001
|
+
} else {
|
|
169002
|
+
const freshSchedule = currentRow ? scheduleFromRow(currentRow) : job.schedule;
|
|
169003
|
+
nextRunAtMs = computeNextRun(freshSchedule, Date.now());
|
|
169004
|
+
}
|
|
169005
|
+
this.db.transaction(() => {
|
|
169006
|
+
this.stmts.updateFull.run({
|
|
169007
|
+
$last_run_at_ms: startMs,
|
|
169008
|
+
$last_status: status,
|
|
169009
|
+
$last_error: error45 ?? null,
|
|
169010
|
+
$next_run_at_ms: nextRunAtMs,
|
|
169011
|
+
$run_history: JSON.stringify(runHistory),
|
|
169012
|
+
$enabled: enabled,
|
|
169013
|
+
$updated_at_ms: Date.now(),
|
|
169014
|
+
$id: job.id
|
|
169015
|
+
});
|
|
169016
|
+
})();
|
|
169017
|
+
this.scheduleNextTick();
|
|
168805
169018
|
}
|
|
168806
|
-
|
|
168807
|
-
|
|
168808
|
-
|
|
168809
|
-
|
|
168810
|
-
|
|
168811
|
-
|
|
168812
|
-
|
|
168813
|
-
|
|
169019
|
+
getNextTickMs() {
|
|
169020
|
+
const rows = this.stmts.selectAll.all();
|
|
169021
|
+
let earliest = null;
|
|
169022
|
+
for (const row of rows) {
|
|
169023
|
+
if (row.enabled === 1 && row.next_run_at_ms !== null) {
|
|
169024
|
+
if (earliest === null || row.next_run_at_ms < earliest) {
|
|
169025
|
+
earliest = row.next_run_at_ms;
|
|
169026
|
+
}
|
|
168814
169027
|
}
|
|
168815
169028
|
}
|
|
169029
|
+
return earliest;
|
|
168816
169030
|
}
|
|
168817
|
-
|
|
168818
|
-
|
|
168819
|
-
|
|
168820
|
-
|
|
168821
|
-
|
|
168822
|
-
this.
|
|
168823
|
-
|
|
168824
|
-
|
|
168825
|
-
|
|
168826
|
-
|
|
168827
|
-
|
|
168828
|
-
|
|
168829
|
-
const delayMs = Math.max(nextMs - Date.now(), 0);
|
|
168830
|
-
this.timerHandle = setTimeout(() => this.onTimer(), delayMs);
|
|
168831
|
-
}
|
|
168832
|
-
async onTimer() {
|
|
168833
|
-
if (!this._running)
|
|
168834
|
-
return;
|
|
168835
|
-
this.loadStore();
|
|
168836
|
-
const now = Date.now();
|
|
168837
|
-
const due = this.store.jobs.filter((j9) => j9.enabled && j9.state.nextRunAtMs !== null && j9.state.nextRunAtMs <= now);
|
|
168838
|
-
for (const job of due) {
|
|
168839
|
-
await this.executeJob(job);
|
|
169031
|
+
scheduleNextTick() {
|
|
169032
|
+
if (this.timerHandle) {
|
|
169033
|
+
clearTimeout(this.timerHandle);
|
|
169034
|
+
this.timerHandle = null;
|
|
169035
|
+
}
|
|
169036
|
+
if (!this._running)
|
|
169037
|
+
return;
|
|
169038
|
+
const nextMs = this.getNextTickMs();
|
|
169039
|
+
if (nextMs === null)
|
|
169040
|
+
return;
|
|
169041
|
+
const delayMs = Math.max(nextMs - Date.now(), 0);
|
|
169042
|
+
this.timerHandle = setTimeout(() => void this.onTimer(), delayMs);
|
|
168840
169043
|
}
|
|
168841
|
-
|
|
168842
|
-
|
|
168843
|
-
}
|
|
168844
|
-
loadStore() {
|
|
168845
|
-
if (!existsSync17(this.storePath))
|
|
168846
|
-
return;
|
|
168847
|
-
try {
|
|
168848
|
-
const stat2 = statSync4(this.storePath);
|
|
168849
|
-
const mtime = stat2.mtimeMs;
|
|
168850
|
-
if (mtime === this.lastMtime && this.store.jobs.length > 0)
|
|
169044
|
+
async onTimer() {
|
|
169045
|
+
if (!this._running)
|
|
168851
169046
|
return;
|
|
168852
|
-
const
|
|
168853
|
-
const
|
|
168854
|
-
|
|
168855
|
-
|
|
168856
|
-
|
|
168857
|
-
|
|
168858
|
-
|
|
168859
|
-
|
|
168860
|
-
|
|
168861
|
-
try {
|
|
168862
|
-
this.lastMtime = statSync4(this.storePath).mtimeMs;
|
|
168863
|
-
} catch {}
|
|
168864
|
-
}
|
|
168865
|
-
}
|
|
168866
|
-
var import_cron_parser, MAX_HISTORY = 20, MIN_INTERVAL_MS = 1e4;
|
|
168867
|
-
var init_service = __esm(() => {
|
|
168868
|
-
init_defaults();
|
|
168869
|
-
import_cron_parser = __toESM(require_dist5(), 1);
|
|
169047
|
+
const now = Date.now();
|
|
169048
|
+
const rows = this.stmts.selectAll.all();
|
|
169049
|
+
const due = rows.filter((r2) => r2.enabled === 1 && r2.next_run_at_ms !== null && r2.next_run_at_ms <= now);
|
|
169050
|
+
for (const row of due) {
|
|
169051
|
+
await this.executeJob(rowToJob(row));
|
|
169052
|
+
}
|
|
169053
|
+
this.scheduleNextTick();
|
|
169054
|
+
}
|
|
169055
|
+
};
|
|
168870
169056
|
});
|
|
168871
169057
|
|
|
168872
169058
|
// src/bus/queue.ts
|
|
@@ -169560,6 +169746,9 @@ class ApprovalManager {
|
|
|
169560
169746
|
getReason(approvalId) {
|
|
169561
169747
|
return this.pending.get(approvalId)?.reason ?? null;
|
|
169562
169748
|
}
|
|
169749
|
+
getPreview(approvalId) {
|
|
169750
|
+
return this.pending.get(approvalId)?.preview;
|
|
169751
|
+
}
|
|
169563
169752
|
getPending(sessionKey) {
|
|
169564
169753
|
const id = this.bySession.get(sessionKey);
|
|
169565
169754
|
if (!id)
|
|
@@ -169893,7 +170082,7 @@ var init_approval_events = __esm(() => {
|
|
|
169893
170082
|
});
|
|
169894
170083
|
|
|
169895
170084
|
// src/services/trading-confirm.ts
|
|
169896
|
-
function buildPreview(title, body) {
|
|
170085
|
+
function buildPreview(title, body, extras) {
|
|
169897
170086
|
const lines = (body.lines ?? []).filter((l2) => l2 !== undefined && l2 !== null);
|
|
169898
170087
|
const steps = (body.steps ?? []).filter((s2) => s2 !== undefined && s2 !== null);
|
|
169899
170088
|
const details = {};
|
|
@@ -169919,7 +170108,10 @@ function buildPreview(title, body) {
|
|
|
169919
170108
|
summary,
|
|
169920
170109
|
details,
|
|
169921
170110
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
169922
|
-
|
|
170111
|
+
symbol: extras?.symbol,
|
|
170112
|
+
direction: blob.includes("buy") || blob.includes("long") ? "long" : blob.includes("sell") || blob.includes("short") ? "short" : undefined,
|
|
170113
|
+
wizard: extras?.wizard,
|
|
170114
|
+
suggestedValue: extras?.suggestedValue
|
|
169923
170115
|
};
|
|
169924
170116
|
}
|
|
169925
170117
|
|
|
@@ -169932,8 +170124,8 @@ class DaemonConfirmService {
|
|
|
169932
170124
|
this.eventBus = eventBus;
|
|
169933
170125
|
this.orchestrator = orchestrator;
|
|
169934
170126
|
}
|
|
169935
|
-
async confirm(title, body) {
|
|
169936
|
-
const preview = buildPreview(title, body);
|
|
170127
|
+
async confirm(title, body, extras) {
|
|
170128
|
+
const preview = buildPreview(title, body, extras);
|
|
169937
170129
|
const sessionKey = `trade:${crypto.randomUUID().slice(0, 8)}`;
|
|
169938
170130
|
const preText = this.orchestrator.getCurrentTurnText();
|
|
169939
170131
|
const origin = this.orchestrator.getCurrentTurnOrigin();
|
|
@@ -180691,6 +180883,50 @@ var init_cloid = __esm(() => {
|
|
|
180691
180883
|
SUFFIX_BYTE_LENGTH = SUFFIX_HEX_LENGTH / 2;
|
|
180692
180884
|
});
|
|
180693
180885
|
|
|
180886
|
+
// src/services/live/info-cache.ts
|
|
180887
|
+
class InfoCache {
|
|
180888
|
+
entries = new Map;
|
|
180889
|
+
ttlMs;
|
|
180890
|
+
constructor(ttlMs = 3000) {
|
|
180891
|
+
this.ttlMs = ttlMs;
|
|
180892
|
+
}
|
|
180893
|
+
get(key, fetcher) {
|
|
180894
|
+
const now = Date.now();
|
|
180895
|
+
const existing = this.entries.get(key);
|
|
180896
|
+
if (existing !== undefined && now - existing.insertedAt < this.ttlMs) {
|
|
180897
|
+
return existing.promise;
|
|
180898
|
+
}
|
|
180899
|
+
const promise6 = fetcher().catch((err) => {
|
|
180900
|
+
if (this.entries.get(key)?.promise === promise6) {
|
|
180901
|
+
this.entries.delete(key);
|
|
180902
|
+
}
|
|
180903
|
+
throw err;
|
|
180904
|
+
});
|
|
180905
|
+
this.entries.set(key, { promise: promise6, insertedAt: now });
|
|
180906
|
+
return promise6;
|
|
180907
|
+
}
|
|
180908
|
+
clear() {
|
|
180909
|
+
this.entries.clear();
|
|
180910
|
+
}
|
|
180911
|
+
}
|
|
180912
|
+
async function runWithConcurrency(items, concurrency, fn) {
|
|
180913
|
+
const results = new Array(items.length);
|
|
180914
|
+
let nextIndex = 0;
|
|
180915
|
+
async function worker() {
|
|
180916
|
+
while (nextIndex < items.length) {
|
|
180917
|
+
const index = nextIndex++;
|
|
180918
|
+
try {
|
|
180919
|
+
results[index] = { status: "fulfilled", value: await fn(items[index], index) };
|
|
180920
|
+
} catch (reason) {
|
|
180921
|
+
results[index] = { status: "rejected", reason };
|
|
180922
|
+
}
|
|
180923
|
+
}
|
|
180924
|
+
}
|
|
180925
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
180926
|
+
await Promise.all(workers);
|
|
180927
|
+
return results;
|
|
180928
|
+
}
|
|
180929
|
+
|
|
180694
180930
|
// src/services/live/client.ts
|
|
180695
180931
|
function mapFill(f2) {
|
|
180696
180932
|
return {
|
|
@@ -180734,6 +180970,45 @@ function parseDex(symbol5) {
|
|
|
180734
180970
|
return { dex: null, name: trimmed };
|
|
180735
180971
|
return { dex, name: trimmed };
|
|
180736
180972
|
}
|
|
180973
|
+
function mapPositionsFromRaw(raw) {
|
|
180974
|
+
const positions = [];
|
|
180975
|
+
for (const ap of raw.assetPositions ?? []) {
|
|
180976
|
+
const pos = ap.position;
|
|
180977
|
+
const szi = parseFloat(pos.szi);
|
|
180978
|
+
if (szi === 0)
|
|
180979
|
+
continue;
|
|
180980
|
+
const entryPx = parseFloat(pos.entryPx);
|
|
180981
|
+
const markPx = pos.positionValue ? Math.abs(parseFloat(pos.positionValue) / szi) : entryPx;
|
|
180982
|
+
positions.push({
|
|
180983
|
+
symbol: pos.coin,
|
|
180984
|
+
side: szi > 0 ? "long" : "short",
|
|
180985
|
+
size: Math.abs(szi),
|
|
180986
|
+
entryPrice: entryPx,
|
|
180987
|
+
markPrice: markPx,
|
|
180988
|
+
liquidationPrice: pos.liquidationPx && pos.liquidationPx !== "0.0" && parseFloat(pos.liquidationPx) > 0 ? parseFloat(pos.liquidationPx) : null,
|
|
180989
|
+
unrealizedPnl: parseFloat(pos.unrealizedPnl),
|
|
180990
|
+
unrealizedPnlPct: parseFloat(pos.returnOnEquity ?? "0") * 100,
|
|
180991
|
+
leverage: parseFloat(pos.leverage?.value ?? "1"),
|
|
180992
|
+
marginMode: pos.leverage?.type === "isolated" ? "isolated" : "cross",
|
|
180993
|
+
margin: parseFloat(pos.marginUsed)
|
|
180994
|
+
});
|
|
180995
|
+
}
|
|
180996
|
+
return positions;
|
|
180997
|
+
}
|
|
180998
|
+
function mapOpenOrderFromRaw(o10) {
|
|
180999
|
+
return {
|
|
181000
|
+
orderId: String(o10.oid),
|
|
181001
|
+
symbol: o10.coin,
|
|
181002
|
+
side: o10.side === "B" ? "buy" : "sell",
|
|
181003
|
+
orderType: o10.orderType ?? "Limit",
|
|
181004
|
+
price: o10.limitPx ? parseFloat(o10.limitPx) : null,
|
|
181005
|
+
triggerPrice: o10.triggerPx && o10.triggerPx !== "0.0" ? parseFloat(o10.triggerPx) : null,
|
|
181006
|
+
size: parseFloat(o10.sz),
|
|
181007
|
+
filled: parseFloat(o10.origSz ?? o10.sz) - parseFloat(o10.sz),
|
|
181008
|
+
reduceOnly: o10.reduceOnly ?? false,
|
|
181009
|
+
timestamp: o10.timestamp ?? Date.now()
|
|
181010
|
+
};
|
|
181011
|
+
}
|
|
180737
181012
|
function ctxToTicker(ctx, symbol5) {
|
|
180738
181013
|
const markPx = parseFloat(ctx.markPx ?? "0");
|
|
180739
181014
|
const prevDay = parseFloat(ctx.prevDayPx ?? "0");
|
|
@@ -180759,12 +181034,18 @@ class HyperliquidClient {
|
|
|
180759
181034
|
assetMap = new Map;
|
|
180760
181035
|
szDecimals = new Map;
|
|
180761
181036
|
maxLeverage = new Map;
|
|
180762
|
-
|
|
181037
|
+
assets = [];
|
|
180763
181038
|
metaLoaded = false;
|
|
181039
|
+
metaInFlight = null;
|
|
180764
181040
|
dexUniverses = new Map;
|
|
180765
181041
|
dexListCache = null;
|
|
180766
181042
|
dexListCacheAt = 0;
|
|
180767
181043
|
DEX_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
181044
|
+
infoCache = new InfoCache(3000);
|
|
181045
|
+
wsTransport = null;
|
|
181046
|
+
wsSubClient = null;
|
|
181047
|
+
wsLifecycle = null;
|
|
181048
|
+
wsDisposed = false;
|
|
180768
181049
|
constructor(config3, logger) {
|
|
180769
181050
|
this.defaultAddress = config3?.address ?? "";
|
|
180770
181051
|
this.testnet = config3?.testnet ?? false;
|
|
@@ -180785,6 +181066,7 @@ class HyperliquidClient {
|
|
|
180785
181066
|
connect(config3) {
|
|
180786
181067
|
this.defaultAddress = config3.address || this.defaultAddress;
|
|
180787
181068
|
this.baseUrl = config3.testnet ? TESTNET_URL : MAINNET_URL;
|
|
181069
|
+
this.wsDisposed = false;
|
|
180788
181070
|
if (config3.privateKey) {
|
|
180789
181071
|
const wallet = privateKeyToAccount(config3.privateKey);
|
|
180790
181072
|
const transport = new HttpTransport({ isTestnet: config3.testnet });
|
|
@@ -180794,18 +181076,35 @@ class HyperliquidClient {
|
|
|
180794
181076
|
disconnect() {
|
|
180795
181077
|
this.defaultAddress = "";
|
|
180796
181078
|
this.exchange = null;
|
|
181079
|
+
this.closeWs();
|
|
180797
181080
|
}
|
|
180798
181081
|
requireExchange() {
|
|
180799
181082
|
if (!this.exchange)
|
|
180800
181083
|
throw new Error("Write operations require a private key. Use the connect_wallet tool or set hlPrivateKey in config.");
|
|
180801
181084
|
return this.exchange;
|
|
180802
181085
|
}
|
|
180803
|
-
async info(type5, extra = {}) {
|
|
181086
|
+
async info(type5, extra = {}, options = {}) {
|
|
181087
|
+
if (!options.cache)
|
|
181088
|
+
return this.fetchInfo(type5, extra, 0);
|
|
181089
|
+
const key = `${type5}:${JSON.stringify(extra)}`;
|
|
181090
|
+
return this.infoCache.get(key, () => this.fetchInfo(type5, extra, 0));
|
|
181091
|
+
}
|
|
181092
|
+
async fetchInfo(type5, extra, attempt) {
|
|
180804
181093
|
const res = await fetch(`${this.baseUrl}/info`, {
|
|
180805
181094
|
method: "POST",
|
|
180806
181095
|
headers: { "Content-Type": "application/json" },
|
|
180807
181096
|
body: JSON.stringify({ type: type5, ...extra })
|
|
180808
181097
|
});
|
|
181098
|
+
if (res.status === 429 && attempt < 3) {
|
|
181099
|
+
const retryAfterRaw = res.headers.get("retry-after");
|
|
181100
|
+
const retryAfterSec = retryAfterRaw !== null ? Number(retryAfterRaw) : NaN;
|
|
181101
|
+
const MAX_RETRY_AFTER_MS = 5000;
|
|
181102
|
+
const baseMs = Number.isFinite(retryAfterSec) && retryAfterSec > 0 ? Math.min(retryAfterSec * 1000, MAX_RETRY_AFTER_MS) : 250 * Math.pow(2, attempt);
|
|
181103
|
+
const jitterMs = Math.random() * 100;
|
|
181104
|
+
this.log.debug({ type: type5, dex: extra.dex, attempt, delayMs: baseMs + jitterMs }, "rate limited \u2014 retrying after backoff");
|
|
181105
|
+
await new Promise((resolve) => setTimeout(resolve, baseMs + jitterMs));
|
|
181106
|
+
return this.fetchInfo(type5, extra, attempt + 1);
|
|
181107
|
+
}
|
|
180809
181108
|
if (!res.ok) {
|
|
180810
181109
|
const text = await res.text();
|
|
180811
181110
|
throw new Error(`Hyperliquid ${type5}: ${res.status} ${text}`);
|
|
@@ -180841,8 +181140,16 @@ class HyperliquidClient {
|
|
|
180841
181140
|
const hasNewDex = currentDexes.some((d2) => !knownDexNames.has(d2.name));
|
|
180842
181141
|
if (!hasNewDex)
|
|
180843
181142
|
return;
|
|
180844
|
-
this.metaLoaded = false;
|
|
180845
181143
|
}
|
|
181144
|
+
if (this.metaInFlight !== null)
|
|
181145
|
+
return this.metaInFlight;
|
|
181146
|
+
this.metaInFlight = this.rebuildMeta().finally(() => {
|
|
181147
|
+
this.metaInFlight = null;
|
|
181148
|
+
});
|
|
181149
|
+
return this.metaInFlight;
|
|
181150
|
+
}
|
|
181151
|
+
async rebuildMeta() {
|
|
181152
|
+
this.metaLoaded = false;
|
|
180846
181153
|
let nativeUniverse = [];
|
|
180847
181154
|
try {
|
|
180848
181155
|
const nativeData = await this.info("meta");
|
|
@@ -180851,7 +181158,7 @@ class HyperliquidClient {
|
|
|
180851
181158
|
this.log.warn({ err }, "Native meta fetch failed \u2014 daemon starts degraded (HIP-3 may still load)");
|
|
180852
181159
|
}
|
|
180853
181160
|
const dexes = await this.listPerpDexes().catch(() => []);
|
|
180854
|
-
const dexResults = await
|
|
181161
|
+
const dexResults = await runWithConcurrency(dexes, 4, (d2) => this.info("meta", { dex: d2.name }));
|
|
180855
181162
|
let merged = [...nativeUniverse];
|
|
180856
181163
|
const nativeNames = nativeUniverse.map((a) => a.name);
|
|
180857
181164
|
this.dexUniverses.set("", nativeNames);
|
|
@@ -180865,7 +181172,7 @@ class HyperliquidClient {
|
|
|
180865
181172
|
this.log.warn({ err: result.reason, dex: dexes[i].name }, "HIP-3 dex meta fetch failed \u2014 skipping");
|
|
180866
181173
|
}
|
|
180867
181174
|
}
|
|
180868
|
-
this.
|
|
181175
|
+
this.assets = merged.map((a) => a.isDelisted ? { symbol: a.name, isDelisted: true } : { symbol: a.name });
|
|
180869
181176
|
merged.forEach((a, idx) => {
|
|
180870
181177
|
const key = this.resolveSymbol(a.name);
|
|
180871
181178
|
this.assetMap.set(key, idx);
|
|
@@ -180875,10 +181182,63 @@ class HyperliquidClient {
|
|
|
180875
181182
|
}
|
|
180876
181183
|
});
|
|
180877
181184
|
this.metaLoaded = true;
|
|
181185
|
+
this.log.info({ dexCount: dexes.length }, "meta rebuild complete");
|
|
180878
181186
|
}
|
|
180879
181187
|
getMaxLeverage(symbol5) {
|
|
180880
181188
|
return this.maxLeverage.get(this.resolveSymbol(symbol5));
|
|
180881
181189
|
}
|
|
181190
|
+
getAllAssetNames() {
|
|
181191
|
+
return this.assets.map((a) => a.symbol);
|
|
181192
|
+
}
|
|
181193
|
+
getAllAssets() {
|
|
181194
|
+
return this.assets;
|
|
181195
|
+
}
|
|
181196
|
+
isKnownSymbol(symbol5) {
|
|
181197
|
+
return this.assetMap.has(this.resolveSymbol(symbol5));
|
|
181198
|
+
}
|
|
181199
|
+
getDexUniverses() {
|
|
181200
|
+
return this.dexUniverses;
|
|
181201
|
+
}
|
|
181202
|
+
async getSubscriptionClient() {
|
|
181203
|
+
if (this.wsSubClient !== null)
|
|
181204
|
+
return this.wsSubClient;
|
|
181205
|
+
if (this.wsLifecycle !== null)
|
|
181206
|
+
return this.wsLifecycle;
|
|
181207
|
+
this.wsLifecycle = (async () => {
|
|
181208
|
+
if (this.wsDisposed)
|
|
181209
|
+
throw new Error("trading client disposed");
|
|
181210
|
+
const transport = new WebSocketTransport({ isTestnet: this.testnet });
|
|
181211
|
+
const client4 = new SubscriptionClient({ transport });
|
|
181212
|
+
if (this.wsDisposed) {
|
|
181213
|
+
await transport.close().catch(() => {});
|
|
181214
|
+
throw new Error("trading client disposed");
|
|
181215
|
+
}
|
|
181216
|
+
this.wsTransport = transport;
|
|
181217
|
+
this.wsSubClient = client4;
|
|
181218
|
+
return client4;
|
|
181219
|
+
})().finally(() => {
|
|
181220
|
+
this.wsLifecycle = null;
|
|
181221
|
+
});
|
|
181222
|
+
return this.wsLifecycle;
|
|
181223
|
+
}
|
|
181224
|
+
async subscribeAllDexsAssetCtxs(listener) {
|
|
181225
|
+
const client4 = await this.getSubscriptionClient();
|
|
181226
|
+
const sub = await client4.allDexsAssetCtxs((e2) => {
|
|
181227
|
+
listener({ ctxs: e2.ctxs });
|
|
181228
|
+
});
|
|
181229
|
+
return { unsubscribe: () => sub.unsubscribe() };
|
|
181230
|
+
}
|
|
181231
|
+
async closeWs() {
|
|
181232
|
+
this.wsDisposed = true;
|
|
181233
|
+
if (this.wsTransport !== null) {
|
|
181234
|
+
try {
|
|
181235
|
+
await this.wsTransport.close();
|
|
181236
|
+
} catch {}
|
|
181237
|
+
}
|
|
181238
|
+
this.wsTransport = null;
|
|
181239
|
+
this.wsSubClient = null;
|
|
181240
|
+
this.wsLifecycle = null;
|
|
181241
|
+
}
|
|
180882
181242
|
resolveSymbol(symbol5) {
|
|
180883
181243
|
const { dex, name } = parseDex(symbol5);
|
|
180884
181244
|
if (dex !== null) {
|
|
@@ -180937,52 +181297,17 @@ class HyperliquidClient {
|
|
|
180937
181297
|
async getPositions(address) {
|
|
180938
181298
|
const user = address ?? this.defaultAddress;
|
|
180939
181299
|
const data = await this.info("clearinghouseState", { user });
|
|
180940
|
-
|
|
180941
|
-
for (const ap of data.assetPositions ?? []) {
|
|
180942
|
-
const pos = ap.position;
|
|
180943
|
-
const szi = parseFloat(pos.szi);
|
|
180944
|
-
if (szi === 0)
|
|
180945
|
-
continue;
|
|
180946
|
-
const entryPx = parseFloat(pos.entryPx);
|
|
180947
|
-
const markPx = pos.positionValue ? Math.abs(parseFloat(pos.positionValue) / szi) : entryPx;
|
|
180948
|
-
const upnl = parseFloat(pos.unrealizedPnl);
|
|
180949
|
-
const margin = parseFloat(pos.marginUsed);
|
|
180950
|
-
positions.push({
|
|
180951
|
-
symbol: pos.coin,
|
|
180952
|
-
side: szi > 0 ? "long" : "short",
|
|
180953
|
-
size: Math.abs(szi),
|
|
180954
|
-
entryPrice: entryPx,
|
|
180955
|
-
markPrice: markPx,
|
|
180956
|
-
liquidationPrice: pos.liquidationPx && pos.liquidationPx !== "0.0" && parseFloat(pos.liquidationPx) > 0 ? parseFloat(pos.liquidationPx) : null,
|
|
180957
|
-
unrealizedPnl: upnl,
|
|
180958
|
-
unrealizedPnlPct: parseFloat(pos.returnOnEquity ?? "0") * 100,
|
|
180959
|
-
leverage: parseFloat(pos.leverage?.value ?? "1"),
|
|
180960
|
-
marginMode: pos.leverage?.type === "isolated" ? "isolated" : "cross",
|
|
180961
|
-
margin
|
|
180962
|
-
});
|
|
180963
|
-
}
|
|
180964
|
-
return positions;
|
|
181300
|
+
return mapPositionsFromRaw(data);
|
|
180965
181301
|
}
|
|
180966
181302
|
async getOpenOrders(address) {
|
|
180967
181303
|
const user = address ?? this.defaultAddress;
|
|
180968
181304
|
const data = await this.info("frontendOpenOrders", { user });
|
|
180969
|
-
return data.map(
|
|
180970
|
-
orderId: String(o10.oid),
|
|
180971
|
-
symbol: o10.coin,
|
|
180972
|
-
side: o10.side === "B" ? "buy" : "sell",
|
|
180973
|
-
orderType: o10.orderType ?? "Limit",
|
|
180974
|
-
price: o10.limitPx ? parseFloat(o10.limitPx) : null,
|
|
180975
|
-
triggerPrice: o10.triggerPx && o10.triggerPx !== "0.0" ? parseFloat(o10.triggerPx) : null,
|
|
180976
|
-
size: parseFloat(o10.sz),
|
|
180977
|
-
filled: parseFloat(o10.origSz ?? o10.sz) - parseFloat(o10.sz),
|
|
180978
|
-
reduceOnly: o10.reduceOnly ?? false,
|
|
180979
|
-
timestamp: o10.timestamp ?? Date.now()
|
|
180980
|
-
}));
|
|
181305
|
+
return data.map(mapOpenOrderFromRaw);
|
|
180981
181306
|
}
|
|
180982
181307
|
async getFills(address, limit2 = 20) {
|
|
180983
181308
|
const user = address ?? this.defaultAddress;
|
|
180984
181309
|
const data = await this.info("userFills", { user });
|
|
180985
|
-
return data.slice(0, limit2).map(
|
|
181310
|
+
return data.slice(0, limit2).map(mapFill);
|
|
180986
181311
|
}
|
|
180987
181312
|
async getFillsByTime(address, startTime, endTime) {
|
|
180988
181313
|
const user = address ?? this.defaultAddress;
|
|
@@ -180990,7 +181315,7 @@ class HyperliquidClient {
|
|
|
180990
181315
|
if (endTime !== undefined)
|
|
180991
181316
|
params.endTime = endTime;
|
|
180992
181317
|
const data = await this.info("userFillsByTime", params);
|
|
180993
|
-
return data.map(
|
|
181318
|
+
return data.map(mapFill);
|
|
180994
181319
|
}
|
|
180995
181320
|
async getHistoricalOrders(address, startTime) {
|
|
180996
181321
|
const user = address ?? this.defaultAddress;
|
|
@@ -181018,7 +181343,7 @@ class HyperliquidClient {
|
|
|
181018
181343
|
async getAllTickers() {
|
|
181019
181344
|
await this.ensureMeta();
|
|
181020
181345
|
const tickers = [];
|
|
181021
|
-
const nativeData = await this.info("metaAndAssetCtxs");
|
|
181346
|
+
const nativeData = await this.info("metaAndAssetCtxs", {}, { cache: true });
|
|
181022
181347
|
const nativeCtxs = nativeData[1] ?? [];
|
|
181023
181348
|
const responseNativeUniverse = nativeData[0]?.universe ?? [];
|
|
181024
181349
|
const nativeNames = responseNativeUniverse.length > 0 ? responseNativeUniverse.map((a) => a.name) : this.dexUniverses.get("") ?? [];
|
|
@@ -181029,7 +181354,7 @@ class HyperliquidClient {
|
|
|
181029
181354
|
tickers.push(ctxToTicker(nativeCtxs[i], name));
|
|
181030
181355
|
}
|
|
181031
181356
|
const dexes = await this.listPerpDexes().catch(() => []);
|
|
181032
|
-
const dexCtxResults = await
|
|
181357
|
+
const dexCtxResults = await runWithConcurrency(dexes, 4, (d2) => this.info("metaAndAssetCtxs", { dex: d2.name }, { cache: true }));
|
|
181033
181358
|
for (let i = 0;i < dexCtxResults.length; i++) {
|
|
181034
181359
|
const r2 = dexCtxResults[i];
|
|
181035
181360
|
if (r2.status !== "fulfilled") {
|
|
@@ -181077,7 +181402,8 @@ class HyperliquidClient {
|
|
|
181077
181402
|
"1w": 604800000
|
|
181078
181403
|
};
|
|
181079
181404
|
const ms2 = intervalMs[interval] ?? 3600000;
|
|
181080
|
-
const
|
|
181405
|
+
const QUANTISE_MS = 3000;
|
|
181406
|
+
const endTime = Math.floor(Date.now() / QUANTISE_MS) * QUANTISE_MS;
|
|
181081
181407
|
const startTime = endTime - limit2 * ms2;
|
|
181082
181408
|
let timer;
|
|
181083
181409
|
try {
|
|
@@ -181085,7 +181411,7 @@ class HyperliquidClient {
|
|
|
181085
181411
|
timer = setTimeout(() => reject(new Error("Hyperliquid klines timeout (10s)")), 1e4);
|
|
181086
181412
|
});
|
|
181087
181413
|
const data = await Promise.race([
|
|
181088
|
-
this.info("candleSnapshot", { req: { coin: resolved, interval, startTime, endTime } }),
|
|
181414
|
+
this.info("candleSnapshot", { req: { coin: resolved, interval, startTime, endTime } }, { cache: true }),
|
|
181089
181415
|
timeout
|
|
181090
181416
|
]);
|
|
181091
181417
|
return (data ?? []).slice(-limit2).map((c2) => ({
|
|
@@ -181118,12 +181444,16 @@ class HyperliquidClient {
|
|
|
181118
181444
|
const decimals = this.szDecimals.get(resolved) ?? 0;
|
|
181119
181445
|
return size2.toFixed(decimals);
|
|
181120
181446
|
}
|
|
181121
|
-
formatPrice(price) {
|
|
181122
|
-
|
|
181447
|
+
formatPrice(symbol5, price) {
|
|
181448
|
+
const resolved = this.resolveSymbol(symbol5);
|
|
181449
|
+
const szDecimals = this.szDecimals.get(resolved) ?? 0;
|
|
181450
|
+
const maxDecimals = Math.max(0, 6 - szDecimals);
|
|
181451
|
+
const trimmed = parseFloat(price.toPrecision(5));
|
|
181452
|
+
return parseFloat(trimmed.toFixed(maxDecimals)).toString();
|
|
181123
181453
|
}
|
|
181124
|
-
slippagePrice(midPrice, isBuy, slippagePct) {
|
|
181454
|
+
slippagePrice(symbol5, midPrice, isBuy, slippagePct) {
|
|
181125
181455
|
const factor = isBuy ? 1 + slippagePct / 100 : 1 - slippagePct / 100;
|
|
181126
|
-
return this.formatPrice(midPrice * factor);
|
|
181456
|
+
return this.formatPrice(symbol5, midPrice * factor);
|
|
181127
181457
|
}
|
|
181128
181458
|
async placeOrder(params) {
|
|
181129
181459
|
const ex = this.requireExchange();
|
|
@@ -181139,42 +181469,42 @@ class HyperliquidClient {
|
|
|
181139
181469
|
switch (params.orderType) {
|
|
181140
181470
|
case "market": {
|
|
181141
181471
|
const ticker = await this.getTicker(params.symbol);
|
|
181142
|
-
price = this.slippagePrice(ticker.midPrice, isBuy, params.slippagePct ?? 0.5);
|
|
181472
|
+
price = this.slippagePrice(params.symbol, ticker.midPrice, isBuy, params.slippagePct ?? 0.5);
|
|
181143
181473
|
orderType = { limit: { tif: "Ioc" } };
|
|
181144
181474
|
break;
|
|
181145
181475
|
}
|
|
181146
181476
|
case "limit": {
|
|
181147
181477
|
if (!params.price)
|
|
181148
181478
|
throw new Error("Limit order requires price");
|
|
181149
|
-
price = this.formatPrice(params.price);
|
|
181479
|
+
price = this.formatPrice(params.symbol, params.price);
|
|
181150
181480
|
orderType = { limit: { tif: params.tif ?? "Gtc" } };
|
|
181151
181481
|
break;
|
|
181152
181482
|
}
|
|
181153
181483
|
case "stop_market": {
|
|
181154
181484
|
if (!params.price)
|
|
181155
181485
|
throw new Error("Stop market order requires trigger price");
|
|
181156
|
-
price = this.formatPrice(params.price);
|
|
181486
|
+
price = this.formatPrice(params.symbol, params.price);
|
|
181157
181487
|
orderType = { trigger: { isMarket: true, triggerPx: price, tpsl: "sl" } };
|
|
181158
181488
|
break;
|
|
181159
181489
|
}
|
|
181160
181490
|
case "stop_limit": {
|
|
181161
181491
|
if (!params.price)
|
|
181162
181492
|
throw new Error("Stop limit order requires trigger price");
|
|
181163
|
-
price = this.formatPrice(params.price);
|
|
181493
|
+
price = this.formatPrice(params.symbol, params.price);
|
|
181164
181494
|
orderType = { trigger: { isMarket: false, triggerPx: price, tpsl: "sl" } };
|
|
181165
181495
|
break;
|
|
181166
181496
|
}
|
|
181167
181497
|
case "take_profit": {
|
|
181168
181498
|
if (!params.price)
|
|
181169
181499
|
throw new Error("Take profit order requires trigger price");
|
|
181170
|
-
price = this.formatPrice(params.price);
|
|
181500
|
+
price = this.formatPrice(params.symbol, params.price);
|
|
181171
181501
|
orderType = { trigger: { isMarket: true, triggerPx: price, tpsl: "tp" } };
|
|
181172
181502
|
break;
|
|
181173
181503
|
}
|
|
181174
181504
|
case "take_profit_limit": {
|
|
181175
181505
|
if (!params.price)
|
|
181176
181506
|
throw new Error("Take profit limit order requires price");
|
|
181177
|
-
price = this.formatPrice(params.price);
|
|
181507
|
+
price = this.formatPrice(params.symbol, params.price);
|
|
181178
181508
|
orderType = { trigger: { isMarket: false, triggerPx: price, tpsl: "tp" } };
|
|
181179
181509
|
break;
|
|
181180
181510
|
}
|
|
@@ -181902,6 +182232,24 @@ class PaperTradingClient {
|
|
|
181902
182232
|
getMaxLeverage(symbol5) {
|
|
181903
182233
|
return this.marketClient.getMaxLeverage(symbol5);
|
|
181904
182234
|
}
|
|
182235
|
+
getAllAssetNames() {
|
|
182236
|
+
return this.marketClient.getAllAssetNames();
|
|
182237
|
+
}
|
|
182238
|
+
isKnownSymbol(symbol5) {
|
|
182239
|
+
return this.marketClient.isKnownSymbol(symbol5);
|
|
182240
|
+
}
|
|
182241
|
+
getAllAssets() {
|
|
182242
|
+
return this.marketClient.getAllAssets();
|
|
182243
|
+
}
|
|
182244
|
+
getDexUniverses() {
|
|
182245
|
+
return this.marketClient.getDexUniverses();
|
|
182246
|
+
}
|
|
182247
|
+
subscribeAllDexsAssetCtxs(_listener) {
|
|
182248
|
+
return Promise.resolve(NOOP_SUB);
|
|
182249
|
+
}
|
|
182250
|
+
closeWs() {
|
|
182251
|
+
return Promise.resolve();
|
|
182252
|
+
}
|
|
181905
182253
|
getBalance(_address) {
|
|
181906
182254
|
return this.engine.getBalance();
|
|
181907
182255
|
}
|
|
@@ -181948,8 +182296,10 @@ class PaperTradingClient {
|
|
|
181948
182296
|
this.engine.reset(newBalance);
|
|
181949
182297
|
}
|
|
181950
182298
|
}
|
|
182299
|
+
var NOOP_SUB;
|
|
181951
182300
|
var init_client7 = __esm(() => {
|
|
181952
182301
|
init_engine();
|
|
182302
|
+
NOOP_SUB = { unsubscribe: async () => {} };
|
|
181953
182303
|
});
|
|
181954
182304
|
|
|
181955
182305
|
// src/services/intel.ts
|
|
@@ -182234,7 +182584,7 @@ var init_alert_rules = __esm(() => {
|
|
|
182234
182584
|
|
|
182235
182585
|
// src/services/notifications.ts
|
|
182236
182586
|
function isKind(raw) {
|
|
182237
|
-
return raw === "price_target" || raw === "liquidation_risk" || raw === "position_closed" || raw === "tp_hit" || raw === "sl_hit" || raw === "order_filled" || raw === "order_canceled" || raw === "proactive";
|
|
182587
|
+
return raw === "price_target" || raw === "liquidation_risk" || raw === "position_closed" || raw === "tp_hit" || raw === "sl_hit" || raw === "order_filled" || raw === "order_canceled" || raw === "news" || raw === "proactive";
|
|
182238
182588
|
}
|
|
182239
182589
|
function rowToNotification(r2) {
|
|
182240
182590
|
let payload;
|
|
@@ -182304,8 +182654,8 @@ class NotificationsService {
|
|
|
182304
182654
|
// src/services/price-cache.ts
|
|
182305
182655
|
class PriceCache {
|
|
182306
182656
|
prices = new Map;
|
|
182307
|
-
set(symbol5, price) {
|
|
182308
|
-
this.prices.set(symbol5, { price, timestamp: Date.now() });
|
|
182657
|
+
set(symbol5, price, prevDayPrice) {
|
|
182658
|
+
this.prices.set(symbol5, { price, timestamp: Date.now(), prevDayPrice });
|
|
182309
182659
|
}
|
|
182310
182660
|
get(symbol5, maxAgeMs = 30000) {
|
|
182311
182661
|
const entry = this.prices.get(symbol5);
|
|
@@ -182905,6 +183255,23 @@ class NewsService {
|
|
|
182905
183255
|
];
|
|
182906
183256
|
return this.db.prepare(sql).all(...params).map((r2) => mapRow(r2));
|
|
182907
183257
|
}
|
|
183258
|
+
listRecentRelevant(sinceTs, limit2 = 20) {
|
|
183259
|
+
const sql = `
|
|
183260
|
+
SELECT id, source_id, external_id, url, title, snippet, image_url, coins,
|
|
183261
|
+
importance, published_at, fetched_at, expires_at, full_summary,
|
|
183262
|
+
ai_relevant, ai_duplicate_of
|
|
183263
|
+
FROM articles
|
|
183264
|
+
WHERE ai_relevant = 1
|
|
183265
|
+
AND full_summary IS NOT NULL
|
|
183266
|
+
AND ai_duplicate_of IS NULL
|
|
183267
|
+
AND dismissed_at IS NULL
|
|
183268
|
+
AND expires_at > unixepoch()
|
|
183269
|
+
AND published_at > ?
|
|
183270
|
+
ORDER BY published_at DESC, id DESC
|
|
183271
|
+
LIMIT ?
|
|
183272
|
+
`;
|
|
183273
|
+
return this.db.prepare(sql).all(sinceTs, limit2).map((r2) => mapRow(r2));
|
|
183274
|
+
}
|
|
182908
183275
|
searchArticles(opts = {}) {
|
|
182909
183276
|
const limit2 = Math.min(opts.limit ?? 50, 100);
|
|
182910
183277
|
const conditions = ["ai_duplicate_of IS NULL"];
|
|
@@ -183640,8 +184007,90 @@ class PreferenceStore {
|
|
|
183640
184007
|
else
|
|
183641
184008
|
this.set(NEWS_FILTER_PROMPT_KEY, prompt);
|
|
183642
184009
|
}
|
|
184010
|
+
getTimezone() {
|
|
184011
|
+
return this.get(USER_TIMEZONE_KEY);
|
|
184012
|
+
}
|
|
184013
|
+
setTimezone(tz) {
|
|
184014
|
+
if (tz.length === 0)
|
|
184015
|
+
this.delete(USER_TIMEZONE_KEY);
|
|
184016
|
+
else
|
|
184017
|
+
this.set(USER_TIMEZONE_KEY, tz);
|
|
184018
|
+
}
|
|
184019
|
+
}
|
|
184020
|
+
var TWEET_FILTER_PROMPT_KEY = "tweets.filter_prompt", NEWS_FILTER_PROMPT_KEY = "news.filter_prompt", USER_TIMEZONE_KEY = "user.timezone";
|
|
184021
|
+
|
|
184022
|
+
// src/scheduler/defaults.ts
|
|
184023
|
+
function detectUserTimezone() {
|
|
184024
|
+
try {
|
|
184025
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
184026
|
+
} catch {
|
|
184027
|
+
return "UTC";
|
|
184028
|
+
}
|
|
184029
|
+
}
|
|
184030
|
+
function buildBuiltInJobs(tz) {
|
|
184031
|
+
return [
|
|
184032
|
+
{
|
|
184033
|
+
name: "morning-briefing",
|
|
184034
|
+
schedule: { kind: "cron", expr: "0 8 * * *", tz },
|
|
184035
|
+
message: BRIEFING_PROMPT,
|
|
184036
|
+
deliver: true
|
|
184037
|
+
},
|
|
184038
|
+
{
|
|
184039
|
+
name: "evening-recap",
|
|
184040
|
+
schedule: { kind: "cron", expr: "0 21 * * *", tz },
|
|
184041
|
+
message: RECAP_PROMPT,
|
|
184042
|
+
deliver: true
|
|
184043
|
+
}
|
|
184044
|
+
];
|
|
183643
184045
|
}
|
|
183644
|
-
var
|
|
184046
|
+
var BRIEFING_PROMPT, RECAP_PROMPT;
|
|
184047
|
+
var init_defaults = __esm(() => {
|
|
184048
|
+
BRIEFING_PROMPT = "Run the morning briefing. Call tools to fetch the latest data: open " + "positions, recent fills / trade history, watchlist, news, and market " + "signals (funding, whale activity, fear & greed). Summarize in under 15 " + "sentences, in the language the user has been chatting in.";
|
|
184049
|
+
RECAP_PROMPT = "Run the end-of-day recap. Call tools to fetch today's trade history and " + "current open positions. Summarize today's PnL, position changes (opened, " + "closed, scaled), and one notable market note. Brief one-liner if I had no " + "activity. Reply in the language the user has been chatting in.";
|
|
184050
|
+
});
|
|
184051
|
+
|
|
184052
|
+
// src/services/timezone.ts
|
|
184053
|
+
function validateTimezone(input) {
|
|
184054
|
+
if (typeof input !== "string") {
|
|
184055
|
+
return { ok: false, error: "Timezone must be a string" };
|
|
184056
|
+
}
|
|
184057
|
+
const trimmed = input.trim();
|
|
184058
|
+
if (!trimmed) {
|
|
184059
|
+
return { ok: false, error: "Timezone cannot be empty" };
|
|
184060
|
+
}
|
|
184061
|
+
if (trimmed.includes("\x00")) {
|
|
184062
|
+
return { ok: false, error: "Timezone contains invalid characters" };
|
|
184063
|
+
}
|
|
184064
|
+
if (trimmed.length > 64) {
|
|
184065
|
+
return { ok: false, error: "Timezone exceeds maximum length" };
|
|
184066
|
+
}
|
|
184067
|
+
try {
|
|
184068
|
+
const resolved = new Intl.DateTimeFormat(undefined, { timeZone: trimmed }).resolvedOptions().timeZone;
|
|
184069
|
+
return { ok: true, tz: resolved };
|
|
184070
|
+
} catch {
|
|
184071
|
+
return { ok: false, error: "Unknown timezone" };
|
|
184072
|
+
}
|
|
184073
|
+
}
|
|
184074
|
+
function detectHostTimezone() {
|
|
184075
|
+
return detectUserTimezone();
|
|
184076
|
+
}
|
|
184077
|
+
function createTimezoneService(prefs) {
|
|
184078
|
+
return {
|
|
184079
|
+
get() {
|
|
184080
|
+
return prefs.getTimezone() ?? "UTC";
|
|
184081
|
+
},
|
|
184082
|
+
set(input) {
|
|
184083
|
+
const result = validateTimezone(input);
|
|
184084
|
+
if (!result.ok)
|
|
184085
|
+
return result;
|
|
184086
|
+
prefs.setTimezone(result.tz);
|
|
184087
|
+
return result;
|
|
184088
|
+
}
|
|
184089
|
+
};
|
|
184090
|
+
}
|
|
184091
|
+
var init_timezone = __esm(() => {
|
|
184092
|
+
init_defaults();
|
|
184093
|
+
});
|
|
183645
184094
|
|
|
183646
184095
|
// src/services/x-follows.ts
|
|
183647
184096
|
function sleep8(ms2) {
|
|
@@ -187422,11 +187871,8 @@ function createAdvancedTradingTools(hl2, watchlist, alerts, priceCache) {
|
|
|
187422
187871
|
async execute(_toolCallId, params) {
|
|
187423
187872
|
try {
|
|
187424
187873
|
const upper2 = params.symbol.toUpperCase();
|
|
187425
|
-
|
|
187426
|
-
await hl2.getTicker(upper2);
|
|
187427
|
-
} catch {
|
|
187874
|
+
if (!hl2.isKnownSymbol(upper2))
|
|
187428
187875
|
return errorResult(`Symbol ${upper2} not found on Hyperliquid`);
|
|
187429
|
-
}
|
|
187430
187876
|
const item = await watchlist.add(upper2, params.notes);
|
|
187431
187877
|
return textResult(`Added ${item.symbol} to watchlist.${item.notes ? ` Notes: ${item.notes}` : ""}`);
|
|
187432
187878
|
} catch (e2) {
|
|
@@ -187493,11 +187939,8 @@ function createAdvancedTradingTools(hl2, watchlist, alerts, priceCache) {
|
|
|
187493
187939
|
try {
|
|
187494
187940
|
const cond = params.condition;
|
|
187495
187941
|
const upper2 = params.symbol.toUpperCase();
|
|
187496
|
-
|
|
187497
|
-
await hl2.getTicker(upper2);
|
|
187498
|
-
} catch {
|
|
187942
|
+
if (!hl2.isKnownSymbol(upper2))
|
|
187499
187943
|
return errorResult(`Symbol ${upper2} not found on Hyperliquid.`);
|
|
187500
|
-
}
|
|
187501
187944
|
const lookup2 = await getCurrentPrice(hl2, priceCache, upper2);
|
|
187502
187945
|
if (lookup2.price !== undefined) {
|
|
187503
187946
|
const past = cond === "above" ? lookup2.price >= params.price : lookup2.price <= params.price;
|
|
@@ -188384,7 +188827,7 @@ class Runner {
|
|
|
188384
188827
|
}
|
|
188385
188828
|
async call(opts) {
|
|
188386
188829
|
const next = this.inFlight.then(async () => {
|
|
188387
|
-
const tools = this.registry.
|
|
188830
|
+
const tools = this.registry.taskAgentTools();
|
|
188388
188831
|
this.agent.state.tools = tools;
|
|
188389
188832
|
for (const tool2 of tools) {
|
|
188390
188833
|
if (isOriginAware(tool2))
|
|
@@ -188576,7 +189019,9 @@ function emptySnapshot() {
|
|
|
188576
189019
|
openOrderIds: [],
|
|
188577
189020
|
lastRestSyncAtMs: 0,
|
|
188578
189021
|
recentCancelOids: [],
|
|
188579
|
-
recentEmittedFillIds: []
|
|
189022
|
+
recentEmittedFillIds: [],
|
|
189023
|
+
recentEmittedNewsIds: [],
|
|
189024
|
+
lastNewsScanTs: 0
|
|
188580
189025
|
};
|
|
188581
189026
|
}
|
|
188582
189027
|
|
|
@@ -188601,7 +189046,9 @@ class ObserverStateStore {
|
|
|
188601
189046
|
openOrderIds: Array.isArray(parsed.openOrderIds) ? parsed.openOrderIds : [],
|
|
188602
189047
|
lastRestSyncAtMs: typeof parsed.lastRestSyncAtMs === "number" ? parsed.lastRestSyncAtMs : 0,
|
|
188603
189048
|
recentCancelOids: Array.isArray(parsed.recentCancelOids) ? parsed.recentCancelOids.filter((o10) => typeof o10 === "string") : [],
|
|
188604
|
-
recentEmittedFillIds: Array.isArray(parsed.recentEmittedFillIds) ? parsed.recentEmittedFillIds.filter((o10) => typeof o10 === "string") : []
|
|
189049
|
+
recentEmittedFillIds: Array.isArray(parsed.recentEmittedFillIds) ? parsed.recentEmittedFillIds.filter((o10) => typeof o10 === "string") : [],
|
|
189050
|
+
recentEmittedNewsIds: Array.isArray(parsed.recentEmittedNewsIds) ? parsed.recentEmittedNewsIds.filter((o10) => typeof o10 === "string") : [],
|
|
189051
|
+
lastNewsScanTs: typeof parsed.lastNewsScanTs === "number" ? parsed.lastNewsScanTs : 0
|
|
188605
189052
|
};
|
|
188606
189053
|
} catch {
|
|
188607
189054
|
return emptySnapshot();
|
|
@@ -188614,7 +189061,7 @@ class ObserverStateStore {
|
|
|
188614
189061
|
this.write.run(KEY_SNAPSHOT, JSON.stringify(emptySnapshot()));
|
|
188615
189062
|
}
|
|
188616
189063
|
}
|
|
188617
|
-
var RECENT_CANCEL_OIDS_CAP = 500, RECENT_FILL_IDS_CAP = 500, KEY_SNAPSHOT = "snapshot";
|
|
189064
|
+
var RECENT_CANCEL_OIDS_CAP = 500, RECENT_FILL_IDS_CAP = 500, RECENT_NEWS_IDS_CAP = 200, KEY_SNAPSHOT = "snapshot";
|
|
188618
189065
|
|
|
188619
189066
|
// src/observer/detect/positions.ts
|
|
188620
189067
|
function posKey(symbol5, side) {
|
|
@@ -188902,6 +189349,36 @@ function detectCanceledOrders(input) {
|
|
|
188902
189349
|
return { events, emittedOids };
|
|
188903
189350
|
}
|
|
188904
189351
|
|
|
189352
|
+
// src/observer/detect/news.ts
|
|
189353
|
+
function detectNews(input) {
|
|
189354
|
+
const events = [];
|
|
189355
|
+
const emittedIds = [];
|
|
189356
|
+
const seenThisTick = new Set;
|
|
189357
|
+
for (const article of input.articles) {
|
|
189358
|
+
if (input.priorEmittedIds.has(article.id))
|
|
189359
|
+
continue;
|
|
189360
|
+
if (seenThisTick.has(article.id))
|
|
189361
|
+
continue;
|
|
189362
|
+
if (article.fullSummary === null)
|
|
189363
|
+
continue;
|
|
189364
|
+
events.push({
|
|
189365
|
+
type: "news",
|
|
189366
|
+
detectedAt: input.nowMs,
|
|
189367
|
+
articleId: article.id,
|
|
189368
|
+
title: article.title,
|
|
189369
|
+
summary: article.fullSummary,
|
|
189370
|
+
source: article.sourceId,
|
|
189371
|
+
url: article.url,
|
|
189372
|
+
publishedAt: article.publishedAt * 1000,
|
|
189373
|
+
importance: article.importance,
|
|
189374
|
+
coins: [...article.coins]
|
|
189375
|
+
});
|
|
189376
|
+
emittedIds.push(article.id);
|
|
189377
|
+
seenThisTick.add(article.id);
|
|
189378
|
+
}
|
|
189379
|
+
return { events, emittedIds };
|
|
189380
|
+
}
|
|
189381
|
+
|
|
188905
189382
|
// src/observer/diff.ts
|
|
188906
189383
|
function diffSnapshot(input) {
|
|
188907
189384
|
const positionsR = detectPositions({
|
|
@@ -188933,18 +189410,25 @@ function diffSnapshot(input) {
|
|
|
188933
189410
|
prices: input.prices,
|
|
188934
189411
|
nowMs: input.nowMs
|
|
188935
189412
|
});
|
|
189413
|
+
const newsR = detectNews({
|
|
189414
|
+
articles: input.articles,
|
|
189415
|
+
priorEmittedIds: new Set(input.prior.recentEmittedNewsIds),
|
|
189416
|
+
nowMs: input.nowMs
|
|
189417
|
+
});
|
|
188936
189418
|
return {
|
|
188937
189419
|
events: [
|
|
188938
189420
|
...positionsR.events,
|
|
188939
189421
|
...fillsR.events,
|
|
188940
189422
|
...fallbackEvents,
|
|
188941
189423
|
...cancelR.events,
|
|
188942
|
-
...priceTargetR.events
|
|
189424
|
+
...priceTargetR.events,
|
|
189425
|
+
...newsR.events
|
|
188943
189426
|
],
|
|
188944
189427
|
nextPositions: positionsR.nextPositions,
|
|
188945
189428
|
firedAlertIds: priceTargetR.firedIds,
|
|
188946
189429
|
emittedCancelOids: cancelR.emittedOids,
|
|
188947
|
-
emittedFillIds: fillsR.emittedFillIds
|
|
189430
|
+
emittedFillIds: fillsR.emittedFillIds,
|
|
189431
|
+
emittedNewsIds: newsR.emittedIds
|
|
188948
189432
|
};
|
|
188949
189433
|
}
|
|
188950
189434
|
var init_diff = () => {};
|
|
@@ -189060,7 +189544,8 @@ var init_judge2 = __esm(() => {
|
|
|
189060
189544
|
Type.Literal("order_canceled"),
|
|
189061
189545
|
Type.Literal("liquidation_risk"),
|
|
189062
189546
|
Type.Literal("pnl_snapshot"),
|
|
189063
|
-
Type.Literal("price_alert")
|
|
189547
|
+
Type.Literal("price_alert"),
|
|
189548
|
+
Type.Literal("news")
|
|
189064
189549
|
];
|
|
189065
189550
|
JudgeResponseSchema = Type.Object({
|
|
189066
189551
|
decision: Type.Union([Type.Literal("fire"), Type.Literal("silent")]),
|
|
@@ -189072,6 +189557,42 @@ var init_judge2 = __esm(() => {
|
|
|
189072
189557
|
});
|
|
189073
189558
|
});
|
|
189074
189559
|
|
|
189560
|
+
// src/observer/pnl-drift.ts
|
|
189561
|
+
function decidePnlDrift(input) {
|
|
189562
|
+
const { state, currentPnl, lastUserActivityMs, nowMs } = input;
|
|
189563
|
+
if (state.lastSnapshotPnl === null) {
|
|
189564
|
+
return { fire: false, nextSnapshotPnl: currentPnl, reason: "seed_baseline" };
|
|
189565
|
+
}
|
|
189566
|
+
const base = Math.max(Math.abs(state.lastSnapshotPnl), PNL_DRIFT_MIN_BASE);
|
|
189567
|
+
const deltaPct = (currentPnl - state.lastSnapshotPnl) / base;
|
|
189568
|
+
const absDeltaPct = Math.abs(deltaPct);
|
|
189569
|
+
if (absDeltaPct < PNL_DRIFT_THRESHOLD_PCT) {
|
|
189570
|
+
return { fire: false, nextSnapshotPnl: state.lastSnapshotPnl, reason: "below_threshold" };
|
|
189571
|
+
}
|
|
189572
|
+
const idleMs = lastUserActivityMs === null ? SENTINEL_MAX_IDLE_MS : nowMs - lastUserActivityMs;
|
|
189573
|
+
if (idleMs < PNL_DRIFT_IDLE_GATE_MS) {
|
|
189574
|
+
return { fire: false, nextSnapshotPnl: state.lastSnapshotPnl, reason: "user_active" };
|
|
189575
|
+
}
|
|
189576
|
+
if (state.lastSentAtMs !== null && nowMs - state.lastSentAtMs < PNL_DRIFT_COOLDOWN_MS) {
|
|
189577
|
+
return { fire: false, nextSnapshotPnl: state.lastSnapshotPnl, reason: "cooldown" };
|
|
189578
|
+
}
|
|
189579
|
+
const event2 = {
|
|
189580
|
+
type: "portfolio_pnl_drift",
|
|
189581
|
+
detectedAt: nowMs,
|
|
189582
|
+
fromPnl: state.lastSnapshotPnl,
|
|
189583
|
+
toPnl: currentPnl,
|
|
189584
|
+
deltaPct,
|
|
189585
|
+
idleMs
|
|
189586
|
+
};
|
|
189587
|
+
return { fire: true, event: event2, nextSnapshotPnl: currentPnl };
|
|
189588
|
+
}
|
|
189589
|
+
var PNL_DRIFT_THRESHOLD_PCT = 0.15, PNL_DRIFT_IDLE_GATE_MS, PNL_DRIFT_COOLDOWN_MS, PNL_DRIFT_MIN_BASE = 10, SENTINEL_MAX_IDLE_MS;
|
|
189590
|
+
var init_pnl_drift = __esm(() => {
|
|
189591
|
+
PNL_DRIFT_IDLE_GATE_MS = 2 * 60 * 60 * 1000;
|
|
189592
|
+
PNL_DRIFT_COOLDOWN_MS = 6 * 60 * 60 * 1000;
|
|
189593
|
+
SENTINEL_MAX_IDLE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
189594
|
+
});
|
|
189595
|
+
|
|
189075
189596
|
// src/channels/base.ts
|
|
189076
189597
|
class BaseChannel {
|
|
189077
189598
|
config;
|
|
@@ -193876,6 +194397,7 @@ function stripCustomTags(text) {
|
|
|
193876
194397
|
out = out.replace(/<verdict\s*[^>]*>([\s\S]*?)<\/verdict>/gi, `${SENTINEL_I_OPEN}$1${SENTINEL_I_CLOSE}`);
|
|
193877
194398
|
out = out.replace(/(\n)(\x00I_OPEN\x00(?:\uD83D\uDC02 |\uD83D\uDC3B |\u3030\uFE0F )?)/g, `$1
|
|
193878
194399
|
$2`);
|
|
194400
|
+
out = out.replace(/<ask_user_question\s*>([\s\S]*?)<\/ask_user_question>/gi, (_full, inner) => formatAskFallback(inner));
|
|
193879
194401
|
const chartHint = (attrs) => {
|
|
193880
194402
|
const symMatch = /\bsymbol\s*=\s*"([^"]+)"/i.exec(attrs);
|
|
193881
194403
|
const intMatch = /\binterval\s*=\s*"([^"]+)"/i.exec(attrs);
|
|
@@ -193896,6 +194418,43 @@ $2`);
|
|
|
193896
194418
|
out = out.replace(/<[a-zA-Z][^>]*$/g, "");
|
|
193897
194419
|
return out;
|
|
193898
194420
|
}
|
|
194421
|
+
function formatAskFallback(inner) {
|
|
194422
|
+
const questionRe = /<question>([\s\S]*?)<\/question>/gi;
|
|
194423
|
+
const titleRe = /<title>([\s\S]*?)<\/title>/i;
|
|
194424
|
+
const optionsRe = /<options>([\s\S]*?)<\/options>/i;
|
|
194425
|
+
const optionRe = /<option>([\s\S]*?)<\/option>/gi;
|
|
194426
|
+
const lines = [];
|
|
194427
|
+
let m6;
|
|
194428
|
+
let i = 1;
|
|
194429
|
+
while ((m6 = questionRe.exec(inner)) !== null) {
|
|
194430
|
+
const body = m6[1];
|
|
194431
|
+
const title = titleRe.exec(body)?.[1]?.trim();
|
|
194432
|
+
if (!title)
|
|
194433
|
+
continue;
|
|
194434
|
+
let entry = `${i}. ${title}`;
|
|
194435
|
+
const optionsBlock = optionsRe.exec(body)?.[1];
|
|
194436
|
+
if (optionsBlock) {
|
|
194437
|
+
const opts = [];
|
|
194438
|
+
optionRe.lastIndex = 0;
|
|
194439
|
+
let om2;
|
|
194440
|
+
while ((om2 = optionRe.exec(optionsBlock)) !== null) {
|
|
194441
|
+
const v4 = om2[1].trim();
|
|
194442
|
+
if (v4)
|
|
194443
|
+
opts.push(v4);
|
|
194444
|
+
}
|
|
194445
|
+
if (opts.length > 0)
|
|
194446
|
+
entry += `
|
|
194447
|
+
Options: ${opts.join(" / ")}`;
|
|
194448
|
+
}
|
|
194449
|
+
lines.push(entry);
|
|
194450
|
+
i++;
|
|
194451
|
+
}
|
|
194452
|
+
if (lines.length === 0)
|
|
194453
|
+
return "";
|
|
194454
|
+
return `
|
|
194455
|
+
${lines.join(`
|
|
194456
|
+
`)}`;
|
|
194457
|
+
}
|
|
193899
194458
|
var CHART_RE_PAIRED, CHART_RE_SELF;
|
|
193900
194459
|
var init_tags = __esm(() => {
|
|
193901
194460
|
CHART_RE_PAIRED = /<chart\s*([^>]*)>([\s\S]*?)<\/chart>/gi;
|
|
@@ -195305,6 +195864,7 @@ class ObserverLoop {
|
|
|
195305
195864
|
store;
|
|
195306
195865
|
cachedRest = null;
|
|
195307
195866
|
lastProactiveAtMs;
|
|
195867
|
+
pnlDriftState = { lastSnapshotPnl: null, lastSentAtMs: null };
|
|
195308
195868
|
constructor(deps) {
|
|
195309
195869
|
this.deps = deps;
|
|
195310
195870
|
this.store = new ObserverStateStore(deps.db);
|
|
@@ -195341,6 +195901,7 @@ class ObserverLoop {
|
|
|
195341
195901
|
const currentOpenOrderIds = rest4.openOrders.map((o10) => o10.orderId);
|
|
195342
195902
|
const prices = this.deps.priceCache.snapshot();
|
|
195343
195903
|
const alertRules = this.deps.alertRules.list();
|
|
195904
|
+
const articles = rest4.positions.length === 0 ? [] : this.deps.newsService.listRecentRelevant(newsScanSinceTs(prior.lastNewsScanTs, nowMs));
|
|
195344
195905
|
const diff = diffSnapshot({
|
|
195345
195906
|
prior,
|
|
195346
195907
|
positions: rest4.positions,
|
|
@@ -195350,19 +195911,42 @@ class ObserverLoop {
|
|
|
195350
195911
|
alertRules,
|
|
195351
195912
|
prices,
|
|
195352
195913
|
liqProgressThreshold: this.deps.config.liquidationProgressThreshold,
|
|
195914
|
+
articles,
|
|
195353
195915
|
nowMs
|
|
195354
195916
|
});
|
|
195355
195917
|
for (const id of diff.firedAlertIds) {
|
|
195356
195918
|
this.deps.alertRules.markFired(id, Math.floor(nowMs / 1000));
|
|
195357
195919
|
}
|
|
195358
195920
|
const gatedEvents = filterPnlSnapshots(diff.events, prior.positions, nowMs);
|
|
195921
|
+
if (rest4.positions.length === 0) {
|
|
195922
|
+
this.pnlDriftState = { lastSnapshotPnl: null, lastSentAtMs: null };
|
|
195923
|
+
} else {
|
|
195924
|
+
const totalUnrealizedPnl = rest4.positions.reduce((sum, p) => sum + p.unrealizedPnl, 0);
|
|
195925
|
+
const session = this.deps.sessionManager.getOrCreate(MAIN_SESSION_KEY);
|
|
195926
|
+
const lastUserActivityMs = session.lastActiveAt?.getTime() ?? null;
|
|
195927
|
+
const driftDecision = decidePnlDrift({
|
|
195928
|
+
state: this.pnlDriftState,
|
|
195929
|
+
currentPnl: totalUnrealizedPnl,
|
|
195930
|
+
lastUserActivityMs,
|
|
195931
|
+
nowMs
|
|
195932
|
+
});
|
|
195933
|
+
if (driftDecision.fire) {
|
|
195934
|
+
gatedEvents.push(driftDecision.event);
|
|
195935
|
+
}
|
|
195936
|
+
this.pnlDriftState = {
|
|
195937
|
+
lastSnapshotPnl: driftDecision.nextSnapshotPnl,
|
|
195938
|
+
lastSentAtMs: driftDecision.fire ? nowMs : this.pnlDriftState.lastSentAtMs
|
|
195939
|
+
};
|
|
195940
|
+
}
|
|
195359
195941
|
const nextSnapshot = {
|
|
195360
195942
|
positions: diff.nextPositions,
|
|
195361
195943
|
lastFillTimestamp: rest4.latestFillTimestamp,
|
|
195362
195944
|
openOrderIds: currentOpenOrderIds,
|
|
195363
195945
|
lastRestSyncAtMs: synced ? nowMs : prior.lastRestSyncAtMs,
|
|
195364
195946
|
recentCancelOids: mergeCancelOids(prior.recentCancelOids, diff.emittedCancelOids),
|
|
195365
|
-
recentEmittedFillIds: mergeBoundedIds(prior.recentEmittedFillIds, diff.emittedFillIds, RECENT_FILL_IDS_CAP)
|
|
195947
|
+
recentEmittedFillIds: mergeBoundedIds(prior.recentEmittedFillIds, diff.emittedFillIds, RECENT_FILL_IDS_CAP),
|
|
195948
|
+
recentEmittedNewsIds: mergeBoundedIds(prior.recentEmittedNewsIds, diff.emittedNewsIds, RECENT_NEWS_IDS_CAP),
|
|
195949
|
+
lastNewsScanTs: maxArticlePublishedAtSec(articles, prior.lastNewsScanTs)
|
|
195366
195950
|
};
|
|
195367
195951
|
const scanCounts = {
|
|
195368
195952
|
positions: rest4.positions.length,
|
|
@@ -195451,6 +196035,7 @@ class ObserverLoop {
|
|
|
195451
196035
|
alertRules: this.deps.alertRules.list(),
|
|
195452
196036
|
prices: this.deps.priceCache.snapshot(),
|
|
195453
196037
|
liqProgressThreshold: this.deps.config.liquidationProgressThreshold,
|
|
196038
|
+
articles: [],
|
|
195454
196039
|
nowMs
|
|
195455
196040
|
});
|
|
195456
196041
|
this.store.save({
|
|
@@ -195459,7 +196044,9 @@ class ObserverLoop {
|
|
|
195459
196044
|
openOrderIds: rest4.openOrders.map((o10) => o10.orderId),
|
|
195460
196045
|
lastRestSyncAtMs: syncDue ? nowMs : prior.lastRestSyncAtMs,
|
|
195461
196046
|
recentCancelOids: mergeCancelOids(prior.recentCancelOids, diff.emittedCancelOids),
|
|
195462
|
-
recentEmittedFillIds: mergeBoundedIds(prior.recentEmittedFillIds, diff.emittedFillIds, RECENT_FILL_IDS_CAP)
|
|
196047
|
+
recentEmittedFillIds: mergeBoundedIds(prior.recentEmittedFillIds, diff.emittedFillIds, RECENT_FILL_IDS_CAP),
|
|
196048
|
+
recentEmittedNewsIds: prior.recentEmittedNewsIds,
|
|
196049
|
+
lastNewsScanTs: prior.lastNewsScanTs
|
|
195463
196050
|
});
|
|
195464
196051
|
} catch (err) {
|
|
195465
196052
|
this.deps.logger.warn({ err }, "observer: baseline refresh failed under confirm gate");
|
|
@@ -195522,12 +196109,14 @@ class ObserverLoop {
|
|
|
195522
196109
|
const m6 = msg;
|
|
195523
196110
|
if (m6.role !== "user" && m6.role !== "assistant")
|
|
195524
196111
|
continue;
|
|
195525
|
-
if (!Array.isArray(m6.content))
|
|
195526
|
-
continue;
|
|
195527
196112
|
let text = "";
|
|
195528
|
-
|
|
195529
|
-
|
|
195530
|
-
|
|
196113
|
+
if (typeof m6.content === "string") {
|
|
196114
|
+
text = m6.content;
|
|
196115
|
+
} else if (Array.isArray(m6.content)) {
|
|
196116
|
+
for (const block of m6.content) {
|
|
196117
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
196118
|
+
text += block.text;
|
|
196119
|
+
}
|
|
195531
196120
|
}
|
|
195532
196121
|
}
|
|
195533
196122
|
if (text.trim().length === 0)
|
|
@@ -195597,22 +196186,38 @@ function mapKindFromEventType(t2) {
|
|
|
195597
196186
|
return "order_canceled";
|
|
195598
196187
|
case "order_filled":
|
|
195599
196188
|
return "order_filled";
|
|
196189
|
+
case "news":
|
|
196190
|
+
return "news";
|
|
195600
196191
|
default:
|
|
195601
196192
|
return "proactive";
|
|
195602
196193
|
}
|
|
195603
196194
|
}
|
|
195604
|
-
|
|
196195
|
+
function newsScanSinceTs(lastScanTs, nowMs) {
|
|
196196
|
+
const slidingFloor = Math.floor(nowMs / 1000) - NEWS_LOOKBACK_FLOOR_SEC;
|
|
196197
|
+
return lastScanTs > slidingFloor ? lastScanTs : slidingFloor;
|
|
196198
|
+
}
|
|
196199
|
+
function maxArticlePublishedAtSec(articles, prior) {
|
|
196200
|
+
let max = prior;
|
|
196201
|
+
for (const a of articles) {
|
|
196202
|
+
if (a.publishedAt > max)
|
|
196203
|
+
max = a.publishedAt;
|
|
196204
|
+
}
|
|
196205
|
+
return max;
|
|
196206
|
+
}
|
|
196207
|
+
var RECENT_CHAT_MAX = 20, MIN_PRICE_PCT_DELTA = 0.5, MIN_COOLDOWN_MS, NEWS_LOOKBACK_FLOOR_SEC;
|
|
195605
196208
|
var init_loop = __esm(() => {
|
|
195606
196209
|
init_diff();
|
|
195607
196210
|
init_judge2();
|
|
196211
|
+
init_pnl_drift();
|
|
195608
196212
|
init_channels();
|
|
195609
196213
|
init_types5();
|
|
195610
196214
|
MIN_COOLDOWN_MS = 60 * 60 * 1000;
|
|
196215
|
+
NEWS_LOOKBACK_FLOOR_SEC = 30 * 60;
|
|
195611
196216
|
});
|
|
195612
196217
|
|
|
195613
196218
|
// src/runtime.ts
|
|
195614
196219
|
import { join as join14 } from "path";
|
|
195615
|
-
import { existsSync as
|
|
196220
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync13, copyFileSync as copyFileSync2, readdirSync as readdirSync7 } from "fs";
|
|
195616
196221
|
async function createRuntime(options) {
|
|
195617
196222
|
const configPath = options.configPath ?? getConfigPath();
|
|
195618
196223
|
const rawConfig = loadConfig(configPath);
|
|
@@ -195652,12 +196257,14 @@ async function createRuntime(options) {
|
|
|
195652
196257
|
const skillsLoader = new SkillsLoader(workspaceDir, builtinSkillsDir);
|
|
195653
196258
|
const skillService = new SkillService(db2, skillsLoader);
|
|
195654
196259
|
skillService.syncState();
|
|
195655
|
-
const
|
|
196260
|
+
const preferenceStore = new PreferenceStore(db2, logger.child({ module: "prefs" }));
|
|
196261
|
+
const timezoneService = createTimezoneService(preferenceStore);
|
|
196262
|
+
const contextBuilder = new ContextBuilder({ workspaceDir, model: config3.model, getTimezone: () => timezoneService.get() }, memoryStore, skillsLoader);
|
|
195656
196263
|
contextBuilder.setDisabledSkillsProvider(() => skillService.getDisabledNames());
|
|
195657
|
-
const cronService = new CronService(
|
|
196264
|
+
const cronService = new CronService(db2);
|
|
195658
196265
|
const tools = createToolRegistry(security2, {
|
|
195659
196266
|
cronService,
|
|
195660
|
-
|
|
196267
|
+
timezoneService,
|
|
195661
196268
|
memoryStore,
|
|
195662
196269
|
logger: logger.child({ module: "tool" })
|
|
195663
196270
|
});
|
|
@@ -195668,7 +196275,6 @@ async function createRuntime(options) {
|
|
|
195668
196275
|
const watchlistService = new WatchlistService(db2);
|
|
195669
196276
|
const newsService = new NewsService(db2, watchlistService, credentials2, logger.child({ module: "news" }));
|
|
195670
196277
|
const tweetService = new TweetService(db2, logger.child({ module: "tweets" }));
|
|
195671
|
-
const preferenceStore = new PreferenceStore(db2, logger.child({ module: "prefs" }));
|
|
195672
196278
|
const xQueryIdCache = new XQueryIdCache;
|
|
195673
196279
|
const xFollowService = new XFollowService(db2, credentials2, xQueryIdCache, logger.child({ module: "x-follows" }));
|
|
195674
196280
|
const taIndicators = new TaIndicatorService(tradingClient);
|
|
@@ -195707,7 +196313,7 @@ async function createRuntime(options) {
|
|
|
195707
196313
|
eventBus,
|
|
195708
196314
|
logger,
|
|
195709
196315
|
customModelRegistry,
|
|
195710
|
-
confirmDeps: { getConfirmService: () => confirmServiceRef }
|
|
196316
|
+
confirmDeps: { getConfirmService: () => confirmServiceRef, approvalManager }
|
|
195711
196317
|
});
|
|
195712
196318
|
const taskAgent = createAgent({
|
|
195713
196319
|
config: config3,
|
|
@@ -195724,7 +196330,8 @@ async function createRuntime(options) {
|
|
|
195724
196330
|
logger: logger.child({ module: "task-agent" }),
|
|
195725
196331
|
customModelRegistry,
|
|
195726
196332
|
confirmDeps: { getConfirmService: () => null },
|
|
195727
|
-
bypassConfirm: true
|
|
196333
|
+
bypassConfirm: true,
|
|
196334
|
+
taskMode: true
|
|
195728
196335
|
});
|
|
195729
196336
|
const runner = new Runner(taskAgent, sessionManager, tools, logger.child({ module: "runner" }));
|
|
195730
196337
|
const rssDiscoveryService = new RssDiscoveryService(runner, logger.child({ module: "rss-discovery" }));
|
|
@@ -195791,7 +196398,7 @@ async function createRuntime(options) {
|
|
|
195791
196398
|
tools.register(t2);
|
|
195792
196399
|
contextBuilder.setTools(tools.all().map((t2) => ({ name: t2.name, description: t2.description })));
|
|
195793
196400
|
agent3.state.tools = tools.all();
|
|
195794
|
-
taskAgent.state.tools = tools.
|
|
196401
|
+
taskAgent.state.tools = tools.taskAgentTools();
|
|
195795
196402
|
setupClaudeCliProvider({
|
|
195796
196403
|
config: config3,
|
|
195797
196404
|
logger,
|
|
@@ -195831,6 +196438,7 @@ async function createRuntime(options) {
|
|
|
195831
196438
|
config: config3.observer,
|
|
195832
196439
|
tradingClient,
|
|
195833
196440
|
alertRules,
|
|
196441
|
+
newsService,
|
|
195834
196442
|
notifications,
|
|
195835
196443
|
priceCache,
|
|
195836
196444
|
approvalManager,
|
|
@@ -195876,6 +196484,7 @@ async function createRuntime(options) {
|
|
|
195876
196484
|
rssDiscoveryService,
|
|
195877
196485
|
tweetService,
|
|
195878
196486
|
preferenceStore,
|
|
196487
|
+
timezoneService,
|
|
195879
196488
|
xFollowService,
|
|
195880
196489
|
skillService,
|
|
195881
196490
|
chartSeries,
|
|
@@ -195909,16 +196518,16 @@ function getApiKey(oauthManager, credentials2, customModelRegistry) {
|
|
|
195909
196518
|
};
|
|
195910
196519
|
}
|
|
195911
196520
|
function createAgent(opts) {
|
|
195912
|
-
const initialOptions = buildAgentOptions(opts.config, opts.model, opts.security, opts.leakDetector, opts.oauthManager, opts.tools, opts.systemPrompt, opts.credentials, opts.extraReadDirs, opts.approvalManager, opts.eventBus, opts.logger, opts.customModelRegistry, opts.confirmDeps, opts.bypassConfirm ?? false);
|
|
196521
|
+
const initialOptions = buildAgentOptions(opts.config, opts.model, opts.security, opts.leakDetector, opts.oauthManager, opts.tools, opts.systemPrompt, opts.credentials, opts.extraReadDirs, opts.approvalManager, opts.eventBus, opts.logger, opts.customModelRegistry, opts.confirmDeps, opts.bypassConfirm ?? false, opts.taskMode ?? false);
|
|
195913
196522
|
return new Agent(initialOptions);
|
|
195914
196523
|
}
|
|
195915
|
-
function buildAgentOptions(config3, model, security2, leakDetector, oauthManager, tools, systemPrompt, credentials2, extraReadDirs, approvalManager, eventBus, logger, customModelRegistry, confirmDeps, bypassConfirm = false) {
|
|
196524
|
+
function buildAgentOptions(config3, model, security2, leakDetector, oauthManager, tools, systemPrompt, credentials2, extraReadDirs, approvalManager, eventBus, logger, customModelRegistry, confirmDeps, bypassConfirm = false, taskMode = false) {
|
|
195916
196525
|
const effectiveThinkingLevel = shouldForceThinkingOff({ id: model.id, baseUrl: model.baseUrl ?? "" }) ? "off" : config3.agent.thinkingLevel;
|
|
195917
196526
|
return {
|
|
195918
196527
|
initialState: {
|
|
195919
196528
|
systemPrompt,
|
|
195920
196529
|
model,
|
|
195921
|
-
tools: config3.provider === "claude-cli" ? [] : tools.all(),
|
|
196530
|
+
tools: config3.provider === "claude-cli" ? [] : taskMode ? tools.taskAgentTools() : tools.all(),
|
|
195922
196531
|
thinkingLevel: effectiveThinkingLevel
|
|
195923
196532
|
},
|
|
195924
196533
|
getApiKey: config3.provider === "claude-cli" ? async () => "claude-cli-no-key-needed" : getApiKey(oauthManager, credentials2, customModelRegistry),
|
|
@@ -196007,6 +196616,7 @@ async function runBatchedConfirm(assistantMessage, callName, callArgs, batchCach
|
|
|
196007
196616
|
let title;
|
|
196008
196617
|
const lines = [];
|
|
196009
196618
|
let steps;
|
|
196619
|
+
let extras;
|
|
196010
196620
|
if (isMulti) {
|
|
196011
196621
|
title = `Confirm ${confirmable.length} actions?`;
|
|
196012
196622
|
const stepList = [];
|
|
@@ -196017,14 +196627,22 @@ async function runBatchedConfirm(assistantMessage, callName, callArgs, batchCach
|
|
|
196017
196627
|
stepList.push(`${head}${tail}`);
|
|
196018
196628
|
}
|
|
196019
196629
|
steps = stepList;
|
|
196630
|
+
extras = undefined;
|
|
196020
196631
|
} else {
|
|
196021
196632
|
const only = confirmable[0];
|
|
196633
|
+
const args = only.args ?? {};
|
|
196634
|
+
const symbol5 = typeof args.symbol === "string" ? args.symbol : undefined;
|
|
196022
196635
|
const desc = describeConfirm(only.name, only.args);
|
|
196023
196636
|
title = desc.title;
|
|
196024
196637
|
lines.push(...desc.bullets);
|
|
196638
|
+
extras = {
|
|
196639
|
+
wizard: desc.wizard,
|
|
196640
|
+
suggestedValue: desc.suggestedValue,
|
|
196641
|
+
symbol: symbol5
|
|
196642
|
+
};
|
|
196025
196643
|
}
|
|
196026
196644
|
try {
|
|
196027
|
-
const res = await confirmService.confirm(title, { lines, steps });
|
|
196645
|
+
const res = await confirmService.confirm(title, { lines, steps }, extras);
|
|
196028
196646
|
return { decision: res.decision, reason: res.reason };
|
|
196029
196647
|
} catch (err) {
|
|
196030
196648
|
logger.warn({ err }, "confirm service threw; treating as rejected");
|
|
@@ -196173,19 +196791,19 @@ function makeTransformContext(maxTokens) {
|
|
|
196173
196791
|
}
|
|
196174
196792
|
function resolveDefaultBuiltinSkillsDir() {
|
|
196175
196793
|
const candidate = join14(import.meta.dir, "skills", "builtin");
|
|
196176
|
-
return
|
|
196794
|
+
return existsSync17(candidate) ? candidate : undefined;
|
|
196177
196795
|
}
|
|
196178
196796
|
function seedWorkspaceTemplates(workspaceDir) {
|
|
196179
196797
|
const templatesDir = join14(import.meta.dir, "templates");
|
|
196180
|
-
if (!
|
|
196798
|
+
if (!existsSync17(templatesDir))
|
|
196181
196799
|
return;
|
|
196182
196800
|
try {
|
|
196183
|
-
|
|
196801
|
+
mkdirSync13(workspaceDir, { recursive: true });
|
|
196184
196802
|
for (const entry of readdirSync7(templatesDir, { withFileTypes: true })) {
|
|
196185
196803
|
if (!entry.isFile())
|
|
196186
196804
|
continue;
|
|
196187
196805
|
const target = join14(workspaceDir, entry.name);
|
|
196188
|
-
if (!
|
|
196806
|
+
if (!existsSync17(target))
|
|
196189
196807
|
copyFileSync2(join14(templatesDir, entry.name), target);
|
|
196190
196808
|
}
|
|
196191
196809
|
} catch {}
|
|
@@ -196237,6 +196855,7 @@ var init_runtime2 = __esm(() => {
|
|
|
196237
196855
|
init_news();
|
|
196238
196856
|
init_rss_discovery();
|
|
196239
196857
|
init_tweets();
|
|
196858
|
+
init_timezone();
|
|
196240
196859
|
init_x_follows();
|
|
196241
196860
|
init_x_query_ids();
|
|
196242
196861
|
init_ta_indicators();
|
|
@@ -196420,14 +197039,6 @@ function getProviderList() {
|
|
|
196420
197039
|
});
|
|
196421
197040
|
}
|
|
196422
197041
|
}
|
|
196423
|
-
list.push({
|
|
196424
|
-
id: "claude-cli",
|
|
196425
|
-
label: "Claude Code",
|
|
196426
|
-
description: "Use Claude Code subscription (no API key)",
|
|
196427
|
-
tier: 0,
|
|
196428
|
-
tierLabel: TIER_LABELS[0] ?? "Other",
|
|
196429
|
-
supportsOAuth: false
|
|
196430
|
-
});
|
|
196431
197042
|
list.push({
|
|
196432
197043
|
id: "custom",
|
|
196433
197044
|
label: "Custom",
|
|
@@ -196458,9 +197069,6 @@ function getProviderList() {
|
|
|
196458
197069
|
return list;
|
|
196459
197070
|
}
|
|
196460
197071
|
function getModelList(providerId) {
|
|
196461
|
-
if (providerId === "claude-cli") {
|
|
196462
|
-
return getClaudeCliModels();
|
|
196463
|
-
}
|
|
196464
197072
|
try {
|
|
196465
197073
|
const raw = getModels(providerId).map((m6) => ({ id: m6.id, name: m6.name || m6.id }));
|
|
196466
197074
|
return filterModelCatalog(providerId, raw);
|
|
@@ -196471,7 +197079,6 @@ function getModelList(providerId) {
|
|
|
196471
197079
|
var OAUTH_PROVIDERS2, PROVIDER_META, TIER_LABELS;
|
|
196472
197080
|
var init_providers = __esm(() => {
|
|
196473
197081
|
init_dist();
|
|
196474
|
-
init_models6();
|
|
196475
197082
|
init_model_catalog();
|
|
196476
197083
|
OAUTH_PROVIDERS2 = new Set(["anthropic", "openai-codex", "github-copilot", "google-gemini-cli", "google-antigravity"]);
|
|
196477
197084
|
PROVIDER_META = {
|
|
@@ -196479,7 +197086,6 @@ var init_providers = __esm(() => {
|
|
|
196479
197086
|
anthropic: { label: "Anthropic", description: "Claude Sonnet & Opus (direct)", tier: 0, apiKeyUrl: "https://console.anthropic.com/settings/keys" },
|
|
196480
197087
|
openai: { label: "OpenAI", description: "GPT-4o, GPT-5 (direct)", tier: 0, apiKeyUrl: "https://platform.openai.com/api-keys" },
|
|
196481
197088
|
"openai-codex": { label: "OpenAI Codex", description: "ChatGPT subscription (OAuth, no API key)", tier: 0 },
|
|
196482
|
-
"claude-cli": { label: "Claude Code", description: "Use Claude Code subscription (no API key)", tier: 0 },
|
|
196483
197089
|
google: { label: "Google Gemini", description: "Gemini 2.0 Flash & Pro", tier: 0, apiKeyUrl: "https://aistudio.google.com/app/apikey" },
|
|
196484
197090
|
"google-gemini-cli": { label: "Google Gemini CLI", description: "Gemini via CLI auth", tier: 0 },
|
|
196485
197091
|
xai: { label: "xAI", description: "Grok 3 & 4", tier: 0, apiKeyUrl: "https://console.x.ai" },
|
|
@@ -196516,17 +197122,15 @@ __export(exports_cli_providers, {
|
|
|
196516
197122
|
listModels: () => listModels
|
|
196517
197123
|
});
|
|
196518
197124
|
function requiresApiKey(provider) {
|
|
196519
|
-
|
|
196520
|
-
return false;
|
|
196521
|
-
if (provider.supportsOAuth)
|
|
196522
|
-
return false;
|
|
196523
|
-
return true;
|
|
197125
|
+
return !provider.supportsOAuth;
|
|
196524
197126
|
}
|
|
196525
197127
|
function listProviders() {
|
|
196526
197128
|
const builtIn = getProviderList().map((p) => ({
|
|
196527
197129
|
id: p.id,
|
|
196528
197130
|
label: p.label,
|
|
196529
197131
|
description: p.description,
|
|
197132
|
+
tier: p.tier,
|
|
197133
|
+
tierLabel: p.tierLabel,
|
|
196530
197134
|
requiresApiKey: requiresApiKey(p),
|
|
196531
197135
|
supportsOAuth: p.supportsOAuth,
|
|
196532
197136
|
apiKeyUrl: p.apiKeyUrl ?? null
|
|
@@ -196537,6 +197141,8 @@ function listProviders() {
|
|
|
196537
197141
|
id: name,
|
|
196538
197142
|
label: name,
|
|
196539
197143
|
description: "Custom provider (from ~/.ghost/models.json)",
|
|
197144
|
+
tier: 4,
|
|
197145
|
+
tierLabel: "\uD83D\uDD27 Custom",
|
|
196540
197146
|
requiresApiKey: true,
|
|
196541
197147
|
supportsOAuth: false,
|
|
196542
197148
|
apiKeyUrl: null,
|
|
@@ -198590,11 +199196,11 @@ ${je2}${r2.trimStart()}`), s2 = 3 + ne(r2.trimStart()).length);
|
|
|
198590
199196
|
});
|
|
198591
199197
|
|
|
198592
199198
|
// src/services/os/utils.ts
|
|
198593
|
-
import { accessSync, constants, existsSync as
|
|
199199
|
+
import { accessSync, constants, existsSync as existsSync18, mkdirSync as mkdirSync14, realpathSync as realpathSync2 } from "fs";
|
|
198594
199200
|
import { join as join15 } from "path";
|
|
198595
199201
|
import { homedir as homedir3 } from "os";
|
|
198596
199202
|
function isExecutable(filePath) {
|
|
198597
|
-
if (!
|
|
199203
|
+
if (!existsSync18(filePath))
|
|
198598
199204
|
return false;
|
|
198599
199205
|
try {
|
|
198600
199206
|
accessSync(filePath, constants.X_OK);
|
|
@@ -198641,8 +199247,8 @@ function resolveBunPath() {
|
|
|
198641
199247
|
return candidates[0];
|
|
198642
199248
|
}
|
|
198643
199249
|
function ensureLogDir(dir) {
|
|
198644
|
-
if (!
|
|
198645
|
-
|
|
199250
|
+
if (!existsSync18(dir)) {
|
|
199251
|
+
mkdirSync14(dir, { recursive: true });
|
|
198646
199252
|
}
|
|
198647
199253
|
}
|
|
198648
199254
|
function defaultLogDir() {
|
|
@@ -198708,7 +199314,7 @@ __export(exports_launchd, {
|
|
|
198708
199314
|
LaunchdController: () => LaunchdController
|
|
198709
199315
|
});
|
|
198710
199316
|
import { spawnSync } from "child_process";
|
|
198711
|
-
import { existsSync as
|
|
199317
|
+
import { existsSync as existsSync19, unlinkSync as unlinkSync6, writeFileSync as writeFileSync10, rmSync as rmSync5 } from "fs";
|
|
198712
199318
|
import { join as join16 } from "path";
|
|
198713
199319
|
import { homedir as homedir4 } from "os";
|
|
198714
199320
|
function plistPath() {
|
|
@@ -198758,7 +199364,7 @@ class LaunchdController {
|
|
|
198758
199364
|
stderrLog,
|
|
198759
199365
|
env: opts.env ?? {}
|
|
198760
199366
|
});
|
|
198761
|
-
|
|
199367
|
+
writeFileSync10(definitionPath, plist, { encoding: "utf8", mode: 420 });
|
|
198762
199368
|
const domain2 = guiDomain();
|
|
198763
199369
|
const uid = process.getuid?.() ?? 0;
|
|
198764
199370
|
const serviceTarget = `${domain2}/${LABEL}`;
|
|
@@ -198808,12 +199414,12 @@ class LaunchdController {
|
|
|
198808
199414
|
const domain2 = guiDomain();
|
|
198809
199415
|
const definition = plistPath();
|
|
198810
199416
|
launchctl(["bootout", domain2, definition]);
|
|
198811
|
-
if (
|
|
199417
|
+
if (existsSync19(definition)) {
|
|
198812
199418
|
unlinkSync6(definition);
|
|
198813
199419
|
}
|
|
198814
199420
|
if (opts.purgeLogs) {
|
|
198815
199421
|
const logDir = defaultLogDir();
|
|
198816
|
-
if (
|
|
199422
|
+
if (existsSync19(logDir)) {
|
|
198817
199423
|
rmSync5(logDir, { recursive: true, force: true });
|
|
198818
199424
|
}
|
|
198819
199425
|
}
|
|
@@ -198897,8 +199503,8 @@ __export(exports_systemd, {
|
|
|
198897
199503
|
SystemdController: () => SystemdController
|
|
198898
199504
|
});
|
|
198899
199505
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
198900
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
198901
|
-
import { dirname as
|
|
199506
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync20, mkdirSync as mkdirSync15, rmSync as rmSync6, unlinkSync as unlinkSync7, writeFileSync as writeFileSync11 } from "fs";
|
|
199507
|
+
import { dirname as dirname8, join as join17 } from "path";
|
|
198902
199508
|
import { homedir as homedir5 } from "os";
|
|
198903
199509
|
function unitPath() {
|
|
198904
199510
|
return join17(homedir5(), ".config", "systemd", "user", SERVICE_NAME);
|
|
@@ -198942,11 +199548,11 @@ class SystemdController {
|
|
|
198942
199548
|
assertSystemdAvailable();
|
|
198943
199549
|
ensureLogDir(opts.logDir);
|
|
198944
199550
|
const path4 = unitPath();
|
|
198945
|
-
const dir =
|
|
198946
|
-
if (!
|
|
198947
|
-
|
|
199551
|
+
const dir = dirname8(path4);
|
|
199552
|
+
if (!existsSync20(dir)) {
|
|
199553
|
+
mkdirSync15(dir, { recursive: true });
|
|
198948
199554
|
}
|
|
198949
|
-
if (
|
|
199555
|
+
if (existsSync20(path4)) {
|
|
198950
199556
|
copyFileSync3(path4, `${path4}.bak`);
|
|
198951
199557
|
}
|
|
198952
199558
|
const unit = buildUnit({
|
|
@@ -198959,7 +199565,7 @@ class SystemdController {
|
|
|
198959
199565
|
GHOST_LOG_DIR: opts.logDir
|
|
198960
199566
|
}
|
|
198961
199567
|
});
|
|
198962
|
-
|
|
199568
|
+
writeFileSync11(path4, unit, "utf8");
|
|
198963
199569
|
const reload = systemctlStrict("daemon-reload");
|
|
198964
199570
|
if (!reload.ok) {
|
|
198965
199571
|
const msg = `daemon-reload failed: ${reload.stderr || reload.stdout}`;
|
|
@@ -199005,7 +199611,7 @@ class SystemdController {
|
|
|
199005
199611
|
}
|
|
199006
199612
|
for (const file3 of [path4, `${path4}.bak`]) {
|
|
199007
199613
|
try {
|
|
199008
|
-
if (
|
|
199614
|
+
if (existsSync20(file3)) {
|
|
199009
199615
|
unlinkSync7(file3);
|
|
199010
199616
|
}
|
|
199011
199617
|
} catch {
|
|
@@ -199016,7 +199622,7 @@ class SystemdController {
|
|
|
199016
199622
|
if (opts.purgeLogs) {
|
|
199017
199623
|
const logDir = defaultLogDir();
|
|
199018
199624
|
try {
|
|
199019
|
-
if (
|
|
199625
|
+
if (existsSync20(logDir)) {
|
|
199020
199626
|
rmSync6(logDir, { recursive: true, force: true });
|
|
199021
199627
|
}
|
|
199022
199628
|
} catch {
|
|
@@ -199034,7 +199640,7 @@ class SystemdController {
|
|
|
199034
199640
|
if (stdout === "active") {
|
|
199035
199641
|
return "running";
|
|
199036
199642
|
}
|
|
199037
|
-
if (
|
|
199643
|
+
if (existsSync20(unitPath())) {
|
|
199038
199644
|
return "stopped";
|
|
199039
199645
|
}
|
|
199040
199646
|
return "not-installed";
|
|
@@ -199056,7 +199662,7 @@ __export(exports_schtasks, {
|
|
|
199056
199662
|
});
|
|
199057
199663
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
199058
199664
|
import { Buffer as Buffer2 } from "buffer";
|
|
199059
|
-
import { existsSync as
|
|
199665
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync16, writeFileSync as writeFileSync12, rmSync as rmSync7 } from "fs";
|
|
199060
199666
|
import { join as join18 } from "path";
|
|
199061
199667
|
import { homedir as homedir6, tmpdir } from "os";
|
|
199062
199668
|
function launcherPath() {
|
|
@@ -199198,7 +199804,7 @@ function buildScheduledTaskXml(invisibleVbs, userId) {
|
|
|
199198
199804
|
function writeTaskXml(xml) {
|
|
199199
199805
|
const xmlPath = join18(tmpdir(), `ghost-task-${process.pid}.xml`);
|
|
199200
199806
|
const bom = Buffer2.from([255, 254]);
|
|
199201
|
-
|
|
199807
|
+
writeFileSync12(xmlPath, Buffer2.concat([bom, Buffer2.from(xml, "utf16le")]));
|
|
199202
199808
|
return xmlPath;
|
|
199203
199809
|
}
|
|
199204
199810
|
function killOrphanedSupervisorChain() {
|
|
@@ -199232,17 +199838,17 @@ class SchtasksController {
|
|
|
199232
199838
|
}
|
|
199233
199839
|
ensureLogDir(opts.logDir);
|
|
199234
199840
|
const stateDir = join18(homedir6(), ".ghost", "state");
|
|
199235
|
-
if (!
|
|
199236
|
-
|
|
199841
|
+
if (!existsSync21(stateDir)) {
|
|
199842
|
+
mkdirSync16(stateDir, { recursive: true });
|
|
199237
199843
|
}
|
|
199238
199844
|
const legacy = legacyStartupPath();
|
|
199239
|
-
if (
|
|
199845
|
+
if (existsSync21(legacy)) {
|
|
199240
199846
|
rmSync7(legacy, RM_RETRY_OPTS);
|
|
199241
199847
|
}
|
|
199242
199848
|
const launcher = launcherPath();
|
|
199243
|
-
|
|
199849
|
+
writeFileSync12(launcher, buildLauncherCmd(opts.bunPath, opts.execPath, opts.env), { encoding: "utf8" });
|
|
199244
199850
|
const invisibleVbs = invisibleLauncherPath();
|
|
199245
|
-
|
|
199851
|
+
writeFileSync12(invisibleVbs, buildInvisibleVbs(launcher), { encoding: "utf8" });
|
|
199246
199852
|
const create4 = createScheduledTask(invisibleVbs, taskUser);
|
|
199247
199853
|
if (create4.status !== 0) {
|
|
199248
199854
|
return {
|
|
@@ -199287,20 +199893,20 @@ class SchtasksController {
|
|
|
199287
199893
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
199288
199894
|
schtasks(["/Delete", "/F", "/TN", TASK_NAME]);
|
|
199289
199895
|
const launcher = launcherPath();
|
|
199290
|
-
if (
|
|
199896
|
+
if (existsSync21(launcher)) {
|
|
199291
199897
|
rmSync7(launcher, RM_RETRY_OPTS);
|
|
199292
199898
|
}
|
|
199293
199899
|
const invisibleVbs = invisibleLauncherPath();
|
|
199294
|
-
if (
|
|
199900
|
+
if (existsSync21(invisibleVbs)) {
|
|
199295
199901
|
rmSync7(invisibleVbs, RM_RETRY_OPTS);
|
|
199296
199902
|
}
|
|
199297
199903
|
const legacy = legacyStartupPath();
|
|
199298
|
-
if (
|
|
199904
|
+
if (existsSync21(legacy)) {
|
|
199299
199905
|
rmSync7(legacy, RM_RETRY_OPTS);
|
|
199300
199906
|
}
|
|
199301
199907
|
if (opts.purgeLogs) {
|
|
199302
199908
|
const logDir = defaultLogDir();
|
|
199303
|
-
if (
|
|
199909
|
+
if (existsSync21(logDir)) {
|
|
199304
199910
|
rmSync7(logDir, { recursive: true, ...RM_RETRY_OPTS });
|
|
199305
199911
|
}
|
|
199306
199912
|
}
|
|
@@ -212192,7 +212798,7 @@ function handleHealth() {
|
|
|
212192
212798
|
}
|
|
212193
212799
|
|
|
212194
212800
|
// src/gateway/static.ts
|
|
212195
|
-
import { existsSync as
|
|
212801
|
+
import { existsSync as existsSync22 } from "fs";
|
|
212196
212802
|
import { join as join20, extname as extname3 } from "path";
|
|
212197
212803
|
function mime2(path4) {
|
|
212198
212804
|
return MIME[extname3(path4).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -212200,7 +212806,7 @@ function mime2(path4) {
|
|
|
212200
212806
|
function resolveWebDist(candidates) {
|
|
212201
212807
|
const list = candidates ?? defaultWebDistCandidates();
|
|
212202
212808
|
for (const c4 of list) {
|
|
212203
|
-
if (
|
|
212809
|
+
if (existsSync22(join20(c4, "index.html")))
|
|
212204
212810
|
return c4;
|
|
212205
212811
|
}
|
|
212206
212812
|
return null;
|
|
@@ -212455,7 +213061,7 @@ function semverGt(a, b5) {
|
|
|
212455
213061
|
}
|
|
212456
213062
|
|
|
212457
213063
|
// src/update/version.ts
|
|
212458
|
-
import { readFileSync as
|
|
213064
|
+
import { readFileSync as readFileSync16, existsSync as existsSync23 } from "fs";
|
|
212459
213065
|
import { join as join21 } from "path";
|
|
212460
213066
|
function resolvePackageJsonPath(candidates) {
|
|
212461
213067
|
const list = candidates ?? [
|
|
@@ -212464,7 +213070,7 @@ function resolvePackageJsonPath(candidates) {
|
|
|
212464
213070
|
join21(process.cwd(), "package.json")
|
|
212465
213071
|
];
|
|
212466
213072
|
for (const p2 of list) {
|
|
212467
|
-
if (
|
|
213073
|
+
if (existsSync23(p2))
|
|
212468
213074
|
return p2;
|
|
212469
213075
|
}
|
|
212470
213076
|
return null;
|
|
@@ -212486,7 +213092,7 @@ function getCurrentVersion(pkgPath) {
|
|
|
212486
213092
|
if (!resolved)
|
|
212487
213093
|
return UNKNOWN_VERSION;
|
|
212488
213094
|
try {
|
|
212489
|
-
const pkg = JSON.parse(
|
|
213095
|
+
const pkg = JSON.parse(readFileSync16(resolved, "utf-8"));
|
|
212490
213096
|
if (pkg.version && pkg.version.length > 0)
|
|
212491
213097
|
return pkg.version;
|
|
212492
213098
|
} catch {}
|
|
@@ -212543,14 +213149,14 @@ var init_status = __esm(() => {
|
|
|
212543
213149
|
});
|
|
212544
213150
|
|
|
212545
213151
|
// src/gateway/memory.ts
|
|
212546
|
-
import { readFileSync as
|
|
213152
|
+
import { readFileSync as readFileSync17, existsSync as existsSync24 } from "fs";
|
|
212547
213153
|
function registerMemoryMethods(register, deps) {
|
|
212548
213154
|
register("memory.get", async () => {
|
|
212549
213155
|
const memory = deps.memoryStore.readLongTerm();
|
|
212550
213156
|
let history = "";
|
|
212551
|
-
if (
|
|
213157
|
+
if (existsSync24(deps.memoryStore.historyFile)) {
|
|
212552
213158
|
try {
|
|
212553
|
-
history =
|
|
213159
|
+
history = readFileSync17(deps.memoryStore.historyFile, "utf-8");
|
|
212554
213160
|
} catch {}
|
|
212555
213161
|
}
|
|
212556
213162
|
return { memory, history };
|
|
@@ -212713,6 +213319,22 @@ function registerCronMethods(register, deps) {
|
|
|
212713
213319
|
});
|
|
212714
213320
|
}
|
|
212715
213321
|
|
|
213322
|
+
// src/gateway/config.ts
|
|
213323
|
+
function registerConfigMethods(register, deps) {
|
|
213324
|
+
register("config.timezone.get", async () => ({
|
|
213325
|
+
tz: deps.timezoneService.get()
|
|
213326
|
+
}));
|
|
213327
|
+
register("config.timezone.set", async (_ctx, payload) => {
|
|
213328
|
+
const p2 = payload;
|
|
213329
|
+
const result = deps.timezoneService.set(p2.tz);
|
|
213330
|
+
if (!result.ok) {
|
|
213331
|
+
return { ok: false, error: result.error };
|
|
213332
|
+
}
|
|
213333
|
+
const updatedJobs = deps.cronService.updateBuiltinJobsTimezone(result.tz);
|
|
213334
|
+
return { ok: true, tz: result.tz, updatedJobs };
|
|
213335
|
+
});
|
|
213336
|
+
}
|
|
213337
|
+
|
|
212716
213338
|
// src/gateway/route-orchestrator-error.ts
|
|
212717
213339
|
function routeOrchestratorError(runId, classified, emit) {
|
|
212718
213340
|
if (classified.type === "TOOL_BLOCKED") {
|
|
@@ -213083,34 +213705,19 @@ function registerTradingMethods(register, deps) {
|
|
|
213083
213705
|
return { fills: filtered, window: { startTime, endTime }, capped };
|
|
213084
213706
|
});
|
|
213085
213707
|
register("trading.tokens.list", async () => {
|
|
213086
|
-
|
|
213087
|
-
const tickers = await deps.tradingClient.getAllTickers();
|
|
213088
|
-
const prices = {};
|
|
213089
|
-
const prevDayPrices = {};
|
|
213090
|
-
const maxLeverages = {};
|
|
213091
|
-
const tokens = [];
|
|
213092
|
-
for (const t10 of tickers) {
|
|
213093
|
-
tokens.push(t10.symbol);
|
|
213094
|
-
prices[t10.symbol] = t10.markPrice;
|
|
213095
|
-
if (t10.prevDayPrice > 0)
|
|
213096
|
-
prevDayPrices[t10.symbol] = t10.prevDayPrice;
|
|
213097
|
-
const lev = deps.tradingClient.getMaxLeverage(t10.symbol);
|
|
213098
|
-
if (typeof lev === "number" && lev > 0)
|
|
213099
|
-
maxLeverages[t10.symbol] = lev;
|
|
213100
|
-
}
|
|
213101
|
-
tokens.sort();
|
|
213102
|
-
return { tokens, prices, prevDayPrices, maxLeverages };
|
|
213103
|
-
} catch {
|
|
213104
|
-
return { tokens: [], prices: {}, prevDayPrices: {}, maxLeverages: {} };
|
|
213105
|
-
}
|
|
213708
|
+
return deps.tokensSnapshot.build();
|
|
213106
213709
|
});
|
|
213107
213710
|
register("trading.price", async (_ctx, payload) => {
|
|
213108
213711
|
const params = typeof payload === "object" && payload !== null ? payload : {};
|
|
213109
213712
|
const symbol5 = typeof params.symbol === "string" ? params.symbol : undefined;
|
|
213110
213713
|
if (!symbol5)
|
|
213111
213714
|
return { price: null, symbol: null };
|
|
213715
|
+
const resolved = deps.tradingClient.resolveSymbol(symbol5);
|
|
213716
|
+
const entry = deps.priceCache.get(resolved, 30000);
|
|
213717
|
+
if (entry)
|
|
213718
|
+
return { symbol: resolved, price: entry.price };
|
|
213112
213719
|
try {
|
|
213113
|
-
const ticker = await deps.tradingClient.getTicker(
|
|
213720
|
+
const ticker = await deps.tradingClient.getTicker(resolved);
|
|
213114
213721
|
return { symbol: ticker.symbol, price: ticker.markPrice };
|
|
213115
213722
|
} catch {
|
|
213116
213723
|
return { price: null, symbol: symbol5 };
|
|
@@ -213129,9 +213736,7 @@ function registerTradingMethods(register, deps) {
|
|
|
213129
213736
|
if (!symbol5)
|
|
213130
213737
|
return { error: "symbol required" };
|
|
213131
213738
|
const resolved = deps.tradingClient.resolveSymbol(symbol5);
|
|
213132
|
-
|
|
213133
|
-
await deps.tradingClient.getTicker(resolved);
|
|
213134
|
-
} catch {
|
|
213739
|
+
if (!deps.tradingClient.isKnownSymbol(resolved)) {
|
|
213135
213740
|
return { error: `Symbol ${resolved} not found on Hyperliquid` };
|
|
213136
213741
|
}
|
|
213137
213742
|
try {
|
|
@@ -213840,7 +214445,7 @@ class CompositePriceFeed {
|
|
|
213840
214445
|
this.log.info({ sources: names }, "composite price feed starting");
|
|
213841
214446
|
await Promise.all(this.sources.map(async (source2) => {
|
|
213842
214447
|
try {
|
|
213843
|
-
await source2.start((symbol5, price) => this.handleTick(source2, symbol5, price));
|
|
214448
|
+
await source2.start((symbol5, price, prevDayPrice) => this.handleTick(source2, symbol5, price, prevDayPrice));
|
|
213844
214449
|
} catch (err) {
|
|
213845
214450
|
this.log.warn({ err, source: source2.name }, "source failed to start");
|
|
213846
214451
|
}
|
|
@@ -213871,7 +214476,7 @@ class CompositePriceFeed {
|
|
|
213871
214476
|
this.healthySince.clear();
|
|
213872
214477
|
this.onPrice = null;
|
|
213873
214478
|
}
|
|
213874
|
-
handleTick(source2, symbol5, price) {
|
|
214479
|
+
handleTick(source2, symbol5, price, prevDayPrice) {
|
|
213875
214480
|
if (!this.onPrice)
|
|
213876
214481
|
return;
|
|
213877
214482
|
if (this.currentPrimary === null) {
|
|
@@ -213881,7 +214486,7 @@ class CompositePriceFeed {
|
|
|
213881
214486
|
}
|
|
213882
214487
|
if (source2.name !== this.currentPrimary)
|
|
213883
214488
|
return;
|
|
213884
|
-
this.onPrice(symbol5, price);
|
|
214489
|
+
this.onPrice(symbol5, price, prevDayPrice);
|
|
213885
214490
|
}
|
|
213886
214491
|
isHealthy(source2) {
|
|
213887
214492
|
const last = source2.getLastTickAt();
|
|
@@ -213960,44 +214565,6 @@ var init_composite3 = __esm(() => {
|
|
|
213960
214565
|
init_types6();
|
|
213961
214566
|
});
|
|
213962
214567
|
|
|
213963
|
-
// src/services/price-feed/sources/bun-ws-compat.ts
|
|
213964
|
-
function applyShim() {
|
|
213965
|
-
const proto = globalThis.WebSocket?.prototype;
|
|
213966
|
-
if (!proto || proto.__ghostBlobSetterPatched)
|
|
213967
|
-
return proto?.__ghostBlobSetterPatched === true;
|
|
213968
|
-
const desc = Object.getOwnPropertyDescriptor(proto, "binaryType");
|
|
213969
|
-
if (!desc?.set)
|
|
213970
|
-
return false;
|
|
213971
|
-
const originalSet = desc.set;
|
|
213972
|
-
Object.defineProperty(proto, "binaryType", {
|
|
213973
|
-
configurable: true,
|
|
213974
|
-
enumerable: desc.enumerable ?? false,
|
|
213975
|
-
get: desc.get,
|
|
213976
|
-
set(value2) {
|
|
213977
|
-
if (value2 === "blob") {
|
|
213978
|
-
const g7 = globalThis;
|
|
213979
|
-
if (!g7.__ghostBlobWarned) {
|
|
213980
|
-
g7.__ghostBlobWarned = true;
|
|
213981
|
-
console.warn("[ghost] WebSocket.binaryType='blob' silently dropped (Bun compat shim \u2014 see src/services/price-feed/sources/bun-ws-compat.ts)");
|
|
213982
|
-
}
|
|
213983
|
-
return;
|
|
213984
|
-
}
|
|
213985
|
-
originalSet.call(this, value2);
|
|
213986
|
-
}
|
|
213987
|
-
});
|
|
213988
|
-
Object.defineProperty(proto, "__ghostBlobSetterPatched", {
|
|
213989
|
-
value: true,
|
|
213990
|
-
writable: false,
|
|
213991
|
-
enumerable: false,
|
|
213992
|
-
configurable: false
|
|
213993
|
-
});
|
|
213994
|
-
return true;
|
|
213995
|
-
}
|
|
213996
|
-
var BUN_WS_COMPAT_APPLIED;
|
|
213997
|
-
var init_bun_ws_compat = __esm(() => {
|
|
213998
|
-
BUN_WS_COMPAT_APPLIED = applyShim();
|
|
213999
|
-
});
|
|
214000
|
-
|
|
214001
214568
|
// src/services/price-feed/sources/hyperliquid.ts
|
|
214002
214569
|
class HyperliquidSource {
|
|
214003
214570
|
name = "hyperliquid";
|
|
@@ -214011,13 +214578,10 @@ class HyperliquidSource {
|
|
|
214011
214578
|
healthCheckIntervalMs;
|
|
214012
214579
|
onTick = null;
|
|
214013
214580
|
stopped = true;
|
|
214014
|
-
|
|
214015
|
-
client = null;
|
|
214016
|
-
subscription = null;
|
|
214581
|
+
wsSubscription = null;
|
|
214017
214582
|
wsRetryTimer = null;
|
|
214018
214583
|
wsRetryCount = 0;
|
|
214019
214584
|
lastWsTickAt = 0;
|
|
214020
|
-
universe = [];
|
|
214021
214585
|
restTimer = null;
|
|
214022
214586
|
restPolling = false;
|
|
214023
214587
|
lastRestTickAt = 0;
|
|
@@ -214052,12 +214616,34 @@ class HyperliquidSource {
|
|
|
214052
214616
|
restIntervalMs: this.restIntervalMs
|
|
214053
214617
|
}, "hyperliquid source starting (WS primary, REST dormant)");
|
|
214054
214618
|
await this.connectWs();
|
|
214619
|
+
if (this.stopped)
|
|
214620
|
+
return;
|
|
214621
|
+
await this.hydrateFromRest();
|
|
214055
214622
|
if (this.stopped)
|
|
214056
214623
|
return;
|
|
214057
214624
|
this.healthTimer = setInterval(() => {
|
|
214058
214625
|
this.reconcileTransports();
|
|
214059
214626
|
}, this.healthCheckIntervalMs);
|
|
214060
214627
|
}
|
|
214628
|
+
async hydrateFromRest() {
|
|
214629
|
+
try {
|
|
214630
|
+
const tickers = await this.tradingClient.getAllTickers();
|
|
214631
|
+
if (this.stopped)
|
|
214632
|
+
return;
|
|
214633
|
+
const callback = this.onTick;
|
|
214634
|
+
if (!callback)
|
|
214635
|
+
return;
|
|
214636
|
+
for (const t10 of tickers) {
|
|
214637
|
+
if (!Number.isFinite(t10.markPrice))
|
|
214638
|
+
continue;
|
|
214639
|
+
const prev = Number.isFinite(t10.prevDayPrice) && t10.prevDayPrice > 0 ? t10.prevDayPrice : undefined;
|
|
214640
|
+
callback(t10.symbol, t10.markPrice, prev);
|
|
214641
|
+
}
|
|
214642
|
+
this.log.info({ count: tickers.length }, "hyperliquid source: REST hydration complete");
|
|
214643
|
+
} catch (err) {
|
|
214644
|
+
this.log.warn({ err }, "hyperliquid source: REST hydration failed; relying on WS");
|
|
214645
|
+
}
|
|
214646
|
+
}
|
|
214061
214647
|
async stop() {
|
|
214062
214648
|
if (this.stopped)
|
|
214063
214649
|
return;
|
|
@@ -214077,89 +214663,53 @@ class HyperliquidSource {
|
|
|
214077
214663
|
async connectWs() {
|
|
214078
214664
|
if (this.stopped)
|
|
214079
214665
|
return;
|
|
214080
|
-
let transport = null;
|
|
214081
|
-
let client4 = null;
|
|
214082
|
-
let subscription = null;
|
|
214083
214666
|
try {
|
|
214084
|
-
|
|
214085
|
-
|
|
214086
|
-
|
|
214087
|
-
|
|
214667
|
+
await this.tradingClient.ensureMeta();
|
|
214668
|
+
if (this.stopped)
|
|
214669
|
+
return;
|
|
214670
|
+
const sub = await this.tradingClient.subscribeAllDexsAssetCtxs((event2) => this.handleAllDexsAssetCtxsEvent(event2));
|
|
214088
214671
|
if (this.stopped) {
|
|
214089
214672
|
try {
|
|
214090
|
-
await
|
|
214091
|
-
} catch {}
|
|
214092
|
-
try {
|
|
214093
|
-
await transport.close();
|
|
214673
|
+
await sub.unsubscribe();
|
|
214094
214674
|
} catch {}
|
|
214095
214675
|
return;
|
|
214096
214676
|
}
|
|
214097
|
-
this.
|
|
214098
|
-
this.client = client4;
|
|
214099
|
-
this.subscription = subscription;
|
|
214677
|
+
this.wsSubscription = sub;
|
|
214100
214678
|
this.wsRetryCount = 0;
|
|
214101
|
-
this.log.info("hyperliquid source: WS connected");
|
|
214102
|
-
this.subscription.failureSignal.addEventListener("abort", () => {
|
|
214103
|
-
if (this.stopped)
|
|
214104
|
-
return;
|
|
214105
|
-
this.log.warn("hyperliquid source: WS subscription failed, scheduling retry");
|
|
214106
|
-
this.scheduleWsRetry();
|
|
214107
|
-
});
|
|
214108
|
-
this.transport.socket.addEventListener("terminate", (ev2) => {
|
|
214109
|
-
if (this.stopped)
|
|
214110
|
-
return;
|
|
214111
|
-
this.log.warn({ reason: ev2.detail?.code, cause: serializeError(ev2.detail?.cause) }, "hyperliquid source: WS transport terminated, scheduling retry");
|
|
214112
|
-
this.scheduleWsRetry();
|
|
214113
|
-
});
|
|
214114
|
-
this.transport.socket.addEventListener("error", (ev2) => {
|
|
214115
|
-
if (this.stopped)
|
|
214116
|
-
return;
|
|
214117
|
-
this.log.warn({ event: serializeWsErrorEvent(ev2) }, "hyperliquid source: WS transport error");
|
|
214118
|
-
});
|
|
214679
|
+
this.log.info("hyperliquid source: WS connected (allDexsAssetCtxs)");
|
|
214119
214680
|
} catch (err) {
|
|
214120
214681
|
this.log.warn({ err }, "hyperliquid source: WS failed to connect");
|
|
214121
|
-
if (subscription) {
|
|
214122
|
-
try {
|
|
214123
|
-
await subscription.unsubscribe();
|
|
214124
|
-
} catch {}
|
|
214125
|
-
}
|
|
214126
|
-
if (transport) {
|
|
214127
|
-
try {
|
|
214128
|
-
await transport.close();
|
|
214129
|
-
} catch {}
|
|
214130
|
-
}
|
|
214131
214682
|
await this.cleanupWs();
|
|
214132
214683
|
this.scheduleWsRetry();
|
|
214133
214684
|
}
|
|
214134
214685
|
}
|
|
214135
|
-
|
|
214136
|
-
try {
|
|
214137
|
-
const tickers = await this.tradingClient.getAllTickers();
|
|
214138
|
-
this.universe = tickers.map((t10) => t10.symbol);
|
|
214139
|
-
} catch (err) {
|
|
214140
|
-
this.log.warn({ err }, "hyperliquid source: universe refresh failed");
|
|
214141
|
-
}
|
|
214142
|
-
}
|
|
214143
|
-
handleAssetCtxsEvent(event2) {
|
|
214686
|
+
handleAllDexsAssetCtxsEvent(event2) {
|
|
214144
214687
|
if (this.stopped)
|
|
214145
214688
|
return;
|
|
214146
214689
|
const callback = this.onTick;
|
|
214147
214690
|
if (!callback)
|
|
214148
214691
|
return;
|
|
214692
|
+
const dexUniverses = this.tradingClient.getDexUniverses();
|
|
214149
214693
|
const now = Date.now();
|
|
214150
214694
|
let anyEmitted = false;
|
|
214151
|
-
for (
|
|
214152
|
-
const
|
|
214153
|
-
|
|
214154
|
-
|
|
214155
|
-
|
|
214156
|
-
|
|
214157
|
-
|
|
214158
|
-
|
|
214159
|
-
|
|
214160
|
-
|
|
214161
|
-
|
|
214162
|
-
|
|
214695
|
+
for (const [dex, ctxs] of event2.ctxs) {
|
|
214696
|
+
const universe = dexUniverses.get(dex) ?? [];
|
|
214697
|
+
for (let i = 0;i < ctxs.length; i++) {
|
|
214698
|
+
const symbol5 = universe[i];
|
|
214699
|
+
if (!symbol5)
|
|
214700
|
+
continue;
|
|
214701
|
+
const ctx = ctxs[i];
|
|
214702
|
+
const raw = ctx?.markPx;
|
|
214703
|
+
if (raw === null || raw === undefined)
|
|
214704
|
+
continue;
|
|
214705
|
+
const mark = typeof raw === "string" ? parseFloat(raw) : Number(raw);
|
|
214706
|
+
if (!Number.isFinite(mark))
|
|
214707
|
+
continue;
|
|
214708
|
+
const prevRaw = ctx?.prevDayPx;
|
|
214709
|
+
const prevDay = prevRaw != null ? typeof prevRaw === "string" ? parseFloat(prevRaw) : Number(prevRaw) : undefined;
|
|
214710
|
+
callback(symbol5, mark, Number.isFinite(prevDay) ? prevDay : undefined);
|
|
214711
|
+
anyEmitted = true;
|
|
214712
|
+
}
|
|
214163
214713
|
}
|
|
214164
214714
|
if (anyEmitted)
|
|
214165
214715
|
this.lastWsTickAt = now;
|
|
@@ -214179,19 +214729,12 @@ class HyperliquidSource {
|
|
|
214179
214729
|
}, delay4);
|
|
214180
214730
|
}
|
|
214181
214731
|
async cleanupWs() {
|
|
214182
|
-
if (this.
|
|
214732
|
+
if (this.wsSubscription) {
|
|
214183
214733
|
try {
|
|
214184
|
-
await this.
|
|
214734
|
+
await this.wsSubscription.unsubscribe();
|
|
214185
214735
|
} catch {}
|
|
214186
|
-
this.
|
|
214736
|
+
this.wsSubscription = null;
|
|
214187
214737
|
}
|
|
214188
|
-
if (this.transport) {
|
|
214189
|
-
try {
|
|
214190
|
-
await this.transport.close();
|
|
214191
|
-
} catch {}
|
|
214192
|
-
this.transport = null;
|
|
214193
|
-
}
|
|
214194
|
-
this.client = null;
|
|
214195
214738
|
}
|
|
214196
214739
|
activateRest() {
|
|
214197
214740
|
if (this.stopped)
|
|
@@ -214229,7 +214772,8 @@ class HyperliquidSource {
|
|
|
214229
214772
|
return;
|
|
214230
214773
|
if (!Number.isFinite(t10.markPrice))
|
|
214231
214774
|
continue;
|
|
214232
|
-
|
|
214775
|
+
const prevDay = Number.isFinite(t10.prevDayPrice) && t10.prevDayPrice > 0 ? t10.prevDayPrice : undefined;
|
|
214776
|
+
callback(t10.symbol, t10.markPrice, prevDay);
|
|
214233
214777
|
anyEmitted = true;
|
|
214234
214778
|
}
|
|
214235
214779
|
if (anyEmitted)
|
|
@@ -214274,7 +214818,7 @@ class HyperliquidSource {
|
|
|
214274
214818
|
this.deactivateRest();
|
|
214275
214819
|
}
|
|
214276
214820
|
maybeForceWsReconnect(wsAge) {
|
|
214277
|
-
if (!this.
|
|
214821
|
+
if (!this.wsSubscription)
|
|
214278
214822
|
return;
|
|
214279
214823
|
if (this.wsRetryTimer)
|
|
214280
214824
|
return;
|
|
@@ -214288,27 +214832,7 @@ class HyperliquidSource {
|
|
|
214288
214832
|
});
|
|
214289
214833
|
}
|
|
214290
214834
|
}
|
|
214291
|
-
function serializeWsErrorEvent(ev2) {
|
|
214292
|
-
const e2 = ev2;
|
|
214293
|
-
const underlying = e2.error;
|
|
214294
|
-
return {
|
|
214295
|
-
type: ev2.type,
|
|
214296
|
-
message: typeof e2.message === "string" ? e2.message : undefined,
|
|
214297
|
-
error: underlying instanceof Error ? { name: underlying.name, message: underlying.message } : underlying !== undefined ? String(underlying) : undefined
|
|
214298
|
-
};
|
|
214299
|
-
}
|
|
214300
|
-
function serializeError(cause) {
|
|
214301
|
-
if (cause === undefined || cause === null)
|
|
214302
|
-
return;
|
|
214303
|
-
if (cause instanceof Error)
|
|
214304
|
-
return `${cause.name}: ${cause.message}`;
|
|
214305
|
-
return String(cause);
|
|
214306
|
-
}
|
|
214307
214835
|
var DEFAULT_REST_INTERVAL_MS = 5000, DEFAULT_WS_STALE_MS = 1e4, DEFAULT_WS_STABILITY_MS = 5000, DEFAULT_HEALTH_CHECK_INTERVAL_MS = 1000;
|
|
214308
|
-
var init_hyperliquid = __esm(() => {
|
|
214309
|
-
init_bun_ws_compat();
|
|
214310
|
-
init_mod6();
|
|
214311
|
-
});
|
|
214312
214836
|
|
|
214313
214837
|
// src/services/price-feed/symbol-mapping.ts
|
|
214314
214838
|
function mapBinanceSymbol(binanceSymbol) {
|
|
@@ -214581,7 +215105,7 @@ class BinanceSource {
|
|
|
214581
215105
|
ws.addEventListener("error", (ev2) => {
|
|
214582
215106
|
if (this.stopped)
|
|
214583
215107
|
return;
|
|
214584
|
-
this.log.warn({ event:
|
|
215108
|
+
this.log.warn({ event: serializeWsErrorEvent(ev2) }, "binance source: WS error");
|
|
214585
215109
|
});
|
|
214586
215110
|
} catch (err) {
|
|
214587
215111
|
this.log.warn({ err }, "binance source: WS failed to open");
|
|
@@ -214612,7 +215136,7 @@ class BinanceSource {
|
|
|
214612
215136
|
const mark = entry?.p;
|
|
214613
215137
|
if (typeof sym !== "string" || typeof mark !== "string")
|
|
214614
215138
|
continue;
|
|
214615
|
-
const emitted = this.emitMapped(sym, mark, callback);
|
|
215139
|
+
const emitted = this.emitMapped(sym, mark, entry.o, callback);
|
|
214616
215140
|
if (emitted)
|
|
214617
215141
|
anyEmitted = true;
|
|
214618
215142
|
}
|
|
@@ -214685,7 +215209,7 @@ class BinanceSource {
|
|
|
214685
215209
|
const mark = row?.markPrice;
|
|
214686
215210
|
if (typeof sym !== "string" || typeof mark !== "string")
|
|
214687
215211
|
continue;
|
|
214688
|
-
const emitted = this.emitMapped(sym, mark, callback);
|
|
215212
|
+
const emitted = this.emitMapped(sym, mark, row.openPrice, callback);
|
|
214689
215213
|
if (emitted)
|
|
214690
215214
|
anyEmitted = true;
|
|
214691
215215
|
}
|
|
@@ -214702,7 +215226,7 @@ class BinanceSource {
|
|
|
214702
215226
|
}
|
|
214703
215227
|
}
|
|
214704
215228
|
}
|
|
214705
|
-
emitMapped(binanceSymbol, priceStr, callback) {
|
|
215229
|
+
emitMapped(binanceSymbol, priceStr, prevDayPriceStr, callback) {
|
|
214706
215230
|
if (!binanceSymbol.endsWith("USDT"))
|
|
214707
215231
|
return false;
|
|
214708
215232
|
const base = binanceSymbol.slice(0, -4);
|
|
@@ -214719,7 +215243,14 @@ class BinanceSource {
|
|
|
214719
215243
|
if (!Number.isFinite(binancePrice))
|
|
214720
215244
|
return false;
|
|
214721
215245
|
const hlPrice = binancePrice * mapping.multiplier;
|
|
214722
|
-
|
|
215246
|
+
let prevDayPrice;
|
|
215247
|
+
if (prevDayPriceStr !== undefined) {
|
|
215248
|
+
const rawPrev = Number.parseFloat(prevDayPriceStr);
|
|
215249
|
+
if (Number.isFinite(rawPrev) && rawPrev > 0) {
|
|
215250
|
+
prevDayPrice = rawPrev * mapping.multiplier;
|
|
215251
|
+
}
|
|
215252
|
+
}
|
|
215253
|
+
callback(mapping.hlSymbol, hlPrice, prevDayPrice);
|
|
214723
215254
|
return true;
|
|
214724
215255
|
}
|
|
214725
215256
|
reconcileTransports() {
|
|
@@ -214751,7 +215282,7 @@ class BinanceSource {
|
|
|
214751
215282
|
this.deactivateRest();
|
|
214752
215283
|
}
|
|
214753
215284
|
}
|
|
214754
|
-
function
|
|
215285
|
+
function serializeWsErrorEvent(ev2) {
|
|
214755
215286
|
const e2 = ev2;
|
|
214756
215287
|
const underlying = e2.error;
|
|
214757
215288
|
return {
|
|
@@ -214778,6 +215309,37 @@ var init_binance2 = __esm(() => {
|
|
|
214778
215309
|
LEVERAGED_SUFFIX_RE = /(?:UP|DOWN|BULL|BEAR)$/;
|
|
214779
215310
|
});
|
|
214780
215311
|
|
|
215312
|
+
// src/services/tokens-snapshot.ts
|
|
215313
|
+
class TokensSnapshotService {
|
|
215314
|
+
client;
|
|
215315
|
+
priceCache;
|
|
215316
|
+
constructor(client4, priceCache) {
|
|
215317
|
+
this.client = client4;
|
|
215318
|
+
this.priceCache = priceCache;
|
|
215319
|
+
}
|
|
215320
|
+
build() {
|
|
215321
|
+
const assets = this.client.getAllAssets();
|
|
215322
|
+
const prices = {};
|
|
215323
|
+
const prevDayPrices = {};
|
|
215324
|
+
const maxLeverages = {};
|
|
215325
|
+
const tokens = [];
|
|
215326
|
+
for (const { symbol: symbol5, isDelisted } of assets) {
|
|
215327
|
+
const entry = this.priceCache.get(symbol5, 30000);
|
|
215328
|
+
if (entry) {
|
|
215329
|
+
prices[symbol5] = entry.price;
|
|
215330
|
+
if (entry.prevDayPrice !== undefined)
|
|
215331
|
+
prevDayPrices[symbol5] = entry.prevDayPrice;
|
|
215332
|
+
}
|
|
215333
|
+
const lev = this.client.getMaxLeverage(symbol5);
|
|
215334
|
+
if (typeof lev === "number" && lev > 0)
|
|
215335
|
+
maxLeverages[symbol5] = lev;
|
|
215336
|
+
tokens.push(isDelisted ? { symbol: symbol5, isDelisted: true } : { symbol: symbol5 });
|
|
215337
|
+
}
|
|
215338
|
+
tokens.sort((a, b5) => a.symbol.localeCompare(b5.symbol));
|
|
215339
|
+
return { tokens, prices, prevDayPrices, maxLeverages };
|
|
215340
|
+
}
|
|
215341
|
+
}
|
|
215342
|
+
|
|
214781
215343
|
// node_modules/viem/_esm/utils/address/isAddressEqual.js
|
|
214782
215344
|
function isAddressEqual(a, b5) {
|
|
214783
215345
|
if (!isAddress(a, { strict: false }))
|
|
@@ -214901,7 +215463,12 @@ function createGateway(gatewayConfig, deps) {
|
|
|
214901
215463
|
registerToolsMethods(registry4.register.bind(registry4), { tools: deps.tools });
|
|
214902
215464
|
registerSessionsMethods(registry4.register.bind(registry4), { sessionManager: deps.sessionManager });
|
|
214903
215465
|
registerCronMethods(registry4.register.bind(registry4), { cronService: deps.cronService });
|
|
214904
|
-
|
|
215466
|
+
registerConfigMethods(registry4.register.bind(registry4), {
|
|
215467
|
+
timezoneService: deps.timezoneService,
|
|
215468
|
+
cronService: deps.cronService
|
|
215469
|
+
});
|
|
215470
|
+
const tokensSnapshot = new TokensSnapshotService(deps.tradingClient, deps.priceCache);
|
|
215471
|
+
registerTradingMethods(registry4.register.bind(registry4), { tradingClient: deps.tradingClient, walletStore: deps.walletStore, alertRules: deps.alertRules, notifications: deps.notifications, newsService: deps.newsService, rssDiscovery: deps.rssDiscoveryService, tweetService: deps.tweetService, xFollowService: deps.xFollowService, preferenceStore: deps.preferenceStore, watchlist: deps.watchlistService, logger: deps.logger, tokensSnapshot, priceCache: deps.priceCache });
|
|
214905
215472
|
registerApprovalMethods(registry4.register.bind(registry4), { approvalManager: deps.approvalManager });
|
|
214906
215473
|
registerToolApprovalMethods(registry4.register.bind(registry4), { approvalManager: deps.approvalManager });
|
|
214907
215474
|
registerSkillsMethods(registry4.register.bind(registry4), { skillService: deps.skillService });
|
|
@@ -214930,10 +215497,6 @@ function createGateway(gatewayConfig, deps) {
|
|
|
214930
215497
|
deps.eventBus.publish(TradingEvents.watchlistChanged({ action, symbol: symbol5 }));
|
|
214931
215498
|
});
|
|
214932
215499
|
const lastPrices = new Map;
|
|
214933
|
-
let watchedSymbols = new Set(deps.watchlistService.list().map((i) => i.symbol));
|
|
214934
|
-
deps.watchlistService.onChanged(() => {
|
|
214935
|
-
watchedSymbols = new Set(deps.watchlistService.list().map((i) => i.symbol));
|
|
214936
|
-
});
|
|
214937
215500
|
const priceFeedConfig = deps.config.priceFeed;
|
|
214938
215501
|
const priceFeedLogger = deps.logger.child({ module: "price-feed" });
|
|
214939
215502
|
function buildPriceSources() {
|
|
@@ -214956,15 +215519,13 @@ function createGateway(gatewayConfig, deps) {
|
|
|
214956
215519
|
staleThresholdMs: priceFeedConfig.staleThresholdMs,
|
|
214957
215520
|
stabilityWindowMs: priceFeedConfig.stabilityWindowMs
|
|
214958
215521
|
}, priceFeedLogger);
|
|
214959
|
-
function broadcastPrice(symbol5, price) {
|
|
215522
|
+
function broadcastPrice(symbol5, price, prevDayPrice) {
|
|
215523
|
+
deps.priceCache.set(symbol5, price, prevDayPrice);
|
|
214960
215524
|
const prev = lastPrices.get(symbol5);
|
|
214961
|
-
if (prev === price)
|
|
215525
|
+
if (prev === price && prevDayPrice === undefined)
|
|
214962
215526
|
return;
|
|
214963
215527
|
lastPrices.set(symbol5, price);
|
|
214964
|
-
deps.
|
|
214965
|
-
if (watchedSymbols.has(symbol5)) {
|
|
214966
|
-
deps.eventBus.publish(TradingEvents.priceUpdate({ symbol: symbol5, price }));
|
|
214967
|
-
}
|
|
215528
|
+
deps.eventBus.publish(TradingEvents.priceUpdate({ symbol: symbol5, price, prevDayPrice }));
|
|
214968
215529
|
}
|
|
214969
215530
|
async function startPriceFeed() {
|
|
214970
215531
|
if (!priceFeedConfig.enabled) {
|
|
@@ -214973,7 +215534,7 @@ function createGateway(gatewayConfig, deps) {
|
|
|
214973
215534
|
}
|
|
214974
215535
|
lastPrices.clear();
|
|
214975
215536
|
try {
|
|
214976
|
-
await compositeFeed.start((symbol5, price) => broadcastPrice(symbol5, price));
|
|
215537
|
+
await compositeFeed.start((symbol5, price, prevDayPrice) => broadcastPrice(symbol5, price, prevDayPrice));
|
|
214977
215538
|
} catch (err) {
|
|
214978
215539
|
priceFeedLogger.warn({ err }, "composite price feed failed to start");
|
|
214979
215540
|
}
|
|
@@ -215202,7 +215763,6 @@ var init_server = __esm(() => {
|
|
|
215202
215763
|
init_wallet_events();
|
|
215203
215764
|
init_trading_events();
|
|
215204
215765
|
init_composite3();
|
|
215205
|
-
init_hyperliquid();
|
|
215206
215766
|
init_binance2();
|
|
215207
215767
|
init_accounts();
|
|
215208
215768
|
init__esm();
|
|
@@ -215229,12 +215789,16 @@ function snapshotRecentUserMessages(sessionManager, limit2) {
|
|
|
215229
215789
|
const out = [];
|
|
215230
215790
|
for (const msg of session.messages) {
|
|
215231
215791
|
const m6 = msg;
|
|
215232
|
-
if (m6.role !== "user"
|
|
215792
|
+
if (m6.role !== "user")
|
|
215233
215793
|
continue;
|
|
215234
215794
|
let text = "";
|
|
215235
|
-
|
|
215236
|
-
|
|
215237
|
-
|
|
215795
|
+
if (typeof m6.content === "string") {
|
|
215796
|
+
text = m6.content;
|
|
215797
|
+
} else if (Array.isArray(m6.content)) {
|
|
215798
|
+
for (const block of m6.content) {
|
|
215799
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
215800
|
+
text += block.text;
|
|
215801
|
+
}
|
|
215238
215802
|
}
|
|
215239
215803
|
}
|
|
215240
215804
|
const trimmed = text.trim();
|
|
@@ -215935,6 +216499,7 @@ async function startDaemon(options) {
|
|
|
215935
216499
|
tweetService,
|
|
215936
216500
|
xFollowService,
|
|
215937
216501
|
preferenceStore,
|
|
216502
|
+
timezoneService,
|
|
215938
216503
|
security: security2,
|
|
215939
216504
|
leakDetector,
|
|
215940
216505
|
skillService,
|
|
@@ -215980,6 +216545,7 @@ async function startDaemon(options) {
|
|
|
215980
216545
|
tools,
|
|
215981
216546
|
sessionManager,
|
|
215982
216547
|
cronService,
|
|
216548
|
+
timezoneService,
|
|
215983
216549
|
configPath,
|
|
215984
216550
|
channels: runtime.channelManager.listChannels().map((ch2) => ({ name: ch2.name })),
|
|
215985
216551
|
tradingClient,
|
|
@@ -216021,7 +216587,7 @@ async function startDaemon(options) {
|
|
|
216021
216587
|
}));
|
|
216022
216588
|
runtime.channelManager.startAllChannels();
|
|
216023
216589
|
if (config3.cron.enableScheduler) {
|
|
216024
|
-
cronService.start();
|
|
216590
|
+
cronService.start({ defaults: buildBuiltInJobs(timezoneService.get()) });
|
|
216025
216591
|
}
|
|
216026
216592
|
const runner = new BackgroundJobRunner({
|
|
216027
216593
|
taskAgent: runtime.taskAgent,
|
|
@@ -216035,6 +216601,9 @@ async function startDaemon(options) {
|
|
|
216035
216601
|
runner.start();
|
|
216036
216602
|
runtime.xFollowService.onEnable(() => runner.kick("tweet-fetch"));
|
|
216037
216603
|
await runtime.walletReady;
|
|
216604
|
+
await tradingClient.ensureMeta().catch((err) => {
|
|
216605
|
+
logger.warn({ err }, "ensureMeta on startup failed \u2014 will retry on first use");
|
|
216606
|
+
});
|
|
216038
216607
|
app.listen({ port: config3.gateway.port, hostname: config3.gateway.host });
|
|
216039
216608
|
printDaemonStartupBanner({
|
|
216040
216609
|
runtime,
|
|
@@ -216061,6 +216630,7 @@ var init_daemon = __esm(() => {
|
|
|
216061
216630
|
init_plugin();
|
|
216062
216631
|
init_types5();
|
|
216063
216632
|
init_pairing_events();
|
|
216633
|
+
init_defaults();
|
|
216064
216634
|
});
|
|
216065
216635
|
|
|
216066
216636
|
// node_modules/colorette/index.cjs
|
|
@@ -218686,7 +219256,7 @@ __export(exports_uninstall, {
|
|
|
218686
219256
|
WIN_HANDLE_RELEASE_MS: () => WIN_HANDLE_RELEASE_MS,
|
|
218687
219257
|
SIGKILL_DELAY_MS: () => SIGKILL_DELAY_MS
|
|
218688
219258
|
});
|
|
218689
|
-
import { existsSync as fsExistsSync, readFileSync as
|
|
219259
|
+
import { existsSync as fsExistsSync, readFileSync as readFileSync19, writeFileSync as writeFileSync14, unlinkSync as unlinkSync9, rmSync as fsRmSync } from "fs";
|
|
218690
219260
|
import { join as join22 } from "path";
|
|
218691
219261
|
import { homedir as homedir7 } from "os";
|
|
218692
219262
|
async function runUninstall(deps) {
|
|
@@ -219012,8 +219582,8 @@ async function runUninstallCli() {
|
|
|
219012
219582
|
return !isCancel(r2) && r2 === true;
|
|
219013
219583
|
},
|
|
219014
219584
|
existsSync: fsExistsSync,
|
|
219015
|
-
readFile: (p2) =>
|
|
219016
|
-
writeFile: (p2, content) =>
|
|
219585
|
+
readFile: (p2) => readFileSync19(p2, "utf8"),
|
|
219586
|
+
writeFile: (p2, content) => writeFileSync14(p2, content, "utf8"),
|
|
219017
219587
|
unlink: (p2) => unlinkSync9(p2),
|
|
219018
219588
|
rmSync: (p2) => fsRmSync(p2, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }),
|
|
219019
219589
|
spawn: (cmd, args) => {
|
|
@@ -219469,7 +220039,7 @@ init_cli_providers();
|
|
|
219469
220039
|
|
|
219470
220040
|
// src/onboard/wizard.ts
|
|
219471
220041
|
init_dist8();
|
|
219472
|
-
import { existsSync as
|
|
220042
|
+
import { existsSync as existsSync27 } from "fs";
|
|
219473
220043
|
init_providers();
|
|
219474
220044
|
init_oauth3();
|
|
219475
220045
|
init_secrets();
|
|
@@ -219483,7 +220053,7 @@ init_dist8();
|
|
|
219483
220053
|
init_dist8();
|
|
219484
220054
|
init_utils8();
|
|
219485
220055
|
init_logs();
|
|
219486
|
-
import { existsSync as
|
|
220056
|
+
import { existsSync as existsSync25 } from "fs";
|
|
219487
220057
|
|
|
219488
220058
|
// src/onboard/steps/service.ts
|
|
219489
220059
|
async function runServiceStep(deps) {
|
|
@@ -219578,7 +220148,7 @@ async function finalizeOnboard(opts) {
|
|
|
219578
220148
|
}
|
|
219579
220149
|
const execPath = resolveGhostExecPath();
|
|
219580
220150
|
const bunPath = resolveBunPath();
|
|
219581
|
-
if (!
|
|
220151
|
+
if (!existsSync25(execPath)) {
|
|
219582
220152
|
O2.warn(`Ghost executable not found at ${execPath}. Service may fail to start.`);
|
|
219583
220153
|
}
|
|
219584
220154
|
const result = await runServiceStep({
|
|
@@ -219654,22 +220224,22 @@ async function startForegroundDaemon(logger) {
|
|
|
219654
220224
|
init_models_config();
|
|
219655
220225
|
import {
|
|
219656
220226
|
chmodSync,
|
|
219657
|
-
existsSync as
|
|
219658
|
-
mkdirSync as
|
|
219659
|
-
readFileSync as
|
|
220227
|
+
existsSync as existsSync26,
|
|
220228
|
+
mkdirSync as mkdirSync17,
|
|
220229
|
+
readFileSync as readFileSync18,
|
|
219660
220230
|
renameSync as renameSync4,
|
|
219661
220231
|
unlinkSync as unlinkSync8,
|
|
219662
|
-
writeFileSync as
|
|
220232
|
+
writeFileSync as writeFileSync13
|
|
219663
220233
|
} from "fs";
|
|
219664
|
-
import { dirname as
|
|
220234
|
+
import { dirname as dirname9 } from "path";
|
|
219665
220235
|
var MODELS_FILE_MODE = 384;
|
|
219666
220236
|
var MODELS_DIR_MODE = 448;
|
|
219667
220237
|
function readModelsConfig(path4) {
|
|
219668
|
-
if (!
|
|
220238
|
+
if (!existsSync26(path4))
|
|
219669
220239
|
return { kind: "missing" };
|
|
219670
220240
|
let raw;
|
|
219671
220241
|
try {
|
|
219672
|
-
raw =
|
|
220242
|
+
raw = readFileSync18(path4, "utf-8");
|
|
219673
220243
|
} catch (err) {
|
|
219674
220244
|
return { kind: "malformed", reason: describeError2(err) };
|
|
219675
220245
|
}
|
|
@@ -219730,15 +220300,15 @@ function upsertModel(current, incoming) {
|
|
|
219730
220300
|
return next;
|
|
219731
220301
|
}
|
|
219732
220302
|
function writeAtomic(path4, contents) {
|
|
219733
|
-
|
|
220303
|
+
mkdirSync17(dirname9(path4), { recursive: true, mode: MODELS_DIR_MODE });
|
|
219734
220304
|
const tmp = `${path4}.tmp-${process.pid}-${Date.now()}`;
|
|
219735
220305
|
try {
|
|
219736
|
-
|
|
220306
|
+
writeFileSync13(tmp, contents, { mode: MODELS_FILE_MODE });
|
|
219737
220307
|
chmodSync(tmp, MODELS_FILE_MODE);
|
|
219738
220308
|
renameSync4(tmp, path4);
|
|
219739
220309
|
} catch (err) {
|
|
219740
220310
|
try {
|
|
219741
|
-
if (
|
|
220311
|
+
if (existsSync26(tmp))
|
|
219742
220312
|
unlinkSync8(tmp);
|
|
219743
220313
|
} catch {}
|
|
219744
220314
|
throw err;
|
|
@@ -219764,6 +220334,22 @@ function applyUpdateModeChanges(existing, overlay) {
|
|
|
219764
220334
|
}
|
|
219765
220335
|
|
|
219766
220336
|
// src/onboard/wizard.ts
|
|
220337
|
+
init_timezone();
|
|
220338
|
+
init_database();
|
|
220339
|
+
init_db();
|
|
220340
|
+
init_registry2();
|
|
220341
|
+
async function persistTimezone(tz, logger) {
|
|
220342
|
+
try {
|
|
220343
|
+
const db2 = initDatabase(getDbPath());
|
|
220344
|
+
await runDbMigrations(db2, DB_MIGRATIONS);
|
|
220345
|
+
const prefs = new PreferenceStore(db2, logger.child({ module: "prefs" }));
|
|
220346
|
+
const tzService = createTimezoneService(prefs);
|
|
220347
|
+
tzService.set(tz);
|
|
220348
|
+
db2.close();
|
|
220349
|
+
} catch (err) {
|
|
220350
|
+
logger.warn({ err }, "onboard: failed to persist timezone");
|
|
220351
|
+
}
|
|
220352
|
+
}
|
|
219767
220353
|
function validateCustomProviderName(name) {
|
|
219768
220354
|
if (!name)
|
|
219769
220355
|
return "Provider name is required.";
|
|
@@ -219778,31 +220364,7 @@ function validateCustomProviderName(name) {
|
|
|
219778
220364
|
function isLocalBaseUrl(url3) {
|
|
219779
220365
|
return /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/u.test(url3);
|
|
219780
220366
|
}
|
|
219781
|
-
|
|
219782
|
-
const which = Bun.spawnSync({ cmd: ["which", binaryPath] });
|
|
219783
|
-
if (which.exitCode !== 0) {
|
|
219784
|
-
return { ok: false, error: `Claude Code not found at "${binaryPath}". Install: curl -fsSL https://claude.ai/install.sh | bash` };
|
|
219785
|
-
}
|
|
219786
|
-
const versionProc = Bun.spawnSync({ cmd: [binaryPath, "--version"] });
|
|
219787
|
-
const version3 = versionProc.stdout.toString().trim();
|
|
219788
|
-
const authProc = Bun.spawnSync({ cmd: [binaryPath, "auth", "status", "--json"] });
|
|
219789
|
-
if (authProc.exitCode !== 0) {
|
|
219790
|
-
return { ok: false, version: version3, error: "Not authenticated. Run: claude login" };
|
|
219791
|
-
}
|
|
219792
|
-
try {
|
|
219793
|
-
const auth = JSON.parse(authProc.stdout.toString());
|
|
219794
|
-
const authenticated = auth.authenticated ?? auth.loggedIn ?? false;
|
|
219795
|
-
if (!authenticated) {
|
|
219796
|
-
return { ok: false, version: version3, error: "Not authenticated. Run: claude login" };
|
|
219797
|
-
}
|
|
219798
|
-
return { ok: true, version: version3, authStatus: auth.plan ?? auth.subscription ?? "authenticated" };
|
|
219799
|
-
} catch {
|
|
219800
|
-
return { ok: true, version: version3, authStatus: "authenticated" };
|
|
219801
|
-
}
|
|
219802
|
-
}
|
|
219803
|
-
function providerRequiresApiKey(providerId, supportsOAuth) {
|
|
219804
|
-
if (providerId === "claude-cli")
|
|
219805
|
-
return false;
|
|
220367
|
+
function providerRequiresApiKey(_providerId, supportsOAuth) {
|
|
219806
220368
|
if (supportsOAuth)
|
|
219807
220369
|
return false;
|
|
219808
220370
|
return true;
|
|
@@ -219830,6 +220392,8 @@ async function runHeadless(headless, daemonOptions) {
|
|
|
219830
220392
|
if (daemonOptions.paper)
|
|
219831
220393
|
customConfig.paper = daemonOptions.paper;
|
|
219832
220394
|
saveConfig(customConfig, configPath);
|
|
220395
|
+
const tzForCustom = resolveHeadlessTz(daemonOptions.logger);
|
|
220396
|
+
await persistTimezone(tzForCustom, daemonOptions.logger);
|
|
219833
220397
|
console.log(`[ghost] Custom provider: ${headless.provider} (from models.json)`);
|
|
219834
220398
|
console.log(`[ghost] Model: ${modelTrimmed}`);
|
|
219835
220399
|
if (daemonOptions?.paper) {
|
|
@@ -219905,8 +220469,11 @@ async function runHeadless(headless, daemonOptions) {
|
|
|
219905
220469
|
await credentials2.set("api_key", apiKey);
|
|
219906
220470
|
}
|
|
219907
220471
|
saveConfig(config3, configPath);
|
|
220472
|
+
const tz = resolveHeadlessTz(daemonOptions.logger);
|
|
220473
|
+
await persistTimezone(tz, daemonOptions.logger);
|
|
219908
220474
|
console.log(`[ghost] Provider: ${providerInfo.label} (${headless.provider})`);
|
|
219909
220475
|
console.log(`[ghost] Model: ${model}`);
|
|
220476
|
+
console.log(`[ghost] Timezone: ${tz}`);
|
|
219910
220477
|
if (daemonOptions?.paper) {
|
|
219911
220478
|
console.log(`[ghost] Mode: Paper trading (${daemonOptions.paper.initialBalance ?? 1e4} USDC)`);
|
|
219912
220479
|
}
|
|
@@ -219914,6 +220481,18 @@ async function runHeadless(headless, daemonOptions) {
|
|
|
219914
220481
|
console.log("[ghost] Config saved. Run 'ghost onboard --service' to register the auto-start service, or 'ghost daemon' to start manually.");
|
|
219915
220482
|
console.log("[ghost] Onboard complete!");
|
|
219916
220483
|
}
|
|
220484
|
+
function resolveHeadlessTz(logger) {
|
|
220485
|
+
const envTz = process.env["GHOST_TIMEZONE"];
|
|
220486
|
+
if (envTz) {
|
|
220487
|
+
const result = validateTimezone(envTz);
|
|
220488
|
+
if (!result.ok) {
|
|
220489
|
+
console.error(`[ghost] GHOST_TIMEZONE="${envTz}" is invalid: ${result.error}`);
|
|
220490
|
+
process.exit(1);
|
|
220491
|
+
}
|
|
220492
|
+
return result.tz;
|
|
220493
|
+
}
|
|
220494
|
+
return detectHostTimezone();
|
|
220495
|
+
}
|
|
219917
220496
|
async function runWizard(daemonOptions) {
|
|
219918
220497
|
if (daemonOptions.headless) {
|
|
219919
220498
|
const { headless, ...rest4 } = daemonOptions;
|
|
@@ -219926,7 +220505,7 @@ async function runWizard(daemonOptions) {
|
|
|
219926
220505
|
const credentials2 = new CredentialStore(getCredentialsPath(), secretStore, daemonOptions.logger.child({ module: "credentials" }));
|
|
219927
220506
|
let config3 = configSchema.parse({});
|
|
219928
220507
|
let mode = "full";
|
|
219929
|
-
if (
|
|
220508
|
+
if (existsSync27(configPath)) {
|
|
219930
220509
|
const modeAnswer = await _t({
|
|
219931
220510
|
message: "Existing config found. What would you like to do?",
|
|
219932
220511
|
options: [
|
|
@@ -219945,7 +220524,7 @@ async function runWizard(daemonOptions) {
|
|
|
219945
220524
|
This wizard will configure your agent in under 60 seconds.`);
|
|
219946
220525
|
if (mode === "full" && !daemonOptions.paper) {
|
|
219947
220526
|
const tradingMode = await _t({
|
|
219948
|
-
message: "Step 1/
|
|
220527
|
+
message: "Step 1/5 \u2014 Select trading mode",
|
|
219949
220528
|
options: [
|
|
219950
220529
|
{ value: "paper", label: "Paper trading (simulated, safe to explore)", hint: "10,000 USDC starting balance" },
|
|
219951
220530
|
{ value: "live", label: "Live trading (real funds on Hyperliquid)" }
|
|
@@ -219963,11 +220542,11 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
219963
220542
|
const providers = getProviderList();
|
|
219964
220543
|
const providerOptions = providers.map((p2) => ({
|
|
219965
220544
|
value: p2.id,
|
|
219966
|
-
label: p2.label
|
|
220545
|
+
label: `${p2.label} \u2014 ${p2.tierLabel}`,
|
|
219967
220546
|
hint: p2.description
|
|
219968
220547
|
}));
|
|
219969
220548
|
const providerId = await _t({
|
|
219970
|
-
message: "Step 2/
|
|
220549
|
+
message: "Step 2/5 \u2014 Select your AI provider",
|
|
219971
220550
|
options: providerOptions
|
|
219972
220551
|
});
|
|
219973
220552
|
if (q(providerId)) {
|
|
@@ -219978,18 +220557,6 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
219978
220557
|
let authMethod = "apikey";
|
|
219979
220558
|
let customProviderName = "";
|
|
219980
220559
|
let customApiKey = "";
|
|
219981
|
-
if (providerId === "claude-cli") {
|
|
219982
|
-
const s1 = fe2();
|
|
219983
|
-
s1.start("Validating Claude Code...");
|
|
219984
|
-
const validation2 = await validateClaudeCli("claude");
|
|
219985
|
-
if (!validation2.ok) {
|
|
219986
|
-
s1.stop(`\u2717 ${validation2.error}`);
|
|
219987
|
-
pt("Fix the issue above and try again.");
|
|
219988
|
-
process.exit(1);
|
|
219989
|
-
}
|
|
219990
|
-
s1.stop(`\u2713 Claude Code ${validation2.version ?? ""} (${validation2.authStatus})`);
|
|
219991
|
-
authMethod = "skip";
|
|
219992
|
-
}
|
|
219993
220560
|
if (providerId === "custom") {
|
|
219994
220561
|
const nameAnswer = await Ot({
|
|
219995
220562
|
message: "Provider name (identifier for ~/.ghost/models.json)",
|
|
@@ -220036,7 +220603,7 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220036
220603
|
{ value: "__custom__", label: "Custom model ID (type manually)" }
|
|
220037
220604
|
];
|
|
220038
220605
|
const selected = await _t({
|
|
220039
|
-
message: "Step 3/
|
|
220606
|
+
message: "Step 3/5 \u2014 Select your default model",
|
|
220040
220607
|
options: modelOptions
|
|
220041
220608
|
});
|
|
220042
220609
|
if (q(selected)) {
|
|
@@ -220055,7 +220622,7 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220055
220622
|
}
|
|
220056
220623
|
} else {
|
|
220057
220624
|
const manual = await Ot({
|
|
220058
|
-
message: "Step 3/
|
|
220625
|
+
message: "Step 3/5 \u2014 Enter model ID",
|
|
220059
220626
|
placeholder: "e.g. claude-sonnet-4-6"
|
|
220060
220627
|
});
|
|
220061
220628
|
if (q(manual)) {
|
|
@@ -220068,7 +220635,7 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220068
220635
|
let apiKey = "";
|
|
220069
220636
|
if (providerInfo?.supportsOAuth && authMethod !== "skip") {
|
|
220070
220637
|
const auth = await _t({
|
|
220071
|
-
message: "Step 4/
|
|
220638
|
+
message: "Step 4/5 \u2014 How do you want to authenticate?",
|
|
220072
220639
|
options: [
|
|
220073
220640
|
{ value: "oauth", label: "OAuth Login (authenticate in browser)", hint: "recommended" },
|
|
220074
220641
|
{ value: "apikey", label: "API Key (paste your key)" },
|
|
@@ -220104,7 +220671,7 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220104
220671
|
const keyUrl = providerInfo?.apiKeyUrl ? `
|
|
220105
220672
|
Get your key at: ${providerInfo.apiKeyUrl}` : "";
|
|
220106
220673
|
const key = await Ot({
|
|
220107
|
-
message: `Step 4/
|
|
220674
|
+
message: `Step 4/5 \u2014 Paste your ${providerInfo?.label ?? providerId} API key${keyUrl}`,
|
|
220108
220675
|
placeholder: "sk-...",
|
|
220109
220676
|
validate: (v4) => !v4 || v4.length >= 5 ? undefined : "API key seems too short"
|
|
220110
220677
|
});
|
|
@@ -220114,7 +220681,7 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220114
220681
|
}
|
|
220115
220682
|
apiKey = key;
|
|
220116
220683
|
}
|
|
220117
|
-
if (authMethod === "skip" && providerId !== "
|
|
220684
|
+
if (authMethod === "skip" && providerId !== "custom") {
|
|
220118
220685
|
O2.warn("No API key set. Export GHOST_API_KEY before running ghost daemon.");
|
|
220119
220686
|
}
|
|
220120
220687
|
if (mode === "update") {
|
|
@@ -220172,14 +220739,59 @@ This wizard will configure your agent in under 60 seconds.`);
|
|
|
220172
220739
|
config3.paper = daemonOptions.paper;
|
|
220173
220740
|
}
|
|
220174
220741
|
saveConfig(config3, configPath);
|
|
220742
|
+
const chosenTz = await promptTimezone();
|
|
220743
|
+
await persistTimezone(chosenTz, daemonOptions.logger);
|
|
220175
220744
|
O2.success("Configuration saved.");
|
|
220176
220745
|
if (providerId === "custom") {
|
|
220177
220746
|
O2.info(`Custom provider "${customProviderName}" written to ${getModelsConfigPath()}`);
|
|
220178
220747
|
}
|
|
220748
|
+
O2.info(`Timezone: ${chosenTz}`);
|
|
220179
220749
|
O2.info("Tip: connect channels from the dashboard after starting the daemon.");
|
|
220180
220750
|
console.log("");
|
|
220181
220751
|
await finalizeOnboard({ interactive: true, logger: daemonOptions.logger });
|
|
220182
220752
|
}
|
|
220753
|
+
function listSupportedTimezones() {
|
|
220754
|
+
const fn = Intl.supportedValuesOf;
|
|
220755
|
+
if (typeof fn !== "function")
|
|
220756
|
+
return [];
|
|
220757
|
+
return [...fn("timeZone")].sort();
|
|
220758
|
+
}
|
|
220759
|
+
async function promptTimezone() {
|
|
220760
|
+
const detected = detectHostTimezone();
|
|
220761
|
+
const allZones = listSupportedTimezones();
|
|
220762
|
+
if (allZones.length === 0) {
|
|
220763
|
+
const entered = await Ot({
|
|
220764
|
+
message: "Step 5/5 \u2014 IANA timezone (e.g. America/New_York):",
|
|
220765
|
+
initialValue: detected,
|
|
220766
|
+
validate(value2) {
|
|
220767
|
+
const v4 = validateTimezone(value2);
|
|
220768
|
+
return v4.ok ? undefined : v4.error;
|
|
220769
|
+
}
|
|
220770
|
+
});
|
|
220771
|
+
if (q(entered)) {
|
|
220772
|
+
pt("Setup cancelled.");
|
|
220773
|
+
process.exit(0);
|
|
220774
|
+
}
|
|
220775
|
+
const result = validateTimezone(entered);
|
|
220776
|
+
return result.ok ? result.tz : detected;
|
|
220777
|
+
}
|
|
220778
|
+
const initialValue = allZones.includes(detected) ? detected : allZones[0];
|
|
220779
|
+
const choice = await Ae2({
|
|
220780
|
+
message: "Step 5/5 \u2014 Select your timezone (type to filter)",
|
|
220781
|
+
placeholder: "e.g. berlin, new_york, utc",
|
|
220782
|
+
options: allZones.map((tz) => ({
|
|
220783
|
+
value: tz,
|
|
220784
|
+
label: tz === detected ? `${tz} (detected)` : tz
|
|
220785
|
+
})),
|
|
220786
|
+
initialValue,
|
|
220787
|
+
maxItems: 10
|
|
220788
|
+
});
|
|
220789
|
+
if (q(choice)) {
|
|
220790
|
+
pt("Setup cancelled.");
|
|
220791
|
+
process.exit(0);
|
|
220792
|
+
}
|
|
220793
|
+
return choice;
|
|
220794
|
+
}
|
|
220183
220795
|
// src/index.ts
|
|
220184
220796
|
init_errors2();
|
|
220185
220797
|
init_logger();
|
|
@@ -220410,14 +221022,14 @@ async function runSkills(subArgs, opts) {
|
|
|
220410
221022
|
const configPath = opts.config ?? getPath();
|
|
220411
221023
|
const config3 = loadConfig2(configPath);
|
|
220412
221024
|
const workspaceDir = getWorkspaceDir2();
|
|
220413
|
-
const { existsSync:
|
|
221025
|
+
const { existsSync: existsSync28 } = await import("fs");
|
|
220414
221026
|
const { join: join23 } = await import("path");
|
|
220415
221027
|
let builtinDir;
|
|
220416
221028
|
if (config3.skills.builtinSkillsDir) {
|
|
220417
221029
|
builtinDir = expandHome2(config3.skills.builtinSkillsDir);
|
|
220418
221030
|
} else {
|
|
220419
221031
|
const candidate = join23(import.meta.dir, "skills", "builtin");
|
|
220420
|
-
if (
|
|
221032
|
+
if (existsSync28(candidate))
|
|
220421
221033
|
builtinDir = candidate;
|
|
220422
221034
|
}
|
|
220423
221035
|
const loader2 = new SkillsLoader2(workspaceDir, builtinDir);
|