@gowelle/stint-agent 1.2.35 → 1.2.37
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-AMDPK7EQ.js} +104 -24
- package/dist/api-AFSILC7K.js +7 -0
- package/dist/{chunk-RC7Z6GTK.js → chunk-HPHXBSGB.js} +34 -3
- package/dist/{chunk-H5GHDNXY.js → chunk-IBGWKTT7.js} +67 -20
- package/dist/{chunk-MISINEGG.js → chunk-NODAAPCO.js} +254 -94
- package/dist/{chunk-4655SBXG.js → chunk-XRNTJYCQ.js} +45 -12
- package/dist/daemon/runner.js +160 -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-HPHXBSGB.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 = {}) {
|
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-NODAAPCO.js";
|
|
7
7
|
import {
|
|
8
8
|
apiService
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-IBGWKTT7.js";
|
|
10
10
|
import {
|
|
11
11
|
gitService,
|
|
12
12
|
projectService,
|
|
13
13
|
removePidFile,
|
|
14
14
|
writePidFile
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-XRNTJYCQ.js";
|
|
16
16
|
import {
|
|
17
17
|
authService,
|
|
18
18
|
logger
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-HPHXBSGB.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,12 +281,61 @@ 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
|
}
|
|
260
291
|
};
|
|
261
292
|
|
|
293
|
+
// src/daemon/sync.ts
|
|
294
|
+
async function syncPendingCommits() {
|
|
295
|
+
logger.info("sync", "Syncing pending commits...");
|
|
296
|
+
const linkedProjects = projectService.getAllLinkedProjects();
|
|
297
|
+
const linkedProjectIds = Object.values(linkedProjects).map(
|
|
298
|
+
(p) => p.projectId
|
|
299
|
+
);
|
|
300
|
+
if (linkedProjectIds.length === 0) {
|
|
301
|
+
logger.info("sync", "No linked projects found locally.");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
const apiProjects = await apiService.getLinkedProjects();
|
|
306
|
+
const relevantProjects = apiProjects.filter(
|
|
307
|
+
(p) => linkedProjectIds.includes(p.id)
|
|
308
|
+
);
|
|
309
|
+
logger.info(
|
|
310
|
+
"sync",
|
|
311
|
+
`Checking ${relevantProjects.length} projects for pending commits...`
|
|
312
|
+
);
|
|
313
|
+
for (const project of relevantProjects) {
|
|
314
|
+
try {
|
|
315
|
+
const pendingCommits = await apiService.getPendingCommits(project.id);
|
|
316
|
+
if (pendingCommits.length > 0) {
|
|
317
|
+
logger.info(
|
|
318
|
+
"sync",
|
|
319
|
+
`Found ${pendingCommits.length} pending commits for project ${project.name}`
|
|
320
|
+
);
|
|
321
|
+
for (const commit of pendingCommits) {
|
|
322
|
+
commitQueue.addToQueue(commit, project);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
logger.error(
|
|
327
|
+
"sync",
|
|
328
|
+
`Failed to sync commits for project ${project.name}`,
|
|
329
|
+
error
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
logger.success("sync", "Pending commit sync complete");
|
|
334
|
+
} catch (error) {
|
|
335
|
+
logger.error("sync", "Failed to sync pending commits", error);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
262
339
|
// src/daemon/index.ts
|
|
263
340
|
var heartbeatInterval = null;
|
|
264
341
|
var isShuttingDown = false;
|
|
@@ -284,11 +361,15 @@ async function startDaemon() {
|
|
|
284
361
|
logger.success("daemon", "WebSocket connected");
|
|
285
362
|
await websocketService.subscribeToUserChannel(user.id);
|
|
286
363
|
websocketService.onCommitApproved((commit, project) => {
|
|
287
|
-
logger.info(
|
|
364
|
+
logger.info(
|
|
365
|
+
"daemon",
|
|
366
|
+
`Commit approved: ${commit.id} for project ${project.name}`
|
|
367
|
+
);
|
|
288
368
|
notify({
|
|
289
369
|
title: "Commit Approved",
|
|
290
370
|
message: `${commit.message}
|
|
291
|
-
Project: ${project.name}
|
|
371
|
+
Project: ${project.name}`,
|
|
372
|
+
category: "commits"
|
|
292
373
|
});
|
|
293
374
|
commitQueue.addToQueue(commit, project);
|
|
294
375
|
});
|
|
@@ -304,21 +385,29 @@ Project: ${project.name}`
|
|
|
304
385
|
}
|
|
305
386
|
notify({
|
|
306
387
|
title: `New Proposal - ${projectName}`,
|
|
307
|
-
message: commit.message
|
|
388
|
+
message: commit.message,
|
|
389
|
+
category: "commits"
|
|
308
390
|
});
|
|
309
391
|
});
|
|
310
392
|
websocketService.onProjectUpdated((project) => {
|
|
311
393
|
logger.info("daemon", `Project updated: ${project.id} - ${project.name}`);
|
|
312
394
|
if (project.settings?.auto_sync) {
|
|
313
|
-
fileWatcher.updateProjectSettings(
|
|
395
|
+
fileWatcher.updateProjectSettings(
|
|
396
|
+
project.id,
|
|
397
|
+
project.settings.auto_sync
|
|
398
|
+
);
|
|
314
399
|
}
|
|
315
400
|
notify({
|
|
316
401
|
title: "Project Updated",
|
|
317
|
-
message: project.name
|
|
402
|
+
message: project.name,
|
|
403
|
+
category: "sync"
|
|
318
404
|
});
|
|
319
405
|
});
|
|
320
406
|
websocketService.onDisconnect(() => {
|
|
321
|
-
logger.warn(
|
|
407
|
+
logger.warn(
|
|
408
|
+
"daemon",
|
|
409
|
+
"WebSocket disconnected, will attempt to reconnect"
|
|
410
|
+
);
|
|
322
411
|
});
|
|
323
412
|
websocketService.onAgentDisconnected(async (reason) => {
|
|
324
413
|
logger.warn("daemon", `Server disconnected agent: ${reason}`);
|
|
@@ -327,13 +416,16 @@ Project: ${project.name}`
|
|
|
327
416
|
process.exit(0);
|
|
328
417
|
});
|
|
329
418
|
websocketService.onSuggestionCreated((suggestion) => {
|
|
330
|
-
logger.info(
|
|
419
|
+
logger.info(
|
|
420
|
+
"daemon",
|
|
421
|
+
`Suggestion created: ${suggestion.title} (${suggestion.priority})`
|
|
422
|
+
);
|
|
331
423
|
notify({
|
|
332
424
|
title: "New Suggestion",
|
|
333
425
|
message: `${suggestion.title}
|
|
334
426
|
Priority: ${suggestion.priority}`,
|
|
335
|
-
open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}
|
|
336
|
-
|
|
427
|
+
open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}`,
|
|
428
|
+
category: "suggestions"
|
|
337
429
|
});
|
|
338
430
|
});
|
|
339
431
|
websocketService.onSyncRequested(async (projectId) => {
|
|
@@ -350,11 +442,16 @@ Priority: ${suggestion.priority}`,
|
|
|
350
442
|
}
|
|
351
443
|
notify({
|
|
352
444
|
title: "Sync Requested",
|
|
353
|
-
message: `Syncing project "${projectName}"
|
|
445
|
+
message: `Syncing project "${projectName}"...`,
|
|
446
|
+
category: "sync"
|
|
354
447
|
});
|
|
355
448
|
await fileWatcher.syncProjectById(projectId);
|
|
356
449
|
} catch (error) {
|
|
357
|
-
logger.error(
|
|
450
|
+
logger.error(
|
|
451
|
+
"daemon",
|
|
452
|
+
`Failed to sync project ${projectId}`,
|
|
453
|
+
error
|
|
454
|
+
);
|
|
358
455
|
}
|
|
359
456
|
});
|
|
360
457
|
setupSignalHandlers();
|
|
@@ -365,16 +462,24 @@ Priority: ${suggestion.priority}`,
|
|
|
365
462
|
const linkedProjects = projectService.getAllLinkedProjects();
|
|
366
463
|
const projectEntries = Object.entries(linkedProjects);
|
|
367
464
|
if (projectEntries.length > 0) {
|
|
368
|
-
logger.info(
|
|
465
|
+
logger.info(
|
|
466
|
+
"daemon",
|
|
467
|
+
`Syncing ${projectEntries.length} linked project(s) on startup...`
|
|
468
|
+
);
|
|
369
469
|
for (const [, linkedProject] of projectEntries) {
|
|
370
470
|
try {
|
|
371
471
|
await fileWatcher.syncProjectById(linkedProject.projectId);
|
|
372
472
|
} catch (error) {
|
|
373
|
-
logger.error(
|
|
473
|
+
logger.error(
|
|
474
|
+
"daemon",
|
|
475
|
+
`Failed to sync project ${linkedProject.projectId} on startup`,
|
|
476
|
+
error
|
|
477
|
+
);
|
|
374
478
|
}
|
|
375
479
|
}
|
|
376
480
|
logger.success("daemon", "Initial project sync complete");
|
|
377
481
|
}
|
|
482
|
+
await syncPendingCommits();
|
|
378
483
|
logger.success("daemon", "Daemon started successfully");
|
|
379
484
|
await new Promise(() => {
|
|
380
485
|
});
|
|
@@ -431,7 +536,11 @@ async function shutdown(reason) {
|
|
|
431
536
|
websocketService.disconnect();
|
|
432
537
|
logger.info("daemon", "Disconnected from WebSocket");
|
|
433
538
|
} catch (error) {
|
|
434
|
-
logger.error(
|
|
539
|
+
logger.error(
|
|
540
|
+
"daemon",
|
|
541
|
+
"Failed to disconnect from WebSocket",
|
|
542
|
+
error
|
|
543
|
+
);
|
|
435
544
|
}
|
|
436
545
|
try {
|
|
437
546
|
await apiService.disconnect(shutdownReason);
|