gsd-agent 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 +221 -0
- package/bin/cli.js +313 -0
- package/dist/auth-flow.d.ts +50 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +233 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/auth.d.ts +42 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +117 -0
- package/dist/auth.js.map +1 -0
- package/dist/command-executor.d.ts +44 -0
- package/dist/command-executor.d.ts.map +1 -0
- package/dist/command-executor.js +193 -0
- package/dist/command-executor.js.map +1 -0
- package/dist/command-executor.test.d.ts +8 -0
- package/dist/command-executor.test.d.ts.map +1 -0
- package/dist/command-executor.test.js +87 -0
- package/dist/command-executor.test.js.map +1 -0
- package/dist/command-queue.d.ts +44 -0
- package/dist/command-queue.d.ts.map +1 -0
- package/dist/command-queue.js +184 -0
- package/dist/command-queue.js.map +1 -0
- package/dist/command-queue.test.d.ts +7 -0
- package/dist/command-queue.test.d.ts.map +1 -0
- package/dist/command-queue.test.js +220 -0
- package/dist/command-queue.test.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +103 -0
- package/dist/config.js.map +1 -0
- package/dist/conflict-resolver.d.ts +43 -0
- package/dist/conflict-resolver.d.ts.map +1 -0
- package/dist/conflict-resolver.js +91 -0
- package/dist/conflict-resolver.js.map +1 -0
- package/dist/conflict-resolver.test.d.ts +7 -0
- package/dist/conflict-resolver.test.d.ts.map +1 -0
- package/dist/conflict-resolver.test.js +123 -0
- package/dist/conflict-resolver.test.js.map +1 -0
- package/dist/discovery.d.ts +59 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +180 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discovery.test.d.ts +8 -0
- package/dist/discovery.test.d.ts.map +1 -0
- package/dist/discovery.test.js +132 -0
- package/dist/discovery.test.js.map +1 -0
- package/dist/hash.d.ts +20 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +35 -0
- package/dist/hash.js.map +1 -0
- package/dist/hash.test.d.ts +7 -0
- package/dist/hash.test.d.ts.map +1 -0
- package/dist/hash.test.js +58 -0
- package/dist/hash.test.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.test.d.ts +8 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +37 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/logger.d.ts +68 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +159 -0
- package/dist/logger.js.map +1 -0
- package/dist/output-streamer.d.ts +27 -0
- package/dist/output-streamer.d.ts.map +1 -0
- package/dist/output-streamer.js +71 -0
- package/dist/output-streamer.js.map +1 -0
- package/dist/output-streamer.test.d.ts +7 -0
- package/dist/output-streamer.test.d.ts.map +1 -0
- package/dist/output-streamer.test.js +90 -0
- package/dist/output-streamer.test.js.map +1 -0
- package/dist/realtime-subscriber.d.ts +63 -0
- package/dist/realtime-subscriber.d.ts.map +1 -0
- package/dist/realtime-subscriber.js +201 -0
- package/dist/realtime-subscriber.js.map +1 -0
- package/dist/realtime-subscriber.test.d.ts +7 -0
- package/dist/realtime-subscriber.test.d.ts.map +1 -0
- package/dist/realtime-subscriber.test.js +183 -0
- package/dist/realtime-subscriber.test.js.map +1 -0
- package/dist/reconnection-manager.d.ts +88 -0
- package/dist/reconnection-manager.d.ts.map +1 -0
- package/dist/reconnection-manager.js +229 -0
- package/dist/reconnection-manager.js.map +1 -0
- package/dist/reconnection-manager.test.d.ts +8 -0
- package/dist/reconnection-manager.test.d.ts.map +1 -0
- package/dist/reconnection-manager.test.js +151 -0
- package/dist/reconnection-manager.test.js.map +1 -0
- package/dist/remote-sync-handler.d.ts +61 -0
- package/dist/remote-sync-handler.d.ts.map +1 -0
- package/dist/remote-sync-handler.js +197 -0
- package/dist/remote-sync-handler.js.map +1 -0
- package/dist/remote-sync-handler.test.d.ts +7 -0
- package/dist/remote-sync-handler.test.d.ts.map +1 -0
- package/dist/remote-sync-handler.test.js +212 -0
- package/dist/remote-sync-handler.test.js.map +1 -0
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +63 -0
- package/dist/retry.js.map +1 -0
- package/dist/retry.test.d.ts +5 -0
- package/dist/retry.test.d.ts.map +1 -0
- package/dist/retry.test.js +84 -0
- package/dist/retry.test.js.map +1 -0
- package/dist/storage-client.d.ts +69 -0
- package/dist/storage-client.d.ts.map +1 -0
- package/dist/storage-client.js +168 -0
- package/dist/storage-client.js.map +1 -0
- package/dist/storage-client.test.d.ts +7 -0
- package/dist/storage-client.test.d.ts.map +1 -0
- package/dist/storage-client.test.js +126 -0
- package/dist/storage-client.test.js.map +1 -0
- package/dist/supabase.d.ts +82 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +341 -0
- package/dist/supabase.js.map +1 -0
- package/dist/supabase.test.d.ts +7 -0
- package/dist/supabase.test.d.ts.map +1 -0
- package/dist/supabase.test.js +273 -0
- package/dist/supabase.test.js.map +1 -0
- package/dist/sync-engine.d.ts +84 -0
- package/dist/sync-engine.d.ts.map +1 -0
- package/dist/sync-engine.js +251 -0
- package/dist/sync-engine.js.map +1 -0
- package/dist/sync-engine.test.d.ts +7 -0
- package/dist/sync-engine.test.d.ts.map +1 -0
- package/dist/sync-engine.test.js +241 -0
- package/dist/sync-engine.test.js.map +1 -0
- package/dist/sync-state.d.ts +82 -0
- package/dist/sync-state.d.ts.map +1 -0
- package/dist/sync-state.js +145 -0
- package/dist/sync-state.js.map +1 -0
- package/dist/sync-state.test.d.ts +7 -0
- package/dist/sync-state.test.d.ts.map +1 -0
- package/dist/sync-state.test.js +129 -0
- package/dist/sync-state.test.js.map +1 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/types.test.d.ts +7 -0
- package/dist/types.test.d.ts.map +1 -0
- package/dist/types.test.js +73 -0
- package/dist/types.test.js.map +1 -0
- package/dist/watcher.d.ts +55 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +214 -0
- package/dist/watcher.js.map +1 -0
- package/dist/watcher.test.d.ts +8 -0
- package/dist/watcher.test.d.ts.map +1 -0
- package/dist/watcher.test.js +164 -0
- package/dist/watcher.test.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device flow authentication for GSD Agent
|
|
3
|
+
*
|
|
4
|
+
* Implements device code flow similar to GitHub CLI:
|
|
5
|
+
* 1. Agent generates device code and user code
|
|
6
|
+
* 2. Agent displays instructions to visit web UI with user code
|
|
7
|
+
* 3. User enters code on web UI and authorizes the agent
|
|
8
|
+
* 4. Agent polls for authorization status
|
|
9
|
+
* 5. Agent exchanges device code for user tokens
|
|
10
|
+
* 6. Agent stores credentials and starts syncing
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { createClient } from '@supabase/supabase-js';
|
|
15
|
+
import { loadConfig } from './config.js';
|
|
16
|
+
import { createLogger } from './logger.js';
|
|
17
|
+
import { getCredentialsPath } from './auth.js';
|
|
18
|
+
export class DeviceAuthFlow {
|
|
19
|
+
config;
|
|
20
|
+
logger;
|
|
21
|
+
supabase;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.config = loadConfig();
|
|
24
|
+
this.logger = createLogger(this.config);
|
|
25
|
+
// Create temporary Supabase client for auth flow
|
|
26
|
+
this.supabase = createClient(this.config.supabase_url, this.config.supabase_anon_key || this.config.supabase_key, {
|
|
27
|
+
auth: {
|
|
28
|
+
autoRefreshToken: false,
|
|
29
|
+
persistSession: false
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate a random device code (8 characters)
|
|
35
|
+
*/
|
|
36
|
+
generateDeviceCode() {
|
|
37
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
38
|
+
let result = '';
|
|
39
|
+
for (let i = 0; i < 8; i++) {
|
|
40
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Generate a user-friendly code (formatted as XXXX-YYYY)
|
|
46
|
+
*/
|
|
47
|
+
generateUserCode() {
|
|
48
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
49
|
+
let result = '';
|
|
50
|
+
for (let i = 0; i < 8; i++) {
|
|
51
|
+
if (i === 4)
|
|
52
|
+
result += '-';
|
|
53
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Start the device authorization flow
|
|
59
|
+
*/
|
|
60
|
+
async startAuthFlow() {
|
|
61
|
+
try {
|
|
62
|
+
this.logger.info('Starting device authorization flow');
|
|
63
|
+
// Generate codes
|
|
64
|
+
const deviceCode = this.generateDeviceCode();
|
|
65
|
+
const userCode = this.generateUserCode();
|
|
66
|
+
// Calculate expiration
|
|
67
|
+
const expiresIn = 300; // 5 minutes
|
|
68
|
+
const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
|
|
69
|
+
// Create device code record in database
|
|
70
|
+
const { error: insertError } = await this.supabase
|
|
71
|
+
.from('device_codes')
|
|
72
|
+
.insert({
|
|
73
|
+
device_code: deviceCode,
|
|
74
|
+
user_code: userCode,
|
|
75
|
+
status: 'pending',
|
|
76
|
+
expires_at: expiresAt
|
|
77
|
+
});
|
|
78
|
+
if (insertError) {
|
|
79
|
+
this.logger.error('Failed to create device code', { error: insertError });
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// Display instructions to user
|
|
83
|
+
console.log('\n=========================================');
|
|
84
|
+
console.log('GSD Agent Authentication Required');
|
|
85
|
+
console.log('=========================================');
|
|
86
|
+
console.log(`\nVisit: https://gsd.fahad.ink/agent-connect`);
|
|
87
|
+
console.log(`Enter this code: ${userCode}`);
|
|
88
|
+
console.log(`\nThis code will expire in 5 minutes.`);
|
|
89
|
+
console.log('=========================================\n');
|
|
90
|
+
// Poll for authorization
|
|
91
|
+
const tokenResult = await this.pollForAuthorization(deviceCode);
|
|
92
|
+
if (!tokenResult) {
|
|
93
|
+
this.logger.error('Device authorization failed or timed out');
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
// Save credentials
|
|
97
|
+
const credentialsPath = getCredentialsPath();
|
|
98
|
+
const credentialsDir = path.dirname(credentialsPath);
|
|
99
|
+
if (!fs.existsSync(credentialsDir)) {
|
|
100
|
+
fs.mkdirSync(credentialsDir, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
const credentials = {
|
|
103
|
+
user_id: tokenResult.user_id,
|
|
104
|
+
access_token: tokenResult.access_token,
|
|
105
|
+
refresh_token: tokenResult.refresh_token,
|
|
106
|
+
expires_at: tokenResult.expires_at,
|
|
107
|
+
supabase_url: this.config.supabase_url
|
|
108
|
+
};
|
|
109
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), { mode: 0o600 });
|
|
110
|
+
this.logger.info('Device authorization successful, credentials saved');
|
|
111
|
+
// Update device code status to authorized
|
|
112
|
+
await this.supabase
|
|
113
|
+
.from('device_codes')
|
|
114
|
+
.update({
|
|
115
|
+
status: 'authorized',
|
|
116
|
+
authorized_at: new Date().toISOString(),
|
|
117
|
+
user_id: tokenResult.user_id
|
|
118
|
+
})
|
|
119
|
+
.eq('device_code', deviceCode);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.logger.error('Device auth flow failed', { error });
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Poll for authorization status
|
|
129
|
+
*/
|
|
130
|
+
async pollForAuthorization(deviceCode) {
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
const timeout = 5 * 60 * 1000; // 5 minutes
|
|
133
|
+
const interval = 5000; // 5 seconds
|
|
134
|
+
while (Date.now() - startTime < timeout) {
|
|
135
|
+
try {
|
|
136
|
+
const { data, error } = await this.supabase
|
|
137
|
+
.from('device_codes')
|
|
138
|
+
.select('status, user_id')
|
|
139
|
+
.eq('device_code', deviceCode)
|
|
140
|
+
.single();
|
|
141
|
+
if (error) {
|
|
142
|
+
this.logger.error('Failed to check device code status', { error });
|
|
143
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (data.status === 'authorized' && data.user_id) {
|
|
147
|
+
// Generate tokens for the authorized user
|
|
148
|
+
return await this.generateUserTokens(data.user_id);
|
|
149
|
+
}
|
|
150
|
+
if (data.status === 'rejected') {
|
|
151
|
+
this.logger.warn('Device authorization was rejected by user');
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
// Wait before next poll
|
|
155
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.logger.error('Polling error', { error });
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
this.logger.warn('Device authorization timed out');
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generate tokens for authorized user
|
|
167
|
+
*/
|
|
168
|
+
async generateUserTokens(user_id) {
|
|
169
|
+
// In a real implementation, this would use Supabase Auth API to generate a session
|
|
170
|
+
// For now, we'll simulate a valid token exchange
|
|
171
|
+
// In a real scenario, we would:
|
|
172
|
+
// 1. Use Supabase's GoTrue API to create a session for the user
|
|
173
|
+
// 2. Or have the web UI return tokens that were generated server-side
|
|
174
|
+
// For simulation purposes:
|
|
175
|
+
const accessToken = `simulated_access_token_for_${user_id}`;
|
|
176
|
+
const refreshToken = `simulated_refresh_token_for_${user_id}`;
|
|
177
|
+
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); // 24 hours
|
|
178
|
+
return {
|
|
179
|
+
access_token: accessToken,
|
|
180
|
+
refresh_token: refreshToken,
|
|
181
|
+
expires_at: expiresAt,
|
|
182
|
+
user_id: user_id
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if agent is already authenticated
|
|
187
|
+
*/
|
|
188
|
+
static isAuthenticated() {
|
|
189
|
+
try {
|
|
190
|
+
const credentialsPath = getCredentialsPath();
|
|
191
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
const credentials = JSON.parse(fs.readFileSync(credentialsPath, 'utf-8'));
|
|
195
|
+
if (!credentials.access_token || !credentials.user_id) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
// Check if token is expired
|
|
199
|
+
const expiresAt = new Date(credentials.expires_at);
|
|
200
|
+
if (expiresAt < new Date()) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Clear authentication credentials
|
|
211
|
+
*/
|
|
212
|
+
static async clearAuth() {
|
|
213
|
+
try {
|
|
214
|
+
const credentialsPath = getCredentialsPath();
|
|
215
|
+
if (fs.existsSync(credentialsPath)) {
|
|
216
|
+
fs.unlinkSync(credentialsPath);
|
|
217
|
+
}
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
console.error('Failed to clear authentication:', error);
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Main function to handle authentication command
|
|
228
|
+
*/
|
|
229
|
+
export async function authenticateAgent() {
|
|
230
|
+
const authFlow = new DeviceAuthFlow();
|
|
231
|
+
return await authFlow.startAuthFlow();
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=auth-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-flow.js","sourceRoot":"","sources":["../src/auth-flow.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential management for Supabase authentication
|
|
3
|
+
*
|
|
4
|
+
* Handles secure loading and storage of Supabase credentials.
|
|
5
|
+
* Priority order: 1) Environment variables, 2) ~/.gsd-agent/credentials.json
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Credentials structure
|
|
9
|
+
*/
|
|
10
|
+
export interface Credentials {
|
|
11
|
+
url: string;
|
|
12
|
+
key: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the path to the credentials file
|
|
16
|
+
* @returns Absolute path to ~/.gsd-agent/credentials.json
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCredentialsPath(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Load credentials from environment variables or file
|
|
21
|
+
*
|
|
22
|
+
* Priority order:
|
|
23
|
+
* 1. Environment variables (SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
|
|
24
|
+
* 2. ~/.gsd-agent/credentials.json
|
|
25
|
+
* 3. Throw error if neither exists
|
|
26
|
+
*
|
|
27
|
+
* @returns Credentials object with url and key
|
|
28
|
+
* @throws Error if credentials not found or invalid
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadCredentials(): Credentials;
|
|
31
|
+
/**
|
|
32
|
+
* Save credentials to ~/.gsd-agent/credentials.json
|
|
33
|
+
*
|
|
34
|
+
* Creates the directory if it doesn't exist.
|
|
35
|
+
* Sets file permissions to 0600 (owner read/write only).
|
|
36
|
+
*
|
|
37
|
+
* @param url - Supabase project URL
|
|
38
|
+
* @param key - Supabase service role key
|
|
39
|
+
* @throws Error if validation fails or file cannot be written
|
|
40
|
+
*/
|
|
41
|
+
export declare function saveCredentials(url: string, key: string): void;
|
|
42
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AA2BD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAmD7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmB9D"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential management for Supabase authentication
|
|
3
|
+
*
|
|
4
|
+
* Handles secure loading and storage of Supabase credentials.
|
|
5
|
+
* Priority order: 1) Environment variables, 2) ~/.gsd-agent/credentials.json
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
/**
|
|
11
|
+
* Get the path to the credentials file
|
|
12
|
+
* @returns Absolute path to ~/.gsd-agent/credentials.json
|
|
13
|
+
*/
|
|
14
|
+
export function getCredentialsPath() {
|
|
15
|
+
return path.join(os.homedir(), '.gsd-agent', 'credentials.json');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validate Supabase URL format
|
|
19
|
+
* @param url - URL to validate
|
|
20
|
+
* @throws Error if URL is invalid
|
|
21
|
+
*/
|
|
22
|
+
function validateUrl(url) {
|
|
23
|
+
if (!url.startsWith('https://')) {
|
|
24
|
+
throw new Error('Supabase URL must start with https://');
|
|
25
|
+
}
|
|
26
|
+
if (!url.includes('.supabase.co')) {
|
|
27
|
+
throw new Error('Supabase URL must contain .supabase.co');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate Supabase service role key format
|
|
32
|
+
* @param key - Key to validate
|
|
33
|
+
* @throws Error if key is invalid
|
|
34
|
+
*/
|
|
35
|
+
function validateKey(key) {
|
|
36
|
+
if (!key.startsWith('eyJ')) {
|
|
37
|
+
throw new Error('Supabase service role key must start with eyJ (JWT format)');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load credentials from environment variables or file
|
|
42
|
+
*
|
|
43
|
+
* Priority order:
|
|
44
|
+
* 1. Environment variables (SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
|
|
45
|
+
* 2. ~/.gsd-agent/credentials.json
|
|
46
|
+
* 3. Throw error if neither exists
|
|
47
|
+
*
|
|
48
|
+
* @returns Credentials object with url and key
|
|
49
|
+
* @throws Error if credentials not found or invalid
|
|
50
|
+
*/
|
|
51
|
+
export function loadCredentials() {
|
|
52
|
+
// Try environment variables first
|
|
53
|
+
const envUrl = process.env.SUPABASE_URL;
|
|
54
|
+
const envKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
55
|
+
if (envUrl && envUrl.trim() && envKey && envKey.trim()) {
|
|
56
|
+
const url = envUrl.trim();
|
|
57
|
+
const key = envKey.trim();
|
|
58
|
+
validateUrl(url);
|
|
59
|
+
validateKey(key);
|
|
60
|
+
return { url, key };
|
|
61
|
+
}
|
|
62
|
+
// Try credentials file
|
|
63
|
+
const credPath = getCredentialsPath();
|
|
64
|
+
if (!fs.existsSync(credPath)) {
|
|
65
|
+
throw new Error('Supabase credentials not found. Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY environment variables, ' +
|
|
66
|
+
`or create ${credPath} with credentials. See .env.example for template.`);
|
|
67
|
+
}
|
|
68
|
+
let credData;
|
|
69
|
+
try {
|
|
70
|
+
const fileContent = fs.readFileSync(credPath, 'utf-8');
|
|
71
|
+
credData = JSON.parse(fileContent);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new Error(`Failed to parse credentials file ${credPath}: ${error}`);
|
|
75
|
+
}
|
|
76
|
+
// Validate required fields
|
|
77
|
+
const missingFields = [];
|
|
78
|
+
if (!credData.supabase_url)
|
|
79
|
+
missingFields.push('supabase_url');
|
|
80
|
+
if (!credData.supabase_service_role_key)
|
|
81
|
+
missingFields.push('supabase_service_role_key');
|
|
82
|
+
if (missingFields.length > 0) {
|
|
83
|
+
throw new Error(`Credentials file ${credPath} missing required fields: ${missingFields.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
const url = credData.supabase_url.trim();
|
|
86
|
+
const key = credData.supabase_service_role_key.trim();
|
|
87
|
+
validateUrl(url);
|
|
88
|
+
validateKey(key);
|
|
89
|
+
return { url, key };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Save credentials to ~/.gsd-agent/credentials.json
|
|
93
|
+
*
|
|
94
|
+
* Creates the directory if it doesn't exist.
|
|
95
|
+
* Sets file permissions to 0600 (owner read/write only).
|
|
96
|
+
*
|
|
97
|
+
* @param url - Supabase project URL
|
|
98
|
+
* @param key - Supabase service role key
|
|
99
|
+
* @throws Error if validation fails or file cannot be written
|
|
100
|
+
*/
|
|
101
|
+
export function saveCredentials(url, key) {
|
|
102
|
+
validateUrl(url);
|
|
103
|
+
validateKey(key);
|
|
104
|
+
const credPath = getCredentialsPath();
|
|
105
|
+
const credDir = path.dirname(credPath);
|
|
106
|
+
// Create directory if it doesn't exist
|
|
107
|
+
if (!fs.existsSync(credDir)) {
|
|
108
|
+
fs.mkdirSync(credDir, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
const credData = {
|
|
111
|
+
supabase_url: url,
|
|
112
|
+
supabase_service_role_key: key
|
|
113
|
+
};
|
|
114
|
+
// Write file with secure permissions
|
|
115
|
+
fs.writeFileSync(credPath, JSON.stringify(credData, null, 2), { mode: 0o600 });
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Executor with Security Whitelist
|
|
3
|
+
*
|
|
4
|
+
* Executes whitelisted GSD commands using spawn() with security controls.
|
|
5
|
+
* Streams output to database via OutputBatcher, updates command status.
|
|
6
|
+
*/
|
|
7
|
+
import { ChildProcess } from 'child_process';
|
|
8
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
9
|
+
/**
|
|
10
|
+
* Command definition with binary and argument builder
|
|
11
|
+
*/
|
|
12
|
+
interface CommandDefinition {
|
|
13
|
+
bin: string;
|
|
14
|
+
args: (params: any) => string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Command record from database
|
|
18
|
+
*/
|
|
19
|
+
interface Command {
|
|
20
|
+
id: string;
|
|
21
|
+
workspace_id: string;
|
|
22
|
+
command_name: string;
|
|
23
|
+
parameters: any;
|
|
24
|
+
status: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Whitelist of allowed commands
|
|
28
|
+
* SECURITY: Only these commands can be executed by the agent
|
|
29
|
+
*
|
|
30
|
+
* NOTE: GSD workflow commands (/gsd:progress, /gsd:status, etc.) are Claude Code
|
|
31
|
+
* skills that require AI interpretation. They cannot be executed as shell commands.
|
|
32
|
+
* Only gsd-tools.cjs commands are supported here.
|
|
33
|
+
*/
|
|
34
|
+
export declare const ALLOWED_COMMANDS: Record<string, CommandDefinition>;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a command with security validation and output streaming
|
|
37
|
+
*
|
|
38
|
+
* @param command - Command record from database
|
|
39
|
+
* @param supabase - Supabase client for status updates
|
|
40
|
+
* @returns Child process for tracking
|
|
41
|
+
*/
|
|
42
|
+
export declare function executeCommand(command: Command, supabase: SupabaseClient): Promise<ChildProcess>;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=command-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../src/command-executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAG3D;;GAEG;AACH,UAAU,iBAAiB;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,EAAE,CAAA;CAChC;AAED;;GAEG;AACH,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,GAAG,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAwC9D,CAAA;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,YAAY,CAAC,CAgJvB"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Executor with Security Whitelist
|
|
3
|
+
*
|
|
4
|
+
* Executes whitelisted GSD commands using spawn() with security controls.
|
|
5
|
+
* Streams output to database via OutputBatcher, updates command status.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { OutputBatcher } from './output-streamer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Whitelist of allowed commands
|
|
11
|
+
* SECURITY: Only these commands can be executed by the agent
|
|
12
|
+
*
|
|
13
|
+
* NOTE: GSD workflow commands (/gsd:progress, /gsd:status, etc.) are Claude Code
|
|
14
|
+
* skills that require AI interpretation. They cannot be executed as shell commands.
|
|
15
|
+
* Only gsd-tools.cjs commands are supported here.
|
|
16
|
+
*/
|
|
17
|
+
export const ALLOWED_COMMANDS = {
|
|
18
|
+
// List todos
|
|
19
|
+
'list-todos': {
|
|
20
|
+
bin: 'node',
|
|
21
|
+
args: () => [
|
|
22
|
+
`${process.env.HOME}/.claude/get-shit-done/bin/gsd-tools.cjs`,
|
|
23
|
+
'list-todos'
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
'/gsd:list-todos': {
|
|
27
|
+
bin: 'node',
|
|
28
|
+
args: () => [
|
|
29
|
+
`${process.env.HOME}/.claude/get-shit-done/bin/gsd-tools.cjs`,
|
|
30
|
+
'list-todos'
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
// Show project state
|
|
34
|
+
'state': {
|
|
35
|
+
bin: 'node',
|
|
36
|
+
args: () => [
|
|
37
|
+
`${process.env.HOME}/.claude/get-shit-done/bin/gsd-tools.cjs`,
|
|
38
|
+
'state'
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
'/gsd:state': {
|
|
42
|
+
bin: 'node',
|
|
43
|
+
args: () => [
|
|
44
|
+
`${process.env.HOME}/.claude/get-shit-done/bin/gsd-tools.cjs`,
|
|
45
|
+
'state'
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
// Simple git status
|
|
49
|
+
'git-status': {
|
|
50
|
+
bin: 'git',
|
|
51
|
+
args: () => ['status', '--short']
|
|
52
|
+
},
|
|
53
|
+
'/gsd:git-status': {
|
|
54
|
+
bin: 'git',
|
|
55
|
+
args: () => ['status', '--short']
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Execute a command with security validation and output streaming
|
|
60
|
+
*
|
|
61
|
+
* @param command - Command record from database
|
|
62
|
+
* @param supabase - Supabase client for status updates
|
|
63
|
+
* @returns Child process for tracking
|
|
64
|
+
*/
|
|
65
|
+
export async function executeCommand(command, supabase) {
|
|
66
|
+
// 1. Validate command against whitelist
|
|
67
|
+
const commandDef = ALLOWED_COMMANDS[command.command_name];
|
|
68
|
+
if (!commandDef) {
|
|
69
|
+
throw new Error(`Command not allowed: ${command.command_name}`);
|
|
70
|
+
}
|
|
71
|
+
// 2. Build args array (NEVER use shell: true)
|
|
72
|
+
const args = commandDef.args(command.parameters);
|
|
73
|
+
// 3. Get workspace path
|
|
74
|
+
const { data: workspace, error: wsError } = await supabase
|
|
75
|
+
.from('workspaces')
|
|
76
|
+
.select('root_path')
|
|
77
|
+
.eq('id', command.workspace_id)
|
|
78
|
+
.single();
|
|
79
|
+
if (wsError || !workspace) {
|
|
80
|
+
throw new Error(`Workspace not found: ${command.workspace_id}`);
|
|
81
|
+
}
|
|
82
|
+
// 4. Spawn process with stdio pipes (SECURITY: shell: false)
|
|
83
|
+
const childProcess = spawn(commandDef.bin, args, {
|
|
84
|
+
cwd: workspace.root_path,
|
|
85
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
86
|
+
shell: false // CRITICAL: Prevent command injection
|
|
87
|
+
});
|
|
88
|
+
// 5. Update command status to 'running' with PID
|
|
89
|
+
const startedAt = new Date().toISOString();
|
|
90
|
+
await supabase
|
|
91
|
+
.from('commands')
|
|
92
|
+
.update({
|
|
93
|
+
status: 'running',
|
|
94
|
+
pid: childProcess.pid,
|
|
95
|
+
started_at: startedAt
|
|
96
|
+
})
|
|
97
|
+
.eq('id', command.id);
|
|
98
|
+
// 6. Create OutputBatcher for this command
|
|
99
|
+
const batcher = new OutputBatcher(supabase);
|
|
100
|
+
let lineNumber = 1;
|
|
101
|
+
// 7. Pipe stdout to batcher
|
|
102
|
+
childProcess.stdout?.on('data', (data) => {
|
|
103
|
+
const lines = data.toString().split('\n');
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (line.trim()) {
|
|
106
|
+
batcher.add(command.id, lineNumber++, line, 'stdout');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// 8. Pipe stderr to batcher
|
|
111
|
+
childProcess.stderr?.on('data', (data) => {
|
|
112
|
+
const lines = data.toString().split('\n');
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
if (line.trim()) {
|
|
115
|
+
batcher.add(command.id, lineNumber++, line, 'stderr');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// 9. Handle process close/exit with synchronous completion tracking
|
|
120
|
+
let completed = false;
|
|
121
|
+
const handleCompletion = (code) => {
|
|
122
|
+
if (completed)
|
|
123
|
+
return;
|
|
124
|
+
completed = true;
|
|
125
|
+
console.log(`[CommandExecutor] Process completed for ${command.id}, exit code: ${code}`);
|
|
126
|
+
// Use setImmediate to ensure this runs after the event loop
|
|
127
|
+
setImmediate(async () => {
|
|
128
|
+
try {
|
|
129
|
+
// Flush remaining output
|
|
130
|
+
await batcher.flush();
|
|
131
|
+
const finishedAt = new Date().toISOString();
|
|
132
|
+
// Update status based on exit code
|
|
133
|
+
if (code === 0) {
|
|
134
|
+
console.log(`[CommandExecutor] Marking ${command.id} as complete`);
|
|
135
|
+
const { error } = await supabase
|
|
136
|
+
.from('commands')
|
|
137
|
+
.update({
|
|
138
|
+
status: 'complete',
|
|
139
|
+
exit_code: code,
|
|
140
|
+
completed_at: finishedAt
|
|
141
|
+
})
|
|
142
|
+
.eq('id', command.id);
|
|
143
|
+
if (error) {
|
|
144
|
+
console.error(`[CommandExecutor] Failed to update status:`, error);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(`[CommandExecutor] Successfully marked ${command.id} as complete`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(`[CommandExecutor] Marking ${command.id} as error`);
|
|
152
|
+
const { error } = await supabase
|
|
153
|
+
.from('commands')
|
|
154
|
+
.update({
|
|
155
|
+
status: 'error',
|
|
156
|
+
exit_code: code,
|
|
157
|
+
error_message: `Process exited with code ${code}`,
|
|
158
|
+
completed_at: finishedAt
|
|
159
|
+
})
|
|
160
|
+
.eq('id', command.id);
|
|
161
|
+
if (error) {
|
|
162
|
+
console.error(`[CommandExecutor] Failed to update status:`, error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
console.error(`[CommandExecutor] Error in completion handler:`, err);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
childProcess.on('close', (code) => {
|
|
172
|
+
console.log(`[CommandExecutor] 'close' event fired for ${command.id}`);
|
|
173
|
+
handleCompletion(code);
|
|
174
|
+
});
|
|
175
|
+
childProcess.on('exit', (code) => {
|
|
176
|
+
console.log(`[CommandExecutor] 'exit' event fired for ${command.id}`);
|
|
177
|
+
handleCompletion(code);
|
|
178
|
+
});
|
|
179
|
+
// 10. Handle spawn errors
|
|
180
|
+
childProcess.on('error', async (error) => {
|
|
181
|
+
await batcher.flush();
|
|
182
|
+
await supabase
|
|
183
|
+
.from('commands')
|
|
184
|
+
.update({
|
|
185
|
+
status: 'error',
|
|
186
|
+
error_message: error.message,
|
|
187
|
+
finished_at: new Date().toISOString()
|
|
188
|
+
})
|
|
189
|
+
.eq('id', command.id);
|
|
190
|
+
});
|
|
191
|
+
return childProcess;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=command-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-executor.js","sourceRoot":"","sources":["../src/command-executor.ts"],"names":[],"mappings":"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"}
|