@dawnai/cli 1.0.8 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,10 @@ dawn account overview
25
25
  dawn account fund
26
26
  dawn account wallet
27
27
 
28
+ dawn wallet list
29
+ dawn wallet use <address-or-name>
30
+ dawn wallet current
31
+
28
32
  dawn strategy list
29
33
  dawn strategy create "<text>"
30
34
  dawn strategy status <id>
@@ -45,6 +49,8 @@ dawn run stop <id>
45
49
 
46
50
  dawn skill list
47
51
  dawn skill install [--force] [--dir <path>]
52
+
53
+ dawn sdk doctor
48
54
  ```
49
55
 
50
56
  ## Environment Variables
@@ -57,5 +63,8 @@ dawn skill install [--force] [--dir <path>]
57
63
  ## Notes
58
64
 
59
65
  - Auth token is stored at `~/.dawn-cli/config.json`.
66
+ - Active Moonpay wallet selection is stored in `~/.dawn-cli/config.json` under `moonpay`.
67
+ - `dawn wallet list` shells out to `mp wallet list` and uses the Polygon address for selection.
60
68
  - `dawn account fund` prints the wallet address and Polygon USDC deposit instructions.
61
69
  - `dawn skill install` copies bundled Dawn skills into `~/.claude/skills` for Claude Code.
70
+ - `dawn sdk doctor` verifies the local Dawn SDK executable (`dawnsdk`) is available.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs/promises';
3
- import http from 'node:http';
4
3
  import os from 'node:os';
5
4
  import path from 'node:path';
6
5
  import process from 'node:process';
6
+ import readline from 'node:readline/promises';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { fileURLToPath } from 'node:url';
9
- const CLI_VERSION = '1.0.8';
9
+ const CLI_VERSION = '1.1.1';
10
10
  const DAWN_API_BASE_URL = 'https://api.dawn.ai';
11
11
  const FUNDING_LINK_TEMPLATE = process.env.DAWN_HELIO_LINK_TEMPLATE || '';
12
12
  const CLI_HOME = process.env.DAWN_CLI_HOME || path.join(os.homedir(), '.dawn-cli');
@@ -195,6 +195,10 @@ function printUsage() {
195
195
  dawn account fund
196
196
  dawn account wallet
197
197
 
198
+ dawn wallet list
199
+ dawn wallet use <address-or-name>
200
+ dawn wallet current
201
+
198
202
  dawn strategy list
199
203
  dawn strategy create <text>
200
204
  dawn strategy status <id>
@@ -217,6 +221,8 @@ function printUsage() {
217
221
  dawn skill list
218
222
  dawn skill install [--force] [--dir <path>]
219
223
 
224
+ dawn sdk doctor
225
+
220
226
  dawn update
221
227
 
222
228
  dawn --version
@@ -226,6 +232,111 @@ Environment variables:
226
232
  DAWN_CLI_HOME Override config directory (default: ~/.dawn-cli)
227
233
  `);
228
234
  }
