@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 +9 -0
- package/dist/index.js +277 -113
- package/package.json +1 -1
- package/postinstall.js +85 -0
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.
|
|
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
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
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
|
-
|
|
488
|
-
|
|
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
|
|
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
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
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>"
|