@cabaltrading/cli 0.4.2 → 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 +321 -124
- package/dist/mcp-server.js +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3872,7 +3872,7 @@ async function createServer() {
|
|
|
3872
3872
|
const res = await fetch(`${baseUrl}/skill.json`);
|
|
3873
3873
|
if (!res.ok)
|
|
3874
3874
|
throw new Error(`Failed to fetch skill.json: ${res.status}`);
|
|
3875
|
-
const data = await res.json();
|
|
3875
|
+
const data = z22.record(z22.string(), z22.unknown()).parse(await res.json());
|
|
3876
3876
|
return textResult(data);
|
|
3877
3877
|
} catch (error) {
|
|
3878
3878
|
return textResult(toStructuredError(error));
|
|
@@ -3971,15 +3971,14 @@ async function loginCommand(options = {}) {
|
|
|
3971
3971
|
body: JSON.stringify(options.ref ? { referralCode: options.ref } : {})
|
|
3972
3972
|
});
|
|
3973
3973
|
if (!res.ok) {
|
|
3974
|
-
const data2 = await res.json().catch(() => ({}));
|
|
3975
3974
|
spinner.fail("Failed to get login code");
|
|
3976
|
-
console.log(chalk2.red(
|
|
3975
|
+
console.log(chalk2.red(" Server error"));
|
|
3977
3976
|
return false;
|
|
3978
3977
|
}
|
|
3979
|
-
const
|
|
3980
|
-
deviceCode = data.deviceCode;
|
|
3981
|
-
userCode = data.userCode;
|
|
3982
|
-
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;
|
|
3983
3982
|
spinner.stop();
|
|
3984
3983
|
} catch (error) {
|
|
3985
3984
|
spinner.fail("Failed to connect to server");
|
|
@@ -4013,7 +4012,8 @@ async function loginCommand(options = {}) {
|
|
|
4013
4012
|
const res = await fetch(`${apiBase}/auth/device-code/poll?code=${deviceCode}`);
|
|
4014
4013
|
if (!res.ok)
|
|
4015
4014
|
continue;
|
|
4016
|
-
const
|
|
4015
|
+
const parsed = deviceCodePollResponseSchema.parse(await res.json());
|
|
4016
|
+
const data = parsed.data;
|
|
4017
4017
|
if (data.status === "completed") {
|
|
4018
4018
|
pollSpinner.succeed(chalk2.green("Logged in!"));
|
|
4019
4019
|
console.log("");
|
|
@@ -4066,12 +4066,28 @@ function sleep(ms) {
|
|
|
4066
4066
|
}
|
|
4067
4067
|
var POLL_INTERVAL_MS = 5000, TIMEOUT_MS;
|
|
4068
4068
|
var init_login = __esm(() => {
|
|
4069
|
+
init_src();
|
|
4069
4070
|
init_browser();
|
|
4070
4071
|
init_env();
|
|
4071
4072
|
init_errors2();
|
|
4072
4073
|
TIMEOUT_MS = 10 * 60 * 1000;
|
|
4073
4074
|
});
|
|
4074
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
|
+
|
|
4075
4091
|
// src/commands/onboard.ts
|
|
4076
4092
|
var exports_onboard = {};
|
|
4077
4093
|
__export(exports_onboard, {
|
|
@@ -4090,21 +4106,21 @@ async function onboardCommand(options) {
|
|
|
4090
4106
|
process.exit(1);
|
|
4091
4107
|
}
|
|
4092
4108
|
if (step === "connect") {
|
|
4093
|
-
await stepConnect();
|
|
4109
|
+
await stepConnect(options);
|
|
4094
4110
|
return;
|
|
4095
4111
|
}
|
|
4096
4112
|
const client = requireClient();
|
|
4097
4113
|
if (step === "profile")
|
|
4098
|
-
await stepProfile(client);
|
|
4114
|
+
await stepProfile(client, options);
|
|
4099
4115
|
else if (step === "avatar")
|
|
4100
|
-
await stepAvatar(client);
|
|
4116
|
+
await stepAvatar(client, options);
|
|
4101
4117
|
else if (step === "verify")
|
|
4102
|
-
await stepVerify(client);
|
|
4118
|
+
await stepVerify(client, options);
|
|
4103
4119
|
return;
|
|
4104
4120
|
}
|
|
4105
|
-
await runWizard();
|
|
4121
|
+
await runWizard(options);
|
|
4106
4122
|
}
|
|
4107
|
-
async function runWizard() {
|
|
4123
|
+
async function runWizard(options) {
|
|
4108
4124
|
if (isConfigured()) {
|
|
4109
4125
|
const client2 = requireClient();
|
|
4110
4126
|
let steps;
|
|
@@ -4113,14 +4129,27 @@ async function runWizard() {
|
|
|
4113
4129
|
} catch {
|
|
4114
4130
|
console.log(chalk3.yellow(`Saved API key is invalid. Starting fresh.
|
|
4115
4131
|
`));
|
|
4116
|
-
const newClient = await stepConnect();
|
|
4132
|
+
const newClient = await stepConnect(options);
|
|
4117
4133
|
if (!newClient)
|
|
4118
4134
|
return;
|
|
4119
|
-
await continueWizard(newClient);
|
|
4135
|
+
await continueWizard(newClient, 1, options);
|
|
4120
4136
|
return;
|
|
4121
4137
|
}
|
|
4122
4138
|
const allDone = steps.every((s) => s.done);
|
|
4123
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
|
+
}
|
|
4124
4153
|
printProgress(steps);
|
|
4125
4154
|
if (allDone) {
|
|
4126
4155
|
const { action: action2 } = await inquirer.prompt([
|
|
@@ -4136,10 +4165,10 @@ async function runWizard() {
|
|
|
4136
4165
|
]);
|
|
4137
4166
|
if (action2 === "quit")
|
|
4138
4167
|
return;
|
|
4139
|
-
const newClient = await stepConnect();
|
|
4168
|
+
const newClient = await stepConnect(options);
|
|
4140
4169
|
if (!newClient)
|
|
4141
4170
|
return;
|
|
4142
|
-
await continueWizard(newClient);
|
|
4171
|
+
await continueWizard(newClient, 1, options);
|
|
4143
4172
|
return;
|
|
4144
4173
|
}
|
|
4145
4174
|
const { action } = await inquirer.prompt([
|
|
@@ -4157,25 +4186,30 @@ async function runWizard() {
|
|
|
4157
4186
|
if (action === "quit")
|
|
4158
4187
|
return;
|
|
4159
4188
|
if (action === "reconfigure") {
|
|
4160
|
-
const newClient = await stepConnect();
|
|
4189
|
+
const newClient = await stepConnect(options);
|
|
4161
4190
|
if (!newClient)
|
|
4162
4191
|
return;
|
|
4163
|
-
await continueWizard(newClient);
|
|
4192
|
+
await continueWizard(newClient, 1, options);
|
|
4164
4193
|
return;
|
|
4165
4194
|
}
|
|
4166
|
-
await continueWizard(client2, firstIncomplete);
|
|
4195
|
+
await continueWizard(client2, firstIncomplete, options);
|
|
4167
4196
|
return;
|
|
4168
4197
|
}
|
|
4169
|
-
const client = await stepConnect();
|
|
4198
|
+
const client = await stepConnect(options);
|
|
4170
4199
|
if (!client)
|
|
4171
4200
|
return;
|
|
4172
|
-
await continueWizard(client);
|
|
4201
|
+
await continueWizard(client, 1, options);
|
|
4173
4202
|
}
|
|
4174
|
-
async function continueWizard(client, startIdx = 1) {
|
|
4203
|
+
async function continueWizard(client, startIdx = 1, options = {}) {
|
|
4175
4204
|
for (let i = startIdx;i < STEP_ORDER.length; i++) {
|
|
4176
4205
|
const step = STEP_ORDER[i];
|
|
4177
4206
|
console.log("");
|
|
4178
|
-
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
|
+
}
|
|
4179
4213
|
const { action } = await inquirer.prompt([
|
|
4180
4214
|
{
|
|
4181
4215
|
type: "list",
|
|
@@ -4194,22 +4228,38 @@ async function continueWizard(client, startIdx = 1) {
|
|
|
4194
4228
|
continue;
|
|
4195
4229
|
}
|
|
4196
4230
|
if (step === "profile")
|
|
4197
|
-
await stepProfile(client);
|
|
4231
|
+
await stepProfile(client, options);
|
|
4198
4232
|
else if (step === "avatar")
|
|
4199
|
-
await stepAvatar(client);
|
|
4233
|
+
await stepAvatar(client, options);
|
|
4200
4234
|
else if (step === "verify")
|
|
4201
|
-
await stepVerify(client);
|
|
4235
|
+
await stepVerify(client, options);
|
|
4202
4236
|
}
|
|
4203
4237
|
console.log("");
|
|
4204
4238
|
console.log(chalk3.green.bold("Onboarding complete!"));
|
|
4205
4239
|
console.log(chalk3.dim("Run `cabal-cli status` to check your agent.\n"));
|
|
4206
4240
|
}
|
|
4207
|
-
async function stepConnect(apiKeyArg) {
|
|
4241
|
+
async function stepConnect(options, apiKeyArg) {
|
|
4208
4242
|
console.log(chalk3.bold(`
|
|
4209
4243
|
Step 1: Connect
|
|
4210
4244
|
`));
|
|
4211
4245
|
let apiKey = apiKeyArg;
|
|
4212
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
|
+
}
|
|
4213
4263
|
console.log(" To connect your agent, you need an API key.");
|
|
4214
4264
|
console.log(" If you already have one, paste it below.");
|
|
4215
4265
|
console.log("");
|
|
@@ -4300,7 +4350,7 @@ async function stepConnect(apiKeyArg) {
|
|
|
4300
4350
|
return null;
|
|
4301
4351
|
}
|
|
4302
4352
|
}
|
|
4303
|
-
async function stepProfile(client) {
|
|
4353
|
+
async function stepProfile(client, options = {}) {
|
|
4304
4354
|
console.log(chalk3.bold(`
|
|
4305
4355
|
Step 2: Profile
|
|
4306
4356
|
`));
|
|
@@ -4315,61 +4365,86 @@ async function stepProfile(client) {
|
|
|
4315
4365
|
printCliError(error);
|
|
4316
4366
|
return;
|
|
4317
4367
|
}
|
|
4318
|
-
|
|
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.
|
|
4319
4389
|
`));
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
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
|
+
}
|
|
4348
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
|
|
4349
4432
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
},
|
|
4357
|
-
{
|
|
4358
|
-
type: "input",
|
|
4359
|
-
name: "strategy",
|
|
4360
|
-
message: "Strategy:",
|
|
4361
|
-
default: agent2.strategy ?? undefined
|
|
4362
|
-
}
|
|
4363
|
-
]);
|
|
4433
|
+
]);
|
|
4434
|
+
name = answers.name;
|
|
4435
|
+
handle = answers.handle;
|
|
4436
|
+
bio = answers.bio;
|
|
4437
|
+
strategy = answers.strategy;
|
|
4438
|
+
}
|
|
4364
4439
|
const updates = {};
|
|
4365
|
-
if (
|
|
4366
|
-
updates.name =
|
|
4367
|
-
if (
|
|
4368
|
-
updates.handle =
|
|
4369
|
-
if (
|
|
4370
|
-
updates.bio =
|
|
4371
|
-
if (
|
|
4372
|
-
updates.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;
|
|
4373
4448
|
if (Object.keys(updates).length === 0) {
|
|
4374
4449
|
console.log(chalk3.dim(`
|
|
4375
4450
|
No changes made.
|
|
@@ -4386,10 +4461,62 @@ async function stepProfile(client) {
|
|
|
4386
4461
|
printCliError(error);
|
|
4387
4462
|
}
|
|
4388
4463
|
}
|
|
4389
|
-
async function stepAvatar(client) {
|
|
4464
|
+
async function stepAvatar(client, options = {}) {
|
|
4390
4465
|
console.log(chalk3.bold(`
|
|
4391
4466
|
Step 3: Avatar
|
|
4392
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
|
+
}
|
|
4393
4520
|
const spinner = ora2("Fetching avatar credits...").start();
|
|
4394
4521
|
try {
|
|
4395
4522
|
const credits = await client.getAvatarCredits();
|
|
@@ -4504,20 +4631,36 @@ async function stepAvatar(client) {
|
|
|
4504
4631
|
printCliError(error);
|
|
4505
4632
|
}
|
|
4506
4633
|
}
|
|
4507
|
-
async function stepVerify(client) {
|
|
4634
|
+
async function stepVerify(client, options = {}) {
|
|
4508
4635
|
console.log(chalk3.bold(`
|
|
4509
4636
|
Step 4: Verify (optional)
|
|
4510
4637
|
`));
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
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
|
+
}
|
|
4521
4664
|
if (!tweetUrl.trim()) {
|
|
4522
4665
|
console.log(chalk3.dim("\n Skipped verification. You can verify later with `cabal-cli onboard --step verify`.\n"));
|
|
4523
4666
|
return;
|
|
@@ -4747,6 +4890,13 @@ import chalk5 from "chalk";
|
|
|
4747
4890
|
import ora4 from "ora";
|
|
4748
4891
|
import inquirer2 from "inquirer";
|
|
4749
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
|
+
}
|
|
4750
4900
|
async function tradeCommand(options) {
|
|
4751
4901
|
if (!isConfigured()) {
|
|
4752
4902
|
console.log(chalk5.red("Error: No API key found. Run `cabal-cli init` first."));
|
|
@@ -4758,31 +4908,52 @@ async function tradeCommand(options) {
|
|
|
4758
4908
|
process.exit(1);
|
|
4759
4909
|
}
|
|
4760
4910
|
let request;
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
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
|
+
}
|
|
4767
4924
|
if (chain === "solana") {
|
|
4768
|
-
|
|
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([{
|
|
4769
4939
|
type: "input",
|
|
4770
4940
|
name: "value",
|
|
4771
4941
|
message: "Input token (e.g. SOL, USDC):",
|
|
4772
4942
|
validate: (v) => v.trim() ? true : "Required"
|
|
4773
|
-
}])).value;
|
|
4774
|
-
const outputToken = options.output
|
|
4943
|
+
}])).value);
|
|
4944
|
+
const outputToken = await promptOrFlag(options.output, async () => (await inquirer2.prompt([{
|
|
4775
4945
|
type: "input",
|
|
4776
4946
|
name: "value",
|
|
4777
4947
|
message: "Output token (e.g. PEPE, BONK):",
|
|
4778
4948
|
validate: (v) => v.trim() ? true : "Required"
|
|
4779
|
-
}])).value;
|
|
4780
|
-
const
|
|
4949
|
+
}])).value);
|
|
4950
|
+
const amountStr = options.amount ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
|
|
4781
4951
|
type: "input",
|
|
4782
4952
|
name: "value",
|
|
4783
4953
|
message: `Amount of ${inputToken} to swap:`,
|
|
4784
4954
|
validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
|
|
4785
4955
|
}])).value);
|
|
4956
|
+
const amount = parseFloat(amountStr);
|
|
4786
4957
|
request = {
|
|
4787
4958
|
chain: "solana",
|
|
4788
4959
|
inputToken: inputToken.trim().toUpperCase(),
|
|
@@ -4791,34 +4962,49 @@ async function tradeCommand(options) {
|
|
|
4791
4962
|
...options.model && { model: modelSchema.parse(options.model) }
|
|
4792
4963
|
};
|
|
4793
4964
|
} else if (chain === "hyperliquid") {
|
|
4794
|
-
|
|
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([{
|
|
4795
4979
|
type: "input",
|
|
4796
4980
|
name: "value",
|
|
4797
4981
|
message: "Coin (e.g. BTC, ETH):",
|
|
4798
4982
|
validate: (v) => v.trim() ? true : "Required"
|
|
4799
|
-
}])).value;
|
|
4800
|
-
const rawSide = options.side
|
|
4983
|
+
}])).value);
|
|
4984
|
+
const rawSide = await promptOrFlag(options.side, async () => (await inquirer2.prompt([{
|
|
4801
4985
|
type: "list",
|
|
4802
4986
|
name: "value",
|
|
4803
4987
|
message: "Side:",
|
|
4804
4988
|
choices: ["buy", "sell"]
|
|
4805
|
-
}])).value;
|
|
4989
|
+
}])).value);
|
|
4806
4990
|
const side = z23.enum(["buy", "sell"]).parse(rawSide);
|
|
4807
|
-
const
|
|
4991
|
+
const sizeStr = options.size ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
|
|
4808
4992
|
type: "input",
|
|
4809
4993
|
name: "value",
|
|
4810
4994
|
message: "Size:",
|
|
4811
4995
|
validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
|
|
4812
4996
|
}])).value);
|
|
4997
|
+
const size = parseFloat(sizeStr);
|
|
4813
4998
|
const orderType = z23.enum(["limit", "market"]).parse(options.orderType || "market");
|
|
4814
4999
|
let price;
|
|
4815
5000
|
if (orderType === "limit") {
|
|
4816
|
-
|
|
5001
|
+
const priceStr = options.price ?? await promptOrFlag(undefined, async () => (await inquirer2.prompt([{
|
|
4817
5002
|
type: "input",
|
|
4818
5003
|
name: "value",
|
|
4819
5004
|
message: "Limit price:",
|
|
4820
5005
|
validate: (v) => parseFloat(v) > 0 ? true : "Must be a positive number"
|
|
4821
5006
|
}])).value);
|
|
5007
|
+
price = parseFloat(priceStr);
|
|
4822
5008
|
}
|
|
4823
5009
|
request = {
|
|
4824
5010
|
chain: "hyperliquid",
|
|
@@ -4846,15 +5032,22 @@ async function tradeCommand(options) {
|
|
|
4846
5032
|
console.log(` ${chalk5.dim("Price:")} $${request.price}`);
|
|
4847
5033
|
}
|
|
4848
5034
|
console.log("");
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
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
|
+
}
|
|
4858
5051
|
}
|
|
4859
5052
|
const spinner = ora4("Executing trade...").start();
|
|
4860
5053
|
try {
|
|
@@ -5449,6 +5642,7 @@ import chalk14 from "chalk";
|
|
|
5449
5642
|
import ora13 from "ora";
|
|
5450
5643
|
import fs2 from "fs";
|
|
5451
5644
|
import path2 from "path";
|
|
5645
|
+
import { z as z26 } from "zod";
|
|
5452
5646
|
function getBaseUrl() {
|
|
5453
5647
|
const creds = getCredentials();
|
|
5454
5648
|
return creds.NEXT_PUBLIC_SITE_URL || "https://cabal.trading";
|
|
@@ -5459,8 +5653,7 @@ async function fetchSkillJson() {
|
|
|
5459
5653
|
if (!res.ok) {
|
|
5460
5654
|
throw new Error(`Failed to fetch skill manifest: ${res.status} ${res.statusText}`);
|
|
5461
5655
|
}
|
|
5462
|
-
|
|
5463
|
-
return { files: data.files || {}, descriptions: data.descriptions || {} };
|
|
5656
|
+
return skillManifestSchema.parse(await res.json());
|
|
5464
5657
|
}
|
|
5465
5658
|
function readInstalledManifest(dir) {
|
|
5466
5659
|
const manifestPath = path2.join(dir, MANIFEST_FILE);
|
|
@@ -5608,10 +5801,14 @@ function formatBytes(bytes) {
|
|
|
5608
5801
|
const kb = bytes / 1024;
|
|
5609
5802
|
return `${kb.toFixed(1)} KB`;
|
|
5610
5803
|
}
|
|
5611
|
-
var MANIFEST_FILE = ".cabal-skills.json";
|
|
5804
|
+
var MANIFEST_FILE = ".cabal-skills.json", skillManifestSchema;
|
|
5612
5805
|
var init_skill = __esm(() => {
|
|
5613
5806
|
init_env();
|
|
5614
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
|
+
});
|
|
5615
5812
|
});
|
|
5616
5813
|
|
|
5617
5814
|
// src/index.ts
|
|
@@ -5653,7 +5850,7 @@ if (process.argv.includes("--mcp")) {
|
|
|
5653
5850
|
printBanner(chalk15);
|
|
5654
5851
|
await loginCommand2(options);
|
|
5655
5852
|
});
|
|
5656
|
-
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) => {
|
|
5657
5854
|
printBanner(chalk15);
|
|
5658
5855
|
await onboardCommand2(options);
|
|
5659
5856
|
});
|
|
@@ -5662,7 +5859,7 @@ if (process.argv.includes("--mcp")) {
|
|
|
5662
5859
|
`));
|
|
5663
5860
|
await statusCommand2();
|
|
5664
5861
|
});
|
|
5665
|
-
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) => {
|
|
5666
5863
|
console.log(chalk15.green.bold("Cabal") + chalk15.dim(` • Trade
|
|
5667
5864
|
`));
|
|
5668
5865
|
await tradeCommand2(options);
|
package/dist/mcp-server.js
CHANGED
|
@@ -3871,7 +3871,7 @@ async function createServer() {
|
|
|
3871
3871
|
const res = await fetch(`${baseUrl}/skill.json`);
|
|
3872
3872
|
if (!res.ok)
|
|
3873
3873
|
throw new Error(`Failed to fetch skill.json: ${res.status}`);
|
|
3874
|
-
const data = await res.json();
|
|
3874
|
+
const data = z22.record(z22.string(), z22.unknown()).parse(await res.json());
|
|
3875
3875
|
return textResult(data);
|
|
3876
3876
|
} catch (error) {
|
|
3877
3877
|
return textResult(toStructuredError(error));
|
package/package.json
CHANGED