@candledottv/cli 0.11.38 → 0.11.40
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 +3 -3
- package/dist/index.js +335 -71
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @candledottv/cli
|
|
2
2
|
|
|
3
|
-
The Candle CLI (current version 0.11.
|
|
3
|
+
The Candle CLI (current version 0.11.39): authorize a device from your browser, keep your own keys
|
|
4
4
|
in an encrypted vault on this machine, hand an agent a TEE wallet it can trade, and manage API
|
|
5
5
|
keys, wallets, and setup health from the terminal. Zero runtime dependencies; the whole thing is
|
|
6
6
|
one self-contained `dist/index.js` that runs under plain Node.
|
|
@@ -122,7 +122,7 @@ emergency procedure, rebinding, trusted wallets and API key access levels are al
|
|
|
122
122
|
| `candle swap <from> <to> --amount <n>\|--percent <n> --wallet <tee> [--to <tee>] [--wait]` | Quote, confirm and swap through the TEE wallet's bound key; the first buy after a launch is this command, unless `candle launch --buy` made it. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana). SOL or USDC to ETH or USDG, or back, is a bridge through Relay into this key's own TEE wallet on the other chain (`--to` names it when there are several); `--wait` follows it for up to ten minutes. |
|
|
123
123
|
| `candle swap status <id> [--kind trade\|swap\|launch] [--wait]` | Read an operation without resending it; `--wait` follows a bridge until it ends. |
|
|
124
124
|
| `candle transfer --to <address\|wallet name\|vault> --asset <SOL\|USDC\|CNDL\|ETH\|USDG>\|--mint <mint>\|--token <0x...> --amount <decimal\|max> [--wallet <tee>] [--rpc-url <url>] [--yes]` | Move funds out of a TEE wallet through its bound Withdraw key, to the wallet's own pinned vault (`--to vault`, any token, `max` allowed, one SOL transfer at most the server's backstop ceiling, 50 SOL by default) or to another of the account's wallets you linked while signed in or marked trusted (a base asset, counted against the key's caps, which must include a per-asset cap and a USD `--tx-limit`). The asset decides the chain: ETH, USDG or `--token` moves from a Hood TEE wallet. The destination and its kind are shown before you confirm; Candle builds, the relay signs, Candle broadcasts. |
|
|
125
|
-
| `candle launch --name <name> --symbol <symbol> --image <path>\|--image-url <url> --wallet <tee
|
|
125
|
+
| `candle launch --name <name> --symbol <symbol> --image <path>\|--image-url <url> --wallet <tee>\|embedded [--banner <file\|URL>] [--buy <amount>] [--quote-asset <asset>] [--dex-version v3\|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0\|100\|200] [--pool-fee 25\|50\|100] [--pool-open legacy\|curve_price] [--exp-pool-shape floor\|full_range] [--dry-run]` | Create a token from a TEE wallet. The image is `--image-url` (an `https://` URL) or `--image <path>` (a PNG, JPEG, GIF or WebP file on this machine, at most 10MB), never both; `--image` uploads the file before the confirm prompt, which makes it public and permanent, and launches with the URL that comes back. `--banner <file|URL>` adds a wide banner from a local file or an https URL. Local banners use `kind=banner`, retain their original shape (wider than 1.5:1), and become public and permanent before confirmation; the result reports `bannerUrl`. Without `--buy` it makes no first buy. `--buy <amount>` adds one from the same wallet, in whole units of the quote asset, reserved against the key's spend window before signing: as its own leg in one Jito bundle with the launch on Solana, as a last leg on Hood. It needs the server's `TEE_LAUNCH_BUYS_ENABLED`, which is off by default; while it is off Candle refuses the buy and nothing is built. Needs `launch:write`, `allowLaunch` turned on for the wallet by the account owner, and the Pro or Max plan. `--wallet embedded --buy <amount>` launches from the account's embedded wallet instead, on Solana, as an atomic launch: Candle's dry run first (nothing built or recorded), the quote to confirm, then one build and one `/launch/atomic/submit`, with Candle signing both legs into one Jito bundle. It needs Pro or Max, `launch:write` and `swap:write`, and does not depend on `TEE_LAUNCH_BUYS_ENABLED`. Without a first buy, every plan can launch from the embedded wallet with the MCP tool `candle_launch_token` or the SDK's `launch()`. The wallet decides the chain: a Hood TEE wallet launches on Hood and needs `--dex-version`. `--quote-asset` is sol, usdc or cndl on Solana, eth or usdg on Hood. `--mode` picks the curve: `open` or `exclusive`, `test-open` or `test-exclusive` where the server has `ENABLE_TEST_CURVES` on, or on Solana with sol or cndl `exp-a`, `exp-b`, `exp-c` and `exp-custom`, which takes `--curve-v <V>` and `--curve-g <G>` in whole units of the quote asset where the server has `EXP_CUSTOM_CURVES` on. `--creator-fee-bps 100` or `200` adds an extra creator fee on an `exp-` mode. On an `exp-` curve the first buy is at most 0.5 SOL (2,000,000 CNDL), or 10% of the threshold when that is lower. Solana pool choices: `--pool-fee 25|50|100` (bps; add `--pool-fee-from <bps> --pool-fee-minutes <min>` for an anti-snipe fall to that rate, at most 2500 bps over 30 minutes), `--pool-open legacy|curve_price` (preset tiers) and `--exp-pool-shape full_range` (exp- curves). For Solana preset tiers, omitting `--pool-open` defaults to `curve_price` (opens at the curve's last price and burns the remaining base) when `stakerAllocationBps` plus the team allocation fit the tier's limit (`graduation.maxStakerCarveBps` in the dry run); otherwise it falls back to `legacy` without refusal. Use `--pool-open legacy` to opt out. Explicit `curve_price` is refused above the limit. Experimental curves and Hood are unchanged. `--dry-run` prints Candle's preview of the launch (the curve's terms, the first buy's share, the fees, how the pool opens at graduation, the dev-buy ceiling) and builds, signs and records nothing; with `--image` the upload still happens. |
|
|
126
126
|
| `candle image upload <path>` | Uploads a PNG, JPEG, GIF or WebP file, at most 10MB, and prints the `https://` URL Candle hosts it at, ready to be a launch's `imageUrl`. The type is read from the file's first bytes. The file becomes public and permanent. Needs `launch:write`; spends nothing and does not count against the launch cap. `--json` adds the content type and the size. |
|
|
127
127
|
| `candle pnl [--profile <name>]` | P&L: realized net, fees, unrealized and total, then open positions, the same figures the console shows. Without `--profile`, the account's books (every profile, the web app and the CLI, one ledger; needs the Read scope), with a By agent table (each key, then Manual, then the account total). With `--profile <name>`, that profile's key's share of the same P&L: realized on its sales and the open positions that belong to it, with a row per wallet that sums to the total. Each wallet is its own average-cost pool, and a recorded move between the account's wallets carries its cost. Both show a Solana block, a Hood block, then the combined figure, the closed positions as money made, minus money lost, plus partial sells, and a `CHAIN` column (and `WALLET`, where the API names it) on positions. Unpriced positions are never valued at zero; dust (worth under one cent) is marked and still counted. A bridge between Solana and Hood is not a trade, and bridge and Relay costs are not netted. DAMM v2 LP positions are included: realized (withdrawals against the cost basis at the add, plus claimed fees), unrealized (open positions at their share of the pool plus unclaimed fees, against that basis) and vs holding (what the deposited tokens would be worth held); `Total` stays the console's token figure and `Total with LP` covers tokens and LP. |
|
|
128
128
|
| `candle portfolio [<filter>] [--chain solana\|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps]` | Vault, TEE and embedded wallets on Solana and Hood in one table with chain, amount, price and value, subtotals per group and per chain, and a total. Solana vault and external balances are read over your own Solana RPC; Candle is sent only the mints, for prices. EVM vault keys are read over your own Hood RPC (`--evm-rpc-url`, else `CANDLE_EVM_RPC_URL`, else the built-in Hood RPC) for ETH and USDG only, priced by asset, never by address; an RPC on another chain is refused. An unpriced Hood token shows why (`no-market-row`, `stale-mark`, ...). `--chain` shows one chain. Each TEE wallet's DAMM v2 LP positions follow the tokens, valued by Candle at the same marks, and count in the total; one whose pool could not be read is shown as not read and the total says it is partial. A Hyperliquid section follows (each EVM TEE wallet's account, read from Hyperliquid directly, never in the total); `--no-perps` skips it. |
|
|
@@ -454,7 +454,7 @@ all returned token warnings. Missing price impact is explicitly **unavailable**,
|
|
|
454
454
|
JSON result containing the quote; the preview and operation id go to stderr. Launch creation
|
|
455
455
|
has no price impact or token receipt and shows its maximum SOL debit. Without `--buy` it makes
|
|
456
456
|
no first buy; use a separate swap for that buy. With `--buy <amount>` (whole units of the quote
|
|
457
|
-
asset, as `--amount` is for a swap) the same wallet buys at launch: in the launch
|
|
457
|
+
asset, as `--amount` is for a swap) the same wallet buys at launch: as its own leg in one Jito bundle with the launch on
|
|
458
458
|
Solana, as a last leg after the curve and the fee on Hood. Candle reserves the buy against the
|
|
459
459
|
key's spend window before anything is signed. `--buy` needs the server's
|
|
460
460
|
`TEE_LAUNCH_BUYS_ENABLED=1`, which is off by default; while it is off Candle refuses the buy
|
package/dist/index.js
CHANGED
|
@@ -37405,7 +37405,7 @@ var init_convert = __esm(() => {
|
|
|
37405
37405
|
});
|
|
37406
37406
|
|
|
37407
37407
|
// ../mcp/src/version.ts
|
|
37408
|
-
var SERVER_VERSION = "0.10.
|
|
37408
|
+
var SERVER_VERSION = "0.10.22";
|
|
37409
37409
|
|
|
37410
37410
|
// ../mcp/src/update-notice.ts
|
|
37411
37411
|
function newer(a, b) {
|
|
@@ -37954,7 +37954,7 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
37954
37954
|
const wrapped = JSON.stringify({ clientTradeId, resolved, api: posted.body });
|
|
37955
37955
|
return posted.ok ? { text: wrapped } : { text: wrapped, isError: true };
|
|
37956
37956
|
}
|
|
37957
|
-
async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs) {
|
|
37957
|
+
async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs, opts = {}) {
|
|
37958
37958
|
const apiKey = requireApiKey(cfg);
|
|
37959
37959
|
const clientLaunchId = args.clientLaunchId ?? randomUUID3();
|
|
37960
37960
|
const { devBuy, dryRun, buyAmount: _rawBuyAmount, ...imageFields } = args;
|
|
@@ -37968,6 +37968,12 @@ async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs) {
|
|
|
37968
37968
|
return converted.err;
|
|
37969
37969
|
buyAmount = converted.raw;
|
|
37970
37970
|
}
|
|
37971
|
+
const atomic = buyAmount !== undefined && (args.chain ?? "solana") === "solana" && !opts.headlessSeed;
|
|
37972
|
+
if (atomic) {
|
|
37973
|
+
const refused2 = seedCeilingRefusal(args.quoteAsset ?? "sol", args.mode, buyAmount, clientLaunchId);
|
|
37974
|
+
if (refused2)
|
|
37975
|
+
return refused2;
|
|
37976
|
+
}
|
|
37971
37977
|
const image = await resolveLaunchImage(imageFields, imageDir, cfg, doFetch, imageFs);
|
|
37972
37978
|
if ("refused" in image)
|
|
37973
37979
|
return { text: imageRefusalBody(image.refused, { clientLaunchId }), isError: true };
|
|
@@ -37981,16 +37987,20 @@ async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs) {
|
|
|
37981
37987
|
return { text: JSON.stringify({ clientLaunchId, api }), isError: true };
|
|
37982
37988
|
}
|
|
37983
37989
|
const uploaded = uploadedFields(image);
|
|
37990
|
+
const transportFailure = (thrown) => {
|
|
37991
|
+
const failed = transportError("clientLaunchId", clientLaunchId, thrown);
|
|
37992
|
+
return Object.keys(uploaded).length === 0 ? failed : { ...failed, text: JSON.stringify({ ...JSON.parse(failed.text), ...uploaded }) };
|
|
37993
|
+
};
|
|
37994
|
+
if (atomic)
|
|
37995
|
+
return launchAndSeedAtomic({ ...image.args, clientLaunchId }, buyAmount, { clientLaunchId, uploaded, dryRun: dryRun === true, transportFailure }, cfg, doFetch);
|
|
37984
37996
|
const path = dryRun ? "/api/v1/launch/headless/dry-run" : "/api/v1/launch/headless";
|
|
37985
37997
|
const posted = await postJson(`${base(cfg)}${path}`, apiKey, {
|
|
37986
37998
|
...image.args,
|
|
37987
37999
|
clientLaunchId,
|
|
37988
38000
|
...buyAmount !== undefined ? { buyAmount } : {}
|
|
37989
38001
|
}, doFetch);
|
|
37990
|
-
if ("thrown" in posted)
|
|
37991
|
-
|
|
37992
|
-
return Object.keys(uploaded).length === 0 ? failed : { ...failed, text: JSON.stringify({ ...JSON.parse(failed.text), ...uploaded }) };
|
|
37993
|
-
}
|
|
38002
|
+
if ("thrown" in posted)
|
|
38003
|
+
return transportFailure(posted.thrown);
|
|
37994
38004
|
if (!posted.ok) {
|
|
37995
38005
|
return { text: JSON.stringify({ clientLaunchId, ...uploaded, api: posted.body }), isError: true };
|
|
37996
38006
|
}
|
|
@@ -37998,25 +38008,109 @@ async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs) {
|
|
|
37998
38008
|
return { text: JSON.stringify({ clientLaunchId, dryRun: true, ...uploaded, api: posted.body }) };
|
|
37999
38009
|
}
|
|
38000
38010
|
const launch = posted.body;
|
|
38001
|
-
|
|
38002
|
-
|
|
38011
|
+
const { market, note } = await readLaunchedMarket(launch.chain ?? "solana", launch.mint, cfg, doFetch);
|
|
38012
|
+
return {
|
|
38013
|
+
text: JSON.stringify({ clientLaunchId, ...uploaded, launch, market, ...note ? { note } : {} })
|
|
38014
|
+
};
|
|
38015
|
+
}
|
|
38016
|
+
async function readLaunchedMarket(chain2, mint, cfg, doFetch) {
|
|
38017
|
+
const note = "launch confirmed; the follow-up market read failed, read it via candle_get_market";
|
|
38003
38018
|
try {
|
|
38004
|
-
if (
|
|
38019
|
+
if (mint === undefined)
|
|
38005
38020
|
throw new Error("the launch response carried no mint");
|
|
38006
|
-
const marketRes = await doFetch(`${base(cfg)}/api/v1/markets/${encodeURIComponent(
|
|
38021
|
+
const marketRes = await doFetch(`${base(cfg)}/api/v1/markets/${encodeURIComponent(chain2)}/${encodeURIComponent(mint)}`, {
|
|
38007
38022
|
method: "GET",
|
|
38008
38023
|
headers: headers()
|
|
38009
38024
|
});
|
|
38010
|
-
if (marketRes.ok)
|
|
38011
|
-
|
|
38012
|
-
|
|
38013
|
-
note = "launch confirmed; the follow-up market read failed, read it via candle_get_market";
|
|
38014
|
-
}
|
|
38025
|
+
if (!marketRes.ok)
|
|
38026
|
+
return { market: null, note };
|
|
38027
|
+
return { market: JSON.parse(await marketRes.text()).market ?? null };
|
|
38015
38028
|
} catch {
|
|
38016
|
-
|
|
38029
|
+
return { market: null, note };
|
|
38030
|
+
}
|
|
38031
|
+
}
|
|
38032
|
+
function seedCeilingRefusal(quoteAsset, mode, buyAmount, clientLaunchId) {
|
|
38033
|
+
const quote = quoteAsset.toLowerCase();
|
|
38034
|
+
const ceiling = SOLANA_SEED_CEILINGS[quote];
|
|
38035
|
+
if (!ceiling)
|
|
38036
|
+
return;
|
|
38037
|
+
const expCap = ceiling.expRaw;
|
|
38038
|
+
const preset = mode === "exp-a" || mode === "exp-b" || mode === "exp-c";
|
|
38039
|
+
const effective = preset && expCap !== undefined && expCap < ceiling.raw ? expCap : ceiling.raw;
|
|
38040
|
+
if (BigInt(buyAmount) <= effective)
|
|
38041
|
+
return;
|
|
38042
|
+
if (preset && expCap !== undefined) {
|
|
38043
|
+
return {
|
|
38044
|
+
text: JSON.stringify({
|
|
38045
|
+
clientLaunchId,
|
|
38046
|
+
success: false,
|
|
38047
|
+
error: {
|
|
38048
|
+
code: "DEV_BUY_TOO_HIGH",
|
|
38049
|
+
message: `buyAmount exceeds this launch's dev-buy ceiling of ${effective} ${ceiling.unit}: at most ${ceiling.raw} ${ceiling.unit}, or 10% of the threshold when that is lower`,
|
|
38050
|
+
field: "devBuy",
|
|
38051
|
+
retryable: false
|
|
38052
|
+
}
|
|
38053
|
+
}),
|
|
38054
|
+
isError: true
|
|
38055
|
+
};
|
|
38017
38056
|
}
|
|
38018
38057
|
return {
|
|
38019
|
-
text: JSON.stringify({
|
|
38058
|
+
text: JSON.stringify({
|
|
38059
|
+
clientLaunchId,
|
|
38060
|
+
success: false,
|
|
38061
|
+
error: {
|
|
38062
|
+
code: "DEV_BUY_TOO_HIGH",
|
|
38063
|
+
message: `buyAmount exceeds the per-launch dev-buy ceiling of ${effective} ${ceiling.unit} for ${quote} on solana`,
|
|
38064
|
+
field: "devBuy",
|
|
38065
|
+
retryable: false
|
|
38066
|
+
}
|
|
38067
|
+
}),
|
|
38068
|
+
isError: true
|
|
38069
|
+
};
|
|
38070
|
+
}
|
|
38071
|
+
async function launchAndSeedAtomic(launchBody, buyAmount, ctx, cfg, doFetch) {
|
|
38072
|
+
const apiKey = requireApiKey(cfg);
|
|
38073
|
+
const { clientLaunchId, uploaded } = ctx;
|
|
38074
|
+
const failed = (api, extra = {}) => ({
|
|
38075
|
+
text: JSON.stringify({ clientLaunchId, ...uploaded, ...extra, api }),
|
|
38076
|
+
isError: true
|
|
38077
|
+
});
|
|
38078
|
+
const body = { ...launchBody, payer: MAIN_PAYER, firstBuys: [{ payer: MAIN_PAYER, amountRaw: buyAmount }] };
|
|
38079
|
+
const built = await postJson(`${base(cfg)}/api/v1/launch/atomic/${ctx.dryRun ? "dry-run" : "build"}`, apiKey, body, doFetch);
|
|
38080
|
+
if ("thrown" in built)
|
|
38081
|
+
return ctx.transportFailure(built.thrown);
|
|
38082
|
+
if (!built.ok)
|
|
38083
|
+
return failed(built.body);
|
|
38084
|
+
if (ctx.dryRun)
|
|
38085
|
+
return { text: JSON.stringify({ clientLaunchId, dryRun: true, ...uploaded, api: built.body }) };
|
|
38086
|
+
const bundle = built.body;
|
|
38087
|
+
if (typeof bundle.bundleId !== "string" || !Array.isArray(bundle.legs) || bundle.legs.length !== 2 || bundle.legs.some((leg) => leg?.signer !== "server"))
|
|
38088
|
+
return failed(built.body, {
|
|
38089
|
+
success: false,
|
|
38090
|
+
error: {
|
|
38091
|
+
code: "MCP_INVALID_RESPONSE",
|
|
38092
|
+
message: "Candle did not build this launch and its seed as two legs it signs from the embedded wallet; nothing was sent. Retry with the same clientLaunchId to build it again."
|
|
38093
|
+
}
|
|
38094
|
+
});
|
|
38095
|
+
const bundleId = bundle.bundleId;
|
|
38096
|
+
const submitted = await postJson(`${base(cfg)}/api/v1/launch/atomic/submit`, apiKey, { bundleId, signedTxsBase64: [] }, doFetch);
|
|
38097
|
+
if ("thrown" in submitted)
|
|
38098
|
+
return ctx.transportFailure(submitted.thrown);
|
|
38099
|
+
const outcome = submitted.body;
|
|
38100
|
+
if (!submitted.ok || outcome.status !== "landed") {
|
|
38101
|
+
const next = outcome.status === "failed" ? "Neither the launch nor the seed is on chain. Retry with the same clientLaunchId: Candle starts a clean attempt under it." : outcome.status === "timeout" ? "The bundle is unresolved and may still land. Call candle_get_operation with this clientLaunchId and kind launch; never relaunch under a new id, which would be a second token." : undefined;
|
|
38102
|
+
return failed(submitted.body, { bundleId, ...next ? { next } : {} });
|
|
38103
|
+
}
|
|
38104
|
+
const { market, note } = await readLaunchedMarket("solana", outcome.mint, cfg, doFetch);
|
|
38105
|
+
return {
|
|
38106
|
+
text: JSON.stringify({
|
|
38107
|
+
clientLaunchId,
|
|
38108
|
+
bundleId,
|
|
38109
|
+
...uploaded,
|
|
38110
|
+
launch: submitted.body,
|
|
38111
|
+
market,
|
|
38112
|
+
...note ? { note } : {}
|
|
38113
|
+
})
|
|
38020
38114
|
};
|
|
38021
38115
|
}
|
|
38022
38116
|
async function executeSweep(args, cfg, doFetch) {
|
|
@@ -38127,11 +38221,17 @@ async function executionStatus(cfg, doFetch) {
|
|
|
38127
38221
|
...unreadable.length > 0 ? { isError: true } : {}
|
|
38128
38222
|
};
|
|
38129
38223
|
}
|
|
38130
|
-
var SWEEP_BASE_ASSETS;
|
|
38224
|
+
var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS;
|
|
38131
38225
|
var init_orchestrate = __esm(() => {
|
|
38132
38226
|
init_convert();
|
|
38133
38227
|
init_image();
|
|
38134
38228
|
init_update_notice();
|
|
38229
|
+
SOLANA_SEED_CEILINGS = {
|
|
38230
|
+
sol: { raw: 500000000n, unit: "lamports", expRaw: 500000000n },
|
|
38231
|
+
usdc: { raw: 500000000n, unit: "USDC raw units" },
|
|
38232
|
+
cndl: { raw: 2000000000000n, unit: "CNDL raw units", expRaw: 33000000000n }
|
|
38233
|
+
};
|
|
38234
|
+
MAIN_PAYER = { type: "main" };
|
|
38135
38235
|
SWEEP_BASE_ASSETS = {
|
|
38136
38236
|
solana: ["USDC", "CNDL", "SOL"],
|
|
38137
38237
|
hood: ["USDG", "ETH"]
|
|
@@ -42567,17 +42667,19 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
42567
42667
|
});
|
|
42568
42668
|
register("candle_launch_and_seed", {
|
|
42569
42669
|
title: "Launch a token and seed it",
|
|
42570
|
-
description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed
|
|
42670
|
+
description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed, then " + "return the fresh market state and token links in one result. On solana a seed makes this " + (options.headlessSeed === true ? "the seed is a separate first-buy transaction in the same Jito bundle as the launch, so " + "both land or neither does. That works on every plan, Free included, and needs no " + "swap:write. On hood the seed is " : "the atomic launch: the launch and the seed are two transactions Candle signs and sends as " + "one Jito bundle, so both land or neither does. That needs Pro or Max (TIER_REQUIRED " + "otherwise) and swap:write on the key, and the result carries bundleId. On hood the seed is ") + "a best-effort follow-up. A launch with no seed works on every plan, Free included. MOVES " + "REAL FUNDS unless dryRun; dry-run first, which spends nothing. Seeds above the platform " + "dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH): at most 0.5 SOL / 500 USDC / 2,000,000 " + "CNDL, or on an exp- curve 10% of the threshold when that is lower; the server holds the " + "creator's own seed to the effective ceiling, so launch with the largest devBuy it allows " + "and top up with candle_trade. " + CUSTOM_CURVE_RULE + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
42571
42671
|
inputSchema: imageDir ? localLaunchAndSeedShape : launchAndSeedShape
|
|
42572
42672
|
}, async (args) => {
|
|
42573
|
-
const result = await executeLaunchAndSeed(args, getConfig(), doFetch, imageDir
|
|
42673
|
+
const result = await executeLaunchAndSeed(args, getConfig(), doFetch, imageDir, undefined, {
|
|
42674
|
+
headlessSeed: options.headlessSeed === true
|
|
42675
|
+
});
|
|
42574
42676
|
return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
|
|
42575
42677
|
});
|
|
42576
42678
|
const marketRead = createPerpsMarketExecutor(getConfig, env, doFetch);
|
|
42577
42679
|
for (const name of PERPS_MARKET_TOOL_NAMES) {
|
|
42578
42680
|
register(name, {
|
|
42579
42681
|
title: name.replace("candle_perps_", "Hyperliquid "),
|
|
42580
|
-
description: "Read live market data directly from Hyperliquid's public API on this machine. Needs a Candle agent key of any scope, no signer. candle.tradeable is Candle's build gate at read time, not a promise; OI caps may still refuse an order. The hosted connector
|
|
42682
|
+
description: "Read live market data directly from Hyperliquid's public API on this machine. Needs a Candle agent key of any scope, no signer. candle.tradeable is Candle's build gate at read time, not a promise; OI caps may still refuse an order. The hosted connector offers the same four reads at its Read level, read by Candle's server instead of this machine.",
|
|
42581
42683
|
inputSchema: perpsMarketShapes[name],
|
|
42582
42684
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
42583
42685
|
}, async (args) => {
|
|
@@ -42833,7 +42935,7 @@ var init_tools = __esm(() => {
|
|
|
42833
42935
|
minutes: exports_external.number().positive().max(30)
|
|
42834
42936
|
}).strict()
|
|
42835
42937
|
]).optional().describe("Solana only: the graduated pool's fee. { tier: 25 | 50 | 100 } is a flat fee in bps (default 50), or " + "{ startBps, endBps, minutes } is an anti-snipe fall from startBps (at most 2500, at least endBps) to " + "endBps (25, 50 or 100) over minutes (at most 30). Cannot be combined with creatorFeeBps unless the tier is 50. " + "Omit for the standard 50 bps pool"),
|
|
42836
|
-
poolOpen: exports_external.enum(["legacy", "curve_price"]).optional().describe(`Solana preset tiers only. "curve_price" opens the pool at the curve's last price (the whole reserve is ` + "seeded and the remaining base burned); stakerAllocationBps plus the team allocation must fit the tier's limit, " + "which the dry run returns as graduation.maxStakerCarveBps.
|
|
42938
|
+
poolOpen: exports_external.enum(["legacy", "curve_price"]).optional().describe(`Solana preset tiers only. "curve_price" opens the pool at the curve's last price (the whole reserve is ` + "seeded and the remaining base burned); stakerAllocationBps plus the team allocation must fit the tier's limit, " + "which the dry run returns as graduation.maxStakerCarveBps. Omitted defaults to curve_price when those " + "allocations fit, otherwise falls back to legacy without refusal. Set legacy explicitly to opt out. " + "Explicit curve_price is refused when the allocations exceed the limit"),
|
|
42837
42939
|
expPoolShape: exports_external.enum(["floor", "full_range"]).optional().describe('Solana exp- curves only. "full_range" opens the pool across the whole price range. Omit for floor'),
|
|
42838
42940
|
dexVersion: exports_external.string().nullable().optional().describe('"v3" or "v4"; required for hood launches'),
|
|
42839
42941
|
buyAmount: exports_external.union([exports_external.string(), exports_external.number()]).optional().describe("Initial buy, in quote base units (string or number)"),
|
|
@@ -42950,7 +43052,7 @@ var init_tools = __esm(() => {
|
|
|
42950
43052
|
({ buyAmount: _rawBuyAmount, ...seedableLaunchShape } = launchTokenShape);
|
|
42951
43053
|
seedShape = {
|
|
42952
43054
|
clientLaunchId: exports_external.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result."),
|
|
42953
|
-
devBuy: exports_external.string().optional().describe('Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood),
|
|
43055
|
+
devBuy: exports_external.string().optional().describe('Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood), paid from the account's embedded wallet; the tool's description says how " + "each chain sends it. Follows quoteAsset, which defaults to sol on solana and eth on hood. " + "Capped by the platform dev-buy ceiling; for a larger seed, launch then follow with candle_trade.")
|
|
42954
43056
|
};
|
|
42955
43057
|
launchAndSeedShape = { ...seedableLaunchShape, ...seedShape };
|
|
42956
43058
|
localImageShape = {
|
|
@@ -70488,7 +70590,7 @@ init_render();
|
|
|
70488
70590
|
init_secret_store();
|
|
70489
70591
|
|
|
70490
70592
|
// src/version.ts
|
|
70491
|
-
var CLI_VERSION = "0.11.
|
|
70593
|
+
var CLI_VERSION = "0.11.40";
|
|
70492
70594
|
|
|
70493
70595
|
// src/commands/auth.ts
|
|
70494
70596
|
init_keys_embedded_wallet();
|
|
@@ -71211,7 +71313,7 @@ var HELP = {
|
|
|
71211
71313
|
perps: {
|
|
71212
71314
|
group: "Trade",
|
|
71213
71315
|
summary: "Hyperliquid perpetuals: markets, market, book, candles; deposit, setup, withdraw, open, close, cancel, modify, margin, tpsl, schedule-cancel, flatten, reduce-only, orders, positions, fills, funding, leverage, account, risk, watch, thresholds",
|
|
71214
|
-
description: "markets, market, book and candles read directly from Hyperliquid with any Candle agent key and no signer or wallet. Candle supplies only the OI gate config; its build gate is not an execution promise. Trade perpetuals on Hyperliquid's main perp exchange from the EVM TEE wallet bound to this key (scope perps:write, opt-in). A key gets an EVM TEE wallet by promoting one from the vault: candle vault new-key --chain evm, then candle vault promote --from <cold> --to-key <prefix>, and candle tee signer new --key <prefix> on the machine that trades it; candle tee new is Solana only. Candle builds each action within the key's limits; this machine recomputes its hash and checks the action type and Candle's builder before anything is signed, then Candle's relay signs and this machine submits to Hyperliquid. Every plan is eligible for perps, once the deployment has perps enabled. Every plan except Max pays Candle a builder fee on each order (0.1% today); Max pays none. candle plans shows the rate in force. Deposits move SOL, USDC, ETH or USDG onto the Hyperliquid account through Relay; withdrawals move USDC back through Relay to the key's own Solana or Hood TEE wallet (scope perps:withdraw, opt-in, after one vault unlock and a typed confirm). candle pnl --profile <name> shows the profile key's Hyperliquid realized P&L, funding and fees, separately from spot and LP totals. Unrealized perps P&L is shown by candle perps positions and candle portfolio. Account-wide candle pnl and its By agent table cover ledger trades only; a key with only perps activity has no row there. account, positions, risk and watch read Hyperliquid's public API directly from this machine and sign nothing: risk and watch exit 0 (ok), 10 (warn) or 11 (critical), and 1 with a PERPS_* code when no level could be read (unknown), so a script never mistakes \"could not tell\" for ok. A position is warn under its key's warn distance from liquidation (default 15%) or when the cross margin ratio is above the warn ratio (default 0.6); critical under 7% or above 0.8; a position with no Hyperliquid mark is at least warn. risk is one stateless read; watch adds hysteresis (a level clears only past its threshold by the clear band, default 2 points). One watcher at the default 10 s interval spends about 136 of the 1,200 request weight per minute Hyperliquid allows per IP. Funding paid since open is Hyperliquid's sign: positive is paid. Until Candle's server-side monitor exists, nothing warns you unless you poll candle perps risk or run candle perps watch. modify, margin, tpsl, schedule-cancel, flatten and close --pct are built, checked against this machine's own read of Hyperliquid, signed and submitted like every other write; tpsl and flatten are plans of several such steps, confirmed once (or --yes), and a plan that does not finish says so and exits 1 (3 when a signed step's submit did not answer). A key that is reduce-only (reduce-only on) can close, cancel, protect, schedule a cancel and add isolated margin, and nothing that opens or grows exposure; only the owner's device token turns it off, and it cancels no resting order and revokes no signed action.",
|
|
71316
|
+
description: "markets, market, book and candles read directly from Hyperliquid with any Candle agent key and no signer or wallet. Candle supplies only the OI gate config; its build gate is not an execution promise. Trade perpetuals on Hyperliquid's main perp exchange from the EVM TEE wallet bound to this key (scope perps:write, opt-in). A key gets an EVM TEE wallet by promoting one from the vault: candle vault new-key --chain evm, then candle vault promote --from <cold> --to-key <prefix>, and candle tee signer new --key <prefix> on the machine that trades it; candle tee new is Solana only. Candle builds each action within the key's limits; this machine recomputes its hash and checks the action type and Candle's builder before anything is signed, then Candle's relay signs and this machine submits to Hyperliquid. Every plan is eligible for perps, once the deployment has perps enabled. Every plan except Max pays Candle a builder fee on each order (0.1% today); Max pays none. candle plans shows the rate in force. Deposits move SOL, USDC, ETH or USDG onto the Hyperliquid account through Relay; withdrawals move USDC back through Relay to the key's own Solana or Hood TEE wallet (scope perps:withdraw, opt-in, after one vault unlock and a typed confirm). candle pnl --profile <name> shows the profile key's Hyperliquid realized P&L, funding and fees, separately from spot and LP totals. Unrealized perps P&L is shown by candle perps positions and candle portfolio. Account-wide candle pnl and its By agent table cover ledger trades only; a key with only perps activity has no row there. account, positions, risk and watch read Hyperliquid's public API directly from this machine and sign nothing: risk and watch exit 0 (ok), 10 (warn) or 11 (critical), and 1 with a PERPS_* code when no level could be read (unknown), so a script never mistakes \"could not tell\" for ok. A position is warn under its key's warn distance from liquidation (default 15%) or when the cross margin ratio is above the warn ratio (default 0.6); critical under 7% or above 0.8; a position with no Hyperliquid mark is at least warn. risk is one stateless read; watch adds hysteresis (a level clears only past its threshold by the clear band, default 2 points). One watcher at the default 10 s interval spends about 136 of the 1,200 request weight per minute Hyperliquid allows per IP. Funding paid since open is Hyperliquid's sign: positive is paid. Until Candle's server-side monitor exists, nothing warns you unless you poll candle perps risk or run candle perps watch. Candle's server may also watch this wallet; GET /perps/risk reports whether it does. modify, margin, tpsl, schedule-cancel, flatten and close --pct are built, checked against this machine's own read of Hyperliquid, signed and submitted like every other write; tpsl and flatten are plans of several such steps, confirmed once (or --yes), and a plan that does not finish says so and exits 1 (3 when a signed step's submit did not answer). A key that is reduce-only (reduce-only on) can close, cancel, protect, schedule a cancel and add isolated margin, and nothing that opens or grows exposure; only the owner's device token turns it off, and it cancels no resting order and revokes no signed action.",
|
|
71215
71317
|
usage: ["candle perps <subcommand> [flags]"],
|
|
71216
71318
|
rows: [
|
|
71217
71319
|
{
|
|
@@ -71446,9 +71548,10 @@ var HELP = {
|
|
|
71446
71548
|
launch: {
|
|
71447
71549
|
group: "Trade",
|
|
71448
71550
|
summary: "Create a token on Solana or Hood, with an optional first buy",
|
|
71449
|
-
description: "Creates a token from a TEE wallet. Without --buy it makes no first buy, so the launch and the position are two decisions, and the buy is a separate candle swap. --buy <amount> adds a first buy from the same wallet, in whole units of the quote asset (SOL, USDC or CNDL on Solana, ETH on Hood; not USDG), within the dev-buy ceilings: on Solana it is its own transaction, sent with the launch as one Jito bundle (an atomic launch), so the launch and the first buy land together or not at all and the bundle pays a Jito tip; on Hood it is a last leg after the curve and the fee. On Solana it also needs the swap:write scope and a Candle deployment with a Jito block engine. Only configured raw caps and USD limits apply; unset means unlimited and zero still binds. When a USD limit is set, Candle reserves the buy against the key's spend window before anything is signed. --buy needs a server with TEE_LAUNCH_BUYS_ENABLED on, which is off by default; while it is off Candle refuses the buy and nothing is built. Needs the launch:write scope and allowLaunch on the wallet, which only the account owner can turn on (candle wallets allow-launch <wallet>, over the device token; an agent key cannot). The wallet decides the chain: a Hood TEE wallet launches on Hood, needs --dex-version, and signs one leg at a time (the curve, then the fee, then the buy with --buy), each only after the one before it landed. Launching from a TEE wallet needs Pro or Max (TIER_REQUIRED otherwise; candle plans).
|
|
71551
|
+
description: "Creates a token from a TEE wallet. Without --buy it makes no first buy, so the launch and the position are two decisions, and the buy is a separate candle swap. --buy <amount> adds a first buy from the same wallet, in whole units of the quote asset (SOL, USDC or CNDL on Solana, ETH on Hood; not USDG), within the dev-buy ceilings: on Solana it is its own transaction, sent with the launch as one Jito bundle (an atomic launch), so the launch and the first buy land together or not at all and the bundle pays a Jito tip; on Hood it is a last leg after the curve and the fee. On Solana it also needs the swap:write scope and a Candle deployment with a Jito block engine. Only configured raw caps and USD limits apply; unset means unlimited and zero still binds. When a USD limit is set, Candle reserves the buy against the key's spend window before anything is signed. --buy needs a server with TEE_LAUNCH_BUYS_ENABLED on, which is off by default; while it is off Candle refuses the buy and nothing is built. Needs the launch:write scope and allowLaunch on the wallet, which only the account owner can turn on (candle wallets allow-launch <wallet>, over the device token; an agent key cannot). The wallet decides the chain: a Hood TEE wallet launches on Hood, needs --dex-version, and signs one leg at a time (the curve, then the fee, then the buy with --buy), each only after the one before it landed. Launching from a TEE wallet needs Pro or Max (TIER_REQUIRED otherwise; candle plans). --wallet embedded (or main) launches from the account's embedded wallet instead, on Solana, as an atomic launch: it needs --buy, the embedded wallet is the creator and the only first buyer, and Candle signs both legs and sends them as one Jito bundle, so the launch and the first buy land together or not at all. It runs Candle's dry run first, which builds, reserves and records nothing, shows that quote to confirm, and only then builds and sends; the build's first-buy floor must be at least the one shown. --dry-run stops after the preview. It needs Pro or Max, launch:write and swap:write, a key allowed the embedded wallet, and does not depend on TEE_LAUNCH_BUYS_ENABLED. Without a first buy, every plan can launch from the embedded wallet through the MCP tool candle_launch_token or the SDK's launch(). The image is one of two flags, never both: --image-url <https-url> for one already hosted, or --image <path> for a PNG, JPEG, GIF or WebP file on this machine, at most 10MB. With --image the file is uploaded to Candle first and the launch is made with the URL that comes back, which --json reports as imageUrl. The upload happens before the confirm prompt and cannot be undone: the file becomes public and permanent even if you then decline or the launch fails. Re-running with the same --client-trade-id resumes the launch without uploading again. --image-url takes only an https:// URL. --banner <file|URL> adds a wide banner: an https URL is passed through; a local file uses the same publication notice and 10MB limit, uploaded with kind=banner so it is never cropped. Banners must be wider than 1.5:1. The result reports bannerUrl, and an atomic retry reuses the saved URL. --mode picks the curve: open or exclusive (the default tier for the pair), test-open or test-exclusive where the server has ENABLE_TEST_CURVES on, or on Solana with sol or cndl the experimental curves exp-a, exp-b, exp-c and exp-custom. exp-custom takes your own curve: --curve-v <V> (the virtual quote it starts with) and --curve-g <G> (what it raises before graduating), in whole units of the quote asset, within limits Candle checks; it works only where the server has EXP_CUSTOM_CURVES on. --creator-fee-bps 100 or 200 adds an extra creator fee on an exp- mode, paid to the creator in full. On an exp- curve the first buy is at most 0.5 SOL (2,000,000 CNDL), or 10% of the threshold when that is lower. --dry-run sends the launch's body to Candle's dry run and prints the preview (the curve's terms, the first buy's share, the fees, how the pool opens at graduation and the dev-buy ceiling) without building, signing or recording anything; the id stays unused. With --image it still uploads the file, which is public and permanent.",
|
|
71450
71552
|
usage: [
|
|
71451
|
-
"candle launch --name <name> --symbol <symbol> --image <path>|--image-url <url> --wallet <tee> [--banner <file|URL>] [--buy <amount>] [--quote-asset <asset>] [--dex-version v3|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0|100|200] [--pool-fee <25|50|100>] [--pool-open <shape>] [--exp-pool-shape <shape>] [--dry-run]"
|
|
71553
|
+
"candle launch --name <name> --symbol <symbol> --image <path>|--image-url <url> --wallet <tee> [--banner <file|URL>] [--buy <amount>] [--quote-asset <asset>] [--dex-version v3|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0|100|200] [--pool-fee <25|50|100>] [--pool-open <shape>] [--exp-pool-shape <shape>] [--dry-run]",
|
|
71554
|
+
"candle launch --name <name> --symbol <symbol> --image <path>|--image-url <url> --wallet embedded --buy <amount> [--banner <file|URL>] [--quote-asset <asset>] [--dry-run]"
|
|
71452
71555
|
],
|
|
71453
71556
|
rows: [],
|
|
71454
71557
|
flags: [
|
|
@@ -71463,7 +71566,10 @@ var HELP = {
|
|
|
71463
71566
|
invocation: "--banner <file|URL>",
|
|
71464
71567
|
description: "A wide banner from a local file or https URL; files become public and permanent, without cropping"
|
|
71465
71568
|
},
|
|
71466
|
-
{
|
|
71569
|
+
{
|
|
71570
|
+
invocation: "--wallet <tee>|embedded",
|
|
71571
|
+
description: "The TEE wallet that creates it, or embedded (main) for the account's embedded wallet, with --buy, as an atomic launch"
|
|
71572
|
+
},
|
|
71467
71573
|
{
|
|
71468
71574
|
invocation: "--buy <amount>",
|
|
71469
71575
|
description: "A first buy from the same wallet, in whole units of the quote asset; needs TEE_LAUNCH_BUYS_ENABLED on the server"
|
|
@@ -71483,7 +71589,7 @@ var HELP = {
|
|
|
71483
71589
|
},
|
|
71484
71590
|
{
|
|
71485
71591
|
invocation: "--pool-open legacy|curve_price",
|
|
71486
|
-
description: "Solana preset tiers: curve_price opens the pool at the curve's last price and burns the rest
|
|
71592
|
+
description: "Solana preset tiers: curve_price opens the pool at the curve's last price and burns the rest. Omitted defaults to curve_price when stakerAllocationBps plus the team allocation fit the tier's limit (dry-run graduation.maxStakerCarveBps), otherwise falls back to legacy without refusal. Use --pool-open legacy to opt out. Explicit curve_price is refused above the limit"
|
|
71487
71593
|
},
|
|
71488
71594
|
{
|
|
71489
71595
|
invocation: "--exp-pool-shape floor|full_range",
|
|
@@ -71511,6 +71617,7 @@ var HELP = {
|
|
|
71511
71617
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet AgentOne",
|
|
71512
71618
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet HoodAgent --dex-version v4",
|
|
71513
71619
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet AgentOne --buy 0.1",
|
|
71620
|
+
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet embedded --buy 0.1 --dry-run",
|
|
71514
71621
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet AgentOne --mode exp-custom --curve-v 4 --curve-g 20 --creator-fee-bps 100 --dry-run"
|
|
71515
71622
|
],
|
|
71516
71623
|
env: ENV_API
|
|
@@ -84802,7 +84909,9 @@ function poolRequestFields(flags) {
|
|
|
84802
84909
|
|
|
84803
84910
|
// src/commands/launch.ts
|
|
84804
84911
|
init_swap();
|
|
84805
|
-
var USAGE6 = "Usage: candle launch --name <name> --symbol <symbol> --image <path>|--image-url <https-url> --wallet <tee
|
|
84912
|
+
var USAGE6 = "Usage: candle launch --name <name> --symbol <symbol> --image <path>|--image-url <https-url> --wallet <tee>|embedded [--banner <file|URL>] [--buy <amount>] [--client-trade-id <id>] [--quote-asset sol|usdc|cndl|eth|usdg] [--dex-version v3|v4] [--mode <mode>] [--curve-v <V> --curve-g <G>] [--creator-fee-bps 0|100|200] [--pool-fee <25|50|100> [--pool-fee-from <bps> --pool-fee-minutes <min>]] [--pool-open legacy|curve_price] [--exp-pool-shape floor|full_range] [--dry-run] [--rpc-url <url>] [--yes]. A Hood TEE wallet launches on Hood and needs --dex-version.";
|
|
84913
|
+
var EMBEDDED_WALLET_NAMES = new Set(["embedded", "main"]);
|
|
84914
|
+
var EMBEDDED_USAGE = "--wallet embedded launches on Solana as an atomic launch: it needs --buy <amount>, takes --quote-asset sol, usdc or cndl, and takes no --dex-version. To launch from the embedded wallet with no first buy, use the MCP tool candle_launch_token or POST /api/v1/launch/headless.";
|
|
84806
84915
|
var QUOTE_CHAIN = {
|
|
84807
84916
|
sol: "solana",
|
|
84808
84917
|
usdc: "solana",
|
|
@@ -84920,7 +85029,15 @@ async function launch(args, ctx) {
|
|
|
84920
85029
|
}
|
|
84921
85030
|
const flags = parsed.values;
|
|
84922
85031
|
const id = flags["--client-trade-id"] ?? `launch-${randomUUID5()}`;
|
|
85032
|
+
const embedded = EMBEDDED_WALLET_NAMES.has(flags["--wallet"] ?? "");
|
|
84923
85033
|
const dryRun = parsed.booleans.has("--dry-run");
|
|
85034
|
+
if (embedded) {
|
|
85035
|
+
const quote = flags["--quote-asset"]?.toLowerCase();
|
|
85036
|
+
if (flags["--buy"] === undefined || flags["--dex-version"] !== undefined || quote && QUOTE_CHAIN[quote] !== "solana") {
|
|
85037
|
+
writeUsageFailure(ctx.deps, `${EMBEDDED_USAGE} ${USAGE6}`, ctx.json);
|
|
85038
|
+
return 2;
|
|
85039
|
+
}
|
|
85040
|
+
}
|
|
84924
85041
|
const curveProblem = curveFlagProblem(flags);
|
|
84925
85042
|
if (curveProblem) {
|
|
84926
85043
|
writeUsageFailure(ctx.deps, `${curveProblem} ${USAGE6}`, ctx.json);
|
|
@@ -84991,6 +85108,16 @@ async function launch(args, ctx) {
|
|
|
84991
85108
|
return printTradingResult(ctx, prior);
|
|
84992
85109
|
}
|
|
84993
85110
|
}
|
|
85111
|
+
if (embedded)
|
|
85112
|
+
return await embeddedLaunch(ctx, key, {
|
|
85113
|
+
flags,
|
|
85114
|
+
id,
|
|
85115
|
+
image,
|
|
85116
|
+
banner,
|
|
85117
|
+
retry,
|
|
85118
|
+
yes: parsed.booleans.has("--yes"),
|
|
85119
|
+
dryRun
|
|
85120
|
+
});
|
|
84994
85121
|
const wallet3 = await tradingWallet(ctx, key, flags["--wallet"], "launch:write", hinted ?? "any");
|
|
84995
85122
|
const chain2 = wallet3.chain === "evm" ? "hood" : "solana";
|
|
84996
85123
|
if (chain2 === "hood" && flags["--dex-version"] === undefined)
|
|
@@ -85000,38 +85127,10 @@ async function launch(args, ctx) {
|
|
|
85000
85127
|
if (atomic)
|
|
85001
85128
|
atomicPreflight(ctx, flags, wallet3, buy);
|
|
85002
85129
|
const solana = chain2 === "solana" && !atomic && !dryRun ? await tradingSolanaClient(ctx, flags["--rpc-url"]) : undefined;
|
|
85003
|
-
|
|
85004
|
-
if (
|
|
85005
|
-
|
|
85006
|
-
|
|
85007
|
-
ctx.deps.stderr.write(uploadNotice(image));
|
|
85008
|
-
try {
|
|
85009
|
-
imageUrl = await uploadImage(ctx, key, image);
|
|
85010
|
-
} catch (error) {
|
|
85011
|
-
ctx.deps.stderr.write(`The launch was not started, and its id ${id} is unused.
|
|
85012
|
-
`);
|
|
85013
|
-
return tradingFailure(ctx, error);
|
|
85014
|
-
}
|
|
85015
|
-
ctx.deps.stderr.write(`Image: ${safeText(imageUrl)}
|
|
85016
|
-
`);
|
|
85017
|
-
}
|
|
85018
|
-
let bannerUrl = retry ? retry.bannerUrl : flags["--banner"]?.startsWith("https://") ? flags["--banner"] : undefined;
|
|
85019
|
-
if (banner && !retry) {
|
|
85020
|
-
ctx.deps.stderr.write(uploadNotice(banner));
|
|
85021
|
-
try {
|
|
85022
|
-
bannerUrl = await uploadImage(ctx, key, banner, "banner");
|
|
85023
|
-
} catch (error) {
|
|
85024
|
-
ctx.deps.stderr.write(`The launch was not started, and its id ${id} is unused.
|
|
85025
|
-
`);
|
|
85026
|
-
return tradingFailure(ctx, error);
|
|
85027
|
-
}
|
|
85028
|
-
ctx.deps.stderr.write(`Banner: ${safeText(bannerUrl)}
|
|
85029
|
-
`);
|
|
85030
|
-
}
|
|
85031
|
-
const used = {
|
|
85032
|
-
...image ? { imageUrl: safeText(imageUrl) } : {},
|
|
85033
|
-
...bannerUrl ? { bannerUrl: safeText(bannerUrl) } : {}
|
|
85034
|
-
};
|
|
85130
|
+
const uploaded = await uploadLaunchImages(ctx, key, { flags, id, image, banner, retry });
|
|
85131
|
+
if (typeof uploaded === "number")
|
|
85132
|
+
return uploaded;
|
|
85133
|
+
const { imageUrl, bannerUrl, used } = uploaded;
|
|
85035
85134
|
if (dryRun)
|
|
85036
85135
|
return await dryRunLaunch(ctx, key, { flags, id, wallet: wallet3, chain: chain2, atomic, buy, imageUrl, bannerUrl, used });
|
|
85037
85136
|
if (atomic)
|
|
@@ -85108,6 +85207,42 @@ async function launch(args, ctx) {
|
|
|
85108
85207
|
return tradingFailure(ctx, error, dryRun ? undefined : id);
|
|
85109
85208
|
}
|
|
85110
85209
|
}
|
|
85210
|
+
async function uploadLaunchImages(ctx, key, args) {
|
|
85211
|
+
const { flags, id, image, banner, retry } = args;
|
|
85212
|
+
let imageUrl = flags["--image-url"] ?? "";
|
|
85213
|
+
if (image && retry?.imageUrl)
|
|
85214
|
+
imageUrl = retry.imageUrl;
|
|
85215
|
+
else if (image) {
|
|
85216
|
+
ctx.deps.stderr.write(uploadNotice(image));
|
|
85217
|
+
try {
|
|
85218
|
+
imageUrl = await uploadImage(ctx, key, image);
|
|
85219
|
+
} catch (error) {
|
|
85220
|
+
ctx.deps.stderr.write(`The launch was not started, and its id ${id} is unused.
|
|
85221
|
+
`);
|
|
85222
|
+
return tradingFailure(ctx, error);
|
|
85223
|
+
}
|
|
85224
|
+
ctx.deps.stderr.write(`Image: ${safeText(imageUrl)}
|
|
85225
|
+
`);
|
|
85226
|
+
}
|
|
85227
|
+
let bannerUrl = retry ? retry.bannerUrl : flags["--banner"]?.startsWith("https://") ? flags["--banner"] : undefined;
|
|
85228
|
+
if (banner && !retry) {
|
|
85229
|
+
ctx.deps.stderr.write(uploadNotice(banner));
|
|
85230
|
+
try {
|
|
85231
|
+
bannerUrl = await uploadImage(ctx, key, banner, "banner");
|
|
85232
|
+
} catch (error) {
|
|
85233
|
+
ctx.deps.stderr.write(`The launch was not started, and its id ${id} is unused.
|
|
85234
|
+
`);
|
|
85235
|
+
return tradingFailure(ctx, error);
|
|
85236
|
+
}
|
|
85237
|
+
ctx.deps.stderr.write(`Banner: ${safeText(bannerUrl)}
|
|
85238
|
+
`);
|
|
85239
|
+
}
|
|
85240
|
+
const used = {
|
|
85241
|
+
...image ? { imageUrl: safeText(imageUrl) } : {},
|
|
85242
|
+
...bannerUrl ? { bannerUrl: safeText(bannerUrl) } : {}
|
|
85243
|
+
};
|
|
85244
|
+
return { imageUrl, ...bannerUrl ? { bannerUrl } : {}, used };
|
|
85245
|
+
}
|
|
85111
85246
|
function launchFields(args) {
|
|
85112
85247
|
const { flags } = args;
|
|
85113
85248
|
return {
|
|
@@ -85237,6 +85372,11 @@ function expThresholdRaw(flags, quoteAsset) {
|
|
|
85237
85372
|
return mode === "exp-a" || mode === "exp-b" || mode === "exp-c" ? EXP_PRESET_THRESHOLD_RAW[quoteAsset] : undefined;
|
|
85238
85373
|
}
|
|
85239
85374
|
function atomicPreflight(ctx, flags, wallet3, buy) {
|
|
85375
|
+
firstBuyCeiling(ctx, flags, buy);
|
|
85376
|
+
if (wallet3.scopes !== undefined && !wallet3.scopes.includes("swap:write"))
|
|
85377
|
+
throw swapScopeRefusal();
|
|
85378
|
+
}
|
|
85379
|
+
function firstBuyCeiling(ctx, flags, buy) {
|
|
85240
85380
|
const quoteAsset = (flags["--quote-asset"] ?? "sol").toLowerCase();
|
|
85241
85381
|
const { ceiling, unit } = devBuyCeiling(ctx.deps.env, quoteAsset);
|
|
85242
85382
|
const threshold = expThresholdRaw(flags, quoteAsset);
|
|
@@ -85251,8 +85391,6 @@ function atomicPreflight(ctx, flags, wallet3, buy) {
|
|
|
85251
85391
|
}
|
|
85252
85392
|
if (BigInt(buy.raw) > ceiling)
|
|
85253
85393
|
throw new TradingError("DEV_BUY_TOO_HIGH", `buyAmount exceeds the per-launch dev-buy ceiling of ${ceiling} ${unit} for ${quoteAsset} on solana`, { details: { field: "buyAmount" } });
|
|
85254
|
-
if (wallet3.scopes !== undefined && !wallet3.scopes.includes("swap:write"))
|
|
85255
|
-
throw swapScopeRefusal();
|
|
85256
85394
|
}
|
|
85257
85395
|
function swapScopeRefusal() {
|
|
85258
85396
|
return new TradingError("SCOPE_MISSING", "This key needs swap:write for a first buy on a TEE launch: the buy is its own leg in an atomic launch. Nothing was built or signed.", {
|
|
@@ -85363,28 +85501,33 @@ async function atomicLaunch(ctx, key, args) {
|
|
|
85363
85501
|
await saveOperationBundle(ctx, key, id, bundle);
|
|
85364
85502
|
ctx.deps.stderr.write(`Bundle: ${safeText(built.bundleId)} (launch ${signatures[0]}, first buy ${signatures[1]})
|
|
85365
85503
|
`);
|
|
85504
|
+
return await submitBundle(ctx, key, { id, bundle, signed, quote, used: args.used });
|
|
85505
|
+
}
|
|
85506
|
+
async function submitBundle(ctx, key, args) {
|
|
85507
|
+
const { id, bundle, signed, quote } = args;
|
|
85508
|
+
const { bundleId, signatures } = bundle;
|
|
85366
85509
|
const submitted = await apiRequest("/api/v1/launch/atomic/submit", {
|
|
85367
85510
|
apiUrl: ctx.apiUrl,
|
|
85368
85511
|
credentials: { apiKey: key },
|
|
85369
85512
|
auth: "key",
|
|
85370
85513
|
method: "POST",
|
|
85371
|
-
body: { bundleId
|
|
85514
|
+
body: { bundleId, signedTxsBase64: signed },
|
|
85372
85515
|
fetch: ctx.deps.fetch,
|
|
85373
85516
|
env: ctx.deps.env
|
|
85374
85517
|
});
|
|
85375
85518
|
const outcome = atomicOutcomeSchema.safeParse(submitted.ok ? submitted.body : submitted.raw);
|
|
85376
|
-
const details = { bundleId
|
|
85377
|
-
if (!outcome.success || outcome.data.bundleId !==
|
|
85519
|
+
const details = { bundleId, signatures };
|
|
85520
|
+
if (!outcome.success || outcome.data.bundleId !== bundleId) {
|
|
85378
85521
|
const refused2 = !submitted.ok && submitted.status >= 400 && submitted.status < 500 && submitted.status !== 409;
|
|
85379
85522
|
if (refused2)
|
|
85380
85523
|
await saveOperationBundle(ctx, key, id, { ...bundle, outcome: "refused" });
|
|
85381
|
-
throw new TradingError(submitted.ok ? "INVALID_RESPONSE" : submitted.code ?? "REQUEST_FAILED", refused2 ? `Candle refused bundle ${safeText(
|
|
85524
|
+
throw new TradingError(submitted.ok ? "INVALID_RESPONSE" : submitted.code ?? "REQUEST_FAILED", refused2 ? `Candle refused bundle ${safeText(bundleId)} and sent nothing: ${safeText(submitted.ok ? "" : submitted.message)}. Re-run with the same --client-trade-id ${id} to build it again.` : `Bundle ${safeText(bundleId)} may have been sent, and its outcome is unknown${submitted.ok ? "" : `: ${safeText(submitted.message)}`}. Re-run with the same --client-trade-id ${id} to read it; nothing is re-signed or re-sent. Never relaunch under a new id: that would be a second token.`, { ...refused2 ? {} : { exitCode: 3 }, details });
|
|
85382
85525
|
}
|
|
85383
85526
|
await saveOperationBundle(ctx, key, id, { ...bundle, outcome: outcome.data.status });
|
|
85384
85527
|
if (outcome.data.status === "failed")
|
|
85385
|
-
throw new TradingError("BUNDLE_FAILED", `Bundle ${safeText(
|
|
85528
|
+
throw new TradingError("BUNDLE_FAILED", `Bundle ${safeText(bundleId)} did not land: neither the launch nor its first buy is on chain. Retry with the same --client-trade-id ${id}; Candle starts a clean attempt under it.`, { details });
|
|
85386
85529
|
if (outcome.data.status === "timeout")
|
|
85387
|
-
throw new TradingError("BUNDLE_UNRESOLVED", `Bundle ${safeText(
|
|
85530
|
+
throw new TradingError("BUNDLE_UNRESOLVED", `Bundle ${safeText(bundleId)} is unresolved and may still land. Re-run with the same --client-trade-id ${id} to read its outcome; nothing is re-signed or re-sent. Never relaunch under a new id: that would be a second token.`, { exitCode: 3, details });
|
|
85388
85531
|
const [launchSignature, buySignature] = outcome.data.signatures;
|
|
85389
85532
|
ctx.deps.stderr.write(`Launched ${safeText(outcome.data.mint)}. Launch signature: ${safeText(launchSignature)}. First buy signature: ${safeText(buySignature)}.
|
|
85390
85533
|
`);
|
|
@@ -85408,6 +85551,127 @@ function resumeAtomicLaunch(ctx, id, local, prior) {
|
|
|
85408
85551
|
return printTradingResult(ctx, { ...prior, ...saved });
|
|
85409
85552
|
throw new TradingError("OPERATION_IN_FLIGHT", `Launch ${id}'s bundle ${safeText(local.bundleId)} is unresolved (the launch job is ${safeText(prior.job.status)}) and may still land. Nothing was re-signed or re-sent. Re-run with the same --client-trade-id to read it again; never relaunch under a new id, which would be a second token.`, { exitCode: 3, details: { ...saved, job: prior.job } });
|
|
85410
85553
|
}
|
|
85554
|
+
var EMBEDDED_PAYER = { type: "main" };
|
|
85555
|
+
var atomicDryRunSchema = exports_external.object({
|
|
85556
|
+
launchWallet: exports_external.string(),
|
|
85557
|
+
launchSigner: exports_external.enum(["server", "client"]),
|
|
85558
|
+
fills: exports_external.array(exports_external.object({
|
|
85559
|
+
index: exports_external.number().int(),
|
|
85560
|
+
signer: exports_external.enum(["server", "client"]),
|
|
85561
|
+
wallet: exports_external.string(),
|
|
85562
|
+
amountRaw: exports_external.string().regex(/^\d+$/),
|
|
85563
|
+
feeRaw: exports_external.string().regex(/^\d+$/),
|
|
85564
|
+
expectedFill: exports_external.object({
|
|
85565
|
+
amountOutRaw: exports_external.string().regex(/^\d+$/),
|
|
85566
|
+
minAmountOutRaw: exports_external.string().regex(/^\d+$/)
|
|
85567
|
+
}),
|
|
85568
|
+
supplyPct: exports_external.number()
|
|
85569
|
+
}).passthrough()),
|
|
85570
|
+
graduatesInBundle: exports_external.boolean().optional()
|
|
85571
|
+
}).passthrough();
|
|
85572
|
+
async function embeddedLaunchWallet(ctx, key) {
|
|
85573
|
+
const discovered = embeddedSchema.parse(await request(ctx, key, "/api/v1/agent/wallets/embedded"));
|
|
85574
|
+
const address = discovered.wallets?.solana?.address;
|
|
85575
|
+
if (!address)
|
|
85576
|
+
throw new TradingError("PAYER_REQUIRED", "This account has no Solana embedded wallet. Create one in the app, or launch from a Solana TEE wallet with --wallet <tee>. Nothing was built.");
|
|
85577
|
+
if (discovered.embeddedWalletPermission === "denied")
|
|
85578
|
+
throw new TradingError("EMBEDDED_WALLET_NOT_PERMITTED", `This key may not use the account's embedded wallet (${address}). Nothing was built.`, {
|
|
85579
|
+
suggestion: "Have the owner allow it: candle keys update <prefix> --embedded-wallet allow (device token), or in the web key manager. Or launch from a TEE wallet bound to this key with --wallet <tee>."
|
|
85580
|
+
});
|
|
85581
|
+
return address;
|
|
85582
|
+
}
|
|
85583
|
+
function embeddedRefusal(error) {
|
|
85584
|
+
if (!(error instanceof TradingError))
|
|
85585
|
+
return error;
|
|
85586
|
+
if (error.code === "SCOPE_MISSING")
|
|
85587
|
+
return new TradingError("SCOPE_MISSING", "This key needs swap:write as well as launch:write: the first buy is its own leg in the atomic launch. Nothing was built or signed.", { suggestion: "Add swap:write to the key, then run the same command again." });
|
|
85588
|
+
if (error.code === "VALIDATION_FAILED" && error.message.includes("configured Jito block engine"))
|
|
85589
|
+
return new TradingError("VALIDATION_FAILED", "An embedded-wallet launch goes to Candle as one Jito bundle, and this Candle deployment has no Jito block engine configured. Nothing was built or signed.");
|
|
85590
|
+
return error;
|
|
85591
|
+
}
|
|
85592
|
+
async function embeddedLaunch(ctx, key, args) {
|
|
85593
|
+
const { flags, id } = args;
|
|
85594
|
+
const buy = launchBuy(flags, "solana");
|
|
85595
|
+
firstBuyCeiling(ctx, flags, buy);
|
|
85596
|
+
const wallet3 = await embeddedLaunchWallet(ctx, key);
|
|
85597
|
+
const uploaded = await uploadLaunchImages(ctx, key, args);
|
|
85598
|
+
if (typeof uploaded === "number")
|
|
85599
|
+
return uploaded;
|
|
85600
|
+
const { imageUrl, bannerUrl, used } = uploaded;
|
|
85601
|
+
const body = {
|
|
85602
|
+
clientLaunchId: id,
|
|
85603
|
+
chain: "solana",
|
|
85604
|
+
name: flags["--name"],
|
|
85605
|
+
symbol: flags["--symbol"],
|
|
85606
|
+
imageUrl,
|
|
85607
|
+
...bannerUrl ? { bannerUrl } : {},
|
|
85608
|
+
...flags["--description"] ? { description: flags["--description"] } : {},
|
|
85609
|
+
...flags["--quote-asset"] ? { quoteAsset: flags["--quote-asset"] } : {},
|
|
85610
|
+
...flags["--mode"] ? { mode: flags["--mode"] } : {},
|
|
85611
|
+
...launchCurveFields(flags),
|
|
85612
|
+
...poolRequestFields(flags),
|
|
85613
|
+
payer: EMBEDDED_PAYER,
|
|
85614
|
+
firstBuys: [{ payer: EMBEDDED_PAYER, amountRaw: buy.raw }]
|
|
85615
|
+
};
|
|
85616
|
+
const answer2 = await request(ctx, key, "/api/v1/launch/atomic/dry-run", body).catch((error) => {
|
|
85617
|
+
throw embeddedRefusal(error);
|
|
85618
|
+
});
|
|
85619
|
+
const preview = atomicDryRunSchema.safeParse(answer2);
|
|
85620
|
+
const fill = preview.success ? preview.data.fills[0] : undefined;
|
|
85621
|
+
if (!preview.success || !fill || preview.data.fills.length !== 1 || preview.data.launchWallet !== wallet3 || preview.data.launchSigner !== "server" || fill.wallet !== wallet3 || fill.signer !== "server" || fill.amountRaw !== buy.raw)
|
|
85622
|
+
throw new TradingError("INVALID_RESPONSE", "Candle's dry run does not read as this launch and its first buy from the embedded wallet; nothing was built.");
|
|
85623
|
+
const quoteAsset = (flags["--quote-asset"] ?? "sol").toUpperCase();
|
|
85624
|
+
const decimals = BASES[quoteAsset]?.decimals ?? 9;
|
|
85625
|
+
const quote = {
|
|
85626
|
+
intent: `Launch ${flags["--name"]} (${flags["--symbol"]}) with a first buy of ${buy.display} from the embedded wallet, as one Jito bundle`,
|
|
85627
|
+
wallet: wallet3,
|
|
85628
|
+
venue: "curve launch (atomic bundle, signed by Candle)",
|
|
85629
|
+
priceImpactPct: null,
|
|
85630
|
+
candleFee: fill.feeRaw !== "0" ? `${decimalAmount(fill.feeRaw, decimals)} ${quoteAsset} (the tier fee on the first buy, in its leg)` : "none on this first buy",
|
|
85631
|
+
minimumReceived: `${fill.expectedFill.minAmountOutRaw} raw units of the new token (the first buy's floor; ${fill.supplyPct}% of the supply expected)`,
|
|
85632
|
+
tokenRisks: []
|
|
85633
|
+
};
|
|
85634
|
+
if (args.dryRun) {
|
|
85635
|
+
ctx.deps.stderr.write(`Dry run: nothing was built, signed or recorded, and launch id ${id} is unused.
|
|
85636
|
+
`);
|
|
85637
|
+
return printTradingResult(ctx, { ...answer2, clientTradeId: id, kind: "launch", quote, ...used });
|
|
85638
|
+
}
|
|
85639
|
+
ctx.deps.stderr.write(`Launch creates the token, then spends ${buy.display} on a first buy from the embedded wallet as its own transaction. Both go to Candle as one Jito bundle: the launch and the first buy land together or not at all. Candle signs both, and the bundle pays a Jito tip from this wallet inside the launch transaction, beside the launch's rent and network fees. Price impact does not apply to creation.
|
|
85640
|
+
`);
|
|
85641
|
+
if (preview.data.graduatesInBundle)
|
|
85642
|
+
ctx.deps.stderr.write(`This first buy alone takes the new curve to graduation inside the bundle.
|
|
85643
|
+
`);
|
|
85644
|
+
if (!await confirmQuote(ctx, quote, args.yes))
|
|
85645
|
+
return printTradingResult(ctx, {
|
|
85646
|
+
success: true,
|
|
85647
|
+
status: "cancelled",
|
|
85648
|
+
clientTradeId: id,
|
|
85649
|
+
kind: "launch",
|
|
85650
|
+
quote,
|
|
85651
|
+
...used
|
|
85652
|
+
});
|
|
85653
|
+
if (!args.retry && !await claimOperation(ctx, key, id, "launch"))
|
|
85654
|
+
throw new TradingError("OPERATION_ALREADY_STARTED", "This machine already started this launch id; no write was resent.");
|
|
85655
|
+
ctx.deps.stderr.write(`Operation: ${id}
|
|
85656
|
+
`);
|
|
85657
|
+
const built = atomicBuildSchema.parse(await request(ctx, key, "/api/v1/launch/atomic/build", body).catch((error) => {
|
|
85658
|
+
throw embeddedRefusal(error);
|
|
85659
|
+
}));
|
|
85660
|
+
const bundle = { bundleId: built.bundleId, signatures: [], imageUrl, ...bannerUrl ? { bannerUrl } : {} };
|
|
85661
|
+
await saveOperationBundle(ctx, key, id, { ...bundle, outcome: "unsigned" });
|
|
85662
|
+
const [launchLeg, buyLeg] = built.legs;
|
|
85663
|
+
if (built.legs.length !== 2 || launchLeg?.role !== "launch" || buyLeg?.role !== "buy" || built.legs.some((leg, index) => leg.index !== index || leg.signer !== "server"))
|
|
85664
|
+
throw new TradingError("INVALID_RESPONSE", `Candle did not build bundle ${safeText(built.bundleId)} as this launch and its first buy, both from the embedded wallet; nothing was sent. Re-run with the same --client-trade-id ${id} to build it again.`);
|
|
85665
|
+
const floor = buyLeg.expectedFill?.minAmountOutRaw;
|
|
85666
|
+
if (floor === undefined || BigInt(floor) < BigInt(fill.expectedFill.minAmountOutRaw))
|
|
85667
|
+
throw new TradingError("QUOTE_CHANGED", `Bundle ${safeText(built.bundleId)}'s first-buy floor (${safeText(floor ?? "none")} raw units) is below the ${fill.expectedFill.minAmountOutRaw} you confirmed; nothing was sent. Re-run with the same --client-trade-id ${id} to preview it again.`);
|
|
85668
|
+
if (!Number.isFinite(built.expiresAt) || built.expiresAt <= ctx.deps.now())
|
|
85669
|
+
throw new TradingError("QUOTE_EXPIRED", "The launch build expired before it was sent.");
|
|
85670
|
+
await saveOperationBundle(ctx, key, id, bundle);
|
|
85671
|
+
ctx.deps.stderr.write(`Bundle: ${safeText(built.bundleId)} (Candle signs the launch and the first buy)
|
|
85672
|
+
`);
|
|
85673
|
+
return await submitBundle(ctx, key, { id, bundle, signed: [], quote, used });
|
|
85674
|
+
}
|
|
85411
85675
|
async function hoodLaunch(ctx, key, args) {
|
|
85412
85676
|
const { flags, id, wallet: wallet3, buy } = args;
|
|
85413
85677
|
if (Object.keys(poolRequestFields(flags)).length)
|
|
@@ -95137,7 +95401,6 @@ function stepReporter(write, tty, ascii = false) {
|
|
|
95137
95401
|
`);
|
|
95138
95402
|
return;
|
|
95139
95403
|
}
|
|
95140
|
-
write(`${FRAMES[0]} ${label}`);
|
|
95141
95404
|
frame = 1;
|
|
95142
95405
|
const interrupt = (signal) => {
|
|
95143
95406
|
clearSpinner();
|
|
@@ -95158,6 +95421,7 @@ function stepReporter(write, tty, ascii = false) {
|
|
|
95158
95421
|
}, INTERVAL_MS3);
|
|
95159
95422
|
if (typeof timer === "object" && "unref" in timer)
|
|
95160
95423
|
timer.unref();
|
|
95424
|
+
write(`${FRAMES[0]} ${label}`);
|
|
95161
95425
|
},
|
|
95162
95426
|
done(label) {
|
|
95163
95427
|
clearSpinner();
|
package/package.json
CHANGED