jaspervault_cli 1.0.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/README.md +138 -0
- package/config/chains.json +104 -0
- package/dist/bin/jv.d.ts +2 -0
- package/dist/bin/jv.js +29 -0
- package/dist/bin/jv.js.map +1 -0
- package/dist/src/client.d.ts +18 -0
- package/dist/src/client.js +128 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/commands/connect.d.ts +22 -0
- package/dist/src/commands/connect.js +128 -0
- package/dist/src/commands/connect.js.map +1 -0
- package/dist/src/commands/deposit.d.ts +59 -0
- package/dist/src/commands/deposit.js +261 -0
- package/dist/src/commands/deposit.js.map +1 -0
- package/dist/src/commands/init.d.ts +4 -0
- package/dist/src/commands/init.js +97 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/job.d.ts +2 -0
- package/dist/src/commands/job.js +20 -0
- package/dist/src/commands/job.js.map +1 -0
- package/dist/src/commands/limit-order.d.ts +2 -0
- package/dist/src/commands/limit-order.js +51 -0
- package/dist/src/commands/limit-order.js.map +1 -0
- package/dist/src/commands/order.d.ts +2 -0
- package/dist/src/commands/order.js +110 -0
- package/dist/src/commands/order.js.map +1 -0
- package/dist/src/commands/orders.d.ts +2 -0
- package/dist/src/commands/orders.js +72 -0
- package/dist/src/commands/orders.js.map +1 -0
- package/dist/src/commands/sl.d.ts +2 -0
- package/dist/src/commands/sl.js +67 -0
- package/dist/src/commands/sl.js.map +1 -0
- package/dist/src/commands/tp.d.ts +2 -0
- package/dist/src/commands/tp.js +67 -0
- package/dist/src/commands/tp.js.map +1 -0
- package/dist/src/commands/vault.d.ts +24 -0
- package/dist/src/commands/vault.js +249 -0
- package/dist/src/commands/vault.js.map +1 -0
- package/dist/src/services/chain-config.d.ts +57 -0
- package/dist/src/services/chain-config.js +96 -0
- package/dist/src/services/chain-config.js.map +1 -0
- package/dist/src/services/key-manager.d.ts +23 -0
- package/dist/src/services/key-manager.js +79 -0
- package/dist/src/services/key-manager.js.map +1 -0
- package/dist/src/services/order-signer.d.ts +24 -0
- package/dist/src/services/order-signer.js +162 -0
- package/dist/src/services/order-signer.js.map +1 -0
- package/dist/src/services/price-service.d.ts +14 -0
- package/dist/src/services/price-service.js +47 -0
- package/dist/src/services/price-service.js.map +1 -0
- package/dist/src/services/subgraph-client.d.ts +53 -0
- package/dist/src/services/subgraph-client.js +120 -0
- package/dist/src/services/subgraph-client.js.map +1 -0
- package/dist/src/services/vault-profile.d.ts +15 -0
- package/dist/src/services/vault-profile.js +56 -0
- package/dist/src/services/vault-profile.js.map +1 -0
- package/dist/src/templates/skill-body.d.ts +11 -0
- package/dist/src/templates/skill-body.js +233 -0
- package/dist/src/templates/skill-body.js.map +1 -0
- package/dist/src/types.d.ts +212 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/config.d.ts +7 -0
- package/dist/src/utils/config.js +14 -0
- package/dist/src/utils/config.js.map +1 -0
- package/dist/src/utils/endpoints.d.ts +8 -0
- package/dist/src/utils/endpoints.js +13 -0
- package/dist/src/utils/endpoints.js.map +1 -0
- package/dist/src/utils/output.d.ts +14 -0
- package/dist/src/utils/output.js +50 -0
- package/dist/src/utils/output.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VaultProfile } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load vault profile from ~/.jaspervault/profile.json.
|
|
4
|
+
* If network is provided, returns null when profile.network does not match.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadVaultProfile(network?: string): VaultProfile | null;
|
|
7
|
+
/**
|
|
8
|
+
* Save vault profile to ~/.jaspervault/profile.json with mode 0600.
|
|
9
|
+
*/
|
|
10
|
+
export declare function saveVaultProfile(profile: VaultProfile): void;
|
|
11
|
+
/**
|
|
12
|
+
* Get position account (holder vault) for a given side from profile.
|
|
13
|
+
* Vault type 37 = long holder, type 33 = short holder.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPositionAccountFromProfile(profile: VaultProfile, side: 'long' | 'short'): string | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, chmodSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
function getJvDir() {
|
|
4
|
+
return join(process.env.HOME || process.env.USERPROFILE || '', '.jaspervault');
|
|
5
|
+
}
|
|
6
|
+
function getProfileFile() {
|
|
7
|
+
return join(getJvDir(), 'profile.json');
|
|
8
|
+
}
|
|
9
|
+
function ensureDir() {
|
|
10
|
+
const dir = getJvDir();
|
|
11
|
+
if (!existsSync(dir)) {
|
|
12
|
+
mkdirSync(dir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load vault profile from ~/.jaspervault/profile.json.
|
|
17
|
+
* If network is provided, returns null when profile.network does not match.
|
|
18
|
+
*/
|
|
19
|
+
export function loadVaultProfile(network) {
|
|
20
|
+
if (!existsSync(getProfileFile())) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const raw = readFileSync(getProfileFile(), 'utf-8');
|
|
24
|
+
const profile = JSON.parse(raw);
|
|
25
|
+
if (!profile.eoa || !profile.delegationAddress || !profile.network) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (network && profile.network !== network) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return profile;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Save vault profile to ~/.jaspervault/profile.json with mode 0600.
|
|
35
|
+
*/
|
|
36
|
+
export function saveVaultProfile(profile) {
|
|
37
|
+
ensureDir();
|
|
38
|
+
const file = getProfileFile();
|
|
39
|
+
writeFileSync(file, JSON.stringify(profile, null, 2), { mode: 0o600 });
|
|
40
|
+
try {
|
|
41
|
+
chmodSync(file, 0o600);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// chmod may fail on some Windows setups; ignore
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get position account (holder vault) for a given side from profile.
|
|
49
|
+
* Vault type 37 = long holder, type 33 = short holder.
|
|
50
|
+
*/
|
|
51
|
+
export function getPositionAccountFromProfile(profile, side) {
|
|
52
|
+
const targetType = side === 'long' ? 37 : 33;
|
|
53
|
+
const entry = profile.vaults?.find((v) => v.type === targetType);
|
|
54
|
+
return entry?.address ?? null;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=vault-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-profile.js","sourceRoot":"","sources":["../../../src/services/vault-profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,SAAS,QAAQ;IACf,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,cAAc,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,cAAc,EAAE,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IAChD,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAqB;IACpD,SAAS,EAAE,CAAC;IACZ,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,OAAqB,EAAE,IAAsB;IACzF,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IAC7E,OAAO,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill template content and banner for jv init command.
|
|
3
|
+
* Exports platform-specific SKILL.md generation and the ASCII banner.
|
|
4
|
+
*/
|
|
5
|
+
/** ANSI Shadow style ASCII art banner (plain text, no ANSI colors) */
|
|
6
|
+
export declare const BANNER: string;
|
|
7
|
+
/**
|
|
8
|
+
* Build platform-specific SKILL.md content.
|
|
9
|
+
* @param platform - One of: openclaw, cursor, claude
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildSkillContent(platform: string): string;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill template content and banner for jv init command.
|
|
3
|
+
* Exports platform-specific SKILL.md generation and the ASCII banner.
|
|
4
|
+
*/
|
|
5
|
+
const CLI_VERSION = '1.0.0';
|
|
6
|
+
/** ANSI Shadow style ASCII art banner (plain text, no ANSI colors) */
|
|
7
|
+
export const BANNER = `
|
|
8
|
+
██╗ █████╗ ███████╗██████╗ ███████╗██████╗
|
|
9
|
+
██║██╔══██╗██╔════╝██╔══██╗██╔════╝██╔══██╗
|
|
10
|
+
██║███████║███████╗██████╔╝█████╗ ██████╔╝
|
|
11
|
+
██ ██║██╔══██║╚════██║██╔═══╝ ██╔══╝ ██╔══██╗
|
|
12
|
+
█████╔╝██║ ██║███████║██║ ███████╗██║ ██║
|
|
13
|
+
╚════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
14
|
+
██╗ ██╗ █████╗ ██╗ ██╗██╗ ████████╗
|
|
15
|
+
██║ ██║██╔══██╗██║ ██║██║ ╚══██╔══╝
|
|
16
|
+
██║ ██║███████║██║ ██║██║ ██║
|
|
17
|
+
╚██╗ ██╔╝██╔══██║██║ ██║██║ ██║
|
|
18
|
+
╚████╔╝ ██║ ██║╚██████╔╝███████╗██║
|
|
19
|
+
╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝
|
|
20
|
+
|
|
21
|
+
── JasperVault CLI v${CLI_VERSION} ──
|
|
22
|
+
⟐ Skill Installer
|
|
23
|
+
`.trim();
|
|
24
|
+
/** Skill body content (shared across platforms, after the main heading) */
|
|
25
|
+
const SKILL_BODY_CONTENT = `
|
|
26
|
+
Interact with JasperVault's perpetual contract platform. You can create market/limit orders, set take-profit and stop-loss, create hedging options, manage limit orders, and query positions — all via the \`jv\` CLI.
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- \`jv\` CLI installed and available in PATH
|
|
31
|
+
- For first-time setup: run \`jv vault init\` (requires \`PRIVATE_KEY\` env var)
|
|
32
|
+
- API URL defaults to \`https://trading-api-test.jaspervault.pro\`; set \`JV_API_URL\` only when debugging locally
|
|
33
|
+
|
|
34
|
+
## Setup Flow (First Time)
|
|
35
|
+
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
# 1. Set your EOA private key (required only for vault init and deposit)
|
|
38
|
+
export PRIVATE_KEY=0x...
|
|
39
|
+
|
|
40
|
+
# 2. Initialize vault and delegation wallet (one-time)
|
|
41
|
+
jv vault init --network jaspervault --vault-types 33,37 --pretty
|
|
42
|
+
|
|
43
|
+
# 3. (Optional) Deposit tokens from Base via Hyperlane
|
|
44
|
+
jv deposit --token jusdc --amount 100 --pretty
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
After init, the delegation wallet is saved to \`~/.jaspervault/keys.json\` and vault addresses to \`~/.jaspervault/profile.json\`. Subsequent commands use these automatically — no \`PRIVATE_KEY\` needed.
|
|
48
|
+
|
|
49
|
+
## Intent Mapping
|
|
50
|
+
|
|
51
|
+
### Creating Orders
|
|
52
|
+
|
|
53
|
+
When the user wants to **open a position**, **place a market order**, or **place a limit order**:
|
|
54
|
+
|
|
55
|
+
\`\`\`bash
|
|
56
|
+
# Market order (Long JBTC, 100 JUSDC notional, 50x leverage)
|
|
57
|
+
jv order create --side long --symbol JBTC --margin-amount 100 --leverage 50 --network jaspervault
|
|
58
|
+
|
|
59
|
+
# Market order (Short)
|
|
60
|
+
jv order create --side short --symbol JBTC --margin-amount 100 --leverage 50 --network jaspervault
|
|
61
|
+
|
|
62
|
+
# Limit order (add --limit-price)
|
|
63
|
+
jv order create --side long --symbol JBTC --margin-amount 100 --leverage 50 --limit-price 95000 --network jaspervault
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
The CLI signs the order internally using the delegation wallet. No pre-signed payload required.
|
|
67
|
+
|
|
68
|
+
On success, a market order returns \`jobId\`; a limit order returns \`limitOrderId\`.
|
|
69
|
+
|
|
70
|
+
### Setting Take-Profit
|
|
71
|
+
|
|
72
|
+
When the user wants to **set take-profit**, **set TP**, or **protect profits**:
|
|
73
|
+
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
jv tp set --order-id <orderId> --price <usdc_price> --symbol <symbol> --network jaspervault
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
\`\`\`bash
|
|
80
|
+
jv tp set --order-id 99 --price 105000 --symbol JBTC --network jaspervault
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
Returns \`limitOrderId\` on success.
|
|
84
|
+
|
|
85
|
+
### Setting Stop-Loss
|
|
86
|
+
|
|
87
|
+
When the user wants to **set stop-loss**, **set SL**, or **limit downside risk**:
|
|
88
|
+
|
|
89
|
+
\`\`\`bash
|
|
90
|
+
jv sl set --order-id <orderId> --price <usdc_price> --symbol <symbol> --network jaspervault
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
\`\`\`bash
|
|
95
|
+
jv sl set --order-id 99 --price 90000 --symbol JBTC --network jaspervault
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
Returns \`limitOrderId\` on success.
|
|
99
|
+
|
|
100
|
+
### Creating Hedging Options
|
|
101
|
+
|
|
102
|
+
When the user wants to **buy option protection**, **hedge a position**, or **create a perps option**:
|
|
103
|
+
|
|
104
|
+
\`\`\`bash
|
|
105
|
+
jv order create-option --order-id <perps_order_id> --data '<signed_option_payload>'
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
Returns \`jobId\` on success. Query execution status with \`jv job status\`.
|
|
109
|
+
|
|
110
|
+
### Querying Job Status
|
|
111
|
+
|
|
112
|
+
When the user asks about **order execution status**, **is my order done**, or **transaction result**:
|
|
113
|
+
|
|
114
|
+
\`\`\`bash
|
|
115
|
+
jv job status <jobId> --pretty
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
Key fields in output:
|
|
119
|
+
- \`status\`: "completed", "failed", "active", "waiting"
|
|
120
|
+
- \`result.transactionHash\`: blockchain tx hash (when completed)
|
|
121
|
+
- \`result.operationType\`: CREATE, ADD_SIZE, CLOSE_SIZE, etc.
|
|
122
|
+
|
|
123
|
+
### Querying Positions
|
|
124
|
+
|
|
125
|
+
When the user asks about **my positions**, **open orders**, or **portfolio**:
|
|
126
|
+
|
|
127
|
+
\`\`\`bash
|
|
128
|
+
# List all positions
|
|
129
|
+
jv orders list --pretty
|
|
130
|
+
|
|
131
|
+
# Filter by side
|
|
132
|
+
jv orders list --position-side LONG
|
|
133
|
+
|
|
134
|
+
# Get a specific position
|
|
135
|
+
jv orders get <orderId>
|
|
136
|
+
|
|
137
|
+
# Get overall statistics
|
|
138
|
+
jv orders stats
|
|
139
|
+
\`\`\`
|
|
140
|
+
|
|
141
|
+
### Managing Limit Orders
|
|
142
|
+
|
|
143
|
+
When the user asks about **my limit orders**, **pending orders**, or wants to **cancel an order**:
|
|
144
|
+
|
|
145
|
+
\`\`\`bash
|
|
146
|
+
# List limit orders for a wallet
|
|
147
|
+
jv limit-order list --wallet <address>
|
|
148
|
+
|
|
149
|
+
# Filter by status
|
|
150
|
+
jv limit-order list --wallet <address> --status PENDING
|
|
151
|
+
|
|
152
|
+
# Check a specific limit order
|
|
153
|
+
jv limit-order status <limitOrderId>
|
|
154
|
+
|
|
155
|
+
# Cancel a limit order
|
|
156
|
+
jv limit-order cancel <limitOrderId>
|
|
157
|
+
\`\`\`
|
|
158
|
+
|
|
159
|
+
Possible statuses: PENDING, TRIGGERED, EXECUTING, COMPLETED, FAILED, EXPIRED, CANCELLED.
|
|
160
|
+
|
|
161
|
+
## Interpreting Output
|
|
162
|
+
|
|
163
|
+
All commands output JSON to stdout. Extract key fields to compose user-friendly responses.
|
|
164
|
+
|
|
165
|
+
**Market order created:**
|
|
166
|
+
\`\`\`json
|
|
167
|
+
{"success":true,"orderType":"market","jobId":"MARKET_ORDER_xxx"}
|
|
168
|
+
\`\`\`
|
|
169
|
+
Tell the user: "Market order submitted. Job ID: {jobId}. Checking status..."
|
|
170
|
+
|
|
171
|
+
**Limit order created:**
|
|
172
|
+
\`\`\`json
|
|
173
|
+
{"success":true,"orderType":"limit","limitOrderId":"0x..."}
|
|
174
|
+
\`\`\`
|
|
175
|
+
Tell the user: "Limit order placed. ID: {limitOrderId}."
|
|
176
|
+
|
|
177
|
+
**Job completed:**
|
|
178
|
+
\`\`\`json
|
|
179
|
+
{"status":"completed","result":{"transactionHash":"0x...","operationType":"CREATE","success":true}}
|
|
180
|
+
\`\`\`
|
|
181
|
+
Tell the user: "Order executed successfully. Tx: {transactionHash}."
|
|
182
|
+
|
|
183
|
+
## Error Handling
|
|
184
|
+
|
|
185
|
+
- **Exit code 1**: Configuration error (e.g. delegation key not found — run \`jv vault init\` first; or invalid parameters). Tell the user to check setup.
|
|
186
|
+
- **Exit code 2**: Network/HTTP error. Tell the user the service may be unreachable.
|
|
187
|
+
- **Exit code 3**: Business error (duplicate order, invalid params, etc.). Read stderr for details and relay the message without exposing raw technical data.
|
|
188
|
+
|
|
189
|
+
When an error occurs, stderr contains the error message. Never expose raw stack traces or internal URLs to the user.
|
|
190
|
+
|
|
191
|
+
## Security Notes
|
|
192
|
+
|
|
193
|
+
- Never log or display private keys, signatures, or API keys in responses to the user.
|
|
194
|
+
- The CLI signs orders internally via the delegation wallet stored in \`~/.jaspervault/keys.json\`.
|
|
195
|
+
- \`PRIVATE_KEY\` is only needed for \`jv vault init\` and \`jv deposit\`; it is not stored by the CLI.
|
|
196
|
+
- \`JV_API_KEY\` (optional Bearer token) should be injected via environment variables or secrets manager, never hardcoded.
|
|
197
|
+
`.trim();
|
|
198
|
+
function getOpenClawFrontmatter() {
|
|
199
|
+
return `---
|
|
200
|
+
name: jasper-vault-cli
|
|
201
|
+
description: Trade perpetual contracts via JasperVault — create orders, set TP/SL, manage limit orders, and query positions through the \`jv\` CLI.
|
|
202
|
+
metadata:
|
|
203
|
+
openclaw:
|
|
204
|
+
requires:
|
|
205
|
+
bins:
|
|
206
|
+
- jv
|
|
207
|
+
---
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
function getClaudeFrontmatter() {
|
|
211
|
+
return `---
|
|
212
|
+
name: jasper-vault-cli
|
|
213
|
+
description: Trade perpetual contracts via JasperVault — create orders, set TP/SL, manage limit orders, and query positions through the \`jv\` CLI.
|
|
214
|
+
---
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Build platform-specific SKILL.md content.
|
|
219
|
+
* @param platform - One of: openclaw, cursor, claude
|
|
220
|
+
*/
|
|
221
|
+
export function buildSkillContent(platform) {
|
|
222
|
+
switch (platform) {
|
|
223
|
+
case 'openclaw':
|
|
224
|
+
return getOpenClawFrontmatter() + '\n# JasperVault Trading Skill\n\n' + SKILL_BODY_CONTENT;
|
|
225
|
+
case 'claude':
|
|
226
|
+
return getClaudeFrontmatter() + '\n# JasperVault Trading Skill\n\n' + SKILL_BODY_CONTENT;
|
|
227
|
+
case 'cursor':
|
|
228
|
+
return '# jasper-vault-cli\n\n' + SKILL_BODY_CONTENT;
|
|
229
|
+
default:
|
|
230
|
+
throw new Error(`Unknown platform: ${platform}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=skill-body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-body.js","sourceRoot":"","sources":["../../../src/templates/skill-body.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B,sEAAsE;AACtE,MAAM,CAAC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;wBAcE,WAAW;;CAElC,CAAC,IAAI,EAAE,CAAC;AAET,2EAA2E;AAC3E,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4K1B,CAAC,IAAI,EAAE,CAAC;AAET,SAAS,sBAAsB;IAC7B,OAAO;;;;;;;;;CASR,CAAC;AACF,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO;;;;CAIR,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,UAAU;YACb,OAAO,sBAAsB,EAAE,GAAG,mCAAmC,GAAG,kBAAkB,CAAC;QAC7F,KAAK,QAAQ;YACX,OAAO,oBAAoB,EAAE,GAAG,mCAAmC,GAAG,kBAAkB,CAAC;QAC3F,KAAK,QAAQ;YACX,OAAO,wBAAwB,GAAG,kBAAkB,CAAC;QACvD;YACE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
export interface ApiResponse<T = unknown> {
|
|
2
|
+
code: number;
|
|
3
|
+
msg: string;
|
|
4
|
+
data: T;
|
|
5
|
+
}
|
|
6
|
+
export interface VaultEntry {
|
|
7
|
+
type: number;
|
|
8
|
+
address: string;
|
|
9
|
+
}
|
|
10
|
+
export interface VaultProfile {
|
|
11
|
+
eoa: string;
|
|
12
|
+
delegationAddress: string;
|
|
13
|
+
network: string;
|
|
14
|
+
vaults: VaultEntry[];
|
|
15
|
+
marginAccount?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface NetworkTokenConfig {
|
|
18
|
+
address: string;
|
|
19
|
+
decimals: number;
|
|
20
|
+
}
|
|
21
|
+
export interface WriterConfig {
|
|
22
|
+
long: string;
|
|
23
|
+
short: string;
|
|
24
|
+
}
|
|
25
|
+
export interface MarginAssetConfig {
|
|
26
|
+
symbol: string;
|
|
27
|
+
address: string;
|
|
28
|
+
decimals: number;
|
|
29
|
+
}
|
|
30
|
+
export interface OrderIntentParams {
|
|
31
|
+
side: 'long' | 'short';
|
|
32
|
+
symbol: string;
|
|
33
|
+
leverage: number;
|
|
34
|
+
marginAmount: string;
|
|
35
|
+
marginAccount?: string;
|
|
36
|
+
positionAccount?: string;
|
|
37
|
+
limitPrice?: string;
|
|
38
|
+
network?: string;
|
|
39
|
+
ttlSeconds?: number;
|
|
40
|
+
orderID?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface TpSlIntentParams {
|
|
43
|
+
orderID: number;
|
|
44
|
+
price: string;
|
|
45
|
+
symbol: string;
|
|
46
|
+
/** Position side being closed; default 'long' (TP/SL on long = place short order) */
|
|
47
|
+
side?: 'long' | 'short';
|
|
48
|
+
network?: string;
|
|
49
|
+
ttlSeconds?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface SignData {
|
|
52
|
+
limitType: number;
|
|
53
|
+
orderID: number;
|
|
54
|
+
orderType: number;
|
|
55
|
+
productType: string;
|
|
56
|
+
targetPrice: string;
|
|
57
|
+
expireTime: number;
|
|
58
|
+
wallet: string;
|
|
59
|
+
writer: string;
|
|
60
|
+
recipient: string;
|
|
61
|
+
chainId: number;
|
|
62
|
+
marginAsset: string;
|
|
63
|
+
marginAmount: string;
|
|
64
|
+
underlyingAsset: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ManagedOrder {
|
|
67
|
+
holder: string;
|
|
68
|
+
quantity: string;
|
|
69
|
+
writer: string;
|
|
70
|
+
recipient: string;
|
|
71
|
+
settingsIndex: string;
|
|
72
|
+
productTypeIndex: string;
|
|
73
|
+
oracleIndex: string;
|
|
74
|
+
nftFreeOption: string;
|
|
75
|
+
optionSourceType: string;
|
|
76
|
+
liquidationToEOA: boolean;
|
|
77
|
+
offerID: string;
|
|
78
|
+
}
|
|
79
|
+
export interface PPOSettings {
|
|
80
|
+
optionExpireTime?: string;
|
|
81
|
+
optionCategory?: 'ATM' | 'OTM';
|
|
82
|
+
ratio?: number;
|
|
83
|
+
}
|
|
84
|
+
export interface CreateOrderRequest {
|
|
85
|
+
signData: SignData;
|
|
86
|
+
signature: string;
|
|
87
|
+
managedOrder: ManagedOrder;
|
|
88
|
+
ppoSettings?: PPOSettings;
|
|
89
|
+
}
|
|
90
|
+
export interface MarketOrderResponse {
|
|
91
|
+
success: boolean;
|
|
92
|
+
message: string;
|
|
93
|
+
timestamp: string;
|
|
94
|
+
executionTimeMs: number;
|
|
95
|
+
orderType: 'market';
|
|
96
|
+
jobId: string;
|
|
97
|
+
statusUrl: string;
|
|
98
|
+
streamUrl: string;
|
|
99
|
+
}
|
|
100
|
+
export interface LimitOrderResponse {
|
|
101
|
+
success: boolean;
|
|
102
|
+
message: string;
|
|
103
|
+
timestamp: string;
|
|
104
|
+
executionTimeMs: number;
|
|
105
|
+
orderType: 'limit';
|
|
106
|
+
limitOrderId: string;
|
|
107
|
+
}
|
|
108
|
+
export interface OrderErrorResponse {
|
|
109
|
+
success: false;
|
|
110
|
+
message: string;
|
|
111
|
+
timestamp: string;
|
|
112
|
+
executionTimeMs: number;
|
|
113
|
+
orderType: string;
|
|
114
|
+
errorMessage: string;
|
|
115
|
+
}
|
|
116
|
+
export interface CreatePerpsOptionRequest {
|
|
117
|
+
managedOrder: ManagedOrder & {
|
|
118
|
+
oracleIndex?: string;
|
|
119
|
+
premiumSign?: Record<string, unknown>;
|
|
120
|
+
};
|
|
121
|
+
signature: string;
|
|
122
|
+
orderId: string;
|
|
123
|
+
}
|
|
124
|
+
export interface OptionOrderResponse {
|
|
125
|
+
success: boolean;
|
|
126
|
+
message: string;
|
|
127
|
+
timestamp: string;
|
|
128
|
+
executionTimeMs: number;
|
|
129
|
+
orderType: 'option';
|
|
130
|
+
jobId: string;
|
|
131
|
+
statusUrl: string;
|
|
132
|
+
streamUrl: string;
|
|
133
|
+
orderId: string;
|
|
134
|
+
operation: string;
|
|
135
|
+
positionSide: string;
|
|
136
|
+
optionType: number;
|
|
137
|
+
strikePrice: string;
|
|
138
|
+
premiumFee: string;
|
|
139
|
+
expireDate: string;
|
|
140
|
+
quantity: string;
|
|
141
|
+
associatedPerpsOrderId: string;
|
|
142
|
+
associatedPositionSide: string;
|
|
143
|
+
}
|
|
144
|
+
export interface JobStatusResponse {
|
|
145
|
+
jobId: string;
|
|
146
|
+
status: string;
|
|
147
|
+
progress: {
|
|
148
|
+
stage: string;
|
|
149
|
+
percentage: number;
|
|
150
|
+
};
|
|
151
|
+
result?: {
|
|
152
|
+
limitOrderId?: string;
|
|
153
|
+
transactionHash?: string;
|
|
154
|
+
operationType?: string;
|
|
155
|
+
operation?: Record<string, unknown>;
|
|
156
|
+
flashblocksConfirmationMs?: number;
|
|
157
|
+
success?: boolean;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export type SSEWatchStatus = 'completed' | 'failed' | 'timeout' | 'not_found';
|
|
161
|
+
export interface SSEWatchResult {
|
|
162
|
+
jobId: string;
|
|
163
|
+
status: SSEWatchStatus;
|
|
164
|
+
transactionHash?: string;
|
|
165
|
+
gasUsed?: string;
|
|
166
|
+
executionTimeMs?: number;
|
|
167
|
+
operation?: unknown;
|
|
168
|
+
error?: string;
|
|
169
|
+
}
|
|
170
|
+
export interface LimitOrderStatus {
|
|
171
|
+
limitOrderId: string;
|
|
172
|
+
status: 'PENDING' | 'TRIGGERED' | 'EXECUTING' | 'COMPLETED' | 'FAILED' | 'EXPIRED' | 'CANCELLED';
|
|
173
|
+
createdAt: number;
|
|
174
|
+
updatedAt: number;
|
|
175
|
+
}
|
|
176
|
+
export interface LimitOrderCancelRequest {
|
|
177
|
+
limitOrderId: string;
|
|
178
|
+
}
|
|
179
|
+
export interface OrdersListParams {
|
|
180
|
+
page?: number;
|
|
181
|
+
limit?: number;
|
|
182
|
+
healthFactorMax?: number;
|
|
183
|
+
healthFactorMin?: number;
|
|
184
|
+
positionSide?: string;
|
|
185
|
+
}
|
|
186
|
+
export interface WcSessionInfo {
|
|
187
|
+
walletAddress: string;
|
|
188
|
+
chainId: number;
|
|
189
|
+
sessionTopic: string;
|
|
190
|
+
pairingTopic: string;
|
|
191
|
+
}
|
|
192
|
+
export interface VaultInitResponse {
|
|
193
|
+
success: boolean;
|
|
194
|
+
message: string;
|
|
195
|
+
transactionHash?: string;
|
|
196
|
+
vaultAddresses?: string[];
|
|
197
|
+
timestamp: string;
|
|
198
|
+
executionTimeMs: number;
|
|
199
|
+
errorMessage?: string;
|
|
200
|
+
}
|
|
201
|
+
export interface DepositResult {
|
|
202
|
+
success: boolean;
|
|
203
|
+
message: string;
|
|
204
|
+
transactionHash?: string;
|
|
205
|
+
recipient: string;
|
|
206
|
+
token: string;
|
|
207
|
+
amount: string;
|
|
208
|
+
baseChainConfirmTimeMs?: number;
|
|
209
|
+
arrivalTimeMs?: number;
|
|
210
|
+
arrived?: boolean;
|
|
211
|
+
errorMessage?: string;
|
|
212
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,uBAAuB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const DEFAULT_API_URL = 'https://trading-api-test.jaspervault.pro';
|
|
2
|
+
export function loadConfig() {
|
|
3
|
+
const apiUrl = process.env.JV_API_URL ?? DEFAULT_API_URL;
|
|
4
|
+
const normalizedUrl = apiUrl.endsWith('/') ? apiUrl.slice(0, -1) : apiUrl;
|
|
5
|
+
const timeout = process.env.JV_TIMEOUT
|
|
6
|
+
? parseInt(process.env.JV_TIMEOUT, 10)
|
|
7
|
+
: 30_000;
|
|
8
|
+
return {
|
|
9
|
+
apiUrl: normalizedUrl,
|
|
10
|
+
apiKey: process.env.JV_API_KEY,
|
|
11
|
+
timeout,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/utils/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,0CAA0C,CAAC;AAQ1E,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,eAAe,CAAC;IACzD,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAE1E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU;QACpC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC;QACtC,CAAC,CAAC,MAAM,CAAC;IAEX,OAAO;QACL,MAAM,EAAE,aAAa;QACrB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;QAC9B,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const ENDPOINTS: {
|
|
2
|
+
readonly ORDER_CREATE: "/order/perps";
|
|
3
|
+
readonly ORDER_CREATE_OPTION: "/order/perpsOptionCreate";
|
|
4
|
+
readonly ORDER_SSE: "/order/perpsSSE";
|
|
5
|
+
readonly VAULT_INIT: "/order/createVaultRequest";
|
|
6
|
+
readonly LIMIT_ORDER_LIST: "/order/getPerpsLimitOrderList";
|
|
7
|
+
readonly LIMIT_ORDER_CANCEL: "/order/canclePerpsLimitOrder";
|
|
8
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const ENDPOINTS = {
|
|
2
|
+
// Order creation
|
|
3
|
+
ORDER_CREATE: '/order/perps',
|
|
4
|
+
ORDER_CREATE_OPTION: '/order/perpsOptionCreate',
|
|
5
|
+
// Job result (blocking POST, replaces both SSE stream and job status GET)
|
|
6
|
+
ORDER_SSE: '/order/perpsSSE',
|
|
7
|
+
// Vault
|
|
8
|
+
VAULT_INIT: '/order/createVaultRequest',
|
|
9
|
+
// Limit orders
|
|
10
|
+
LIMIT_ORDER_LIST: '/order/getPerpsLimitOrderList',
|
|
11
|
+
LIMIT_ORDER_CANCEL: '/order/canclePerpsLimitOrder',
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=endpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoints.js","sourceRoot":"","sources":["../../../src/utils/endpoints.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,iBAAiB;IACjB,YAAY,EAAE,cAAc;IAC5B,mBAAmB,EAAE,0BAA0B;IAE/C,0EAA0E;IAC1E,SAAS,EAAE,iBAAiB;IAE5B,QAAQ;IACR,UAAU,EAAE,2BAA2B;IAEvC,eAAe;IACf,gBAAgB,EAAE,+BAA+B;IACjD,kBAAkB,EAAE,8BAA8B;CAC1C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const EXIT_CODES: {
|
|
2
|
+
readonly SUCCESS: 0;
|
|
3
|
+
readonly CONFIG_ERROR: 1;
|
|
4
|
+
readonly HTTP_ERROR: 2;
|
|
5
|
+
readonly BUSINESS_ERROR: 3;
|
|
6
|
+
};
|
|
7
|
+
export declare function printJson(data: unknown, pretty?: boolean): void;
|
|
8
|
+
export declare function printError(message: string): void;
|
|
9
|
+
export declare function exitWithError(message: string, code?: number): never;
|
|
10
|
+
export declare function readStdin(): Promise<string>;
|
|
11
|
+
export declare function resolvePayload(options: {
|
|
12
|
+
data?: string;
|
|
13
|
+
payload?: string;
|
|
14
|
+
}): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const EXIT_CODES = {
|
|
2
|
+
SUCCESS: 0,
|
|
3
|
+
CONFIG_ERROR: 1,
|
|
4
|
+
HTTP_ERROR: 2,
|
|
5
|
+
BUSINESS_ERROR: 3,
|
|
6
|
+
};
|
|
7
|
+
export function printJson(data, pretty = false) {
|
|
8
|
+
const output = pretty
|
|
9
|
+
? JSON.stringify(data, null, 2)
|
|
10
|
+
: JSON.stringify(data);
|
|
11
|
+
process.stdout.write(output + '\n');
|
|
12
|
+
}
|
|
13
|
+
export function printError(message) {
|
|
14
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
15
|
+
}
|
|
16
|
+
export function exitWithError(message, code = EXIT_CODES.BUSINESS_ERROR) {
|
|
17
|
+
printError(message);
|
|
18
|
+
process.exit(code);
|
|
19
|
+
}
|
|
20
|
+
export function readStdin() {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
|
24
|
+
process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
25
|
+
process.stdin.on('error', reject);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export async function resolvePayload(options) {
|
|
29
|
+
let raw;
|
|
30
|
+
if (options.data) {
|
|
31
|
+
raw = options.data;
|
|
32
|
+
}
|
|
33
|
+
else if (options.payload) {
|
|
34
|
+
const { readFile } = await import('node:fs/promises');
|
|
35
|
+
raw = await readFile(options.payload, 'utf-8');
|
|
36
|
+
}
|
|
37
|
+
else if (!process.stdin.isTTY) {
|
|
38
|
+
raw = await readStdin();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
exitWithError('No payload provided. Use --data <json>, --payload <file>, or pipe via stdin.', EXIT_CODES.CONFIG_ERROR);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(raw.trim());
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
exitWithError('Invalid JSON payload.', EXIT_CODES.CONFIG_ERROR);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/utils/output.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,CAAC;IACV,YAAY,EAAE,CAAC;IACf,UAAU,EAAE,CAAC;IACb,cAAc,EAAE,CAAC;CACT,CAAC;AAEX,MAAM,UAAU,SAAS,CAAC,IAAa,EAAE,MAAM,GAAG,KAAK;IACrD,MAAM,MAAM,GAAG,MAAM;QACnB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,OAAe,UAAU,CAAC,cAAc;IACrF,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAChF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAGpC;IACC,IAAI,GAAW,CAAC;IAEhB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IACrB,CAAC;SAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACtD,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,aAAa,CACX,8EAA8E,EAC9E,UAAU,CAAC,YAAY,CACxB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,CAAC,uBAAuB,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
|