pdauth 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROXY-SPEC.md +159 -0
- package/README.md +142 -0
- package/bin/ga.mjs +329 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +88 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/apps.d.ts +8 -0
- package/dist/commands/apps.d.ts.map +1 -0
- package/dist/commands/apps.js +76 -0
- package/dist/commands/apps.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +58 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/connect.d.ts +6 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +67 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/proxy.d.ts +12 -0
- package/dist/commands/proxy.d.ts.map +1 -0
- package/dist/commands/proxy.js +127 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +117 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tools.d.ts +10 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +123 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/pipedream.d.ts +52 -0
- package/dist/pipedream.d.ts.map +1 -0
- package/dist/pipedream.js +142 -0
- package/dist/pipedream.js.map +1 -0
- package/package.json +46 -0
- package/src/cli.ts +111 -0
- package/src/commands/apps.ts +88 -0
- package/src/commands/config.ts +68 -0
- package/src/commands/connect.ts +76 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/proxy.ts +155 -0
- package/src/commands/status.ts +143 -0
- package/src/commands/tools.ts +150 -0
- package/src/config.ts +78 -0
- package/src/index.ts +6 -0
- package/src/pipedream.ts +216 -0
- package/tsconfig.json +20 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import {
|
|
5
|
+
configCommand,
|
|
6
|
+
appsCommand,
|
|
7
|
+
appInfoCommand,
|
|
8
|
+
connectCommand,
|
|
9
|
+
statusCommand,
|
|
10
|
+
disconnectCommand,
|
|
11
|
+
toolsCommand,
|
|
12
|
+
callCommand,
|
|
13
|
+
proxyCommand,
|
|
14
|
+
} from './commands/index.js';
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('pdauth')
|
|
20
|
+
.description('š Pipedream OAuth CLI - Give AI agents access to 2500+ APIs via dynamic OAuth')
|
|
21
|
+
.version('1.0.0');
|
|
22
|
+
|
|
23
|
+
// Config command
|
|
24
|
+
program
|
|
25
|
+
.command('config')
|
|
26
|
+
.description('Configure Pipedream credentials')
|
|
27
|
+
.option('-s, --show', 'Show current configuration')
|
|
28
|
+
.option('--set <key=value>', 'Set a configuration value')
|
|
29
|
+
.action(configCommand);
|
|
30
|
+
|
|
31
|
+
// Apps commands
|
|
32
|
+
program
|
|
33
|
+
.command('apps')
|
|
34
|
+
.description('List available apps (2500+ APIs)')
|
|
35
|
+
.option('-s, --search <query>', 'Search for apps')
|
|
36
|
+
.option('-j, --json', 'Output as JSON')
|
|
37
|
+
.action(appsCommand);
|
|
38
|
+
|
|
39
|
+
program
|
|
40
|
+
.command('app <slug>')
|
|
41
|
+
.description('Get info about a specific app')
|
|
42
|
+
.option('-j, --json', 'Output as JSON')
|
|
43
|
+
.action(appInfoCommand);
|
|
44
|
+
|
|
45
|
+
// Connect command
|
|
46
|
+
program
|
|
47
|
+
.command('connect <app>')
|
|
48
|
+
.description('Generate OAuth link for an app')
|
|
49
|
+
.option('-u, --user <id>', 'User ID (default: "default")')
|
|
50
|
+
.option('-j, --json', 'Output as JSON')
|
|
51
|
+
.option('-c, --copy', 'Copy link to clipboard')
|
|
52
|
+
.action(connectCommand);
|
|
53
|
+
|
|
54
|
+
// Status command
|
|
55
|
+
program
|
|
56
|
+
.command('status')
|
|
57
|
+
.description('List connected accounts')
|
|
58
|
+
.option('-u, --user <id>', 'User ID (default: "default")')
|
|
59
|
+
.option('-a, --all', 'Show all users')
|
|
60
|
+
.option('-j, --json', 'Output as JSON')
|
|
61
|
+
.action(statusCommand);
|
|
62
|
+
|
|
63
|
+
// Disconnect command
|
|
64
|
+
program
|
|
65
|
+
.command('disconnect <app>')
|
|
66
|
+
.description('Disconnect an app')
|
|
67
|
+
.option('-u, --user <id>', 'User ID')
|
|
68
|
+
.option('-f, --force', 'Skip confirmation')
|
|
69
|
+
.action(disconnectCommand);
|
|
70
|
+
|
|
71
|
+
// Tools command
|
|
72
|
+
program
|
|
73
|
+
.command('tools <app>')
|
|
74
|
+
.description('List available MCP tools for an app')
|
|
75
|
+
.option('-u, --user <id>', 'User ID')
|
|
76
|
+
.option('-j, --json', 'Output as JSON')
|
|
77
|
+
.action(toolsCommand);
|
|
78
|
+
|
|
79
|
+
// Call command
|
|
80
|
+
program
|
|
81
|
+
.command('call <tool>')
|
|
82
|
+
.description('Invoke an MCP tool (format: app.tool_name)')
|
|
83
|
+
.argument('[args...]', 'Tool arguments as key=value pairs')
|
|
84
|
+
.option('-u, --user <id>', 'User ID')
|
|
85
|
+
.option('-j, --json', 'Output as JSON')
|
|
86
|
+
.option('-a, --args <json>', 'Arguments as JSON')
|
|
87
|
+
.action(callCommand);
|
|
88
|
+
|
|
89
|
+
// Proxy command - make authenticated API requests
|
|
90
|
+
program
|
|
91
|
+
.command('proxy <app> <path>')
|
|
92
|
+
.description('Make authenticated API request via Pipedream proxy')
|
|
93
|
+
.option('-X, --method <method>', 'HTTP method (GET|POST|PUT|DELETE|PATCH)', 'GET')
|
|
94
|
+
.option('-u, --user <id>', 'User ID')
|
|
95
|
+
.option('-d, --data <json>', 'Request body as JSON string')
|
|
96
|
+
.option('-H, --header <header>', 'Custom header (repeatable)', (v, p: string[]) => p.concat([v]), [] as string[])
|
|
97
|
+
.option('-q, --query <param>', 'Query parameter (repeatable)', (v, p: string[]) => p.concat([v]), [] as string[])
|
|
98
|
+
.option('-j, --json', 'Output response as JSON (compact)')
|
|
99
|
+
.option('-v, --verbose', 'Show request details')
|
|
100
|
+
.action(proxyCommand);
|
|
101
|
+
|
|
102
|
+
// Quick auth link helper
|
|
103
|
+
program
|
|
104
|
+
.command('link <app>')
|
|
105
|
+
.description('Alias for connect - generate OAuth link')
|
|
106
|
+
.option('-u, --user <id>', 'User ID')
|
|
107
|
+
.option('-j, --json', 'Output as JSON')
|
|
108
|
+
.option('-c, --copy', 'Copy to clipboard')
|
|
109
|
+
.action(connectCommand);
|
|
110
|
+
|
|
111
|
+
program.parse();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { searchApps, getApp } from '../pipedream.js';
|
|
4
|
+
import { isConfigured } from '../config.js';
|
|
5
|
+
|
|
6
|
+
export async function appsCommand(options: { search?: string; json?: boolean }): Promise<void> {
|
|
7
|
+
if (!isConfigured()) {
|
|
8
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const spinner = ora('Fetching apps...').start();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const apps = await searchApps(options.search);
|
|
16
|
+
spinner.stop();
|
|
17
|
+
|
|
18
|
+
if (options.json) {
|
|
19
|
+
console.log(JSON.stringify(apps, null, 2));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (apps.length === 0) {
|
|
24
|
+
console.log(chalk.yellow(`No apps found${options.search ? ` for "${options.search}"` : ''}`));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(chalk.bold(`\nš¦ Available Apps${options.search ? ` matching "${options.search}"` : ''}\n`));
|
|
29
|
+
|
|
30
|
+
for (const app of apps.slice(0, 50)) {
|
|
31
|
+
console.log(` ${chalk.cyan(app.nameSlug.padEnd(30))} ${chalk.gray(app.name)}`);
|
|
32
|
+
if (app.description) {
|
|
33
|
+
console.log(` ${' '.repeat(30)} ${chalk.dim(app.description.slice(0, 60))}${app.description.length > 60 ? '...' : ''}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (apps.length > 50) {
|
|
38
|
+
console.log(chalk.gray(`\n ... and ${apps.length - 50} more. Use --search to filter.`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(chalk.gray(`\n Total: ${apps.length} apps\n`));
|
|
42
|
+
console.log(chalk.gray(' Browse all: https://mcp.pipedream.com\n'));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
spinner.fail('Failed to fetch apps');
|
|
45
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function appInfoCommand(appSlug: string, options: { json?: boolean }): Promise<void> {
|
|
51
|
+
if (!isConfigured()) {
|
|
52
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const spinner = ora(`Fetching ${appSlug}...`).start();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const app = await getApp(appSlug);
|
|
60
|
+
spinner.stop();
|
|
61
|
+
|
|
62
|
+
if (!app) {
|
|
63
|
+
console.error(chalk.red(`App not found: ${appSlug}`));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options.json) {
|
|
68
|
+
console.log(JSON.stringify(app, null, 2));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(chalk.bold(`\nš¦ ${app.name}\n`));
|
|
73
|
+
console.log(` ${chalk.gray('Slug:')} ${chalk.cyan(app.nameSlug)}`);
|
|
74
|
+
console.log(` ${chalk.gray('Auth:')} ${app.authType}`);
|
|
75
|
+
if (app.description) {
|
|
76
|
+
console.log(` ${chalk.gray('Description:')} ${app.description}`);
|
|
77
|
+
}
|
|
78
|
+
if (app.categories?.length) {
|
|
79
|
+
console.log(` ${chalk.gray('Categories:')} ${app.categories.join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
console.log(` ${chalk.gray('MCP Server:')} https://mcp.pipedream.com/app/${app.nameSlug}`);
|
|
82
|
+
console.log();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
spinner.fail('Failed to fetch app');
|
|
85
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getConfig, setConfig, isConfigured, getConfigPath, type PdAuthConfig } from '../config.js';
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
|
|
5
|
+
async function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
6
|
+
const rl = createInterface({
|
|
7
|
+
input: process.stdin,
|
|
8
|
+
output: process.stdout,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const displayDefault = defaultValue ? ` [${defaultValue}]` : '';
|
|
13
|
+
rl.question(`${question}${displayDefault}: `, (answer) => {
|
|
14
|
+
rl.close();
|
|
15
|
+
resolve(answer.trim() || defaultValue || '');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function configCommand(options: { show?: boolean; set?: string }): Promise<void> {
|
|
21
|
+
if (options.show) {
|
|
22
|
+
const cfg = getConfig();
|
|
23
|
+
console.log(chalk.bold('\nš Current Configuration\n'));
|
|
24
|
+
console.log(` ${chalk.gray('Path:')} ${getConfigPath()}`);
|
|
25
|
+
console.log(` ${chalk.gray('Client ID:')} ${cfg.clientId ? chalk.green(cfg.clientId.slice(0, 8) + '...') : chalk.red('not set')}`);
|
|
26
|
+
console.log(` ${chalk.gray('Client Secret:')} ${cfg.clientSecret ? chalk.green('********') : chalk.red('not set')}`);
|
|
27
|
+
console.log(` ${chalk.gray('Project ID:')} ${cfg.projectId ? chalk.green(cfg.projectId) : chalk.red('not set')}`);
|
|
28
|
+
console.log(` ${chalk.gray('Environment:')} ${chalk.cyan(cfg.environment)}`);
|
|
29
|
+
console.log(` ${chalk.gray('Status:')} ${isConfigured() ? chalk.green('ā Configured') : chalk.red('ā Not configured')}\n`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (options.set) {
|
|
34
|
+
// Format: key=value
|
|
35
|
+
const [key, ...valueParts] = options.set.split('=');
|
|
36
|
+
const value = valueParts.join('=');
|
|
37
|
+
|
|
38
|
+
const validKeys = ['clientId', 'clientSecret', 'projectId', 'environment'];
|
|
39
|
+
if (!validKeys.includes(key)) {
|
|
40
|
+
console.error(chalk.red(`Invalid key: ${key}. Valid keys: ${validKeys.join(', ')}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setConfig({ [key]: value } as Partial<PdAuthConfig>);
|
|
45
|
+
console.log(chalk.green(`ā Set ${key}`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Interactive configuration
|
|
50
|
+
console.log(chalk.bold('\nš Pipedream OAuth Configuration\n'));
|
|
51
|
+
console.log(chalk.gray('Get your credentials at: https://pipedream.com/settings/api\n'));
|
|
52
|
+
|
|
53
|
+
const current = getConfig();
|
|
54
|
+
|
|
55
|
+
const clientId = await prompt('Client ID', current.clientId || undefined);
|
|
56
|
+
const clientSecret = await prompt('Client Secret', current.clientSecret ? '********' : undefined);
|
|
57
|
+
const projectId = await prompt('Project ID', current.projectId || undefined);
|
|
58
|
+
const environment = await prompt('Environment (development/production)', current.environment || 'development');
|
|
59
|
+
|
|
60
|
+
setConfig({
|
|
61
|
+
clientId,
|
|
62
|
+
clientSecret: clientSecret === '********' ? current.clientSecret : clientSecret,
|
|
63
|
+
projectId,
|
|
64
|
+
environment: environment as 'development' | 'production',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(chalk.green('\nā Configuration saved!\n'));
|
|
68
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { createConnectLink, getApp } from '../pipedream.js';
|
|
4
|
+
import { isConfigured, getDefaultUser, addUser } from '../config.js';
|
|
5
|
+
|
|
6
|
+
export async function connectCommand(
|
|
7
|
+
appSlug: string,
|
|
8
|
+
options: { user?: string; json?: boolean; copy?: boolean }
|
|
9
|
+
): Promise<void> {
|
|
10
|
+
if (!isConfigured()) {
|
|
11
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const userId = options.user || getDefaultUser();
|
|
16
|
+
addUser(userId);
|
|
17
|
+
|
|
18
|
+
const spinner = ora(`Generating OAuth link for ${appSlug}...`).start();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Verify app exists
|
|
22
|
+
const app = await getApp(appSlug);
|
|
23
|
+
if (!app) {
|
|
24
|
+
spinner.fail(`App not found: ${appSlug}`);
|
|
25
|
+
console.log(chalk.gray('Run: pdauth apps --search <query> to find available apps'));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = await createConnectLink(userId, appSlug);
|
|
30
|
+
spinner.stop();
|
|
31
|
+
|
|
32
|
+
if (options.json) {
|
|
33
|
+
console.log(JSON.stringify({
|
|
34
|
+
app: appSlug,
|
|
35
|
+
appName: app.name,
|
|
36
|
+
user: userId,
|
|
37
|
+
connectLinkUrl: result.connectLinkUrl,
|
|
38
|
+
expiresAt: result.expiresAt,
|
|
39
|
+
}, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(chalk.bold(`\nš OAuth Link for ${chalk.cyan(app.name)}\n`));
|
|
44
|
+
console.log(` ${chalk.gray('User:')} ${userId}`);
|
|
45
|
+
console.log(` ${chalk.gray('Expires:')} ${new Date(result.expiresAt).toLocaleString()}\n`);
|
|
46
|
+
|
|
47
|
+
console.log(chalk.bold(' Link:\n'));
|
|
48
|
+
console.log(` ${chalk.underline.blue(result.connectLinkUrl)}\n`);
|
|
49
|
+
|
|
50
|
+
console.log(chalk.gray(' Share this link to authorize access.\n'));
|
|
51
|
+
console.log(chalk.gray(' After authorization, run: pdauth status\n'));
|
|
52
|
+
|
|
53
|
+
// Try to copy to clipboard (cross-platform)
|
|
54
|
+
if (options.copy) {
|
|
55
|
+
try {
|
|
56
|
+
const { execSync } = await import('child_process');
|
|
57
|
+
const platform = process.platform;
|
|
58
|
+
|
|
59
|
+
if (platform === 'darwin') {
|
|
60
|
+
execSync(`echo "${result.connectLinkUrl}" | pbcopy`);
|
|
61
|
+
} else if (platform === 'linux') {
|
|
62
|
+
execSync(`echo "${result.connectLinkUrl}" | xclip -selection clipboard 2>/dev/null || echo "${result.connectLinkUrl}" | xsel --clipboard 2>/dev/null`);
|
|
63
|
+
} else if (platform === 'win32') {
|
|
64
|
+
execSync(`echo ${result.connectLinkUrl} | clip`);
|
|
65
|
+
}
|
|
66
|
+
console.log(chalk.green(' ā Copied to clipboard!\n'));
|
|
67
|
+
} catch {
|
|
68
|
+
// Clipboard not available, skip silently
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
spinner.fail('Failed to generate link');
|
|
73
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { configCommand } from './config.js';
|
|
2
|
+
export { appsCommand, appInfoCommand } from './apps.js';
|
|
3
|
+
export { connectCommand } from './connect.js';
|
|
4
|
+
export { statusCommand, disconnectCommand } from './status.js';
|
|
5
|
+
export { toolsCommand, callCommand } from './tools.js';
|
|
6
|
+
export { proxyCommand } from './proxy.js';
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getClient, listAccounts } from '../pipedream.js';
|
|
4
|
+
import { isConfigured, getDefaultUser } from '../config.js';
|
|
5
|
+
|
|
6
|
+
interface ProxyOptions {
|
|
7
|
+
method?: string;
|
|
8
|
+
user?: string;
|
|
9
|
+
data?: string;
|
|
10
|
+
header?: string[];
|
|
11
|
+
query?: string[];
|
|
12
|
+
json?: boolean;
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseParams(params: string[]): Record<string, string> {
|
|
17
|
+
const result: Record<string, string> = {};
|
|
18
|
+
for (const p of params) {
|
|
19
|
+
const [key, ...rest] = p.split('=');
|
|
20
|
+
if (key) {
|
|
21
|
+
result[key] = rest.join('=');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseHeaders(headers: string[]): Record<string, string> {
|
|
28
|
+
const result: Record<string, string> = {};
|
|
29
|
+
for (const h of headers) {
|
|
30
|
+
const colonIndex = h.indexOf(':');
|
|
31
|
+
if (colonIndex > 0) {
|
|
32
|
+
const key = h.slice(0, colonIndex).trim();
|
|
33
|
+
const value = h.slice(colonIndex + 1).trim();
|
|
34
|
+
result[key] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function proxyCommand(
|
|
41
|
+
app: string,
|
|
42
|
+
path: string,
|
|
43
|
+
options: ProxyOptions
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
if (!isConfigured()) {
|
|
46
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const userId = options.user || getDefaultUser();
|
|
51
|
+
const method = (options.method || 'GET').toUpperCase();
|
|
52
|
+
|
|
53
|
+
if (options.verbose) {
|
|
54
|
+
console.log(chalk.gray(`User: ${userId}`));
|
|
55
|
+
console.log(chalk.gray(`App: ${app}`));
|
|
56
|
+
console.log(chalk.gray(`Path: ${path}`));
|
|
57
|
+
console.log(chalk.gray(`Method: ${method}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const spinner = ora('Finding account...').start();
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// 1. Get account ID for app + user
|
|
64
|
+
const accounts = await listAccounts(userId);
|
|
65
|
+
const account = accounts.find(a => a.app.nameSlug === app);
|
|
66
|
+
|
|
67
|
+
if (!account) {
|
|
68
|
+
spinner.fail(`No ${app} account connected for user ${userId}`);
|
|
69
|
+
const connectedApps = accounts.map(a => a.app.nameSlug).join(', ');
|
|
70
|
+
console.log(chalk.gray(`Connected apps: ${connectedApps || 'none'}`));
|
|
71
|
+
console.log(chalk.gray(`\nRun: pdauth connect ${app} --user ${userId}`));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options.verbose) {
|
|
76
|
+
spinner.succeed(`Using account: ${account.name} (${account.id})`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 2. Build request
|
|
80
|
+
const client = getClient();
|
|
81
|
+
const params = parseParams(options.query || []);
|
|
82
|
+
const headers = parseHeaders(options.header || []);
|
|
83
|
+
|
|
84
|
+
let body: Record<string, unknown> | undefined;
|
|
85
|
+
if (options.data) {
|
|
86
|
+
try {
|
|
87
|
+
body = JSON.parse(options.data);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
spinner.fail('Invalid JSON in --data');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const request = {
|
|
95
|
+
url: path,
|
|
96
|
+
externalUserId: userId,
|
|
97
|
+
accountId: account.id,
|
|
98
|
+
params: Object.keys(params).length > 0 ? params : undefined,
|
|
99
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
100
|
+
body,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
if (options.verbose) {
|
|
104
|
+
console.log(chalk.gray('\nRequest:'));
|
|
105
|
+
console.log(chalk.gray(JSON.stringify(request, null, 2)));
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
spinner.text = `${method} ${path}...`;
|
|
110
|
+
|
|
111
|
+
// 3. Call proxy based on method
|
|
112
|
+
let response: unknown;
|
|
113
|
+
|
|
114
|
+
switch (method) {
|
|
115
|
+
case 'GET':
|
|
116
|
+
response = await client.proxy.get(request);
|
|
117
|
+
break;
|
|
118
|
+
case 'POST':
|
|
119
|
+
response = await client.proxy.post(request as Parameters<typeof client.proxy.post>[0]);
|
|
120
|
+
break;
|
|
121
|
+
case 'PUT':
|
|
122
|
+
response = await client.proxy.put(request as Parameters<typeof client.proxy.put>[0]);
|
|
123
|
+
break;
|
|
124
|
+
case 'DELETE':
|
|
125
|
+
response = await client.proxy.delete(request);
|
|
126
|
+
break;
|
|
127
|
+
case 'PATCH':
|
|
128
|
+
response = await client.proxy.patch(request as Parameters<typeof client.proxy.patch>[0]);
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
spinner.fail(`Unsupported method: ${method}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
spinner.stop();
|
|
136
|
+
|
|
137
|
+
// 4. Output response
|
|
138
|
+
if (options.json) {
|
|
139
|
+
console.log(JSON.stringify(response));
|
|
140
|
+
} else if (typeof response === 'object' && response !== null) {
|
|
141
|
+
console.log(JSON.stringify(response, null, 2));
|
|
142
|
+
} else {
|
|
143
|
+
console.log(response);
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
spinner.fail('Request failed');
|
|
147
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
148
|
+
console.error(chalk.red(message));
|
|
149
|
+
|
|
150
|
+
if (options.verbose && error instanceof Error && error.stack) {
|
|
151
|
+
console.error(chalk.gray(error.stack));
|
|
152
|
+
}
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { listAccounts, deleteAccount } from '../pipedream.js';
|
|
4
|
+
import { isConfigured, getDefaultUser, getUsers } from '../config.js';
|
|
5
|
+
|
|
6
|
+
export async function statusCommand(options: { user?: string; json?: boolean; all?: boolean }): Promise<void> {
|
|
7
|
+
if (!isConfigured()) {
|
|
8
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const spinner = ora('Fetching connected accounts...').start();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (options.all) {
|
|
16
|
+
// Show all users
|
|
17
|
+
const users = getUsers();
|
|
18
|
+
const userIds = Object.keys(users);
|
|
19
|
+
|
|
20
|
+
if (userIds.length === 0) {
|
|
21
|
+
spinner.stop();
|
|
22
|
+
console.log(chalk.yellow('No users registered. Run: pdauth connect <app> --user <id>'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allAccounts: Array<{ userId: string; accounts: Awaited<ReturnType<typeof listAccounts>> }> = [];
|
|
27
|
+
|
|
28
|
+
for (const userId of userIds) {
|
|
29
|
+
const accounts = await listAccounts(userId);
|
|
30
|
+
allAccounts.push({ userId, accounts });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
spinner.stop();
|
|
34
|
+
|
|
35
|
+
if (options.json) {
|
|
36
|
+
console.log(JSON.stringify(allAccounts, null, 2));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(chalk.bold('\nš All Connected Accounts\n'));
|
|
41
|
+
|
|
42
|
+
for (const { userId, accounts } of allAccounts) {
|
|
43
|
+
const userMeta = users[userId];
|
|
44
|
+
const label = userMeta?.label ? ` (${userMeta.label})` : '';
|
|
45
|
+
console.log(chalk.bold(` š¤ ${userId}${label}`));
|
|
46
|
+
|
|
47
|
+
if (accounts.length === 0) {
|
|
48
|
+
console.log(chalk.gray(' No connected accounts\n'));
|
|
49
|
+
} else {
|
|
50
|
+
for (const account of accounts) {
|
|
51
|
+
const status = account.dead ? chalk.red('ā dead') :
|
|
52
|
+
account.healthy ? chalk.green('ā healthy') : chalk.yellow('ā unhealthy');
|
|
53
|
+
console.log(` ${chalk.cyan(account.app.nameSlug.padEnd(25))} ${status}`);
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const userId = options.user || getDefaultUser();
|
|
62
|
+
const accounts = await listAccounts(userId);
|
|
63
|
+
spinner.stop();
|
|
64
|
+
|
|
65
|
+
if (options.json) {
|
|
66
|
+
console.log(JSON.stringify({ user: userId, accounts }, null, 2));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(chalk.bold(`\nš Connected Accounts for ${chalk.cyan(userId)}\n`));
|
|
71
|
+
|
|
72
|
+
if (accounts.length === 0) {
|
|
73
|
+
console.log(chalk.yellow(' No connected accounts.\n'));
|
|
74
|
+
console.log(chalk.gray(' Run: pdauth connect <app> to authorize an app\n'));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const account of accounts) {
|
|
79
|
+
const status = account.dead ? chalk.red('ā dead') :
|
|
80
|
+
account.healthy ? chalk.green('ā healthy') : chalk.yellow('ā unhealthy');
|
|
81
|
+
|
|
82
|
+
console.log(` ${chalk.cyan(account.app.nameSlug.padEnd(25))} ${account.app.name}`);
|
|
83
|
+
console.log(` ${' '.repeat(25)} ${chalk.gray('Status:')} ${status}`);
|
|
84
|
+
console.log(` ${' '.repeat(25)} ${chalk.gray('Account ID:')} ${account.id}`);
|
|
85
|
+
console.log(` ${' '.repeat(25)} ${chalk.gray('Connected:')} ${new Date(account.createdAt).toLocaleDateString()}`);
|
|
86
|
+
console.log();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log(chalk.gray(` Total: ${accounts.length} connected app(s)\n`));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
spinner.fail('Failed to fetch accounts');
|
|
92
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function disconnectCommand(
|
|
98
|
+
appSlugOrAccountId: string,
|
|
99
|
+
options: { user?: string; force?: boolean }
|
|
100
|
+
): Promise<void> {
|
|
101
|
+
if (!isConfigured()) {
|
|
102
|
+
console.error(chalk.red('Not configured. Run: pdauth config'));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const userId = options.user || getDefaultUser();
|
|
107
|
+
const spinner = ora('Finding account...').start();
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const accounts = await listAccounts(userId);
|
|
111
|
+
|
|
112
|
+
// Find account by app slug or account ID
|
|
113
|
+
const account = accounts.find(
|
|
114
|
+
a => a.app.nameSlug === appSlugOrAccountId || a.id === appSlugOrAccountId
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (!account) {
|
|
118
|
+
spinner.fail(`No connected account found: ${appSlugOrAccountId}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!options.force) {
|
|
123
|
+
spinner.stop();
|
|
124
|
+
console.log(chalk.yellow(`\nā ļø This will disconnect ${chalk.cyan(account.app.name)} for user ${userId}`));
|
|
125
|
+
console.log(chalk.gray(' Run with --force to confirm\n'));
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
spinner.text = `Disconnecting ${account.app.name}...`;
|
|
130
|
+
const success = await deleteAccount(account.id);
|
|
131
|
+
|
|
132
|
+
if (success) {
|
|
133
|
+
spinner.succeed(`Disconnected ${account.app.name}`);
|
|
134
|
+
} else {
|
|
135
|
+
spinner.fail(`Failed to disconnect ${account.app.name}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
spinner.fail('Failed to disconnect');
|
|
140
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|