@gowelle/stint-agent 1.2.26 → 1.2.28

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.
@@ -2,10 +2,10 @@ import {
2
2
  gitService,
3
3
  projectService,
4
4
  validatePidFile
5
- } from "./chunk-Z7ZSQCVR.js";
5
+ } from "./chunk-JIRQHCFR.js";
6
6
  import {
7
7
  authService
8
- } from "./chunk-ZHIGA6EY.js";
8
+ } from "./chunk-RHWE22KH.js";
9
9
 
10
10
  // src/components/StatusDashboard.tsx
11
11
  import { useState, useEffect } from "react";
@@ -0,0 +1,7 @@
1
+ import {
2
+ apiService
3
+ } from "./chunk-UJBUIRQZ.js";
4
+ import "./chunk-RHWE22KH.js";
5
+ export {
6
+ apiService
7
+ };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  apiService
3
- } from "./chunk-YXNR7UVV.js";
3
+ } from "./chunk-UJBUIRQZ.js";
4
4
  import {
5
5
  gitService,
6
6
  projectService
7
- } from "./chunk-Z7ZSQCVR.js";
7
+ } from "./chunk-JIRQHCFR.js";
8
8
  import {
9
9
  authService,
10
10
  config,
11
11
  logger
12
- } from "./chunk-ZHIGA6EY.js";
12
+ } from "./chunk-RHWE22KH.js";
13
13
 
14
14
  // src/utils/notify.ts
15
15
  import notifier from "node-notifier";
@@ -561,6 +561,10 @@ var WebSocketServiceImpl = class {
561
561
  forceTLS = true;
562
562
  }
563
563
  logger.info("websocket", `Connecting to ${wsHost}:${wsPort} with key ${reverbAppKey}...`);
