@bragduck/mcp 1.0.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/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # @bragduck/mcp
2
+
3
+ MCP (Model Context Protocol) server that lets AI agents log career achievements to [BragDuck](https://bragduck.com).
4
+
5
+ Works with any MCP-compatible client: Claude Code, Cursor, Windsurf, and others.
6
+
7
+ ## Setup
8
+
9
+ ### Claude Code
10
+
11
+ ```bash
12
+ claude mcp add bragduck -- npx @bragduck/mcp
13
+ ```
14
+
15
+ ### Cursor
16
+
17
+ Add to `~/.cursor/mcp.json`:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "bragduck": {
23
+ "command": "npx",
24
+ "args": ["@bragduck/mcp"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### VS Code (Copilot)
31
+
32
+ Add to your VS Code settings JSON:
33
+
34
+ ```json
35
+ {
36
+ "mcp": {
37
+ "servers": {
38
+ "bragduck": {
39
+ "command": "npx",
40
+ "args": ["@bragduck/mcp"]
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Authentication
48
+
49
+ On first use, call the `login` tool. This opens your browser for OAuth authentication with BragDuck. Credentials are stored locally in `~/.bragduck/credentials.enc` (encrypted, shared with the BragDuck CLI).
50
+
51
+ ## Tools
52
+
53
+ ### `login`
54
+
55
+ Authenticate with BragDuck via browser-based OAuth.
56
+
57
+ | Parameter | Type | Required | Description |
58
+ |-----------|------|----------|-------------|
59
+ | `force` | boolean | No | Force re-authentication even if already logged in |
60
+
61
+ ### `log_brag`
62
+
63
+ Log a career achievement to BragDuck.
64
+
65
+ | Parameter | Type | Required | Description |
66
+ |-----------|------|----------|-------------|
67
+ | `title` | string | Yes | Short title (10-200 characters) |
68
+ | `description` | string | Yes | Detailed description of what was accomplished (50-2000 characters) |
69
+ | `type` | string | Yes | Category: `progress`, `achievement`, `recognition`, `support`, `code`, `leadership`, `learning`, `collaboration`, `challenge`, `innovation`, `meeting`, `bug`, `others` |
70
+ | `impact_level` | number | Yes | 0 = none, 1 = low (individual), 2 = medium (team), 3 = high (organization) |
71
+ | `impact_description` | string | Yes | Narrative with scope and measurable outcomes |
72
+ | `tags` | string[] | No | Tags to categorize the brag |
73
+ | `date` | string | No | ISO datetime (defaults to now) |
74
+ | `orgId` | string | No | Organisation ID to associate with |
75
+
76
+ ### `list_brags`
77
+
78
+ List recent brags with optional filtering.
79
+
80
+ | Parameter | Type | Required | Description |
81
+ |-----------|------|----------|-------------|
82
+ | `limit` | number | No | Number of brags to return (default: 10, max: 50) |
83
+ | `search` | string | No | Search by title or description |
84
+ | `tags` | string[] | No | Filter by tags |
85
+
86
+ ### `list_objectives`
87
+
88
+ List your OKRs (Objectives and Key Results). Returns goal IDs that can be used with `link_brag_to_goal`.
89
+
90
+ No parameters required.
91
+
92
+ ### `link_brag_to_goal`
93
+
94
+ Link a brag to a goal/key-result.
95
+
96
+ | Parameter | Type | Required | Description |
97
+ |-----------|------|----------|-------------|
98
+ | `bragId` | string | Yes | The brag ID |
99
+ | `goalId` | string | Yes | The goal ID |
100
+
101
+ ## Environment Variables
102
+
103
+ | Variable | Default | Description |
104
+ |----------|---------|-------------|
105
+ | `BRAGDUCK_API_URL` | `https://api.bragduck.com` | Override API base URL |
106
+
107
+ ## Requirements
108
+
109
+ - Node.js >= 20.0.0
110
+ - A [BragDuck](https://bragduck.com) account
111
+
112
+ ## License
113
+
114
+ MIT
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Make an authenticated API request to the BragDuck backend.
3
+ * Automatically retries once on 401 with a refreshed token.
4
+ */
5
+ export declare function apiRequest<T>(endpoint: string, options?: {
6
+ method?: string;
7
+ body?: unknown;
8
+ params?: Record<string, string | string[]>;
9
+ }): Promise<T>;
@@ -0,0 +1,102 @@
1
+ import { getAccessToken, getRefreshToken, storeCredentials } from './credentials.js';
2
+ const API_BASE_URL = process.env.BRAGDUCK_API_URL || 'https://api.bragduck.com';
3
+ const OAUTH_CLIENT_ID = 'bragduck-cli';
4
+ const TOKEN_ENDPOINT = '/v1/auth/cli/token';
5
+ /**
6
+ * Attempt to refresh the access token using the stored refresh token
7
+ */
8
+ async function refreshAccessToken() {
9
+ const refreshToken = getRefreshToken();
10
+ if (!refreshToken)
11
+ return null;
12
+ try {
13
+ const response = await fetch(`${API_BASE_URL}${TOKEN_ENDPOINT}`, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify({
17
+ refresh_token: refreshToken,
18
+ client_id: OAUTH_CLIENT_ID,
19
+ grant_type: 'refresh_token',
20
+ }),
21
+ });
22
+ if (!response.ok)
23
+ return null;
24
+ const data = await response.json();
25
+ const expiresAt = data.expires_in
26
+ ? Date.now() + data.expires_in * 1000
27
+ : undefined;
28
+ storeCredentials({
29
+ accessToken: data.access_token,
30
+ refreshToken: data.refresh_token || refreshToken,
31
+ expiresAt,
32
+ });
33
+ return data.access_token;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * Make an authenticated API request to the BragDuck backend.
41
+ * Automatically retries once on 401 with a refreshed token.
42
+ */
43
+ export async function apiRequest(endpoint, options = {}) {
44
+ const { method = 'GET', body, params } = options;
45
+ let token = getAccessToken();
46
+ if (!token) {
47
+ throw new Error('NOT_AUTHENTICATED');
48
+ }
49
+ let url = `${API_BASE_URL}${endpoint}`;
50
+ if (params) {
51
+ const searchParams = new URLSearchParams();
52
+ for (const [key, value] of Object.entries(params)) {
53
+ if (Array.isArray(value)) {
54
+ value.forEach((v) => searchParams.append(`${key}[]`, v));
55
+ }
56
+ else {
57
+ searchParams.append(key, value);
58
+ }
59
+ }
60
+ url += `?${searchParams.toString()}`;
61
+ }
62
+ const headers = {
63
+ Authorization: `Bearer ${token}`,
64
+ 'User-Agent': 'BragDuck-MCP/1.0.0',
65
+ };
66
+ if (body) {
67
+ headers['Content-Type'] = 'application/json';
68
+ }
69
+ let response = await fetch(url, {
70
+ method,
71
+ headers,
72
+ body: body ? JSON.stringify(body) : undefined,
73
+ });
74
+ // On 401, try refreshing the token once
75
+ if (response.status === 401) {
76
+ const newToken = await refreshAccessToken();
77
+ if (!newToken) {
78
+ throw new Error('TOKEN_EXPIRED');
79
+ }
80
+ headers.Authorization = `Bearer ${newToken}`;
81
+ response = await fetch(url, {
82
+ method,
83
+ headers,
84
+ body: body ? JSON.stringify(body) : undefined,
85
+ });
86
+ }
87
+ if (!response.ok) {
88
+ const text = await response.text().catch(() => '');
89
+ let msg = `API error: ${response.status} ${response.statusText}`;
90
+ try {
91
+ const errorData = JSON.parse(text);
92
+ msg = errorData.message || errorData.error || msg;
93
+ }
94
+ catch {
95
+ if (text)
96
+ msg = text;
97
+ }
98
+ throw new Error(msg);
99
+ }
100
+ return response.json();
101
+ }
102
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Run the full OAuth login flow: start callback server, open browser, wait for auth.
3
+ * Returns user info on success.
4
+ */
5
+ export declare function login(): Promise<{
6
+ email: string;
7
+ name: string;
8
+ }>;
package/dist/auth.js ADDED
@@ -0,0 +1,202 @@
1
+ import { randomBytes } from 'crypto';
2
+ import { createServer } from 'http';
3
+ import { parse } from 'url';
4
+ import { exec } from 'child_process';
5
+ import { promisify } from 'util';
6
+ import { storeCredentials, storeUserInfo } from './credentials.js';
7
+ const execAsync = promisify(exec);
8
+ const API_BASE_URL = process.env.BRAGDUCK_API_URL || 'https://api.bragduck.com';
9
+ const OAUTH_CLIENT_ID = 'bragduck-cli';
10
+ const CALLBACK_PATH = '/callback';
11
+ const TIMEOUT_MS = 120000;
12
+ const MIN_PORT = 8010;
13
+ const MAX_PORT = 8014;
14
+ async function findAvailablePort() {
15
+ for (let port = MIN_PORT; port <= MAX_PORT; port++) {
16
+ try {
17
+ await new Promise((resolve, reject) => {
18
+ const testServer = createServer();
19
+ testServer.once('error', reject);
20
+ testServer.once('listening', () => {
21
+ testServer.close(() => resolve());
22
+ });
23
+ testServer.listen(port, '127.0.0.1');
24
+ });
25
+ return port;
26
+ }
27
+ catch {
28
+ continue;
29
+ }
30
+ }
31
+ throw new Error(`No available ports found in range ${MIN_PORT}-${MAX_PORT}`);
32
+ }
33
+ async function openBrowser(url) {
34
+ const platform = process.platform;
35
+ let command;
36
+ switch (platform) {
37
+ case 'darwin':
38
+ command = `open "${url}"`;
39
+ break;
40
+ case 'win32':
41
+ command = `start "" "${url}"`;
42
+ break;
43
+ default:
44
+ command = `xdg-open "${url}"`;
45
+ break;
46
+ }
47
+ await execAsync(command);
48
+ }
49
+ const SUCCESS_HTML = `<!DOCTYPE html>
50
+ <html><head><meta charset="UTF-8"><title>BragDuck - Authenticated</title>
51
+ <style>
52
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
53
+ display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0;
54
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
55
+ .container { text-align: center; padding: 2rem; background: rgba(255,255,255,0.1);
56
+ border-radius: 1rem; backdrop-filter: blur(10px); max-width: 500px; }
57
+ h1 { font-size: 2rem; margin: 0 0 1rem; }
58
+ .check { font-size: 4rem; animation: pop 0.3s ease-out; }
59
+ p { font-size: 1.1rem; opacity: 0.9; }
60
+ @keyframes pop { from { transform: scale(0); } to { transform: scale(1); } }
61
+ </style></head><body>
62
+ <div class="container">
63
+ <div class="check">&#10003;</div>
64
+ <h1>Authentication Successful</h1>
65
+ <p>You can close this window and return to your editor.</p>
66
+ </div></body></html>`;
67
+ const ERROR_HTML = (error) => `<!DOCTYPE html>
68
+ <html><head><meta charset="UTF-8"><title>BragDuck - Auth Failed</title>
69
+ <style>
70
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
71
+ display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0;
72
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; }
73
+ .container { text-align: center; padding: 2rem; background: rgba(255,255,255,0.1);
74
+ border-radius: 1rem; backdrop-filter: blur(10px); max-width: 500px; }
75
+ h1 { font-size: 2rem; margin: 0 0 1rem; }
76
+ .err { font-size: 4rem; }
77
+ p { font-size: 1.1rem; opacity: 0.9; }
78
+ .details { background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 0.5rem;
79
+ font-family: monospace; font-size: 0.9rem; margin-top: 1rem; }
80
+ </style></head><body>
81
+ <div class="container">
82
+ <div class="err">&#10007;</div>
83
+ <h1>Authentication Failed</h1>
84
+ <p>Please try again.</p>
85
+ <div class="details">${error}</div>
86
+ </div></body></html>`;
87
+ async function exchangeCodeForToken(code, callbackUrl) {
88
+ const response = await fetch(`${API_BASE_URL}/v1/auth/cli/token`, {
89
+ method: 'POST',
90
+ headers: { 'Content-Type': 'application/json' },
91
+ body: JSON.stringify({
92
+ code,
93
+ redirect_uri: callbackUrl,
94
+ client_id: OAUTH_CLIENT_ID,
95
+ grant_type: 'authorization_code',
96
+ }),
97
+ });
98
+ if (!response.ok) {
99
+ const errorData = await response.json().catch(() => ({}));
100
+ throw new Error(errorData.message || `Token exchange failed: ${response.statusText}`);
101
+ }
102
+ return response.json();
103
+ }
104
+ /**
105
+ * Run the full OAuth login flow: start callback server, open browser, wait for auth.
106
+ * Returns user info on success.
107
+ */
108
+ export async function login() {
109
+ const state = randomBytes(32).toString('hex');
110
+ const port = await findAvailablePort();
111
+ const callbackUrl = `http://127.0.0.1:${port}${CALLBACK_PATH}`;
112
+ const authParams = new URLSearchParams({
113
+ client_id: OAUTH_CLIENT_ID,
114
+ redirect_uri: callbackUrl,
115
+ state,
116
+ });
117
+ const authUrl = `${API_BASE_URL}/v1/auth/cli/initiate?${authParams.toString()}`;
118
+ return new Promise((resolve, reject) => {
119
+ let server = null;
120
+ let timeoutId = null;
121
+ const cleanup = () => {
122
+ if (timeoutId)
123
+ globalThis.clearTimeout(timeoutId);
124
+ if (server) {
125
+ if (typeof server.closeAllConnections === 'function') {
126
+ server.closeAllConnections();
127
+ }
128
+ server.close();
129
+ server.unref();
130
+ }
131
+ };
132
+ const handleRequest = async (req, res) => {
133
+ const parsedUrl = parse(req.url || '', true);
134
+ if (parsedUrl.pathname !== CALLBACK_PATH) {
135
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
136
+ res.end('Not Found');
137
+ return;
138
+ }
139
+ const { code, state: returnedState, error, error_description } = parsedUrl.query;
140
+ if (error) {
141
+ const errorMsg = String(error_description || error);
142
+ res.writeHead(400, { 'Content-Type': 'text/html' });
143
+ res.end(ERROR_HTML(errorMsg));
144
+ cleanup();
145
+ reject(new Error(`OAuth error: ${errorMsg}`));
146
+ return;
147
+ }
148
+ if (!code || !returnedState) {
149
+ res.writeHead(400, { 'Content-Type': 'text/html' });
150
+ res.end(ERROR_HTML('Missing code or state parameter'));
151
+ cleanup();
152
+ reject(new Error('Missing code or state parameter'));
153
+ return;
154
+ }
155
+ if (returnedState !== state) {
156
+ res.writeHead(400, { 'Content-Type': 'text/html' });
157
+ res.end(ERROR_HTML('Invalid state parameter'));
158
+ cleanup();
159
+ reject(new Error('Invalid state parameter (possible CSRF)'));
160
+ return;
161
+ }
162
+ res.writeHead(200, { 'Content-Type': 'text/html' });
163
+ res.end(SUCCESS_HTML);
164
+ try {
165
+ const tokenResponse = await exchangeCodeForToken(String(code), callbackUrl);
166
+ const expiresAt = tokenResponse.expires_in
167
+ ? Date.now() + tokenResponse.expires_in * 1000
168
+ : undefined;
169
+ storeCredentials({
170
+ accessToken: tokenResponse.access_token,
171
+ refreshToken: tokenResponse.refresh_token,
172
+ expiresAt,
173
+ });
174
+ const user = tokenResponse.user || { id: 'unknown', email: 'unknown', name: 'Unknown' };
175
+ storeUserInfo(user);
176
+ globalThis.setTimeout(() => {
177
+ cleanup();
178
+ resolve({ email: user.email, name: user.name });
179
+ }, 100);
180
+ }
181
+ catch (err) {
182
+ cleanup();
183
+ reject(err);
184
+ }
185
+ };
186
+ server = createServer(handleRequest);
187
+ server.on('error', (err) => {
188
+ cleanup();
189
+ reject(new Error(`OAuth server error: ${err.message}`));
190
+ });
191
+ server.listen(port, '127.0.0.1', () => {
192
+ openBrowser(authUrl).catch(() => {
193
+ // If browser opening fails, we'll report it but keep the server running
194
+ });
195
+ });
196
+ timeoutId = globalThis.setTimeout(() => {
197
+ cleanup();
198
+ reject(new Error('Authentication timed out. No callback received within 2 minutes.'));
199
+ }, TIMEOUT_MS);
200
+ });
201
+ }
202
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,YAAY,EAA0D,MAAM,MAAM,CAAC;AAC5F,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnE,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,0BAA0B,CAAC;AAChF,MAAM,eAAe,GAAG,cAAc,CAAC;AACvC,MAAM,aAAa,GAAG,WAAW,CAAC;AAClC,MAAM,UAAU,GAAG,MAAM,CAAC;AAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,MAAM,QAAQ,GAAG,IAAI,CAAC;AAUtB,KAAK,UAAU,iBAAiB;IAC9B,KAAK,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,UAAU,GAAG,YAAY,EAAE,CAAC;gBAClC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACjC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;oBAChC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBACpC,CAAC,CAAC,CAAC;gBACH,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,GAAW;IACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,OAAe,CAAC;IACpB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC;YAC1B,MAAM;QACR,KAAK,OAAO;YACV,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;YAC9B,MAAM;QACR;YACE,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;YAC9B,MAAM;IACV,CAAC;IACD,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;qBAiBA,CAAC;AAEtB,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC;;;;;;;;;;;;;;;;;;yBAkBb,KAAK;qBACT,CAAC;AAEtB,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,WAAmB;IACnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,oBAAoB,EAAE;QAChE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,IAAI;YACJ,YAAY,EAAE,WAAW;YACzB,SAAS,EAAE,eAAe;YAC1B,UAAU,EAAE,oBAAoB;SACjC,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAyB,CAAC;QAClF,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,IAAI,0BAA0B,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAA4B,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACvC,MAAM,WAAW,GAAG,oBAAoB,IAAI,GAAG,aAAa,EAAE,CAAC;IAE/D,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC;QACrC,SAAS,EAAE,eAAe;QAC1B,YAAY,EAAE,WAAW;QACzB,KAAK;KACN,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,GAAG,YAAY,yBAAyB,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC;IAEhF,OAAO,IAAI,OAAO,CAAkC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtE,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,SAAS,GAAoD,IAAI,CAAC;QAEtE,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,SAAS;gBAAE,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;YAClD,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,OAAQ,MAAc,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;oBAC7D,MAAc,CAAC,mBAAmB,EAAE,CAAC;gBACxC,CAAC;gBACD,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,aAAa,GAAG,KAAK,EAAE,GAAoB,EAAE,GAAmB,EAAE,EAAE;YACxE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;YAE7C,IAAI,SAAS,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;gBACzC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;gBACrD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACrB,OAAO;YACT,CAAC;YAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC;YAEjF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,IAAI,KAAK,CAAC,CAAC;gBACpD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC9B,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YAED,IAAI,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,iCAAiC,CAAC,CAAC,CAAC;gBACvD,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;gBACrD,OAAO;YACT,CAAC;YAED,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;gBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBAC/C,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;YACpD,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAEtB,IAAI,CAAC;gBACH,MAAM,aAAa,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;gBAE5E,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU;oBACxC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,UAAU,GAAG,IAAI;oBAC9C,CAAC,CAAC,SAAS,CAAC;gBAEd,gBAAgB,CAAC;oBACf,WAAW,EAAE,aAAa,CAAC,YAAY;oBACvC,YAAY,EAAE,aAAa,CAAC,aAAa;oBACzC,SAAS;iBACV,CAAC,CAAC;gBAEH,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBACxF,aAAa,CAAC,IAAI,CAAC,CAAC;gBAEpB,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE;oBACzB,OAAO,EAAE,CAAC;oBACV,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBAClD,CAAC,EAAE,GAAG,CAAC,CAAC;YACV,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QAErC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;YACpC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC9B,wEAAwE;YAC1E,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE;YACrC,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC,CAAC;QACxF,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,31 @@
1
+ interface ServiceCredentials {
2
+ accessToken: string;
3
+ refreshToken?: string;
4
+ expiresAt?: number;
5
+ }
6
+ interface UserInfo {
7
+ id: string;
8
+ email: string;
9
+ name: string;
10
+ }
11
+ /**
12
+ * Get the bragduck access token from stored credentials
13
+ */
14
+ export declare function getAccessToken(): string | null;
15
+ /**
16
+ * Get the refresh token from stored credentials
17
+ */
18
+ export declare function getRefreshToken(): string | null;
19
+ /**
20
+ * Store credentials after login or token refresh
21
+ */
22
+ export declare function storeCredentials(creds: ServiceCredentials): void;
23
+ /**
24
+ * Get stored user info from the Conf config store (shared with CLI)
25
+ */
26
+ export declare function getUserInfo(): UserInfo | null;
27
+ /**
28
+ * Store user info in the Conf config store (shared with CLI)
29
+ */
30
+ export declare function storeUserInfo(userInfo: UserInfo): void;
31
+ export {};
@@ -0,0 +1,150 @@
1
+ import { createDecipheriv, scryptSync, randomBytes, createCipheriv } from 'crypto';
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+ import Conf from 'conf';
6
+ const APP_NAME = 'bragduck';
7
+ const CREDENTIALS_DIR = '.bragduck';
8
+ const CREDENTIALS_FILE = 'credentials.enc';
9
+ const ENCRYPTION = {
10
+ ALGORITHM: 'aes-256-gcm',
11
+ KEY_LENGTH: 32,
12
+ IV_LENGTH: 16,
13
+ AUTH_TAG_LENGTH: 16,
14
+ SALT_LENGTH: 32,
15
+ };
16
+ function deriveEncryptionKey(salt) {
17
+ const password = `${APP_NAME}-${homedir()}-${process.platform}`;
18
+ return scryptSync(password, salt, ENCRYPTION.KEY_LENGTH);
19
+ }
20
+ function decrypt(encryptedData, key) {
21
+ const decipher = createDecipheriv(ENCRYPTION.ALGORITHM, key, Buffer.from(encryptedData.iv, 'hex'), { authTagLength: ENCRYPTION.AUTH_TAG_LENGTH });
22
+ decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
23
+ let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
24
+ decrypted += decipher.final('utf8');
25
+ return decrypted;
26
+ }
27
+ function encrypt(data, key) {
28
+ const iv = randomBytes(ENCRYPTION.IV_LENGTH);
29
+ const cipher = createCipheriv(ENCRYPTION.ALGORITHM, key, iv, {
30
+ authTagLength: ENCRYPTION.AUTH_TAG_LENGTH,
31
+ });
32
+ let encrypted = cipher.update(data, 'utf8', 'hex');
33
+ encrypted += cipher.final('hex');
34
+ const authTag = cipher.getAuthTag();
35
+ return {
36
+ encrypted,
37
+ iv: iv.toString('hex'),
38
+ authTag: authTag.toString('hex'),
39
+ salt: '',
40
+ };
41
+ }
42
+ /**
43
+ * Read stored credentials from the CLI's encrypted file
44
+ */
45
+ function readStoredCredentials() {
46
+ const credentialsPath = join(homedir(), CREDENTIALS_DIR, CREDENTIALS_FILE);
47
+ if (!existsSync(credentialsPath)) {
48
+ return null;
49
+ }
50
+ try {
51
+ const encryptedData = JSON.parse(readFileSync(credentialsPath, 'utf8'));
52
+ const salt = Buffer.from(encryptedData.salt, 'hex');
53
+ const key = deriveEncryptionKey(salt);
54
+ const decrypted = decrypt(encryptedData, key);
55
+ return JSON.parse(decrypted);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ /**
62
+ * Write credentials to the encrypted file (shared with CLI)
63
+ */
64
+ function writeStoredCredentials(credentials) {
65
+ const credentialsDir = join(homedir(), CREDENTIALS_DIR);
66
+ const credentialsPath = join(credentialsDir, CREDENTIALS_FILE);
67
+ if (!existsSync(credentialsDir)) {
68
+ mkdirSync(credentialsDir, { recursive: true, mode: 0o700 });
69
+ }
70
+ const data = JSON.stringify(credentials);
71
+ const salt = randomBytes(ENCRYPTION.SALT_LENGTH);
72
+ const key = deriveEncryptionKey(salt);
73
+ const encrypted = encrypt(data, key);
74
+ encrypted.salt = salt.toString('hex');
75
+ writeFileSync(credentialsPath, JSON.stringify(encrypted), {
76
+ mode: 0o600,
77
+ encoding: 'utf8',
78
+ });
79
+ }
80
+ /**
81
+ * Get the bragduck access token from stored credentials
82
+ */
83
+ export function getAccessToken() {
84
+ const credentials = readStoredCredentials();
85
+ if (!credentials)
86
+ return null;
87
+ // Handle legacy format
88
+ if (credentials.accessToken && !credentials.services) {
89
+ return credentials.accessToken;
90
+ }
91
+ return credentials.services?.bragduck?.accessToken || null;
92
+ }
93
+ /**
94
+ * Get the refresh token from stored credentials
95
+ */
96
+ export function getRefreshToken() {
97
+ const credentials = readStoredCredentials();
98
+ if (!credentials)
99
+ return null;
100
+ if (credentials.accessToken && !credentials.services) {
101
+ return credentials.refreshToken || null;
102
+ }
103
+ return credentials.services?.bragduck?.refreshToken || null;
104
+ }
105
+ /**
106
+ * Store credentials after login or token refresh
107
+ */
108
+ export function storeCredentials(creds) {
109
+ let existing = readStoredCredentials();
110
+ // Migrate legacy format
111
+ if (existing && existing.accessToken && !existing.services) {
112
+ existing = {
113
+ services: {
114
+ bragduck: {
115
+ accessToken: existing.accessToken,
116
+ refreshToken: existing.refreshToken,
117
+ expiresAt: existing.expiresAt,
118
+ },
119
+ },
120
+ };
121
+ }
122
+ const updated = {
123
+ ...existing,
124
+ services: {
125
+ ...existing?.services,
126
+ bragduck: creds,
127
+ },
128
+ };
129
+ writeStoredCredentials(updated);
130
+ }
131
+ /**
132
+ * Get stored user info from the Conf config store (shared with CLI)
133
+ */
134
+ export function getUserInfo() {
135
+ try {
136
+ const config = new Conf({ projectName: APP_NAME });
137
+ return config.get('userInfo') || null;
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ /**
144
+ * Store user info in the Conf config store (shared with CLI)
145
+ */
146
+ export function storeUserInfo(userInfo) {
147
+ const config = new Conf({ projectName: APP_NAME });
148
+ config.set('userInfo', userInfo);
149
+ }
150
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};