starkbot-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1571 -0
  2. package/package.json +50 -0
package/dist/index.js ADDED
@@ -0,0 +1,1571 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/lib/ui.ts
13
+ import chalk from "chalk";
14
+ import ora from "ora";
15
+ function banner() {
16
+ console.log(brand.bold("\n \u26A1 Starkbot CLI\n"));
17
+ }
18
+ function spinner(text) {
19
+ return ora({ text, color: "magenta" });
20
+ }
21
+ function printError(msg) {
22
+ console.error(`${error.bold("Error:")} ${msg}`);
23
+ }
24
+ function printSuccess(msg) {
25
+ console.log(`${success("\u2713")} ${msg}`);
26
+ }
27
+ function printWarning(msg) {
28
+ console.log(`${warn("!")} ${msg}`);
29
+ }
30
+ function printKeyValue(key, value) {
31
+ console.log(` ${dim(key + ":")} ${value ?? dim("\u2014")}`);
32
+ }
33
+ var brand, success, error, warn, dim, info, bold, prefix;
34
+ var init_ui = __esm({
35
+ "src/lib/ui.ts"() {
36
+ "use strict";
37
+ brand = chalk.hex("#7C3AED");
38
+ success = chalk.green;
39
+ error = chalk.red;
40
+ warn = chalk.yellow;
41
+ dim = chalk.dim;
42
+ info = chalk.cyan;
43
+ bold = chalk.bold;
44
+ prefix = {
45
+ you: chalk.green.bold("you>"),
46
+ agent: chalk.cyan.bold("agent>"),
47
+ system: chalk.blue.bold("system>"),
48
+ error: chalk.red.bold("error>")
49
+ };
50
+ }
51
+ });
52
+
53
+ // src/lib/config.ts
54
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
55
+ import { homedir } from "os";
56
+ import { join } from "path";
57
+ function loadConfig() {
58
+ try {
59
+ if (!existsSync(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
60
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
61
+ return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
62
+ } catch {
63
+ return { ...DEFAULT_CONFIG };
64
+ }
65
+ }
66
+ var CONFIG_DIR, CONFIG_FILE, DEFAULT_CONFIG;
67
+ var init_config = __esm({
68
+ "src/lib/config.ts"() {
69
+ "use strict";
70
+ CONFIG_DIR = join(homedir(), ".starkbot-cli");
71
+ CONFIG_FILE = join(CONFIG_DIR, "config.json");
72
+ DEFAULT_CONFIG = {
73
+ flash_base_url: "https://starkbot.cloud"
74
+ };
75
+ }
76
+ });
77
+
78
+ // src/lib/flash-client.ts
79
+ var FlashClient;
80
+ var init_flash_client = __esm({
81
+ "src/lib/flash-client.ts"() {
82
+ "use strict";
83
+ init_config();
84
+ FlashClient = class {
85
+ baseUrl;
86
+ jwt;
87
+ constructor(jwt) {
88
+ this.baseUrl = loadConfig().flash_base_url;
89
+ this.jwt = jwt;
90
+ }
91
+ headers() {
92
+ return {
93
+ Authorization: `Bearer ${this.jwt}`,
94
+ "Content-Type": "application/json"
95
+ };
96
+ }
97
+ async request(method, path, body) {
98
+ const url = `${this.baseUrl}/api${path}`;
99
+ const resp = await fetch(url, {
100
+ method,
101
+ headers: this.headers(),
102
+ body: body ? JSON.stringify(body) : void 0
103
+ });
104
+ if (!resp.ok) {
105
+ const text = await resp.text().catch(() => "");
106
+ let msg;
107
+ try {
108
+ const json = JSON.parse(text);
109
+ msg = json.error || text;
110
+ } catch {
111
+ msg = text || `HTTP ${resp.status}`;
112
+ }
113
+ throw new Error(msg);
114
+ }
115
+ return resp.json();
116
+ }
117
+ // ==================== Auth ====================
118
+ /** Get X OAuth authorization URL (for CLI flow) */
119
+ async getAuthUrl(cliRedirect) {
120
+ const url = `${this.baseUrl}/api/auth/x?cli_redirect=${encodeURIComponent(cliRedirect)}`;
121
+ const resp = await fetch(url);
122
+ if (!resp.ok) {
123
+ const text = await resp.text().catch(() => "");
124
+ throw new Error(`Failed to get auth URL: ${text}`);
125
+ }
126
+ return resp.json();
127
+ }
128
+ // ==================== User Info ====================
129
+ async getMe() {
130
+ return this.request("GET", "/me");
131
+ }
132
+ // ==================== Tenant ====================
133
+ async getTenantStatus() {
134
+ return this.request("GET", "/tenant/status");
135
+ }
136
+ async provision() {
137
+ return this.request("POST", "/provision", {});
138
+ }
139
+ async getDeploymentStatus() {
140
+ return this.request(
141
+ "GET",
142
+ "/tenant/deployment-status"
143
+ );
144
+ }
145
+ // ==================== Gateway Token ====================
146
+ async getGatewayToken() {
147
+ return this.request("GET", "/tenant/gateway-token");
148
+ }
149
+ // ==================== Subscription ====================
150
+ async activateSubscription(txHash) {
151
+ return this.request("POST", "/subscription/activate", { tx_hash: txHash });
152
+ }
153
+ // ==================== Instances (Multi-Tenant) ====================
154
+ async listInstances() {
155
+ return this.request("GET", "/instances");
156
+ }
157
+ async createInstance(displayName) {
158
+ return this.request("POST", "/instances", {
159
+ display_name: displayName ?? null
160
+ });
161
+ }
162
+ async switchInstance(tenantId) {
163
+ return this.request("POST", "/instances/switch", {
164
+ tenant_id: tenantId
165
+ });
166
+ }
167
+ async deleteInstance() {
168
+ return this.request("POST", "/tenant/delete");
169
+ }
170
+ // ==================== Vouchers ====================
171
+ async redeemVoucher(code) {
172
+ return this.request("POST", "/voucher/redeem", { code });
173
+ }
174
+ async claimTrial() {
175
+ return this.request("POST", "/voucher/claim-trial");
176
+ }
177
+ };
178
+ }
179
+ });
180
+
181
+ // src/lib/auth.ts
182
+ import { createServer } from "http";
183
+ import { createInterface } from "readline";
184
+ import { randomBytes } from "crypto";
185
+ import { URL } from "url";
186
+ import open from "open";
187
+ async function runOAuthFlow() {
188
+ return new Promise((resolve, reject) => {
189
+ let server;
190
+ let timeout;
191
+ const csrfNonce = randomBytes(16).toString("hex");
192
+ server = createServer((req, res) => {
193
+ const url = new URL(req.url ?? "/", `http://localhost`);
194
+ if (url.pathname === "/callback") {
195
+ const returnedNonce = url.searchParams.get("nonce");
196
+ if (returnedNonce !== csrfNonce) {
197
+ res.writeHead(403, { "Content-Type": "text/html" });
198
+ res.end("<h1>Forbidden</h1><p>Invalid CSRF nonce.</p>");
199
+ return;
200
+ }
201
+ const jwt = url.searchParams.get("jwt");
202
+ const username = url.searchParams.get("username");
203
+ const tenantId = url.searchParams.get("tenant_id");
204
+ if (jwt && username) {
205
+ res.writeHead(200, { "Content-Type": "text/html" });
206
+ res.end(`
207
+ <!DOCTYPE html>
208
+ <html>
209
+ <head><title>Starkbot CLI</title></head>
210
+ <body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0f0f0f; color: #e0e0e0;">
211
+ <div style="text-align: center;">
212
+ <h1 style="color: #7C3AED;">&#9889; Logged in!</h1>
213
+ <p>You can close this tab and return to the terminal.</p>
214
+ </div>
215
+ </body>
216
+ </html>
217
+ `);
218
+ clearTimeout(timeout);
219
+ server.close();
220
+ resolve({
221
+ jwt,
222
+ username,
223
+ tenant_id: tenantId ?? void 0
224
+ });
225
+ } else {
226
+ res.writeHead(400, { "Content-Type": "text/html" });
227
+ res.end("<h1>Login failed</h1><p>Missing JWT or username in callback.</p>");
228
+ clearTimeout(timeout);
229
+ server.close();
230
+ reject(new Error("OAuth callback missing jwt or username"));
231
+ }
232
+ } else {
233
+ res.writeHead(404);
234
+ res.end("Not found");
235
+ }
236
+ });
237
+ server.listen(0, "127.0.0.1", async () => {
238
+ const addr = server.address();
239
+ if (!addr || typeof addr === "string") {
240
+ reject(new Error("Failed to start local server"));
241
+ return;
242
+ }
243
+ const port = addr.port;
244
+ const callbackUrl = `http://localhost:${port}/callback?nonce=${csrfNonce}`;
245
+ try {
246
+ const spin = spinner("Preparing login...");
247
+ spin.start();
248
+ const client = new FlashClient("");
249
+ const { url } = await client.getAuthUrl(callbackUrl);
250
+ spin.stop();
251
+ console.log(dim(" Your browser will open for X authorization.\n"));
252
+ await new Promise((res) => {
253
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
254
+ rl.question(" Press Enter when you're ready to log in to X...", () => {
255
+ rl.close();
256
+ res();
257
+ });
258
+ });
259
+ const loginSpin = spinner("Opening browser for X login...");
260
+ loginSpin.start();
261
+ await open(url);
262
+ loginSpin.text = "Waiting for login in browser...";
263
+ timeout = setTimeout(() => {
264
+ loginSpin.fail("Login timed out (5 minutes)");
265
+ server.close();
266
+ reject(new Error("Login timed out"));
267
+ }, 5 * 60 * 1e3);
268
+ } catch (err) {
269
+ printError("Failed to start login");
270
+ server.close();
271
+ reject(err);
272
+ }
273
+ });
274
+ server.on("error", (err) => {
275
+ reject(new Error(`Local server error: ${err.message}`));
276
+ });
277
+ });
278
+ }
279
+ var init_auth = __esm({
280
+ "src/lib/auth.ts"() {
281
+ "use strict";
282
+ init_flash_client();
283
+ init_ui();
284
+ }
285
+ });
286
+
287
+ // src/lib/credentials.ts
288
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync } from "fs";
289
+ import { homedir as homedir2 } from "os";
290
+ import { join as join2 } from "path";
291
+ function ensureDir() {
292
+ if (!existsSync2(CONFIG_DIR2)) {
293
+ mkdirSync2(CONFIG_DIR2, { recursive: true, mode: 448 });
294
+ }
295
+ }
296
+ function loadCredentials() {
297
+ try {
298
+ if (!existsSync2(CREDS_FILE)) return null;
299
+ const raw = readFileSync2(CREDS_FILE, "utf-8");
300
+ return JSON.parse(raw);
301
+ } catch {
302
+ return null;
303
+ }
304
+ }
305
+ function saveCredentials(creds) {
306
+ ensureDir();
307
+ writeFileSync2(CREDS_FILE, JSON.stringify(creds, null, 2), { mode: 384 });
308
+ }
309
+ function updateCredentials(partial) {
310
+ const existing = loadCredentials();
311
+ if (!existing) throw new Error("No credentials stored. Run `starkbot login` first.");
312
+ saveCredentials({ ...existing, ...partial });
313
+ }
314
+ function clearCredentials() {
315
+ try {
316
+ if (existsSync2(CREDS_FILE)) unlinkSync(CREDS_FILE);
317
+ } catch {
318
+ }
319
+ }
320
+ function isJwtExpired(creds) {
321
+ if (!creds.jwt) return true;
322
+ try {
323
+ const parts = creds.jwt.split(".");
324
+ if (parts.length !== 3) return true;
325
+ const payload = JSON.parse(
326
+ Buffer.from(parts[1], "base64url").toString("utf-8")
327
+ );
328
+ const exp = payload.exp;
329
+ if (!exp) return true;
330
+ return Date.now() / 1e3 > exp - 300;
331
+ } catch {
332
+ return true;
333
+ }
334
+ }
335
+ function requireCredentials() {
336
+ const creds = loadCredentials();
337
+ if (!creds) {
338
+ throw new Error("Not logged in. Run `starkbot login` first.");
339
+ }
340
+ if (isJwtExpired(creds)) {
341
+ throw new Error("Session expired. Run `starkbot login` to re-authenticate.");
342
+ }
343
+ return creds;
344
+ }
345
+ var CONFIG_DIR2, CREDS_FILE;
346
+ var init_credentials = __esm({
347
+ "src/lib/credentials.ts"() {
348
+ "use strict";
349
+ CONFIG_DIR2 = join2(homedir2(), ".starkbot-cli");
350
+ CREDS_FILE = join2(CONFIG_DIR2, "credentials.json");
351
+ }
352
+ });
353
+
354
+ // src/commands/login.ts
355
+ var login_exports = {};
356
+ __export(login_exports, {
357
+ loginCommand: () => loginCommand
358
+ });
359
+ async function loginCommand() {
360
+ const existing = loadCredentials();
361
+ if (existing && !isJwtExpired(existing)) {
362
+ printWarning(`Already logged in as @${existing.username}. Use \`starkbot logout\` first to switch accounts.`);
363
+ return;
364
+ }
365
+ const result = await runOAuthFlow();
366
+ const spin = spinner("Fetching account info...");
367
+ spin.start();
368
+ try {
369
+ const client = new FlashClient(result.jwt);
370
+ const me = await client.getMe();
371
+ saveCredentials({
372
+ jwt: result.jwt,
373
+ username: me.user.username,
374
+ tenant_id: me.tenant.id,
375
+ instance_domain: me.tenant.domain ?? void 0,
376
+ jwt_expires_at: new Date(Date.now() + 24 * 60 * 60 * 1e3).toISOString()
377
+ });
378
+ spin.stop();
379
+ printSuccess(`Logged in as @${me.user.username}`);
380
+ if (me.tenant.status === "pending") {
381
+ console.log("\n Your bot is not yet provisioned. Run `starkbot subscribe` then `starkbot provision`.");
382
+ } else if (me.tenant.status === "active" && me.tenant.domain) {
383
+ console.log(`
384
+ Your bot is running at ${me.tenant.domain}`);
385
+ }
386
+ } catch {
387
+ spin.stop();
388
+ saveCredentials({
389
+ jwt: result.jwt,
390
+ username: result.username,
391
+ tenant_id: result.tenant_id ?? ""
392
+ });
393
+ printSuccess(`Logged in as @${result.username}`);
394
+ }
395
+ }
396
+ var init_login = __esm({
397
+ "src/commands/login.ts"() {
398
+ "use strict";
399
+ init_auth();
400
+ init_credentials();
401
+ init_flash_client();
402
+ init_ui();
403
+ }
404
+ });
405
+
406
+ // src/commands/logout.ts
407
+ var logout_exports = {};
408
+ __export(logout_exports, {
409
+ logoutCommand: () => logoutCommand
410
+ });
411
+ async function logoutCommand() {
412
+ const creds = loadCredentials();
413
+ clearCredentials();
414
+ if (creds) {
415
+ printSuccess(`Logged out from @${creds.username}`);
416
+ } else {
417
+ console.log(dim("No credentials stored."));
418
+ }
419
+ }
420
+ var init_logout = __esm({
421
+ "src/commands/logout.ts"() {
422
+ "use strict";
423
+ init_credentials();
424
+ init_ui();
425
+ }
426
+ });
427
+
428
+ // src/commands/status.ts
429
+ var status_exports = {};
430
+ __export(status_exports, {
431
+ statusCommand: () => statusCommand
432
+ });
433
+ async function statusCommand() {
434
+ const creds = requireCredentials();
435
+ const client = new FlashClient(creds.jwt);
436
+ const spin = spinner("Fetching account status...");
437
+ spin.start();
438
+ const me = await client.getMe();
439
+ spin.stop();
440
+ console.log(bold("\n Account"));
441
+ printKeyValue("Username", `@${me.user.username}`);
442
+ printKeyValue("Display name", me.user.display_name);
443
+ printKeyValue("Wallet", me.user.wallet_address);
444
+ console.log(bold("\n Subscription"));
445
+ if (me.subscription) {
446
+ const statusColor2 = me.subscription.status === "active" ? success : warn;
447
+ printKeyValue("Status", statusColor2(me.subscription.status));
448
+ printKeyValue("Expires", me.subscription.expires_at);
449
+ printKeyValue("Days remaining", String(me.subscription.days_remaining));
450
+ if (me.subscription.is_expiring_soon) {
451
+ console.log(warn(" \u26A0 Subscription expiring soon!"));
452
+ }
453
+ } else {
454
+ printKeyValue("Status", dim("none"));
455
+ console.log(dim(" Run `starkbot subscribe` to get started."));
456
+ }
457
+ console.log(bold("\n Active Instance"));
458
+ printKeyValue("ID", me.tenant.id);
459
+ const tenantStatus = me.tenant.status;
460
+ const statusColor = tenantStatus === "active" ? success : tenantStatus === "pending" ? dim : warn;
461
+ printKeyValue("Status", statusColor(tenantStatus));
462
+ printKeyValue("Domain", me.tenant.domain);
463
+ printKeyValue("Auto-update", me.tenant.auto_update_enabled ? "enabled" : "disabled");
464
+ if (me.tenant.updating) {
465
+ console.log(warn(" \u26A0 Instance is updating..."));
466
+ }
467
+ if (me.tenants && me.tenants.length > 1) {
468
+ console.log(bold(`
469
+ All Instances (${me.tenants.length})`));
470
+ for (const t of me.tenants) {
471
+ const active = t.id === me.tenant.id;
472
+ const tColor = t.status === "active" ? success : t.status === "pending" ? dim : warn;
473
+ const label = t.display_name ?? t.id;
474
+ console.log(` ${active ? success("*") : " "} ${label} ${dim("\u2014")} ${tColor(t.status)}${t.domain ? dim(` (${t.domain})`) : ""}`);
475
+ }
476
+ console.log(dim("\n Use `starkbot instances select` to change active instance."));
477
+ }
478
+ if (me.premium && me.premium.status !== "none") {
479
+ console.log(bold("\n Premium"));
480
+ printKeyValue("Status", me.premium.status);
481
+ printKeyValue("Domain type", me.premium.domain_type);
482
+ if (me.premium.custom_subdomain) {
483
+ printKeyValue("Subdomain", me.premium.custom_subdomain);
484
+ }
485
+ if (me.premium.custom_domain) {
486
+ printKeyValue("Custom domain", me.premium.custom_domain);
487
+ }
488
+ }
489
+ console.log();
490
+ }
491
+ var init_status = __esm({
492
+ "src/commands/status.ts"() {
493
+ "use strict";
494
+ init_credentials();
495
+ init_flash_client();
496
+ init_ui();
497
+ }
498
+ });
499
+
500
+ // src/commands/subscribe.ts
501
+ var subscribe_exports = {};
502
+ __export(subscribe_exports, {
503
+ subscribeCommand: () => subscribeCommand
504
+ });
505
+ import inquirer from "inquirer";
506
+ async function subscribeCommand() {
507
+ const creds = requireCredentials();
508
+ const client = new FlashClient(creds.jwt);
509
+ const spin = spinner("Checking subscription...");
510
+ spin.start();
511
+ const me = await client.getMe();
512
+ spin.stop();
513
+ if (me.subscription && me.subscription.status === "active") {
514
+ printSuccess(`You already have an active subscription (${me.subscription.days_remaining} days remaining).`);
515
+ return;
516
+ }
517
+ const { action } = await inquirer.prompt([
518
+ {
519
+ type: "list",
520
+ name: "action",
521
+ message: "How would you like to subscribe?",
522
+ choices: [
523
+ { name: "Claim free trial (7 days)", value: "trial" },
524
+ { name: "Redeem a voucher code", value: "voucher" },
525
+ { name: "Pay with crypto", value: "pay" }
526
+ ]
527
+ }
528
+ ]);
529
+ if (action === "trial") {
530
+ const spin2 = spinner("Claiming free trial...");
531
+ spin2.start();
532
+ try {
533
+ const result = await client.claimTrial();
534
+ spin2.stop();
535
+ if (result.success) {
536
+ printSuccess("Free trial activated! You can now run `starkbot provision`.");
537
+ } else {
538
+ printWarning(result.message ?? "Could not claim trial \u2014 you may have already used it.");
539
+ }
540
+ } catch (err) {
541
+ spin2.stop();
542
+ printError(err.message);
543
+ }
544
+ } else if (action === "voucher") {
545
+ const { code } = await inquirer.prompt([
546
+ {
547
+ type: "input",
548
+ name: "code",
549
+ message: "Enter voucher code:",
550
+ validate: (v) => v.trim().length > 0 || "Code cannot be empty"
551
+ }
552
+ ]);
553
+ const spin2 = spinner("Redeeming voucher...");
554
+ spin2.start();
555
+ try {
556
+ const result = await client.redeemVoucher(code.trim());
557
+ spin2.stop();
558
+ if (result.success) {
559
+ printSuccess("Voucher redeemed! You can now run `starkbot provision`.");
560
+ } else {
561
+ printWarning(result.message ?? "Invalid or already redeemed voucher.");
562
+ }
563
+ } catch (err) {
564
+ spin2.stop();
565
+ printError(err.message);
566
+ }
567
+ } else {
568
+ console.log(`
569
+ Visit ${dim("https://starkbot.cloud")} to pay with crypto.`);
570
+ console.log(` After payment, run ${dim("`starkbot status`")} to verify.
571
+ `);
572
+ }
573
+ }
574
+ var init_subscribe = __esm({
575
+ "src/commands/subscribe.ts"() {
576
+ "use strict";
577
+ init_credentials();
578
+ init_flash_client();
579
+ init_ui();
580
+ }
581
+ });
582
+
583
+ // src/commands/provision.ts
584
+ var provision_exports = {};
585
+ __export(provision_exports, {
586
+ provisionCommand: () => provisionCommand
587
+ });
588
+ async function provisionCommand() {
589
+ const creds = requireCredentials();
590
+ const client = new FlashClient(creds.jwt);
591
+ const spin = spinner("Checking instance status...");
592
+ spin.start();
593
+ try {
594
+ const me = await client.getMe();
595
+ if (me.tenant.status === "active" && me.tenant.domain) {
596
+ spin.stop();
597
+ printSuccess(`Instance already running at ${me.tenant.domain}`);
598
+ console.log(dim(` Run \`starkbot connect\` to set up the gateway connection.`));
599
+ return;
600
+ }
601
+ if (!me.subscription || me.subscription.status !== "active") {
602
+ spin.stop();
603
+ printWarning("No active subscription. Run `starkbot subscribe` first.");
604
+ return;
605
+ }
606
+ spin.text = "Provisioning your Starkbot instance...";
607
+ const result = await client.provision();
608
+ if (result.success) {
609
+ spin.text = `Deployed to ${result.domain} \u2014 waiting for instance to start...`;
610
+ let attempts = 0;
611
+ const maxAttempts = 60;
612
+ while (attempts < maxAttempts) {
613
+ await new Promise((r) => setTimeout(r, 2e3));
614
+ attempts++;
615
+ try {
616
+ const status = await client.getDeploymentStatus();
617
+ if (status.ready) {
618
+ break;
619
+ }
620
+ if (status.needs_reprovision) {
621
+ spin.fail(`Deployment failed: ${status.status}`);
622
+ return;
623
+ }
624
+ const statusLower = status.status.toLowerCase();
625
+ if (statusLower === "failed" || statusLower === "crashed") {
626
+ spin.fail(`Deployment failed: ${status.status}`);
627
+ return;
628
+ }
629
+ spin.text = `Waiting for instance... (${status.status})`;
630
+ } catch {
631
+ }
632
+ }
633
+ spin.stop();
634
+ updateCredentials({ instance_domain: result.domain });
635
+ printSuccess(`Instance provisioned at ${result.domain}`);
636
+ console.log(dim(` Run \`starkbot connect\` to set up the gateway connection.`));
637
+ } else {
638
+ spin.fail("Provisioning failed");
639
+ }
640
+ } catch (err) {
641
+ spin.fail("Provisioning failed");
642
+ printError(err.message);
643
+ }
644
+ }
645
+ var init_provision = __esm({
646
+ "src/commands/provision.ts"() {
647
+ "use strict";
648
+ init_credentials();
649
+ init_flash_client();
650
+ init_ui();
651
+ }
652
+ });
653
+
654
+ // src/lib/gateway-client.ts
655
+ var GatewayClient;
656
+ var init_gateway_client = __esm({
657
+ "src/lib/gateway-client.ts"() {
658
+ "use strict";
659
+ GatewayClient = class {
660
+ baseUrl;
661
+ token;
662
+ sessionId;
663
+ constructor(baseUrl, token, sessionId) {
664
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
665
+ this.token = token;
666
+ this.sessionId = sessionId;
667
+ }
668
+ headers() {
669
+ return {
670
+ Authorization: `Bearer ${this.token}`,
671
+ "Content-Type": "application/json"
672
+ };
673
+ }
674
+ chatBody(message) {
675
+ return {
676
+ message,
677
+ ...this.sessionId ? { session_id: this.sessionId } : {}
678
+ };
679
+ }
680
+ /** Send a message and get the full response */
681
+ async chat(message) {
682
+ const url = `${this.baseUrl}/api/gateway/chat`;
683
+ const resp = await fetch(url, {
684
+ method: "POST",
685
+ headers: this.headers(),
686
+ body: JSON.stringify(this.chatBody(message))
687
+ });
688
+ if (!resp.ok) {
689
+ const text = await resp.text().catch(() => "");
690
+ throw new Error(`HTTP ${resp.status}: ${text}`);
691
+ }
692
+ return resp.json();
693
+ }
694
+ /** Send a message and stream SSE events */
695
+ async chatStream(message, onEvent) {
696
+ const url = `${this.baseUrl}/api/gateway/chat/stream`;
697
+ const resp = await fetch(url, {
698
+ method: "POST",
699
+ headers: this.headers(),
700
+ body: JSON.stringify(this.chatBody(message))
701
+ });
702
+ if (!resp.ok) {
703
+ const text = await resp.text().catch(() => "");
704
+ throw new Error(`HTTP ${resp.status}: ${text}`);
705
+ }
706
+ if (!resp.body) throw new Error("No response body");
707
+ const reader = resp.body.getReader();
708
+ const decoder = new TextDecoder();
709
+ let buffer = "";
710
+ while (true) {
711
+ const { done, value } = await reader.read();
712
+ if (done) break;
713
+ buffer += decoder.decode(value, { stream: true });
714
+ let pos;
715
+ while ((pos = buffer.indexOf("\n\n")) !== -1) {
716
+ const frame = buffer.slice(0, pos);
717
+ buffer = buffer.slice(pos + 2);
718
+ for (const line of frame.split("\n")) {
719
+ if (line.startsWith("data: ")) {
720
+ try {
721
+ const event = JSON.parse(line.slice(6));
722
+ const isDone = event.type === "done";
723
+ onEvent(event);
724
+ if (isDone) return;
725
+ } catch {
726
+ }
727
+ }
728
+ }
729
+ }
730
+ }
731
+ }
732
+ /** Create a new session */
733
+ async newSession() {
734
+ const url = `${this.baseUrl}/api/gateway/sessions/new`;
735
+ const resp = await fetch(url, {
736
+ method: "POST",
737
+ headers: this.headers()
738
+ });
739
+ if (!resp.ok) {
740
+ const text = await resp.text().catch(() => "");
741
+ throw new Error(`HTTP ${resp.status}: ${text}`);
742
+ }
743
+ return resp.json();
744
+ }
745
+ /** List sessions */
746
+ async listSessions() {
747
+ const url = `${this.baseUrl}/api/gateway/sessions`;
748
+ const resp = await fetch(url, {
749
+ method: "GET",
750
+ headers: this.headers()
751
+ });
752
+ if (!resp.ok) {
753
+ const text = await resp.text().catch(() => "");
754
+ throw new Error(`HTTP ${resp.status}: ${text}`);
755
+ }
756
+ return resp.json();
757
+ }
758
+ /** Get message history for a session */
759
+ async getHistory(sessionId) {
760
+ const url = `${this.baseUrl}/api/gateway/sessions/${sessionId}/messages`;
761
+ const resp = await fetch(url, {
762
+ method: "GET",
763
+ headers: this.headers()
764
+ });
765
+ if (!resp.ok) {
766
+ const text = await resp.text().catch(() => "");
767
+ throw new Error(`HTTP ${resp.status}: ${text}`);
768
+ }
769
+ return resp.json();
770
+ }
771
+ /** Simple health check */
772
+ async ping() {
773
+ try {
774
+ const resp = await fetch(`${this.baseUrl}/api/health`, {
775
+ headers: this.headers()
776
+ });
777
+ return resp.ok;
778
+ } catch {
779
+ return false;
780
+ }
781
+ }
782
+ };
783
+ }
784
+ });
785
+
786
+ // src/commands/connect.ts
787
+ var connect_exports = {};
788
+ __export(connect_exports, {
789
+ connectCommand: () => connectCommand
790
+ });
791
+ import inquirer2 from "inquirer";
792
+ async function connectCommand(opts = {}) {
793
+ const creds = requireCredentials();
794
+ if (opts.token) {
795
+ const domain = opts.domain ?? creds.instance_domain;
796
+ if (!domain) {
797
+ printError("Instance domain required. Use --domain <your-instance.starkbot.cloud> or run `starkbot status` first.");
798
+ return;
799
+ }
800
+ updateCredentials({
801
+ gateway_token: opts.token,
802
+ instance_domain: domain
803
+ });
804
+ const spin2 = spinner("Testing connection...");
805
+ spin2.start();
806
+ try {
807
+ const gw = new GatewayClient(`https://${domain}`, opts.token);
808
+ const ok = await gw.ping();
809
+ spin2.stop();
810
+ if (ok) {
811
+ printSuccess(`Connected to ${domain}`);
812
+ console.log(dim(` Run \`starkbot chat\` to start chatting.`));
813
+ } else {
814
+ printWarning(`Gateway token saved for ${domain}, but instance is not responding yet.`);
815
+ console.log(dim(" The instance may still be starting. Try again in a moment."));
816
+ }
817
+ } catch (err) {
818
+ spin2.fail("Connection failed");
819
+ printError(err.message);
820
+ }
821
+ return;
822
+ }
823
+ const client = new FlashClient(creds.jwt);
824
+ const spin = spinner("Fetching gateway credentials...");
825
+ spin.start();
826
+ try {
827
+ const { token, domain } = await client.getGatewayToken();
828
+ updateCredentials({
829
+ gateway_token: token,
830
+ instance_domain: domain
831
+ });
832
+ spin.text = "Testing connection...";
833
+ const gw = new GatewayClient(`https://${domain}`, token);
834
+ const ok = await gw.ping();
835
+ spin.stop();
836
+ if (ok) {
837
+ printSuccess(`Connected to ${domain}`);
838
+ console.log(dim(` Run \`starkbot chat\` to start chatting.`));
839
+ } else {
840
+ printWarning(`Gateway token saved for ${domain}, but instance is not responding yet.`);
841
+ console.log(dim(" The instance may still be starting. Try again in a moment."));
842
+ }
843
+ } catch (err) {
844
+ spin.fail("Connection failed");
845
+ if (err.message?.includes("No CLI gateway token found")) {
846
+ printWarning("This instance was provisioned before CLI support \u2014 no gateway token on file.");
847
+ console.log(dim(" You can grab your token and domain from the instance dashboard.\n"));
848
+ const answers = await inquirer2.prompt([
849
+ {
850
+ type: "input",
851
+ name: "token",
852
+ message: "Gateway token:",
853
+ validate: (v) => v.trim().length > 0 || "Token is required"
854
+ },
855
+ {
856
+ type: "input",
857
+ name: "domain",
858
+ message: "Instance domain (e.g. my-bot.starkbot.cloud):",
859
+ default: creds.instance_domain || void 0,
860
+ validate: (v) => v.trim().length > 0 || "Domain is required"
861
+ }
862
+ ]);
863
+ return connectCommand({ token: answers.token.trim(), domain: answers.domain.trim() });
864
+ } else {
865
+ printError(err.message);
866
+ }
867
+ }
868
+ }
869
+ var init_connect = __esm({
870
+ "src/commands/connect.ts"() {
871
+ "use strict";
872
+ init_credentials();
873
+ init_flash_client();
874
+ init_gateway_client();
875
+ init_ui();
876
+ }
877
+ });
878
+
879
+ // src/commands/chat.ts
880
+ var chat_exports = {};
881
+ __export(chat_exports, {
882
+ chatOneShotCommand: () => chatOneShotCommand,
883
+ chatReplCommand: () => chatReplCommand
884
+ });
885
+ import { createInterface as createInterface2 } from "readline";
886
+ function requireGateway() {
887
+ const creds = requireCredentials();
888
+ if (!creds.gateway_token || !creds.instance_domain) {
889
+ throw new Error(
890
+ "Gateway not configured. Run `starkbot connect` first."
891
+ );
892
+ }
893
+ return new GatewayClient(
894
+ `https://${creds.instance_domain}`,
895
+ creds.gateway_token
896
+ );
897
+ }
898
+ function printSseEvent(event) {
899
+ switch (event.type) {
900
+ case "tool_call": {
901
+ const name = event.tool_name ?? "unknown";
902
+ process.stdout.write(dim(`[${name}...] `));
903
+ break;
904
+ }
905
+ case "tool_result":
906
+ break;
907
+ case "text":
908
+ if (event.content) {
909
+ process.stdout.write(event.content);
910
+ }
911
+ break;
912
+ case "done":
913
+ break;
914
+ }
915
+ }
916
+ async function chatOneShotCommand(message) {
917
+ const gw = requireGateway();
918
+ process.stdout.write(`${prefix.agent} `);
919
+ try {
920
+ await gw.chatStream(message, printSseEvent);
921
+ console.log();
922
+ } catch (err) {
923
+ console.log();
924
+ printError(err.message);
925
+ process.exit(1);
926
+ }
927
+ }
928
+ async function chatReplCommand() {
929
+ const gw = requireGateway();
930
+ console.log(
931
+ dim(
932
+ "Starkbot CLI \u2014 type a message, /new to reset, /sessions to list, /quit to exit"
933
+ )
934
+ );
935
+ const rl = createInterface2({
936
+ input: process.stdin,
937
+ output: process.stdout,
938
+ terminal: true
939
+ });
940
+ const prompt = () => {
941
+ rl.question(`${prefix.you} `, async (input) => {
942
+ const trimmed = input.trim();
943
+ if (!trimmed) {
944
+ prompt();
945
+ return;
946
+ }
947
+ switch (trimmed) {
948
+ case "/quit":
949
+ case "/exit":
950
+ case "/q":
951
+ rl.close();
952
+ console.log(dim("Goodbye!"));
953
+ return;
954
+ case "/new": {
955
+ try {
956
+ const resp = await gw.newSession();
957
+ console.log(
958
+ `${prefix.system} New session created (id: ${resp.session_id})`
959
+ );
960
+ } catch (err) {
961
+ console.log(`${prefix.error} ${err.message}`);
962
+ }
963
+ prompt();
964
+ return;
965
+ }
966
+ case "/sessions": {
967
+ try {
968
+ const resp = await gw.listSessions();
969
+ if (resp.sessions.length === 0) {
970
+ console.log(`${prefix.system} No sessions found`);
971
+ } else {
972
+ console.log(`${prefix.system} Sessions:`);
973
+ for (const s of resp.sessions) {
974
+ console.log(
975
+ ` ${info(s.session_key)} | ${s.message_count} msgs | last active: ${dim(s.last_activity_at)}`
976
+ );
977
+ }
978
+ }
979
+ } catch (err) {
980
+ console.log(`${prefix.error} ${err.message}`);
981
+ }
982
+ prompt();
983
+ return;
984
+ }
985
+ case "/help":
986
+ console.log(`${prefix.system} Commands:`);
987
+ console.log(` ${info("/new")} \u2014 Start a new session`);
988
+ console.log(` ${info("/sessions")} \u2014 List sessions`);
989
+ console.log(
990
+ ` ${info("/history <id>")} \u2014 Show message history`
991
+ );
992
+ console.log(` ${info("/quit")} \u2014 Exit`);
993
+ prompt();
994
+ return;
995
+ default:
996
+ if (trimmed.startsWith("/history")) {
997
+ const parts = trimmed.split(/\s+/);
998
+ if (parts.length < 2) {
999
+ console.log(
1000
+ `${prefix.system} Usage: /history <session_id>`
1001
+ );
1002
+ } else {
1003
+ const sid = parseInt(parts[1], 10);
1004
+ if (isNaN(sid)) {
1005
+ console.log(`${prefix.error} Invalid session ID: ${parts[1]}`);
1006
+ } else {
1007
+ try {
1008
+ const resp = await gw.getHistory(sid);
1009
+ for (const m of resp.messages) {
1010
+ const p = m.role === "user" ? prefix.you : m.role === "assistant" ? prefix.agent : dim(`${m.role}>`);
1011
+ console.log(`${p} ${m.content}`);
1012
+ }
1013
+ } catch (err) {
1014
+ console.log(`${prefix.error} ${err.message}`);
1015
+ }
1016
+ }
1017
+ }
1018
+ prompt();
1019
+ return;
1020
+ }
1021
+ process.stdout.write(`${prefix.agent} `);
1022
+ try {
1023
+ await gw.chatStream(trimmed, printSseEvent);
1024
+ console.log();
1025
+ } catch (err) {
1026
+ console.log();
1027
+ console.log(`${prefix.error} ${err.message}`);
1028
+ }
1029
+ prompt();
1030
+ }
1031
+ });
1032
+ };
1033
+ prompt();
1034
+ rl.on("close", () => {
1035
+ process.exit(0);
1036
+ });
1037
+ }
1038
+ var init_chat = __esm({
1039
+ "src/commands/chat.ts"() {
1040
+ "use strict";
1041
+ init_credentials();
1042
+ init_gateway_client();
1043
+ init_ui();
1044
+ }
1045
+ });
1046
+
1047
+ // src/commands/instances.ts
1048
+ var instances_exports = {};
1049
+ __export(instances_exports, {
1050
+ instancesDeleteCommand: () => instancesDeleteCommand,
1051
+ instancesListCommand: () => instancesListCommand,
1052
+ instancesNewCommand: () => instancesNewCommand,
1053
+ instancesSelectCommand: () => instancesSelectCommand
1054
+ });
1055
+ import inquirer3 from "inquirer";
1056
+ async function instancesListCommand() {
1057
+ const creds = requireCredentials();
1058
+ const client = new FlashClient(creds.jwt);
1059
+ const spin = spinner("Fetching instances...");
1060
+ spin.start();
1061
+ try {
1062
+ const instances2 = await client.listInstances();
1063
+ spin.stop();
1064
+ if (instances2.length === 0) {
1065
+ printWarning("No instances found. Run `starkbot instances new` to create one.");
1066
+ return;
1067
+ }
1068
+ console.log(bold(`
1069
+ Your Instances (${instances2.length})
1070
+ `));
1071
+ for (const inst of instances2) {
1072
+ const active = inst.id === creds.tenant_id;
1073
+ const marker = active ? success(" * ") : " ";
1074
+ const statusColor = inst.status === "active" ? success : inst.status === "pending" ? dim : warn;
1075
+ const name = inst.display_name ?? inst.id;
1076
+ console.log(`${marker}${bold(name)}${active ? dim(" (active)") : ""}`);
1077
+ printKeyValue(" ID", inst.id);
1078
+ printKeyValue(" Status", statusColor(inst.status));
1079
+ printKeyValue(" Domain", inst.domain);
1080
+ printKeyValue(" Wallet", inst.wallet_address);
1081
+ if (inst.subscription) {
1082
+ printKeyValue(" Subscription", inst.subscription.status);
1083
+ }
1084
+ console.log();
1085
+ }
1086
+ } catch (err) {
1087
+ spin.fail("Failed to fetch instances");
1088
+ printError(err.message);
1089
+ }
1090
+ }
1091
+ async function instancesNewCommand(opts) {
1092
+ const creds = requireCredentials();
1093
+ const client = new FlashClient(creds.jwt);
1094
+ const spin = spinner("Creating new instance...");
1095
+ spin.start();
1096
+ try {
1097
+ const result = await client.createInstance(opts.name);
1098
+ spin.stop();
1099
+ printSuccess(`Created instance ${bold(result.tenant.id)}`);
1100
+ if (result.tenant.domain) {
1101
+ printKeyValue("Domain", result.tenant.domain);
1102
+ }
1103
+ printKeyValue("Status", result.tenant.status);
1104
+ if (result.jwt) {
1105
+ saveCredentials({
1106
+ ...creds,
1107
+ jwt: result.jwt,
1108
+ tenant_id: result.tenant.id,
1109
+ // Clear gateway credentials since this is a new instance
1110
+ gateway_token: void 0,
1111
+ instance_domain: void 0
1112
+ });
1113
+ console.log(dim("\n Switched to new instance. Run `starkbot provision` to deploy it."));
1114
+ }
1115
+ } catch (err) {
1116
+ spin.fail("Failed to create instance");
1117
+ printError(err.message);
1118
+ }
1119
+ }
1120
+ async function instancesSelectCommand(tenantId) {
1121
+ const creds = requireCredentials();
1122
+ const client = new FlashClient(creds.jwt);
1123
+ if (!tenantId) {
1124
+ const spin2 = spinner("Fetching instances...");
1125
+ spin2.start();
1126
+ try {
1127
+ const instances2 = await client.listInstances();
1128
+ spin2.stop();
1129
+ if (instances2.length <= 1) {
1130
+ printWarning("You only have one instance. Nothing to switch to.");
1131
+ return;
1132
+ }
1133
+ const choices = instances2.map((inst) => ({
1134
+ name: `${inst.display_name ?? inst.id} ${dim(`(${inst.status}${inst.domain ? ` - ${inst.domain}` : ""})`)}${inst.id === creds.tenant_id ? success(" *active*") : ""}`,
1135
+ value: inst.id
1136
+ }));
1137
+ const answer = await inquirer3.prompt([
1138
+ {
1139
+ type: "list",
1140
+ name: "tenantId",
1141
+ message: "Select instance:",
1142
+ choices
1143
+ }
1144
+ ]);
1145
+ tenantId = answer.tenantId;
1146
+ } catch (err) {
1147
+ spin2.fail("Failed to fetch instances");
1148
+ printError(err.message);
1149
+ return;
1150
+ }
1151
+ }
1152
+ if (tenantId === creds.tenant_id) {
1153
+ printWarning("Already on that instance.");
1154
+ return;
1155
+ }
1156
+ const spin = spinner("Switching instance...");
1157
+ spin.start();
1158
+ try {
1159
+ const result = await client.switchInstance(tenantId);
1160
+ spin.stop();
1161
+ saveCredentials({
1162
+ ...creds,
1163
+ jwt: result.jwt,
1164
+ tenant_id: result.tenant.id,
1165
+ // Clear gateway credentials for the new active instance
1166
+ gateway_token: void 0,
1167
+ instance_domain: result.tenant.domain ?? void 0
1168
+ });
1169
+ const name = result.tenant.id;
1170
+ printSuccess(`Switched to ${bold(name)}`);
1171
+ printKeyValue("Status", result.tenant.status);
1172
+ printKeyValue("Domain", result.tenant.domain);
1173
+ if (result.tenant.status === "active" && result.tenant.domain) {
1174
+ console.log(dim("\n Run `starkbot connect` to set up the gateway connection."));
1175
+ } else if (result.tenant.status === "pending") {
1176
+ console.log(dim("\n Run `starkbot provision` to deploy this instance."));
1177
+ }
1178
+ } catch (err) {
1179
+ spin.fail("Failed to switch instance");
1180
+ printError(err.message);
1181
+ }
1182
+ }
1183
+ async function instancesDeleteCommand(tenantId) {
1184
+ const creds = requireCredentials();
1185
+ const client = new FlashClient(creds.jwt);
1186
+ if (tenantId && tenantId !== creds.tenant_id) {
1187
+ const spin = spinner("Switching to target instance...");
1188
+ spin.start();
1189
+ try {
1190
+ const result = await client.switchInstance(tenantId);
1191
+ spin.stop();
1192
+ saveCredentials({
1193
+ ...creds,
1194
+ jwt: result.jwt,
1195
+ tenant_id: result.tenant.id,
1196
+ gateway_token: void 0,
1197
+ instance_domain: void 0
1198
+ });
1199
+ const newCreds = requireCredentials();
1200
+ const newClient = new FlashClient(newCreds.jwt);
1201
+ await doDelete(newClient, tenantId, newCreds);
1202
+ } catch (err) {
1203
+ spin.fail("Failed to switch to target instance");
1204
+ printError(err.message);
1205
+ }
1206
+ return;
1207
+ }
1208
+ await doDelete(client, creds.tenant_id, creds);
1209
+ }
1210
+ async function doDelete(client, tenantId, creds) {
1211
+ const answer = await inquirer3.prompt([
1212
+ {
1213
+ type: "confirm",
1214
+ name: "confirm",
1215
+ message: `Delete instance ${bold(tenantId)}? This cannot be undone.`,
1216
+ default: false
1217
+ }
1218
+ ]);
1219
+ if (!answer.confirm) {
1220
+ console.log(dim("Cancelled."));
1221
+ return;
1222
+ }
1223
+ const spin = spinner("Deleting instance...");
1224
+ spin.start();
1225
+ try {
1226
+ await client.deleteInstance();
1227
+ spin.stop();
1228
+ printSuccess(`Deleted instance ${tenantId}`);
1229
+ const me = await new FlashClient(creds.jwt).getMe();
1230
+ if (me.tenants && me.tenants.length > 0) {
1231
+ const first = me.tenants[0];
1232
+ const switchResult = await new FlashClient(creds.jwt).switchInstance(first.id);
1233
+ saveCredentials({
1234
+ ...creds,
1235
+ jwt: switchResult.jwt,
1236
+ tenant_id: switchResult.tenant.id,
1237
+ gateway_token: void 0,
1238
+ instance_domain: switchResult.tenant.domain ?? void 0
1239
+ });
1240
+ console.log(dim(` Switched to instance ${first.id}`));
1241
+ }
1242
+ } catch (err) {
1243
+ spin.fail("Failed to delete instance");
1244
+ printError(err.message);
1245
+ }
1246
+ }
1247
+ var init_instances = __esm({
1248
+ "src/commands/instances.ts"() {
1249
+ "use strict";
1250
+ init_credentials();
1251
+ init_flash_client();
1252
+ init_ui();
1253
+ }
1254
+ });
1255
+
1256
+ // src/commands/sessions.ts
1257
+ var sessions_exports = {};
1258
+ __export(sessions_exports, {
1259
+ sessionsCommand: () => sessionsCommand
1260
+ });
1261
+ async function sessionsCommand() {
1262
+ const creds = requireCredentials();
1263
+ if (!creds.gateway_token || !creds.instance_domain) {
1264
+ throw new Error("Gateway not configured. Run `starkbot connect` first.");
1265
+ }
1266
+ const gw = new GatewayClient(
1267
+ `https://${creds.instance_domain}`,
1268
+ creds.gateway_token
1269
+ );
1270
+ const spin = spinner("Fetching sessions...");
1271
+ spin.start();
1272
+ try {
1273
+ const resp = await gw.listSessions();
1274
+ spin.stop();
1275
+ if (resp.sessions.length === 0) {
1276
+ console.log(dim(" No sessions found."));
1277
+ return;
1278
+ }
1279
+ console.log(bold(`
1280
+ Sessions (${resp.sessions.length}):
1281
+ `));
1282
+ for (const s of resp.sessions) {
1283
+ console.log(
1284
+ ` ${info(String(s.id).padStart(4))} ${s.session_key} ${dim(`${s.message_count} msgs`)} ${dim(s.last_activity_at)}`
1285
+ );
1286
+ }
1287
+ console.log();
1288
+ } catch (err) {
1289
+ spin.fail("Failed to fetch sessions");
1290
+ printError(err.message);
1291
+ }
1292
+ }
1293
+ var init_sessions = __esm({
1294
+ "src/commands/sessions.ts"() {
1295
+ "use strict";
1296
+ init_credentials();
1297
+ init_gateway_client();
1298
+ init_ui();
1299
+ }
1300
+ });
1301
+
1302
+ // src/commands/history.ts
1303
+ var history_exports = {};
1304
+ __export(history_exports, {
1305
+ historyCommand: () => historyCommand
1306
+ });
1307
+ async function historyCommand(sessionId) {
1308
+ const creds = requireCredentials();
1309
+ if (!creds.gateway_token || !creds.instance_domain) {
1310
+ throw new Error("Gateway not configured. Run `starkbot connect` first.");
1311
+ }
1312
+ const sid = parseInt(sessionId, 10);
1313
+ if (isNaN(sid)) {
1314
+ printError(`Invalid session ID: ${sessionId}`);
1315
+ process.exit(1);
1316
+ }
1317
+ const gw = new GatewayClient(
1318
+ `https://${creds.instance_domain}`,
1319
+ creds.gateway_token
1320
+ );
1321
+ const spin = spinner("Fetching message history...");
1322
+ spin.start();
1323
+ try {
1324
+ const resp = await gw.getHistory(sid);
1325
+ spin.stop();
1326
+ if (resp.messages.length === 0) {
1327
+ console.log(dim(" No messages in this session."));
1328
+ return;
1329
+ }
1330
+ console.log();
1331
+ for (const m of resp.messages) {
1332
+ const p = m.role === "user" ? prefix.you : m.role === "assistant" ? prefix.agent : dim(`${m.role}>`);
1333
+ console.log(`${p} ${m.content}`);
1334
+ }
1335
+ console.log();
1336
+ } catch (err) {
1337
+ spin.fail("Failed to fetch history");
1338
+ printError(err.message);
1339
+ }
1340
+ }
1341
+ var init_history = __esm({
1342
+ "src/commands/history.ts"() {
1343
+ "use strict";
1344
+ init_credentials();
1345
+ init_gateway_client();
1346
+ init_ui();
1347
+ }
1348
+ });
1349
+
1350
+ // src/commands/wizard.ts
1351
+ var wizard_exports = {};
1352
+ __export(wizard_exports, {
1353
+ wizardCommand: () => wizardCommand
1354
+ });
1355
+ import inquirer4 from "inquirer";
1356
+ async function wizardCommand() {
1357
+ banner();
1358
+ const creds = loadCredentials();
1359
+ if (!creds || isJwtExpired(creds)) {
1360
+ console.log(bold(" Step 1: Login with X\n"));
1361
+ await loginCommand();
1362
+ console.log();
1363
+ } else {
1364
+ printSuccess(`Logged in as @${creds.username}`);
1365
+ }
1366
+ const currentCreds = requireCredentials();
1367
+ const client = new FlashClient(currentCreds.jwt);
1368
+ const spin = spinner("Checking account...");
1369
+ spin.start();
1370
+ let me;
1371
+ try {
1372
+ me = await client.getMe();
1373
+ spin.stop();
1374
+ } catch (err) {
1375
+ spin.fail("Failed to check account status");
1376
+ console.log(dim(` Error: ${err.message}`));
1377
+ return;
1378
+ }
1379
+ const hasInstances = me.tenants && me.tenants.length > 0;
1380
+ if (!me.is_new_user && hasInstances) {
1381
+ const instances2 = me.tenants;
1382
+ const choices = instances2.map((inst) => {
1383
+ const statusTag = inst.status === "active" ? "active" : inst.status;
1384
+ const domain = inst.domain ? ` \u2014 ${inst.domain}` : "";
1385
+ const name = inst.display_name || inst.id.slice(0, 8);
1386
+ const isCurrent = inst.id === me.tenant.id;
1387
+ const label = `${name}${domain} (${statusTag})${isCurrent ? " \u2190 current" : ""}`;
1388
+ return { name: label, value: inst.id };
1389
+ });
1390
+ choices.push({ name: "Create new instance", value: "__new__" });
1391
+ console.log(bold("\n Select an instance\n"));
1392
+ const { instanceChoice } = await inquirer4.prompt([
1393
+ {
1394
+ type: "list",
1395
+ name: "instanceChoice",
1396
+ message: "Which instance do you want to work with?",
1397
+ choices,
1398
+ default: me.tenant.id
1399
+ }
1400
+ ]);
1401
+ if (instanceChoice === "__new__") {
1402
+ const { displayName } = await inquirer4.prompt([
1403
+ {
1404
+ type: "input",
1405
+ name: "displayName",
1406
+ message: "Display name for the new instance (optional):"
1407
+ }
1408
+ ]);
1409
+ const createSpin = spinner("Creating instance...");
1410
+ createSpin.start();
1411
+ try {
1412
+ me = await client.createInstance(displayName || void 0);
1413
+ updateCredentials({ tenant_id: me.tenant.id, gateway_token: void 0, instance_domain: void 0 });
1414
+ createSpin.succeed("Instance created");
1415
+ } catch (err) {
1416
+ createSpin.fail("Failed to create instance");
1417
+ console.log(dim(` Error: ${err.message}`));
1418
+ return;
1419
+ }
1420
+ } else if (instanceChoice !== me.tenant.id) {
1421
+ const switchSpin = spinner("Switching instance...");
1422
+ switchSpin.start();
1423
+ try {
1424
+ me = await client.switchInstance(instanceChoice);
1425
+ updateCredentials({ tenant_id: me.tenant.id, gateway_token: void 0, instance_domain: void 0 });
1426
+ switchSpin.succeed(`Switched to instance ${me.tenant.domain || instanceChoice}`);
1427
+ } catch (err) {
1428
+ switchSpin.fail("Failed to switch instance");
1429
+ console.log(dim(` Error: ${err.message}`));
1430
+ return;
1431
+ }
1432
+ } else {
1433
+ printSuccess(`Using current instance${me.tenant.domain ? ` (${me.tenant.domain})` : ""}`);
1434
+ }
1435
+ console.log();
1436
+ }
1437
+ if (!me.subscription || me.subscription.status !== "active") {
1438
+ console.log(bold("\n Subscribe\n"));
1439
+ await subscribeCommand();
1440
+ try {
1441
+ me = await client.getMe();
1442
+ } catch {
1443
+ return;
1444
+ }
1445
+ if (!me.subscription || me.subscription.status !== "active") {
1446
+ printWarning("Subscription required to continue. Run `starkbot subscribe` when ready.");
1447
+ return;
1448
+ }
1449
+ console.log();
1450
+ } else {
1451
+ printSuccess(`Subscription active (${me.subscription.days_remaining} days remaining)`);
1452
+ }
1453
+ if (me.tenant.status !== "active" || !me.tenant.domain) {
1454
+ console.log(bold("\n Provision your bot\n"));
1455
+ await provisionCommand();
1456
+ console.log();
1457
+ } else {
1458
+ printSuccess(`Instance running at ${me.tenant.domain}`);
1459
+ }
1460
+ const updatedCreds = loadCredentials();
1461
+ if (!updatedCreds?.gateway_token || !updatedCreds?.instance_domain) {
1462
+ console.log(bold("\n Connect to gateway\n"));
1463
+ await connectCommand();
1464
+ console.log();
1465
+ } else {
1466
+ printSuccess("Gateway connected");
1467
+ }
1468
+ const finalCreds = loadCredentials();
1469
+ if (finalCreds?.gateway_token && finalCreds?.instance_domain) {
1470
+ const { startChat } = await inquirer4.prompt([
1471
+ {
1472
+ type: "confirm",
1473
+ name: "startChat",
1474
+ message: "Start chatting with your bot?",
1475
+ default: true
1476
+ }
1477
+ ]);
1478
+ if (startChat) {
1479
+ console.log();
1480
+ await chatReplCommand();
1481
+ }
1482
+ }
1483
+ }
1484
+ var init_wizard = __esm({
1485
+ "src/commands/wizard.ts"() {
1486
+ "use strict";
1487
+ init_credentials();
1488
+ init_flash_client();
1489
+ init_ui();
1490
+ init_login();
1491
+ init_subscribe();
1492
+ init_provision();
1493
+ init_connect();
1494
+ init_chat();
1495
+ }
1496
+ });
1497
+
1498
+ // src/index.ts
1499
+ init_ui();
1500
+ import { Command } from "commander";
1501
+ var program = new Command();
1502
+ program.name("starkbot").description("CLI for Starkbot \u2014 login, provision, and chat with your bot").version("0.1.0").addHelpCommand("help", "Show help for a command");
1503
+ program.command("login").description("Login with your X (Twitter) account").action(async () => {
1504
+ const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), login_exports));
1505
+ await loginCommand2();
1506
+ });
1507
+ program.command("logout").description("Clear stored credentials").action(async () => {
1508
+ const { logoutCommand: logoutCommand2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
1509
+ await logoutCommand2();
1510
+ });
1511
+ program.command("status").description("Show account, subscription, and instance status").action(async () => {
1512
+ const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
1513
+ await statusCommand2();
1514
+ });
1515
+ program.command("subscribe").description("Claim trial, redeem voucher, or subscribe").action(async () => {
1516
+ const { subscribeCommand: subscribeCommand2 } = await Promise.resolve().then(() => (init_subscribe(), subscribe_exports));
1517
+ await subscribeCommand2();
1518
+ });
1519
+ program.command("provision").description("Provision your Starkbot instance").action(async () => {
1520
+ const { provisionCommand: provisionCommand2 } = await Promise.resolve().then(() => (init_provision(), provision_exports));
1521
+ await provisionCommand2();
1522
+ });
1523
+ program.command("connect").description("Fetch gateway token and test connection").option("-t, --token <token>", "Manually provide a gateway token (for existing instances)").option("-d, --domain <domain>", "Instance domain (e.g. mybot-abc123.starkbot.cloud)").action(async (opts) => {
1524
+ const { connectCommand: connectCommand2 } = await Promise.resolve().then(() => (init_connect(), connect_exports));
1525
+ await connectCommand2(opts);
1526
+ });
1527
+ program.command("chat [message]").description("Chat with your bot (REPL or one-shot)").action(async (message) => {
1528
+ const { chatOneShotCommand: chatOneShotCommand2, chatReplCommand: chatReplCommand2 } = await Promise.resolve().then(() => (init_chat(), chat_exports));
1529
+ if (message) {
1530
+ await chatOneShotCommand2(message);
1531
+ } else {
1532
+ await chatReplCommand2();
1533
+ }
1534
+ });
1535
+ var instances = program.command("instances").description("Manage your Starkbot instances").addHelpCommand("help", "Show help for instance commands");
1536
+ instances.command("list").description("List all your instances").action(async () => {
1537
+ const { instancesListCommand: instancesListCommand2 } = await Promise.resolve().then(() => (init_instances(), instances_exports));
1538
+ await instancesListCommand2();
1539
+ });
1540
+ instances.command("new").description("Create a new instance").option("-n, --name <name>", "Display name for the instance").action(async (opts) => {
1541
+ const { instancesNewCommand: instancesNewCommand2 } = await Promise.resolve().then(() => (init_instances(), instances_exports));
1542
+ await instancesNewCommand2(opts);
1543
+ });
1544
+ instances.command("select [tenant-id]").description("Select the active instance").action(async (tenantId) => {
1545
+ const { instancesSelectCommand: instancesSelectCommand2 } = await Promise.resolve().then(() => (init_instances(), instances_exports));
1546
+ await instancesSelectCommand2(tenantId);
1547
+ });
1548
+ instances.command("delete [tenant-id]").description("Delete a deprovisioned instance").action(async (tenantId) => {
1549
+ const { instancesDeleteCommand: instancesDeleteCommand2 } = await Promise.resolve().then(() => (init_instances(), instances_exports));
1550
+ await instancesDeleteCommand2(tenantId);
1551
+ });
1552
+ instances.action(async () => {
1553
+ const { instancesListCommand: instancesListCommand2 } = await Promise.resolve().then(() => (init_instances(), instances_exports));
1554
+ await instancesListCommand2();
1555
+ });
1556
+ program.command("sessions").description("List chat sessions").action(async () => {
1557
+ const { sessionsCommand: sessionsCommand2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
1558
+ await sessionsCommand2();
1559
+ });
1560
+ program.command("history <session-id>").description("Show message history for a session").action(async (sessionId) => {
1561
+ const { historyCommand: historyCommand2 } = await Promise.resolve().then(() => (init_history(), history_exports));
1562
+ await historyCommand2(sessionId);
1563
+ });
1564
+ program.action(async () => {
1565
+ const { wizardCommand: wizardCommand2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
1566
+ await wizardCommand2();
1567
+ });
1568
+ program.parseAsync(process.argv).catch((err) => {
1569
+ printError(err.message);
1570
+ process.exit(1);
1571
+ });