235
+ function normalizeEvmAddress(value) {
236
+ if (typeof value !== 'string') {
237
+ return null;
238
+ }
239
+ const trimmed = value.trim();
240
+ if (!/^0x[a-fA-F0-9]{40}$/.test(trimmed)) {
241
+ return null;
242
+ }
243
+ return trimmed.toLowerCase();
244
+ }
245
+ function getMoonpayConfig(config) {
246
+ if (!config.moonpay || typeof config.moonpay !== 'object' || Array.isArray(config.moonpay)) {
247
+ return {};
248
+ }
249
+ const moonpay = config.moonpay;
250
+ return {
251
+ activeWalletAddress: typeof moonpay.activeWalletAddress === 'string' ? moonpay.activeWalletAddress : undefined,
252
+ activeWalletName: typeof moonpay.activeWalletName === 'string' ? moonpay.activeWalletName : undefined,
253
+ };
254
+ }
255
+ async function runMoonpayCommand(args) {
256
+ const result = await new Promise((resolve) => {
257
+ const child = spawn('mp', args, {
258
+ stdio: ['ignore', 'pipe', 'pipe'],
259
+ });
260
+ let stdout = '';
261
+ let stderr = '';
262
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
263
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
264
+ child.on('close', (code) => { resolve({ code: code ?? 1, stdout, stderr }); });
265
+ });
266
+ if (result.code !== 0) {
267
+ throw new Error(result.stderr.trim() || result.stdout.trim() || 'Moonpay CLI command failed.');
268
+ }
269
+ return result.stdout;
270
+ }
271
+ function parseMoonpayWalletList(output) {
272
+ const lines = output.split(/\r?\n/);
273
+ const wallets = [];
274
+ let currentName = null;
275
+ let currentPolygonAddress = null;
276
+ const flushCurrent = () => {
277
+ if (currentName && currentPolygonAddress) {
278
+ wallets.push({
279
+ name: currentName,
280
+ address: currentPolygonAddress,
281
+ });
282
+ }
283
+ };
284
+ for (const line of lines) {
285
+ const nameMatch = line.match(/^\s*-\s*name:\s*(.+)\s*$/);
286
+ if (nameMatch) {
287
+ flushCurrent();
288
+ currentName = nameMatch[1].trim();
289
+ currentPolygonAddress = null;
290
+ continue;
291
+ }
292
+ const polygonMatch = line.match(/^\s*polygon:\s*(0x[a-fA-F0-9]{40})\s*$/);
293
+ if (polygonMatch) {
294
+ currentPolygonAddress = polygonMatch[1].toLowerCase();
295
+ }
296
+ }
297
+ flushCurrent();
298
+ return wallets;
299
+ }
300
+ async function loadMoonpayWallets() {
301
+ const output = await runMoonpayCommand(['wallet', 'list']);
302
+ const wallets = parseMoonpayWalletList(output);
303
+ if (!wallets.length) {
304
+ throw new Error('Could not find Polygon wallets from `mp wallet list`. Ensure Moonpay CLI is installed and has wallets.');
305
+ }
306
+ return wallets;
307
+ }
308
+ function findMoonpayWalletBySelector(wallets, selector) {
309
+ const normalizedAddress = normalizeEvmAddress(selector);
310
+ if (normalizedAddress) {
311
+ const byAddress = wallets.find((wallet) => wallet.address === normalizedAddress);
312
+ if (byAddress) {
313
+ return byAddress;
314
+ }
315
+ }
316
+ const lowered = selector.toLowerCase();
317
+ const byName = wallets.filter((wallet) => wallet.name.toLowerCase() === lowered);
318
+ if (byName.length === 1) {
319
+ return byName[0];
320
+ }
321
+ if (byName.length > 1) {
322
+ throw new Error(`Multiple Moonpay wallets match "${selector}". Use full address or wallet ID instead.`);
323
+ }
324
+ throw new Error(`No Moonpay wallet matched "${selector}". Run: dawn wallet list`);
325
+ }
326
+ function printMoonpayWalletTable(wallets, activeAddress) {
327
+ const rows = wallets.map((wallet) => ({
328
+ active: activeAddress && wallet.address === activeAddress ? '*' : ' ',
329
+ name: wallet.name,
330
+ address: wallet.address,
331
+ }));
332
+ const nameWidth = Math.max(8, ...rows.map((row) => row.name.length));
333
+ const addressWidth = Math.max(42, ...rows.map((row) => row.address.length));
334
+ console.log(`${'A'.padEnd(2)}${'NAME'.padEnd(nameWidth)} ${'POLYGON_ADDRESS'.padEnd(addressWidth)}`);
335
+ console.log(`${'-'.repeat(2)}${'-'.repeat(nameWidth)} ${'-'.repeat(addressWidth)}`);
336
+ for (const row of rows) {
337
+ console.log(`${row.active.padEnd(2)}${row.name.padEnd(nameWidth)} ${row.address}`);
338
+ }
339
+ }
229
340
  function parseFlags(args) {
230
341
  const flags = {};
231
342
  const positional = [];
@@ -462,20 +573,27 @@ async function apiFetch(config, endpoint, options = {}) {
462
573
  }
463
574
  return body;
464
575
  }
