@cabaltrading/cli 0.4.1 → 0.4.3

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/dist/index.js CHANGED
@@ -3389,6 +3389,7 @@ var init_dbc_configs = __esm(() => {
3389
3389
  name: z20.string().min(1).max(100),
3390
3390
  description: z20.string().max(500).optional(),
3391
3391
  config: z20.record(z20.string(), z20.unknown()).optional(),
3392
+ feeClaimer: z20.string().min(32).max(44).optional(),
3392
3393
  isProtocolApproved: z20.boolean().default(false)
3393
3394
  });
3394
3395
  dbcConfigRequestSchema = z20.discriminatedUnion("mode", [
@@ -3494,7 +3495,9 @@ var init_device_code = __esm(() => {
3494
3495
  apiKey: z21.string().optional(),
3495
3496
  agentName: z21.string().optional(),
3496
3497
  solanaAddress: z21.string().optional(),
3497
- handle: z21.string().optional()
3498
+ handle: z21.string().optional(),
3499
+ avatarUrl: z21.string().nullable().optional(),
3500
+ claimed: z21.boolean().optional()
3498
3501
  });
3499
3502
  deviceCodePollResponseSchema = successEnvelope(deviceCodePollResponseDataSchema);
3500
3503
  });
@@ -3869,7 +3872,7 @@ async function createServer() {
3869
3872
  const res = await fetch(`${baseUrl}/skill.json`);
3870
3873
  if (!res.ok)
3871
3874
  throw new Error(`Failed to fetch skill.json: ${res.status}`);
3872
- const data = await res.json();
3875
+ const data = z22.record(z22.string(), z22.unknown()).parse(await res.json());
3873
3876
  return textResult(data);
3874
3877
  } catch (error) {
3875
3878
  return textResult(toStructuredError(error));
@@ -3968,15 +3971,14 @@ async function loginCommand(options = {}) {
3968
3971
  body: JSON.stringify(options.ref ? { referralCode: options.ref } : {})
3969
3972
  });
3970
3973
  if (!res.ok) {
3971
- const data2 = await res.json().catch(() => ({}));
3972
3974
  spinner.fail("Failed to get login code");
3973
- console.log(chalk2.red(` ${data2?.error?.message || "Server error"}`));
3975
+ console.log(chalk2.red(" Server error"));
3974
3976
  return false;
3975
3977
  }
3976
- const { data } = await res.json();
3977
- deviceCode = data.deviceCode;
3978
- userCode = data.userCode;
3979
- verificationUrl = data.verificationUrl;
3978
+ const parsed = deviceCodeCreateResponseSchema.parse(await res.json());
3979
+ deviceCode = parsed.data.deviceCode;
3980
+ userCode = parsed.data.userCode;
3981
+ verificationUrl = parsed.data.verificationUrl;
3980
3982
  spinner.stop();
