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.
Files changed (155) hide show
  1. package/README.md +221 -0
  2. package/bin/cli.js +313 -0
  3. package/dist/auth-flow.d.ts +50 -0
  4. package/dist/auth-flow.d.ts.map +1 -0
  5. package/dist/auth-flow.js +233 -0
  6. package/dist/auth-flow.js.map +1 -0
  7. package/dist/auth.d.ts +42 -0
  8. package/dist/auth.d.ts.map +1 -0
  9. package/dist/auth.js +117 -0
  10. package/dist/auth.js.map +1 -0
  11. package/dist/command-executor.d.ts +44 -0
  12. package/dist/command-executor.d.ts.map +1 -0
  13. package/dist/command-executor.js +193 -0
  14. package/dist/command-executor.js.map +1 -0
  15. package/dist/command-executor.test.d.ts +8 -0
  16. package/dist/command-executor.test.d.ts.map +1 -0
  17. package/dist/command-executor.test.js +87 -0
  18. package/dist/command-executor.test.js.map +1 -0
  19. package/dist/command-queue.d.ts +44 -0
  20. package/dist/command-queue.d.ts.map +1 -0
  21. package/dist/command-queue.js +184 -0
  22. package/dist/command-queue.js.map +1 -0
  23. package/dist/command-queue.test.d.ts +7 -0
  24. package/dist/command-queue.test.d.ts.map +1 -0
  25. package/dist/command-queue.test.js +220 -0
  26. package/dist/command-queue.test.js.map +1 -0
  27. package/dist/config.d.ts +25 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +103 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/conflict-resolver.d.ts +43 -0
  32. package/dist/conflict-resolver.d.ts.map +1 -0
  33. package/dist/conflict-resolver.js +91 -0
  34. package/dist/conflict-resolver.js.map +1 -0
  35. package/dist/conflict-resolver.test.d.ts +7 -0
  36. package/dist/conflict-resolver.test.d.ts.map +1 -0
  37. package/dist/conflict-resolver.test.js +123 -0
  38. package/dist/conflict-resolver.test.js.map +1 -0
  39. package/dist/discovery.d.ts +59 -0
  40. package/dist/discovery.d.ts.map +1 -0
  41. package/dist/discovery.js +180 -0
  42. package/dist/discovery.js.map +1 -0
  43. package/dist/discovery.test.d.ts +8 -0
  44. package/dist/discovery.test.d.ts.map +1 -0
  45. package/dist/discovery.test.js +132 -0
  46. package/dist/discovery.test.js.map +1 -0
  47. package/dist/hash.d.ts +20 -0
  48. package/dist/hash.d.ts.map +1 -0
  49. package/dist/hash.js +35 -0
  50. package/dist/hash.js.map +1 -0
  51. package/dist/hash.test.d.ts +7 -0
  52. package/dist/hash.test.d.ts.map +1 -0
  53. package/dist/hash.test.js +58 -0
  54. package/dist/hash.test.js.map +1 -0
  55. package/dist/index.d.ts +11 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +202 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/integration.test.d.ts +8 -0
  60. package/dist/integration.test.d.ts.map +1 -0
  61. package/dist/integration.test.js +37 -0
  62. package/dist/integration.test.js.map +1 -0
  63. package/dist/logger.d.ts +68 -0
  64. package/dist/logger.d.ts.map +1 -0
  65. package/dist/logger.js +159 -0
  66. package/dist/logger.js.map +1 -0
  67. package/dist/output-streamer.d.ts +27 -0
  68. package/dist/output-streamer.d.ts.map +1 -0
  69. package/dist/output-streamer.js +71 -0
  70. package/dist/output-streamer.js.map +1 -0
  71. package/dist/output-streamer.test.d.ts +7 -0
  72. package/dist/output-streamer.test.d.ts.map +1 -0
  73. package/dist/output-streamer.test.js +90 -0
  74. package/dist/output-streamer.test.js.map +1 -0
  75. package/dist/realtime-subscriber.d.ts +63 -0
  76. package/dist/realtime-subscriber.d.ts.map +1 -0
  77. package/dist/realtime-subscriber.js +201 -0
  78. package/dist/realtime-subscriber.js.map +1 -0
  79. package/dist/realtime-subscriber.test.d.ts +7 -0
  80. package/dist/realtime-subscriber.test.d.ts.map +1 -0
  81. package/dist/realtime-subscriber.test.js +183 -0
  82. package/dist/realtime-subscriber.test.js.map +1 -0
  83. package/dist/reconnection-manager.d.ts +88 -0
  84. package/dist/reconnection-manager.d.ts.map +1 -0
  85. package/dist/reconnection-manager.js +229 -0
  86. package/dist/reconnection-manager.js.map +1 -0
  87. package/dist/reconnection-manager.test.d.ts +8 -0
  88. package/dist/reconnection-manager.test.d.ts.map +1 -0
  89. package/dist/reconnection-manager.test.js +151 -0
  90. package/dist/reconnection-manager.test.js.map +1 -0
  91. package/dist/remote-sync-handler.d.ts +61 -0
  92. package/dist/remote-sync-handler.d.ts.map +1 -0
  93. package/dist/remote-sync-handler.js +197 -0
  94. package/dist/remote-sync-handler.js.map +1 -0
  95. package/dist/remote-sync-handler.test.d.ts +7 -0
  96. package/dist/remote-sync-handler.test.d.ts.map +1 -0
  97. package/dist/remote-sync-handler.test.js +212 -0
  98. package/dist/remote-sync-handler.test.js.map +1 -0
  99. package/dist/retry.d.ts +35 -0
  100. package/dist/retry.d.ts.map +1 -0
  101. package/dist/retry.js +63 -0
  102. package/dist/retry.js.map +1 -0
  103. package/dist/retry.test.d.ts +5 -0
  104. package/dist/retry.test.d.ts.map +1 -0
  105. package/dist/retry.test.js +84 -0
  106. package/dist/retry.test.js.map +1 -0
  107. package/dist/storage-client.d.ts +69 -0
  108. package/dist/storage-client.d.ts.map +1 -0
  109. package/dist/storage-client.js +168 -0
  110. package/dist/storage-client.js.map +1 -0
  111. package/dist/storage-client.test.d.ts +7 -0
  112. package/dist/storage-client.test.d.ts.map +1 -0
  113. package/dist/storage-client.test.js +126 -0
  114. package/dist/storage-client.test.js.map +1 -0
  115. package/dist/supabase.d.ts +82 -0
  116. package/dist/supabase.d.ts.map +1 -0
  117. package/dist/supabase.js +341 -0
  118. package/dist/supabase.js.map +1 -0
  119. package/dist/supabase.test.d.ts +7 -0
  120. package/dist/supabase.test.d.ts.map +1 -0
  121. package/dist/supabase.test.js +273 -0
  122. package/dist/supabase.test.js.map +1 -0
  123. package/dist/sync-engine.d.ts +84 -0
  124. package/dist/sync-engine.d.ts.map +1 -0
  125. package/dist/sync-engine.js +251 -0
  126. package/dist/sync-engine.js.map +1 -0
  127. package/dist/sync-engine.test.d.ts +7 -0
  128. package/dist/sync-engine.test.d.ts.map +1 -0
  129. package/dist/sync-engine.test.js +241 -0
  130. package/dist/sync-engine.test.js.map +1 -0
  131. package/dist/sync-state.d.ts +82 -0
  132. package/dist/sync-state.d.ts.map +1 -0
  133. package/dist/sync-state.js +145 -0
  134. package/dist/sync-state.js.map +1 -0
  135. package/dist/sync-state.test.d.ts +7 -0
  136. package/dist/sync-state.test.d.ts.map +1 -0
  137. package/dist/sync-state.test.js +129 -0
  138. package/dist/sync-state.test.js.map +1 -0
  139. package/dist/types.d.ts +148 -0
  140. package/dist/types.d.ts.map +1 -0
  141. package/dist/types.js +8 -0
  142. package/dist/types.js.map +1 -0
  143. package/dist/types.test.d.ts +7 -0
  144. package/dist/types.test.d.ts.map +1 -0
  145. package/dist/types.test.js +73 -0
  146. package/dist/types.test.js.map +1 -0
  147. package/dist/watcher.d.ts +55 -0
  148. package/dist/watcher.d.ts.map +1 -0
  149. package/dist/watcher.js +214 -0
  150. package/dist/watcher.js.map +1 -0
  151. package/dist/watcher.test.d.ts +8 -0
  152. package/dist/watcher.test.d.ts.map +1 -0
  153. package/dist/watcher.test.js +164 -0
  154. package/dist/watcher.test.js.map +1 -0
  155. package/package.json +58 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Tests for workspace discovery