465
- function openBrowser(url) {
466
- const platform = process.platform;
467
- if (platform === 'darwin') {
468
- spawn('open', [url], { stdio: 'ignore', detached: true }).unref();
469
- return;
576
+ async function promptForHeadlessCode() {
577
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
578
+ throw new Error('Interactive input is unavailable. Set DAWN_JWT_TOKEN for non-interactive environments.');
470
579
  }
471
- if (platform === 'win32') {
472
- spawn('cmd', ['/c', 'start', '', url], {
473
- stdio: 'ignore',
474
- detached: true,
475
- }).unref();
476
- return;
580
+ const rl = readline.createInterface({
581
+ input: process.stdin,
582
+ output: process.stdout,
583
+ });
584
+ try {
585
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
586
+ const raw = (await rl.question('Enter the 6-digit code: ')).trim();
587
+ if (/^\d{6}$/.test(raw)) {
588
+ return raw;
589
+ }
590
+ console.log('Invalid code format. Please enter exactly 6 digits.');
591
+ }
592
+ }
593
+ finally {
594
+ rl.close();
477
595
  }
478
- spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();
596
+ throw new Error('Too many invalid code entry attempts.');
479
597
  }
480
598
  async function handleAuth(args) {
481
599
  if (args.includes('--help') || args.includes('-h')) {
@@ -484,11 +602,11 @@ async function handleAuth(args) {
484
602
  dawn auth status
485
603
  dawn auth logout
486
604
 
487
- For headless/CI environments, set the DAWN_JWT_TOKEN environment variable
488
- instead of using interactive login.`);
605
+ Login uses a browser URL + 6-digit code flow.
606
+ For non-interactive headless/CI environments, set DAWN_JWT_TOKEN.`);
489
607
  return;
490
608
  }
491
- const [subcommand, ...rest] = args;
609
+ const [subcommand] = args;
492
610
  if (!subcommand) {
493
611
  throw new Error('Usage: dawn auth <login|status|logout>');
494
612
  }
@@ -513,103 +631,36 @@ instead of using interactive login.`);
513
631
  if (subcommand !== 'login') {
514
632
  throw new Error(`Unknown auth command: ${subcommand}`);
515
633
  }
516
- const loginArgs = rest;
517
- const { flags } = parseFlags(loginArgs);
518
634
  const config = await loadConfig();
519
- await new Promise((resolve, reject) => {
520
- const timeoutMs = 180000;
521
- let isSettled = false;
522
- let timeoutId = null;
523
- const settle = (callback) => {
524
- if (isSettled) {
525
- return;
526
- }
527
- isSettled = true;
528
- if (timeoutId) {
529
- clearTimeout(timeoutId);
530
- timeoutId = null;
531
- }
532
- callback();
533
- };
534
- const resolveOnce = () => {
535
- settle(resolve);
536
- };
537
- const rejectOnce = (error) => {
538
- settle(() => reject(error));
539
- };
540
- const closeServer = () => {
541
- try {
542
- server.close();
543
- }
544
- catch {
545
- // no-op
546
- }
547
- };
548
- const server = http.createServer(async (req, res) => {
549
- try {
550
- const reqUrl = new URL(req.url || '/', 'http://127.0.0.1');
551
- if (reqUrl.pathname !== '/callback') {
552
- res.statusCode = 404;
553
- res.end('Not found');
554
- return;
555
- }
556
- const token = reqUrl.searchParams.get('token');
557
- const error = reqUrl.searchParams.get('error');
558
- if (error) {
559
- res.statusCode = 200;
560
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
561
- res.end('<h3>Authentication failed. You can close this window.</h3>');
562
- closeServer();
563
- rejectOnce(new Error(`Auth failed: ${error}`));
564
- return;
565
- }
566
- if (!token) {
567
- res.statusCode = 200;
568
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
569
- res.end('<h3>Missing token. You can close this window.</h3>');
570
- closeServer();
571
- rejectOnce(new Error('Missing token in auth callback.'));
572
- return;
573
- }
574
- const latestConfig = await loadConfig();
575
- await saveConfig({ ...latestConfig, token });
576
- res.statusCode = 200;
577
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
578
- res.end('<h3>Authentication complete. You can close this window.</h3>');
579
- closeServer();
580
- resolveOnce();
581
- }
582
- catch (error) {
583
- closeServer();
584
- rejectOnce(error);
585
- }
586
- });
587
- server.listen(0, '127.0.0.1', () => {
588
- const addr = server.address();
589
- if (!addr || typeof addr === 'string') {
590
- rejectOnce(new Error('Failed to open local auth callback server.'));
591
- return;
592
- }
593
- const redirect = `http://127.0.0.1:${addr.port}/callback`;
594
- void (async () => {
595
- try {
596
- const apiBaseUrl = await getApiBaseUrl(config);
597
- const authUrl = `${apiBaseUrl}/v1/auth/google/?redirect=${encodeURIComponent(redirect)}`;
598
- console.log(`Using API: ${apiBaseUrl}`);
599
- console.log('Opening browser for Dawn auth...');
600
- openBrowser(authUrl);
601
- console.log('Waiting for authentication callback...');
602
- }
603
- catch (error) {
604
- rejectOnce(error);
605
- }
606
- })();
607
- });
608
- timeoutId = setTimeout(() => {
609
- closeServer();
610
- rejectOnce(new Error('Timed out waiting for authentication callback.'));
611
- }, timeoutMs);
612
- });
635
+ const apiBaseUrl = await getApiBaseUrl(config);
636
+ const initiateResponse = (await apiFetch(config, '/v1/auth/google/?headless=true'));
637
+ const authUrl = initiateResponse.authUrl;
638
+ const loginRequestId = initiateResponse.loginRequestId;
639
+ if (!authUrl || !loginRequestId) {
640
+ throw new Error('Failed to initialize headless authentication flow.');
641
+ }
642
+ console.log(`Using API: ${apiBaseUrl}`);
643
+ console.log('');
644
+ console.log('Open this URL in any browser/device and complete Google login:');
645
+ console.log(authUrl);
646
+ if (typeof initiateResponse.expiresInSeconds === 'number') {
647
+ console.log(`Code expires in about ${Math.max(1, Math.floor(initiateResponse.expiresInSeconds / 60))} minute(s).`);
648
+ }
649
+ console.log('');
650
+ const code = await promptForHeadlessCode();
651
+ const completeResponse = (await apiFetch(config, '/v1/auth/google/headless/complete', {
652
+ method: 'POST',
653
+ body: JSON.stringify({
654
+ loginRequestId,
655
+ code,
656
+ }),
657
+ }));
658
+ const token = completeResponse.jwtToken;
659
+ if (!token) {
660
+ throw new Error('Authentication did not return a JWT token.');
661
+ }
662
+ const latestConfig = await loadConfig();
663
+ await saveConfig({ ...latestConfig, token });
613
664
  console.log('Authentication successful.');