3981
3983
  } catch (error) {
3982
3984
  spinner.fail("Failed to connect to server");
@@ -4010,7 +4012,8 @@ async function loginCommand(options = {}) {
4010
4012
  const res = await fetch(`${apiBase}/auth/device-code/poll?code=${deviceCode}`);
4011
4013
  if (!res.ok)
4012
4014
  continue;
4013
- const { data } = await res.json();
4015
+ const parsed = deviceCodePollResponseSchema.parse(await res.json());
4016
+ const data = parsed.data;
4014
4017
  if (data.status === "completed") {
4015
4018
  pollSpinner.succeed(chalk2.green("Logged in!"));
4016
4019
  console.log("");
@@ -4035,8 +4038,15 @@ async function loginCommand(options = {}) {
4035
4038
  if (data.solanaAddress)
4036
4039
  console.log(` ${chalk2.dim("Wallet:")} ${chalk2.cyan(data.solanaAddress)}`);
4037
4040
  console.log("");
4041
+ const needsOnboard = !data.handle || !data.avatarUrl || !data.claimed;
4042
+ if (needsOnboard) {
4043
+ console.log(chalk2.dim(" Run `cabal-cli onboard` to finish setting up your profile."));
4044
+ }
4038
4045
  console.log(chalk2.dim(" Run `cabal-cli status` to check balances."));
4039
- console.log(chalk2.dim(" Run `cabal-cli trade` to make your first trade.\n"));
4046
+ if (!needsOnboard) {
4047
+ console.log(chalk2.dim(" Run `cabal-cli trade` to make your first trade."));
4048
+ }
4049
+ console.log("");
4040
4050
  }
4041
4051
  return true;
4042
4052
  }
@@ -4056,12 +4066,28 @@ function sleep(ms) {
4056
4066
  }
4057
4067
  var POLL_INTERVAL_MS = 5000, TIMEOUT_MS;
4058
4068
  var init_login = __esm(() => {
4069
+ init_src();
4059
4070
  init_browser();
4060
4071
  init_env();
4061
4072
  init_errors2();
4062
4073
  TIMEOUT_MS = 10 * 60 * 1000;
4063
4074
  });
4064
4075
 
4076
+ // src/lib/tty.ts
4077
+ function isTTY() {
4078
+ return !!process.stdin.isTTY;
4079
+ }
4080
+ function exitMissingFlags(command, missing) {
4081
+ console.error(`Error: this command requires an interactive terminal, or provide these flags:
4082
+ `);
4083
+ for (const { flag, description } of missing) {
4084
+ console.error(` ${flag.padEnd(22)} ${description}`);
4085
+ }
4086
+ console.error(`
4087
+ Example: cabal-cli ${command} ${missing.map((f) => f.flag + " <value>").join(" ")}`);
4088
+ process.exit(1);
4089
+ }
4090
+
4065
4091
  // src/commands/onboard.ts
4066
4092
  var exports_onboard = {};
4067
4093
  __export(exports_onboard, {
@@ -4080,21 +4106,21 @@ async function onboardCommand(options) {
4080
4106
  process.exit(1);
4081
4107
  }
4082
4108
  if (step === "connect") {
4083
- await stepConnect();
4109
+ await stepConnect(options);
4084
4110
  return;
4085
4111
  }
4086
4112
  const client = requireClient();
4087
4113
  if (step === "profile")
4088
- await stepProfile(client);
4114
+ await stepProfile(client, options);
4089
4115
  else if (step === "avatar")
4090
- await stepAvatar(client);
4116
+ await stepAvatar(client, options);
4091
4117
  else if (step === "verify")
4092
- await stepVerify(client);
4118
+ await stepVerify(client, options);
4093
4119
  return;
4094
4120
  }
4095
- await runWizard();
4121
+ await runWizard(options);
4096
4122
  }
4097
- async function runWizard() {
4123
+ async function runWizard(options) {
4098
4124
  if (isConfigured()) {
4099
4125
  const client2 = requireClient();
4100
4126
  let steps;
@@ -4103,14 +4129,27 @@ async function runWizard() {
4103
4129
  } catch {
4104
4130
  console.log(chalk3.yellow(`Saved API key is invalid. Starting fresh.
4105
4131
  `));
4106
- const newClient = await stepConnect();
4132
+ const newClient = await stepConnect(options);
4107
4133
  if (!newClient)
4108
4134
  return;
4109
- await continueWizard(newClient);
4135
+ await continueWizard(newClient, 1, options);
4110
4136
  return;
4111
4137
  }
4112
4138
  const allDone = steps.every((s) => s.done);
4113
4139
  const firstIncomplete = steps.findIndex((s) => !s.done);
4140
+ if (options.confirm) {
4141
+ if (allDone) {
4142
+ console.log(chalk3.green.bold("All onboarding steps already complete."));
4143
+ return;
4144
+ }
4145
+ await continueWizard(client2, firstIncomplete, options);
4146
+ return;
4147
+ }
4148
+ if (!isTTY()) {
4149
+ exitMissingFlags("onboard", [
4150
+ { flag: "-y, --confirm", description: "Auto-continue through wizard (required in non-TTY)" }
4151
+ ]);
4152
+ }
4114
4153
  printProgress(steps);
4115
4154
  if (allDone) {
4116
4155
  const { action: action2 } = await inquirer.prompt([
@@ -4126,10 +4165,10 @@ async function runWizard() {
4126
4165
  ]);
4127
4166
  if (action2 === "quit")
4128
4167
  return;
4129
- const newClient = await stepConnect();
4168
+ const newClient = await stepConnect(options);
4130
4169
  if (!newClient)
4131
4170
  return;
4132
- await continueWizard(newClient);
4171
+ await continueWizard(newClient, 1, options);
4133
4172
  return;
4134
4173
  }
4135
4174
  const { action } = await inquirer.prompt([
@@ -4147,25 +4186,30 @@ async function runWizard() {
4147
4186
  if (action === "quit")
4148
4187
  return;
4149
4188
  if (action === "reconfigure") {
4150
- const newClient = await stepConnect();
4189
+ const newClient = await stepConnect(options);
4151
4190
  if (!newClient)
4152
4191
  return;
4153
- await continueWizard(newClient);
4192
+ await continueWizard(newClient, 1, options);
4154
4193
  return;
4155
4194
  }
4156
- await continueWizard(client2, firstIncomplete);
4195
+ await continueWizard(client2, firstIncomplete, options);
4157
4196
  return;
4158
4197
  }
4159
- const client = await stepConnect();
4198
+ const client = await stepConnect(options);
4160
4199
  if (!client)
4161
4200
  return;
4162
- await continueWizard(client);
4201
+ await continueWizard(client, 1, options);
4163
4202
  }
4164
- async function continueWizard(client, startIdx = 1) {
4203
+ async function continueWizard(client, startIdx = 1, options = {}) {
4165
4204
  for (let i = startIdx;i < STEP_ORDER.length; i++) {
4166
4205
  const step = STEP_ORDER[i];
4167
4206
  console.log("");
4168
- if (i > startIdx) {
4207
+ if (i > startIdx && !options.confirm) {
4208
+ if (!isTTY()) {
4209
+ exitMissingFlags("onboard", [
4210
+ { flag: "-y, --confirm", description: "Auto-continue through wizard (required in non-TTY)" }
4211
+ ]);
4212
+ }
4169
4213
  const { action } = await inquirer.prompt([
4170
4214
  {
4171
4215
  type: "list",
@@ -4184,22 +4228,38 @@ async function continueWizard(client, startIdx = 1) {
4184
4228
  continue;
4185
4229
  }
4186
4230
  if (step === "profile")
4187
- await stepProfile(client);
4231
+ await stepProfile(client, options);
4188
4232
  else if (step === "avatar")
4189
- await stepAvatar(client);
4233
+ await stepAvatar(client, options);
4190
4234
  else if (step === "verify")
4191
- await stepVerify(client);
4235
+ await stepVerify(client, options);
4192
4236
  }
4193
4237
  console.log("");
4194
4238
  console.log(chalk3.green.bold("Onboarding complete!"));
4195
4239
  console.log(chalk3.dim("Run `cabal-cli status` to check your agent.\n"));
4196
4240
  }
4197
- async function stepConnect(apiKeyArg) {
4241
+ async function stepConnect(options, apiKeyArg) {
4198
4242
  console.log(chalk3.bold(`
4199
4243
  Step 1: Connect
4200
4244
  `));
4201
4245
  let apiKey = apiKeyArg;
4202
4246
  if (!apiKey) {
4247
+ if (isConfigured()) {
4248
+ const creds = getCredentials();
4249
+ if (creds.CABAL_API_KEY) {
4250
+ try {
4251
+ const client = new AgentClient(creds.CABAL_API_KEY, creds.NEXT_PUBLIC_SITE_URL);
4252
+ await client.getStatus();
4253
+ console.log(chalk3.dim(` Already connected. Using existing API key.
4254
+ `));
4255
+ return client;
4256
+ } catch {}
4257
+ }
4258
+ }
4259
+ if (!isTTY()) {
4260
+ console.error("Error: No valid API key found. Run `cabal-cli login` in a terminal first.");
4261
+ process.exit(1);
4262
+ }
4203
4263
  console.log(" To connect your agent, you need an API key.");
4204
4264
  console.log(" If you already have one, paste it below.");
4205
4265
  console.log("");
@@ -4290,7 +4350,7 @@ async function stepConnect(apiKeyArg) {
4290
4350
  return null;
4291
4351
  }
4292
4352
  }
4293
- async function stepProfile(client) {
4353
+ async function stepProfile(client, options = {}) {
4294
4354
  console.log(chalk3.bold(`
4295
4355
  Step 2: Profile
4296
4356
  `));
@@ -4305,61 +4365,86 @@ async function stepProfile(client) {
4305
4365
  printCliError(error);
4306
4366
  return;
4307
4367
  }
4308
- console.log(chalk3.dim(` Press Enter to keep current values.
4368
+ if (!isTTY()) {
4369
+ const missing = [];
4370
+ if (!options.name)
4371
+ missing.push({ flag: "--name", description: "Display name" });
4372
+ if (!options.handle)
4373
+ missing.push({ flag: "--handle", description: "Unique handle" });
4374
+ if (!options.bio)
4375
+ missing.push({ flag: "--bio", description: "Agent bio" });
4376
+ if (!options.strategy)
4377
+ missing.push({ flag: "--strategy", description: "Trading strategy" });
4378
+ if (missing.length > 0)
4379
+ exitMissingFlags("onboard --step profile", missing);
4380
+ }
4381
+ let name, handle, bio, strategy;
4382
+ if (options.name && options.handle && options.bio && options.strategy) {
4383
+ name = options.name;
4384
+ handle = options.handle;
4385
+ bio = options.bio;
4386
+ strategy = options.strategy;
4387
+ } else {
4388
+ console.log(chalk3.dim(` Press Enter to keep current values.
4309
4389
  `));
4310
- const answers = await inquirer.prompt([
4311
- {
4312
- type: "input",
4313
- name: "name",
4314
- message: "Display name:",
4315
- default: agent2.name
4316
- },
4317
- {
4318
- type: "input",
4319
- name: "handle",
4320
- message: "Handle (unique):",
4321
- default: agent2.handle ?? undefined,
4322
- validate: async (input) => {
4323
- if (!input)
4324
- return true;
4325
- if (!/^[a-zA-Z0-9_-]+$/.test(input))
4326
- return "Handle must be alphanumeric with _ or -";
4327
- if (input.length > 120)
4328
- return "Handle must be 120 characters or fewer";
4329
- if (input === agent2.handle)
4330
- return true;
4331
- try {
4332
- const result2 = await client.checkHandle(input);
4333
- if (!result2.available)
4334
- return result2.reason ?? "Handle is not available";
4335
- return true;
4336
- } catch {
4337
- return "Could not validate handle — try again";
4390
+ const answers = await inquirer.prompt([
4391
+ {
4392
+ type: "input",
4393
+ name: "name",
4394
+ message: "Display name:",
4395
+ default: options.name ?? agent2.name
4396
+ },
4397
+ {
4398
+ type: "input",
4399
+ name: "handle",
4400
+ message: "Handle (unique):",
4401
+ default: options.handle ?? agent2.handle ?? undefined,
4402
+ validate: async (input) => {
4403
+ if (!input)
4404
+ return true;
4405
+ if (!/^[a-zA-Z0-9_-]+$/.test(input))
4406
+ return "Handle must be alphanumeric with _ or -";
4407
+ if (input.length > 120)
4408
+ return "Handle must be 120 characters or fewer";
4409
+ if (input === agent2.handle)
4410
+ return true;
4411
+ try {
4412
+ const result2 = await client.checkHandle(input);
4413
+ if (!result2.available)
4414
+ return result2.reason ?? "Handle is not available";
4415
+ return true;
4416
+ } catch {
4417
+ return "Could not validate handle — try again";
4418
+ }
4338
4419
  }
4420
+ },
4421
+ {
4422
+ type: "input",
4423
+ name: "bio",
4424
+ message: "Bio:",
4425
+ default: options.bio ?? agent2.bio ?? undefined
4426
+ },
4427
+ {
4428
+ type: "input",
4429
+ name: "strategy",
4430
+ message: "Strategy:",
4431
+ default: options.strategy ?? agent2.strategy ?? undefined
4339
4432
  }
4340
- },
4341
- {
4342
- type: "input",
4343
- name: "bio",
4344
- message: "Bio:",
4345
- default: agent2.bio ?? undefined
4346
- },
4347
- {
4348
- type: "input",
4349
- name: "strategy",
4350
- message: "Strategy:",
4351
- default: agent2.strategy ?? undefined
4352
- }
4353
- ]);
4433
+ ]);
4434
+ name = answers.name;
4435
+ handle = answers.handle;
4436
+ bio = answers.bio;
4437
+ strategy = answers.strategy;
4438
+ }
4354
4439
  const updates = {};
4355
- if (answers.name && answers.name !== agent2.name)
4356
- updates.name = answers.name;
4357
- if (answers.handle && answers.handle !== agent2.handle)
4358
- updates.handle = answers.handle;
4359
- if (answers.bio && answers.bio !== agent2.bio)
4360
- updates.bio = answers.bio;
4361
- if (answers.strategy && answers.strategy !== agent2.strategy)
4362
- updates.strategy = answers.strategy;
4440
+ if (name && name !== agent2.name)
4441
+ updates.name = name;
4442
+ if (handle && handle !== agent2.handle)
4443
+ updates.handle = handle;
4444
+ if (bio && bio !== agent2.bio)
4445
+ updates.bio = bio;
4446
+ if (strategy && strategy !== agent2.strategy)
4447
+ updates.strategy = strategy;
4363
4448
  if (Object.keys(updates).length === 0) {
4364
4449
  console.log(chalk3.dim(`
4365
4450
  No changes made.
@@ -4376,10 +4461,62 @@ async function stepProfile(client) {
4376
4461
  printCliError(error);
4377
4462
  }
4378
4463
  }
4379
- async function stepAvatar(client) {
4464
+ async function stepAvatar(client, options = {}) {
4380
4465
  console.log(chalk3.bold(`
4381
4466
  Step 3: Avatar
4382
4467
  `));
4468
+ if (options.skipAvatar) {
4469
+ console.log(chalk3.dim(` Skipped avatar step.
4470
+ `));
4471
+ return;
4472
+ }
4473
+ if (options.avatarId) {
4474
+ const selectSpinner = ora2("Selecting avatar...").start();
4475
+ try {
4476
+ await client.selectAvatar(options.avatarId);
4477
+ selectSpinner.succeed("Avatar updated!");
4478
+ } catch (error) {
4479
+ selectSpinner.fail("Failed to select avatar");
4480
+ printCliError(error);
4481
+ }
4482
+ console.log("");
4483
+ return;
4484
+ }
4485
+ if (options.avatar) {
4486
+ const genSpinner = ora2("Generating avatar...").start();
4487
+ try {
4488
+ const result2 = await client.generateAvatar(options.avatar);
4489
+ genSpinner.succeed("Avatar generated!");
4490
+ console.log("");
4491
+ if (result2.generationId) {
4492
+ console.log(` ${chalk3.dim("Generation ID:")} ${result2.generationId}`);
4493
+ }
4494
+ console.log(` ${chalk3.dim("Image URL:")} ${chalk3.cyan(result2.imageUrl)}`);
4495
+ console.log(` ${chalk3.dim("Credits left:")} ${result2.creditsRemaining}`);
4496
+ if (result2.generationId) {
4497
+ const selectSpinner = ora2("Setting avatar...").start();
4498
+ try {
4499
+ await client.selectAvatar(result2.generationId);
4500
+ selectSpinner.succeed("Avatar set!");
4501
+ } catch (err2) {
4502
+ selectSpinner.fail("Failed to set avatar");
4503
+ printCliError(err2);
4504
+ }
4505
+ }
4506
+ } catch (error) {
4507
+ genSpinner.fail("Failed to generate avatar");
4508
+ printCliError(error);
4509
+ }
4510
+ console.log("");
4511
+ return;
4512
+ }
4513
+ if (!isTTY()) {
4514
+ exitMissingFlags("onboard --step avatar", [
4515
+ { flag: "--avatar", description: "Generate avatar from this description" },
4516
+ { flag: "--avatar-id", description: "Select an existing avatar generation by ID" },
4517
+ { flag: "--skip-avatar", description: "Skip avatar step entirely" }
4518
+ ]);
4519
+ }
4383
4520
  const spinner = ora2("Fetching avatar credits...").start();
4384
4521
  try {
4385
4522
  const credits = await client.getAvatarCredits();
@@ -4494,20 +4631,36 @@ async function stepAvatar(client) {
4494
4631
  printCliError(error);
4495
4632
  }
4496
4633
  }
4497
- async function stepVerify(client) {
4634
+ async function stepVerify(client, options = {}) {
4498
4635
  console.log(chalk3.bold(`
4499
4636
  Step 4: Verify (optional)
4500
4637
  `));
4501
- console.log(" Claim your agent by tweeting a verification post.");
4502
- console.log(` ${chalk3.dim("Dashboard:")} ${chalk3.cyan(DASHBOARD_URL)}`);
4503
- console.log("");
4504
- const { tweetUrl } = await inquirer.prompt([
4505
- {
4506
- type: "input",
4507
- name: "tweetUrl",
4508
- message: "Tweet URL (or press Enter to skip):"
4509
- }
4510
- ]);
4638
+ if (options.skipVerify) {
4639
+ console.log(chalk3.dim(` Skipped verification.
4640
+ `));
4641
+ return;
4642
+ }
4643
+ let tweetUrl;
4644
+ if (options.tweetUrl) {
4645
+ tweetUrl = options.tweetUrl;
4646
+ } else if (!isTTY()) {
4647
+ exitMissingFlags("onboard --step verify", [
4648
+ { flag: "--tweet-url", description: "Tweet URL for verification" },
4649
+ { flag: "--skip-verify", description: "Skip verification step" }
4650
+ ]);
4651
+ } else {
4652
+ console.log(" Claim your agent by tweeting a verification post.");
4653
+ console.log(` ${chalk3.dim("Dashboard:")} ${chalk3.cyan(DASHBOARD_URL)}`);
4654
+ console.log("");
4655
+ const answer = await inquirer.prompt([
4656
+ {
4657
+ type: "input",
4658
+ name: "tweetUrl",
4659
+ message: "Tweet URL (or press Enter to skip):"
4660
+ }
4661
+ ]);
4662
+ tweetUrl = answer.tweetUrl;
4663
+ }
4511
4664
  if (!tweetUrl.trim()) {
4512
4665
  console.log(chalk3.dim("\n Skipped verification. You can verify later with `cabal-cli onboard --step verify`.\n"));
4513
4666
  return;
@@ -4737,6 +4890,13 @@ import chalk5 from "chalk";
4737
4890
  import ora4 from "ora";
4738
4891
  import inquirer2 from "inquirer";
4739
4892
  import { z as z23 } from "zod";
4893
+ async function promptOrFlag(flagValue, promptFn) {
4894
+ if (flagValue !== undefined)
4895
+ return flagValue;
4896
+ if (!isTTY())
4897
+ throw new Error("__missing_flag__");
4898
+ return promptFn();
4899
+ }
4740
4900
  async function tradeCommand(options) {
4741
4901
  if (!isConfigured()) {
4742
4902
  console.log(chalk5.red("Error: No API key found. Run `cabal-cli init` first."));
@@ -4748,31 +4908,52 @@ async function tradeCommand(options) {
4748
4908
  process.exit(1);
4749
4909
  }
4750
4910
  let request;
4751
- const chain = options.chain || (await inquirer2.prompt([{
4752
- type: "list",
4753
- name: "chain",
4754
- message: "Chain:",
4755
- choices: ["solana", "hyperliquid"]
4756
- }])).chain;
4911
+ let chain;
4912
+ try {
4913
+ chain = await promptOrFlag(options.chain, async () => (await inquirer2.prompt([{
4914
+ type: "list",
4915
+ name: "chain",
4916
+ message: "Chain:",
4917
+ choices: ["solana", "hyperliquid"]
4918
+ }])).chain);
4919
+ } catch {
4920
+ exitMissingFlags("trade", [
4921
+ { flag: "-c, --chain", description: "Chain: solana or hyperliquid" }
4922
+ ]);
4923
+ }
4757
4924
  if (chain === "solana") {
4758
- const inputToken = options.input || (await inquirer2.prompt([{
4925
+ if (!isTTY()) {
4926
+ const missing = [];
4927
+ if (!options.input)
4928
+ missing.push({ flag: "-i, --input", description: "Input token (e.g. SOL, USDC)" });
4929
+ if (!options.output)
4930
+ missing.push({ flag: "-o, --output", description: "Output token (e.g. PEPE, BONK)" });
4931
+ if (!options.amount)
4932
+ missing.push({ flag: "-a, --amount", description: "Amount of input token" });
4933
+ if (!options.confirm)
4934
+ missing.push({ flag: "-y, --confirm", description: "Skip confirmation prompt" });
4935
+ if (missing.length > 0)
4936
+ exitMissingFlags("trade --chain solana", missing);
4937
+ }
4938
+ const inputToken = await promptOrFlag(options.input, async () => (await inquirer2.prompt([{
4759
4939
  type: "input",
4760
4940
  name: "value",
4761
4941
  message: "Input token (e.g. SOL, USDC):",
4762
4942
  validate: (v) => v.trim() ? true : "Required"
4763
- }])).value;
4764
- const outputToken = options.output || (await inquirer2.prompt([{
4943
+ }])).value);
4944
+ const outputToken = await promptOrFlag(options.output, async () => (await inquirer2.prompt([{
4765
4945
  type: "input",
4766
4946
  name: "value",
4767
4947
  message: "Output token (e.g. PEPE, BONK):",
4768
4948
  validate: (v) => v.trim() ? true : "Required"
4769
- }])).value;
4770
- const amount = options.amount ? parseFloat(options.amount) : parseFloat((await inquirer2.prompt([{
4949
+ }])).value);
4950
+ const amountStr = options.amount ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
4771
4951
  type: "input",
4772
4952
  name: "value",
4773
4953
  message: `Amount of ${inputToken} to swap:`,
4774
4954
  validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
4775
4955
  }])).value);
4956
+ const amount = parseFloat(amountStr);
4776
4957
  request = {
4777
4958
  chain: "solana",
4778
4959
  inputToken: inputToken.trim().toUpperCase(),
@@ -4781,34 +4962,49 @@ async function tradeCommand(options) {
4781
4962
  ...options.model && { model: modelSchema.parse(options.model) }
4782
4963
  };
4783
4964
  } else if (chain === "hyperliquid") {
4784
- const coin = options.coin || (await inquirer2.prompt([{
4965
+ if (!isTTY()) {
4966
+ const missing = [];
4967
+ if (!options.coin)
4968
+ missing.push({ flag: "--coin", description: "Coin symbol (e.g. BTC, ETH)" });
4969
+ if (!options.side)
4970
+ missing.push({ flag: "--side", description: "Side: buy or sell" });
4971
+ if (!options.size)
4972
+ missing.push({ flag: "--size", description: "Position size" });
4973
+ if (!options.confirm)
4974
+ missing.push({ flag: "-y, --confirm", description: "Skip confirmation prompt" });
4975
+ if (missing.length > 0)
4976
+ exitMissingFlags("trade --chain hyperliquid", missing);
4977
+ }
4978
+ const coin = await promptOrFlag(options.coin, async () => (await inquirer2.prompt([{
4785
4979
  type: "input",
4786
4980
  name: "value",
4787
4981
  message: "Coin (e.g. BTC, ETH):",
4788
4982
  validate: (v) => v.trim() ? true : "Required"
4789
- }])).value;
4790
- const rawSide = options.side || (await inquirer2.prompt([{
4983
+ }])).value);
4984
+ const rawSide = await promptOrFlag(options.side, async () => (await inquirer2.prompt([{
4791
4985
  type: "list",
4792
4986
  name: "value",
4793
4987
  message: "Side:",
4794
4988
  choices: ["buy", "sell"]
4795
- }])).value;
4989
+ }])).value);
4796
4990
  const side = z23.enum(["buy", "sell"]).parse(rawSide);
4797
- const size = options.size ? parseFloat(options.size) : parseFloat((await inquirer2.prompt([{
4991
+ const sizeStr = options.size ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
4798
4992
  type: "input",
4799
4993
  name: "value",
4800
4994
  message: "Size:",
4801
4995
  validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
4802
4996
  }])).value);
4997
+ const size = parseFloat(sizeStr);
4803
4998
  const orderType = z23.enum(["limit", "market"]).parse(options.orderType || "market");
4804
4999
  let price;
4805
5000
  if (orderType === "limit") {
4806
- price = options.price ? parseFloat(options.price) : parseFloat((await inquirer2.prompt([{
5001
+ const priceStr = options.price ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
4807
5002
  type: "input",
4808
5003
  name: "value",
4809
5004
  message: "Limit price:",
4810
5005
  validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
4811
5006
  }])).value);
5007
+ price = parseFloat(priceStr);
4812
5008
  }
4813
5009
  request = {
4814
5010
  chain: "hyperliquid",
@@ -4836,15 +5032,22 @@ async function tradeCommand(options) {
4836
5032
  console.log(` ${chalk5.dim("Price:")} $${request.price}`);
4837
5033
  }
4838
5034
  console.log("");
4839
- const { confirm } = await inquirer2.prompt([{
4840
- type: "confirm",
4841
- name: "confirm",
4842
- message: "Execute this trade?",
4843
- default: false
4844
- }]);
4845
- if (!confirm) {
4846
- console.log(chalk5.dim("Trade cancelled."));
4847
- return;
5035
+ if (!options.confirm) {
5036
+ if (!isTTY()) {
5037
+ exitMissingFlags("trade", [
5038
+ { flag: "-y, --confirm", description: "Skip confirmation prompt" }
5039
+ ]);
5040
+ }
5041
+ const { confirm } = await inquirer2.prompt([{
5042
+ type: "confirm",
5043
+ name: "confirm",
5044
+ message: "Execute this trade?",
5045
+ default: false
5046
+ }]);
5047
+ if (!confirm) {
5048
+ console.log(chalk5.dim("Trade cancelled."));
5049
+ return;
5050
+ }
4848
5051
  }
4849
5052
  const spinner = ora4("Executing trade...").start();
4850
5053
  try {
@@ -5439,6 +5642,7 @@ import chalk14 from "chalk";
5439
5642
  import ora13 from "ora";
5440
5643
  import fs2 from "fs";
5441
5644
  import path2 from "path";
5645
+ import { z as z26 } from "zod";
5442
5646
  function getBaseUrl() {
5443
5647
  const creds = getCredentials();
5444
5648
  return creds.NEXT_PUBLIC_SITE_URL || "https://cabal.trading";
@@ -5449,8 +5653,7 @@ async function fetchSkillJson() {
5449
5653
  if (!res.ok) {
5450
5654
  throw new Error(`Failed to fetch skill manifest: ${res.status} ${res.statusText}`);
5451
5655
  }
5452
- const data = await res.json();
5453
- return { files: data.files || {}, descriptions: data.descriptions || {} };
5656
+ return skillManifestSchema.parse(await res.json());
5454
5657
  }
5455
5658
  function readInstalledManifest(dir) {
5456
5659
  const manifestPath = path2.join(dir, MANIFEST_FILE);
@@ -5598,10 +5801,14 @@ function formatBytes(bytes) {
5598
5801
  const kb = bytes / 1024;
5599
5802
  return `${kb.toFixed(1)} KB`;
5600
5803
  }
5601
- var MANIFEST_FILE = ".cabal-skills.json";
5804
+ var MANIFEST_FILE = ".cabal-skills.json", skillManifestSchema;
5602
5805
  var init_skill = __esm(() => {
5603
5806
  init_env();
5604
5807
  init_errors2();
5808
+ skillManifestSchema = z26.object({
5809
+ files: z26.record(z26.string(), z26.string()).optional().default({}),
5810
+ descriptions: z26.record(z26.string(), z26.string()).optional().default({})
5811
+ });
5605
5812
  });
5606
5813
 
5607
5814
  // src/index.ts
@@ -5643,7 +5850,7 @@ if (process.argv.includes("--mcp")) {
5643
5850
  printBanner(chalk15);
5644
5851
  await loginCommand2(options);
5645
5852
  });
5646
- program.command("onboard").description("Guided setup wizard — connect, profile, avatar, verify").option("--step <step>", "Jump to a specific step: connect, profile, avatar, verify").action(async (options) => {
5853
+ program.command("onboard").description("Guided setup wizard — connect, profile, avatar, verify").option("--step <step>", "Jump to a specific step: connect, profile, avatar, verify").option("--name <name>", "Display name").option("--handle <handle>", "Unique handle").option("--bio <bio>", "Agent bio").option("--strategy <strategy>", "Trading strategy").option("--avatar <description>", "Generate avatar from this description").option("--avatar-id <id>", "Select an existing avatar generation by ID").option("--skip-avatar", "Skip avatar step").option("--tweet-url <url>", "Tweet URL for verification").option("--skip-verify", "Skip verification step").option("-y, --confirm", "Auto-continue through wizard (required in non-TTY)").action(async (options) => {
5647
5854
  printBanner(chalk15);
5648
5855
  await onboardCommand2(options);
5649
5856
  });
@@ -5652,7 +5859,7 @@ if (process.argv.includes("--mcp")) {
5652
5859
  `));
5653
5860
  await statusCommand2();
5654
5861
  });
5655
- program.command("trade").description("Execute a trade on Solana or Hyperliquid").option("-c, --chain <chain>", "Chain: solana or hyperliquid").option("-i, --input <token>", "Solana: input token symbol").option("-o, --output <token>", "Solana: output token symbol").option("-a, --amount <amount>", "Solana: amount of input token").option("--coin <coin>", "Hyperliquid: coin symbol").option("--side <side>", "Hyperliquid: buy or sell").option("--size <size>", "Hyperliquid: position size").option("--order-type <type>", "Hyperliquid: market or limit").option("--price <price>", "Hyperliquid: limit price").option("--model <model>", "AI model name for attribution").action(async (options) => {
5862
+ program.command("trade").description("Execute a trade on Solana or Hyperliquid").option("-c, --chain <chain>", "Chain: solana or hyperliquid").option("-i, --input <token>", "Solana: input token symbol").option("-o, --output <token>", "Solana: output token symbol").option("-a, --amount <amount>", "Solana: amount of input token").option("--coin <coin>", "Hyperliquid: coin symbol").option("--side <side>", "Hyperliquid: buy or sell").option("--size <size>", "Hyperliquid: position size").option("--order-type <type>", "Hyperliquid: market or limit").option("--price <price>", "Hyperliquid: limit price").option("--model <model>", "AI model name for attribution").option("-y, --confirm", "Skip confirmation prompt").action(async (options) => {
5656
5863
  console.log(chalk15.green.bold("Cabal") + chalk15.dim(` • Trade
5657
5864
  `));
5658
5865
  await tradeCommand2(options);
@@ -3388,6 +3388,7 @@ var init_dbc_configs = __esm(() => {
3388
3388
  name: z20.string().min(1).max(100),
3389
3389
  description: z20.string().max(500).optional(),
3390
3390
  config: z20.record(z20.string(), z20.unknown()).optional(),
3391
+ feeClaimer: z20.string().min(32).max(44).optional(),
3391
3392
  isProtocolApproved: z20.boolean().default(false)
3392
3393
  });
3393
3394
  dbcConfigRequestSchema = z20.discriminatedUnion("mode", [
@@ -3493,7 +3494,9 @@ var init_device_code = __esm(() => {
3493
3494
  apiKey: z21.string().optional(),
3494
3495
  agentName: z21.string().optional(),
3495
3496
  solanaAddress: z21.string().optional(),
3496
- handle: z21.string().optional()
3497
+ handle: z21.string().optional(),
3498
+ avatarUrl: z21.string().nullable().optional(),
3499
+ claimed: z21.boolean().optional()
3497
3500
  });
3498
3501
  deviceCodePollResponseSchema = successEnvelope(deviceCodePollResponseDataSchema);
3499
3502
  });
@@ -3868,7 +3871,7 @@ async function createServer() {
3868
3871
  const res = await fetch(`${baseUrl}/skill.json`);
3869
3872
  if (!res.ok)
3870
3873
  throw new Error(`Failed to fetch skill.json: ${res.status}`);
3871
- const data = await res.json();
3874
+ const data = z22.record(z22.string(), z22.unknown()).parse(await res.json());
3872
3875
  return textResult(data);
3873
3876
  } catch (error) {
3874
3877
  return textResult(toStructuredError(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabaltrading/cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "CLI for Cabal - connect your AI agent and start trading.",
5
5
  "keywords": [
6
6
  "cabal",
@@ -54,4 +54,4 @@
54
54
  "engines": {
55
55
  "node": ">=18.0.0"
56
56
  }
57
- }
57
+ }