@kashdao/cli 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 +78 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +868 -0
- package/SECURITY.md +110 -0
- package/dist/account-AVFLEM5D.js +90 -0
- package/dist/account-AVFLEM5D.js.map +1 -0
- package/dist/auth-GUJCVKTD.js +239 -0
- package/dist/auth-GUJCVKTD.js.map +1 -0
- package/dist/chunk-BN2CUM42.js +9 -0
- package/dist/chunk-BN2CUM42.js.map +1 -0
- package/dist/chunk-BRK7KJ4O.js +154 -0
- package/dist/chunk-BRK7KJ4O.js.map +1 -0
- package/dist/chunk-KMBMQIZ7.js +393 -0
- package/dist/chunk-KMBMQIZ7.js.map +1 -0
- package/dist/chunk-LBIRQHX5.js +133 -0
- package/dist/chunk-LBIRQHX5.js.map +1 -0
- package/dist/chunk-MIXOZU2S.js +366 -0
- package/dist/chunk-MIXOZU2S.js.map +1 -0
- package/dist/chunk-QJMF73M5.js +129 -0
- package/dist/chunk-QJMF73M5.js.map +1 -0
- package/dist/chunk-UZNSYATZ.js +680 -0
- package/dist/chunk-UZNSYATZ.js.map +1 -0
- package/dist/chunk-VIADBYFY.js +94 -0
- package/dist/chunk-VIADBYFY.js.map +1 -0
- package/dist/chunk-YHCG2SUC.js +159 -0
- package/dist/chunk-YHCG2SUC.js.map +1 -0
- package/dist/chunk-YJX3JJ4M.js +174 -0
- package/dist/chunk-YJX3JJ4M.js.map +1 -0
- package/dist/client-IOM55ZCS.js +14 -0
- package/dist/client-IOM55ZCS.js.map +1 -0
- package/dist/completion-ZZGX5BQC.js +100 -0
- package/dist/completion-ZZGX5BQC.js.map +1 -0
- package/dist/config-XJL5TSYK.js +690 -0
- package/dist/config-XJL5TSYK.js.map +1 -0
- package/dist/config-store-3ZSYGXMQ.js +44 -0
- package/dist/config-store-3ZSYGXMQ.js.map +1 -0
- package/dist/docs-MDKKSJDC.js +102 -0
- package/dist/docs-MDKKSJDC.js.map +1 -0
- package/dist/eoa-IQ72EIHR.js +551 -0
- package/dist/eoa-IQ72EIHR.js.map +1 -0
- package/dist/errors-WMZIEGQI.js +18 -0
- package/dist/errors-WMZIEGQI.js.map +1 -0
- package/dist/explain-H4EH3KH3.js +165 -0
- package/dist/explain-H4EH3KH3.js.map +1 -0
- package/dist/global-options-XOLJUPTT.js +18 -0
- package/dist/global-options-XOLJUPTT.js.map +1 -0
- package/dist/health-VZEIII74.js +98 -0
- package/dist/health-VZEIII74.js.map +1 -0
- package/dist/help-footer-GTANVDNP.js +43 -0
- package/dist/help-footer-GTANVDNP.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +251 -0
- package/dist/index.js.map +1 -0
- package/dist/intro-PQQTYWR7.js +41 -0
- package/dist/intro-PQQTYWR7.js.map +1 -0
- package/dist/markets-C37JZDPE.js +295 -0
- package/dist/markets-C37JZDPE.js.map +1 -0
- package/dist/output-VCBZ3FM7.js +22 -0
- package/dist/output-VCBZ3FM7.js.map +1 -0
- package/dist/portfolio-2AEPIJIG.js +115 -0
- package/dist/portfolio-2AEPIJIG.js.map +1 -0
- package/dist/protocol-XEQXF2GW.js +1572 -0
- package/dist/protocol-XEQXF2GW.js.map +1 -0
- package/dist/quote-I4DVJ7ZB.js +144 -0
- package/dist/quote-I4DVJ7ZB.js.map +1 -0
- package/dist/schema-PIFQ65TS.js +410 -0
- package/dist/schema-PIFQ65TS.js.map +1 -0
- package/dist/setup-USZ6IODD.js +260 -0
- package/dist/setup-USZ6IODD.js.map +1 -0
- package/dist/stdin-YW2CEQXU.js +28 -0
- package/dist/stdin-YW2CEQXU.js.map +1 -0
- package/dist/trace-IZBYTUFO.js +103 -0
- package/dist/trace-IZBYTUFO.js.map +1 -0
- package/dist/trade-IIZSEXEI.js +586 -0
- package/dist/trade-IIZSEXEI.js.map +1 -0
- package/dist/version-JDK3PEP5.js +117 -0
- package/dist/version-JDK3PEP5.js.map +1 -0
- package/dist/version-check-TDCION37.js +138 -0
- package/dist/version-check-TDCION37.js.map +1 -0
- package/dist/webhooks-PTVRKICZ.js +680 -0
- package/dist/webhooks-PTVRKICZ.js.map +1 -0
- package/dist/with-retry-4FIZG3A7.js +223 -0
- package/dist/with-retry-4FIZG3A7.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
capitalize,
|
|
4
|
+
colorStatus,
|
|
5
|
+
createTable,
|
|
6
|
+
formatDate,
|
|
7
|
+
formatUsdcDecimal,
|
|
8
|
+
shortId
|
|
9
|
+
} from "./chunk-BRK7KJ4O.js";
|
|
10
|
+
import {
|
|
11
|
+
buildClient
|
|
12
|
+
} from "./chunk-QJMF73M5.js";
|
|
13
|
+
import "./chunk-KMBMQIZ7.js";
|
|
14
|
+
import {
|
|
15
|
+
parseOutcomeIndex,
|
|
16
|
+
validateUsdcDecimalShape
|
|
17
|
+
} from "./chunk-YHCG2SUC.js";
|
|
18
|
+
import "./chunk-BN2CUM42.js";
|
|
19
|
+
import {
|
|
20
|
+
parsePositiveInt,
|
|
21
|
+
readGlobals
|
|
22
|
+
} from "./chunk-LBIRQHX5.js";
|
|
23
|
+
import {
|
|
24
|
+
isQuiet,
|
|
25
|
+
log,
|
|
26
|
+
print,
|
|
27
|
+
printJson,
|
|
28
|
+
style,
|
|
29
|
+
writeNdjson
|
|
30
|
+
} from "./chunk-VIADBYFY.js";
|
|
31
|
+
import "./chunk-MIXOZU2S.js";
|
|
32
|
+
import {
|
|
33
|
+
CliValidationError,
|
|
34
|
+
toCliError
|
|
35
|
+
} from "./chunk-UZNSYATZ.js";
|
|
36
|
+
|
|
37
|
+
// src/commands/trade/index.ts
|
|
38
|
+
import { Command as Command6 } from "commander";
|
|
39
|
+
|
|
40
|
+
// src/commands/trade/buy.ts
|
|
41
|
+
import { Command } from "commander";
|
|
42
|
+
|
|
43
|
+
// src/commands/trade/place.ts
|
|
44
|
+
import ora from "ora";
|
|
45
|
+
async function placeTrade({
|
|
46
|
+
marketId,
|
|
47
|
+
side,
|
|
48
|
+
options,
|
|
49
|
+
globals
|
|
50
|
+
}) {
|
|
51
|
+
const json = globals.json;
|
|
52
|
+
const outcomeIndex = parseOutcomeIndex(options.outcome);
|
|
53
|
+
const amount = options.amount?.trim();
|
|
54
|
+
if (!amount) {
|
|
55
|
+
throw new CliValidationError(
|
|
56
|
+
"--amount is required.",
|
|
57
|
+
'Use a USDC decimal like "10" or "12.50".'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
validateUsdcDecimalShape(amount, "amount");
|
|
61
|
+
const timeoutMs = options.timeout ? parsePositiveInt(options.timeout, "timeout") : 6e4;
|
|
62
|
+
const pollIntervalMs = options.pollInterval ? parsePositiveInt(options.pollInterval, "poll-interval") : 2e3;
|
|
63
|
+
let resolvedIdempotencyKey = options.idempotencyKey;
|
|
64
|
+
if (resolvedIdempotencyKey === void 0 && options.autoIdempotencyKey) {
|
|
65
|
+
resolvedIdempotencyKey = crypto.randomUUID();
|
|
66
|
+
if (!json) {
|
|
67
|
+
log.detail("Generated Idempotency-Key", resolvedIdempotencyKey);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (options.dryRun === true) {
|
|
71
|
+
const wouldSend = {
|
|
72
|
+
marketId,
|
|
73
|
+
outcomeIndex,
|
|
74
|
+
amount,
|
|
75
|
+
side,
|
|
76
|
+
...options.clientRequestId === void 0 ? {} : { clientRequestId: options.clientRequestId }
|
|
77
|
+
};
|
|
78
|
+
const envelope = {
|
|
79
|
+
wouldSend,
|
|
80
|
+
idempotencyKey: resolvedIdempotencyKey ?? null,
|
|
81
|
+
endpoint: { method: "POST", path: "/v1/trades" }
|
|
82
|
+
};
|
|
83
|
+
if (json) {
|
|
84
|
+
printJson(envelope);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
print("");
|
|
88
|
+
print(style.bold("Dry run \u2014 no request sent."));
|
|
89
|
+
print(` ${style.dim("Endpoint ")} POST /v1/trades`);
|
|
90
|
+
print(` ${style.dim("Market ")} ${marketId}`);
|
|
91
|
+
print(` ${style.dim("Side ")} ${side}`);
|
|
92
|
+
print(` ${style.dim("Outcome ")} ${String(outcomeIndex)}`);
|
|
93
|
+
print(` ${style.dim("Amount ")} ${amount} USDC`);
|
|
94
|
+
if (options.clientRequestId !== void 0) {
|
|
95
|
+
print(` ${style.dim("Client req ")} ${options.clientRequestId}`);
|
|
96
|
+
}
|
|
97
|
+
if (resolvedIdempotencyKey !== void 0) {
|
|
98
|
+
print(` ${style.dim("Idem. key ")} ${resolvedIdempotencyKey}`);
|
|
99
|
+
}
|
|
100
|
+
print("");
|
|
101
|
+
print(style.dim("Re-run without --dry-run to submit."));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let response;
|
|
105
|
+
try {
|
|
106
|
+
const { client } = await buildClient({ requireAuth: true, globals });
|
|
107
|
+
response = await client.trades.create(
|
|
108
|
+
{
|
|
109
|
+
marketId,
|
|
110
|
+
outcomeIndex,
|
|
111
|
+
amount,
|
|
112
|
+
side,
|
|
113
|
+
...options.clientRequestId === void 0 ? {} : { clientRequestId: options.clientRequestId }
|
|
114
|
+
},
|
|
115
|
+
resolvedIdempotencyKey === void 0 ? {} : { idempotencyKey: resolvedIdempotencyKey }
|
|
116
|
+
);
|
|
117
|
+
const augment = (payload) => resolvedIdempotencyKey === void 0 ? payload : { ...payload, idempotencyKey: resolvedIdempotencyKey };
|
|
118
|
+
if (response.confirmation) {
|
|
119
|
+
if (json) {
|
|
120
|
+
printJson(augment(response));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
log.warn(
|
|
124
|
+
"High-value trade \u2014 server returned 202. Confirm with `kash trade confirm <id> <token>`."
|
|
125
|
+
);
|
|
126
|
+
print(` ${style.dim("Trade id ")} ${response.id}`);
|
|
127
|
+
print(` ${style.dim("Status ")} ${colorStatus(response.status)}`);
|
|
128
|
+
print(` ${style.dim("Token ")} ${response.confirmation.token}`);
|
|
129
|
+
print(` ${style.dim("Expires ")} ${response.confirmation.expiresAt}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!options.wait) {
|
|
133
|
+
if (json) {
|
|
134
|
+
printJson(augment(response));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
log.success(
|
|
138
|
+
`${capitalize(side)} accepted${response.idempotent ? " (idempotent replay)" : ""}: ${shortId(response.id)}`
|
|
139
|
+
);
|
|
140
|
+
print(` ${style.dim("Trade id ")} ${response.id}`);
|
|
141
|
+
print(` ${style.dim("Status ")} ${colorStatus(response.status)}`);
|
|
142
|
+
print(` ${style.dim("Amount ")} ${formatUsdcDecimal(response.amount)}`);
|
|
143
|
+
log.info("Run 'kash trade status <id> --poll' to follow it to completion.");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
let completed;
|
|
147
|
+
try {
|
|
148
|
+
completed = await waitForCompletion(client, response, {
|
|
149
|
+
timeoutMs,
|
|
150
|
+
pollIntervalMs,
|
|
151
|
+
json
|
|
152
|
+
});
|
|
153
|
+
} catch (waitCause) {
|
|
154
|
+
if (json) {
|
|
155
|
+
printJson({
|
|
156
|
+
id: response.id,
|
|
157
|
+
status: response.status,
|
|
158
|
+
waited: false,
|
|
159
|
+
partial: true
|
|
160
|
+
});
|
|
161
|
+
} else {
|
|
162
|
+
log.warn(
|
|
163
|
+
`Trade created but poll did not reach a terminal state. Resume with: kash trade status ${response.id} --poll`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
throw waitCause;
|
|
167
|
+
}
|
|
168
|
+
if (json) {
|
|
169
|
+
printJson(augment({ ...completed, idempotent: response.idempotent, waited: true }));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
renderTerminal(completed);
|
|
173
|
+
} catch (cause) {
|
|
174
|
+
throw toCliError(cause);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function waitForCompletion(client, initial, opts) {
|
|
178
|
+
const ac = new AbortController();
|
|
179
|
+
const onSig = () => ac.abort();
|
|
180
|
+
process.on("SIGINT", onSig);
|
|
181
|
+
process.on("SIGTERM", onSig);
|
|
182
|
+
try {
|
|
183
|
+
if (opts.json || isQuiet()) {
|
|
184
|
+
return await client.trades.waitForCompletion(initial.id, {
|
|
185
|
+
timeoutMs: opts.timeoutMs,
|
|
186
|
+
pollIntervalMs: opts.pollIntervalMs,
|
|
187
|
+
signal: ac.signal
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const spinner = ora({
|
|
191
|
+
text: `Waiting for trade ${shortId(initial.id)}\u2026`,
|
|
192
|
+
stream: process.stderr
|
|
193
|
+
}).start();
|
|
194
|
+
try {
|
|
195
|
+
const completed = await client.trades.waitForCompletion(initial.id, {
|
|
196
|
+
timeoutMs: opts.timeoutMs,
|
|
197
|
+
pollIntervalMs: opts.pollIntervalMs,
|
|
198
|
+
signal: ac.signal,
|
|
199
|
+
onStatus: (trade) => {
|
|
200
|
+
spinner.text = `Trade ${shortId(trade.id)} \u2014 status: ${trade.status}`;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
if (completed.status === "completed") {
|
|
204
|
+
spinner.succeed(`Trade ${shortId(completed.id)} completed.`);
|
|
205
|
+
} else {
|
|
206
|
+
spinner.fail(`Trade ${shortId(completed.id)} ${completed.status}.`);
|
|
207
|
+
}
|
|
208
|
+
return completed;
|
|
209
|
+
} catch (cause) {
|
|
210
|
+
spinner.fail("Polling stopped.");
|
|
211
|
+
throw cause;
|
|
212
|
+
}
|
|
213
|
+
} finally {
|
|
214
|
+
process.off("SIGINT", onSig);
|
|
215
|
+
process.off("SIGTERM", onSig);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function renderTerminal(trade) {
|
|
219
|
+
print("");
|
|
220
|
+
print(` ${style.dim("Trade id ")} ${trade.id}`);
|
|
221
|
+
print(` ${style.dim("Status ")} ${colorStatus(trade.status)}`);
|
|
222
|
+
print(` ${style.dim("Amount ")} ${formatUsdcDecimal(trade.amount)}`);
|
|
223
|
+
if (trade.txHash !== null) {
|
|
224
|
+
print(` ${style.dim("Tx hash ")} ${trade.txHash}`);
|
|
225
|
+
}
|
|
226
|
+
if (trade.tokensOut !== null) {
|
|
227
|
+
print(` ${style.dim("Tokens out ")} ${trade.tokensOut}`);
|
|
228
|
+
}
|
|
229
|
+
if (trade.errorMessage !== null || trade.errorCode !== null) {
|
|
230
|
+
print(
|
|
231
|
+
` ${style.dim("Error ")} [${trade.errorCode ?? "UNKNOWN"}] ${trade.errorMessage ?? ""}`
|
|
232
|
+
);
|
|
233
|
+
log.info(`Inspect the full timeline: kash trace ${trade.correlationId}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/commands/trade/buy.ts
|
|
238
|
+
var buyCommand = new Command("buy").description("Buy outcome tokens for a market.").argument("<marketId>", "market UUID").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption("-a, --amount <usdc>", "USDC amount as a decimal (max 6 fractional digits)").option("--wait", "block until the trade reaches a terminal state").option(
|
|
239
|
+
"--wait-timeout-ms, --timeout <ms>",
|
|
240
|
+
"wait timeout in milliseconds (default 60000) \u2014 distinct from the global --timeout-ms (per-HTTP-request)"
|
|
241
|
+
).option(
|
|
242
|
+
"--poll-interval-ms, --poll-interval <ms>",
|
|
243
|
+
"wait poll interval in milliseconds (default 2000)"
|
|
244
|
+
).option("--idempotency-key <key>", "sets the Idempotency-Key HTTP header").option(
|
|
245
|
+
"--auto-idempotency-key",
|
|
246
|
+
"auto-generate an Idempotency-Key (UUID v4) and surface it in the response"
|
|
247
|
+
).option("--client-request-id <id>", "sets the body-level clientRequestId for replay safety").option(
|
|
248
|
+
"--dry-run",
|
|
249
|
+
"preview the request without sending \u2014 emits the would-be body and resolved headers"
|
|
250
|
+
).addHelpText(
|
|
251
|
+
"after",
|
|
252
|
+
`
|
|
253
|
+
Examples:
|
|
254
|
+
$ kash trade buy 9f0b... --outcome 0 --amount 10
|
|
255
|
+
$ kash trade buy 9f0b... --outcome 1 --amount 25 --wait
|
|
256
|
+
$ kash trade buy 9f0b... --outcome 0 --amount 5 --auto-idempotency-key --json --quiet
|
|
257
|
+
$ kash trade buy 9f0b... --outcome 0 --amount 10 --dry-run --json
|
|
258
|
+
`
|
|
259
|
+
).action(async (marketId, options, cmd) => {
|
|
260
|
+
const globals = readGlobals(cmd);
|
|
261
|
+
await placeTrade({ marketId, side: "buy", options, globals });
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// src/commands/trade/confirm.ts
|
|
265
|
+
import { Command as Command2 } from "commander";
|
|
266
|
+
var confirmCommand = new Command2("confirm").description("Confirm a high-value trade using its one-time token.").argument("<id>", "trade UUID").argument("[token]", "confirmation token (omit to read from stdin or prompt)").option("--token-stdin", "read the confirmation token from stdin").addHelpText(
|
|
267
|
+
"after",
|
|
268
|
+
`
|
|
269
|
+
Examples:
|
|
270
|
+
# Interactive prompt (no shell history footprint).
|
|
271
|
+
$ kash trade confirm 9f0b...
|
|
272
|
+
|
|
273
|
+
# Pipe from a captured token.
|
|
274
|
+
$ cat token.txt | kash trade confirm 9f0b... --token-stdin
|
|
275
|
+
|
|
276
|
+
# Positional (convenient but shows up in ~/.bash_history).
|
|
277
|
+
$ kash trade confirm 9f0b... eyJ...
|
|
278
|
+
$ kash trade confirm 9f0b... eyJ... --json --quiet
|
|
279
|
+
`
|
|
280
|
+
).action(
|
|
281
|
+
async (id, positional, options, cmd) => {
|
|
282
|
+
const globals = readGlobals(cmd);
|
|
283
|
+
const token = await resolveToken(positional, options);
|
|
284
|
+
let trade;
|
|
285
|
+
try {
|
|
286
|
+
const { client } = await buildClient({ requireAuth: true, globals });
|
|
287
|
+
trade = await client.trades.confirm(id, { token });
|
|
288
|
+
} catch (cause) {
|
|
289
|
+
throw toCliError(cause);
|
|
290
|
+
}
|
|
291
|
+
if (globals.json) {
|
|
292
|
+
printJson(trade);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
log.success(`Trade ${trade.id} confirmed.`);
|
|
296
|
+
print(` ${style.dim("Status ")} ${colorStatus(trade.status)}`);
|
|
297
|
+
print(` ${style.dim("Amount ")} ${formatUsdcDecimal(trade.amount)}`);
|
|
298
|
+
log.info("Run 'kash trade status <id> --poll' to follow it to completion.");
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
async function resolveToken(positional, options) {
|
|
302
|
+
if (positional !== void 0 && positional !== "" && options.tokenStdin === true) {
|
|
303
|
+
throw new CliValidationError(
|
|
304
|
+
"Pass either the positional [token] OR --token-stdin, not both.",
|
|
305
|
+
"The positional token would silently win and the piped value would be dropped.",
|
|
306
|
+
"token"
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
if (positional !== void 0 && positional !== "") {
|
|
310
|
+
return positional;
|
|
311
|
+
}
|
|
312
|
+
if (options.tokenStdin === true) {
|
|
313
|
+
const { readStdinTrimmed } = await import("./stdin-YW2CEQXU.js");
|
|
314
|
+
return readStdinTrimmed();
|
|
315
|
+
}
|
|
316
|
+
if (process.stdin.isTTY) {
|
|
317
|
+
const { password } = await import("@inquirer/prompts");
|
|
318
|
+
const answer = await password({
|
|
319
|
+
message: "Paste the confirmation token:",
|
|
320
|
+
mask: "*"
|
|
321
|
+
});
|
|
322
|
+
return answer.trim();
|
|
323
|
+
}
|
|
324
|
+
throw new CliValidationError(
|
|
325
|
+
"No confirmation token provided.",
|
|
326
|
+
"Pass the token positionally, use --token-stdin to read it from a pipe, or run interactively for a prompt.",
|
|
327
|
+
"token"
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/commands/trade/list.ts
|
|
332
|
+
import { Command as Command3 } from "commander";
|
|
333
|
+
var listTradesCommand = new Command3("list").description("List your trades.").option(
|
|
334
|
+
"-s, --status <status...>",
|
|
335
|
+
"status filter; repeat or comma-separate (e.g. --status pending --status executing | --status pending,executing)"
|
|
336
|
+
).option("-m, --market <id>", "filter by market id").option("-l, --limit <n>", "page size (1-100)", "20").option("-c, --cursor <cursor>", "pagination cursor").option("-a, --all", "walk every page (use with --json for export)").option(
|
|
337
|
+
"--ndjson",
|
|
338
|
+
"stream results as newline-delimited JSON (one record per line); implies --all"
|
|
339
|
+
).addHelpText(
|
|
340
|
+
"after",
|
|
341
|
+
`
|
|
342
|
+
Examples:
|
|
343
|
+
$ kash trade list
|
|
344
|
+
$ kash trade list --status pending,executing --json --quiet
|
|
345
|
+
$ kash trade list --status pending --status executing --json --quiet
|
|
346
|
+
$ kash trade list --market 9f0b... --json --quiet | jq '.data[] | select(.status=="completed")'
|
|
347
|
+
$ kash trade list --all --ndjson | while read -r line; do echo "$line" | jq -r .id; done
|
|
348
|
+
`
|
|
349
|
+
).action(async (options, cmd) => {
|
|
350
|
+
const globals = readGlobals(cmd);
|
|
351
|
+
const limit = options.limit === void 0 ? 20 : parsePositiveInt(options.limit, "limit");
|
|
352
|
+
if (limit < 1 || limit > 100) {
|
|
353
|
+
throw new CliValidationError("--limit must be between 1 and 100.", void 0, "limit");
|
|
354
|
+
}
|
|
355
|
+
const status = normaliseStatus(options.status);
|
|
356
|
+
let response;
|
|
357
|
+
try {
|
|
358
|
+
const { client } = await buildClient({ requireAuth: true, globals });
|
|
359
|
+
response = await client.trades.list({
|
|
360
|
+
limit,
|
|
361
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor },
|
|
362
|
+
...status === void 0 ? {} : { status },
|
|
363
|
+
...options.market === void 0 ? {} : { marketId: options.market }
|
|
364
|
+
});
|
|
365
|
+
if (options.ndjson) {
|
|
366
|
+
for await (const trade of response) {
|
|
367
|
+
writeNdjson(trade);
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (options.all) {
|
|
372
|
+
const collected = [];
|
|
373
|
+
for await (const trade of response) collected.push(trade);
|
|
374
|
+
if (globals.json) {
|
|
375
|
+
printJson({
|
|
376
|
+
data: collected,
|
|
377
|
+
pagination: { hasMore: false, cursor: null },
|
|
378
|
+
count: collected.length
|
|
379
|
+
});
|
|
380
|
+
} else {
|
|
381
|
+
renderTable(collected);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
} catch (cause) {
|
|
386
|
+
throw toCliError(cause);
|
|
387
|
+
}
|
|
388
|
+
if (globals.json) {
|
|
389
|
+
printJson({
|
|
390
|
+
data: response.data,
|
|
391
|
+
pagination: response.pagination,
|
|
392
|
+
count: response.data.length
|
|
393
|
+
});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (response.data.length === 0) {
|
|
397
|
+
log.info("No trades matched the filter.");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
renderTable(response.data);
|
|
401
|
+
if (response.pagination.hasMore && response.pagination.cursor) {
|
|
402
|
+
log.detail("Next page", `--cursor ${response.pagination.cursor}`);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
function normaliseStatus(raw) {
|
|
406
|
+
if (!raw || raw.length === 0) return void 0;
|
|
407
|
+
const seen = /* @__PURE__ */ new Set();
|
|
408
|
+
for (const entry of raw) {
|
|
409
|
+
for (const piece of entry.split(",")) {
|
|
410
|
+
const trimmed = piece.trim();
|
|
411
|
+
if (trimmed.length > 0) seen.add(trimmed);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return seen.size === 0 ? void 0 : [...seen].join(",");
|
|
415
|
+
}
|
|
416
|
+
function renderTable(trades) {
|
|
417
|
+
const table = createTable(["Trade", "Market", "Side", "Out#", "Amount", "Status", "Tx"]);
|
|
418
|
+
for (const t of trades) {
|
|
419
|
+
table.push([
|
|
420
|
+
shortId(t.id),
|
|
421
|
+
shortId(t.marketId),
|
|
422
|
+
t.side,
|
|
423
|
+
String(t.outcomeIndex),
|
|
424
|
+
formatUsdcDecimal(t.amount),
|
|
425
|
+
colorStatus(t.status),
|
|
426
|
+
t.txHash ? shortId(t.txHash, 10) : "-"
|
|
427
|
+
]);
|
|
428
|
+
}
|
|
429
|
+
print(table.toString());
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/commands/trade/sell.ts
|
|
433
|
+
import { Command as Command4 } from "commander";
|
|
434
|
+
var sellCommand = new Command4("sell").description("Sell outcome tokens back to the market (Kash-orchestrated / hosted-API flow).").argument("<marketId>", "market UUID").requiredOption("-o, --outcome <index>", "outcome index (0-based)").requiredOption(
|
|
435
|
+
"-a, --amount <usdc>",
|
|
436
|
+
"target USDC to receive (decimal, max 6 fractional digits) \u2014 NOT tokens-in. Use `kash quote sell --tokens <n>` if you have a tokens-in figure and want a USDC quote first."
|
|
437
|
+
).option("--wait", "block until the trade reaches a terminal state").option(
|
|
438
|
+
"--wait-timeout-ms, --timeout <ms>",
|
|
439
|
+
"wait timeout in milliseconds (default 60000) \u2014 distinct from the global --timeout-ms (per-HTTP-request)"
|
|
440
|
+
).option(
|
|
441
|
+
"--poll-interval-ms, --poll-interval <ms>",
|
|
442
|
+
"wait poll interval in milliseconds (default 2000)"
|
|
443
|
+
).option("--idempotency-key <key>", "sets the Idempotency-Key HTTP header").option(
|
|
444
|
+
"--auto-idempotency-key",
|
|
445
|
+
"auto-generate an Idempotency-Key (UUID v4) and surface it in the response"
|
|
446
|
+
).option("--client-request-id <id>", "sets the body-level clientRequestId for replay safety").option(
|
|
447
|
+
"--dry-run",
|
|
448
|
+
"preview the request without sending \u2014 emits the would-be body and resolved headers"
|
|
449
|
+
).addHelpText(
|
|
450
|
+
"after",
|
|
451
|
+
`
|
|
452
|
+
Examples:
|
|
453
|
+
$ kash trade sell 9f0b... --outcome 0 --amount 10 # target $10 USDC out
|
|
454
|
+
$ kash trade sell 9f0b... --outcome 1 --amount 25 --wait
|
|
455
|
+
$ kash trade sell 9f0b... --outcome 0 --amount 5 --auto-idempotency-key --json --quiet
|
|
456
|
+
$ kash trade sell 9f0b... --outcome 0 --amount 10 --dry-run --json
|
|
457
|
+
|
|
458
|
+
Notes:
|
|
459
|
+
--amount is in USDC for both buy AND sell:
|
|
460
|
+
BUY \u2014 USDC you spend (input).
|
|
461
|
+
SELL \u2014 USDC you target receiving (output).
|
|
462
|
+
This is the API contract, not a CLI choice \u2014 \`kash quote sell --tokens N\`
|
|
463
|
+
takes a tokens-in figure if that's the shape you have on hand.
|
|
464
|
+
`
|
|
465
|
+
).action(async (marketId, options, cmd) => {
|
|
466
|
+
const globals = readGlobals(cmd);
|
|
467
|
+
await placeTrade({ marketId, side: "sell", options, globals });
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// src/commands/trade/status.ts
|
|
471
|
+
import { Command as Command5 } from "commander";
|
|
472
|
+
import ora2 from "ora";
|
|
473
|
+
var statusCommand = new Command5("status").description("Show the status of a trade. Use --poll to block until terminal.").argument("<id>", "trade UUID").addHelpText(
|
|
474
|
+
"after",
|
|
475
|
+
`
|
|
476
|
+
Examples:
|
|
477
|
+
$ kash trade status 9f0b...
|
|
478
|
+
$ kash trade status 9f0b... --poll
|
|
479
|
+
$ kash trade status 9f0b... --poll --json --quiet | jq -r '.txHash'
|
|
480
|
+
`
|
|
481
|
+
).option("--poll", "poll until the trade reaches a terminal state").option(
|
|
482
|
+
"--wait-timeout-ms, --timeout <ms>",
|
|
483
|
+
"poll timeout in milliseconds (default 60000) \u2014 distinct from the global --timeout-ms (per-HTTP-request)"
|
|
484
|
+
).option(
|
|
485
|
+
"--poll-interval-ms, --poll-interval <ms>",
|
|
486
|
+
"poll interval in milliseconds (default 2000)"
|
|
487
|
+
).action(async (id, options, cmd) => {
|
|
488
|
+
const globals = readGlobals(cmd);
|
|
489
|
+
let trade;
|
|
490
|
+
try {
|
|
491
|
+
const { client } = await buildClient({ requireAuth: true, globals });
|
|
492
|
+
if (options.poll) {
|
|
493
|
+
const timeoutMs = options.timeout ? parsePositiveInt(options.timeout, "timeout") : 6e4;
|
|
494
|
+
const pollIntervalMs = options.pollInterval ? parsePositiveInt(options.pollInterval, "poll-interval") : 2e3;
|
|
495
|
+
const ac = new AbortController();
|
|
496
|
+
const onSig = () => ac.abort();
|
|
497
|
+
process.on("SIGINT", onSig);
|
|
498
|
+
process.on("SIGTERM", onSig);
|
|
499
|
+
try {
|
|
500
|
+
if (globals.json || isQuiet()) {
|
|
501
|
+
trade = await client.trades.waitForCompletion(id, {
|
|
502
|
+
timeoutMs,
|
|
503
|
+
pollIntervalMs,
|
|
504
|
+
signal: ac.signal
|
|
505
|
+
});
|
|
506
|
+
} else {
|
|
507
|
+
const spinner = ora2({
|
|
508
|
+
text: `Polling trade ${shortId(id)}\u2026`,
|
|
509
|
+
stream: process.stderr
|
|
510
|
+
}).start();
|
|
511
|
+
try {
|
|
512
|
+
trade = await client.trades.waitForCompletion(id, {
|
|
513
|
+
timeoutMs,
|
|
514
|
+
pollIntervalMs,
|
|
515
|
+
signal: ac.signal,
|
|
516
|
+
onStatus: (current) => {
|
|
517
|
+
spinner.text = `Trade ${shortId(current.id)} \u2014 status: ${current.status}`;
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
if (trade.status === "completed") {
|
|
521
|
+
spinner.succeed(`Trade ${shortId(trade.id)} completed.`);
|
|
522
|
+
} else {
|
|
523
|
+
spinner.fail(`Trade ${shortId(trade.id)} ${trade.status}.`);
|
|
524
|
+
}
|
|
525
|
+
} catch (cause) {
|
|
526
|
+
spinner.fail("Polling stopped.");
|
|
527
|
+
throw cause;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
} finally {
|
|
531
|
+
process.off("SIGINT", onSig);
|
|
532
|
+
process.off("SIGTERM", onSig);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
trade = await client.trades.get(id);
|
|
536
|
+
}
|
|
537
|
+
} catch (cause) {
|
|
538
|
+
throw toCliError(cause);
|
|
539
|
+
}
|
|
540
|
+
if (globals.json) {
|
|
541
|
+
printJson(trade);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
print("");
|
|
545
|
+
print(` ${style.dim("Trade id ")} ${trade.id}`);
|
|
546
|
+
print(` ${style.dim("Market id ")} ${trade.marketId}`);
|
|
547
|
+
print(` ${style.dim("Side ")} ${trade.side}`);
|
|
548
|
+
print(` ${style.dim("Outcome idx ")} ${String(trade.outcomeIndex)}`);
|
|
549
|
+
print(` ${style.dim("Amount ")} ${formatUsdcDecimal(trade.amount)}`);
|
|
550
|
+
print(` ${style.dim("Status ")} ${colorStatus(trade.status)}`);
|
|
551
|
+
if (trade.txHash !== null) {
|
|
552
|
+
print(` ${style.dim("Tx hash ")} ${trade.txHash}`);
|
|
553
|
+
}
|
|
554
|
+
if (trade.tokensOut !== null) {
|
|
555
|
+
print(` ${style.dim("Tokens out ")} ${trade.tokensOut}`);
|
|
556
|
+
}
|
|
557
|
+
if (trade.errorCode !== null || trade.errorMessage !== null) {
|
|
558
|
+
print(
|
|
559
|
+
` ${style.dim("Error ")} [${trade.errorCode ?? "UNKNOWN"}] ${trade.errorMessage ?? ""}`
|
|
560
|
+
);
|
|
561
|
+
log.info(`Inspect the full timeline: kash trace ${trade.correlationId}`);
|
|
562
|
+
}
|
|
563
|
+
print(` ${style.dim("Created ")} ${formatDate(trade.createdAt)}`);
|
|
564
|
+
print(` ${style.dim("Updated ")} ${formatDate(trade.updatedAt)}`);
|
|
565
|
+
if (trade.webhookDelivery !== null) {
|
|
566
|
+
const wh = trade.webhookDelivery;
|
|
567
|
+
const failure = wh.lastFailureCode === null ? "" : ` (${wh.lastFailureCode})`;
|
|
568
|
+
const httpStatus = wh.lastStatusCode === null ? "" : ` HTTP ${String(wh.lastStatusCode)}`;
|
|
569
|
+
print(
|
|
570
|
+
` ${style.dim("Webhook ")} ${colorStatus(wh.status)} attempts=${String(wh.attempts)}${httpStatus}${failure}`
|
|
571
|
+
);
|
|
572
|
+
if (wh.lastAttemptedAt !== null) {
|
|
573
|
+
print(` ${style.dim(" Last try ")} ${formatDate(wh.lastAttemptedAt)}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (trade.status === "pending_confirmation") {
|
|
577
|
+
log.warn("Trade is awaiting high-value confirmation. Run `kash trade confirm <id> <token>`.");
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
// src/commands/trade/index.ts
|
|
582
|
+
var tradeCommand = new Command6("trade").description("Place trades and inspect their status.").addCommand(buyCommand).addCommand(sellCommand).addCommand(statusCommand).addCommand(listTradesCommand).addCommand(confirmCommand);
|
|
583
|
+
export {
|
|
584
|
+
tradeCommand
|
|
585
|
+
};
|
|
586
|
+
//# sourceMappingURL=trade-IIZSEXEI.js.map
|