@tuskydp/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 (153) hide show
  1. package/dist/bin/tuskydp.d.ts +3 -0
  2. package/dist/bin/tuskydp.d.ts.map +1 -0
  3. package/dist/bin/tuskydp.js +3 -0
  4. package/dist/bin/tuskydp.js.map +1 -0
  5. package/dist/src/client.d.ts +120 -0
  6. package/dist/src/client.d.ts.map +1 -0
  7. package/dist/src/client.js +152 -0
  8. package/dist/src/client.js.map +1 -0
  9. package/dist/src/commands/account.d.ts +3 -0
  10. package/dist/src/commands/account.d.ts.map +1 -0
  11. package/dist/src/commands/account.js +66 -0
  12. package/dist/src/commands/account.js.map +1 -0
  13. package/dist/src/commands/auth.d.ts +3 -0
  14. package/dist/src/commands/auth.d.ts.map +1 -0
  15. package/dist/src/commands/auth.js +171 -0
  16. package/dist/src/commands/auth.js.map +1 -0
  17. package/dist/src/commands/decrypt.d.ts +15 -0
  18. package/dist/src/commands/decrypt.d.ts.map +1 -0
  19. package/dist/src/commands/decrypt.js +224 -0
  20. package/dist/src/commands/decrypt.js.map +1 -0
  21. package/dist/src/commands/download.d.ts +5 -0
  22. package/dist/src/commands/download.d.ts.map +1 -0
  23. package/dist/src/commands/download.js +67 -0
  24. package/dist/src/commands/download.js.map +1 -0
  25. package/dist/src/commands/encryption.d.ts +3 -0
  26. package/dist/src/commands/encryption.d.ts.map +1 -0
  27. package/dist/src/commands/encryption.js +254 -0
  28. package/dist/src/commands/encryption.js.map +1 -0
  29. package/dist/src/commands/export.d.ts +70 -0
  30. package/dist/src/commands/export.d.ts.map +1 -0
  31. package/dist/src/commands/export.js +178 -0
  32. package/dist/src/commands/export.js.map +1 -0
  33. package/dist/src/commands/files.d.ts +3 -0
  34. package/dist/src/commands/files.d.ts.map +1 -0
  35. package/dist/src/commands/files.js +179 -0
  36. package/dist/src/commands/files.js.map +1 -0
  37. package/dist/src/commands/mcp.d.ts +8 -0
  38. package/dist/src/commands/mcp.d.ts.map +1 -0
  39. package/dist/src/commands/mcp.js +178 -0
  40. package/dist/src/commands/mcp.js.map +1 -0
  41. package/dist/src/commands/rehydrate.d.ts +5 -0
  42. package/dist/src/commands/rehydrate.d.ts.map +1 -0
  43. package/dist/src/commands/rehydrate.js +27 -0
  44. package/dist/src/commands/rehydrate.js.map +1 -0
  45. package/dist/src/commands/tui.d.ts +3 -0
  46. package/dist/src/commands/tui.d.ts.map +1 -0
  47. package/dist/src/commands/tui.js +10 -0
  48. package/dist/src/commands/tui.js.map +1 -0
  49. package/dist/src/commands/upload.d.ts +6 -0
  50. package/dist/src/commands/upload.d.ts.map +1 -0
  51. package/dist/src/commands/upload.js +121 -0
  52. package/dist/src/commands/upload.js.map +1 -0
  53. package/dist/src/commands/vault.d.ts +3 -0
  54. package/dist/src/commands/vault.d.ts.map +1 -0
  55. package/dist/src/commands/vault.js +118 -0
  56. package/dist/src/commands/vault.js.map +1 -0
  57. package/dist/src/config.d.ts +15 -0
  58. package/dist/src/config.d.ts.map +1 -0
  59. package/dist/src/config.js +26 -0
  60. package/dist/src/config.js.map +1 -0
  61. package/dist/src/crypto.d.ts +16 -0
  62. package/dist/src/crypto.d.ts.map +1 -0
  63. package/dist/src/crypto.js +95 -0
  64. package/dist/src/crypto.js.map +1 -0
  65. package/dist/src/index.d.ts +2 -0
  66. package/dist/src/index.d.ts.map +1 -0
  67. package/dist/src/index.js +59 -0
  68. package/dist/src/index.js.map +1 -0
  69. package/dist/src/lib/keyring.d.ts +4 -0
  70. package/dist/src/lib/keyring.d.ts.map +1 -0
  71. package/dist/src/lib/keyring.js +51 -0
  72. package/dist/src/lib/keyring.js.map +1 -0
  73. package/dist/src/lib/output.d.ts +6 -0
  74. package/dist/src/lib/output.d.ts.map +1 -0
  75. package/dist/src/lib/output.js +37 -0
  76. package/dist/src/lib/output.js.map +1 -0
  77. package/dist/src/lib/progress.d.ts +2 -0
  78. package/dist/src/lib/progress.d.ts.map +1 -0
  79. package/dist/src/lib/progress.js +5 -0
  80. package/dist/src/lib/progress.js.map +1 -0
  81. package/dist/src/lib/resolve.d.ts +3 -0
  82. package/dist/src/lib/resolve.d.ts.map +1 -0
  83. package/dist/src/lib/resolve.js +25 -0
  84. package/dist/src/lib/resolve.js.map +1 -0
  85. package/dist/src/mcp/context.d.ts +19 -0
  86. package/dist/src/mcp/context.d.ts.map +1 -0
  87. package/dist/src/mcp/context.js +8 -0
  88. package/dist/src/mcp/context.js.map +1 -0
  89. package/dist/src/mcp/server.d.ts +13 -0
  90. package/dist/src/mcp/server.d.ts.map +1 -0
  91. package/dist/src/mcp/server.js +113 -0
  92. package/dist/src/mcp/server.js.map +1 -0
  93. package/dist/src/mcp/tools/account.d.ts +7 -0
  94. package/dist/src/mcp/tools/account.d.ts.map +1 -0
  95. package/dist/src/mcp/tools/account.js +31 -0
  96. package/dist/src/mcp/tools/account.js.map +1 -0
  97. package/dist/src/mcp/tools/files.d.ts +10 -0
  98. package/dist/src/mcp/tools/files.d.ts.map +1 -0
  99. package/dist/src/mcp/tools/files.js +310 -0
  100. package/dist/src/mcp/tools/files.js.map +1 -0
  101. package/dist/src/mcp/tools/folders.d.ts +7 -0
  102. package/dist/src/mcp/tools/folders.d.ts.map +1 -0
  103. package/dist/src/mcp/tools/folders.js +67 -0
  104. package/dist/src/mcp/tools/folders.js.map +1 -0
  105. package/dist/src/mcp/tools/helpers.d.ts +15 -0
  106. package/dist/src/mcp/tools/helpers.d.ts.map +1 -0
  107. package/dist/src/mcp/tools/helpers.js +25 -0
  108. package/dist/src/mcp/tools/helpers.js.map +1 -0
  109. package/dist/src/mcp/tools/trash.d.ts +7 -0
  110. package/dist/src/mcp/tools/trash.d.ts.map +1 -0
  111. package/dist/src/mcp/tools/trash.js +46 -0
  112. package/dist/src/mcp/tools/trash.js.map +1 -0
  113. package/dist/src/mcp/tools/vaults.d.ts +7 -0
  114. package/dist/src/mcp/tools/vaults.d.ts.map +1 -0
  115. package/dist/src/mcp/tools/vaults.js +85 -0
  116. package/dist/src/mcp/tools/vaults.js.map +1 -0
  117. package/dist/src/sdk.d.ts +53 -0
  118. package/dist/src/sdk.d.ts.map +1 -0
  119. package/dist/src/sdk.js +88 -0
  120. package/dist/src/sdk.js.map +1 -0
  121. package/dist/src/tui/auth-screen.d.ts +6 -0
  122. package/dist/src/tui/auth-screen.d.ts.map +1 -0
  123. package/dist/src/tui/auth-screen.js +165 -0
  124. package/dist/src/tui/auth-screen.js.map +1 -0
  125. package/dist/src/tui/dialogs.d.ts +10 -0
  126. package/dist/src/tui/dialogs.d.ts.map +1 -0
  127. package/dist/src/tui/dialogs.js +303 -0
  128. package/dist/src/tui/dialogs.js.map +1 -0
  129. package/dist/src/tui/files-panel.d.ts +34 -0
  130. package/dist/src/tui/files-panel.d.ts.map +1 -0
  131. package/dist/src/tui/files-panel.js +135 -0
  132. package/dist/src/tui/files-panel.js.map +1 -0
  133. package/dist/src/tui/helpers.d.ts +38 -0
  134. package/dist/src/tui/helpers.d.ts.map +1 -0
  135. package/dist/src/tui/helpers.js +187 -0
  136. package/dist/src/tui/helpers.js.map +1 -0
  137. package/dist/src/tui/index.d.ts +2 -0
  138. package/dist/src/tui/index.d.ts.map +1 -0
  139. package/dist/src/tui/index.js +382 -0
  140. package/dist/src/tui/index.js.map +1 -0
  141. package/dist/src/tui/overview.d.ts +4 -0
  142. package/dist/src/tui/overview.d.ts.map +1 -0
  143. package/dist/src/tui/overview.js +136 -0
  144. package/dist/src/tui/overview.js.map +1 -0
  145. package/dist/src/tui/status-bar.d.ts +18 -0
  146. package/dist/src/tui/status-bar.d.ts.map +1 -0
  147. package/dist/src/tui/status-bar.js +54 -0
  148. package/dist/src/tui/status-bar.js.map +1 -0
  149. package/dist/src/tui/vaults-panel.d.ts +26 -0
  150. package/dist/src/tui/vaults-panel.d.ts.map +1 -0
  151. package/dist/src/tui/vaults-panel.js +118 -0
  152. package/dist/src/tui/vaults-panel.js.map +1 -0
  153. package/package.json +42 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * SDK client factory for CLI commands + AuthClient for pre-auth flows.
