@gowelle/stint-agent 1.0.2 → 1.0.3
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
CHANGED
|
@@ -34,7 +34,7 @@ stint login
|
|
|
34
34
|
# Check your authentication status
|
|
35
35
|
stint whoami
|
|
36
36
|
|
|
37
|
-
# Link a project
|
|
37
|
+
# Link a project (or create a new one)
|
|
38
38
|
cd /path/to/your/project
|
|
39
39
|
stint link
|
|
40
40
|
|
|
@@ -47,6 +47,13 @@ stint daemon status
|
|
|
47
47
|
|
|
48
48
|
## Commands
|
|
49
49
|
|
|
50
|
+
### General
|
|
51
|
+
|
|
52
|
+
| Command | Description |
|
|
53
|
+
|---------|-------------|
|
|
54
|
+
| `stint --version`, `stint -V` | Show current agent version |
|
|
55
|
+
| `stint --help`, `stint -h` | Show help information |
|
|
56
|
+
|
|
50
57
|
### Authentication
|
|
51
58
|
|
|
52
59
|
| Command | Description |
|
|
@@ -69,7 +76,7 @@ stint daemon status
|
|
|
69
76
|
|
|
70
77
|
| Command | Description |
|
|
71
78
|
|---------|-------------|
|
|
72
|
-
| `stint link` | Link current directory to a Stint project |
|
|
79
|
+
| `stint link` | Link current directory to a Stint project (or create a new one) |
|
|
73
80
|
| `stint unlink [--force]` | Remove project link |
|
|
74
81
|
| `stint status` | Show project, git, auth, and daemon status |
|
|
75
82
|
| `stint sync` | Manually sync repository information to server |
|
|
@@ -254,7 +254,7 @@ var AuthServiceImpl = class {
|
|
|
254
254
|
return null;
|
|
255
255
|
}
|
|
256
256
|
try {
|
|
257
|
-
const { apiService: apiService2 } = await import("./api-
|
|
257
|
+
const { apiService: apiService2 } = await import("./api-37DCZYLF.js");
|
|
258
258
|
const user = await apiService2.getCurrentUser();
|
|
259
259
|
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
260
260
|
return user;
|
|
@@ -274,7 +274,7 @@ var AuthServiceImpl = class {
|
|
|
274
274
|
var authService = new AuthServiceImpl();
|
|
275
275
|
|
|
276
276
|
// src/services/api.ts
|
|
277
|
-
var AGENT_VERSION = "1.0.
|
|
277
|
+
var AGENT_VERSION = "1.0.3";
|
|
278
278
|
var ApiServiceImpl = class {
|
|
279
279
|
sessionId = null;
|
|
280
280
|
async getHeaders() {
|
|
@@ -351,7 +351,7 @@ var ApiServiceImpl = class {
|
|
|
351
351
|
return session;
|
|
352
352
|
}, "Connect");
|
|
353
353
|
}
|
|
354
|
-
async disconnect() {
|
|
354
|
+
async disconnect(reason) {
|
|
355
355
|
if (!this.sessionId) {
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
@@ -359,7 +359,8 @@ var ApiServiceImpl = class {
|
|
|
359
359
|
await this.request("/api/agent/disconnect", {
|
|
360
360
|
method: "POST",
|
|
361
361
|
body: JSON.stringify({
|
|
362
|
-
session_id: this.sessionId
|
|
362
|
+
session_id: this.sessionId,
|
|
363
|
+
reason: reason || "Agent disconnected"
|
|
363
364
|
})
|
|
364
365
|
});
|
|
365
366
|
this.sessionId = null;
|
package/dist/daemon/runner.js
CHANGED
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
projectService,
|
|
6
6
|
removePidFile,
|
|
7
7
|
writePidFile
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-EVWSJ3RU.js";
|
|
9
9
|
import {
|
|
10
10
|
apiService,
|
|
11
11
|
authService,
|
|
12
12
|
config,
|
|
13
13
|
logger
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-NMOUYAW2.js";
|
|
15
15
|
|
|
16
16
|
// src/daemon/runner.ts
|
|
17
17
|
import "dotenv/config";
|
|
@@ -27,8 +27,12 @@ var WebSocketServiceImpl = class {
|
|
|
27
27
|
isManualDisconnect = false;
|
|
28
28
|
// Event handlers
|
|
29
29
|
commitApprovedHandlers = [];
|
|
30
|
+
commitPendingHandlers = [];
|
|
31
|
+
suggestionCreatedHandlers = [];
|
|
30
32
|
projectUpdatedHandlers = [];
|
|
31
33
|
disconnectHandlers = [];
|
|
34
|
+
agentDisconnectedHandlers = [];
|
|
35
|
+
syncRequestedHandlers = [];
|
|
32
36
|
async connect() {
|
|
33
37
|
try {
|
|
34
38
|
const token = await authService.getToken();
|
|
@@ -108,12 +112,24 @@ var WebSocketServiceImpl = class {
|
|
|
108
112
|
onCommitApproved(handler) {
|
|
109
113
|
this.commitApprovedHandlers.push(handler);
|
|
110
114
|
}
|
|
115
|
+
onCommitPending(handler) {
|
|
116
|
+
this.commitPendingHandlers.push(handler);
|
|
117
|
+
}
|
|
118
|
+
onSuggestionCreated(handler) {
|
|
119
|
+
this.suggestionCreatedHandlers.push(handler);
|
|
120
|
+
}
|
|
111
121
|
onProjectUpdated(handler) {
|
|
112
122
|
this.projectUpdatedHandlers.push(handler);
|
|
113
123
|
}
|
|
114
124
|
onDisconnect(handler) {
|
|
115
125
|
this.disconnectHandlers.push(handler);
|
|
116
126
|
}
|
|
127
|
+
onAgentDisconnected(handler) {
|
|
128
|
+
this.agentDisconnectedHandlers.push(handler);
|
|
129
|
+
}
|
|
130
|
+
onSyncRequested(handler) {
|
|
131
|
+
this.syncRequestedHandlers.push(handler);
|
|
132
|
+
}
|
|
117
133
|
sendMessage(message) {
|
|
118
134
|
if (!this.isConnected()) {
|
|
119
135
|
logger.warn("websocket", "Cannot send message: not connected");
|
|
@@ -139,6 +155,18 @@ var WebSocketServiceImpl = class {
|
|
|
139
155
|
this.commitApprovedHandlers.forEach((handler) => handler(commit, project));
|
|
140
156
|
return;
|
|
141
157
|
}
|
|
158
|
+
if (message.event === "commit.pending") {
|
|
159
|
+
const { pendingCommit } = message.data;
|
|
160
|
+
logger.info("websocket", `Commit pending: ${pendingCommit.id}`);
|
|
161
|
+
this.commitPendingHandlers.forEach((handler) => handler(pendingCommit));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (message.event === "suggestion.created") {
|
|
165
|
+
const { suggestion } = message.data;
|
|
166
|
+
logger.info("websocket", `Suggestion created: ${suggestion.id}`);
|
|
167
|
+
this.suggestionCreatedHandlers.forEach((handler) => handler(suggestion));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
142
170
|
if (message.event === "project.updated") {
|
|
143
171
|
const { project } = message.data;
|
|
144
172
|
logger.info("websocket", `Project updated: ${project.id}`);
|
|
@@ -148,6 +176,13 @@ var WebSocketServiceImpl = class {
|
|
|
148
176
|
if (message.event === "sync.requested") {
|
|
149
177
|
const { projectId } = message.data;
|
|
150
178
|
logger.info("websocket", `Sync requested for project: ${projectId}`);
|
|
179
|
+
this.syncRequestedHandlers.forEach((handler) => handler(projectId));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (message.event === "agent.disconnected") {
|
|
183
|
+
const reason = message.data?.reason || "Server requested disconnect";
|
|
184
|
+
logger.warn("websocket", `Agent disconnected by server: ${reason}`);
|
|
185
|
+
this.agentDisconnectedHandlers.forEach((handler) => handler(reason));
|
|
151
186
|
return;
|
|
152
187
|
}
|
|
153
188
|
logger.debug("websocket", `Unhandled event: ${message.event}`);
|
|
@@ -358,6 +393,19 @@ var FileWatcher = class {
|
|
|
358
393
|
addProject(projectPath, projectId) {
|
|
359
394
|
this.watchProject(projectPath, projectId);
|
|
360
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Sync a project by ID (called when server requests a sync)
|
|
398
|
+
*/
|
|
399
|
+
async syncProjectById(projectId) {
|
|
400
|
+
const linkedProjects = projectService.getAllLinkedProjects();
|
|
401
|
+
for (const [projectPath, linkedProject] of Object.entries(linkedProjects)) {
|
|
402
|
+
if (linkedProject.projectId === projectId) {
|
|
403
|
+
await this.performSync(projectPath, projectId);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
logger.warn("watcher", `Cannot sync: project ${projectId} not found in linked projects`);
|
|
408
|
+
}
|
|
361
409
|
/**
|
|
362
410
|
* Remove a project from watching (called when a project is unlinked)
|
|
363
411
|
*/
|
|
@@ -377,9 +425,32 @@ var FileWatcher = class {
|
|
|
377
425
|
}
|
|
378
426
|
};
|
|
379
427
|
|
|
428
|
+
// src/utils/notify.ts
|
|
429
|
+
import notifier from "node-notifier";
|
|
430
|
+
function notify(options) {
|
|
431
|
+
try {
|
|
432
|
+
notifier.notify({
|
|
433
|
+
title: options.title,
|
|
434
|
+
message: options.message,
|
|
435
|
+
open: options.open,
|
|
436
|
+
sound: true,
|
|
437
|
+
wait: false,
|
|
438
|
+
appID: "Stint Agent"
|
|
439
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
440
|
+
}, (error) => {
|
|
441
|
+
if (error) {
|
|
442
|
+
logger.error("notify", "Failed to send notification", error);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
} catch (error) {
|
|
446
|
+
logger.error("notify", "Failed to send notification", error);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
380
450
|
// src/daemon/index.ts
|
|
381
451
|
var heartbeatInterval = null;
|
|
382
452
|
var isShuttingDown = false;
|
|
453
|
+
var shutdownReason;
|
|
383
454
|
var fileWatcher = new FileWatcher();
|
|
384
455
|
async function startDaemon() {
|
|
385
456
|
logger.info("daemon", "Starting daemon...");
|
|
@@ -406,6 +477,30 @@ async function startDaemon() {
|
|
|
406
477
|
websocketService.onDisconnect(() => {
|
|
407
478
|
logger.warn("daemon", "WebSocket disconnected, will attempt to reconnect");
|
|
408
479
|
});
|
|
480
|
+
websocketService.onAgentDisconnected(async (reason) => {
|
|
481
|
+
logger.warn("daemon", `Server disconnected agent: ${reason}`);
|
|
482
|
+
logger.info("daemon", "Initiating graceful shutdown...");
|
|
483
|
+
await shutdown(`Server: ${reason}`);
|
|
484
|
+
process.exit(0);
|
|
485
|
+
});
|
|
486
|
+
websocketService.onSuggestionCreated((suggestion) => {
|
|
487
|
+
logger.info("daemon", `Suggestion created: ${suggestion.title} (${suggestion.priority})`);
|
|
488
|
+
notify({
|
|
489
|
+
title: "New Suggestion",
|
|
490
|
+
message: `${suggestion.title}
|
|
491
|
+
Priority: ${suggestion.priority}`,
|
|
492
|
+
open: `https://stint.codes/projects/${suggestion.project_id}/suggestions/${suggestion.id}`
|
|
493
|
+
// Hypothetical URL structure
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
websocketService.onSyncRequested(async (projectId) => {
|
|
497
|
+
logger.info("daemon", `Server requested sync for project: ${projectId}`);
|
|
498
|
+
try {
|
|
499
|
+
await fileWatcher.syncProjectById(projectId);
|
|
500
|
+
} catch (error) {
|
|
501
|
+
logger.error("daemon", `Failed to sync project ${projectId}`, error);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
409
504
|
setupSignalHandlers();
|
|
410
505
|
startHeartbeat();
|
|
411
506
|
fileWatcher.start();
|
|
@@ -443,15 +538,16 @@ function setupSignalHandlers() {
|
|
|
443
538
|
signals.forEach((signal) => {
|
|
444
539
|
process.on(signal, async () => {
|
|
445
540
|
logger.info("daemon", `Received ${signal}, shutting down...`);
|
|
446
|
-
await shutdown();
|
|
541
|
+
await shutdown(`Signal: ${signal}`);
|
|
447
542
|
process.exit(0);
|
|
448
543
|
});
|
|
449
544
|
});
|
|
450
545
|
logger.info("daemon", "Signal handlers registered");
|
|
451
546
|
}
|
|
452
|
-
async function shutdown() {
|
|
547
|
+
async function shutdown(reason) {
|
|
453
548
|
if (isShuttingDown) return;
|
|
454
549
|
isShuttingDown = true;
|
|
550
|
+
shutdownReason = reason;
|
|
455
551
|
logger.info("daemon", "Shutting down daemon...");
|
|
456
552
|
stopHeartbeat();
|
|
457
553
|
try {
|
|
@@ -467,7 +563,7 @@ async function shutdown() {
|
|
|
467
563
|
logger.error("daemon", "Failed to disconnect from WebSocket", error);
|
|
468
564
|
}
|
|
469
565
|
try {
|
|
470
|
-
await apiService.disconnect();
|
|
566
|
+
await apiService.disconnect(shutdownReason);
|
|
471
567
|
logger.info("daemon", "Disconnected from API");
|
|
472
568
|
} catch (error) {
|
|
473
569
|
logger.error("daemon", "Failed to disconnect from API", error);
|
package/dist/index.js
CHANGED
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
projectService,
|
|
9
9
|
spawnDetached,
|
|
10
10
|
validatePidFile
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EVWSJ3RU.js";
|
|
12
12
|
import {
|
|
13
13
|
apiService,
|
|
14
14
|
authService,
|
|
15
15
|
config,
|
|
16
16
|
logger
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-NMOUYAW2.js";
|
|
18
18
|
|
|
19
19
|
// src/index.ts
|
|
20
20
|
import "dotenv/config";
|
|
@@ -514,7 +514,7 @@ function registerLinkCommand(program2) {
|
|
|
514
514
|
try {
|
|
515
515
|
repoInfo = await gitService.getRepoInfo(cwd);
|
|
516
516
|
} catch (e) {
|
|
517
|
-
logger.warn("link",
|
|
517
|
+
logger.warn("link", `Failed to get repo info for creation metadata: ${e.message}`);
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
520
|
const newProject = await apiService.createProject({
|
|
@@ -1062,7 +1062,7 @@ function getTimeAgo(date) {
|
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
1064
|
// src/index.ts
|
|
1065
|
-
var AGENT_VERSION = "1.0.
|
|
1065
|
+
var AGENT_VERSION = "1.0.3";
|
|
1066
1066
|
var program = new Command();
|
|
1067
1067
|
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-V, --version", "output the current version").addHelpText("after", `
|
|
1068
1068
|
${chalk10.bold("Examples:")}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gowelle/stint-agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Local agent for Stint - Project Assistant",
|
|
5
5
|
"author": "Gowelle John <gowelle.john@icloud.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"conf": "^12.0.0",
|
|
43
43
|
"dotenv": "^17.2.3",
|
|
44
44
|
"node-fetch": "^3.3.2",
|
|
45
|
+
"node-notifier": "^10.0.1",
|
|
45
46
|
"open": "^10.0.0",
|
|
46
47
|
"ora": "^8.0.1",
|
|
47
48
|
"simple-git": "^3.22.0",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/node": "^20.11.0",
|
|
53
|
+
"@types/node-notifier": "^8.0.5",
|
|
52
54
|
"@types/ws": "^8.5.10",
|
|
53
55
|
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
|
54
56
|
"@typescript-eslint/parser": "^8.50.0",
|