614
665
  }
615
666
  async function ensureWallet(config) {
@@ -792,6 +843,69 @@ async function handleAccount(args) {
792
843
  }
793
844
  throw new Error(`Unknown account command: ${subcommand}`);
794
845
  }
846
+ async function handleWallet(args) {
847
+ if (args.includes('--help') || args.includes('-h')) {
848
+ console.log(`Usage:
849
+ dawn wallet list
850
+ dawn wallet use <address-or-name>
851
+ dawn wallet current`);
852
+ return;
853
+ }
854
+ const [subcommand, ...rest] = args;
855
+ if (!subcommand) {
856
+ throw new Error('Missing wallet command.');
857
+ }
858
+ const config = await loadConfig();
859
+ const moonpayConfig = getMoonpayConfig(config);
860
+ if (subcommand === 'list') {
861
+ const wallets = await loadMoonpayWallets();
862
+ printMoonpayWalletTable(wallets, moonpayConfig.activeWalletAddress);
863
+ console.log('\nUse a wallet: dawn wallet use <address-or-name>');
864
+ return;
865
+ }
866
+ if (subcommand === 'current') {
867
+ const activeAddress = moonpayConfig.activeWalletAddress;
868
+ if (!activeAddress) {
869
+ console.log('No active Moonpay wallet selected. Run: dawn wallet use <address-or-name>');
870
+ return;
871
+ }
872
+ const wallets = await loadMoonpayWallets();
873
+ const wallet = wallets.find((entry) => entry.address === activeAddress);
874
+ if (!wallet) {
875
+ throw new Error(`Active wallet ${activeAddress} is no longer present in Moonpay wallets. Run: dawn wallet list`);
876
+ }
877
+ console.log('Active Moonpay wallet');
878
+ console.log('=====================');
879
+ printKeyValue('Name', wallet.name);
880
+ printKeyValue('Address', wallet.address);
881
+ printKeyValue('Source', 'mp wallet list');
882
+ return;
883
+ }
884
+ if (subcommand === 'use') {
885
+ const selector = rest[0];
886
+ if (!selector) {
887
+ throw new Error('Usage: dawn wallet use <address-or-name>');
888
+ }
889
+ const wallets = await loadMoonpayWallets();
890
+ const selectedWallet = findMoonpayWalletBySelector(wallets, selector);
891
+ const nextConfig = {
892
+ ...config,
893
+ moonpay: {
894
+ ...moonpayConfig,
895
+ activeWalletAddress: selectedWallet.address,
896
+ activeWalletName: selectedWallet.name,
897
+ },
898
+ };
899
+ await saveConfig(nextConfig);
900
+ console.log('Selected Moonpay wallet');
901
+ console.log('=======================');
902
+ printKeyValue('Name', selectedWallet.name);
903
+ printKeyValue('Address', selectedWallet.address);
904
+ printKeyValue('Source', 'mp wallet list');
905
+ return;
906
+ }
907
+ throw new Error(`Unknown wallet command: ${subcommand}`);
908
+ }
795
909
  function renderConversationList(conversations) {
796
910
  if (!Array.isArray(conversations) || conversations.length === 0) {
797
911
  console.log('No strategies found.');
@@ -1514,6 +1628,48 @@ async function handleSkill(args) {
1514
1628
  }
1515
1629
  throw new Error(`Unknown skill command: ${subcommand}`);
1516
1630
  }
1631
+ async function handleSdk(args) {
1632
+ if (args.includes('--help') || args.includes('-h')) {
1633
+ console.log(`Usage:
1634
+ dawn sdk doctor`);
1635
+ return;
1636
+ }
1637
+ const [subcommand] = args;
1638
+ if (!subcommand) {
1639
+ throw new Error('Missing sdk command. Run: dawn sdk doctor');
1640
+ }
1641
+ if (subcommand === 'doctor') {
1642
+ const result = await new Promise((resolve) => {
1643
+ const child = spawn('dawnsdk', ['doctor'], {
1644
+ stdio: ['ignore', 'pipe', 'pipe'],
1645
+ });
1646
+ let stdout = '';
1647
+ let stderr = '';
1648
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
1649
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
1650
+ child.on('close', (code) => { resolve({ code: code ?? 1, stdout, stderr }); });
1651
+ child.on('error', (error) => {
1652
+ resolve({
1653
+ code: 1,
1654
+ stdout: '',
1655
+ stderr: error instanceof Error ? error.message : String(error),
1656
+ });
1657
+ });
1658
+ });
1659
+ if (result.code !== 0) {
1660
+ throw new Error(`Failed to run SDK doctor. ${result.stderr.trim() || result.stdout.trim()}\nInstall locally from repo: cd py/sdk && uv tool install --from . dawnai-sdk --force`);
1661
+ }
1662
+ const output = result.stdout.trim();
1663
+ if (output) {
1664
+ console.log(output);
1665
+ }
1666
+ else {
1667
+ console.log('SDK doctor completed successfully.');
1668
+ }
1669
+ return;
1670
+ }
1671
+ throw new Error(`Unknown sdk command: ${subcommand}`);
1672
+ }
1517
1673
  async function handleUpdate() {
1518
1674
  console.log('Checking for updates...');
1519
1675
  const result = await new Promise((resolve) => {
@@ -1559,6 +1715,10 @@ async function main() {
1559
1715
  await handleAccount(args);
1560
1716
  return;
1561
1717
  }
1718
+ if (cmd === 'wallet') {
1719
+ await handleWallet(args);
1720
+ return;
1721
+ }
1562
1722
  if (cmd === 'strategy') {
1563
1723
  await handleStrategy(args);
1564
1724
  return;
@@ -1571,6 +1731,10 @@ async function main() {
1571
1731
  await handleSkill(args);
1572
1732
  return;
1573
1733
  }
1734
+ if (cmd === 'sdk') {
1735
+ await handleSdk(args);
1736
+ return;
1737
+ }
1574
1738
  if (cmd === 'update') {
1575
1739
  await handleUpdate();
1576
1740
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dawnai/cli",
3
- "version": "1.0.8",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "User-facing Dawn CLI",
6
6
  "license": "MIT",
package/postinstall.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import fs from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
6
7
  import { fileURLToPath } from 'node:url';
7
8
 
8
9
  // Only show the banner for global installs (skip when installing as a dependency).
@@ -40,6 +41,73 @@ async function installDefaultClaudeSkill() {
40
41
  return { status: 'installed', path: destinationSkillPath };
41
42
  }
42
43
 
44
+ function runCommand(command, args) {
45
+ return new Promise((resolve) => {
46
+ const child = spawn(command, args, {
47
+ stdio: ['ignore', 'pipe', 'pipe'],
48
+ });
49
+ let stdout = '';
50
+ let stderr = '';
51
+ child.stdout.on('data', (data) => { stdout += data.toString(); });
52
+ child.stderr.on('data', (data) => { stderr += data.toString(); });
53
+ child.on('close', (code) => {
54
+ resolve({
55
+ code: code ?? 1,
56
+ stdout: stdout.trim(),
57
+ stderr: stderr.trim(),
58
+ });
59
+ });
60
+ child.on('error', (error) => {
61
+ resolve({
62
+ code: 1,
63
+ stdout: '',
64
+ stderr: error instanceof Error ? error.message : String(error),
65
+ });
66
+ });
67
+ });
68
+ }
69
+
70
+ async function installLocalSdkExecutable() {
71
+ const currentFilePath = fileURLToPath(import.meta.url);
72
+ const currentDir = path.dirname(currentFilePath);
73
+ const sdkPath = path.resolve(currentDir, '..', 'py', 'sdk');
74
+
75
+ try {
76
+ await fs.access(path.join(sdkPath, 'pyproject.toml'));
77
+ } catch {
78
+ return {
79
+ status: 'missing',
80
+ message:
81
+ 'Local SDK source not found (expected ../py/sdk). Skipping SDK executable bootstrap.',
82
+ };
83
+ }
84
+
85
+ // Preferred path: uv tool install from local sdk package.
86
+ const uvInstall = await runCommand('uv', [
87
+ 'tool',
88
+ 'install',
89
+ '--from',
90
+ sdkPath,
91
+ 'dawnai-sdk',
92
+ '--force',
93
+ ]);
94
+ if (uvInstall.code === 0) {
95
+ return {
96
+ status: 'installed',
97
+ message: `Installed Dawn SDK executable from local source: ${sdkPath}`,
98
+ };
99
+ }
100
+
101
+ return {
102
+ status: 'failed',
103
+ message: [
104
+ 'Could not auto-install Dawn SDK executable from local source using uv.',
105
+ `uv error: ${uvInstall.stderr || uvInstall.stdout || 'unknown error'}`,
106
+ `Manual setup: cd "${sdkPath}" && uv tool install --from . dawnai-sdk --force`,
107
+ ].join('\n '),
108
+ };
109
+ }
110
+
43
111
  let skillInstallMessage = '';
44
112
  try {
45
113
  const result = await installDefaultClaudeSkill();
@@ -53,16 +121,33 @@ try {
53
121
  skillInstallMessage = `\n Note: could not auto-install default Claude skill (${message}).\n`;
54
122
  }
55
123
 
124
+ let sdkInstallMessage = '';
125
+ try {
126
+ const result = await installLocalSdkExecutable();
127
+ if (result.status === 'installed') {
128
+ sdkInstallMessage = `\n SDK bootstrap:\n ${result.message}\n`;
129
+ } else if (result.status === 'missing') {
130
+ sdkInstallMessage = `\n SDK bootstrap:\n ${result.message}\n`;
131
+ } else {
132
+ sdkInstallMessage = `\n SDK bootstrap warning:\n ${result.message}\n`;
133
+ }
134
+ } catch (error) {
135
+ const message = error instanceof Error ? error.message : String(error);
136
+ sdkInstallMessage = `\n SDK bootstrap warning:\n Unexpected error: ${message}\n`;
137
+ }
138
+
56
139
  console.log(`
57
140
  @dawnai/cli installed successfully.
58
141
 
59
142
  Get started:
60
143
  dawn --version Check installed version
61
144
  dawn auth login Authenticate (opens browser)
145
+ dawn sdk doctor Verify local SDK executable
62
146
  dawn skill install Install all bundled Claude Code skills
63
147
  dawn --help Show all commands
64
148
 
65
149
  ${skillInstallMessage}
150
+ ${sdkInstallMessage}
66
151
 
67
152
  Headless / CI / agent environments:
68
153
  export DAWN_JWT_TOKEN="<your-token>"