@tuskydp/cli 0.2.1 → 0.4.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 (160) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/src/commands/account.d.ts.map +1 -1
  3. package/dist/src/commands/account.js +0 -1
  4. package/dist/src/commands/account.js.map +1 -1
  5. package/dist/src/commands/auth.d.ts.map +1 -1
  6. package/dist/src/commands/auth.js +8 -5
  7. package/dist/src/commands/auth.js.map +1 -1
  8. package/dist/src/commands/download.d.ts +1 -0
  9. package/dist/src/commands/download.d.ts.map +1 -1
  10. package/dist/src/commands/download.js +35 -22
  11. package/dist/src/commands/download.js.map +1 -1
  12. package/dist/src/commands/export.d.ts +9 -24
  13. package/dist/src/commands/export.d.ts.map +1 -1
  14. package/dist/src/commands/export.js +31 -59
  15. package/dist/src/commands/export.js.map +1 -1
  16. package/dist/src/commands/files.d.ts.map +1 -1
  17. package/dist/src/commands/files.js +91 -12
  18. package/dist/src/commands/files.js.map +1 -1
  19. package/dist/src/commands/folder.d.ts +3 -0
  20. package/dist/src/commands/folder.d.ts.map +1 -0
  21. package/dist/src/commands/folder.js +151 -0
  22. package/dist/src/commands/folder.js.map +1 -0
  23. package/dist/src/commands/mcp.d.ts.map +1 -1
  24. package/dist/src/commands/mcp.js +15 -9
  25. package/dist/src/commands/mcp.js.map +1 -1
  26. package/dist/src/commands/rehydrate.d.ts +1 -0
  27. package/dist/src/commands/rehydrate.d.ts.map +1 -1
  28. package/dist/src/commands/rehydrate.js +15 -7
  29. package/dist/src/commands/rehydrate.js.map +1 -1
  30. package/dist/src/commands/sui.d.ts +3 -0
  31. package/dist/src/commands/sui.d.ts.map +1 -0
  32. package/dist/src/commands/sui.js +64 -0
  33. package/dist/src/commands/sui.js.map +1 -0
  34. package/dist/src/commands/trash.d.ts +3 -0
  35. package/dist/src/commands/trash.d.ts.map +1 -0
  36. package/dist/src/commands/trash.js +109 -0
  37. package/dist/src/commands/trash.js.map +1 -0
  38. package/dist/src/commands/upload.d.ts +4 -0
  39. package/dist/src/commands/upload.d.ts.map +1 -1
  40. package/dist/src/commands/upload.js +82 -27
  41. package/dist/src/commands/upload.js.map +1 -1
  42. package/dist/src/commands/vault.d.ts.map +1 -1
  43. package/dist/src/commands/vault.js +2 -24
  44. package/dist/src/commands/vault.js.map +1 -1
  45. package/dist/src/commands/wallet.d.ts +3 -0
  46. package/dist/src/commands/wallet.d.ts.map +1 -0
  47. package/dist/src/commands/wallet.js +126 -0
  48. package/dist/src/commands/wallet.js.map +1 -0
  49. package/dist/src/commands/webhook.d.ts +3 -0
  50. package/dist/src/commands/webhook.d.ts.map +1 -0
  51. package/dist/src/commands/webhook.js +172 -0
  52. package/dist/src/commands/webhook.js.map +1 -0
  53. package/dist/src/config.d.ts +2 -2
  54. package/dist/src/config.d.ts.map +1 -1
  55. package/dist/src/config.js +2 -3
  56. package/dist/src/config.js.map +1 -1
  57. package/dist/src/index.js +19 -9
  58. package/dist/src/index.js.map +1 -1
  59. package/dist/src/lib/resolve.d.ts.map +1 -1
  60. package/dist/src/lib/resolve.js +4 -5
  61. package/dist/src/lib/resolve.js.map +1 -1
  62. package/dist/src/mcp/context.d.ts +1 -9
  63. package/dist/src/mcp/context.d.ts.map +1 -1
  64. package/dist/src/mcp/context.js +1 -2
  65. package/dist/src/mcp/context.js.map +1 -1
  66. package/dist/src/mcp/server.d.ts.map +1 -1
  67. package/dist/src/mcp/server.js +2 -59
  68. package/dist/src/mcp/server.js.map +1 -1
  69. package/dist/src/mcp/tools/account.d.ts.map +1 -1
  70. package/dist/src/mcp/tools/account.js +1 -3
  71. package/dist/src/mcp/tools/account.js.map +1 -1
  72. package/dist/src/mcp/tools/files.d.ts +2 -3
  73. package/dist/src/mcp/tools/files.d.ts.map +1 -1
  74. package/dist/src/mcp/tools/files.js +46 -49
  75. package/dist/src/mcp/tools/files.js.map +1 -1
  76. package/dist/src/mcp/tools/vaults.js +2 -2
  77. package/dist/src/mcp/tools/vaults.js.map +1 -1
  78. package/dist/src/seal.d.ts +16 -0
  79. package/dist/src/seal.d.ts.map +1 -1
  80. package/dist/src/seal.js +23 -0
  81. package/dist/src/seal.js.map +1 -1
  82. package/dist/src/tui/files-panel.d.ts +31 -2
  83. package/dist/src/tui/files-panel.d.ts.map +1 -1
  84. package/dist/src/tui/files-panel.js +119 -13
  85. package/dist/src/tui/files-panel.js.map +1 -1
  86. package/dist/src/tui/index.d.ts.map +1 -1
  87. package/dist/src/tui/index.js +252 -48
  88. package/dist/src/tui/index.js.map +1 -1
  89. package/dist/src/tui/overview.d.ts.map +1 -1
  90. package/dist/src/tui/overview.js +21 -9
  91. package/dist/src/tui/overview.js.map +1 -1
  92. package/dist/src/tui/trash-screen.d.ts +4 -0
  93. package/dist/src/tui/trash-screen.d.ts.map +1 -0
  94. package/dist/src/tui/trash-screen.js +190 -0
  95. package/dist/src/tui/trash-screen.js.map +1 -0
  96. package/dist/src/tui/vaults-panel.d.ts +8 -0
  97. package/dist/src/tui/vaults-panel.d.ts.map +1 -1
  98. package/dist/src/tui/vaults-panel.js +45 -6
  99. package/dist/src/tui/vaults-panel.js.map +1 -1
  100. package/dist/src/version.d.ts +2 -0
  101. package/dist/src/version.d.ts.map +1 -0
  102. package/dist/src/version.js +21 -0
  103. package/dist/src/version.js.map +1 -0
  104. package/package.json +3 -3
  105. package/src/__tests__/seal.test.ts +7 -54
  106. package/src/commands/account.ts +0 -1
  107. package/src/commands/auth.ts +7 -5
  108. package/src/commands/download.ts +38 -28
  109. package/src/commands/export.ts +37 -81
  110. package/src/commands/files.ts +95 -11
  111. package/src/commands/folder.ts +169 -0
  112. package/src/commands/mcp.ts +16 -10
  113. package/src/commands/rehydrate.ts +15 -8
  114. package/src/commands/sui.ts +69 -0
  115. package/src/commands/trash.ts +121 -0
  116. package/src/commands/upload.ts +98 -31
  117. package/src/commands/vault.ts +2 -23
  118. package/src/commands/wallet.ts +183 -0
  119. package/src/commands/webhook.ts +193 -0
  120. package/src/config.ts +3 -4
  121. package/src/index.ts +19 -10
  122. package/src/lib/resolve.ts +3 -4
  123. package/src/mcp/context.ts +1 -11
  124. package/src/mcp/server.ts +2 -70
  125. package/src/mcp/tools/account.ts +1 -3
  126. package/src/mcp/tools/files.ts +50 -63
  127. package/src/mcp/tools/vaults.ts +3 -3
  128. package/src/seal.ts +34 -1
  129. package/src/tui/files-panel.ts +140 -14
  130. package/src/tui/index.ts +264 -52
  131. package/src/tui/overview.ts +20 -9
  132. package/src/tui/trash-screen.ts +203 -0
  133. package/src/tui/vaults-panel.ts +55 -6
  134. package/src/version.ts +21 -0
  135. package/vitest.config.ts +1 -0
  136. package/dist/src/client.d.ts +0 -120
  137. package/dist/src/client.d.ts.map +0 -1
  138. package/dist/src/client.js +0 -152
  139. package/dist/src/client.js.map +0 -1
  140. package/dist/src/commands/decrypt.d.ts +0 -15
  141. package/dist/src/commands/decrypt.d.ts.map +0 -1
  142. package/dist/src/commands/decrypt.js +0 -224
  143. package/dist/src/commands/decrypt.js.map +0 -1
  144. package/dist/src/commands/encryption.d.ts +0 -3
  145. package/dist/src/commands/encryption.d.ts.map +0 -1
  146. package/dist/src/commands/encryption.js +0 -254
  147. package/dist/src/commands/encryption.js.map +0 -1
  148. package/dist/src/crypto.d.ts +0 -16
  149. package/dist/src/crypto.d.ts.map +0 -1
  150. package/dist/src/crypto.js +0 -95
  151. package/dist/src/crypto.js.map +0 -1
  152. package/dist/src/lib/keyring.d.ts +0 -4
  153. package/dist/src/lib/keyring.d.ts.map +0 -1
  154. package/dist/src/lib/keyring.js +0 -49
  155. package/dist/src/lib/keyring.js.map +0 -1
  156. package/src/__tests__/crypto.test.ts +0 -315
  157. package/src/commands/decrypt.ts +0 -276
  158. package/src/commands/encryption.ts +0 -305
  159. package/src/crypto.ts +0 -130
  160. package/src/lib/keyring.ts +0 -50