3
+ *
4
+ * The TuskyClient SDK requires an API key, so it can't be used for
5
+ * register/login/createApiKeyWithJwt flows. AuthClient handles those
6
+ * with raw fetch calls.
7
+ */
8
+ import { TuskyClient } from '@tuskydp/sdk';
9
+ import type { Command } from 'commander';
10
+ /** Create a TuskyClient from Commander options (uses stored API key). */
11
+ export declare function getSDKClient(program: Command): TuskyClient;
12
+ /**
13
+ * Create a TuskyClient from Commander options, resolved from a sub-command
14
+ * whose parent holds the global options.
15
+ */
16
+ export declare function getSDKClientFromParent(program: Command): TuskyClient;
17
+ /** Create a TuskyClient from explicit url + key (used in TUI). */
18
+ export declare function createSDKClient(apiUrl: string, apiKey: string): TuskyClient;
19
+ /**
20
+ * Lightweight client for unauthenticated / JWT-authenticated API calls.
21
+ * Used for register, login, createApiKeyWithJwt, and getPlans.
22
+ */
23
+ export declare class AuthClient {
24
+ private apiUrl;
25
+ constructor(apiUrl: string);
26
+ private fetchJSON;
27
+ register(email: string, password: string, displayName?: string): Promise<{
28
+ user: {
29
+ email: string;
30
+ id: string;
31
+ };
32
+ accessToken: string;
33
+ refreshToken: string;
34
+ }>;
35
+ login(email: string, password: string): Promise<{
36
+ user: {
37
+ email: string;
38
+ id: string;
39
+ };
40
+ accessToken: string;
41
+ refreshToken: string;
42
+ }>;
43
+ createApiKeyWithJwt(accessToken: string, name: string): Promise<{
44
+ apiKey: string;
45
+ name: string;
46
+ keyPrefix: string;
47
+ }>;
48
+ getPlans(): Promise<{
49
+ plans: any[];
50
+ }>;
51
+ }
52
+ export { TuskyError } from '@tuskydp/sdk';
53
+ //# sourceMappingURL=sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/sdk.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAc,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAI1D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAKpE;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAE3E;AAMD;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;YAIZ,SAAS;IASjB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;cACpC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE;qBAAe,MAAM;sBAAgB,MAAM;;IAUlG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;cACX;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE;qBAAe,MAAM;sBAAgB,MAAM;;IAUlG,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;gBACzB,MAAM;cAAQ,MAAM;mBAAa,MAAM;;IAcnE,QAAQ;eACmB,GAAG,EAAE;;CAQvC;AAGD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * SDK client factory for CLI commands + AuthClient for pre-auth flows.
3
+ *
4
+ * The TuskyClient SDK requires an API key, so it can't be used for
5
+ * register/login/createApiKeyWithJwt flows. AuthClient handles those
6
+ * with raw fetch calls.
7
+ */
8
+ import { TuskyClient, TuskyError } from '@tuskydp/sdk';
9
+ import { getApiUrl, getApiKey } from './config.js';
10
+ const CLI_VERSION = '0.1.0';
11
+ // ---------------------------------------------------------------------------
12
+ // SDK client factory (for authenticated commands)
13
+ // ---------------------------------------------------------------------------
14
+ /** Create a TuskyClient from Commander options (uses stored API key). */
15
+ export function getSDKClient(program) {
16
+ const apiUrl = getApiUrl(program.opts().apiUrl);
17
+ const apiKey = getApiKey(program.opts().apiKey);
18
+ return new TuskyClient({ apiKey, baseUrl: apiUrl });
19
+ }
20
+ /**
21
+ * Create a TuskyClient from Commander options, resolved from a sub-command
22
+ * whose parent holds the global options.
23
+ */
24
+ export function getSDKClientFromParent(program) {
25
+ const root = program.parent || program;
26
+ const apiUrl = getApiUrl(root.opts().apiUrl);
27
+ const apiKey = getApiKey(root.opts().apiKey);
28
+ return new TuskyClient({ apiKey, baseUrl: apiUrl });
29
+ }
30
+ /** Create a TuskyClient from explicit url + key (used in TUI). */
31
+ export function createSDKClient(apiUrl, apiKey) {
32
+ return new TuskyClient({ apiKey, baseUrl: apiUrl });
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // AuthClient — pre-authentication flows (no API key needed)
36
+ // ---------------------------------------------------------------------------
37
+ /**
38
+ * Lightweight client for unauthenticated / JWT-authenticated API calls.
39
+ * Used for register, login, createApiKeyWithJwt, and getPlans.
40
+ */
41
+ export class AuthClient {
42
+ apiUrl;
43
+ constructor(apiUrl) {
44
+ this.apiUrl = apiUrl.replace(/\/$/, '');
45
+ }
46
+ async fetchJSON(url, init) {
47
+ const response = await fetch(url, init);
48
+ if (!response.ok) {
49
+ const body = await response.json().catch(() => ({ error: response.statusText }));
50
+ throw new TuskyError(response.status, body.error || 'Unknown error');
51
+ }
52
+ return response.json();
53
+ }
54
+ async register(email, password, displayName) {
55
+ return this.fetchJSON(`${this.apiUrl}/api/auth/register`, {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/json', 'User-Agent': `tusky-cli/${CLI_VERSION}` },
58
+ body: JSON.stringify({ email, password, displayName }),
59
+ });
60
+ }
61
+ async login(email, password) {
62
+ return this.fetchJSON(`${this.apiUrl}/api/auth/login`, {
63
+ method: 'POST',
64
+ headers: { 'Content-Type': 'application/json', 'User-Agent': `tusky-cli/${CLI_VERSION}` },
65
+ body: JSON.stringify({ email, password }),
66
+ });
67
+ }
68
+ async createApiKeyWithJwt(accessToken, name) {
69
+ return this.fetchJSON(`${this.apiUrl}/api/auth/api-keys`, {
70
+ method: 'POST',
71
+ headers: {
72
+ 'Content-Type': 'application/json',
73
+ 'Authorization': `Bearer ${accessToken}`,
74
+ 'User-Agent': `tusky-cli/${CLI_VERSION}`,
75
+ },
76
+ body: JSON.stringify({ name, scopes: ['files:read', 'files:write', 'vaults:read', 'vaults:write'] }),
77
+ });
78
+ }
79
+ async getPlans() {
80
+ return this.fetchJSON(`${this.apiUrl}/api/plans`, {
81
+ method: 'GET',
82
+ headers: { 'User-Agent': `tusky-cli/${CLI_VERSION}` },
83
+ });
84
+ }
85
+ }
86
+ // Re-export TuskyError for catch blocks that need to check error type
87
+ export { TuskyError } from '@tuskydp/sdk';
88
+ //# sourceMappingURL=sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../../src/sdk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import blessed from 'blessed';
2
+ export declare function showAuthScreen(screen: blessed.Widgets.Screen): Promise<{
3
+ apiKey: string;
4
+ apiUrl: string;
5
+ } | null>;
6
+ //# sourceMappingURL=auth-screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-screen.d.ts","sourceRoot":"","sources":["../../../src/tui/auth-screen.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAO9B,wBAAsB,cAAc,CAClC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAgKpD"}
@@ -0,0 +1,165 @@
1
+ import blessed from 'blessed';
2
+ import { AuthClient, createSDKClient } from '../sdk.js';
3
+ import { cliConfig, getApiUrl } from '../config.js';
4
+ import { textInputDialog } from './dialogs.js';
5
+ import { showError } from './helpers.js';
6
+ export async function showAuthScreen(screen) {
7
+ return new Promise((resolve) => {
8
+ const container = blessed.box({
9
+ parent: screen,
10
+ top: 0,
11
+ left: 0,
12
+ width: '100%',
13
+ height: '100%',
14
+ tags: true,
15
+ });
16
+ const logo = blessed.box({
17
+ parent: container,
18
+ top: 2,
19
+ left: 'center',
20
+ width: 'shrink',
21
+ height: 'shrink',
22
+ tags: true,
23
+ content: [
24
+ '{bold}{cyan-fg}╔════════════════════════════╗{/cyan-fg}{/bold}',
25
+ '{bold}{cyan-fg}║ TUSKY TUI ║{/cyan-fg}{/bold}',
26
+ '{bold}{cyan-fg}║ Decentralized Storage ║{/cyan-fg}{/bold}',
27
+ '{bold}{cyan-fg}╚════════════════════════════╝{/cyan-fg}{/bold}',
28
+ ].join('\n'),
29
+ });
30
+ const prompt = blessed.box({
31
+ parent: container,
32
+ top: 8,
33
+ left: 'center',
34
+ width: 40,
35
+ height: 3,
36
+ tags: true,
37
+ content: '{center}Not authenticated.{/center}\n{center}Choose an option to continue:{/center}',
38
+ });
39
+ const menu = blessed.list({
40
+ parent: container,
41
+ top: 12,
42
+ left: 'center',
43
+ width: 30,
44
+ height: 5,
45
+ border: { type: 'line' },
46
+ style: {
47
+ border: { fg: 'cyan' },
48
+ fg: 'white',
49
+ selected: {
50
+ bg: 'cyan',
51
+ fg: 'black',
52
+ },
53
+ },
54
+ keys: true,
55
+ vi: true,
56
+ items: ['Sign Up (email)', 'Login (email)', 'Login (API key)'],
57
+ interactive: true,
58
+ });
59
+ const quitHint = blessed.box({
60
+ parent: container,
61
+ top: 18,
62
+ left: 'center',
63
+ width: 'shrink',
64
+ height: 1,
65
+ tags: true,
66
+ content: '{gray-fg}q to quit{/gray-fg}',
67
+ });
68
+ menu.on('select', async (_item, index) => {
69
+ if (index === 0) {
70
+ // Sign Up
71
+ const email = await textInputDialog(screen, 'Enter email');
72
+ if (!email) {
73
+ menu.focus();
74
+ screen.render();
75
+ return;
76
+ }
77
+ const password = await textInputDialog(screen, 'Enter password');
78
+ if (!password) {
79
+ menu.focus();
80
+ screen.render();
81
+ return;
82
+ }
83
+ const apiUrl = getApiUrl();
84
+ const authClient = new AuthClient(apiUrl);
85
+ try {
86
+ const { accessToken } = await authClient.register(email, password);
87
+ const keyResult = await authClient.createApiKeyWithJwt(accessToken, 'TUI Key');
88
+ cliConfig.set('apiKey', keyResult.apiKey);
89
+ cliConfig.set('apiUrl', apiUrl);
90
+ container.destroy();
91
+ screen.render();
92
+ resolve({ apiKey: keyResult.apiKey, apiUrl });
93
+ }
94
+ catch (err) {
95
+ showError(screen, err.message);
96
+ menu.focus();
97
+ screen.render();
98
+ }
99
+ }
100
+ else if (index === 1) {
101
+ // Login with email
102
+ const email = await textInputDialog(screen, 'Enter email');
103
+ if (!email) {
104
+ menu.focus();
105
+ screen.render();
106
+ return;
107
+ }
108
+ const password = await textInputDialog(screen, 'Enter password');
109
+ if (!password) {
110
+ menu.focus();
111
+ screen.render();
112
+ return;
113
+ }
114
+ const apiUrl = getApiUrl();
115
+ const authClient = new AuthClient(apiUrl);
116
+ try {
117
+ const { accessToken } = await authClient.login(email, password);
118
+ const keyResult = await authClient.createApiKeyWithJwt(accessToken, 'CLI Key');
119
+ cliConfig.set('apiKey', keyResult.apiKey);
120
+ cliConfig.set('apiUrl', apiUrl);
121
+ container.destroy();
122
+ screen.render();
123
+ resolve({ apiKey: keyResult.apiKey, apiUrl });
124
+ }
125
+ catch (err) {
126
+ showError(screen, 'Login failed: ' + err.message);
127
+ menu.focus();
128
+ screen.render();
129
+ }
130
+ }
131
+ else {
132
+ // Login with API key
133
+ const key = await textInputDialog(screen, 'Paste API key');
134
+ if (!key) {
135
+ menu.focus();
136
+ screen.render();
137
+ return;
138
+ }
139
+ const apiUrl = getApiUrl();
140
+ const sdk = createSDKClient(apiUrl, key);
141
+ try {
142
+ await sdk.account.get();
143
+ cliConfig.set('apiKey', key);
144
+ cliConfig.set('apiUrl', apiUrl);
145
+ container.destroy();
146
+ screen.render();
147
+ resolve({ apiKey: key, apiUrl });
148
+ }
149
+ catch (err) {
150
+ showError(screen, 'Invalid API key: ' + err.message);
151
+ menu.focus();
152
+ screen.render();
153
+ }
154
+ }
155
+ });
156
+ menu.key(['q'], () => {
157
+ container.destroy();
158
+ screen.render();
159
+ resolve(null);
160
+ });
161
+ menu.focus();
162
+ screen.render();
163
+ });
164
+ }
165
+ //# sourceMappingURL=auth-screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-screen.js","sourceRoot":"","sources":["../../../src/tui/auth-screen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import blessed from 'blessed';
2
+ export declare function confirmDialog(screen: blessed.Widgets.Screen, message: string): Promise<boolean>;
3
+ export declare function textInputDialog(screen: blessed.Widgets.Screen, label: string, defaultValue?: string): Promise<string | null>;
4
+ export declare function selectDialog(screen: blessed.Widgets.Screen, label: string, items: string[]): Promise<number>;
5
+ export declare function fileBrowserDialog(screen: blessed.Widgets.Screen, startDir?: string): Promise<{
6
+ path: string;
7
+ isDirectory: boolean;
8
+ } | null>;
9
+ export declare function detailPopup(screen: blessed.Widgets.Screen, title: string, content: string): void;
10
+ //# sourceMappingURL=dialogs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.d.ts","sourceRoot":"","sources":["../../../src/tui/dialogs.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAI9B,wBAAgB,aAAa,CAC3B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAC9B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAwBlB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAC9B,KAAK,EAAE,MAAM,EACb,YAAY,SAAK,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6DxB;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAC9B,QAAQ,SAA0B,GACjC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAAC,CAwIxD;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,QAgDhB"}
@@ -0,0 +1,303 @@
1
+ import blessed from 'blessed';
2
+ import { readdirSync } from 'fs';
3
+ import { resolve as pathResolve, dirname, join as pathJoin } from 'path';
4
+ export function confirmDialog(screen, message) {
5
+ return new Promise((resolve) => {
6
+ const dialog = blessed.question({
7
+ parent: screen,
8
+ top: 'center',
9
+ left: 'center',
10
+ width: '50%',
11
+ height: 'shrink',
12
+ border: { type: 'line' },
13
+ style: {
14
+ border: { fg: 'yellow' },
15
+ fg: 'white',
16
+ },
17
+ label: ' Confirm ',
18
+ tags: true,
19
+ keys: true,
20
+ vi: true,
21
+ });
22
+ dialog.ask(message, (err, value) => {
23
+ dialog.destroy();
24
+ screen.render();
25
+ resolve(!err && !!value);
26
+ });
27
+ });
28
+ }
29
+ export function textInputDialog(screen, label, defaultValue = '') {
30
+ return new Promise((resolve) => {
31
+ const form = blessed.form({
32
+ parent: screen,
33
+ top: 'center',
34
+ left: 'center',
35
+ width: '60%',
36
+ height: 7,
37
+ border: { type: 'line' },
38
+ style: {
39
+ border: { fg: 'cyan' },
40
+ fg: 'white',
41
+ },
42
+ label: ` ${label} `,
43
+ tags: true,
44
+ keys: true,
45
+ });
46
+ const input = blessed.textbox({
47
+ parent: form,
48
+ top: 1,
49
+ left: 1,
50
+ right: 1,
51
+ height: 1,
52
+ inputOnFocus: true,
53
+ style: {
54
+ fg: 'white',
55
+ bg: 'black',
56
+ focus: {
57
+ bg: 'black',
58
+ fg: 'cyan',
59
+ },
60
+ },
61
+ value: defaultValue,
62
+ });
63
+ const hint = blessed.box({
64
+ parent: form,
65
+ top: 3,
66
+ left: 1,
67
+ right: 1,
68
+ height: 1,
69
+ content: 'Enter to confirm, Escape to cancel',
70
+ style: { fg: 'gray' },
71
+ });
72
+ input.on('submit', (value) => {
73
+ form.destroy();
74
+ screen.render();
75
+ resolve(value || null);
76
+ });
77
+ input.on('cancel', () => {
78
+ form.destroy();
79
+ screen.render();
80
+ resolve(null);
81
+ });
82
+ input.focus();
83
+ screen.render();
84
+ });
85
+ }
86
+ export function selectDialog(screen, label, items) {
87
+ return new Promise((resolve) => {
88
+ const list = blessed.list({
89
+ parent: screen,
90
+ top: 'center',
91
+ left: 'center',
92
+ width: '50%',
93
+ height: items.length + 2,
94
+ border: { type: 'line' },
95
+ style: {
96
+ border: { fg: 'cyan' },
97
+ fg: 'white',
98
+ selected: {
99
+ bg: 'cyan',
100
+ fg: 'black',
101
+ },
102
+ },
103
+ label: ` ${label} `,
104
+ tags: true,
105
+ keys: true,
106
+ vi: true,
107
+ items,
108
+ interactive: true,
109
+ });
110
+ list.on('select', (_item, index) => {
111
+ list.destroy();
112
+ screen.render();
113
+ resolve(index);
114
+ });
115
+ list.on('cancel', () => {
116
+ list.destroy();
117
+ screen.render();
118
+ resolve(-1);
119
+ });
120
+ list.focus();
121
+ screen.render();
122
+ });
123
+ }
124
+ export function fileBrowserDialog(screen, startDir = process.env.HOME || '/') {
125
+ return new Promise((res) => {
126
+ let currentDir = pathResolve(startDir);
127
+ let resolved = false;
128
+ function finish(result) {
129
+ if (resolved)
130
+ return;
131
+ resolved = true;
132
+ box.destroy();
133
+ screen.render();
134
+ res(result);
135
+ }
136
+ const box = blessed.box({
137
+ parent: screen,
138
+ top: 'center',
139
+ left: 'center',
140
+ width: '70%',
141
+ height: '70%',
142
+ border: { type: 'line' },
143
+ style: {
144
+ border: { fg: 'cyan' },
145
+ fg: 'white',
146
+ },
147
+ label: ` Browse `,
148
+ tags: true,
149
+ });
150
+ const pathBar = blessed.box({
151
+ parent: box,
152
+ top: 0,
153
+ left: 0,
154
+ width: '100%',
155
+ height: 1,
156
+ tags: true,
157
+ style: { fg: 'cyan' },
158
+ });
159
+ const list = blessed.list({
160
+ parent: box,
161
+ top: 2,
162
+ left: 0,
163
+ width: '100%',
164
+ height: '100%-5',
165
+ style: {
166
+ fg: 'white',
167
+ selected: { bg: 'cyan', fg: 'black' },
168
+ },
169
+ keys: true,
170
+ vi: true,
171
+ interactive: true,
172
+ scrollable: true,
173
+ alwaysScroll: true,
174
+ scrollbar: { style: { bg: 'cyan' } },
175
+ tags: true,
176
+ padding: { left: 1, right: 1 },
177
+ });
178
+ const hint = blessed.box({
179
+ parent: box,
180
+ bottom: 0,
181
+ left: 0,
182
+ width: '100%',
183
+ height: 1,
184
+ tags: true,
185
+ style: { fg: 'gray' },
186
+ content: ' {bold}Enter{/bold} open/select {bold}u{/bold} upload folder {bold}Esc{/bold} cancel',
187
+ });
188
+ // Cache dir listing to avoid re-reading on selection
189
+ let cachedDirs = [];
190
+ let cachedFiles = [];
191
+ function loadDir(dir) {
192
+ currentDir = dir;
193
+ pathBar.setContent(` ${currentDir}`);
194
+ try {
195
+ const entries = readdirSync(dir, { withFileTypes: true });
196
+ cachedDirs = [];
197
+ cachedFiles = [];
198
+ for (const e of entries) {
199
+ if (e.name.startsWith('.'))
200
+ continue;
201
+ if (e.isDirectory())
202
+ cachedDirs.push(e.name);
203
+ else if (e.isFile())
204
+ cachedFiles.push(e.name);
205
+ }
206
+ cachedDirs.sort((a, b) => a.localeCompare(b));
207
+ cachedFiles.sort((a, b) => a.localeCompare(b));
208
+ const items = ['{yellow-fg}../{/yellow-fg}'];
209
+ for (const d of cachedDirs)
210
+ items.push(`{cyan-fg}📁 ${d}/{/cyan-fg}`);
211
+ for (const f of cachedFiles)
212
+ items.push(` ${f}`);
213
+ list.setItems(items);
214
+ list.select(0);
215
+ screen.render();
216
+ }
217
+ catch {
218
+ list.setItems(['{red-fg}Permission denied{/red-fg}']);
219
+ screen.render();
220
+ }
221
+ }
222
+ function getSelectedEntry() {
223
+ const idx = list.selected;
224
+ if (idx === 0)
225
+ return { name: '..', fullPath: dirname(currentDir), isDir: true };
226
+ const dirOffset = 1;
227
+ if (idx < dirOffset + cachedDirs.length) {
228
+ const name = cachedDirs[idx - dirOffset];
229
+ return { name, fullPath: pathJoin(currentDir, name), isDir: true };
230
+ }
231
+ const fileOffset = dirOffset + cachedDirs.length;
232
+ if (idx < fileOffset + cachedFiles.length) {
233
+ const name = cachedFiles[idx - fileOffset];
234
+ return { name, fullPath: pathJoin(currentDir, name), isDir: false };
235
+ }
236
+ return null;
237
+ }
238
+ list.on('select', () => {
239
+ const entry = getSelectedEntry();
240
+ if (!entry)
241
+ return;
242
+ if (entry.isDir) {
243
+ loadDir(entry.fullPath);
244
+ }
245
+ else {
246
+ finish({ path: entry.fullPath, isDirectory: false });
247
+ }
248
+ });
249
+ list.key(['escape'], () => finish(null));
250
+ box.key(['escape'], () => finish(null));
251
+ list.key(['u'], () => {
252
+ finish({ path: currentDir, isDirectory: true });
253
+ });
254
+ loadDir(currentDir);
255
+ list.focus();
256
+ screen.render();
257
+ });
258
+ }
259
+ export function detailPopup(screen, title, content) {
260
+ // Count content lines to auto-size height
261
+ const lines = content.split('\n').length;
262
+ const height = Math.min(lines + 5, Math.floor(screen.height * 0.5));
263
+ const box = blessed.box({
264
+ parent: screen,
265
+ top: 'center',
266
+ left: 'center',
267
+ width: '55%',
268
+ height,
269
+ border: { type: 'line' },
270
+ style: {
271
+ border: { fg: 'cyan' },
272
+ fg: 'white',
273
+ },
274
+ label: ` ${title} `,
275
+ tags: true,
276
+ keys: true,
277
+ vi: true,
278
+ scrollable: true,
279
+ alwaysScroll: true,
280
+ scrollbar: {
281
+ style: { bg: 'cyan' },
282
+ },
283
+ padding: { top: 1, bottom: 1, left: 1, right: 1 },
284
+ content,
285
+ });
286
+ const hint = blessed.box({
287
+ parent: box,
288
+ bottom: 0,
289
+ left: 0,
290
+ width: '100%',
291
+ height: 1,
292
+ content: '{gray-fg}Esc to close{/gray-fg}',
293
+ tags: true,
294
+ style: { fg: 'gray' },
295
+ });
296
+ box.key(['escape', 'q', 'enter'], () => {
297
+ box.destroy();
298
+ screen.render();
299
+ });
300
+ box.focus();
301
+ screen.render();
302
+ }
303
+ //# sourceMappingURL=dialogs.js.map