@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.
- package/dist/bin/tuskydp.d.ts +3 -0
- package/dist/bin/tuskydp.d.ts.map +1 -0
- package/dist/bin/tuskydp.js +3 -0
- package/dist/bin/tuskydp.js.map +1 -0
- package/dist/src/client.d.ts +120 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +152 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/commands/account.d.ts +3 -0
- package/dist/src/commands/account.d.ts.map +1 -0
- package/dist/src/commands/account.js +66 -0
- package/dist/src/commands/account.js.map +1 -0
- package/dist/src/commands/auth.d.ts +3 -0
- package/dist/src/commands/auth.d.ts.map +1 -0
- package/dist/src/commands/auth.js +171 -0
- package/dist/src/commands/auth.js.map +1 -0
- package/dist/src/commands/decrypt.d.ts +15 -0
- package/dist/src/commands/decrypt.d.ts.map +1 -0
- package/dist/src/commands/decrypt.js +224 -0
- package/dist/src/commands/decrypt.js.map +1 -0
- package/dist/src/commands/download.d.ts +5 -0
- package/dist/src/commands/download.d.ts.map +1 -0
- package/dist/src/commands/download.js +67 -0
- package/dist/src/commands/download.js.map +1 -0
- package/dist/src/commands/encryption.d.ts +3 -0
- package/dist/src/commands/encryption.d.ts.map +1 -0
- package/dist/src/commands/encryption.js +254 -0
- package/dist/src/commands/encryption.js.map +1 -0
- package/dist/src/commands/export.d.ts +70 -0
- package/dist/src/commands/export.d.ts.map +1 -0
- package/dist/src/commands/export.js +178 -0
- package/dist/src/commands/export.js.map +1 -0
- package/dist/src/commands/files.d.ts +3 -0
- package/dist/src/commands/files.d.ts.map +1 -0
- package/dist/src/commands/files.js +179 -0
- package/dist/src/commands/files.js.map +1 -0
- package/dist/src/commands/mcp.d.ts +8 -0
- package/dist/src/commands/mcp.d.ts.map +1 -0
- package/dist/src/commands/mcp.js +178 -0
- package/dist/src/commands/mcp.js.map +1 -0
- package/dist/src/commands/rehydrate.d.ts +5 -0
- package/dist/src/commands/rehydrate.d.ts.map +1 -0
- package/dist/src/commands/rehydrate.js +27 -0
- package/dist/src/commands/rehydrate.js.map +1 -0
- package/dist/src/commands/tui.d.ts +3 -0
- package/dist/src/commands/tui.d.ts.map +1 -0
- package/dist/src/commands/tui.js +10 -0
- package/dist/src/commands/tui.js.map +1 -0
- package/dist/src/commands/upload.d.ts +6 -0
- package/dist/src/commands/upload.d.ts.map +1 -0
- package/dist/src/commands/upload.js +121 -0
- package/dist/src/commands/upload.js.map +1 -0
- package/dist/src/commands/vault.d.ts +3 -0
- package/dist/src/commands/vault.d.ts.map +1 -0
- package/dist/src/commands/vault.js +118 -0
- package/dist/src/commands/vault.js.map +1 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +26 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/crypto.d.ts +16 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +95 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +59 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/keyring.d.ts +4 -0
- package/dist/src/lib/keyring.d.ts.map +1 -0
- package/dist/src/lib/keyring.js +51 -0
- package/dist/src/lib/keyring.js.map +1 -0
- package/dist/src/lib/output.d.ts +6 -0
- package/dist/src/lib/output.d.ts.map +1 -0
- package/dist/src/lib/output.js +37 -0
- package/dist/src/lib/output.js.map +1 -0
- package/dist/src/lib/progress.d.ts +2 -0
- package/dist/src/lib/progress.d.ts.map +1 -0
- package/dist/src/lib/progress.js +5 -0
- package/dist/src/lib/progress.js.map +1 -0
- package/dist/src/lib/resolve.d.ts +3 -0
- package/dist/src/lib/resolve.d.ts.map +1 -0
- package/dist/src/lib/resolve.js +25 -0
- package/dist/src/lib/resolve.js.map +1 -0
- package/dist/src/mcp/context.d.ts +19 -0
- package/dist/src/mcp/context.d.ts.map +1 -0
- package/dist/src/mcp/context.js +8 -0
- package/dist/src/mcp/context.js.map +1 -0
- package/dist/src/mcp/server.d.ts +13 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +113 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/account.d.ts +7 -0
- package/dist/src/mcp/tools/account.d.ts.map +1 -0
- package/dist/src/mcp/tools/account.js +31 -0
- package/dist/src/mcp/tools/account.js.map +1 -0
- package/dist/src/mcp/tools/files.d.ts +10 -0
- package/dist/src/mcp/tools/files.d.ts.map +1 -0
- package/dist/src/mcp/tools/files.js +310 -0
- package/dist/src/mcp/tools/files.js.map +1 -0
- package/dist/src/mcp/tools/folders.d.ts +7 -0
- package/dist/src/mcp/tools/folders.d.ts.map +1 -0
- package/dist/src/mcp/tools/folders.js +67 -0
- package/dist/src/mcp/tools/folders.js.map +1 -0
- package/dist/src/mcp/tools/helpers.d.ts +15 -0
- package/dist/src/mcp/tools/helpers.d.ts.map +1 -0
- package/dist/src/mcp/tools/helpers.js +25 -0
- package/dist/src/mcp/tools/helpers.js.map +1 -0
- package/dist/src/mcp/tools/trash.d.ts +7 -0
- package/dist/src/mcp/tools/trash.d.ts.map +1 -0
- package/dist/src/mcp/tools/trash.js +46 -0
- package/dist/src/mcp/tools/trash.js.map +1 -0
- package/dist/src/mcp/tools/vaults.d.ts +7 -0
- package/dist/src/mcp/tools/vaults.d.ts.map +1 -0
- package/dist/src/mcp/tools/vaults.js +85 -0
- package/dist/src/mcp/tools/vaults.js.map +1 -0
- package/dist/src/sdk.d.ts +53 -0
- package/dist/src/sdk.d.ts.map +1 -0
- package/dist/src/sdk.js +88 -0
- package/dist/src/sdk.js.map +1 -0
- package/dist/src/tui/auth-screen.d.ts +6 -0
- package/dist/src/tui/auth-screen.d.ts.map +1 -0
- package/dist/src/tui/auth-screen.js +165 -0
- package/dist/src/tui/auth-screen.js.map +1 -0
- package/dist/src/tui/dialogs.d.ts +10 -0
- package/dist/src/tui/dialogs.d.ts.map +1 -0
- package/dist/src/tui/dialogs.js +303 -0
- package/dist/src/tui/dialogs.js.map +1 -0
- package/dist/src/tui/files-panel.d.ts +34 -0
- package/dist/src/tui/files-panel.d.ts.map +1 -0
- package/dist/src/tui/files-panel.js +135 -0
- package/dist/src/tui/files-panel.js.map +1 -0
- package/dist/src/tui/helpers.d.ts +38 -0
- package/dist/src/tui/helpers.d.ts.map +1 -0
- package/dist/src/tui/helpers.js +187 -0
- package/dist/src/tui/helpers.js.map +1 -0
- package/dist/src/tui/index.d.ts +2 -0
- package/dist/src/tui/index.d.ts.map +1 -0
- package/dist/src/tui/index.js +382 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/overview.d.ts +4 -0
- package/dist/src/tui/overview.d.ts.map +1 -0
- package/dist/src/tui/overview.js +136 -0
- package/dist/src/tui/overview.js.map +1 -0
- package/dist/src/tui/status-bar.d.ts +18 -0
- package/dist/src/tui/status-bar.d.ts.map +1 -0
- package/dist/src/tui/status-bar.js +54 -0
- package/dist/src/tui/status-bar.js.map +1 -0
- package/dist/src/tui/vaults-panel.d.ts +26 -0
- package/dist/src/tui/vaults-panel.d.ts.map +1 -0
- package/dist/src/tui/vaults-panel.js +118 -0
- package/dist/src/tui/vaults-panel.js.map +1 -0
- 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"}
|
package/dist/src/sdk.js
ADDED
|
@@ -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 @@
|
|
|
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
|