@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
package/README.md
CHANGED
|
@@ -141,18 +141,45 @@ stint status
|
|
|
141
141
|
|
|
142
142
|
### Desktop Notifications
|
|
143
143
|
|
|
144
|
-
The daemon sends desktop notifications for important events
|
|
144
|
+
The daemon sends desktop notifications for important events. Notifications can be controlled globally or per-category.
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
#### Notification Categories
|
|
147
|
+
|
|
148
|
+
| Category | Events | Default |
|
|
149
|
+
| ------------- | --------------------------------------------- | ------- |
|
|
150
|
+
| `commits` | Commit approved, pending, pushed, failed | ✅ On |
|
|
151
|
+
| `sync` | Sync requested, project updated | ❌ Off |
|
|
152
|
+
| `suggestions` | New AI suggestions | ✅ On |
|
|
153
|
+
|
|
154
|
+
> **Note:** `sync` is disabled by default because these events fire frequently during active development.
|
|
155
|
+
|
|
156
|
+
#### Toggle All Notifications
|
|
147
157
|
|
|
148
158
|
```bash
|
|
159
|
+
# Disable all notifications
|
|
149
160
|
stint config set notifications.enabled false
|
|
161
|
+
|
|
162
|
+
# Enable all notifications
|
|
163
|
+
stint config set notifications.enabled true
|
|
150
164
|
```
|
|
151
165
|
|
|
152
|
-
|
|
166
|
+
#### Toggle by Category
|
|
153
167
|
|
|
154
168
|
```bash
|
|
155
|
-
|
|
169
|
+
# Disable sync notifications (noisy during development)
|
|
170
|
+
stint config set notifications.sync false
|
|
171
|
+
|
|
172
|
+
# Enable commit notifications
|
|
173
|
+
stint config set notifications.commits true
|
|
174
|
+
|
|
175
|
+
# Disable suggestion notifications
|
|
176
|
+
stint config set notifications.suggestions false
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### View Current Settings
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
stint config list
|
|
156
183
|
```
|
|
157
184
|
|
|
158
185
|
> **Note:** Events are still logged even when notifications are disabled.
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
gitService,
|
|
3
3
|
projectService,
|
|
4
4
|
validatePidFile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XRNTJYCQ.js";
|
|
6
6
|
import {
|
|
7
7
|
authService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HPHXBSGB.js";
|
|
9
9
|
|
|
10
10
|
// src/components/StatusDashboard.tsx
|
|
11
11
|
import { useState, useEffect } from "react";
|
|
@@ -14,7 +14,11 @@ import { Box as Box3, Text as Text3, useApp, useInput } from "ink";
|
|
|
14
14
|
// src/components/Panel.tsx
|
|
15
15
|
import { Box, Text } from "ink";
|
|
16
16
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
-
function Panel({
|
|
17
|
+
function Panel({
|
|
18
|
+
title,
|
|
19
|
+
icon,
|
|
20
|
+
children
|
|
21
|
+
}) {
|
|
18
22
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
19
23
|
/* @__PURE__ */ jsxs(Text, { color: "blue", children: [
|
|
20
24
|
icon ? `${icon} ` : "",
|
|
@@ -29,7 +33,11 @@ function Panel({ title, icon, children }) {
|
|
|
29
33
|
// src/components/StatusRow.tsx
|
|
30
34
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
31
35
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
32
|
-
function StatusRow({
|
|
36
|
+
function StatusRow({
|
|
37
|
+
label,
|
|
38
|
+
value,
|
|
39
|
+
labelWidth = 12
|
|
40
|
+
}) {
|
|
33
41
|
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
34
42
|
/* @__PURE__ */ jsx2(Text2, { bold: true, children: label.padEnd(labelWidth) }),
|
|
35
43
|
typeof value === "string" ? /* @__PURE__ */ jsx2(Text2, { children: value }) : value
|
|
@@ -38,7 +46,9 @@ function StatusRow({ label, value, labelWidth = 12 }) {
|
|
|
38
46
|
|
|
39
47
|
// src/components/StatusDashboard.tsx
|
|
40
48
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
41
|
-
function StatusDashboard({
|
|
49
|
+
function StatusDashboard({
|
|
50
|
+
cwd
|
|
51
|
+
}) {
|
|
42
52
|
const { exit } = useApp();
|
|
43
53
|
const [state, setState] = useState({
|
|
44
54
|
linkedProject: null,
|
|
@@ -101,12 +111,22 @@ function StatusDashboard({ cwd }) {
|
|
|
101
111
|
setShowHelp((prev) => !prev);
|
|
102
112
|
}
|
|
103
113
|
});
|
|
104
|
-
const {
|
|
114
|
+
const {
|
|
115
|
+
linkedProject,
|
|
116
|
+
repoInfo,
|
|
117
|
+
user,
|
|
118
|
+
daemonRunning,
|
|
119
|
+
daemonPid,
|
|
120
|
+
isRepo,
|
|
121
|
+
loading,
|
|
122
|
+
lastRefresh,
|
|
123
|
+
error
|
|
124
|
+
} = state;
|
|
105
125
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", padding: 1, children: [
|
|
106
126
|
/* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" }) }),
|
|
107
127
|
/* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
|
|
108
128
|
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2502" }),
|
|
109
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "
|
|
129
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: " \u{1F4CA} Stint Status Dashboard " }),
|
|
110
130
|
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2502" })
|
|
111
131
|
] }),
|
|
112
132
|
/* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" }) }),
|
|
@@ -116,16 +136,46 @@ function StatusDashboard({ cwd }) {
|
|
|
116
136
|
] }) }),
|
|
117
137
|
loading && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "\u27F3 Refreshing..." }) }),
|
|
118
138
|
/* @__PURE__ */ jsx3(Panel, { title: "Project Status", icon: "\u{1F4E6}", children: linkedProject ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
119
|
-
/* @__PURE__ */ jsx3(
|
|
139
|
+
/* @__PURE__ */ jsx3(
|
|
140
|
+
StatusRow,
|
|
141
|
+
{
|
|
142
|
+
label: "Status:",
|
|
143
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Linked" })
|
|
144
|
+
}
|
|
145
|
+
),
|
|
120
146
|
/* @__PURE__ */ jsx3(StatusRow, { label: "Project ID:", value: linkedProject.projectId }),
|
|
121
|
-
/* @__PURE__ */ jsx3(
|
|
147
|
+
/* @__PURE__ */ jsx3(
|
|
148
|
+
StatusRow,
|
|
149
|
+
{
|
|
150
|
+
label: "Linked At:",
|
|
151
|
+
value: new Date(linkedProject.linkedAt).toLocaleString()
|
|
152
|
+
}
|
|
153
|
+
)
|
|
122
154
|
] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
123
|
-
/* @__PURE__ */ jsx3(
|
|
155
|
+
/* @__PURE__ */ jsx3(
|
|
156
|
+
StatusRow,
|
|
157
|
+
{
|
|
158
|
+
label: "Status:",
|
|
159
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not linked" })
|
|
160
|
+
}
|
|
161
|
+
),
|
|
124
162
|
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint link" to link this directory to a project.' })
|
|
125
163
|
] }) }),
|
|
126
164
|
/* @__PURE__ */ jsx3(Panel, { title: "Git Repository", icon: "\u{1F4C2}", children: isRepo && repoInfo ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
127
|
-
/* @__PURE__ */ jsx3(
|
|
128
|
-
|
|
165
|
+
/* @__PURE__ */ jsx3(
|
|
166
|
+
StatusRow,
|
|
167
|
+
{
|
|
168
|
+
label: "Branch:",
|
|
169
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "cyan", children: repoInfo.currentBranch })
|
|
170
|
+
}
|
|
171
|
+
),
|
|
172
|
+
/* @__PURE__ */ jsx3(
|
|
173
|
+
StatusRow,
|
|
174
|
+
{
|
|
175
|
+
label: "Remote:",
|
|
176
|
+
value: repoInfo.remoteUrl || /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "None" })
|
|
177
|
+
}
|
|
178
|
+
),
|
|
129
179
|
/* @__PURE__ */ jsx3(
|
|
130
180
|
StatusRow,
|
|
131
181
|
{
|
|
@@ -136,18 +186,42 @@ function StatusDashboard({ cwd }) {
|
|
|
136
186
|
renderGitChanges(repoInfo)
|
|
137
187
|
] }) : /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not a git repository" }) }),
|
|
138
188
|
/* @__PURE__ */ jsx3(Panel, { title: "Authentication", icon: "\u{1F510}", children: user ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
139
|
-
/* @__PURE__ */ jsx3(
|
|
189
|
+
/* @__PURE__ */ jsx3(
|
|
190
|
+
StatusRow,
|
|
191
|
+
{
|
|
192
|
+
label: "Status:",
|
|
193
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Authenticated" })
|
|
194
|
+
}
|
|
195
|
+
),
|
|
140
196
|
/* @__PURE__ */ jsx3(StatusRow, { label: "User:", value: `${user.name} (${user.email})` }),
|
|
141
197
|
/* @__PURE__ */ jsx3(StatusRow, { label: "Machine:", value: authService.getMachineName() })
|
|
142
198
|
] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
143
|
-
/* @__PURE__ */ jsx3(
|
|
199
|
+
/* @__PURE__ */ jsx3(
|
|
200
|
+
StatusRow,
|
|
201
|
+
{
|
|
202
|
+
label: "Status:",
|
|
203
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not logged in" })
|
|
204
|
+
}
|
|
205
|
+
),
|
|
144
206
|
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint login" to authenticate.' })
|
|
145
207
|
] }) }),
|
|
146
208
|
/* @__PURE__ */ jsx3(Panel, { title: "Daemon", icon: "\u2699\uFE0F", children: daemonRunning ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
147
|
-
/* @__PURE__ */ jsx3(
|
|
209
|
+
/* @__PURE__ */ jsx3(
|
|
210
|
+
StatusRow,
|
|
211
|
+
{
|
|
212
|
+
label: "Status:",
|
|
213
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 Running" })
|
|
214
|
+
}
|
|
215
|
+
),
|
|
148
216
|
/* @__PURE__ */ jsx3(StatusRow, { label: "PID:", value: String(daemonPid) })
|
|
149
217
|
] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
150
|
-
/* @__PURE__ */ jsx3(
|
|
218
|
+
/* @__PURE__ */ jsx3(
|
|
219
|
+
StatusRow,
|
|
220
|
+
{
|
|
221
|
+
label: "Status:",
|
|
222
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "Not running" })
|
|
223
|
+
}
|
|
224
|
+
),
|
|
151
225
|
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: 'Run "stint daemon start" to start the background agent.' })
|
|
152
226
|
] }) }),
|
|
153
227
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
@@ -161,9 +235,9 @@ function StatusDashboard({ cwd }) {
|
|
|
161
235
|
] }),
|
|
162
236
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
|
|
163
237
|
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[q]" }),
|
|
164
|
-
/* @__PURE__ */ jsx3(Text3, { children: " Quit
|
|
238
|
+
/* @__PURE__ */ jsx3(Text3, { children: " Quit " }),
|
|
165
239
|
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[r]" }),
|
|
166
|
-
/* @__PURE__ */ jsx3(Text3, { children: " Refresh
|
|
240
|
+
/* @__PURE__ */ jsx3(Text3, { children: " Refresh " }),
|
|
167
241
|
/* @__PURE__ */ jsx3(Text3, { color: "cyan", children: "[?]" }),
|
|
168
242
|
/* @__PURE__ */ jsx3(Text3, { children: " Help" })
|
|
169
243
|
] })
|
|
@@ -178,9 +252,9 @@ function StatusDashboard({ cwd }) {
|
|
|
178
252
|
padding: 1,
|
|
179
253
|
children: [
|
|
180
254
|
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Keyboard Shortcuts" }),
|
|
181
|
-
/* @__PURE__ */ jsx3(Text3, { children: "
|
|
182
|
-
/* @__PURE__ */ jsx3(Text3, { children: "
|
|
183
|
-
/* @__PURE__ */ jsx3(Text3, { children: "
|
|
255
|
+
/* @__PURE__ */ jsx3(Text3, { children: " q, Esc - Exit dashboard" }),
|
|
256
|
+
/* @__PURE__ */ jsx3(Text3, { children: " r - Refresh status immediately" }),
|
|
257
|
+
/* @__PURE__ */ jsx3(Text3, { children: " ? - Toggle this help" })
|
|
184
258
|
]
|
|
185
259
|
}
|
|
186
260
|
)
|
|
@@ -202,18 +276,24 @@ function renderGitChanges(repoInfo) {
|
|
|
202
276
|
}
|
|
203
277
|
),
|
|
204
278
|
staged.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "green", children: [
|
|
205
|
-
"Staged:
|
|
279
|
+
"Staged: ",
|
|
206
280
|
staged.length
|
|
207
281
|
] }) }),
|
|
208
282
|
unstaged.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "yellow", children: [
|
|
209
|
-
"Unstaged:
|
|
283
|
+
"Unstaged: ",
|
|
210
284
|
unstaged.length
|
|
211
285
|
] }) }),
|
|
212
286
|
untracked.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
|
|
213
287
|
"Untracked: ",
|
|
214
288
|
untracked.length
|
|
215
289
|
] }) })
|
|
216
|
-
] }) : /* @__PURE__ */ jsx3(
|
|
290
|
+
] }) : /* @__PURE__ */ jsx3(
|
|
291
|
+
StatusRow,
|
|
292
|
+
{
|
|
293
|
+
label: "Changes:",
|
|
294
|
+
value: /* @__PURE__ */ jsx3(Text3, { color: "green", children: "Clean working tree" })
|
|
295
|
+
}
|
|
296
|
+
),
|
|
217
297
|
(ahead > 0 || behind > 0) && /* @__PURE__ */ jsx3(
|
|
218
298
|
StatusRow,
|
|
219
299
|
{
|
|
@@ -44,7 +44,13 @@ var DEFAULT_CONFIG = {
|
|
|
44
44
|
reverbAppKey: "wtn6tu6lirfv6yflujk7",
|
|
45
45
|
projects: {},
|
|
46
46
|
notifications: {
|
|
47
|
-
enabled: true
|
|
47
|
+
enabled: true,
|
|
48
|
+
commits: true,
|
|
49
|
+
// Critical - on by default
|
|
50
|
+
sync: false,
|
|
51
|
+
// Noisy - off by default
|
|
52
|
+
suggestions: true
|
|
53
|
+
// Actionable - on by default
|
|
48
54
|
}
|
|
49
55
|
};
|
|
50
56
|
var ConfigManager = class {
|
|
@@ -164,8 +170,33 @@ var ConfigManager = class {
|
|
|
164
170
|
const notifConfig = this.conf.get("notifications");
|
|
165
171
|
return notifConfig?.enabled ?? true;
|
|
166
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if a specific notification category is enabled.
|
|
175
|
+
* Returns false if master toggle is off, regardless of category setting.
|
|
176
|
+
*/
|
|
177
|
+
isCategoryEnabled(category) {
|
|
178
|
+
if (!this.areNotificationsEnabled()) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const notifConfig = this.conf.get("notifications");
|
|
182
|
+
switch (category) {
|
|
183
|
+
case "commits":
|
|
184
|
+
return notifConfig?.commits ?? true;
|
|
185
|
+
case "sync":
|
|
186
|
+
return notifConfig?.sync ?? false;
|
|
187
|
+
case "suggestions":
|
|
188
|
+
return notifConfig?.suggestions ?? true;
|
|
189
|
+
default:
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
167
193
|
setNotificationsEnabled(enabled) {
|
|
168
|
-
this.conf.
|
|
194
|
+
const current = this.conf.get("notifications") || {};
|
|
195
|
+
this.conf.set("notifications", { ...current, enabled });
|
|
196
|
+
}
|
|
197
|
+
setNotificationCategory(category, enabled) {
|
|
198
|
+
const current = this.conf.get("notifications") || { enabled: true };
|
|
199
|
+
this.conf.set("notifications", { ...current, [category]: enabled });
|
|
169
200
|
}
|
|
170
201
|
};
|
|
171
202
|
var config = new ConfigManager();
|
|
@@ -315,7 +346,7 @@ var AuthServiceImpl = class {
|
|
|
315
346
|
return null;
|
|
316
347
|
}
|
|
317
348
|
try {
|
|
318
|
-
const { apiService } = await import("./api-
|
|
349
|
+
const { apiService } = await import("./api-AFSILC7K.js");
|
|
319
350
|
const user = await apiService.getCurrentUser();
|
|
320
351
|
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
321
352
|
return user;
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
authService,
|
|
3
3
|
config,
|
|
4
4
|
logger
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-HPHXBSGB.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.37";
|
|
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;
|