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
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# GSD Agent - Local Filesystem Sync Daemon
|
|
2
|
+
|
|
3
|
+
The GSD Agent is a lightweight Node.js daemon that runs on your local machine, automatically discovering GSD projects and synchronizing their planning files to the cloud for real-time web UI updates.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Real-time Sync**: Changes to your `.planning/` directories sync to the cloud within seconds
|
|
8
|
+
- **Multi-user Support**: Secure authentication and data isolation for each user
|
|
9
|
+
- **Automatic Discovery**: Finds GSD projects in your configured watch directories
|
|
10
|
+
- **Conflict Resolution**: Handles concurrent changes between local and web UI
|
|
11
|
+
- **Large File Support**: Files >200KB automatically uploaded to secure storage
|
|
12
|
+
- **Secure Authentication**: Device flow authentication with encrypted credentials
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Node.js 20+ installed
|
|
17
|
+
- Git installed on your system
|
|
18
|
+
- Active GSD account (sign up at your GSD web UI)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### Public npm Package (Recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g gsd-agent
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This installs the agent globally on your system.
|
|
29
|
+
|
|
30
|
+
### Run Without Installing
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx gsd-agent@latest auth
|
|
34
|
+
npx gsd-agent@latest start
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This runs the agent without installing it permanently.
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### 1. Authenticate with your account
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
gsd-agent auth
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This will:
|
|
48
|
+
- Generate a device code
|
|
49
|
+
- Display instructions to visit the web UI
|
|
50
|
+
- Prompt you to enter the code
|
|
51
|
+
- Complete authentication flow
|
|
52
|
+
|
|
53
|
+
### 2. Start the agent daemon
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
gsd-agent start
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Verify status
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
gsd-agent status
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Commands
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|---------|-------------|
|
|
69
|
+
| `gsd-agent auth` | Authenticate with web account |
|
|
70
|
+
| `gsd-agent start` | Start the agent daemon |
|
|
71
|
+
| `gsd-agent status` | Show sync status |
|
|
72
|
+
| `gsd-agent disconnect` | Revoke credentials |
|
|
73
|
+
| `gsd-agent logs` | Tail agent logs |
|
|
74
|
+
| `gsd-agent version` | Show version |
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
Configuration is stored in `~/.gsd-agent/config.json`:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"watch_dirs": [
|
|
83
|
+
"~/Projects",
|
|
84
|
+
"~/Code",
|
|
85
|
+
"~/workspace"
|
|
86
|
+
],
|
|
87
|
+
"ignored_patterns": [
|
|
88
|
+
"node_modules/",
|
|
89
|
+
".git/",
|
|
90
|
+
"dist/",
|
|
91
|
+
"build/",
|
|
92
|
+
"*.log"
|
|
93
|
+
],
|
|
94
|
+
"log_level": "INFO",
|
|
95
|
+
"batch_size": 50,
|
|
96
|
+
"debounce_ms": 300
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## How It Works
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
104
|
+
│ Local Files │ │ GSD Agent │ │ Cloud (Web UI) │
|
|
105
|
+
│ (.planning/) │◄──►│ (Node.js) │◄──►│ (Supabase) │
|
|
106
|
+
│ │ │ - FileWatcher │ │ - Database │
|
|
107
|
+
│ MyProject/ │ │ - SyncEngine │ │ - Storage │
|
|
108
|
+
│ └─ .planning/ │ │ - Realtime │ │ - Realtime │
|
|
109
|
+
│ ├─ PLAN.md │ │ - Auth Flow │ │ │
|
|
110
|
+
│ ├─ STATE.md │ │ │ │ │
|
|
111
|
+
│ └─ etc... │ │ │ │ │
|
|
112
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
1. **File Watching**: Agent monitors `.planning/` directories for changes
|
|
116
|
+
2. **Local Processing**: Changes batched and processed locally
|
|
117
|
+
3. **Cloud Sync**: Changes securely transmitted to Supabase
|
|
118
|
+
4. **Real-time Updates**: Web UI receives updates via Supabase Realtime
|
|
119
|
+
5. **Web Changes**: UI changes pushed back to local files
|
|
120
|
+
|
|
121
|
+
## Security
|
|
122
|
+
|
|
123
|
+
- **End-to-End Encryption**: All data encrypted in transit
|
|
124
|
+
- **Secure Storage**: Credentials stored with restricted permissions (0600)
|
|
125
|
+
- **User Isolation**: Row-level security ensures data isolation
|
|
126
|
+
- **Token Management**: Automatic refresh and secure storage
|
|
127
|
+
- **Audit Trail**: All operations logged for security review
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
### Agent won't start
|
|
132
|
+
- Check authentication: `gsd-agent status`
|
|
133
|
+
- Verify credentials file exists: `~/.gsd-agent/credentials.json`
|
|
134
|
+
- Check logs: `gsd-agent logs`
|
|
135
|
+
|
|
136
|
+
### Files not syncing
|
|
137
|
+
- Ensure `.planning/` directory exists in your project
|
|
138
|
+
- Verify watch directories in config
|
|
139
|
+
- Check network connectivity to Supabase
|
|
140
|
+
|
|
141
|
+
### Authentication issues
|
|
142
|
+
- Run `gsd-agent disconnect` then `gsd-agent auth`
|
|
143
|
+
- Clear credentials: `rm -rf ~/.gsd-agent/credentials.json`
|
|
144
|
+
- Re-authenticate
|
|
145
|
+
|
|
146
|
+
## Auto-start
|
|
147
|
+
|
|
148
|
+
### Linux (systemd)
|
|
149
|
+
```bash
|
|
150
|
+
# Create systemd user service
|
|
151
|
+
mkdir -p ~/.config/systemd/user
|
|
152
|
+
cat > ~/.config/systemd/user/gsd-agent.service << EOF
|
|
153
|
+
[Unit]
|
|
154
|
+
Description=GSD Agent
|
|
155
|
+
After=network.target
|
|
156
|
+
|
|
157
|
+
[Service]
|
|
158
|
+
Type=simple
|
|
159
|
+
ExecStart=$(which gsd-agent) start
|
|
160
|
+
Restart=always
|
|
161
|
+
|
|
162
|
+
[Install]
|
|
163
|
+
WantedBy=default.target
|
|
164
|
+
EOF
|
|
165
|
+
|
|
166
|
+
systemctl --user enable gsd-agent
|
|
167
|
+
systemctl --user start gsd-agent
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### macOS (launchd)
|
|
171
|
+
```bash
|
|
172
|
+
cat > ~/Library/LaunchAgents/com.gsd.agent.plist << EOF
|
|
173
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
174
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
175
|
+
<plist version="1.0">
|
|
176
|
+
<dict>
|
|
177
|
+
<key>Label</key>
|
|
178
|
+
<string>com.gsd.agent</string>
|
|
179
|
+
<key>ProgramArguments</key>
|
|
180
|
+
<array>
|
|
181
|
+
<string>$(which gsd-agent)</string>
|
|
182
|
+
<string>start</string>
|
|
183
|
+
</array>
|
|
184
|
+
<key>RunAtLoad</key>
|
|
185
|
+
<true/>
|
|
186
|
+
<key>KeepAlive</key>
|
|
187
|
+
<true/>
|
|
188
|
+
</dict>
|
|
189
|
+
</plist>
|
|
190
|
+
EOF
|
|
191
|
+
|
|
192
|
+
launchctl load ~/Library/LaunchAgents/com.gsd.agent.plist
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Development
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Clone and install
|
|
199
|
+
git clone https://github.com/your-org/gsd-agent.git
|
|
200
|
+
cd gsd-agent
|
|
201
|
+
npm install
|
|
202
|
+
|
|
203
|
+
# Build
|
|
204
|
+
npm run build
|
|
205
|
+
|
|
206
|
+
# Development mode
|
|
207
|
+
npm run dev
|
|
208
|
+
|
|
209
|
+
# Run tests
|
|
210
|
+
npm test
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Support
|
|
214
|
+
|
|
215
|
+
- Documentation: https://gsd.fahad.ink/docs
|
|
216
|
+
- Issues: https://github.com/your-org/gsd-agent/issues
|
|
217
|
+
- Community: https://discord.gg/your-server
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
**Note**: This agent requires an active internet connection to sync with the cloud. All data is encrypted and secured according to industry standards.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSD Agent CLI
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* gsd-agent auth - Authenticate with web account
|
|
8
|
+
* gsd-agent start - Start the agent daemon
|
|
9
|
+
* gsd-agent status - Show sync status
|
|
10
|
+
* gsd-agent disconnect - Revoke credentials
|
|
11
|
+
* gsd-agent logs - Tail agent logs
|
|
12
|
+
* gsd-agent version - Show version
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { execSync, spawn } = require('child_process');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
|
|
20
|
+
const AGENT_DIST = path.join(__dirname, '..', 'dist', 'index.js');
|
|
21
|
+
const CREDENTIALS_PATH = path.join(os.homedir(), '.gsd-agent', 'credentials.json');
|
|
22
|
+
const LOG_FILE = path.join(os.homedir(), '.gsd-agent', 'logs', 'agent.log');
|
|
23
|
+
const VERSION = '1.0.0';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if agent is authenticated
|
|
27
|
+
*/
|
|
28
|
+
function isAuthenticated() {
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(CREDENTIALS_PATH)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
|
|
34
|
+
return !!(credentials.access_token && credentials.user_id);
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Show help message
|
|
42
|
+
*/
|
|
43
|
+
function showHelp() {
|
|
44
|
+
console.log(`
|
|
45
|
+
GSD Agent v${VERSION}
|
|
46
|
+
Local filesystem sync daemon for GSD projects
|
|
47
|
+
|
|
48
|
+
Usage:
|
|
49
|
+
gsd-agent <command> [options]
|
|
50
|
+
|
|
51
|
+
Commands:
|
|
52
|
+
auth Authenticate with your web account
|
|
53
|
+
start Start the agent daemon (requires auth)
|
|
54
|
+
status Show sync status and connection info
|
|
55
|
+
disconnect Revoke credentials and stop agent
|
|
56
|
+
logs Tail agent logs
|
|
57
|
+
version Show version
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
gsd-agent auth # Start authentication flow
|
|
61
|
+
gsd-agent start # Start syncing
|
|
62
|
+
gsd-agent status # Check sync status
|
|
63
|
+
gsd-agent logs --lines 50 # Show last 50 log lines
|
|
64
|
+
|
|
65
|
+
Documentation:
|
|
66
|
+
https://gsd-webui-domain.com/docs/agent-setup
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Run authentication flow
|
|
72
|
+
*/
|
|
73
|
+
async function runAuth() {
|
|
74
|
+
console.log('Starting GSD Agent authentication...\n');
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Import and run the auth flow
|
|
78
|
+
const { authenticateAgent } = await import('../dist/auth-flow.js');
|
|
79
|
+
const success = await authenticateAgent();
|
|
80
|
+
|
|
81
|
+
if (success) {
|
|
82
|
+
console.log('\n✓ Authentication successful!');
|
|
83
|
+
console.log('You can now run: gsd-agent start\n');
|
|
84
|
+
process.exit(0);
|
|
85
|
+
} else {
|
|
86
|
+
console.log('\n✗ Authentication failed. Please try again.\n');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('Authentication error:', error.message);
|
|
91
|
+
console.log('\nMake sure the agent is built: npm run build\n');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Start the agent daemon
|
|
98
|
+
*/
|
|
99
|
+
function startAgent() {
|
|
100
|
+
if (!isAuthenticated()) {
|
|
101
|
+
console.log('Not authenticated. Please run: gsd-agent auth\n');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('Starting GSD Agent daemon...\n');
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(AGENT_DIST)) {
|
|
108
|
+
console.log('Agent not built. Running build...\n');
|
|
109
|
+
try {
|
|
110
|
+
execSync('npm run build', { stdio: 'inherit', cwd: path.join(__dirname, '..') });
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Build failed. Please run: npm run build\n');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Start the agent
|
|
118
|
+
const agent = spawn('node', [AGENT_DIST], {
|
|
119
|
+
stdio: 'inherit',
|
|
120
|
+
env: { ...process.env }
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
agent.on('error', (error) => {
|
|
124
|
+
console.error('Failed to start agent:', error.message);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
agent.on('exit', (code) => {
|
|
129
|
+
if (code !== 0) {
|
|
130
|
+
console.error(`Agent exited with code ${code}`);
|
|
131
|
+
process.exit(code);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Show agent status
|
|
138
|
+
*/
|
|
139
|
+
function showStatus() {
|
|
140
|
+
console.log('GSD Agent Status\n');
|
|
141
|
+
console.log('================\n');
|
|
142
|
+
|
|
143
|
+
// Check authentication
|
|
144
|
+
if (isAuthenticated()) {
|
|
145
|
+
const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf-8'));
|
|
146
|
+
console.log('Authentication: ✓ Connected');
|
|
147
|
+
console.log(`User ID: ${credentials.user_id}`);
|
|
148
|
+
console.log(`Token expires: ${credentials.expires_at}`);
|
|
149
|
+
} else {
|
|
150
|
+
console.log('Authentication: ✗ Not authenticated');
|
|
151
|
+
console.log('Run: gsd-agent auth\n');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log('');
|
|
155
|
+
|
|
156
|
+
// Check if agent process is running
|
|
157
|
+
try {
|
|
158
|
+
const result = execSync('pgrep -f "gsd-agent" || true', { encoding: 'utf-8' });
|
|
159
|
+
if (result.trim()) {
|
|
160
|
+
console.log('Agent Process: ✓ Running');
|
|
161
|
+
console.log(`PID: ${result.trim().split('\n').join(', ')}`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log('Agent Process: ✗ Not running');
|
|
164
|
+
console.log('Run: gsd-agent start\n');
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
console.log('Agent Process: ✗ Not running');
|
|
168
|
+
console.log('Run: gsd-agent start\n');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log('');
|
|
172
|
+
|
|
173
|
+
// Check log file
|
|
174
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
175
|
+
const stats = fs.statSync(LOG_FILE);
|
|
176
|
+
console.log(`Log File: ${LOG_FILE}`);
|
|
177
|
+
console.log(`Last modified: ${stats.mtime.toISOString()}`);
|
|
178
|
+
console.log('View logs: gsd-agent logs\n');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log('================\n');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Disconnect and clear credentials
|
|
186
|
+
*/
|
|
187
|
+
function disconnect() {
|
|
188
|
+
console.log('Disconnecting GSD Agent...\n');
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Stop running agent process
|
|
192
|
+
try {
|
|
193
|
+
execSync('pkill -f "gsd-agent" || true', { stdio: 'ignore' });
|
|
194
|
+
console.log('✓ Agent process stopped');
|
|
195
|
+
} catch {
|
|
196
|
+
// Process wasn't running
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Clear credentials
|
|
200
|
+
if (fs.existsSync(CREDENTIALS_PATH)) {
|
|
201
|
+
fs.unlinkSync(CREDENTIALS_PATH);
|
|
202
|
+
console.log('✓ Credentials cleared');
|
|
203
|
+
} else {
|
|
204
|
+
console.log('✓ No credentials found');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log('\nAgent disconnected. Run "gsd-agent auth" to reconnect.\n');
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('Disconnect error:', error.message);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Tail agent logs
|
|
216
|
+
*/
|
|
217
|
+
function showLogs(options = {}) {
|
|
218
|
+
const lines = options.lines || 50;
|
|
219
|
+
|
|
220
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
221
|
+
console.log('No log file found. Agent may not have been started yet.\n');
|
|
222
|
+
console.log(`Expected location: ${LOG_FILE}\n`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`Tailing agent logs (last ${lines} lines)...\n`);
|
|
227
|
+
console.log('Press Ctrl+C to stop\n');
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
// Use tail -f to follow logs
|
|
231
|
+
const tail = spawn('tail', ['-f', '-n', String(lines), LOG_FILE], {
|
|
232
|
+
stdio: 'inherit'
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
tail.on('error', (error) => {
|
|
236
|
+
console.error('Failed to tail logs:', error.message);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('Error:', error.message);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Show version
|
|
247
|
+
*/
|
|
248
|
+
function showVersion() {
|
|
249
|
+
console.log(`gsd-agent v${VERSION}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Main CLI entry point
|
|
254
|
+
*/
|
|
255
|
+
async function main() {
|
|
256
|
+
const args = process.argv.slice(2);
|
|
257
|
+
const command = args[0];
|
|
258
|
+
|
|
259
|
+
// Parse options
|
|
260
|
+
const options = {};
|
|
261
|
+
for (let i = 1; i < args.length; i++) {
|
|
262
|
+
if (args[i].startsWith('--')) {
|
|
263
|
+
const key = args[i].slice(2);
|
|
264
|
+
const value = args[i + 1];
|
|
265
|
+
if (value && !value.startsWith('--')) {
|
|
266
|
+
options[key] = value;
|
|
267
|
+
i++;
|
|
268
|
+
} else {
|
|
269
|
+
options[key] = true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
switch (command) {
|
|
275
|
+
case 'auth':
|
|
276
|
+
await runAuth();
|
|
277
|
+
break;
|
|
278
|
+
case 'start':
|
|
279
|
+
startAgent();
|
|
280
|
+
break;
|
|
281
|
+
case 'status':
|
|
282
|
+
showStatus();
|
|
283
|
+
break;
|
|
284
|
+
case 'disconnect':
|
|
285
|
+
disconnect();
|
|
286
|
+
break;
|
|
287
|
+
case 'logs':
|
|
288
|
+
showLogs(options);
|
|
289
|
+
break;
|
|
290
|
+
case 'version':
|
|
291
|
+
showVersion();
|
|
292
|
+
break;
|
|
293
|
+
case '--help':
|
|
294
|
+
case '-h':
|
|
295
|
+
case 'help':
|
|
296
|
+
showHelp();
|
|
297
|
+
break;
|
|
298
|
+
default:
|
|
299
|
+
if (!command) {
|
|
300
|
+
showHelp();
|
|
301
|
+
} else {
|
|
302
|
+
console.log(`Unknown command: ${command}\n`);
|
|
303
|
+
showHelp();
|
|
304
|
+
}
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Run CLI
|
|
310
|
+
main().catch((error) => {
|
|
311
|
+
console.error('Fatal error:', error.message);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
export declare class DeviceAuthFlow {
|
|
13
|
+
private config;
|
|
14
|
+
private logger;
|
|
15
|
+
private supabase;
|
|
16
|
+
constructor();
|
|
17
|
+
/**
|
|
18
|
+
* Generate a random device code (8 characters)
|
|
19
|
+
*/
|
|
20
|
+
private generateDeviceCode;
|
|
21
|
+
/**
|
|
22
|
+
* Generate a user-friendly code (formatted as XXXX-YYYY)
|
|
23
|
+
*/
|
|
24
|
+
private generateUserCode;
|
|
25
|
+
/**
|
|
26
|
+
* Start the device authorization flow
|
|
27
|
+
*/
|
|
28
|
+
startAuthFlow(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Poll for authorization status
|
|
31
|
+
*/
|
|
32
|
+
private pollForAuthorization;
|
|
33
|
+
/**
|
|
34
|
+
* Generate tokens for authorized user
|
|
35
|
+
*/
|
|
36
|
+
private generateUserTokens;
|
|
37
|
+
/**
|
|
38
|
+
* Check if agent is already authenticated
|
|
39
|
+
*/
|
|
40
|
+
static isAuthenticated(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Clear authentication credentials
|
|
43
|
+
*/
|
|
44
|
+
static clearAuth(): Promise<boolean>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Main function to handle authentication command
|
|
48
|
+
*/
|
|
49
|
+
export declare function authenticateAgent(): Promise<boolean>;
|
|
50
|
+
//# sourceMappingURL=auth-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-flow.d.ts","sourceRoot":"","sources":["../src/auth-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyBH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAM;;IAetB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAiFvC;;OAEG;YACW,oBAAoB;IAyClC;;OAEG;YACW,kBAAkB;IAqBhC;;OAEG;IACH,MAAM,CAAC,eAAe,IAAI,OAAO;IAwBjC;;OAEG;WACU,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAY3C;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG1D"}
|