@gowelle/stint-agent 1.2.17 → 1.2.18
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/assets/logo.png +0 -0
- package/dist/{StatusDashboard-ATB5BFZR.js → StatusDashboard-KCHNLVD7.js} +2 -3
- package/dist/api-6EGHLTCT.js +7 -0
- package/dist/{chunk-QQP6IASS.js → chunk-A2CVSQ3K.js} +1 -1
- package/dist/{chunk-VHCNMUHN.js → chunk-SXPP272L.js} +211 -154
- package/dist/{chunk-XHXSWLUC.js → chunk-WETVBZ6Z.js} +89 -1
- package/dist/{chunk-D6BP2Z5S.js → chunk-XPZNWXB4.js} +3 -5
- package/dist/daemon/runner.js +9 -149
- package/dist/index.js +33 -13
- package/package.json +3 -1
- package/dist/api-ORVLKYAQ.js +0 -8
- package/dist/chunk-DCY3EXDX.js +0 -40
- package/dist/chunk-ES33YYVA.js +0 -95
- package/dist/notify-NXUEEO7M.js +0 -7
package/assets/logo.png
CHANGED
|
Binary file
|
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
gitService,
|
|
3
3
|
projectService,
|
|
4
4
|
validatePidFile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-A2CVSQ3K.js";
|
|
6
6
|
import {
|
|
7
7
|
authService
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-XHXSWLUC.js";
|
|
8
|
+
} from "./chunk-WETVBZ6Z.js";
|
|
10
9
|
|
|
11
10
|
// src/components/StatusDashboard.tsx
|
|
12
11
|
import { useState, useEffect } from "react";
|
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
import {
|
|
2
2
|
apiService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XPZNWXB4.js";
|
|
4
4
|
import {
|
|
5
5
|
gitService,
|
|
6
6
|
projectService
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
authService
|
|
10
|
-
} from "./chunk-ES33YYVA.js";
|
|
11
|
-
import {
|
|
12
|
-
notify
|
|
13
|
-
} from "./chunk-DCY3EXDX.js";
|
|
7
|
+
} from "./chunk-A2CVSQ3K.js";
|
|
14
8
|
import {
|
|
9
|
+
authService,
|
|
15
10
|
config,
|
|
16
11
|
logger
|
|
17
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WETVBZ6Z.js";
|
|
13
|
+
|
|
14
|
+
// src/utils/notify.ts
|
|
15
|
+
import notifier from "node-notifier";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
var __dirname = path.dirname(__filename);
|
|
20
|
+
var DEFAULT_ICON = path.resolve(__dirname, "../assets/logo.png");
|
|
21
|
+
function notify(options) {
|
|
22
|
+
if (!config.areNotificationsEnabled()) {
|
|
23
|
+
logger.debug("notify", "Notifications disabled, skipping notification");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
notifier.notify({
|
|
28
|
+
title: options.title,
|
|
29
|
+
message: options.message,
|
|
30
|
+
open: options.open,
|
|
31
|
+
icon: options.icon || DEFAULT_ICON,
|
|
32
|
+
sound: true,
|
|
33
|
+
wait: false,
|
|
34
|
+
appID: "Stint Agent"
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
}, (error) => {
|
|
37
|
+
if (error) {
|
|
38
|
+
logger.error("notify", "Failed to send notification", error);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.error("notify", "Failed to send notification", error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
18
45
|
|
|
19
46
|
// src/daemon/queue.ts
|
|
20
47
|
var CommitQueueProcessor = class {
|
|
@@ -132,7 +159,7 @@ Run "git pull --rebase" to resolve.`
|
|
|
132
159
|
}
|
|
133
160
|
return sha;
|
|
134
161
|
} catch (error) {
|
|
135
|
-
msg = error.message;
|
|
162
|
+
const msg = error.message;
|
|
136
163
|
logger.error("queue", `Commit execution failed: ${msg}`);
|
|
137
164
|
notify({
|
|
138
165
|
title: `Commit Failed - ${project.name}`,
|
|
@@ -174,9 +201,9 @@ Run "git pull --rebase" to resolve.`
|
|
|
174
201
|
*/
|
|
175
202
|
findProjectPath(projectId) {
|
|
176
203
|
const allProjects = projectService.getAllLinkedProjects();
|
|
177
|
-
for (const [
|
|
204
|
+
for (const [path3, linkedProject] of Object.entries(allProjects)) {
|
|
178
205
|
if (linkedProject.projectId === projectId) {
|
|
179
|
-
return
|
|
206
|
+
return path3;
|
|
180
207
|
}
|
|
181
208
|
}
|
|
182
209
|
return null;
|
|
@@ -197,11 +224,33 @@ Run "git pull --rebase" to resolve.`
|
|
|
197
224
|
var commitQueue = new CommitQueueProcessor();
|
|
198
225
|
|
|
199
226
|
// src/services/websocket.ts
|
|
200
|
-
import
|
|
227
|
+
import Echo from "laravel-echo";
|
|
228
|
+
import Pusher from "pusher-js";
|
|
229
|
+
import fs from "fs";
|
|
230
|
+
import os from "os";
|
|
231
|
+
import path2 from "path";
|
|
232
|
+
var STATUS_FILE_PATH = path2.join(os.homedir(), ".config", "stint", "daemon.status.json");
|
|
233
|
+
function writeStatus(update) {
|
|
234
|
+
try {
|
|
235
|
+
const dir = path2.dirname(STATUS_FILE_PATH);
|
|
236
|
+
if (!fs.existsSync(dir)) {
|
|
237
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
238
|
+
}
|
|
239
|
+
let status = { websocket: { connected: false } };
|
|
240
|
+
if (fs.existsSync(STATUS_FILE_PATH)) {
|
|
241
|
+
try {
|
|
242
|
+
status = JSON.parse(fs.readFileSync(STATUS_FILE_PATH, "utf8"));
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
status.websocket = { ...status.websocket, ...update };
|
|
247
|
+
fs.writeFileSync(STATUS_FILE_PATH, JSON.stringify(status, null, 2));
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
}
|
|
201
251
|
var WebSocketServiceImpl = class {
|
|
202
|
-
|
|
252
|
+
echo = null;
|
|
203
253
|
userId = null;
|
|
204
|
-
socketId = null;
|
|
205
254
|
reconnectAttempts = 0;
|
|
206
255
|
maxReconnectAttempts = 10;
|
|
207
256
|
reconnectTimer = null;
|
|
@@ -215,7 +264,7 @@ var WebSocketServiceImpl = class {
|
|
|
215
264
|
agentDisconnectedHandlers = [];
|
|
216
265
|
syncRequestedHandlers = [];
|
|
217
266
|
/**
|
|
218
|
-
* Connect to the WebSocket server
|
|
267
|
+
* Connect to the WebSocket server using Laravel Echo
|
|
219
268
|
* @throws Error if connection fails or no auth token available
|
|
220
269
|
*/
|
|
221
270
|
async connect() {
|
|
@@ -224,31 +273,117 @@ var WebSocketServiceImpl = class {
|
|
|
224
273
|
if (!token) {
|
|
225
274
|
throw new Error("No authentication token available");
|
|
226
275
|
}
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
276
|
+
const reverbAppKey = config.getReverbAppKey();
|
|
277
|
+
if (!reverbAppKey) {
|
|
278
|
+
throw new Error("Reverb app key not configured");
|
|
279
|
+
}
|
|
280
|
+
const apiUrl = config.getApiUrl();
|
|
281
|
+
const environment = config.getEnvironment();
|
|
282
|
+
let wsHost;
|
|
283
|
+
let wsPort;
|
|
284
|
+
let forceTLS;
|
|
285
|
+
if (environment === "development") {
|
|
286
|
+
wsHost = "localhost";
|
|
287
|
+
wsPort = 8080;
|
|
288
|
+
forceTLS = false;
|
|
289
|
+
} else {
|
|
290
|
+
wsHost = "stint.codes";
|
|
291
|
+
wsPort = 443;
|
|
292
|
+
forceTLS = true;
|
|
293
|
+
}
|
|
294
|
+
logger.info("websocket", `Connecting to ${wsHost}:${wsPort} with key ${reverbAppKey}...`);
|
|
295
|
+
const pusherClient = new Pusher(reverbAppKey, {
|
|
296
|
+
wsHost,
|
|
297
|
+
wsPort,
|
|
298
|
+
forceTLS,
|
|
299
|
+
enabledTransports: ["ws", "wss"],
|
|
300
|
+
disableStats: true,
|
|
301
|
+
cluster: "",
|
|
302
|
+
// Required but unused for Reverb
|
|
303
|
+
authorizer: (channel) => ({
|
|
304
|
+
authorize: async (socketId, callback) => {
|
|
305
|
+
try {
|
|
306
|
+
const response = await fetch(`${apiUrl}/api/broadcasting/auth`, {
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers: {
|
|
309
|
+
"Authorization": `Bearer ${token}`,
|
|
310
|
+
"Accept": "application/json",
|
|
311
|
+
"Content-Type": "application/json"
|
|
312
|
+
},
|
|
313
|
+
body: JSON.stringify({
|
|
314
|
+
socket_id: socketId,
|
|
315
|
+
channel_name: channel.name
|
|
316
|
+
})
|
|
317
|
+
});
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
const errorText = await response.text();
|
|
320
|
+
logger.error("websocket", `Auth failed (${response.status}): ${errorText}`);
|
|
321
|
+
callback(new Error(`Auth failed: ${response.status}`));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const data = await response.json();
|
|
325
|
+
callback(null, data);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
logger.error("websocket", "Channel auth error", error);
|
|
328
|
+
callback(error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
});
|
|
333
|
+
this.echo = new Echo({
|
|
334
|
+
broadcaster: "reverb",
|
|
335
|
+
key: reverbAppKey,
|
|
336
|
+
wsHost,
|
|
337
|
+
wsPort,
|
|
338
|
+
forceTLS,
|
|
339
|
+
disableStats: true,
|
|
340
|
+
enabledTransports: ["ws", "wss"],
|
|
341
|
+
authEndpoint: `${apiUrl}/api/broadcasting/auth`,
|
|
342
|
+
auth: {
|
|
343
|
+
headers: {
|
|
344
|
+
Authorization: `Bearer ${token}`,
|
|
345
|
+
Accept: "application/json"
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
client: pusherClient
|
|
349
|
+
});
|
|
350
|
+
logger.info("websocket", "Echo instance created, setting up connection handlers...");
|
|
231
351
|
return new Promise((resolve, reject) => {
|
|
232
|
-
if (!this.
|
|
233
|
-
reject(new Error("
|
|
352
|
+
if (!this.echo) {
|
|
353
|
+
reject(new Error("Echo not initialized"));
|
|
234
354
|
return;
|
|
235
355
|
}
|
|
236
|
-
|
|
237
|
-
|
|
356
|
+
const connectionTimeout = setTimeout(() => {
|
|
357
|
+
const state = this.echo?.connector.pusher.connection.state || "unknown";
|
|
358
|
+
logger.error("websocket", `Connection timeout after 15s (state: ${state})`);
|
|
359
|
+
reject(new Error(`Connection timeout - stuck in state: ${state}`));
|
|
360
|
+
}, 15e3);
|
|
361
|
+
this.echo.connector.pusher.connection.bind("state_change", (states) => {
|
|
362
|
+
logger.info("websocket", `Connection state: ${states.previous} -> ${states.current}`);
|
|
363
|
+
});
|
|
364
|
+
this.echo.connector.pusher.connection.bind("connected", () => {
|
|
365
|
+
clearTimeout(connectionTimeout);
|
|
366
|
+
logger.success("websocket", "\u2705 Connected to Broadcaster via Sanctum");
|
|
367
|
+
writeStatus({ connected: true });
|
|
238
368
|
this.reconnectAttempts = 0;
|
|
239
369
|
this.isManualDisconnect = false;
|
|
240
370
|
resolve();
|
|
241
371
|
});
|
|
242
|
-
this.
|
|
243
|
-
|
|
372
|
+
this.echo.connector.pusher.connection.bind("error", (error) => {
|
|
373
|
+
clearTimeout(connectionTimeout);
|
|
374
|
+
const errorMessage = error instanceof Error ? error.message : JSON.stringify(error) || "Unknown connection error";
|
|
375
|
+
logger.error("websocket", `WebSocket error: ${errorMessage}`);
|
|
376
|
+
reject(new Error(errorMessage));
|
|
244
377
|
});
|
|
245
|
-
this.
|
|
378
|
+
this.echo.connector.pusher.connection.bind("disconnected", () => {
|
|
246
379
|
logger.warn("websocket", "WebSocket disconnected");
|
|
380
|
+
writeStatus({ connected: false });
|
|
247
381
|
this.handleDisconnect();
|
|
248
382
|
});
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
383
|
+
this.echo.connector.pusher.connection.bind("failed", () => {
|
|
384
|
+
clearTimeout(connectionTimeout);
|
|
385
|
+
logger.error("websocket", "WebSocket connection failed");
|
|
386
|
+
reject(new Error("WebSocket connection failed"));
|
|
252
387
|
});
|
|
253
388
|
});
|
|
254
389
|
} catch (error) {
|
|
@@ -266,17 +401,12 @@ var WebSocketServiceImpl = class {
|
|
|
266
401
|
clearTimeout(this.reconnectTimer);
|
|
267
402
|
this.reconnectTimer = null;
|
|
268
403
|
}
|
|
269
|
-
if (this.
|
|
404
|
+
if (this.echo) {
|
|
270
405
|
if (this.userId) {
|
|
271
|
-
this.
|
|
272
|
-
event: "pusher:unsubscribe",
|
|
273
|
-
data: {
|
|
274
|
-
channel: `private-user.${this.userId}`
|
|
275
|
-
}
|
|
276
|
-
});
|
|
406
|
+
this.echo.leave(`user.${this.userId}`);
|
|
277
407
|
}
|
|
278
|
-
this.
|
|
279
|
-
this.
|
|
408
|
+
this.echo.disconnect();
|
|
409
|
+
this.echo = null;
|
|
280
410
|
logger.info("websocket", "WebSocket disconnected");
|
|
281
411
|
}
|
|
282
412
|
}
|
|
@@ -285,38 +415,55 @@ var WebSocketServiceImpl = class {
|
|
|
285
415
|
* @returns True if connected and ready
|
|
286
416
|
*/
|
|
287
417
|
isConnected() {
|
|
288
|
-
return this.
|
|
418
|
+
return this.echo !== null && this.echo.connector.pusher.connection.state === "connected";
|
|
289
419
|
}
|
|
290
420
|
/**
|
|
291
|
-
* Subscribe to user-specific channel for real-time updates
|
|
421
|
+
* Subscribe to user-specific private channel for real-time updates
|
|
292
422
|
* @param userId - User ID to subscribe to
|
|
293
423
|
*/
|
|
294
424
|
async subscribeToUserChannel(userId) {
|
|
295
425
|
this.userId = userId;
|
|
296
|
-
if (this.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
this.sendMessage({
|
|
300
|
-
event: "pusher:subscribe",
|
|
301
|
-
data: {
|
|
302
|
-
channel
|
|
303
|
-
}
|
|
304
|
-
});
|
|
426
|
+
if (!this.echo) {
|
|
427
|
+
logger.warn("websocket", "Cannot subscribe: not connected");
|
|
428
|
+
return;
|
|
305
429
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
430
|
+
if (!this.isConnected()) {
|
|
431
|
+
logger.warn("websocket", "Cannot subscribe: not connected");
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const channel = `user.${userId}`;
|
|
435
|
+
logger.info("websocket", `Subscribing to private channel: ${channel}`);
|
|
436
|
+
const privateChannel = this.echo.private(channel);
|
|
437
|
+
writeStatus({ channel });
|
|
438
|
+
privateChannel.listen(".commit.approved", (data) => {
|
|
439
|
+
logger.info("websocket", `Commit approved: ${data.pendingCommit.id}`);
|
|
440
|
+
writeStatus({ lastEvent: "commit.approved", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
441
|
+
this.commitApprovedHandlers.forEach(
|
|
442
|
+
(handler) => handler(data.pendingCommit, data.pendingCommit.project)
|
|
443
|
+
);
|
|
444
|
+
}).listen(".commit.pending", (data) => {
|
|
445
|
+
logger.info("websocket", `Commit pending: ${data.pendingCommit.id}`);
|
|
446
|
+
writeStatus({ lastEvent: "commit.pending", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
447
|
+
this.commitPendingHandlers.forEach((handler) => handler(data.pendingCommit));
|
|
448
|
+
}).listen(".suggestion.created", (data) => {
|
|
449
|
+
logger.info("websocket", `Suggestion created: ${data.suggestion.id}`);
|
|
450
|
+
writeStatus({ lastEvent: "suggestion.created", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
451
|
+
this.suggestionCreatedHandlers.forEach((handler) => handler(data.suggestion));
|
|
452
|
+
}).listen(".project.updated", (data) => {
|
|
453
|
+
logger.info("websocket", `Project updated: ${data.project.id}`);
|
|
454
|
+
writeStatus({ lastEvent: "project.updated", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
455
|
+
this.projectUpdatedHandlers.forEach((handler) => handler(data.project));
|
|
456
|
+
}).listen(".sync.requested", (data) => {
|
|
457
|
+
logger.info("websocket", `Sync requested for project: ${data.project.id}`);
|
|
458
|
+
writeStatus({ lastEvent: "sync.requested", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
459
|
+
this.syncRequestedHandlers.forEach((handler) => handler(data.project.id));
|
|
460
|
+
}).listen(".agent.disconnected", (data) => {
|
|
461
|
+
const reason = data.reason ?? "Server requested disconnect";
|
|
462
|
+
logger.warn("websocket", `Agent disconnected by server: ${reason}`);
|
|
463
|
+
writeStatus({ lastEvent: "agent.disconnected", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
464
|
+
this.agentDisconnectedHandlers.forEach((handler) => handler(reason));
|
|
318
465
|
});
|
|
319
|
-
|
|
466
|
+
logger.success("websocket", `Subscribed to private channel: ${channel}`);
|
|
320
467
|
}
|
|
321
468
|
/**
|
|
322
469
|
* Register handler for commit approved events
|
|
@@ -343,98 +490,7 @@ var WebSocketServiceImpl = class {
|
|
|
343
490
|
onSyncRequested(handler) {
|
|
344
491
|
this.syncRequestedHandlers.push(handler);
|
|
345
492
|
}
|
|
346
|
-
sendMessage(message) {
|
|
347
|
-
if (!this.isConnected()) {
|
|
348
|
-
logger.warn("websocket", "Cannot send message: not connected");
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
this.ws.send(JSON.stringify(message));
|
|
352
|
-
}
|
|
353
|
-
async handleMessage(data) {
|
|
354
|
-
try {
|
|
355
|
-
const message = JSON.parse(data.toString());
|
|
356
|
-
logger.info("websocket", `Received message: ${message.event}`);
|
|
357
|
-
if (message.event === "pusher:connection_established") {
|
|
358
|
-
try {
|
|
359
|
-
const connectionData = typeof message.data === "string" ? JSON.parse(message.data) : message.data;
|
|
360
|
-
this.socketId = connectionData.socket_id;
|
|
361
|
-
logger.success("websocket", `Connection established (socket_id: ${this.socketId})`);
|
|
362
|
-
if (this.userId) {
|
|
363
|
-
this.subscribeToUserChannel(this.userId);
|
|
364
|
-
}
|
|
365
|
-
} catch (error) {
|
|
366
|
-
logger.success("websocket", "Connection established");
|
|
367
|
-
}
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
if (message.event === "pusher_internal:subscription_succeeded") {
|
|
371
|
-
logger.success("websocket", `Subscribed to channel: ${message.channel}`);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
if (message.event === "pusher:error") {
|
|
375
|
-
try {
|
|
376
|
-
const errorData = typeof message.data === "string" ? JSON.parse(message.data) : message.data;
|
|
377
|
-
const errorCode = errorData.code;
|
|
378
|
-
const errorMessage = errorData.message;
|
|
379
|
-
logger.error("websocket", `WebSocket error (${errorCode}): ${errorMessage}`);
|
|
380
|
-
if (errorCode === 4001) {
|
|
381
|
-
logger.error("websocket", "Application does not exist - check Reverb app key configuration");
|
|
382
|
-
} else if (errorCode === 4009) {
|
|
383
|
-
logger.error("websocket", "Connection is unauthorized - authentication token may be invalid or expired");
|
|
384
|
-
const { notify: notify2 } = await import("./notify-NXUEEO7M.js");
|
|
385
|
-
notify2({
|
|
386
|
-
title: "Stint Agent - Connection Issue",
|
|
387
|
-
message: "WebSocket authentication failed. Notifications may be delayed (falling back to polling)."
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
} catch (parseError) {
|
|
391
|
-
logger.error("websocket", `WebSocket error: ${JSON.stringify(message.data)}`);
|
|
392
|
-
}
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
if (message.event === "commit.approved") {
|
|
396
|
-
const { pendingCommit } = message.data;
|
|
397
|
-
logger.info("websocket", `Commit approved: ${pendingCommit.id}`);
|
|
398
|
-
this.commitApprovedHandlers.forEach((handler) => handler(pendingCommit, pendingCommit.project));
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
if (message.event === "commit.pending") {
|
|
402
|
-
const { pendingCommit } = message.data;
|
|
403
|
-
logger.info("websocket", `Commit pending: ${pendingCommit.id}`);
|
|
404
|
-
this.commitPendingHandlers.forEach((handler) => handler(pendingCommit));
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (message.event === "suggestion.created") {
|
|
408
|
-
const { suggestion } = message.data;
|
|
409
|
-
logger.info("websocket", `Suggestion created: ${suggestion.id}`);
|
|
410
|
-
this.suggestionCreatedHandlers.forEach((handler) => handler(suggestion));
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
if (message.event === "project.updated") {
|
|
414
|
-
const { project } = message.data;
|
|
415
|
-
logger.info("websocket", `Project updated: ${project.id}`);
|
|
416
|
-
this.projectUpdatedHandlers.forEach((handler) => handler(project));
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
if (message.event === "sync.requested") {
|
|
420
|
-
const { project } = message.data;
|
|
421
|
-
logger.info("websocket", `Sync requested for project: ${project.id}`);
|
|
422
|
-
this.syncRequestedHandlers.forEach((handler) => handler(project.id));
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
if (message.event === "agent.disconnected") {
|
|
426
|
-
const { reason } = message.data;
|
|
427
|
-
logger.warn("websocket", `Agent disconnected by server: ${reason ?? "Server requested disconnect"}`);
|
|
428
|
-
this.agentDisconnectedHandlers.forEach((handler) => handler(reason ?? "Server requested disconnect"));
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
logger.info("websocket", `Unhandled event: ${message.event}, payload: ${JSON.stringify(message)}`);
|
|
432
|
-
} catch (error) {
|
|
433
|
-
logger.error("websocket", "Failed to parse message", error);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
493
|
handleDisconnect() {
|
|
437
|
-
this.ws = null;
|
|
438
494
|
this.disconnectHandlers.forEach((handler) => handler());
|
|
439
495
|
if (this.isManualDisconnect) {
|
|
440
496
|
return;
|
|
@@ -472,6 +528,7 @@ var WebSocketServiceImpl = class {
|
|
|
472
528
|
var websocketService = new WebSocketServiceImpl();
|
|
473
529
|
|
|
474
530
|
export {
|
|
531
|
+
notify,
|
|
475
532
|
commitQueue,
|
|
476
533
|
websocketService
|
|
477
534
|
};
|
|
@@ -214,7 +214,95 @@ var Logger = class {
|
|
|
214
214
|
};
|
|
215
215
|
var logger = new Logger();
|
|
216
216
|
|
|
217
|
+
// src/utils/crypto.ts
|
|
218
|
+
import crypto from "crypto";
|
|
219
|
+
import os3 from "os";
|
|
220
|
+
function getMachineKey() {
|
|
221
|
+
const machineInfo = `${os3.hostname()}-${os3.platform()}-${os3.arch()}`;
|
|
222
|
+
return crypto.createHash("sha256").update(machineInfo).digest();
|
|
223
|
+
}
|
|
224
|
+
var ALGORITHM = "aes-256-gcm";
|
|
225
|
+
var IV_LENGTH = 16;
|
|
226
|
+
var AUTH_TAG_LENGTH = 16;
|
|
227
|
+
function encrypt(text) {
|
|
228
|
+
const key = getMachineKey();
|
|
229
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
230
|
+
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
231
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
232
|
+
encrypted += cipher.final("hex");
|
|
233
|
+
const authTag = cipher.getAuthTag();
|
|
234
|
+
return iv.toString("hex") + authTag.toString("hex") + encrypted;
|
|
235
|
+
}
|
|
236
|
+
function decrypt(encryptedText) {
|
|
237
|
+
const key = getMachineKey();
|
|
238
|
+
const iv = Buffer.from(encryptedText.slice(0, IV_LENGTH * 2), "hex");
|
|
239
|
+
const authTag = Buffer.from(
|
|
240
|
+
encryptedText.slice(IV_LENGTH * 2, (IV_LENGTH + AUTH_TAG_LENGTH) * 2),
|
|
241
|
+
"hex"
|
|
242
|
+
);
|
|
243
|
+
const encrypted = encryptedText.slice((IV_LENGTH + AUTH_TAG_LENGTH) * 2);
|
|
244
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
245
|
+
decipher.setAuthTag(authTag);
|
|
246
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
247
|
+
decrypted += decipher.final("utf8");
|
|
248
|
+
return decrypted;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/services/auth.ts
|
|
252
|
+
var AuthServiceImpl = class {
|
|
253
|
+
async saveToken(token) {
|
|
254
|
+
try {
|
|
255
|
+
const encryptedToken = encrypt(token);
|
|
256
|
+
config.setToken(encryptedToken);
|
|
257
|
+
logger.info("auth", "Token saved successfully");
|
|
258
|
+
} catch (error) {
|
|
259
|
+
logger.error("auth", "Failed to save token", error);
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async getToken() {
|
|
264
|
+
try {
|
|
265
|
+
const encryptedToken = config.getToken();
|
|
266
|
+
if (!encryptedToken) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
return decrypt(encryptedToken);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
logger.error("auth", "Failed to decrypt token", error);
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async clearToken() {
|
|
276
|
+
config.clearToken();
|
|
277
|
+
logger.info("auth", "Token cleared");
|
|
278
|
+
}
|
|
279
|
+
async validateToken() {
|
|
280
|
+
const token = await this.getToken();
|
|
281
|
+
if (!token) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
const { apiService } = await import("./api-6EGHLTCT.js");
|
|
286
|
+
const user = await apiService.getCurrentUser();
|
|
287
|
+
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
288
|
+
return user;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.warn("auth", "Token validation failed");
|
|
291
|
+
logger.error("auth", "Failed to validate token", error);
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
getMachineId() {
|
|
296
|
+
return config.getMachineId();
|
|
297
|
+
}
|
|
298
|
+
getMachineName() {
|
|
299
|
+
return config.getMachineName();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
var authService = new AuthServiceImpl();
|
|
303
|
+
|
|
217
304
|
export {
|
|
218
305
|
config,
|
|
219
|
-
logger
|
|
306
|
+
logger,
|
|
307
|
+
authService
|
|
220
308
|
};
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
authService
|
|
3
|
-
} from "./chunk-ES33YYVA.js";
|
|
4
|
-
import {
|
|
2
|
+
authService,
|
|
5
3
|
config,
|
|
6
4
|
logger
|
|
7
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WETVBZ6Z.js";
|
|
8
6
|
|
|
9
7
|
// src/utils/circuit-breaker.ts
|
|
10
8
|
var CircuitBreaker = class {
|
|
@@ -100,7 +98,7 @@ var CircuitBreaker = class {
|
|
|
100
98
|
};
|
|
101
99
|
|
|
102
100
|
// src/services/api.ts
|
|
103
|
-
var AGENT_VERSION = "1.2.
|
|
101
|
+
var AGENT_VERSION = "1.2.18";
|
|
104
102
|
var ApiServiceImpl = class {
|
|
105
103
|
sessionId = null;
|
|
106
104
|
circuitBreaker = new CircuitBreaker({
|
package/dist/daemon/runner.js
CHANGED
|
@@ -1,149 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
commitQueue,
|
|
4
|
+
notify,
|
|
4
5
|
websocketService
|
|
5
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-SXPP272L.js";
|
|
6
7
|
import {
|
|
7
8
|
apiService
|
|
8
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-XPZNWXB4.js";
|
|
9
10
|
import {
|
|
10
11
|
gitService,
|
|
11
12
|
projectService,
|
|
12
13
|
removePidFile,
|
|
13
14
|
writePidFile
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import {
|
|
16
|
-
authService
|
|
17
|
-
} from "../chunk-ES33YYVA.js";
|
|
18
|
-
import {
|
|
19
|
-
notify
|
|
20
|
-
} from "../chunk-DCY3EXDX.js";
|
|
15
|
+
} from "../chunk-A2CVSQ3K.js";
|
|
21
16
|
import {
|
|
17
|
+
authService,
|
|
22
18
|
logger
|
|
23
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-WETVBZ6Z.js";
|
|
24
20
|
|
|
25
21
|
// src/daemon/runner.ts
|
|
26
22
|
import "dotenv/config";
|
|
27
23
|
|
|
28
|
-
// src/services/polling.ts
|
|
29
|
-
var PollingServiceImpl = class {
|
|
30
|
-
interval = null;
|
|
31
|
-
knownCommitIds = /* @__PURE__ */ new Set();
|
|
32
|
-
knownProjects = /* @__PURE__ */ new Map();
|
|
33
|
-
commitApprovedHandlers = [];
|
|
34
|
-
projectUpdatedHandlers = [];
|
|
35
|
-
isFirstRun = true;
|
|
36
|
-
isPolling = false;
|
|
37
|
-
/**
|
|
38
|
-
* Start polling for pending commits
|
|
39
|
-
* @param intervalMs - Polling interval in milliseconds (default: 10000)
|
|
40
|
-
*/
|
|
41
|
-
start(intervalMs = 1e4) {
|
|
42
|
-
if (this.interval) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
logger.info("polling", `Starting polling service (interval: ${intervalMs}ms)`);
|
|
46
|
-
this.poll();
|
|
47
|
-
this.interval = setInterval(() => {
|
|
48
|
-
this.poll();
|
|
49
|
-
}, intervalMs);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Stop polling
|
|
53
|
-
*/
|
|
54
|
-
stop() {
|
|
55
|
-
if (this.interval) {
|
|
56
|
-
clearInterval(this.interval);
|
|
57
|
-
this.interval = null;
|
|
58
|
-
logger.info("polling", "Stopping polling service");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Register handler for commit approved event
|
|
63
|
-
* @param handler - Function to call when a commit is approved where it was previously unknown
|
|
64
|
-
*/
|
|
65
|
-
onCommitApproved(handler) {
|
|
66
|
-
this.commitApprovedHandlers.push(handler);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Register handler for project updated event
|
|
70
|
-
* @param handler - Function to call when a project is updated
|
|
71
|
-
*/
|
|
72
|
-
onProjectUpdated(handler) {
|
|
73
|
-
this.projectUpdatedHandlers.push(handler);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Poll for updates
|
|
77
|
-
*/
|
|
78
|
-
async poll() {
|
|
79
|
-
if (this.isPolling) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
this.isPolling = true;
|
|
83
|
-
try {
|
|
84
|
-
const linkedProjects = projectService.getAllLinkedProjects();
|
|
85
|
-
const projectIds = Object.values(linkedProjects).map((p) => p.projectId);
|
|
86
|
-
if (projectIds.length === 0) {
|
|
87
|
-
this.isFirstRun = false;
|
|
88
|
-
this.isPolling = false;
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const apiProjects = await apiService.getLinkedProjects();
|
|
92
|
-
const projects = apiProjects.filter((p) => projectIds.includes(p.id));
|
|
93
|
-
for (const project of projects) {
|
|
94
|
-
const cachedProject = this.knownProjects.get(project.id);
|
|
95
|
-
if (cachedProject) {
|
|
96
|
-
if (cachedProject.updatedAt !== project.updatedAt) {
|
|
97
|
-
if (!this.isFirstRun) {
|
|
98
|
-
logger.info("polling", `Project update detected: ${project.id}`);
|
|
99
|
-
this.notifyProjectUpdated(project);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
this.knownProjects.set(project.id, project);
|
|
104
|
-
try {
|
|
105
|
-
const commits = await apiService.getPendingCommits(project.id);
|
|
106
|
-
for (const commit of commits) {
|
|
107
|
-
if (!this.knownCommitIds.has(commit.id)) {
|
|
108
|
-
this.knownCommitIds.add(commit.id);
|
|
109
|
-
if (!this.isFirstRun) {
|
|
110
|
-
logger.info("polling", `New pending commit detected: ${commit.id}`);
|
|
111
|
-
this.notifyCommitApproved(commit, project);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
logger.debug("polling", `Failed to poll project ${project.id}: ${error.message}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
this.isFirstRun = false;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
logger.error("polling", "Poll cycle failed", error);
|
|
122
|
-
} finally {
|
|
123
|
-
this.isPolling = false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
notifyCommitApproved(commit, project) {
|
|
127
|
-
this.commitApprovedHandlers.forEach((handler) => {
|
|
128
|
-
try {
|
|
129
|
-
handler(commit, project);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
logger.error("polling", "Error in commit approved handler", error);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
notifyProjectUpdated(project) {
|
|
136
|
-
this.projectUpdatedHandlers.forEach((handler) => {
|
|
137
|
-
try {
|
|
138
|
-
handler(project);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.error("polling", "Error in project updated handler", error);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
var pollingService = new PollingServiceImpl();
|
|
146
|
-
|
|
147
24
|
// src/daemon/watcher.ts
|
|
148
25
|
import fs from "fs";
|
|
149
26
|
var FileWatcher = class {
|
|
@@ -291,7 +168,7 @@ var FileWatcher = class {
|
|
|
291
168
|
const projects = await apiService.getLinkedProjects();
|
|
292
169
|
const p = projects.find((proj) => proj.id === projectId);
|
|
293
170
|
if (p) projectName = p.name;
|
|
294
|
-
} catch (
|
|
171
|
+
} catch (_e) {
|
|
295
172
|
}
|
|
296
173
|
notify({
|
|
297
174
|
title: "Sync Complete",
|
|
@@ -387,15 +264,6 @@ async function startDaemon() {
|
|
|
387
264
|
notify({
|
|
388
265
|
title: "Commit Approved",
|
|
389
266
|
message: `${commit.message}
|
|
390
|
-
Project: ${project.name}`
|
|
391
|
-
});
|
|
392
|
-
commitQueue.addToQueue(commit, project);
|
|
393
|
-
});
|
|
394
|
-
pollingService.onCommitApproved((commit, project) => {
|
|
395
|
-
logger.info("daemon", `Commit approved (via polling): ${commit.id} for project ${project.name}`);
|
|
396
|
-
notify({
|
|
397
|
-
title: "Commit Approved",
|
|
398
|
-
message: `${commit.message}
|
|
399
267
|
Project: ${project.name}`
|
|
400
268
|
});
|
|
401
269
|
commitQueue.addToQueue(commit, project);
|
|
@@ -408,7 +276,7 @@ Project: ${project.name}`
|
|
|
408
276
|
const projects = await apiService.getLinkedProjects();
|
|
409
277
|
const p = projects.find((proj) => proj.id === commit.projectId);
|
|
410
278
|
if (p) projectName = p.name;
|
|
411
|
-
} catch (
|
|
279
|
+
} catch (_e) {
|
|
412
280
|
}
|
|
413
281
|
notify({
|
|
414
282
|
title: `New Proposal - ${projectName}`,
|
|
@@ -422,13 +290,6 @@ Project: ${project.name}`
|
|
|
422
290
|
message: project.name
|
|
423
291
|
});
|
|
424
292
|
});
|
|
425
|
-
pollingService.onProjectUpdated((project) => {
|
|
426
|
-
logger.info("daemon", `Project updated (via polling): ${project.id} - ${project.name}`);
|
|
427
|
-
notify({
|
|
428
|
-
title: "Project Updated",
|
|
429
|
-
message: project.name
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
293
|
websocketService.onDisconnect(() => {
|
|
433
294
|
logger.warn("daemon", "WebSocket disconnected, will attempt to reconnect");
|
|
434
295
|
});
|
|
@@ -458,7 +319,7 @@ Priority: ${suggestion.priority}`,
|
|
|
458
319
|
if (project) {
|
|
459
320
|
projectName = project.name;
|
|
460
321
|
}
|
|
461
|
-
} catch (
|
|
322
|
+
} catch (_e) {
|
|
462
323
|
}
|
|
463
324
|
notify({
|
|
464
325
|
title: "Sync Requested",
|
|
@@ -477,7 +338,6 @@ Priority: ${suggestion.priority}`,
|
|
|
477
338
|
logger.info("daemon", `Linked project: ${project.projectId}`);
|
|
478
339
|
fileWatcher.watchProject(project.projectId);
|
|
479
340
|
});
|
|
480
|
-
pollingService.start();
|
|
481
341
|
logger.info("daemon", "Daemon started successfully");
|
|
482
342
|
const projectEntries = Object.entries(linkedProjects);
|
|
483
343
|
if (projectEntries.length > 0) {
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commitQueue,
|
|
4
4
|
websocketService
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SXPP272L.js";
|
|
6
6
|
import {
|
|
7
7
|
apiService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XPZNWXB4.js";
|
|
9
9
|
import {
|
|
10
10
|
getPidFilePath,
|
|
11
11
|
gitService,
|
|
@@ -14,15 +14,12 @@ import {
|
|
|
14
14
|
projectService,
|
|
15
15
|
spawnDetached,
|
|
16
16
|
validatePidFile
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import {
|
|
19
|
-
authService
|
|
20
|
-
} from "./chunk-ES33YYVA.js";
|
|
21
|
-
import "./chunk-DCY3EXDX.js";
|
|
17
|
+
} from "./chunk-A2CVSQ3K.js";
|
|
22
18
|
import {
|
|
19
|
+
authService,
|
|
23
20
|
config,
|
|
24
21
|
logger
|
|
25
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WETVBZ6Z.js";
|
|
26
23
|
|
|
27
24
|
// src/index.ts
|
|
28
25
|
import "dotenv/config";
|
|
@@ -623,7 +620,7 @@ function registerStatusCommand(program2) {
|
|
|
623
620
|
try {
|
|
624
621
|
const { render } = await import("ink");
|
|
625
622
|
const { createElement } = await import("react");
|
|
626
|
-
const { StatusDashboard } = await import("./StatusDashboard-
|
|
623
|
+
const { StatusDashboard } = await import("./StatusDashboard-KCHNLVD7.js");
|
|
627
624
|
render(createElement(StatusDashboard, { cwd }));
|
|
628
625
|
return;
|
|
629
626
|
} catch (error) {
|
|
@@ -784,9 +781,8 @@ function getProcessStats(pid) {
|
|
|
784
781
|
} else if (platform === "win32") {
|
|
785
782
|
return getWindowsStats(pid);
|
|
786
783
|
}
|
|
787
|
-
|
|
788
|
-
} catch
|
|
789
|
-
logger.error("monitor", `Failed to get process stats for PID ${pid}`, error);
|
|
784
|
+
return null;
|
|
785
|
+
} catch {
|
|
790
786
|
return null;
|
|
791
787
|
}
|
|
792
788
|
}
|
|
@@ -1025,6 +1021,30 @@ function registerDaemonCommands(program2) {
|
|
|
1025
1021
|
console.log(`${chalk8.bold("Threads:")} ${stats.threads}`);
|
|
1026
1022
|
console.log(`${chalk8.bold("Uptime:")} ${formatUptime(stats.uptime)}`);
|
|
1027
1023
|
}
|
|
1024
|
+
const statusPath = path3.join(os3.homedir(), ".config", "stint", "daemon.status.json");
|
|
1025
|
+
if (fs.existsSync(statusPath)) {
|
|
1026
|
+
try {
|
|
1027
|
+
const statusData = JSON.parse(fs.readFileSync(statusPath, "utf8"));
|
|
1028
|
+
console.log(chalk8.blue("\n\u{1F4E1} WebSocket Status:"));
|
|
1029
|
+
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
1030
|
+
if (statusData.websocket?.connected) {
|
|
1031
|
+
console.log(`${chalk8.bold("Connected:")} ${chalk8.green("\u2713 Yes")}`);
|
|
1032
|
+
if (statusData.websocket.channel) {
|
|
1033
|
+
console.log(`${chalk8.bold("Channel:")} ${statusData.websocket.channel}`);
|
|
1034
|
+
}
|
|
1035
|
+
} else {
|
|
1036
|
+
console.log(`${chalk8.bold("Connected:")} ${chalk8.yellow("\u2717 No")}`);
|
|
1037
|
+
}
|
|
1038
|
+
if (statusData.websocket?.lastEvent) {
|
|
1039
|
+
console.log(`${chalk8.bold("Last Event:")} ${statusData.websocket.lastEvent}`);
|
|
1040
|
+
if (statusData.websocket.lastEventTime) {
|
|
1041
|
+
const ago = Math.floor((Date.now() - new Date(statusData.websocket.lastEventTime).getTime()) / 1e3);
|
|
1042
|
+
console.log(`${chalk8.bold("Event Time:")} ${formatUptime(ago)} ago`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1028
1048
|
} else {
|
|
1029
1049
|
console.log(`${chalk8.bold("Status:")} ${chalk8.yellow("Not running")}`);
|
|
1030
1050
|
console.log(chalk8.gray('Run "stint daemon start" to start the daemon.'));
|
|
@@ -2003,7 +2023,7 @@ function registerDoctorCommand(program2) {
|
|
|
2003
2023
|
}
|
|
2004
2024
|
|
|
2005
2025
|
// src/index.ts
|
|
2006
|
-
var AGENT_VERSION = "1.2.
|
|
2026
|
+
var AGENT_VERSION = "1.2.18";
|
|
2007
2027
|
var program = new Command();
|
|
2008
2028
|
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText("after", `
|
|
2009
2029
|
${chalk13.bold("Examples:")}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gowelle/stint-agent",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.18",
|
|
4
4
|
"description": "Local agent for Stint - Project Assistant",
|
|
5
5
|
"author": "Gowelle John <gowelle.john@icloud.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,10 +44,12 @@
|
|
|
44
44
|
"conf": "^12.0.0",
|
|
45
45
|
"dotenv": "^17.2.3",
|
|
46
46
|
"ink": "^5.2.1",
|
|
47
|
+
"laravel-echo": "^2.2.6",
|
|
47
48
|
"node-fetch": "^3.3.2",
|
|
48
49
|
"node-notifier": "^10.0.1",
|
|
49
50
|
"open": "^10.0.0",
|
|
50
51
|
"ora": "^8.0.1",
|
|
52
|
+
"pusher-js": "^8.4.0",
|
|
51
53
|
"react": "^18.3.1",
|
|
52
54
|
"simple-git": "^3.22.0",
|
|
53
55
|
"ws": "^8.16.0"
|
package/dist/api-ORVLKYAQ.js
DELETED
package/dist/chunk-DCY3EXDX.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
config,
|
|
3
|
-
logger
|
|
4
|
-
} from "./chunk-XHXSWLUC.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/notify.ts
|
|
7
|
-
import notifier from "node-notifier";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import { fileURLToPath } from "url";
|
|
10
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
var __dirname = path.dirname(__filename);
|
|
12
|
-
var DEFAULT_ICON = path.resolve(__dirname, "../assets/logo.png");
|
|
13
|
-
function notify(options) {
|
|
14
|
-
if (!config.areNotificationsEnabled()) {
|
|
15
|
-
logger.debug("notify", "Notifications disabled, skipping notification");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
notifier.notify({
|
|
20
|
-
title: options.title,
|
|
21
|
-
message: options.message,
|
|
22
|
-
open: options.open,
|
|
23
|
-
icon: options.icon || DEFAULT_ICON,
|
|
24
|
-
sound: true,
|
|
25
|
-
wait: false,
|
|
26
|
-
appID: "Stint Agent"
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
}, (error) => {
|
|
29
|
-
if (error) {
|
|
30
|
-
logger.error("notify", "Failed to send notification", error);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
} catch (error) {
|
|
34
|
-
logger.error("notify", "Failed to send notification", error);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export {
|
|
39
|
-
notify
|
|
40
|
-
};
|
package/dist/chunk-ES33YYVA.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
config,
|
|
3
|
-
logger
|
|
4
|
-
} from "./chunk-XHXSWLUC.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/crypto.ts
|
|
7
|
-
import crypto from "crypto";
|
|
8
|
-
import os from "os";
|
|
9
|
-
function getMachineKey() {
|
|
10
|
-
const machineInfo = `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
11
|
-
return crypto.createHash("sha256").update(machineInfo).digest();
|
|
12
|
-
}
|
|
13
|
-
var ALGORITHM = "aes-256-gcm";
|
|
14
|
-
var IV_LENGTH = 16;
|
|
15
|
-
var AUTH_TAG_LENGTH = 16;
|
|
16
|
-
function encrypt(text) {
|
|
17
|
-
const key = getMachineKey();
|
|
18
|
-
const iv = crypto.randomBytes(IV_LENGTH);
|
|
19
|
-
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
20
|
-
let encrypted = cipher.update(text, "utf8", "hex");
|
|
21
|
-
encrypted += cipher.final("hex");
|
|
22
|
-
const authTag = cipher.getAuthTag();
|
|
23
|
-
return iv.toString("hex") + authTag.toString("hex") + encrypted;
|
|
24
|
-
}
|
|
25
|
-
function decrypt(encryptedText) {
|
|
26
|
-
const key = getMachineKey();
|
|
27
|
-
const iv = Buffer.from(encryptedText.slice(0, IV_LENGTH * 2), "hex");
|
|
28
|
-
const authTag = Buffer.from(
|
|
29
|
-
encryptedText.slice(IV_LENGTH * 2, (IV_LENGTH + AUTH_TAG_LENGTH) * 2),
|
|
30
|
-
"hex"
|
|
31
|
-
);
|
|
32
|
-
const encrypted = encryptedText.slice((IV_LENGTH + AUTH_TAG_LENGTH) * 2);
|
|
33
|
-
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
34
|
-
decipher.setAuthTag(authTag);
|
|
35
|
-
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
36
|
-
decrypted += decipher.final("utf8");
|
|
37
|
-
return decrypted;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/services/auth.ts
|
|
41
|
-
var AuthServiceImpl = class {
|
|
42
|
-
async saveToken(token) {
|
|
43
|
-
try {
|
|
44
|
-
const encryptedToken = encrypt(token);
|
|
45
|
-
config.setToken(encryptedToken);
|
|
46
|
-
logger.info("auth", "Token saved successfully");
|
|
47
|
-
} catch (error) {
|
|
48
|
-
logger.error("auth", "Failed to save token", error);
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async getToken() {
|
|
53
|
-
try {
|
|
54
|
-
const encryptedToken = config.getToken();
|
|
55
|
-
if (!encryptedToken) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
return decrypt(encryptedToken);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
logger.error("auth", "Failed to decrypt token", error);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async clearToken() {
|
|
65
|
-
config.clearToken();
|
|
66
|
-
logger.info("auth", "Token cleared");
|
|
67
|
-
}
|
|
68
|
-
async validateToken() {
|
|
69
|
-
const token = await this.getToken();
|
|
70
|
-
if (!token) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
const { apiService } = await import("./api-ORVLKYAQ.js");
|
|
75
|
-
const user = await apiService.getCurrentUser();
|
|
76
|
-
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
77
|
-
return user;
|
|
78
|
-
} catch (error) {
|
|
79
|
-
logger.warn("auth", "Token validation failed");
|
|
80
|
-
logger.error("auth", "Failed to validate token", error);
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
getMachineId() {
|
|
85
|
-
return config.getMachineId();
|
|
86
|
-
}
|
|
87
|
-
getMachineName() {
|
|
88
|
-
return config.getMachineName();
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
var authService = new AuthServiceImpl();
|
|
92
|
-
|
|
93
|
-
export {
|
|
94
|
-
authService
|
|
95
|
-
};
|