@@ -0,0 +1,183 @@
1
+ import type { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
4
+ import { cliConfig, getApiUrl, getApiKey } from '../config.js';
5
+ import { createTable, formatDate } from '../lib/output.js';
6
+
7
+ /**
8
+ * Lightweight fetch helper that uses the API key for auth.
9
+ * The SDK does not have a wallet resource, so we call the API directly.
10
+ */
11
+ async function walletFetch<T>(apiUrl: string, apiKey: string, path: string, init?: RequestInit): Promise<T> {
12
+ const url = `${apiUrl.replace(/\/$/, '')}${path}`;
13
+ const response = await fetch(url, {
14
+ ...init,
15
+ headers: {
16
+ 'Authorization': `Bearer ${apiKey}`,
17
+ 'Content-Type': 'application/json',
18
+ ...(init?.headers || {}),
19
+ },
20
+ });
21
+ if (!response.ok) {
22
+ const body = await response.json().catch(() => ({ error: response.statusText }));
23
+ throw new Error((body as Record<string, string>).error || `HTTP ${response.status}`);
24
+ }
25
+ return response.json() as Promise<T>;
26
+ }
27
+
28
+ interface WalletInfo {
29
+ id: string;
30
+ suiAddress: string;
31
+ walBalance: string;
32
+ walBalanceFormatted: string;
33
+ suiBalance: string;
34
+ suiBalanceFormatted: string;
35
+ usdcBalance?: string;
36
+ usdcBalanceFormatted?: string;
37
+ createdAt: string;
38
+ }
39
+
40
+ interface DepositInfo {
41
+ suiAddress: string;
42
+ network: string;
43
+ supportedTokens: string[];
44
+ }
45
+
46
+ interface PaymentEntry {
47
+ id: string;
48
+ fileId: string;
49
+ type: string;
50
+ walMist: string;
51
+ suiGasMist: string;
52
+ epochs: number;
53
+ createdAt: string;
54
+ }
55
+
56
+ interface PaymentsResponse {
57
+ payments: PaymentEntry[];
58
+ totalSpentWal: string;
59
+ totalSpentWalFormatted: string;
60
+ }
61
+
62
+ export function registerWalletCommands(program: Command) {
63
+ const wallet = program.command('wallet').description('Manage wallet and payments (PPU)');
64
+
65
+ // ── info ────────────────────────────────────────────────────────────
66
+ wallet.command('info')
67
+ .description('Show wallet balances and address')
68
+ .action(async () => {
69
+ const root = wallet.parent || wallet;
70
+ const apiUrl = getApiUrl(root.opts().apiUrl);
71
+ const apiKey = getApiKey(root.opts().apiKey);
72
+ const format = root.opts().format || cliConfig.get('outputFormat');
73
+
74
+ const w = await walletFetch<WalletInfo>(apiUrl, apiKey, '/api/wallet');
75
+
76
+ if (format === 'json') {
77
+ console.log(JSON.stringify(w, null, 2));
78
+ return;
79
+ }
80
+
81
+ console.log(chalk.bold('Wallet'));
82
+ console.log(` Address: ${w.suiAddress}`);
83
+ console.log(` WAL: ${w.walBalanceFormatted}`);
84
+ console.log(` SUI: ${w.suiBalanceFormatted}`);
85
+ if (w.usdcBalanceFormatted) {
86
+ console.log(` USDC: ${w.usdcBalanceFormatted}`);
87
+ }
88
+ console.log(` Created: ${new Date(w.createdAt).toLocaleString()}`);
89
+ });
90
+
91
+ // ── deposit ─────────────────────────────────────────────────────────
92
+ wallet.command('deposit')
93
+ .description('Show deposit address and supported tokens')
94
+ .action(async () => {
95
+ const root = wallet.parent || wallet;
96
+ const apiUrl = getApiUrl(root.opts().apiUrl);
97
+ const apiKey = getApiKey(root.opts().apiKey);
98
+ const format = root.opts().format || cliConfig.get('outputFormat');
99
+
100
+ const d = await walletFetch<DepositInfo>(apiUrl, apiKey, '/api/wallet/deposit-info');
101
+
102
+ if (format === 'json') {
103
+ console.log(JSON.stringify(d, null, 2));
104
+ return;
105
+ }
106
+
107
+ console.log(chalk.bold('Deposit Info'));
108
+ console.log(` Address: ${d.suiAddress}`);
109
+ console.log(` Network: ${d.network}`);
110
+ console.log(` Tokens: ${d.supportedTokens.join(', ')}`);
111
+ console.log('');
112
+ console.log(chalk.dim('Send WAL, SUI, or USDC to the address above on the Sui network.'));
113
+ });
114
+
115
+ // ── payments ────────────────────────────────────────────────────────
116
+ wallet.command('payments')
117
+ .description('List payment history')
118
+ .option('--limit <n>', 'Max results', '20')
119
+ .action(async (options) => {
120
+ const root = wallet.parent || wallet;
121
+ const apiUrl = getApiUrl(root.opts().apiUrl);
122
+ const apiKey = getApiKey(root.opts().apiKey);
123
+ const format = root.opts().format || cliConfig.get('outputFormat');
124
+
125
+ const data = await walletFetch<PaymentsResponse>(
126
+ apiUrl, apiKey,
127
+ `/api/wallet/payments?limit=${options.limit}`,
128
+ );
129
+
130
+ if (format === 'json') {
131
+ console.log(JSON.stringify(data, null, 2));
132
+ return;
133
+ }
134
+
135
+ if (data.payments.length === 0) {
136
+ console.log(chalk.dim('No payments found.'));
137
+ return;
138
+ }
139
+
140
+ const table = createTable(['Type', 'WAL', 'SUI Gas', 'Epochs', 'File', 'Date']);
141
+ for (const p of data.payments) {
142
+ table.push([
143
+ p.type,
144
+ p.walMist,
145
+ p.suiGasMist,
146
+ String(p.epochs),
147
+ chalk.dim(p.fileId.slice(0, 8) + '...'),
148
+ formatDate(p.createdAt),
149
+ ]);
150
+ }
151
+ console.log(table.toString());
152
+ console.log(chalk.dim(`\nTotal spent: ${data.totalSpentWalFormatted}`));
153
+ });
154
+
155
+ // ── generate ─────────────────────────────────────────────────────
156
+ wallet.command('generate')
157
+ .description('Generate a new Sui Ed25519 keypair for use with shared vaults (SEAL encryption)')
158
+ .action(async () => {
159
+ const root = wallet.parent || wallet;
160
+ const format = root.opts().format || cliConfig.get('outputFormat');
161
+
162
+ const keypair = Ed25519Keypair.generate();
163
+ const address = keypair.toSuiAddress();
164
+ const privateKey = keypair.getSecretKey(); // bech32 suiprivkey1... format
165
+
166
+ if (format === 'json') {
167
+ console.log(JSON.stringify({ address, privateKey }, null, 2));
168
+ return;
169
+ }
170
+
171
+ console.log(chalk.bold('Generated Sui Ed25519 Keypair'));
172
+ console.log(` Address: ${chalk.cyan(address)}`);
173
+ console.log(` Private key: ${chalk.yellow(privateKey)}`);
174
+ console.log('');
175
+ console.log(chalk.dim('Set TUSKYDP_SUI_PRIVATE_KEY=<private key> to use this keypair for shared vault encryption.'));
176
+ console.log(chalk.dim('Link the address to your Tusky account: tusky account link-sui ' + address));
177
+ });
178
+
179
+ // Default action for `tusky wallet` with no subcommand → run info
180
+ wallet.action(async () => {
181
+ await wallet.commands.find(c => c.name() === 'info')?.parseAsync([], { from: 'user' });
182
+ });
183
+ }
@@ -0,0 +1,193 @@
1
+ import type { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import { cliConfig } from '../config.js';
5
+ import { getSDKClientFromParent } from '../sdk.js';
6
+ import { createTable, formatDate, shortId } from '../lib/output.js';
7
+
8
+ // Import canonical event list from shared — keep in sync
9
+ import { WEBHOOK_EVENTS } from '@tuskydp/shared/constants.js';
10
+
11
+ export function registerWebhookCommands(program: Command) {
12
+ const webhook = program.command('webhook').description('Manage webhook endpoints');
13
+
14
+ // ── create ──────────────────────────────────────────────────────────
15
+ webhook.command('create <url>')
16
+ .description('Create a webhook endpoint')
17
+ .requiredOption('--events <events>', 'Comma-separated list of events to subscribe to')
18
+ .option('--description <text>', 'Webhook description')
19
+ .action(async (url: string, options) => {
20
+ const sdk = getSDKClientFromParent(webhook);
21
+ const events = options.events.split(',').map((e: string) => e.trim());
22
+
23
+ const created = await sdk.webhooks.create({
24
+ url,
25
+ events: events as any,
26
+ description: options.description,
27
+ });
28
+
29
+ console.log(chalk.green(`Webhook created: ${created.id}`));
30
+ console.log(`URL: ${created.url}`);
31
+ console.log(`Events: ${created.events.join(', ')}`);
32
+ if (created.secret) {
33
+ console.log(chalk.yellow.bold(`Secret: ${created.secret}`));
34
+ console.log(chalk.yellow(' Save this secret — it will not be shown again.'));
35
+ }
36
+ });
37
+
38
+ // ── list ────────────────────────────────────────────────────────────
39
+ webhook.command('list')
40
+ .description('List webhook endpoints')
41
+ .action(async () => {
42
+ const sdk = getSDKClientFromParent(webhook);
43
+ const root = webhook.parent || webhook;
44
+ const format = root.opts().format || cliConfig.get('outputFormat');
45
+ const webhooks = await sdk.webhooks.list();
46
+
47
+ if (format === 'json') {
48
+ console.log(JSON.stringify(webhooks, null, 2));
49
+ return;
50
+ }
51
+
52
+ if (webhooks.length === 0) {
53
+ console.log(chalk.dim('No webhook endpoints found.'));
54
+ return;
55
+ }
56
+
57
+ const table = createTable(['URL', 'Events', 'Active', 'Failures', 'ID']);
58
+ for (const w of webhooks) {
59
+ table.push([
60
+ w.url.length > 40 ? w.url.slice(0, 39) + '...' : w.url,
61
+ String(w.events.length) + ' event(s)',
62
+ w.active ? chalk.green('Yes') : chalk.red('No'),
63
+ w.failureCount > 0 ? chalk.yellow(String(w.failureCount)) : '0',
64
+ chalk.dim(shortId(w.id)),
65
+ ]);
66
+ }
67
+ console.log(table.toString());
68
+ });
69
+
70
+ // ── info ────────────────────────────────────────────────────────────
71
+ webhook.command('info <webhook-id>')
72
+ .description('Show webhook endpoint details')
73
+ .action(async (webhookId: string) => {
74
+ const sdk = getSDKClientFromParent(webhook);
75
+ const root = webhook.parent || webhook;
76
+ const format = root.opts().format || cliConfig.get('outputFormat');
77
+ const w = await sdk.webhooks.get(webhookId);
78
+
79
+ if (format === 'json') {
80
+ console.log(JSON.stringify(w, null, 2));
81
+ return;
82
+ }
83
+
84
+ console.log(`URL: ${w.url}`);
85
+ console.log(`Active: ${w.active ? chalk.green('Yes') : chalk.red('No')}`);
86
+ console.log(`Events: ${w.events.join(', ')}`);
87
+ console.log(`Description: ${w.description || chalk.dim('(none)')}`);
88
+ console.log(`Failures: ${w.failureCount}`);
89
+ if (w.lastDeliveryAt) {
90
+ console.log(`Last Delivery: ${formatDate(w.lastDeliveryAt)} (${w.lastDeliveryStatus})`);
91
+ }
92
+ console.log(`Created: ${new Date(w.createdAt).toLocaleString()}`);
93
+ console.log(`ID: ${w.id}`);
94
+ });
95
+
96
+ // ── update ──────────────────────────────────────────────────────────
97
+ webhook.command('update <webhook-id>')
98
+ .description('Update a webhook endpoint')
99
+ .option('--url <url>', 'New webhook URL')
100
+ .option('--events <events>', 'Comma-separated list of events')
101
+ .option('--active <bool>', 'Enable or disable (true/false)')
102
+ .option('--description <text>', 'New description')
103
+ .action(async (webhookId: string, options) => {
104
+ const sdk = getSDKClientFromParent(webhook);
105
+
106
+ const params: { url?: string; events?: any[]; active?: boolean; description?: string } = {};
107
+ if (options.url) params.url = options.url;
108
+ if (options.events) params.events = options.events.split(',').map((e: string) => e.trim());
109
+ if (options.active !== undefined) params.active = options.active === 'true';
110
+ if (options.description) params.description = options.description;
111
+
112
+ if (Object.keys(params).length === 0) {
113
+ console.error(chalk.red('Provide at least one option to update (--url, --events, --active, --description).'));
114
+ return;
115
+ }
116
+
117
+ await sdk.webhooks.update(webhookId, params);
118
+ console.log(chalk.green('Webhook updated.'));
119
+ });
120
+
121
+ // ── delete ──────────────────────────────────────────────────────────
122
+ webhook.command('delete <webhook-id>')
123
+ .description('Delete a webhook endpoint')
124
+ .option('--force', 'Skip confirmation prompt')
125
+ .action(async (webhookId: string, options) => {
126
+ const sdk = getSDKClientFromParent(webhook);
127
+
128
+ if (!options.force) {
129
+ const answers = await inquirer.prompt([{
130
+ type: 'confirm',
131
+ name: 'confirm',
132
+ message: 'Delete this webhook endpoint?',
133
+ default: false,
134
+ }]);
135
+ if (!answers.confirm) return;
136
+ }
137
+
138
+ await sdk.webhooks.delete(webhookId);
139
+ console.log(chalk.green('Webhook deleted.'));
140
+ });
141
+
142
+ // ── test ────────────────────────────────────────────────────────────
143
+ webhook.command('test <webhook-id>')
144
+ .description('Send a test delivery to the webhook')
145
+ .action(async (webhookId: string) => {
146
+ const sdk = getSDKClientFromParent(webhook);
147
+ const delivery = await sdk.webhooks.test(webhookId);
148
+ console.log(chalk.green('Test delivery sent.'));
149
+ console.log(`Delivery ID: ${delivery.id}`);
150
+ console.log(`Event: ${delivery.event}`);
151
+ console.log(`Status: ${delivery.status}`);
152
+ });
153
+
154
+ // ── deliveries ──────────────────────────────────────────────────────
155
+ webhook.command('deliveries <webhook-id>')
156
+ .description('List recent webhook deliveries')
157
+ .option('--limit <n>', 'Max results', '20')
158
+ .action(async (webhookId: string, options) => {
159
+ const sdk = getSDKClientFromParent(webhook);
160
+ const root = webhook.parent || webhook;
161
+ const format = root.opts().format || cliConfig.get('outputFormat');
162
+ const deliveries = await sdk.webhooks.listDeliveries(webhookId, { limit: parseInt(options.limit) });
163
+
164
+ if (format === 'json') {
165
+ console.log(JSON.stringify(deliveries, null, 2));
166
+ return;
167
+ }
168
+
169
+ if (deliveries.length === 0) {
170
+ console.log(chalk.dim('No deliveries found.'));
171
+ return;
172
+ }
173
+
174
+ const table = createTable(['Event', 'Status', 'HTTP', 'Attempts', 'Created', 'ID']);
175
+ const statusColors: Record<string, typeof chalk.green> = {
176
+ success: chalk.green,
177
+ failed: chalk.red,
178
+ pending: chalk.yellow,
179
+ };
180
+ for (const d of deliveries) {
181
+ const sc = statusColors[d.status] || chalk.white;
182
+ table.push([
183
+ d.event,
184
+ sc(d.status),
185
+ d.httpStatus ? String(d.httpStatus) : '-',
186
+ `${d.attempts}/${d.maxAttempts}`,
187
+ formatDate(d.createdAt),
188
+ chalk.dim(shortId(d.id)),
189
+ ]);
190
+ }
191
+ console.log(table.toString());
192
+ });
193
+ }
package/src/config.ts CHANGED
@@ -5,7 +5,7 @@ export interface TuskyDPConfig {
5
5
  apiKey?: string;
6
6
  defaultVault?: string;
7
7
  outputFormat: 'table' | 'json' | 'plain';
8
- encryptionEnabled: boolean;
8
+ suiPrivateKey?: string;
9
9
  }
10
10
 
11
11
  export const cliConfig = new Conf<TuskyDPConfig>({
@@ -13,7 +13,6 @@ export const cliConfig = new Conf<TuskyDPConfig>({
13
13
  defaults: {
14
14
  apiUrl: 'https://api.tusky.ai',
15
15
  outputFormat: 'table',
16
- encryptionEnabled: true,
17
16
  },
18
17
  });
19
18
 
@@ -39,8 +38,8 @@ export function getOutputFormat(override?: string): 'table' | 'json' | 'plain' {
39
38
 
40
39
  /**
41
40
  * Get the Sui private key for SEAL operations on shared vaults.
42
- * Only read from env var (never persisted to config for security).
41
+ * Priority: TUSKYDP_SUI_PRIVATE_KEY env var > stored config key.
43
42
  */
44
43
  export function getSuiPrivateKey(): string | null {
45
- return process.env.TUSKYDP_SUI_PRIVATE_KEY || null;
44
+ return process.env.TUSKYDP_SUI_PRIVATE_KEY || cliConfig.get('suiPrivateKey') || null;
46
45
  }
package/src/index.ts CHANGED
@@ -1,28 +1,28 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { registerAuthCommands } from './commands/auth.js';
4
- import { registerEncryptionCommands } from './commands/encryption.js';
5
4
  import { registerVaultCommands } from './commands/vault.js';
6
5
  import { registerFileCommands } from './commands/files.js';
7
6
  import { registerAccountCommands } from './commands/account.js';
7
+ import { registerFolderCommands } from './commands/folder.js';
8
+ import { registerTrashCommands } from './commands/trash.js';
9
+ import { registerWebhookCommands } from './commands/webhook.js';
10
+ import { registerWalletCommands } from './commands/wallet.js';
8
11
  import { uploadCommand } from './commands/upload.js';
9
12
  import { downloadCommand } from './commands/download.js';
10
13
  import { rehydrateCommand } from './commands/rehydrate.js';
11
14
  import { registerTuiCommand } from './commands/tui.js';
12
15
  import { registerMcpCommands } from './commands/mcp.js';
16
+ import { registerSuiCommands } from './commands/sui.js';
13
17
  import { registerExportCommand } from './commands/export.js';
14
- import { registerDecryptCommand } from './commands/decrypt.js';
15
-
16
- import { createRequire } from 'module';
17
- const __require = createRequire(import.meta.url);
18
- const { version: VERSION } = __require('../../package.json');
18
+ import { CLI_VERSION } from './version.js';
19
19
 
20
20
  const program = new Command();
21
21
 
22
22
  program
23
23
  .name('tusky')
24
24
  .description('Tusky — Encrypted decentralized storage for developers, apps, and agents')
25
- .version(VERSION)
25
+ .version(CLI_VERSION)
26
26
  .option('--api-key <key>', 'Override API key')
27
27
  .option('--api-url <url>', 'Override API URL')
28
28
  .option('--format <fmt>', 'Output format: table, json, plain', 'table')
@@ -38,20 +38,27 @@ program
38
38
  });
39
39
 
40
40
  registerAuthCommands(program);
41
- registerEncryptionCommands(program);
42
41
  registerVaultCommands(program);
43
42
  registerFileCommands(program);
43
+ registerFolderCommands(program);
44
+ registerTrashCommands(program);
45
+ registerWebhookCommands(program);
46
+ registerWalletCommands(program);
44
47
  registerAccountCommands(program);
45
48
  registerTuiCommand(program);
46
49
  registerMcpCommands(program);
50
+ registerSuiCommands(program);
47
51
  registerExportCommand(program);
48
- registerDecryptCommand(program);
49
52
 
50
53
  // Direct shortcuts for common operations
51
- program.command('upload <paths...>')
54
+ program.command('upload [paths...]')
52
55
  .description('Upload files')
53
56
  .option('--vault <vault>', 'Target vault')
57
+ .option('--folder <folder-id>', 'Target folder ID within the vault')
54
58
  .option('--recursive', 'Upload directory contents')
59
+ .option('--content <text>', 'Upload inline text content instead of a file path')
60
+ .option('--stdin', 'Read file content from stdin')
61
+ .option('--name <filename>', 'File name to use (required with --content or --stdin)')
55
62
  .action(async (paths: string[], options) => {
56
63
  await uploadCommand(paths, options, program);
57
64
  });
@@ -59,6 +66,7 @@ program.command('upload <paths...>')
59
66
  program.command('download <file-id>')
60
67
  .description('Download a file')
61
68
  .option('--output <path>', 'Output path')
69
+ .option('--stdout', 'Write file content to stdout (for sandboxed agents without filesystem access)')
62
70
  .action(async (fileId: string, options) => {
63
71
  await downloadCommand(fileId, options, program);
64
72
  });
@@ -66,6 +74,7 @@ program.command('download <file-id>')
66
74
  program.command('rehydrate <blob-id>')
67
75
  .description('Download a file directly from Walrus by blob ID')
68
76
  .option('--output <path>', 'Output file path')
77
+ .option('--stdout', 'Write blob content to stdout (for sandboxed agents without filesystem access)')
69
78
  .action(async (blobId: string, options) => {
70
79
  await rehydrateCommand(blobId, options, program);
71
80
  });
@@ -7,12 +7,11 @@ export async function resolveVault(sdk: TuskyClient, vaultRef?: string): Promise
7
7
  const defaultVault = cliConfig.get('defaultVault');
8
8
  if (defaultVault) return defaultVault;
9
9
 
10
- // Fall back to the user's default vault
10
+ // Fall back to the first vault
11
11
  const vaults = await sdk.vaults.list();
12
- const defaultV = vaults.find((v) => v.isDefault);
13
- if (defaultV) return defaultV.id;
12
+ if (vaults.length > 0) return vaults[0].id;
14
13
 
15
- throw new Error('No default vault found. Create one with: tusky vault create <name>');
14
+ throw new Error('No vault found. Create one with: tusky vault create <name>');
16
15
  }
17
16
 
18
17
  // If it looks like a UUID, use it directly
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Shared context for MCP tool handlers.
3
3
  *
4
- * Holds the authenticated SDK client, the unlocked master key for
5
- * private vault encryption, and the Sui keypair for shared vault
4
+ * Holds the authenticated SDK client and the Sui keypair for shared vault
6
5
  * SEAL encryption/decryption.
7
6
  */
8
7
 
@@ -13,15 +12,6 @@ export interface McpContext {
13
12
  /** Authenticated Tusky SDK client. */
14
13
  sdk: TuskyClient;
15
14
 
16
- /**
17
- * Returns the in-memory master key, or null if encryption was not
18
- * unlocked (i.e. TUSKYDP_PASSWORD was not provided or setup is incomplete).
19
- */
20
- getMasterKey(): Buffer | null;
21
-
22
- /** True when the master key is available for encrypt/decrypt operations. */
23
- isEncryptionReady(): boolean;
24
-
25
15
  /**
26
16
  * Returns the Sui Ed25519 keypair for SEAL operations, or null if
27
17
  * TUSKYDP_SUI_PRIVATE_KEY was not provided.
package/src/mcp/server.ts CHANGED
@@ -9,12 +9,6 @@
9
9
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
10
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
11
  import { TuskyClient, TuskyError } from '@tuskydp/sdk';
12
- import {
13
- deriveMasterKey,
14
- verifyPassphrase,
15
- unwrapMasterKey,
16
- } from '../crypto.js';
17
- import { loadMasterKey } from '../lib/keyring.js';
18
12
  import { getSuiKeypair } from '../seal.js';
19
13
  import type { McpContext } from './context.js';
20
14
 
@@ -25,58 +19,7 @@ import { registerFolderTools } from './tools/folders.js';
25
19
  import { registerFileTools } from './tools/files.js';
26
20
  import { registerTrashTools } from './tools/trash.js';
27
21
  import { registerSharedVaultTools } from './tools/sharedVaults.js';
28
-
29
- // ---------------------------------------------------------------------------
30
- // Encryption bootstrap
31
- // ---------------------------------------------------------------------------
32
-
33
- /**
34
- * Attempt to unlock the master key using (in order):
35
- * 1. TUSKYDP_PASSWORD env var → derive wrapping key → unwrap master key
36
- * 2. Existing session file (~/.tusky/session.enc)
37
- *
38
- * Returns the master key Buffer, or null if neither method works.
39
- */
40
- async function unlockMasterKey(sdk: TuskyClient): Promise<Buffer | null> {
41
- const password = process.env.TUSKYDP_PASSWORD;
42
-
43
- if (password) {
44
- try {
45
- const params = await sdk.account.getEncryptionParams();
46
- if (!params.setupComplete) {
47
- console.error('[tusky-mcp] Encryption not set up on this account. Skipping encryption unlock.');
48
- return null;
49
- }
50
-
51
- const salt = Buffer.from(params.salt!, 'base64');
52
- const verifier = Buffer.from(params.verifier!, 'base64');
53
- const wrappingKey = deriveMasterKey(password, salt);
54
-
55
- if (!verifyPassphrase(wrappingKey, verifier)) {
56
- console.error('[tusky-mcp] TUSKYDP_PASSWORD is incorrect. Encryption will be unavailable.');
57
- return null;
58
- }
59
-
60
- if (params.encryptedMasterKey) {
61
- return unwrapMasterKey(Buffer.from(params.encryptedMasterKey, 'base64'), wrappingKey);
62
- }
63
- // Legacy accounts where wrapping key IS the master key
64
- return wrappingKey;
65
- } catch (err: any) {
66
- console.error(`[tusky-mcp] Failed to unlock encryption: ${err.message}`);
67
- return null;
68
- }
69
- }
70
-
71
- // Fallback: try existing session file
72
- const sessionKey = loadMasterKey();
73
- if (sessionKey) {
74
- console.error('[tusky-mcp] Using encryption key from existing session.');
75
- return sessionKey;
76
- }
77
-
78
- return null;
79
- }
22
+ import { CLI_VERSION } from '../version.js';
80
23
 
81
24
  // ---------------------------------------------------------------------------
82
25
  // Server lifecycle
@@ -106,15 +49,6 @@ export async function startMcpServer(options: McpServerOptions): Promise<void> {
106
49
  process.exit(1);
107
50
  }
108
51
 
109
- // Unlock encryption (best effort)
110
- const masterKey = await unlockMasterKey(sdk);
111
- if (masterKey) {
112
- console.error('[tusky-mcp] Encryption unlocked — private vault operations are available.');
113
- } else {
114
- console.error('[tusky-mcp] Encryption not unlocked — private vault encrypt/decrypt unavailable.');
115
- console.error('[tusky-mcp] Set TUSKYDP_PASSWORD env var or run `tusky encryption unlock` first.');
116
- }
117
-
118
52
  // Load Sui keypair for SEAL shared vault operations (best effort)
119
53
  const suiKeypair = getSuiKeypair();
120
54
  if (suiKeypair) {
@@ -126,8 +60,6 @@ export async function startMcpServer(options: McpServerOptions): Promise<void> {
126
60
  // Build context
127
61
  const ctx: McpContext = {
128
62
  sdk,
129
- getMasterKey: () => masterKey,
130
- isEncryptionReady: () => masterKey !== null,
131
63
  getSuiKeypair: () => suiKeypair,
132
64
  isSealReady: () => suiKeypair !== null,
133
65
  };
@@ -135,7 +67,7 @@ export async function startMcpServer(options: McpServerOptions): Promise<void> {
135
67
  // Create MCP server
136
68
  const server = new McpServer({
137
69
  name: 'tusky',
138
- version: '0.1.0',
70
+ version: CLI_VERSION,
139
71
  });
140
72
 
141
73
  // Register all tools
@@ -9,7 +9,7 @@ import { wrapToolError } from './helpers.js';
9
9
  export function registerAccountTools(server: McpServer, ctx: McpContext) {
10
10
  server.tool(
11
11
  'tusky_account_info',
12
- 'Get account information including email, plan, storage usage, and encryption status',
12
+ 'Get account information including email, plan, and storage usage',
13
13
  {},
14
14
  async () => {
15
15
  try {
@@ -24,8 +24,6 @@ export function registerAccountTools(server: McpServer, ctx: McpContext) {
24
24
  storageLimit: account.storageLimitFormatted,
25
25
  storageUsedBytes: account.storageUsedBytes,
26
26
  storageLimitBytes: account.storageLimitBytes,
27
- encryptionSetup: account.encryptionSetupComplete,
28
- encryptionUnlocked: ctx.isEncryptionReady(),
29
27
  createdAt: account.createdAt,
30
28
  };
31
29