3
+ *
4
+ * Verifies that the agent can find .planning/ directories, extract project names,
5
+ * and register workspaces with proper metadata.
6
+ */
7
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import os from 'os';
11
+ import { WorkspaceDiscovery } from './discovery.js';
12
+ import { Logger } from './logger.js';
13
+ describe('WorkspaceDiscovery', () => {
14
+ let tempDir;
15
+ let config;
16
+ let logger;
17
+ beforeEach(() => {
18
+ // Create temporary test directory
19
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-discovery-test-'));
20
+ config = {
21
+ supabase_url: '',
22
+ supabase_key: '',
23
+ watch_dirs: [tempDir],
24
+ ignored_patterns: ['node_modules/', '.git/', 'dist/', 'build/'],
25
+ debounce_ms: 300,
26
+ batch_size: 50,
27
+ log_level: 'ERROR',
28
+ log_file: path.join(tempDir, 'test.log'),
29
+ log_rotation_days: 7
30
+ };
31
+ logger = new Logger(config);
32
+ });
33
+ afterEach(() => {
34
+ // Clean up temporary directory
35
+ if (fs.existsSync(tempDir)) {
36
+ fs.rmSync(tempDir, { recursive: true, force: true });
37
+ }
38
+ });
39
+ it('should find workspace when .planning/ directory exists', async () => {
40
+ // Create test workspace structure
41
+ const projectDir = path.join(tempDir, 'myapp');
42
+ const planningDir = path.join(projectDir, '.planning');
43
+ fs.mkdirSync(planningDir, { recursive: true });
44
+ // Create PROJECT.md
45
+ const projectMd = path.join(planningDir, 'PROJECT.md');
46
+ fs.writeFileSync(projectMd, '# My Test Project\n\nDescription here.');
47
+ const discovery = new WorkspaceDiscovery(config, logger);
48
+ const workspaces = await discovery.scan();
49
+ expect(workspaces).toHaveLength(1);
50
+ expect(workspaces[0].root_path).toBe(projectDir);
51
+ expect(workspaces[0].name).toBe('My Test Project');
52
+ expect(workspaces[0].status).toBe('active');
53
+ expect(workspaces[0].id).toBeTruthy();
54
+ expect(workspaces[0].discovered_at).toBeTruthy();
55
+ expect(workspaces[0].last_sync).toBeTruthy();
56
+ });
57
+ it('should ignore node_modules when scanning', async () => {
58
+ // Create workspace in node_modules (should be ignored)
59
+ const nodeModulesDir = path.join(tempDir, 'node_modules', 'some-package');
60
+ const planningDir = path.join(nodeModulesDir, '.planning');
61
+ fs.mkdirSync(planningDir, { recursive: true });
62
+ fs.writeFileSync(path.join(planningDir, 'PROJECT.md'), '# Package Project');
63
+ const discovery = new WorkspaceDiscovery(config, logger);
64
+ const workspaces = await discovery.scan();
65
+ expect(workspaces).toHaveLength(0);
66
+ });
67
+ it('should extract project name from PROJECT.md', async () => {
68
+ const projectDir = path.join(tempDir, 'test-project');
69
+ const planningDir = path.join(projectDir, '.planning');
70
+ fs.mkdirSync(planningDir, { recursive: true });
71
+ const projectMd = path.join(planningDir, 'PROJECT.md');
72
+ fs.writeFileSync(projectMd, '# GSD Project Manager\n\nSome content.');
73
+ const discovery = new WorkspaceDiscovery(config, logger);
74
+ const result = discovery.parseProjectFile(projectMd);
75
+ expect(result.name).toBe('GSD Project Manager');
76
+ });
77
+ it('should use directory name when PROJECT.md is missing', async () => {
78
+ const projectDir = path.join(tempDir, 'fallback-project');
79
+ const planningDir = path.join(projectDir, '.planning');
80
+ fs.mkdirSync(planningDir, { recursive: true });
81
+ const discovery = new WorkspaceDiscovery(config, logger);
82
+ const workspaces = await discovery.scan();
83
+ expect(workspaces).toHaveLength(1);
84
+ expect(workspaces[0].name).toBe('fallback-project');
85
+ });
86
+ it('should not register duplicate workspaces', async () => {
87
+ const projectDir = path.join(tempDir, 'unique-project');
88
+ const planningDir = path.join(projectDir, '.planning');
89
+ fs.mkdirSync(planningDir, { recursive: true });
90
+ fs.writeFileSync(path.join(planningDir, 'PROJECT.md'), '# Unique Project');
91
+ const discovery = new WorkspaceDiscovery(config, logger);
92
+ // First scan finds the workspace
93
+ const firstScan = await discovery.scan();
94
+ expect(firstScan).toHaveLength(1);
95
+ expect(firstScan[0].name).toBe('Unique Project');
96
+ // Second scan should not return it again (already discovered)
97
+ const secondScan = await discovery.scan();
98
+ expect(secondScan).toHaveLength(0);
99
+ });
100
+ it('should handle multiple workspaces in different directories', async () => {
101
+ // Create two separate workspaces
102
+ const project1 = path.join(tempDir, 'project1');
103
+ const project2 = path.join(tempDir, 'project2');
104
+ fs.mkdirSync(path.join(project1, '.planning'), { recursive: true });
105
+ fs.mkdirSync(path.join(project2, '.planning'), { recursive: true });
106
+ fs.writeFileSync(path.join(project1, '.planning', 'PROJECT.md'), '# Project One');
107
+ fs.writeFileSync(path.join(project2, '.planning', 'PROJECT.md'), '# Project Two');
108
+ const discovery = new WorkspaceDiscovery(config, logger);
109
+ const workspaces = await discovery.scan();
110
+ expect(workspaces).toHaveLength(2);
111
+ const names = workspaces.map(w => w.name).sort();
112
+ expect(names).toEqual(['Project One', 'Project Two']);
113
+ });
114
+ it('should skip directories without read permissions', async () => {
115
+ const restrictedDir = path.join(tempDir, 'restricted');
116
+ const planningDir = path.join(restrictedDir, '.planning');
117
+ fs.mkdirSync(planningDir, { recursive: true });
118
+ // Remove read permissions (Unix only)
119
+ if (process.platform !== 'win32') {
120
+ fs.chmodSync(restrictedDir, 0o000);
121
+ }
122
+ const discovery = new WorkspaceDiscovery(config, logger);
123
+ const workspaces = await discovery.scan();
124
+ // Should not throw, just skip the directory
125
+ expect(workspaces).toHaveLength(0);
126
+ // Restore permissions for cleanup
127
+ if (process.platform !== 'win32') {
128
+ fs.chmodSync(restrictedDir, 0o755);
129
+ }
130
+ });
131
+ });
132
+ //# sourceMappingURL=discovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../src/discovery.test.ts"],"names":[],"mappings":"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"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Content hashing utilities for GSD Agent
3
+ *
4
+ * Provides SHA-256 hashing for file content to detect changes and prevent
5
+ * redundant syncs. Used by FileWatcher to compute content_hash field.
6
+ */
7
+ /**
8
+ * Compute SHA-256 hash of string content
9
+ * @param content String content to hash
10
+ * @returns Lowercase hex string (64 characters)
11
+ */
12
+ export declare function computeHash(content: string): string;
13
+ /**
14
+ * Read file and compute SHA-256 hash of its content
15
+ * @param filePath Absolute path to file
16
+ * @returns Lowercase hex string (64 characters)
17
+ * @throws Error if file doesn't exist or cannot be read
18
+ */
19
+ export declare function hashFile(filePath: string): Promise<string>;
20
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUhE"}
package/dist/hash.js ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Content hashing utilities for GSD Agent
3
+ *
4
+ * Provides SHA-256 hashing for file content to detect changes and prevent
5
+ * redundant syncs. Used by FileWatcher to compute content_hash field.
6
+ */
7
+ import { createHash } from 'crypto';
8
+ import fs from 'fs/promises';
9
+ /**
10
+ * Compute SHA-256 hash of string content
11
+ * @param content String content to hash
12
+ * @returns Lowercase hex string (64 characters)
13
+ */
14
+ export function computeHash(content) {
15
+ return createHash('sha256').update(content).digest('hex');
16
+ }
17
+ /**
18
+ * Read file and compute SHA-256 hash of its content
19
+ * @param filePath Absolute path to file
20
+ * @returns Lowercase hex string (64 characters)
21
+ * @throws Error if file doesn't exist or cannot be read
22
+ */
23
+ export async function hashFile(filePath) {
24
+ try {
25
+ const content = await fs.readFile(filePath, 'utf-8');
26
+ return computeHash(content);
27
+ }
28
+ catch (error) {
29
+ if (error.code === 'ENOENT') {
30
+ throw new Error(`File not found: ${filePath}`);
31
+ }
32
+ throw new Error(`Failed to read file: ${filePath}`);
33
+ }
34
+ }
35
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,MAAM,aAAa,CAAA;AAE5B;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QACpD,OAAO,WAAW,CAAC,OAAO,CAAC,CAAA;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tests for content hashing utilities
3
+ *
4
+ * Verifies SHA-256 hash computation for strings and files.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=hash.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.test.d.ts","sourceRoot":"","sources":["../src/hash.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Tests for content hashing utilities
3
+ *
4
+ * Verifies SHA-256 hash computation for strings and files.
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import { computeHash, hashFile } from './hash.js';
11
+ describe('Hash utilities', () => {
12
+ let tempDir;
13
+ beforeEach(() => {
14
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-hash-test-'));
15
+ });
16
+ afterEach(() => {
17
+ if (fs.existsSync(tempDir)) {
18
+ fs.rmSync(tempDir, { recursive: true, force: true });
19
+ }
20
+ });
21
+ it('should compute SHA-256 hash of string', () => {
22
+ const hash = computeHash('hello');
23
+ // Expected SHA-256 hash of "hello"
24
+ expect(hash).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
25
+ expect(hash).toHaveLength(64);
26
+ });
27
+ it('should compute hash of empty string', () => {
28
+ const hash = computeHash('');
29
+ // Expected SHA-256 hash of empty string
30
+ expect(hash).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
31
+ expect(hash).toHaveLength(64);
32
+ });
33
+ it('should compute different hashes for different content', () => {
34
+ const hash1 = computeHash('hello');
35
+ const hash2 = computeHash('world');
36
+ expect(hash1).not.toBe(hash2);
37
+ });
38
+ it('should read file and compute hash', async () => {
39
+ const testFile = path.join(tempDir, 'test.txt');
40
+ fs.writeFileSync(testFile, 'hello');
41
+ const hash = await hashFile(testFile);
42
+ expect(hash).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
43
+ expect(hash).toHaveLength(64);
44
+ });
45
+ it('should throw error for non-existent file', async () => {
46
+ const nonExistentFile = path.join(tempDir, 'does-not-exist.txt');
47
+ await expect(hashFile(nonExistentFile)).rejects.toThrow('File not found');
48
+ });
49
+ it('should handle multiline file content', async () => {
50
+ const testFile = path.join(tempDir, 'multiline.txt');
51
+ const content = 'line 1\nline 2\nline 3';
52
+ fs.writeFileSync(testFile, content);
53
+ const fileHash = await hashFile(testFile);
54
+ const stringHash = computeHash(content);
55
+ expect(fileHash).toBe(stringHash);
56
+ });
57
+ });
58
+ //# sourceMappingURL=hash.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.test.js","sourceRoot":"","sources":["../src/hash.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * GSD Agent - Main Entry Point
3
+ *
4
+ * Initializes and runs the local filesystem sync daemon.
5
+ * Discovers workspaces, connects to Supabase, starts file watcher and sync engine.
6
+ */
7
+ /**
8
+ * Main function - initializes and runs the agent daemon
9
+ */
10
+ export declare function main(): Promise<void>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiDH;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyM1C"}
package/dist/index.js ADDED
@@ -0,0 +1,202 @@
1
+ /**
2
+ * GSD Agent - Main Entry Point
3
+ *
4
+ * Initializes and runs the local filesystem sync daemon.
5
+ * Discovers workspaces, connects to Supabase, starts file watcher and sync engine.
6
+ */
7
+ import { loadConfig } from './config.js';
8
+ import { createLogger } from './logger.js';
9
+ import { WorkspaceDiscovery } from './discovery.js';
10
+ import { FileWatcher } from './watcher.js';
11
+ import { createSupabaseClient } from './supabase.js';
12
+ import { createStorageClient } from './storage-client.js';
13
+ import { SyncEngine } from './sync-engine.js';
14
+ import { createRealtimeSubscriber } from './realtime-subscriber.js';
15
+ import { createRemoteSyncHandler } from './remote-sync-handler.js';
16
+ import { createConflictResolver } from './conflict-resolver.js';
17
+ import { createSyncStateManager } from './sync-state.js';
18
+ import { createReconnectionManager } from './reconnection-manager.js';
19
+ import { CommandQueue } from './command-queue.js';
20
+ import { createClient } from '@supabase/supabase-js';
21
+ import os from 'os';
22
+ import path from 'path';
23
+ /**
24
+ * Check if agent is authenticated
25
+ */
26
+ function isAuthenticated() {
27
+ try {
28
+ const configDir = path.join(os.homedir(), '.gsd-agent');
29
+ const credentialsPath = path.join(configDir, 'credentials.json');
30
+ if (!require('fs').existsSync(credentialsPath)) {
31
+ return false;
32
+ }
33
+ const credentials = JSON.parse(require('fs').readFileSync(credentialsPath, 'utf-8'));
34
+ if (!credentials.access_token || !credentials.user_id) {
35
+ return false;
36
+ }
37
+ // Check if token is expired
38
+ const expiresAt = new Date(credentials.expires_at);
39
+ if (expiresAt < new Date()) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ /**
49
+ * Main function - initializes and runs the agent daemon
50
+ */
51
+ export async function main() {
52
+ // Check authentication first
53
+ if (!isAuthenticated()) {
54
+ console.error('GSD Agent not authenticated.');
55
+ console.error('Run: gsd-agent auth');
56
+ console.error('Before starting the agent daemon.');
57
+ process.exit(1);
58
+ }
59
+ // 1. Load configuration
60
+ const config = loadConfig();
61
+ // 2. Create logger
62
+ const logger = createLogger(config);
63
+ logger.info('GSD Agent starting', { version: '1.0.0' });
64
+ try {
65
+ // 3. Create SyncStateManager and load state
66
+ const configDir = path.join(os.homedir(), '.gsd-agent');
67
+ const syncStateManager = createSyncStateManager(configDir, logger);
68
+ syncStateManager.loadState();
69
+ logger.info('Loaded sync state', {
70
+ workspace_count: syncStateManager.getAllWorkspaceStates().length
71
+ });
72
+ // 4. Discover workspaces
73
+ logger.info('Scanning for workspaces', { watch_dirs: config.watch_dirs });
74
+ const discovery = new WorkspaceDiscovery(config, logger);
75
+ const workspaces = await discovery.scan();
76
+ logger.info(`Found ${workspaces.length} workspace(s)`, { count: workspaces.length });
77
+ if (workspaces.length === 0) {
78
+ logger.warn('No workspaces found. Create a GSD project with /gsd:new-project');
79
+ // Continue running to detect new workspaces
80
+ }
81
+ // 5. Connect to Supabase with user credentials
82
+ logger.info('Connecting to Supabase');
83
+ const credentialsPath = path.join(configDir, 'credentials.json');
84
+ const credentials = JSON.parse(require('fs').readFileSync(credentialsPath, 'utf-8'));
85
+ const supabaseJsClient = createClient(config.supabase_url, credentials.access_token, {
86
+ auth: {
87
+ persistSession: false,
88
+ autoRefreshToken: false
89
+ },
90
+ db: {
91
+ schema: 'public'
92
+ }
93
+ });
94
+ // 6. Create StorageClient
95
+ const storageClient = createStorageClient(supabaseJsClient, logger);
96
+ // 7. Create SupabaseClient with StorageClient
97
+ const supabase = createSupabaseClient(config, logger, storageClient, credentials.user_id);
98
+ const connectResult = await supabase.connect();
99
+ if (!connectResult.success) {
100
+ logger.error('Failed to connect to Supabase', { error: connectResult.error });
101
+ process.exit(1);
102
+ }
103
+ logger.info('Connected to Supabase');
104
+ // 8. Sync discovered workspaces (MUST succeed before file sync)
105
+ const syncedWorkspaces = [];
106
+ for (const workspace of workspaces) {
107
+ logger.info('Syncing workspace', { name: workspace.name, path: workspace.root_path });
108
+ // Add user_id to workspace for multi-user support
109
+ const workspaceWithUser = {
110
+ ...workspace,
111
+ user_id: credentials.user_id
112
+ };
113
+ const syncResult = await supabase.syncWorkspace(workspaceWithUser);
114
+ if (!syncResult.success) {
115
+ logger.error('Failed to sync workspace, skipping', {
116
+ name: workspace.name,
117
+ error: syncResult.error
118
+ });
119
+ continue;
120
+ }
121
+ syncedWorkspaces.push(workspace);
122
+ }
123
+ if (syncedWorkspaces.length === 0) {
124
+ logger.error('No workspaces successfully synced to database');
125
+ process.exit(1);
126
+ }
127
+ logger.info(`Successfully synced ${syncedWorkspaces.length} workspace(s) to database`);
128
+ // 9. Create file watcher (only for successfully synced workspaces)
129
+ logger.info('Starting filesystem watcher');
130
+ const watcher = new FileWatcher(syncedWorkspaces, config, logger);
131
+ logger.info('FileWatcher created');
132
+ // 10. Create sync engine
133
+ logger.info('Creating SyncEngine');
134
+ const syncEngine = new SyncEngine(watcher, supabase, config, logger);
135
+ logger.info('SyncEngine created');
136
+ // 11. Create RealtimeSubscriber
137
+ logger.info('Creating RealtimeSubscriber');
138
+ const subscriber = createRealtimeSubscriber(supabaseJsClient, config, logger);
139
+ logger.info('RealtimeSubscriber created');
140
+ // 12. Create ConflictResolver
141
+ const conflictResolver = createConflictResolver(logger);
142
+ // 13. Create workspace map for handlers (only synced workspaces)
143
+ const workspaceMap = new Map(syncedWorkspaces.map(w => [w.id, w]));
144
+ // 14. Create RemoteSyncHandler
145
+ const remoteSyncHandler = createRemoteSyncHandler(subscriber, conflictResolver, workspaceMap, logger);
146
+ // 15. Create ReconnectionManager
147
+ const reconnectionManager = createReconnectionManager(supabase, subscriber, remoteSyncHandler, syncEngine, syncStateManager, workspaceMap, logger);
148
+ // 16. Start ReconnectionManager
149
+ reconnectionManager.start();
150
+ logger.info('ReconnectionManager started');
151
+ // 17. Start SyncEngine
152
+ syncEngine.start();
153
+ logger.info('SyncEngine started');
154
+ // 18. Subscribe to Realtime for each synced workspace
155
+ for (const workspace of syncedWorkspaces) {
156
+ subscriber.subscribe(workspace.id);
157
+ logger.info('Subscribed to Realtime', { workspace_id: workspace.id });
158
+ }
159
+ // 19. Start RemoteSyncHandler
160
+ remoteSyncHandler.start();
161
+ logger.info('RemoteSyncHandler started');
162
+ // 20. Create and start CommandQueue
163
+ logger.info('Creating CommandQueue');
164
+ const commandQueue = new CommandQueue(supabaseJsClient);
165
+ commandQueue.start();
166
+ logger.info('Command execution enabled');
167
+ logger.info('Agent running', { workspaces: syncedWorkspaces.length });
168
+ // 20. Handle graceful shutdown
169
+ const shutdown = async (signal) => {
170
+ logger.info(`Received ${signal}, shutting down gracefully`);
171
+ // Stop CommandQueue
172
+ commandQueue.stop();
173
+ logger.info('Command queue stopped');
174
+ // Stop ReconnectionManager
175
+ reconnectionManager.stop();
176
+ // Stop RemoteSyncHandler
177
+ remoteSyncHandler.stop();
178
+ // Unsubscribe from all Realtime channels
179
+ await subscriber.unsubscribeAll();
180
+ // Stop SyncEngine
181
+ await syncEngine.stop();
182
+ // Save sync state to disk
183
+ syncStateManager.saveState();
184
+ logger.info('Saved sync state');
185
+ // Disconnect from Supabase
186
+ await supabase.disconnect();
187
+ logger.info('Agent stopped');
188
+ process.exit(0);
189
+ };
190
+ process.on('SIGINT', () => shutdown('SIGINT'));
191
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
192
+ // Keep process alive
193
+ await new Promise(() => { }); // Never resolves
194
+ }
195
+ catch (error) {
196
+ logger.error('Agent failed to start', { error });
197
+ process.exit(1);
198
+ }
199
+ }
200
+ // Run main function
201
+ main().catch(console.error);
202
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Integration tests for GSD Agent
3
+ *
4
+ * Tests the full agent workflow including workspace discovery, Supabase connection,
5
+ * and component initialization. Full integration requires Supabase instance.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Integration tests for GSD Agent
3
+ *
4
+ * Tests the full agent workflow including workspace discovery, Supabase connection,
5
+ * and component initialization. Full integration requires Supabase instance.
6
+ */
7
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
8
+ import { main } from './index.js';
9
+ import fs from 'fs/promises';
10
+ import path from 'path';
11
+ import os from 'os';
12
+ describe('Agent Integration', () => {
13
+ let testDir;
14
+ beforeAll(async () => {
15
+ // Create temporary test workspace
16
+ testDir = path.join(os.tmpdir(), 'gsd-agent-test-' + Date.now());
17
+ await fs.mkdir(testDir, { recursive: true });
18
+ await fs.mkdir(path.join(testDir, '.planning'), { recursive: true });
19
+ // Create PROJECT.md
20
+ await fs.writeFile(path.join(testDir, '.planning', 'PROJECT.md'), '# Test Project\n\nTest workspace for agent integration test');
21
+ // Set test config
22
+ process.env.SUPABASE_URL = 'https://test.supabase.co';
23
+ process.env.SUPABASE_SERVICE_ROLE_KEY = 'eyJtest';
24
+ });
25
+ afterAll(async () => {
26
+ // Cleanup
27
+ await fs.rm(testDir, { recursive: true, force: true });
28
+ });
29
+ it('should discover test workspace', async () => {
30
+ // This test verifies workspace discovery works
31
+ // Full integration test requires Supabase connection
32
+ // For now, just verify imports and types
33
+ expect(main).toBeDefined();
34
+ expect(typeof main).toBe('function');
35
+ });
36
+ });
37
+ //# sourceMappingURL=integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,MAAM,aAAa,CAAA;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,OAAe,CAAA;IAEnB,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,kCAAkC;QAClC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAChE,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAEpE,oBAAoB;QACpB,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,EAC7C,6DAA6D,CAC9D,CAAA;QAED,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,0BAA0B,CAAA;QACrD,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,SAAS,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,UAAU;QACV,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,+CAA+C;QAC/C,qDAAqD;QACrD,yCAAyC;QACzC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1B,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Structured logging with file rotation for GSD Agent
3
+ *
4
+ * Provides console and file logging with configurable levels, timestamps,
5
+ * and automatic daily log rotation with cleanup of old logs.
6
+ */
7
+ import { SyncConfig } from './types.js';
8
+ /**
9
+ * Logger class for structured logging
10
+ *
11
+ * Writes to both console and file with timestamps, level prefixes, and optional metadata.
12
+ * Respects configured log level to filter messages.
13
+ */
14
+ export declare class Logger {
15
+ private logLevel;
16
+ private logFile;
17
+ private logDir;
18
+ private rotationDays;
19
+ private currentDate;
20
+ constructor(config: SyncConfig);
21
+ /**
22
+ * Get current date string for log rotation (YYYY-MM-DD)
23
+ */
24
+ private getDateString;
25
+ /**
26
+ * Get rotated log file path for a specific date
27
+ */
28
+ private getRotatedLogPath;
29
+ /**
30
+ * Check if log rotation is needed and rotate if necessary
31
+ */
32
+ private checkRotation;
33
+ /**
34
+ * Delete logs older than rotation_days
35
+ */
36
+ private cleanupOldLogs;
37
+ /**
38
+ * Check if a message at the given level should be logged
39
+ */
40
+ private shouldLog;
41
+ /**
42
+ * Format and write log message to console and file
43
+ */
44
+ private log;
45
+ /**
46
+ * Log error message
47
+ */
48
+ error(message: string, meta?: Record<string, any>): void;
49
+ /**
50
+ * Log warning message
51
+ */
52
+ warn(message: string, meta?: Record<string, any>): void;
53
+ /**
54
+ * Log info message
55
+ */
56
+ info(message: string, meta?: Record<string, any>): void;
57
+ /**
58
+ * Log debug message
59
+ */
60
+ debug(message: string, meta?: Record<string, any>): void;
61
+ }
62
+ /**
63
+ * Create a logger instance with the given configuration
64
+ * @param config SyncConfig containing log settings
65
+ * @returns Logger instance
66
+ */
67
+ export declare function createLogger(config: SyncConfig): Logger;
68
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAWvC;;;;;GAKG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,WAAW,CAAQ;gBAEf,MAAM,EAAE,UAAU;IAgB9B;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAiBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACH,OAAO,CAAC,GAAG;IAuBX;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAIxD;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAIvD;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;CAGzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAEvD"}