564
+ if (typeof global.WebSocket === "undefined") {
565
+ const { WebSocket } = await import("ws");
566
+ global.WebSocket = WebSocket;
567
+ }
564
568
  const pusherClient = new Pusher(reverbAppKey, {
565
569
  wsHost,
566
570
  wsPort,
@@ -569,6 +573,12 @@ var WebSocketServiceImpl = class {
569
573
  disableStats: true,
570
574
  cluster: "",
571
575
  // Required but unused for Reverb
576
+ // Activity timeout: if no activity for this duration, Pusher sends a ping
577
+ activityTimeout: 6e4,
578
+ // 60 seconds (default is 120s)
579
+ // Pong timeout: wait this long for pong response before considering connection dead
580
+ pongTimeout: 3e4,
581
+ // 30 seconds (default is 30s)
572
582
  authorizer: (channel) => ({
573
583
  authorize: async (socketId, callback) => {
574
584
  try {
@@ -599,7 +609,7 @@ var WebSocketServiceImpl = class {
599
609
  }
600
610
  })
601
611
  });
602
- this.echo = new Echo({
612
+ const echoInstance = new Echo({
603
613
  broadcaster: "reverb",
604
614
  key: reverbAppKey,
605
615
  wsHost,
@@ -616,21 +626,32 @@ var WebSocketServiceImpl = class {
616
626
  },
617
627
  client: pusherClient
618
628
  });
629
+ this.echo = echoInstance;
619
630
  logger.info("websocket", "Echo instance created, setting up connection handlers...");
620
631
  return new Promise((resolve, reject) => {
621
- if (!this.echo) {
622
- reject(new Error("Echo not initialized"));
632
+ if (pusherClient.connection.state === "connected") {
633
+ logger.success("websocket", "\u2705 Already connected to Broadcaster");
634
+ writeStatus({ connected: true });
635
+ this.reconnectAttempts = 0;
636
+ this.isManualDisconnect = false;
637
+ resolve();
623
638
  return;
624
639
  }
625
640
  const connectionTimeout = setTimeout(() => {
626
- const state = this.echo?.connector.pusher.connection.state || "unknown";
627
- logger.error("websocket", `Connection timeout after 15s (state: ${state})`);
628
- reject(new Error(`Connection timeout - stuck in state: ${state}`));
629
- }, 15e3);
630
- this.echo.connector.pusher.connection.bind("state_change", (states) => {
631
- logger.info("websocket", `Connection state: ${states.previous} -> ${states.current}`);
641
+ if (this.echo !== echoInstance) return;
642
+ const state = pusherClient.connection.state || "unknown";
643
+ logger.error("websocket", `Connection timeout after 30s (state: ${state})`);
644
+ if (state !== "connected") {
645
+ reject(new Error(`Connection timeout - stuck in state: ${state}`));
646
+ }
647
+ }, 3e4);
648
+ pusherClient.connection.bind("state_change", (states) => {
649
+ if (this.echo === echoInstance) {
650
+ logger.info("websocket", `Connection state: ${states.previous} -> ${states.current}`);
651
+ }
632
652
  });
633
- this.echo.connector.pusher.connection.bind("connected", () => {
653
+ pusherClient.connection.bind("connected", () => {
654
+ if (this.echo !== echoInstance) return;
634
655
  clearTimeout(connectionTimeout);
635
656
  logger.success("websocket", "\u2705 Connected to Broadcaster via Sanctum");
636
657
  writeStatus({ connected: true });
@@ -638,27 +659,37 @@ var WebSocketServiceImpl = class {
638
659
  this.isManualDisconnect = false;
639
660
  resolve();
640
661
  });
641
- this.echo.connector.pusher.connection.bind("error", (error) => {
662
+ pusherClient.connection.bind("error", (error) => {
663
+ if (this.echo !== echoInstance) return;
642
664
  clearTimeout(connectionTimeout);
643
665
  const errorMessage = error instanceof Error ? error.message : JSON.stringify(error) || "Unknown connection error";
644
666
  logger.error("websocket", `WebSocket error: ${errorMessage}`);
645
- if (error?.data?.code === 1006) {
667
+ if (error && typeof error === "object" && "data" in error && error.data?.code === 1006) {
646
668
  logger.warn("websocket", "Detected abnormal closure (1006), ensuring reconnection...");
647
669
  this.handleDisconnect();
648
670
  }
649
- reject(new Error(errorMessage));
671
+ if (pusherClient.connection.state === "failed") {
672
+ reject(new Error(errorMessage));
673
+ }
650
674
  });
651
- this.echo.connector.pusher.connection.bind("disconnected", () => {
675
+ pusherClient.connection.bind("disconnected", () => {
676
+ if (this.echo !== echoInstance) return;
652
677
  logger.warn("websocket", "WebSocket disconnected");
653
678
  writeStatus({ connected: false });
654
679
  this.handleDisconnect();
655
680
  });
656
- this.echo.connector.pusher.connection.bind("failed", () => {
681
+ pusherClient.connection.bind("failed", () => {
682
+ if (this.echo !== echoInstance) return;
657
683
  clearTimeout(connectionTimeout);
658
684
  logger.error("websocket", "WebSocket connection failed");
659
685
  this.handleDisconnect();
660
686
  reject(new Error("WebSocket connection failed"));
661
687
  });
688
+ pusherClient.connection.bind("unavailable", () => {
689
+ if (this.echo !== echoInstance) return;
690
+ logger.warn("websocket", "WebSocket connection unavailable, attempting auto-reconnect");
691
+ writeStatus({ connected: false });
692
+ });
662
693
  });
663
694
  } catch (error) {
664
695
  logger.error("websocket", "Failed to connect", error);
@@ -716,7 +747,7 @@ var WebSocketServiceImpl = class {
716
747
  if (commit.has_large_files) {
717
748
  try {
718
749
  logger.info("websocket", `Commit ${commit.id} marked as large, fetching full details...`);
719
- const { apiService: apiService2 } = await import("./api-3Y72TBXG.js");
750
+ const { apiService: apiService2 } = await import("./api-MEUGSQYO.js");
720
751
  const fullCommit = await apiService2.getCommit(commit.id);
721
752
  commit = {
722
753
  ...commit,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  config,
3
3
  logger
4
- } from "./chunk-ZHIGA6EY.js";
4
+ } from "./chunk-RHWE22KH.js";
5
5
 
6
6
  // src/services/git.ts
7
7
  import simpleGit from "simple-git";
@@ -308,7 +308,7 @@ var AuthServiceImpl = class {
308
308
  return null;
309
309
  }
310
310
  try {
311
- const { apiService } = await import("./api-3Y72TBXG.js");
311
+ const { apiService } = await import("./api-MEUGSQYO.js");
312
312
  const user = await apiService.getCurrentUser();
313
313
  logger.info("auth", `Token validated for user: ${user.email}`);
314
314
  return user;
@@ -2,7 +2,7 @@ import {
2
2
  authService,
3
3
  config,
4
4
  logger
5
- } from "./chunk-ZHIGA6EY.js";
5
+ } from "./chunk-RHWE22KH.js";
6
6
 
7
7
  // src/utils/circuit-breaker.ts
8
8
  var CircuitBreaker = class {
@@ -98,7 +98,7 @@ var CircuitBreaker = class {
98
98
  };
99
99
 
100
100
  // src/services/api.ts
101
- var AGENT_VERSION = "1.2.26";
101
+ var AGENT_VERSION = "1.2.28";
102
102
  var ApiServiceImpl = class {
103
103
  sessionId = null;
104
104
  circuitBreaker = new CircuitBreaker({
@@ -358,7 +358,7 @@ var ApiServiceImpl = class {
358
358
  */
359
359
  async syncProject(projectId, data, changedFiles) {
360
360
  logger.info("api", `Syncing project ${projectId}`);
361
- await this.withRetry(async () => {
361
+ return this.withRetry(async () => {
362
362
  const payload = {
363
363
  repo_path: data.repoPath,
364
364
  remote_url: data.remoteUrl,
@@ -369,11 +369,14 @@ var ApiServiceImpl = class {
369
369
  if (changedFiles && changedFiles.length > 0) {
370
370
  payload.changed_files = changedFiles;
371
371
  }
372
- await this.request(`/api/agent/projects/${projectId}/sync`, {
372
+ const response = await this.request(`/api/agent/projects/${projectId}/sync`, {
373
373
  method: "POST",
374
374
  body: JSON.stringify(payload)
375
375
  });
376
376
  logger.success("api", `Project ${projectId} synced (${changedFiles?.length ?? 0} changed files)`);
377
+ return {
378
+ auto_sync: response.auto_sync
379
+ };
377
380
  }, "Sync project");
378
381
  }
379
382
  /**
@@ -3,20 +3,20 @@ import {
3
3
  commitQueue,
4
4
  notify,
5
5
  websocketService
6
- } from "../chunk-CCMXVYVO.js";
6
+ } from "../chunk-GLD7IU2F.js";
7
7
  import {
8
8
  apiService
9
- } from "../chunk-YXNR7UVV.js";
9
+ } from "../chunk-UJBUIRQZ.js";
10
10
  import {
11
11
  gitService,
12
12
  projectService,
13
13
  removePidFile,
14
14
  writePidFile
15
- } from "../chunk-Z7ZSQCVR.js";
15
+ } from "../chunk-JIRQHCFR.js";
16
16
  import {
17
17
  authService,
18
18
  logger
19
- } from "../chunk-ZHIGA6EY.js";
19
+ } from "../chunk-RHWE22KH.js";
20
20
 
21
21
  // src/daemon/runner.ts
22
22
  import "dotenv/config";
@@ -25,8 +25,8 @@ import "dotenv/config";
25
25
  import fs from "fs";
26
26
  var FileWatcher = class {
27
27
  watchers = /* @__PURE__ */ new Map();
28
- DEBOUNCE_DELAY = 3e3;
29
- // 3 seconds
28
+ DEFAULT_DEBOUNCE_DELAY = 3e3;
29
+ // 3 seconds fallback
30
30
  IGNORE_PATTERNS = [
31
31
  ".git",
32
32
  "node_modules",
@@ -76,6 +76,11 @@ var FileWatcher = class {
76
76
  return;
77
77
  }
78
78
  logger.debug("watcher", `File change detected: ${filenameStr} (${eventType}) in ${projectPath}`);
79
+ const watcherInfo = this.watchers.get(projectPath);
80
+ if (watcherInfo && !watcherInfo.enabled) {
81
+ logger.debug("watcher", `Auto-sync disabled for project ${projectId}, skipping sync`);
82
+ return;
83
+ }
79
84
  this.debounceSync(projectPath, projectId);
80
85
  });
81
86
  watcher.on("error", (error) => {
@@ -91,7 +96,10 @@ var FileWatcher = class {
91
96
  this.watchers.set(projectPath, {
92
97
  watcher,
93
98
  debounceTimer: null,
94
- projectId
99
+ projectId,
100
+ debounceDelay: this.DEFAULT_DEBOUNCE_DELAY,
101
+ enabled: true
102
+ // Enabled by default until settings are fetched
95
103
  });
96
104
  logger.info("watcher", `Started watching: ${projectPath} (project: ${projectId})`);
97
105
  } catch (error) {
@@ -147,7 +155,7 @@ var FileWatcher = class {
147
155
  } finally {
148
156
  watcherInfo.debounceTimer = null;
149
157
  }
150
- }, this.DEBOUNCE_DELAY);
158
+ }, watcherInfo.debounceDelay);
151
159
  }
152
160
  /**
153
161
  * Perform sync operation for a project
@@ -162,7 +170,10 @@ var FileWatcher = class {
162
170
  }
163
171
  const repoInfo = await gitService.getRepoInfo(projectPath);
164
172
  const changedFiles = await gitService.getChangedFiles(projectPath);
165
- await apiService.syncProject(projectId, repoInfo, changedFiles);
173
+ const response = await apiService.syncProject(projectId, repoInfo, changedFiles);
174
+ if (response.auto_sync) {
175
+ this.updateProjectSettings(projectId, response.auto_sync);
176
+ }
166
177
  logger.success("watcher", `Synced project ${projectId}`);
167
178
  let projectName = projectId;
168
179
  try {
@@ -234,6 +245,18 @@ var FileWatcher = class {
234
245
  logger.error("watcher", `Error removing watcher for ${projectPath}`, error);
235
246
  }
236
247
  }
248
+ /**
249
+ * Update auto-sync settings for a project
250
+ */
251
+ updateProjectSettings(projectId, settings) {
252
+ for (const watcherInfo of this.watchers.values()) {
253
+ if (watcherInfo.projectId === projectId) {
254
+ watcherInfo.enabled = settings.enabled;
255
+ watcherInfo.debounceDelay = settings.interval * 1e3;
256
+ logger.debug("watcher", `Updated settings for project ${projectId}: enabled=${settings.enabled}, interval=${settings.interval}s`);
257
+ }
258
+ }
259
+ }
237
260
  };
238
261
 
239
262
  // src/daemon/index.ts
@@ -286,6 +309,9 @@ Project: ${project.name}`
286
309
  });
287
310
  websocketService.onProjectUpdated((project) => {
288
311
  logger.info("daemon", `Project updated: ${project.id} - ${project.name}`);
312
+ if (project.settings?.auto_sync) {
313
+ fileWatcher.updateProjectSettings(project.id, project.settings.auto_sync);
314
+ }
289
315
  notify({
290
316
  title: "Project Updated",
291
317
  message: project.name
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  commitQueue,
4
4
  websocketService
5
- } from "./chunk-CCMXVYVO.js";
5
+ } from "./chunk-GLD7IU2F.js";
6
6
  import {
7
7
  apiService
8
- } from "./chunk-YXNR7UVV.js";
8
+ } from "./chunk-UJBUIRQZ.js";
9
9
  import {
10
10
  getPidFilePath,
11
11
  gitService,
@@ -14,14 +14,14 @@ import {
14
14
  projectService,
15
15
  spawnDetached,
16
16
  validatePidFile
17
- } from "./chunk-Z7ZSQCVR.js";
17
+ } from "./chunk-JIRQHCFR.js";
18
18
  import {
19
19
  __commonJS,
20
20
  __toESM,
21
21
  authService,
22
22
  config,
23
23
  logger
24
- } from "./chunk-ZHIGA6EY.js";
24
+ } from "./chunk-RHWE22KH.js";
25
25
 
26
26
  // node_modules/semver/internal/constants.js
27
27
  var require_constants = __commonJS({
@@ -2552,7 +2552,7 @@ function registerStatusCommand(program2) {
2552
2552
  try {
2553
2553
  const { render } = await import("ink");
2554
2554
  const { createElement } = await import("react");
2555
- const { StatusDashboard } = await import("./StatusDashboard-6BRCPQQO.js");
2555
+ const { StatusDashboard } = await import("./StatusDashboard-BV7RHIIH.js");
2556
2556
  render(createElement(StatusDashboard, { cwd }));
2557
2557
  return;
2558
2558
  } catch (error) {
@@ -4356,7 +4356,7 @@ ${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
4356
4356
  }
4357
4357
 
4358
4358
  // src/index.ts
4359
- var AGENT_VERSION = "1.2.26";
4359
+ var AGENT_VERSION = "1.2.28";
4360
4360
  var program = new Command();
4361
4361
  program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText("after", `
4362
4362
  ${chalk15.bold("Examples:")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gowelle/stint-agent",
3
- "version": "1.2.26",
3
+ "version": "1.2.28",
4
4
  "description": "Local agent for Stint - Project Assistant",
5
5
  "author": "Gowelle John <gowelle.john@icloud.com>",
6
6
  "license": "MIT",
@@ -1,7 +0,0 @@
1
- import {
2
- apiService
3
- } from "./chunk-YXNR7UVV.js";
4
- import "./chunk-ZHIGA6EY.js";
5
- export {
6
- apiService
7
- };