@raintree-technology/perps 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/adapters/aevo.d.ts +64 -0
- package/dist/adapters/aevo.js +899 -0
- package/dist/adapters/certification.d.ts +33 -0
- package/dist/adapters/certification.js +99 -0
- package/dist/adapters/decibel/order-manager.d.ts +45 -0
- package/dist/adapters/decibel/order-manager.js +140 -0
- package/dist/adapters/decibel/rest-client.d.ts +176 -0
- package/dist/adapters/decibel/rest-client.js +155 -0
- package/dist/adapters/decibel/ws-feed.d.ts +28 -0
- package/dist/adapters/decibel/ws-feed.js +166 -0
- package/dist/adapters/decibel.d.ts +108 -0
- package/dist/adapters/decibel.js +1377 -0
- package/dist/adapters/hyperliquid.d.ts +63 -0
- package/dist/adapters/hyperliquid.js +797 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/interface.d.ts +310 -0
- package/dist/adapters/interface.js +15 -0
- package/dist/adapters/orderly.d.ts +70 -0
- package/dist/adapters/orderly.js +936 -0
- package/dist/adapters/paradex.d.ts +69 -0
- package/dist/adapters/paradex.js +862 -0
- package/dist/adapters/utils.d.ts +17 -0
- package/dist/adapters/utils.js +122 -0
- package/dist/cli/command-metadata.d.ts +2 -0
- package/dist/cli/command-metadata.js +44 -0
- package/dist/cli/context.d.ts +14 -0
- package/dist/cli/context.js +59 -0
- package/dist/cli/experience.d.ts +48 -0
- package/dist/cli/experience.js +243 -0
- package/dist/cli/ink/app/AppShell.d.ts +12 -0
- package/dist/cli/ink/app/AppShell.js +32 -0
- package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
- package/dist/cli/ink/app/MetricStrip.js +14 -0
- package/dist/cli/ink/app/Panel.d.ts +9 -0
- package/dist/cli/ink/app/Panel.js +7 -0
- package/dist/cli/ink/app/ascii.d.ts +2 -0
- package/dist/cli/ink/app/ascii.js +46 -0
- package/dist/cli/ink/app/index.d.ts +5 -0
- package/dist/cli/ink/app/index.js +4 -0
- package/dist/cli/ink/app/types.d.ts +15 -0
- package/dist/cli/ink/app/types.js +1 -0
- package/dist/cli/ink/components/PnL.d.ts +12 -0
- package/dist/cli/ink/components/PnL.js +23 -0
- package/dist/cli/ink/components/Spinner.d.ts +13 -0
- package/dist/cli/ink/components/Spinner.js +13 -0
- package/dist/cli/ink/components/Table.d.ts +14 -0
- package/dist/cli/ink/components/Table.js +42 -0
- package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
- package/dist/cli/ink/components/WatchHeader.js +18 -0
- package/dist/cli/ink/components/index.d.ts +4 -0
- package/dist/cli/ink/components/index.js +4 -0
- package/dist/cli/ink/index.d.ts +4 -0
- package/dist/cli/ink/index.js +4 -0
- package/dist/cli/ink/render.d.ts +12 -0
- package/dist/cli/ink/render.js +21 -0
- package/dist/cli/ink/theme.d.ts +29 -0
- package/dist/cli/ink/theme.js +40 -0
- package/dist/cli/network-defaults.d.ts +10 -0
- package/dist/cli/network-defaults.js +35 -0
- package/dist/cli/output.d.ts +11 -0
- package/dist/cli/output.js +115 -0
- package/dist/cli/program.d.ts +18 -0
- package/dist/cli/program.js +164 -0
- package/dist/cli/watch.d.ts +19 -0
- package/dist/cli/watch.js +35 -0
- package/dist/client/index.d.ts +55 -0
- package/dist/client/index.js +157 -0
- package/dist/commands/account/add.d.ts +2 -0
- package/dist/commands/account/add.js +510 -0
- package/dist/commands/account/balances-simple.d.ts +5 -0
- package/dist/commands/account/balances-simple.js +63 -0
- package/dist/commands/account/index.d.ts +2 -0
- package/dist/commands/account/index.js +17 -0
- package/dist/commands/account/ls.d.ts +2 -0
- package/dist/commands/account/ls.js +95 -0
- package/dist/commands/account/positions-simple.d.ts +5 -0
- package/dist/commands/account/positions-simple.js +77 -0
- package/dist/commands/account/remove.d.ts +2 -0
- package/dist/commands/account/remove.js +47 -0
- package/dist/commands/account/set-default.d.ts +2 -0
- package/dist/commands/account/set-default.js +47 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +126 -0
- package/dist/commands/arb/alert.d.ts +6 -0
- package/dist/commands/arb/alert.js +88 -0
- package/dist/commands/arb/basis-execute.d.ts +6 -0
- package/dist/commands/arb/basis-execute.js +332 -0
- package/dist/commands/arb/basis.d.ts +6 -0
- package/dist/commands/arb/basis.js +181 -0
- package/dist/commands/arb/compare.d.ts +6 -0
- package/dist/commands/arb/compare.js +216 -0
- package/dist/commands/arb/execute.d.ts +6 -0
- package/dist/commands/arb/execute.js +467 -0
- package/dist/commands/arb/funding.d.ts +6 -0
- package/dist/commands/arb/funding.js +201 -0
- package/dist/commands/arb/history.d.ts +6 -0
- package/dist/commands/arb/history.js +153 -0
- package/dist/commands/arb/index.d.ts +6 -0
- package/dist/commands/arb/index.js +29 -0
- package/dist/commands/arb/positions.d.ts +6 -0
- package/dist/commands/arb/positions.js +158 -0
- package/dist/commands/arb/spread.d.ts +6 -0
- package/dist/commands/arb/spread.js +253 -0
- package/dist/commands/arb/track.d.ts +6 -0
- package/dist/commands/arb/track.js +259 -0
- package/dist/commands/asset/book-simple.d.ts +5 -0
- package/dist/commands/asset/book-simple.js +77 -0
- package/dist/commands/asset/index.d.ts +2 -0
- package/dist/commands/asset/index.js +5 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +161 -0
- package/dist/commands/config/index.d.ts +5 -0
- package/dist/commands/config/index.js +109 -0
- package/dist/commands/data/index.d.ts +31 -0
- package/dist/commands/data/index.js +1466 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +201 -0
- package/dist/commands/exchange/index.d.ts +2 -0
- package/dist/commands/exchange/index.js +107 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +48 -0
- package/dist/commands/markets/index.d.ts +2 -0
- package/dist/commands/markets/index.js +5 -0
- package/dist/commands/markets/ls-simple.d.ts +7 -0
- package/dist/commands/markets/ls-simple.js +277 -0
- package/dist/commands/operator/index.d.ts +2 -0
- package/dist/commands/operator/index.js +146 -0
- package/dist/commands/order/cancel-simple.d.ts +5 -0
- package/dist/commands/order/cancel-simple.js +104 -0
- package/dist/commands/order/index.d.ts +2 -0
- package/dist/commands/order/index.js +13 -0
- package/dist/commands/order/limit-simple.d.ts +5 -0
- package/dist/commands/order/limit-simple.js +195 -0
- package/dist/commands/order/market-simple.d.ts +5 -0
- package/dist/commands/order/market-simple.js +190 -0
- package/dist/commands/order/shared.d.ts +17 -0
- package/dist/commands/order/shared.js +51 -0
- package/dist/commands/order/trigger-simple.d.ts +5 -0
- package/dist/commands/order/trigger-simple.js +246 -0
- package/dist/commands/referral/index.d.ts +2 -0
- package/dist/commands/referral/index.js +7 -0
- package/dist/commands/referral/set.d.ts +2 -0
- package/dist/commands/referral/set.js +26 -0
- package/dist/commands/referral/status.d.ts +2 -0
- package/dist/commands/referral/status.js +31 -0
- package/dist/commands/replay/index.d.ts +2 -0
- package/dist/commands/replay/index.js +152 -0
- package/dist/commands/risk/analytics.d.ts +2 -0
- package/dist/commands/risk/analytics.js +64 -0
- package/dist/commands/risk/audit.d.ts +2 -0
- package/dist/commands/risk/audit.js +52 -0
- package/dist/commands/risk/index.d.ts +2 -0
- package/dist/commands/risk/index.js +9 -0
- package/dist/commands/risk/rules.d.ts +2 -0
- package/dist/commands/risk/rules.js +102 -0
- package/dist/commands/server.d.ts +2 -0
- package/dist/commands/server.js +208 -0
- package/dist/commands/setup/index.d.ts +2 -0
- package/dist/commands/setup/index.js +478 -0
- package/dist/commands/signal/index.d.ts +2 -0
- package/dist/commands/signal/index.js +129 -0
- package/dist/commands/state/index.d.ts +2 -0
- package/dist/commands/state/index.js +5 -0
- package/dist/commands/state/show.d.ts +2 -0
- package/dist/commands/state/show.js +105 -0
- package/dist/commands/strategy/index.d.ts +4 -0
- package/dist/commands/strategy/index.js +73 -0
- package/dist/commands/traces/index.d.ts +2 -0
- package/dist/commands/traces/index.js +76 -0
- package/dist/commands/ui/demo.d.ts +9 -0
- package/dist/commands/ui/demo.js +195 -0
- package/dist/commands/ui/index.d.ts +2 -0
- package/dist/commands/ui/index.js +7 -0
- package/dist/commands/ui/terminal.d.ts +2 -0
- package/dist/commands/ui/terminal.js +255 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +98 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/lib/agent/audit.d.ts +12 -0
- package/dist/lib/agent/audit.js +13 -0
- package/dist/lib/agent/gateway.d.ts +13 -0
- package/dist/lib/agent/gateway.js +598 -0
- package/dist/lib/agent/metrics.d.ts +33 -0
- package/dist/lib/agent/metrics.js +175 -0
- package/dist/lib/agent/signature.d.ts +8 -0
- package/dist/lib/agent/signature.js +28 -0
- package/dist/lib/agent/tools.d.ts +28 -0
- package/dist/lib/agent/tools.js +453 -0
- package/dist/lib/agent/x402.d.ts +23 -0
- package/dist/lib/agent/x402.js +62 -0
- package/dist/lib/api-wallet.d.ts +69 -0
- package/dist/lib/api-wallet.js +101 -0
- package/dist/lib/balance-watcher.d.ts +25 -0
- package/dist/lib/balance-watcher.js +83 -0
- package/dist/lib/book-watcher.d.ts +25 -0
- package/dist/lib/book-watcher.js +48 -0
- package/dist/lib/config.d.ts +88 -0
- package/dist/lib/config.js +427 -0
- package/dist/lib/constants.d.ts +50 -0
- package/dist/lib/constants.js +84 -0
- package/dist/lib/contracts.d.ts +7 -0
- package/dist/lib/contracts.js +8 -0
- package/dist/lib/credential-vault.d.ts +22 -0
- package/dist/lib/credential-vault.js +109 -0
- package/dist/lib/db/accounts.d.ts +83 -0
- package/dist/lib/db/accounts.js +203 -0
- package/dist/lib/db/funding-history.d.ts +69 -0
- package/dist/lib/db/funding-history.js +183 -0
- package/dist/lib/db/index.d.ts +11 -0
- package/dist/lib/db/index.js +272 -0
- package/dist/lib/events/bus.d.ts +10 -0
- package/dist/lib/events/bus.js +17 -0
- package/dist/lib/events/types.d.ts +51 -0
- package/dist/lib/events/types.js +1 -0
- package/dist/lib/exchange.d.ts +30 -0
- package/dist/lib/exchange.js +84 -0
- package/dist/lib/execution/journal.d.ts +25 -0
- package/dist/lib/execution/journal.js +158 -0
- package/dist/lib/execution/safety.d.ts +34 -0
- package/dist/lib/execution/safety.js +197 -0
- package/dist/lib/exit-codes.d.ts +18 -0
- package/dist/lib/exit-codes.js +60 -0
- package/dist/lib/fetch.d.ts +18 -0
- package/dist/lib/fetch.js +66 -0
- package/dist/lib/fs-security.d.ts +10 -0
- package/dist/lib/fs-security.js +26 -0
- package/dist/lib/funding-tracker.d.ts +40 -0
- package/dist/lib/funding-tracker.js +118 -0
- package/dist/lib/logger.d.ts +27 -0
- package/dist/lib/logger.js +82 -0
- package/dist/lib/network-model.d.ts +13 -0
- package/dist/lib/network-model.js +30 -0
- package/dist/lib/onboarding.d.ts +133 -0
- package/dist/lib/onboarding.js +1459 -0
- package/dist/lib/operator-state.d.ts +25 -0
- package/dist/lib/operator-state.js +82 -0
- package/dist/lib/orders-watcher.d.ts +24 -0
- package/dist/lib/orders-watcher.js +74 -0
- package/dist/lib/paths.d.ts +20 -0
- package/dist/lib/paths.js +23 -0
- package/dist/lib/portfolio-watcher.d.ts +33 -0
- package/dist/lib/portfolio-watcher.js +95 -0
- package/dist/lib/position-watcher.d.ts +16 -0
- package/dist/lib/position-watcher.js +44 -0
- package/dist/lib/price-watcher.d.ts +15 -0
- package/dist/lib/price-watcher.js +84 -0
- package/dist/lib/prompts.d.ts +32 -0
- package/dist/lib/prompts.js +105 -0
- package/dist/lib/rate-limit.d.ts +32 -0
- package/dist/lib/rate-limit.js +88 -0
- package/dist/lib/risk/analytics.d.ts +39 -0
- package/dist/lib/risk/analytics.js +98 -0
- package/dist/lib/risk/drawdown.d.ts +18 -0
- package/dist/lib/risk/drawdown.js +49 -0
- package/dist/lib/risk/evaluation-log.d.ts +29 -0
- package/dist/lib/risk/evaluation-log.js +61 -0
- package/dist/lib/risk/index.d.ts +4 -0
- package/dist/lib/risk/index.js +4 -0
- package/dist/lib/risk/limits.d.ts +23 -0
- package/dist/lib/risk/limits.js +27 -0
- package/dist/lib/risk/manager.d.ts +32 -0
- package/dist/lib/risk/manager.js +85 -0
- package/dist/lib/risk/policy-middleware.d.ts +33 -0
- package/dist/lib/risk/policy-middleware.js +267 -0
- package/dist/lib/risk/position-sizer.d.ts +9 -0
- package/dist/lib/risk/position-sizer.js +14 -0
- package/dist/lib/risk/rules-store.d.ts +16 -0
- package/dist/lib/risk/rules-store.js +47 -0
- package/dist/lib/schema.d.ts +254 -0
- package/dist/lib/schema.js +199 -0
- package/dist/lib/secrets.d.ts +3 -0
- package/dist/lib/secrets.js +62 -0
- package/dist/lib/settings.d.ts +24 -0
- package/dist/lib/settings.js +86 -0
- package/dist/lib/signals.d.ts +73 -0
- package/dist/lib/signals.js +136 -0
- package/dist/lib/stable-stringify.d.ts +6 -0
- package/dist/lib/stable-stringify.js +17 -0
- package/dist/lib/state-context.d.ts +44 -0
- package/dist/lib/state-context.js +133 -0
- package/dist/lib/strategy/basis-trade.d.ts +2 -0
- package/dist/lib/strategy/basis-trade.js +24 -0
- package/dist/lib/strategy/funding-arb.d.ts +2 -0
- package/dist/lib/strategy/funding-arb.js +23 -0
- package/dist/lib/strategy/interface.d.ts +23 -0
- package/dist/lib/strategy/interface.js +1 -0
- package/dist/lib/strategy/registry.d.ts +4 -0
- package/dist/lib/strategy/registry.js +10 -0
- package/dist/lib/telemetry.d.ts +25 -0
- package/dist/lib/telemetry.js +101 -0
- package/dist/lib/trace-queries.d.ts +20 -0
- package/dist/lib/trace-queries.js +133 -0
- package/dist/lib/trace.d.ts +1 -0
- package/dist/lib/trace.js +4 -0
- package/dist/lib/trade-reputation.d.ts +6 -0
- package/dist/lib/trade-reputation.js +99 -0
- package/dist/lib/ui-tokens.d.ts +21 -0
- package/dist/lib/ui-tokens.js +26 -0
- package/dist/lib/validate.d.ts +39 -0
- package/dist/lib/validate.js +108 -0
- package/dist/lib/validation.d.ts +9 -0
- package/dist/lib/validation.js +64 -0
- package/dist/server/cache.d.ts +38 -0
- package/dist/server/cache.js +56 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +89 -0
- package/dist/server/ipc.d.ts +18 -0
- package/dist/server/ipc.js +159 -0
- package/dist/server/subscriptions.d.ts +18 -0
- package/dist/server/subscriptions.js +114 -0
- package/package.json +124 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface OperatorHeartbeatSnapshot {
|
|
2
|
+
exchange: string;
|
|
3
|
+
market?: string;
|
|
4
|
+
equity: number;
|
|
5
|
+
exposureUsd: number;
|
|
6
|
+
drawdownPct: number;
|
|
7
|
+
peakEquity: number;
|
|
8
|
+
halted: boolean;
|
|
9
|
+
haltReason?: string;
|
|
10
|
+
updatedAt: number;
|
|
11
|
+
}
|
|
12
|
+
export interface KillSwitchState {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
reason?: string;
|
|
15
|
+
updatedAt: number;
|
|
16
|
+
}
|
|
17
|
+
export interface OperatorState {
|
|
18
|
+
heartbeat: OperatorHeartbeatSnapshot | null;
|
|
19
|
+
killSwitch: KillSwitchState;
|
|
20
|
+
}
|
|
21
|
+
export declare function readOperatorState(): OperatorState;
|
|
22
|
+
export declare function writeOperatorState(state: OperatorState): void;
|
|
23
|
+
export declare function updateOperatorHeartbeat(heartbeat: OperatorHeartbeatSnapshot): OperatorState;
|
|
24
|
+
export declare function setKillSwitch(enabled: boolean, reason?: string): OperatorState;
|
|
25
|
+
export declare function resetOperatorState(): void;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { eventBus } from "./events/bus.js";
|
|
3
|
+
import { HL_DIR, OPERATOR_STATE_PATH } from "./paths.js";
|
|
4
|
+
import { ensurePrivateDir, hardenPrivateFile, PRIVATE_FILE_MODE } from "./fs-security.js";
|
|
5
|
+
const DEFAULT_OPERATOR_STATE = {
|
|
6
|
+
heartbeat: null,
|
|
7
|
+
killSwitch: {
|
|
8
|
+
enabled: false,
|
|
9
|
+
updatedAt: 0,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
function ensureOperatorStateDir() {
|
|
13
|
+
ensurePrivateDir(HL_DIR);
|
|
14
|
+
}
|
|
15
|
+
export function readOperatorState() {
|
|
16
|
+
ensureOperatorStateDir();
|
|
17
|
+
if (!existsSync(OPERATOR_STATE_PATH)) {
|
|
18
|
+
return { ...DEFAULT_OPERATOR_STATE };
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(OPERATOR_STATE_PATH, "utf-8");
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
return {
|
|
24
|
+
heartbeat: parsed.heartbeat ?? null,
|
|
25
|
+
killSwitch: {
|
|
26
|
+
enabled: parsed.killSwitch?.enabled ?? false,
|
|
27
|
+
reason: parsed.killSwitch?.reason,
|
|
28
|
+
updatedAt: parsed.killSwitch?.updatedAt ?? 0,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { ...DEFAULT_OPERATOR_STATE };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function writeOperatorState(state) {
|
|
37
|
+
ensureOperatorStateDir();
|
|
38
|
+
writeFileSync(OPERATOR_STATE_PATH, `${JSON.stringify(state, null, 2)}\n`, {
|
|
39
|
+
encoding: "utf-8",
|
|
40
|
+
mode: PRIVATE_FILE_MODE,
|
|
41
|
+
});
|
|
42
|
+
hardenPrivateFile(OPERATOR_STATE_PATH);
|
|
43
|
+
}
|
|
44
|
+
export function updateOperatorHeartbeat(heartbeat) {
|
|
45
|
+
const state = readOperatorState();
|
|
46
|
+
const next = {
|
|
47
|
+
...state,
|
|
48
|
+
heartbeat,
|
|
49
|
+
};
|
|
50
|
+
writeOperatorState(next);
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
export function setKillSwitch(enabled, reason) {
|
|
54
|
+
const state = readOperatorState();
|
|
55
|
+
const next = {
|
|
56
|
+
...state,
|
|
57
|
+
killSwitch: {
|
|
58
|
+
enabled,
|
|
59
|
+
reason,
|
|
60
|
+
updatedAt: Date.now(),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
writeOperatorState(next);
|
|
64
|
+
if (enabled) {
|
|
65
|
+
eventBus.emit("operator.halt", {
|
|
66
|
+
reason: reason ?? "Manual kill switch",
|
|
67
|
+
source: "manual",
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
eventBus.emit("operator.resume", {
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return next;
|
|
77
|
+
}
|
|
78
|
+
export function resetOperatorState() {
|
|
79
|
+
writeOperatorState({
|
|
80
|
+
...DEFAULT_OPERATOR_STATE,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
export interface OrderData {
|
|
3
|
+
oid: number;
|
|
4
|
+
coin: string;
|
|
5
|
+
side: string;
|
|
6
|
+
sz: string;
|
|
7
|
+
limitPx: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
export interface OrdersWatcher {
|
|
11
|
+
start(): Promise<void>;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface OrdersWatcherConfig {
|
|
15
|
+
user: Address;
|
|
16
|
+
isTestnet: boolean;
|
|
17
|
+
onUpdate: (orders: OrderData[]) => void;
|
|
18
|
+
onError: (error: Error) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates an orders watcher that subscribes to orderUpdates
|
|
22
|
+
* and polls open orders on each update
|
|
23
|
+
*/
|
|
24
|
+
export declare function createOrdersWatcher(config: OrdersWatcherConfig): OrdersWatcher;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { WebSocketTransport, SubscriptionClient, HttpTransport, InfoClient, } from "@nktkas/hyperliquid";
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
/**
|
|
4
|
+
* Creates an orders watcher that subscribes to orderUpdates
|
|
5
|
+
* and polls open orders on each update
|
|
6
|
+
*/
|
|
7
|
+
export function createOrdersWatcher(config) {
|
|
8
|
+
let wsTransport = null;
|
|
9
|
+
let subscriptionClient = null;
|
|
10
|
+
let subscription = null;
|
|
11
|
+
let httpClient = null;
|
|
12
|
+
const fetchOrders = async () => {
|
|
13
|
+
if (!httpClient)
|
|
14
|
+
return [];
|
|
15
|
+
const orders = await httpClient.openOrders({ user: config.user, dex: "ALL_DEXS" });
|
|
16
|
+
return orders.map((o) => ({
|
|
17
|
+
oid: o.oid,
|
|
18
|
+
coin: o.coin,
|
|
19
|
+
side: o.side,
|
|
20
|
+
sz: o.sz,
|
|
21
|
+
limitPx: o.limitPx,
|
|
22
|
+
timestamp: o.timestamp,
|
|
23
|
+
}));
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
async start() {
|
|
27
|
+
// Create HTTP client for polling open orders
|
|
28
|
+
const httpTransport = new HttpTransport({ isTestnet: config.isTestnet });
|
|
29
|
+
httpClient = new InfoClient({ transport: httpTransport });
|
|
30
|
+
// Fetch initial orders
|
|
31
|
+
const initialOrders = await fetchOrders();
|
|
32
|
+
config.onUpdate(initialOrders);
|
|
33
|
+
wsTransport = new WebSocketTransport({
|
|
34
|
+
isTestnet: config.isTestnet,
|
|
35
|
+
reconnect: { WebSocket: WebSocket },
|
|
36
|
+
});
|
|
37
|
+
subscriptionClient = new SubscriptionClient({ transport: wsTransport });
|
|
38
|
+
await wsTransport.ready();
|
|
39
|
+
// Subscribe to order updates
|
|
40
|
+
subscription = await subscriptionClient.orderUpdates({ user: config.user }, async () => {
|
|
41
|
+
// Re-fetch orders on any update
|
|
42
|
+
try {
|
|
43
|
+
const orders = await fetchOrders();
|
|
44
|
+
config.onUpdate(orders);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
config.onError(err instanceof Error ? err : new Error(String(err)));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
async stop() {
|
|
52
|
+
if (subscription) {
|
|
53
|
+
try {
|
|
54
|
+
await subscription.unsubscribe();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Ignore errors during unsubscribe
|
|
58
|
+
}
|
|
59
|
+
subscription = null;
|
|
60
|
+
}
|
|
61
|
+
if (wsTransport) {
|
|
62
|
+
try {
|
|
63
|
+
await wsTransport.close();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Ignore errors during close
|
|
67
|
+
}
|
|
68
|
+
wsTransport = null;
|
|
69
|
+
}
|
|
70
|
+
subscriptionClient = null;
|
|
71
|
+
httpClient = null;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const PERP_DIR: string;
|
|
2
|
+
export declare const LEGACY_HL_DIR: string;
|
|
3
|
+
export declare const HL_DIR: string;
|
|
4
|
+
export declare const SERVER_SOCKET_PATH: string;
|
|
5
|
+
export declare const SERVER_PID_PATH: string;
|
|
6
|
+
export declare const SERVER_LOG_PATH: string;
|
|
7
|
+
export declare const SERVER_CONFIG_PATH: string;
|
|
8
|
+
export declare const OPERATOR_STATE_PATH: string;
|
|
9
|
+
export declare const TELEMETRY_PATH: string;
|
|
10
|
+
export declare const CREDENTIAL_VAULT_PATH: string;
|
|
11
|
+
export declare const ONBOARDING_STATE_DIR: string;
|
|
12
|
+
export declare const AGENT_AUDIT_LOG_PATH: string;
|
|
13
|
+
export declare const AGENT_METRICS_PATH: string;
|
|
14
|
+
export declare const DB_PATH: string;
|
|
15
|
+
export declare const LEGACY_DB_PATH: string;
|
|
16
|
+
export declare const MASTER_KEY_PATH: string;
|
|
17
|
+
export interface ServerConfig {
|
|
18
|
+
testnet: boolean;
|
|
19
|
+
startedAt: number;
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
// Base directory for all perps runtime files
|
|
4
|
+
export const PERP_DIR = join(homedir(), ".perp");
|
|
5
|
+
// Legacy path retained for one-way migration helpers
|
|
6
|
+
export const LEGACY_HL_DIR = join(homedir(), ".hl");
|
|
7
|
+
// Backward-compatible alias for older imports
|
|
8
|
+
export const HL_DIR = PERP_DIR;
|
|
9
|
+
// Server files
|
|
10
|
+
export const SERVER_SOCKET_PATH = join(PERP_DIR, "server.sock");
|
|
11
|
+
export const SERVER_PID_PATH = join(PERP_DIR, "server.pid");
|
|
12
|
+
export const SERVER_LOG_PATH = join(PERP_DIR, "server.log");
|
|
13
|
+
export const SERVER_CONFIG_PATH = join(PERP_DIR, "server.json");
|
|
14
|
+
export const OPERATOR_STATE_PATH = join(PERP_DIR, "operator-state.json");
|
|
15
|
+
export const TELEMETRY_PATH = join(PERP_DIR, "telemetry.json");
|
|
16
|
+
export const CREDENTIAL_VAULT_PATH = join(PERP_DIR, "credentials-vault.json");
|
|
17
|
+
export const ONBOARDING_STATE_DIR = join(PERP_DIR, "onboarding");
|
|
18
|
+
export const AGENT_AUDIT_LOG_PATH = join(PERP_DIR, "agent-audit.log");
|
|
19
|
+
export const AGENT_METRICS_PATH = join(PERP_DIR, "agent-metrics.json");
|
|
20
|
+
// Database files
|
|
21
|
+
export const DB_PATH = join(PERP_DIR, "perp.db");
|
|
22
|
+
export const LEGACY_DB_PATH = join(LEGACY_HL_DIR, "hl.db");
|
|
23
|
+
export const MASTER_KEY_PATH = join(PERP_DIR, "master.key");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
export interface PortfolioData {
|
|
3
|
+
positions: Array<{
|
|
4
|
+
coin: string;
|
|
5
|
+
size: string;
|
|
6
|
+
entryPx: string;
|
|
7
|
+
positionValue: string;
|
|
8
|
+
unrealizedPnl: string;
|
|
9
|
+
leverage: string;
|
|
10
|
+
}>;
|
|
11
|
+
spotBalances: Array<{
|
|
12
|
+
token: string;
|
|
13
|
+
total: string;
|
|
14
|
+
hold: string;
|
|
15
|
+
}>;
|
|
16
|
+
accountValue: string;
|
|
17
|
+
totalMarginUsed: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PortfolioWatcher {
|
|
20
|
+
start(): Promise<void>;
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export interface PortfolioWatcherConfig {
|
|
24
|
+
user: Address;
|
|
25
|
+
isTestnet: boolean;
|
|
26
|
+
onUpdate: (data: PortfolioData) => void;
|
|
27
|
+
onError: (error: Error) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates a portfolio watcher that subscribes to perp state updates
|
|
31
|
+
* and polls spot balances on each update
|
|
32
|
+
*/
|
|
33
|
+
export declare function createPortfolioWatcher(config: PortfolioWatcherConfig): PortfolioWatcher;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { WebSocketTransport, SubscriptionClient, HttpTransport, InfoClient } from "@nktkas/hyperliquid";
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a portfolio watcher that subscribes to perp state updates
|
|
5
|
+
* and polls spot balances on each update
|
|
6
|
+
*/
|
|
7
|
+
export function createPortfolioWatcher(config) {
|
|
8
|
+
let wsTransport = null;
|
|
9
|
+
let subscriptionClient = null;
|
|
10
|
+
let perpSubscription = null;
|
|
11
|
+
let httpClient = null;
|
|
12
|
+
return {
|
|
13
|
+
async start() {
|
|
14
|
+
// Create HTTP client for spot balance polling
|
|
15
|
+
const httpTransport = new HttpTransport({ isTestnet: config.isTestnet });
|
|
16
|
+
httpClient = new InfoClient({ transport: httpTransport });
|
|
17
|
+
// Fetch initial spot state
|
|
18
|
+
const spotState = await httpClient.spotClearinghouseState({ user: config.user });
|
|
19
|
+
let currentSpotBalances = spotState.balances
|
|
20
|
+
.filter((b) => parseFloat(b.total) !== 0)
|
|
21
|
+
.map((b) => ({
|
|
22
|
+
token: b.coin,
|
|
23
|
+
total: b.total,
|
|
24
|
+
hold: b.hold,
|
|
25
|
+
}));
|
|
26
|
+
wsTransport = new WebSocketTransport({
|
|
27
|
+
isTestnet: config.isTestnet,
|
|
28
|
+
reconnect: { WebSocket: WebSocket },
|
|
29
|
+
});
|
|
30
|
+
subscriptionClient = new SubscriptionClient({ transport: wsTransport });
|
|
31
|
+
await wsTransport.ready();
|
|
32
|
+
// Subscribe to perp clearinghouse state
|
|
33
|
+
perpSubscription = await subscriptionClient.allDexsClearinghouseState({ user: config.user }, async (state) => {
|
|
34
|
+
const clearinghouseState = state.clearinghouseStates[0]?.[1];
|
|
35
|
+
const accountValue = clearinghouseState?.marginSummary.accountValue || "0";
|
|
36
|
+
const totalMarginUsed = clearinghouseState?.marginSummary.totalMarginUsed || "0";
|
|
37
|
+
const positions = state.clearinghouseStates
|
|
38
|
+
.flatMap((c) => c[1].assetPositions)
|
|
39
|
+
.filter((p) => parseFloat(p.position.szi) !== 0)
|
|
40
|
+
.map((p) => ({
|
|
41
|
+
coin: p.position.coin,
|
|
42
|
+
size: p.position.szi,
|
|
43
|
+
entryPx: p.position.entryPx,
|
|
44
|
+
positionValue: p.position.positionValue,
|
|
45
|
+
unrealizedPnl: p.position.unrealizedPnl,
|
|
46
|
+
leverage: `${p.position.leverage.value}x ${p.position.leverage.type}`,
|
|
47
|
+
}));
|
|
48
|
+
// Refresh spot balances on each perp update
|
|
49
|
+
if (httpClient) {
|
|
50
|
+
try {
|
|
51
|
+
const freshSpotState = await httpClient.spotClearinghouseState({ user: config.user });
|
|
52
|
+
currentSpotBalances = freshSpotState.balances
|
|
53
|
+
.filter((b) => parseFloat(b.total) !== 0)
|
|
54
|
+
.map((b) => ({
|
|
55
|
+
token: b.coin,
|
|
56
|
+
total: b.total,
|
|
57
|
+
hold: b.hold,
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Keep previous spot balances on error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
config.onUpdate({
|
|
65
|
+
positions,
|
|
66
|
+
spotBalances: currentSpotBalances,
|
|
67
|
+
accountValue,
|
|
68
|
+
totalMarginUsed,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
async stop() {
|
|
73
|
+
if (perpSubscription) {
|
|
74
|
+
try {
|
|
75
|
+
await perpSubscription.unsubscribe();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Ignore errors during unsubscribe
|
|
79
|
+
}
|
|
80
|
+
perpSubscription = null;
|
|
81
|
+
}
|
|
82
|
+
if (wsTransport) {
|
|
83
|
+
try {
|
|
84
|
+
await wsTransport.close();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Ignore errors during close
|
|
88
|
+
}
|
|
89
|
+
wsTransport = null;
|
|
90
|
+
}
|
|
91
|
+
subscriptionClient = null;
|
|
92
|
+
httpClient = null;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
import { AllDexsClearinghouseStateEvent } from "@nktkas/hyperliquid/api/subscription";
|
|
3
|
+
export interface PositionWatcher {
|
|
4
|
+
start(): Promise<void>;
|
|
5
|
+
stop(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface PositionWatcherConfig {
|
|
8
|
+
user: Address;
|
|
9
|
+
isTestnet: boolean;
|
|
10
|
+
onUpdate: (state: AllDexsClearinghouseStateEvent) => void;
|
|
11
|
+
onError: (error: Error) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a position watcher that subscribes to clearinghouseState updates via WebSocket
|
|
15
|
+
*/
|
|
16
|
+
export declare function createPositionWatcher(config: PositionWatcherConfig): PositionWatcher;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WebSocketTransport, SubscriptionClient } from "@nktkas/hyperliquid";
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a position watcher that subscribes to clearinghouseState updates via WebSocket
|
|
5
|
+
*/
|
|
6
|
+
export function createPositionWatcher(config) {
|
|
7
|
+
let wsTransport = null;
|
|
8
|
+
let subscriptionClient = null;
|
|
9
|
+
let subscription = null;
|
|
10
|
+
return {
|
|
11
|
+
async start() {
|
|
12
|
+
wsTransport = new WebSocketTransport({
|
|
13
|
+
isTestnet: config.isTestnet,
|
|
14
|
+
reconnect: { WebSocket: WebSocket },
|
|
15
|
+
});
|
|
16
|
+
subscriptionClient = new SubscriptionClient({ transport: wsTransport });
|
|
17
|
+
await wsTransport.ready();
|
|
18
|
+
subscription = await subscriptionClient.allDexsClearinghouseState({ user: config.user }, (state) => {
|
|
19
|
+
config.onUpdate(state);
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
async stop() {
|
|
23
|
+
if (subscription) {
|
|
24
|
+
try {
|
|
25
|
+
await subscription.unsubscribe();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Ignore errors during unsubscribe
|
|
29
|
+
}
|
|
30
|
+
subscription = null;
|
|
31
|
+
}
|
|
32
|
+
if (wsTransport) {
|
|
33
|
+
try {
|
|
34
|
+
await wsTransport.close();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Ignore errors during close
|
|
38
|
+
}
|
|
39
|
+
wsTransport = null;
|
|
40
|
+
}
|
|
41
|
+
subscriptionClient = null;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PriceWatcher {
|
|
2
|
+
start(): Promise<void>;
|
|
3
|
+
stop(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export interface PriceWatcherConfig {
|
|
6
|
+
coin: string;
|
|
7
|
+
isTestnet: boolean;
|
|
8
|
+
onUpdate: (price: string) => void;
|
|
9
|
+
onError: (error: Error) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a price watcher that uses server cache polling if available,
|
|
13
|
+
* otherwise falls back to direct WebSocket subscription
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPriceWatcher(config: PriceWatcherConfig): PriceWatcher;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { tryConnectToServer } from "../client/index.js";
|
|
2
|
+
import { WebSocketTransport, SubscriptionClient } from "@nktkas/hyperliquid";
|
|
3
|
+
import WebSocket from "ws";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a price watcher that uses server cache polling if available,
|
|
6
|
+
* otherwise falls back to direct WebSocket subscription
|
|
7
|
+
*/
|
|
8
|
+
export function createPriceWatcher(config) {
|
|
9
|
+
let serverClient = null;
|
|
10
|
+
let pollInterval = null;
|
|
11
|
+
let wsTransport = null;
|
|
12
|
+
let subscription = null;
|
|
13
|
+
let stopped = false;
|
|
14
|
+
const pollServerPrice = async () => {
|
|
15
|
+
if (stopped || !serverClient)
|
|
16
|
+
return;
|
|
17
|
+
try {
|
|
18
|
+
const { data } = await serverClient.getPrices();
|
|
19
|
+
const price = data[config.coin];
|
|
20
|
+
if (price !== undefined) {
|
|
21
|
+
config.onUpdate(price);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
config.onError(err instanceof Error ? err : new Error(String(err)));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
async start() {
|
|
30
|
+
stopped = false;
|
|
31
|
+
// Try to use server cache first
|
|
32
|
+
serverClient = await tryConnectToServer();
|
|
33
|
+
if (serverClient) {
|
|
34
|
+
// Poll server every 500ms for price updates
|
|
35
|
+
await pollServerPrice();
|
|
36
|
+
pollInterval = setInterval(pollServerPrice, 500);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// No server, use direct WebSocket subscription
|
|
40
|
+
wsTransport = new WebSocketTransport({
|
|
41
|
+
isTestnet: config.isTestnet,
|
|
42
|
+
reconnect: { WebSocket: WebSocket },
|
|
43
|
+
});
|
|
44
|
+
const subscriptionClient = new SubscriptionClient({ transport: wsTransport });
|
|
45
|
+
await wsTransport.ready();
|
|
46
|
+
subscription = await subscriptionClient.allMids({ dex: "ALL_DEXS" }, (event) => {
|
|
47
|
+
const price = event.mids[config.coin];
|
|
48
|
+
if (price !== undefined) {
|
|
49
|
+
config.onUpdate(price);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
async stop() {
|
|
55
|
+
stopped = true;
|
|
56
|
+
if (pollInterval) {
|
|
57
|
+
clearInterval(pollInterval);
|
|
58
|
+
pollInterval = null;
|
|
59
|
+
}
|
|
60
|
+
if (serverClient) {
|
|
61
|
+
serverClient.close();
|
|
62
|
+
serverClient = null;
|
|
63
|
+
}
|
|
64
|
+
if (subscription) {
|
|
65
|
+
try {
|
|
66
|
+
await subscription.unsubscribe();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Ignore errors during unsubscribe
|
|
70
|
+
}
|
|
71
|
+
subscription = null;
|
|
72
|
+
}
|
|
73
|
+
if (wsTransport) {
|
|
74
|
+
try {
|
|
75
|
+
await wsTransport.close();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Ignore errors during close
|
|
79
|
+
}
|
|
80
|
+
wsTransport = null;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt for text input
|
|
3
|
+
*/
|
|
4
|
+
export declare function prompt(question: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Prompt for selection from a list of options with arrow key navigation
|
|
7
|
+
*/
|
|
8
|
+
export declare function select<T extends string>(question: string, options: {
|
|
9
|
+
value: T;
|
|
10
|
+
label: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}[]): Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Prompt for multiple selections with checkboxes
|
|
15
|
+
*/
|
|
16
|
+
export declare function multiSelect<T extends string>(question: string, options: {
|
|
17
|
+
value: T;
|
|
18
|
+
label: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}[]): Promise<T[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Prompt for yes/no confirmation
|
|
23
|
+
*/
|
|
24
|
+
export declare function confirm(question: string, defaultValue?: boolean): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Wait for user to press Enter
|
|
27
|
+
*/
|
|
28
|
+
export declare function waitForEnter(message?: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Wait for user to press Enter (returns true) or Escape (returns false)
|
|
31
|
+
*/
|
|
32
|
+
export declare function pressEnterOrEsc(message: string): Promise<boolean>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { input, select as inquirerSelect, confirm as inquirerConfirm, checkbox, } from "@inquirer/prompts";
|
|
2
|
+
import { inquirerTheme, highlighter } from "./ui-tokens.js";
|
|
3
|
+
/**
|
|
4
|
+
* Prompt for text input
|
|
5
|
+
*/
|
|
6
|
+
export async function prompt(question) {
|
|
7
|
+
const answer = await input({
|
|
8
|
+
message: question,
|
|
9
|
+
theme: inquirerTheme,
|
|
10
|
+
});
|
|
11
|
+
return answer.trim();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Prompt for selection from a list of options with arrow key navigation
|
|
15
|
+
*/
|
|
16
|
+
export async function select(question, options) {
|
|
17
|
+
const result = await inquirerSelect({
|
|
18
|
+
message: question,
|
|
19
|
+
choices: options.map((opt) => ({
|
|
20
|
+
value: opt.value,
|
|
21
|
+
name: opt.label,
|
|
22
|
+
description: opt.description,
|
|
23
|
+
})),
|
|
24
|
+
theme: inquirerTheme,
|
|
25
|
+
});
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Prompt for multiple selections with checkboxes
|
|
30
|
+
*/
|
|
31
|
+
export async function multiSelect(question, options) {
|
|
32
|
+
const results = await checkbox({
|
|
33
|
+
message: question,
|
|
34
|
+
choices: options.map((opt) => ({
|
|
35
|
+
value: opt.value,
|
|
36
|
+
name: opt.label,
|
|
37
|
+
description: opt.description,
|
|
38
|
+
})),
|
|
39
|
+
theme: inquirerTheme,
|
|
40
|
+
});
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Prompt for yes/no confirmation
|
|
45
|
+
*/
|
|
46
|
+
export async function confirm(question, defaultValue = false) {
|
|
47
|
+
return inquirerConfirm({
|
|
48
|
+
message: question,
|
|
49
|
+
default: defaultValue,
|
|
50
|
+
theme: inquirerTheme,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Wait for user to press Enter
|
|
55
|
+
*/
|
|
56
|
+
export async function waitForEnter(message = "Press Enter to continue...") {
|
|
57
|
+
await input({
|
|
58
|
+
message,
|
|
59
|
+
theme: {
|
|
60
|
+
...inquirerTheme,
|
|
61
|
+
prefix: {
|
|
62
|
+
idle: highlighter.warn("→"),
|
|
63
|
+
done: highlighter.success("✔"),
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Wait for user to press Enter (returns true) or Escape (returns false)
|
|
70
|
+
*/
|
|
71
|
+
export async function pressEnterOrEsc(message) {
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
console.log(`${highlighter.warn("→")} ${message}`);
|
|
74
|
+
const stdin = process.stdin;
|
|
75
|
+
const wasRaw = stdin.isRaw;
|
|
76
|
+
stdin.setRawMode(true);
|
|
77
|
+
stdin.resume();
|
|
78
|
+
const onData = (key) => {
|
|
79
|
+
const char = key.toString();
|
|
80
|
+
// Enter key
|
|
81
|
+
if (char === "\r" || char === "\n") {
|
|
82
|
+
cleanup();
|
|
83
|
+
console.log(`${highlighter.success("✔")} Opening browser...`);
|
|
84
|
+
resolve(true);
|
|
85
|
+
}
|
|
86
|
+
// Escape key
|
|
87
|
+
else if (char === "\x1b") {
|
|
88
|
+
cleanup();
|
|
89
|
+
console.log(`${highlighter.dimWhite("✔")} Skipped`);
|
|
90
|
+
resolve(false);
|
|
91
|
+
}
|
|
92
|
+
// Ctrl+C
|
|
93
|
+
else if (char === "\x03") {
|
|
94
|
+
cleanup();
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const cleanup = () => {
|
|
99
|
+
stdin.removeListener("data", onData);
|
|
100
|
+
stdin.setRawMode(wasRaw);
|
|
101
|
+
stdin.pause();
|
|
102
|
+
};
|
|
103
|
+
stdin.on("data", onData);
|
|
104
|
+
});
|
|
105
|
+
}
|