@gowelle/stint-agent 1.2.35 → 1.2.36
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 +31 -4
- package/dist/{StatusDashboard-N3HF2TD3.js → StatusDashboard-VOOOQA7A.js} +104 -24
- package/dist/api-6KY43TBB.js +7 -0
- package/dist/{chunk-MISINEGG.js → chunk-BYBGWAQO.js} +254 -94
- package/dist/{chunk-RC7Z6GTK.js → chunk-E3WNZ2CK.js} +34 -3
- package/dist/{chunk-4655SBXG.js → chunk-KHLFCZRY.js} +45 -12
- package/dist/{chunk-H5GHDNXY.js → chunk-VE7Z43P7.js} +67 -20
- package/dist/daemon/runner.js +113 -51
- package/dist/index.js +714 -316
- package/package.json +1 -1
- package/dist/api-Z3HF3YU7.js +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
config,
|
|
3
3
|
logger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-E3WNZ2CK.js";
|
|
5
5
|
|
|
6
6
|
// src/services/git.ts
|
|
7
7
|
import simpleGit from "simple-git";
|
|
@@ -30,7 +30,11 @@ var GitServiceImpl = class {
|
|
|
30
30
|
const git = this.getGit(path3);
|
|
31
31
|
return await git.checkIsRepo();
|
|
32
32
|
} catch (error) {
|
|
33
|
-
logger.error(
|
|
33
|
+
logger.error(
|
|
34
|
+
"git",
|
|
35
|
+
`Failed to check if ${path3} is a repo`,
|
|
36
|
+
error
|
|
37
|
+
);
|
|
34
38
|
return false;
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -53,7 +57,10 @@ var GitServiceImpl = class {
|
|
|
53
57
|
onProgress?.("Determining default branch...");
|
|
54
58
|
let defaultBranch = currentBranch;
|
|
55
59
|
try {
|
|
56
|
-
const result = await git.raw([
|
|
60
|
+
const result = await git.raw([
|
|
61
|
+
"symbolic-ref",
|
|
62
|
+
"refs/remotes/origin/HEAD"
|
|
63
|
+
]);
|
|
57
64
|
const match = result.trim().match(/refs\/remotes\/origin\/(.+)/);
|
|
58
65
|
if (match) {
|
|
59
66
|
defaultBranch = match[1];
|
|
@@ -85,8 +92,14 @@ var GitServiceImpl = class {
|
|
|
85
92
|
lastCommitDate: lastCommit.date
|
|
86
93
|
};
|
|
87
94
|
} catch (error) {
|
|
88
|
-
logger.error(
|
|
89
|
-
|
|
95
|
+
logger.error(
|
|
96
|
+
"git",
|
|
97
|
+
`Failed to get repo info for ${path3}`,
|
|
98
|
+
error
|
|
99
|
+
);
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Failed to get repository information: ${error.message}`
|
|
102
|
+
);
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
async stageAll(path3, onOutput) {
|
|
@@ -127,8 +140,14 @@ var GitServiceImpl = class {
|
|
|
127
140
|
const branchSummary = await git.branch();
|
|
128
141
|
return branchSummary.current;
|
|
129
142
|
} catch (error) {
|
|
130
|
-
logger.error(
|
|
131
|
-
|
|
143
|
+
logger.error(
|
|
144
|
+
"git",
|
|
145
|
+
`Failed to get current branch in ${path3}`,
|
|
146
|
+
error
|
|
147
|
+
);
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Failed to get current branch: ${error.message}`
|
|
150
|
+
);
|
|
132
151
|
}
|
|
133
152
|
}
|
|
134
153
|
async getBranches(path3) {
|
|
@@ -221,11 +240,20 @@ var GitServiceImpl = class {
|
|
|
221
240
|
});
|
|
222
241
|
}
|
|
223
242
|
}
|
|
224
|
-
logger.info(
|
|
243
|
+
logger.info(
|
|
244
|
+
"git",
|
|
245
|
+
`Found ${changedFiles.length} changed files in ${path3}`
|
|
246
|
+
);
|
|
225
247
|
return changedFiles;
|
|
226
248
|
} catch (error) {
|
|
227
|
-
logger.error(
|
|
228
|
-
|
|
249
|
+
logger.error(
|
|
250
|
+
"git",
|
|
251
|
+
`Failed to get changed files in ${path3}`,
|
|
252
|
+
error
|
|
253
|
+
);
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Failed to get changed files: ${error.message}`
|
|
256
|
+
);
|
|
229
257
|
}
|
|
230
258
|
}
|
|
231
259
|
/**
|
|
@@ -279,7 +307,10 @@ var ProjectServiceImpl = class {
|
|
|
279
307
|
linkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
280
308
|
};
|
|
281
309
|
config.setProject(linkPath, linkedProject);
|
|
282
|
-
logger.success(
|
|
310
|
+
logger.success(
|
|
311
|
+
"project",
|
|
312
|
+
`Linked ${linkPath} must be to project ${projectId}`
|
|
313
|
+
);
|
|
283
314
|
} catch (error) {
|
|
284
315
|
logger.error("project", "Failed to link project", error);
|
|
285
316
|
throw error;
|
|
@@ -350,7 +381,9 @@ function killProcess(pid, signal = "SIGTERM") {
|
|
|
350
381
|
logger.info("process", `Sent ${signal} to process ${pid}`);
|
|
351
382
|
} catch (error) {
|
|
352
383
|
logger.error("process", `Failed to kill process ${pid}`, error);
|
|
353
|
-
throw new Error(
|
|
384
|
+
throw new Error(
|
|
385
|
+
`Failed to kill process ${pid}: ${error.message}`
|
|
386
|
+
);
|
|
354
387
|
}
|
|
355
388
|
}
|
|
356
389
|
function spawnDetached(command, args, options = {}) {
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
authService,
|
|
3
3
|
config,
|
|
4
4
|
logger
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-E3WNZ2CK.js";
|
|
6
6
|
|
|
7
7
|
// src/utils/circuit-breaker.ts
|
|
8
8
|
var CircuitBreaker = class {
|
|
@@ -61,7 +61,9 @@ var CircuitBreaker = class {
|
|
|
61
61
|
this.lastFailureTime = Date.now();
|
|
62
62
|
this.failureTimestamps.push(this.lastFailureTime);
|
|
63
63
|
const windowStart = this.lastFailureTime - this.options.windowSize;
|
|
64
|
-
this.failureTimestamps = this.failureTimestamps.filter(
|
|
64
|
+
this.failureTimestamps = this.failureTimestamps.filter(
|
|
65
|
+
(ts) => ts > windowStart
|
|
66
|
+
);
|
|
65
67
|
this.failures = this.failureTimestamps.length;
|
|
66
68
|
if (this.state === "HALF_OPEN") {
|
|
67
69
|
this.state = "OPEN";
|
|
@@ -98,7 +100,7 @@ var CircuitBreaker = class {
|
|
|
98
100
|
};
|
|
99
101
|
|
|
100
102
|
// src/services/api.ts
|
|
101
|
-
var AGENT_VERSION = "1.2.
|
|
103
|
+
var AGENT_VERSION = "1.2.36";
|
|
102
104
|
var ApiServiceImpl = class {
|
|
103
105
|
sessionId = null;
|
|
104
106
|
circuitBreaker = new CircuitBreaker({
|
|
@@ -109,6 +111,16 @@ var ApiServiceImpl = class {
|
|
|
109
111
|
windowSize: 6e4
|
|
110
112
|
// 60s failure window
|
|
111
113
|
});
|
|
114
|
+
/**
|
|
115
|
+
* Sync notification preferences from server to local config
|
|
116
|
+
*/
|
|
117
|
+
syncNotificationPreferences(prefs) {
|
|
118
|
+
config.setNotificationsEnabled(prefs.enabled);
|
|
119
|
+
config.setNotificationCategory("commits", prefs.commits);
|
|
120
|
+
config.setNotificationCategory("sync", prefs.sync);
|
|
121
|
+
config.setNotificationCategory("suggestions", prefs.suggestions);
|
|
122
|
+
logger.debug("api", "Synced notification preferences from server");
|
|
123
|
+
}
|
|
112
124
|
/**
|
|
113
125
|
* Get authentication headers for API requests
|
|
114
126
|
* @returns Headers object with Authorization and Content-Type
|
|
@@ -117,12 +129,14 @@ var ApiServiceImpl = class {
|
|
|
117
129
|
async getHeaders() {
|
|
118
130
|
const token = await authService.getToken();
|
|
119
131
|
if (!token) {
|
|
120
|
-
throw new Error(
|
|
132
|
+
throw new Error(
|
|
133
|
+
'No authentication token found. Please run "stint login" first.'
|
|
134
|
+
);
|
|
121
135
|
}
|
|
122
136
|
return {
|
|
123
137
|
Authorization: `Bearer ${token}`,
|
|
124
138
|
"Content-Type": "application/json",
|
|
125
|
-
|
|
139
|
+
Accept: "application/json"
|
|
126
140
|
};
|
|
127
141
|
}
|
|
128
142
|
/**
|
|
@@ -146,7 +160,9 @@ var ApiServiceImpl = class {
|
|
|
146
160
|
});
|
|
147
161
|
if (!response.ok) {
|
|
148
162
|
const errorText = await response.text();
|
|
149
|
-
throw new Error(
|
|
163
|
+
throw new Error(
|
|
164
|
+
`API request failed: ${response.status} ${errorText}`
|
|
165
|
+
);
|
|
150
166
|
}
|
|
151
167
|
return await response.json();
|
|
152
168
|
} catch (error) {
|
|
@@ -175,10 +191,16 @@ var ApiServiceImpl = class {
|
|
|
175
191
|
}
|
|
176
192
|
if (attempt < maxRetries) {
|
|
177
193
|
const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 5e3);
|
|
178
|
-
logger.warn(
|
|
194
|
+
logger.warn(
|
|
195
|
+
"api",
|
|
196
|
+
`${operationName} failed, retrying in ${delay}ms (attempt ${attempt}/${maxRetries})`
|
|
197
|
+
);
|
|
179
198
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
180
199
|
} else {
|
|
181
|
-
logger.error(
|
|
200
|
+
logger.error(
|
|
201
|
+
"api",
|
|
202
|
+
`${operationName} failed after ${maxRetries} attempts`
|
|
203
|
+
);
|
|
182
204
|
}
|
|
183
205
|
}
|
|
184
206
|
}
|
|
@@ -204,6 +226,9 @@ var ApiServiceImpl = class {
|
|
|
204
226
|
});
|
|
205
227
|
const session = response.data;
|
|
206
228
|
this.sessionId = session.id;
|
|
229
|
+
if (response.notification_preferences) {
|
|
230
|
+
this.syncNotificationPreferences(response.notification_preferences);
|
|
231
|
+
}
|
|
207
232
|
logger.success("api", `Agent session connected: ${session.id}`);
|
|
208
233
|
return session;
|
|
209
234
|
}, "Connect");
|
|
@@ -236,12 +261,15 @@ var ApiServiceImpl = class {
|
|
|
236
261
|
throw new Error("No active session");
|
|
237
262
|
}
|
|
238
263
|
await this.withRetry(async () => {
|
|
239
|
-
await this.request("/api/agent/heartbeat", {
|
|
264
|
+
const response = await this.request("/api/agent/heartbeat", {
|
|
240
265
|
method: "POST",
|
|
241
266
|
body: JSON.stringify({
|
|
242
267
|
session_id: this.sessionId
|
|
243
268
|
})
|
|
244
269
|
});
|
|
270
|
+
if (response.notification_preferences) {
|
|
271
|
+
this.syncNotificationPreferences(response.notification_preferences);
|
|
272
|
+
}
|
|
245
273
|
logger.debug("api", "Heartbeat sent");
|
|
246
274
|
}, "Heartbeat");
|
|
247
275
|
}
|
|
@@ -259,7 +287,9 @@ var ApiServiceImpl = class {
|
|
|
259
287
|
const projectId2 = item.project_id || item.projectId;
|
|
260
288
|
const createdAt = item.created_at || item.createdAt;
|
|
261
289
|
if (!projectId2 || !createdAt) {
|
|
262
|
-
throw new Error(
|
|
290
|
+
throw new Error(
|
|
291
|
+
`Invalid commit data received from API: ${JSON.stringify(item)}`
|
|
292
|
+
);
|
|
263
293
|
}
|
|
264
294
|
return {
|
|
265
295
|
id: item.id,
|
|
@@ -279,13 +309,17 @@ var ApiServiceImpl = class {
|
|
|
279
309
|
*/
|
|
280
310
|
async getCommit(commitId) {
|
|
281
311
|
logger.info("api", `Fetching commit ${commitId}`);
|
|
282
|
-
const response = await this.request(
|
|
312
|
+
const response = await this.request(
|
|
313
|
+
`/api/agent/commits/${commitId}`
|
|
314
|
+
);
|
|
283
315
|
const item = response.data;
|
|
284
316
|
const projectId = item.project_id || item.projectId;
|
|
285
317
|
const createdAt = item.created_at || item.createdAt;
|
|
286
318
|
const hasLargeFiles = item.has_large_files || item.hasLargeFiles;
|
|
287
319
|
if (!projectId || !createdAt) {
|
|
288
|
-
throw new Error(
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Invalid commit data received from API: ${JSON.stringify(item)}`
|
|
322
|
+
);
|
|
289
323
|
}
|
|
290
324
|
return {
|
|
291
325
|
id: item.id,
|
|
@@ -305,7 +339,10 @@ var ApiServiceImpl = class {
|
|
|
305
339
|
* @returns Updated commit data
|
|
306
340
|
*/
|
|
307
341
|
async markCommitExecuted(commitId, sha, pushed = true, pushError) {
|
|
308
|
-
logger.info(
|
|
342
|
+
logger.info(
|
|
343
|
+
"api",
|
|
344
|
+
`Marking commit ${commitId} as executed (SHA: ${sha}, pushed: ${pushed})`
|
|
345
|
+
);
|
|
309
346
|
return this.withRetry(async () => {
|
|
310
347
|
const response = await this.request(
|
|
311
348
|
`/api/agent/commits/${commitId}/executed`,
|
|
@@ -319,7 +356,9 @@ var ApiServiceImpl = class {
|
|
|
319
356
|
const createdAt = data.created_at || data.createdAt;
|
|
320
357
|
const executedAt = data.executed_at || data.executedAt;
|
|
321
358
|
if (!projectId || !createdAt) {
|
|
322
|
-
throw new Error(
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Invalid commit data received from API: ${JSON.stringify(data)}`
|
|
361
|
+
);
|
|
323
362
|
}
|
|
324
363
|
const commit = {
|
|
325
364
|
id: data.id,
|
|
@@ -373,7 +412,10 @@ var ApiServiceImpl = class {
|
|
|
373
412
|
method: "POST",
|
|
374
413
|
body: JSON.stringify(payload)
|
|
375
414
|
});
|
|
376
|
-
logger.success(
|
|
415
|
+
logger.success(
|
|
416
|
+
"api",
|
|
417
|
+
`Project ${projectId} synced (${changedFiles?.length ?? 0} changed files)`
|
|
418
|
+
);
|
|
377
419
|
return {
|
|
378
420
|
auto_sync: response.auto_sync
|
|
379
421
|
};
|
|
@@ -391,7 +433,9 @@ var ApiServiceImpl = class {
|
|
|
391
433
|
}
|
|
392
434
|
async getLinkedProjects() {
|
|
393
435
|
logger.info("api", "Fetching linked projects");
|
|
394
|
-
const response = await this.request(
|
|
436
|
+
const response = await this.request(
|
|
437
|
+
"/api/agent/projects"
|
|
438
|
+
);
|
|
395
439
|
const projects = response.data;
|
|
396
440
|
logger.info("api", `Found ${projects.length} linked projects`);
|
|
397
441
|
return projects;
|
|
@@ -407,10 +451,13 @@ var ApiServiceImpl = class {
|
|
|
407
451
|
*/
|
|
408
452
|
async createProject(data) {
|
|
409
453
|
logger.info("api", `Creating project: ${data.name}`);
|
|
410
|
-
const response = await this.request(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
454
|
+
const response = await this.request(
|
|
455
|
+
"/api/agent/projects",
|
|
456
|
+
{
|
|
457
|
+
method: "POST",
|
|
458
|
+
body: JSON.stringify(data)
|
|
459
|
+
}
|
|
460
|
+
);
|
|
414
461
|
const project = response.data;
|
|
415
462
|
logger.success("api", `Created project: ${project.name} (${project.id})`);
|
|
416
463
|
return project;
|
package/dist/daemon/runner.js
CHANGED
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
commitQueue,
|
|
4
4
|
notify,
|
|
5
5
|
websocketService
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-BYBGWAQO.js";
|
|
7
7
|
import {
|
|
8
8
|
apiService
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-VE7Z43P7.js";
|
|
10
10
|
import {
|
|
11
11
|
gitService,
|
|
12
12
|
projectService,
|
|
13
13
|
removePidFile,
|
|
14
14
|
writePidFile
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-KHLFCZRY.js";
|
|
16
16
|
import {
|
|
17
17
|
authService,
|
|
18
18
|
logger
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-E3WNZ2CK.js";
|
|
20
20
|
|
|
21
21
|
// src/daemon/runner.ts
|
|
22
22
|
import "dotenv/config";
|
|
@@ -69,20 +69,30 @@ var FileWatcher = class {
|
|
|
69
69
|
logger.warn("watcher", `Project path does not exist: ${projectPath}`);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
const watcher = fs.watch(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
logger.debug(
|
|
82
|
-
|
|
72
|
+
const watcher = fs.watch(
|
|
73
|
+
projectPath,
|
|
74
|
+
{ recursive: true },
|
|
75
|
+
(eventType, filename) => {
|
|
76
|
+
if (!filename) return;
|
|
77
|
+
const filenameStr = Buffer.isBuffer(filename) ? filename.toString("utf8") : filename;
|
|
78
|
+
if (this.shouldIgnore(filenameStr)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
logger.debug(
|
|
82
|
+
"watcher",
|
|
83
|
+
`File change detected: ${filenameStr} (${eventType}) in ${projectPath}`
|
|
84
|
+
);
|
|
85
|
+
const watcherInfo = this.watchers.get(projectPath);
|
|
86
|
+
if (watcherInfo && !watcherInfo.enabled) {
|
|
87
|
+
logger.debug(
|
|
88
|
+
"watcher",
|
|
89
|
+
`Auto-sync disabled for project ${projectId}, skipping sync`
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.debounceSync(projectPath, projectId);
|
|
83
94
|
}
|
|
84
|
-
|
|
85
|
-
});
|
|
95
|
+
);
|
|
86
96
|
watcher.on("error", (error) => {
|
|
87
97
|
logger.error("watcher", `Watcher error for ${projectPath}`, error);
|
|
88
98
|
const watcherInfo = this.watchers.get(projectPath);
|
|
@@ -101,7 +111,10 @@ var FileWatcher = class {
|
|
|
101
111
|
enabled: true
|
|
102
112
|
// Enabled by default until settings are fetched
|
|
103
113
|
});
|
|
104
|
-
logger.info(
|
|
114
|
+
logger.info(
|
|
115
|
+
"watcher",
|
|
116
|
+
`Started watching: ${projectPath} (project: ${projectId})`
|
|
117
|
+
);
|
|
105
118
|
} catch (error) {
|
|
106
119
|
logger.error("watcher", `Failed to watch ${projectPath}`, error);
|
|
107
120
|
}
|
|
@@ -151,7 +164,11 @@ var FileWatcher = class {
|
|
|
151
164
|
try {
|
|
152
165
|
await this.performSync(projectPath, projectId);
|
|
153
166
|
} catch (error) {
|
|
154
|
-
logger.error(
|
|
167
|
+
logger.error(
|
|
168
|
+
"watcher",
|
|
169
|
+
`Sync failed for ${projectPath}`,
|
|
170
|
+
error
|
|
171
|
+
);
|
|
155
172
|
} finally {
|
|
156
173
|
watcherInfo.debounceTimer = null;
|
|
157
174
|
}
|
|
@@ -165,29 +182,29 @@ var FileWatcher = class {
|
|
|
165
182
|
try {
|
|
166
183
|
const isRepo = await gitService.isRepo(projectPath);
|
|
167
184
|
if (!isRepo) {
|
|
168
|
-
logger.warn(
|
|
185
|
+
logger.warn(
|
|
186
|
+
"watcher",
|
|
187
|
+
`Project ${projectPath} is no longer a git repository`
|
|
188
|
+
);
|
|
169
189
|
return;
|
|
170
190
|
}
|
|
171
191
|
const repoInfo = await gitService.getRepoInfo(projectPath);
|
|
172
192
|
const changedFiles = await gitService.getChangedFiles(projectPath);
|
|
173
|
-
const response = await apiService.syncProject(
|
|
193
|
+
const response = await apiService.syncProject(
|
|
194
|
+
projectId,
|
|
195
|
+
repoInfo,
|
|
196
|
+
changedFiles
|
|
197
|
+
);
|
|
174
198
|
if (response.auto_sync) {
|
|
175
199
|
this.updateProjectSettings(projectId, response.auto_sync);
|
|
176
200
|
}
|
|
177
201
|
logger.success("watcher", `Synced project ${projectId}`);
|
|
178
|
-
let projectName = projectId;
|
|
179
|
-
try {
|
|
180
|
-
const projects = await apiService.getLinkedProjects();
|
|
181
|
-
const p = projects.find((proj) => proj.id === projectId);
|
|
182
|
-
if (p) projectName = p.name;
|
|
183
|
-
} catch (_e) {
|
|
184
|
-
}
|
|
185
|
-
notify({
|
|
186
|
-
title: "Sync Complete",
|
|
187
|
-
message: `Project "${projectName}" synced successfully.`
|
|
188
|
-
});
|
|
189
202
|
} catch (error) {
|
|
190
|
-
logger.error(
|
|
203
|
+
logger.error(
|
|
204
|
+
"watcher",
|
|
205
|
+
`Failed to sync project ${projectId}`,
|
|
206
|
+
error
|
|
207
|
+
);
|
|
191
208
|
}
|
|
192
209
|
}
|
|
193
210
|
/**
|
|
@@ -203,7 +220,11 @@ var FileWatcher = class {
|
|
|
203
220
|
watcherInfo.watcher.close();
|
|
204
221
|
logger.debug("watcher", `Stopped watching: ${projectPath}`);
|
|
205
222
|
} catch (error) {
|
|
206
|
-
logger.error(
|
|
223
|
+
logger.error(
|
|
224
|
+
"watcher",
|
|
225
|
+
`Error stopping watcher for ${projectPath}`,
|
|
226
|
+
error
|
|
227
|
+
);
|
|
207
228
|
}
|
|
208
229
|
}
|
|
209
230
|
this.watchers.clear();
|
|
@@ -226,7 +247,10 @@ var FileWatcher = class {
|
|
|
226
247
|
return;
|
|
227
248
|
}
|
|
228
249
|
}
|
|
229
|
-
logger.warn(
|
|
250
|
+
logger.warn(
|
|
251
|
+
"watcher",
|
|
252
|
+
`Cannot sync: project ${projectId} not found in linked projects`
|
|
253
|
+
);
|
|
230
254
|
}
|
|
231
255
|
/**
|
|
232
256
|
* Remove a project from watching (called when a project is unlinked)
|
|
@@ -242,7 +266,11 @@ var FileWatcher = class {
|
|
|
242
266
|
this.watchers.delete(projectPath);
|
|
243
267
|
logger.info("watcher", `Stopped watching: ${projectPath}`);
|
|
244
268
|
} catch (error) {
|
|
245
|
-
logger.error(
|
|
269
|
+
logger.error(
|
|
270
|
+
"watcher",
|
|
271
|
+
`Error removing watcher for ${projectPath}`,
|
|
272
|
+
error
|
|
273
|
+
);
|
|
246
274
|
}
|
|
247
275
|
}
|
|
248
276
|
/**
|
|
@@ -253,7 +281,10 @@ var FileWatcher = class {
|
|
|
253
281
|
if (watcherInfo.projectId === projectId) {
|
|
254
282
|
watcherInfo.enabled = settings.enabled;
|
|
255
283
|
watcherInfo.debounceDelay = settings.interval * 1e3;
|
|
256
|
-
logger.debug(
|
|
284
|
+
logger.debug(
|
|
285
|
+
"watcher",
|
|
286
|
+
`Updated settings for project ${projectId}: enabled=${settings.enabled}, interval=${settings.interval}s`
|
|
287
|
+
);
|
|
257
288
|
}
|
|
258
289
|
}
|
|
259
290
|
}
|
|
@@ -284,11 +315,15 @@ async function startDaemon() {
|
|
|
284
315
|
logger.success("daemon", "WebSocket connected");
|
|
285
316
|
await websocketService.subscribeToUserChannel(user.id);
|
|
286
317
|
websocketService.onCommitApproved((commit, project) => {
|
|
287
|
-
logger.info(
|
|
318
|
+
logger.info(
|
|
319
|
+
"daemon",
|
|
320
|
+
`Commit approved: ${commit.id} for project ${project.name}`
|
|
321
|
+
);
|
|
288
322
|
notify({
|
|
289
323
|
title: "Commit Approved",
|
|
290
324
|
message: `${commit.message}
|
|
291
|
-
Project: ${project.name}
|
|
325
|
+
Project: ${project.name}`,
|
|
326
|
+
category: "commits"
|
|
292
327
|
});
|
|
293
328
|
commitQueue.addToQueue(commit, project);
|
|
294
329
|
});
|
|
@@ -304,21 +339,29 @@ Project: ${project.name}`
|
|
|
304
339
|
}
|
|
305
340
|
notify({
|
|
306
341
|
title: `New Proposal - ${projectName}`,
|
|
307
|
-
message: commit.message
|
|
342
|
+
message: commit.message,
|
|
343
|
+
category: "commits"
|
|
308
344
|
});
|
|
309
345
|
});
|
|
310
346
|
websocketService.onProjectUpdated((project) => {
|
|
311
347
|
logger.info("daemon", `Project updated: ${project.id} - ${project.name}`);
|
|
312
348
|
if (project.settings?.auto_sync) {
|
|
313
|
-
fileWatcher.updateProjectSettings(
|
|
349
|
+
fileWatcher.updateProjectSettings(
|
|
350
|
+
project.id,
|
|
351
|
+
project.settings.auto_sync
|
|
352
|
+
);
|
|
314
353
|
}
|
|
315
354
|
notify({
|
|
316
355
|
title: "Project Updated",
|
|
317
|
-
message: project.name
|
|
356
|
+
message: project.name,
|
|
357
|
+
category: "sync"
|
|
318
358
|
});
|
|
319
359
|
});
|
|
320
360
|
websocketService.onDisconnect(() => {
|
|
321
|
-
logger.warn(
|
|
361
|
+
logger.warn(
|
|
362
|
+
"daemon",
|
|
363
|
+
"WebSocket disconnected, will attempt to reconnect"
|
|
364
|
+
);
|
|
322
365
|
});
|
|
323
366
|
websocketService.onAgentDisconnected(async (reason) => {
|
|
324
367
|
logger.warn("daemon", `Server disconnected agent: ${reason}`);
|
|
@@ -327,13 +370,16 @@ Project: ${project.name}`
|
|
|
327
370
|
process.exit(0);
|
|
328
371
|
});
|
|
329
372
|
websocketService.onSuggestionCreated((suggestion) => {
|
|
330
|
-
logger.info(
|
|
373
|
+
logger.info(
|
|
374
|
+
"daemon",
|
|
375
|
+
`Suggestion created: ${suggestion.title} (${suggestion.priority})`
|
|
376
|
+
);
|
|
331
377
|
notify({
|
|
332
378
|
title: "New Suggestion",
|
|
333
379
|
message: `${suggestion.title}
|
|
334
380
|
Priority: ${suggestion.priority}`,
|
|
335
|
-
open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}
|
|
336
|
-
|
|
381
|
+
open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}`,
|
|
382
|
+
category: "suggestions"
|
|
337
383
|
});
|
|
338
384
|
});
|
|
339
385
|
websocketService.onSyncRequested(async (projectId) => {
|
|
@@ -350,11 +396,16 @@ Priority: ${suggestion.priority}`,
|
|
|
350
396
|
}
|
|
351
397
|
notify({
|
|
352
398
|
title: "Sync Requested",
|
|
353
|
-
message: `Syncing project "${projectName}"
|
|
399
|
+
message: `Syncing project "${projectName}"...`,
|
|
400
|
+
category: "sync"
|
|
354
401
|
});
|
|
355
402
|
await fileWatcher.syncProjectById(projectId);
|
|
356
403
|
} catch (error) {
|
|
357
|
-
logger.error(
|
|
404
|
+
logger.error(
|
|
405
|
+
"daemon",
|
|
406
|
+
`Failed to sync project ${projectId}`,
|
|
407
|
+
error
|
|
408
|
+
);
|
|
358
409
|
}
|
|
359
410
|
});
|
|
360
411
|
setupSignalHandlers();
|
|
@@ -365,12 +416,19 @@ Priority: ${suggestion.priority}`,
|
|
|
365
416
|
const linkedProjects = projectService.getAllLinkedProjects();
|
|
366
417
|
const projectEntries = Object.entries(linkedProjects);
|
|
367
418
|
if (projectEntries.length > 0) {
|
|
368
|
-
logger.info(
|
|
419
|
+
logger.info(
|
|
420
|
+
"daemon",
|
|
421
|
+
`Syncing ${projectEntries.length} linked project(s) on startup...`
|
|
422
|
+
);
|
|
369
423
|
for (const [, linkedProject] of projectEntries) {
|
|
370
424
|
try {
|
|
371
425
|
await fileWatcher.syncProjectById(linkedProject.projectId);
|
|
372
426
|
} catch (error) {
|
|
373
|
-
logger.error(
|
|
427
|
+
logger.error(
|
|
428
|
+
"daemon",
|
|
429
|
+
`Failed to sync project ${linkedProject.projectId} on startup`,
|
|
430
|
+
error
|
|
431
|
+
);
|
|
374
432
|
}
|
|
375
433
|
}
|
|
376
434
|
logger.success("daemon", "Initial project sync complete");
|
|
@@ -431,7 +489,11 @@ async function shutdown(reason) {
|
|
|
431
489
|
websocketService.disconnect();
|
|
432
490
|
logger.info("daemon", "Disconnected from WebSocket");
|
|
433
491
|
} catch (error) {
|
|
434
|
-
logger.error(
|
|
492
|
+
logger.error(
|
|
493
|
+
"daemon",
|
|
494
|
+
"Failed to disconnect from WebSocket",
|
|
495
|
+
error
|
|
496
|
+
);
|
|
435
497
|
}
|
|
436
498
|
try {
|
|
437
499
|
await apiService.disconnect(shutdownReason);
|