@gowelle/stint-agent 1.1.0 → 1.2.0
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 +44 -34
- package/dist/StatusDashboard-7ZIGEOQ4.js +232 -0
- package/dist/api-HLTR2LTF.js +7 -0
- package/dist/{chunk-OHOFKJL7.js → chunk-FBQA4K5J.js} +95 -207
- package/dist/chunk-IJUJ6NEL.js +369 -0
- package/dist/chunk-RHMTZK2J.js +297 -0
- package/dist/{chunk-IAERVP6F.js → chunk-W4JGOGR7.js} +42 -291
- package/dist/daemon/runner.js +8 -236
- package/dist/index.js +577 -70
- package/package.json +23 -1
- package/dist/api-YI2HWZGL.js +0 -6
|
@@ -1,102 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
apiService,
|
|
3
2
|
config,
|
|
4
3
|
logger
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
|
|
7
|
-
// src/utils/process.ts
|
|
8
|
-
import fs from "fs";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import os from "os";
|
|
11
|
-
import { spawn } from "child_process";
|
|
12
|
-
var PID_FILE = path.join(os.homedir(), ".config", "stint", "daemon.pid");
|
|
13
|
-
function isProcessRunning(pid) {
|
|
14
|
-
try {
|
|
15
|
-
process.kill(pid, 0);
|
|
16
|
-
return true;
|
|
17
|
-
} catch {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function killProcess(pid, signal = "SIGTERM") {
|
|
22
|
-
try {
|
|
23
|
-
process.kill(pid, signal);
|
|
24
|
-
logger.info("process", `Sent ${signal} to process ${pid}`);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
logger.error("process", `Failed to kill process ${pid}`, error);
|
|
27
|
-
throw new Error(`Failed to kill process ${pid}: ${error.message}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function spawnDetached(command, args, options = {}) {
|
|
31
|
-
const logDir = path.join(os.homedir(), ".config", "stint", "logs");
|
|
32
|
-
if (!fs.existsSync(logDir)) {
|
|
33
|
-
fs.mkdirSync(logDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
const stdoutPath = options.stdout || path.join(logDir, "daemon.log");
|
|
36
|
-
const stderrPath = options.stderr || path.join(logDir, "daemon-error.log");
|
|
37
|
-
const out = fs.openSync(stdoutPath, "a");
|
|
38
|
-
const err = fs.openSync(stderrPath, "a");
|
|
39
|
-
const child = spawn(command, args, {
|
|
40
|
-
detached: true,
|
|
41
|
-
stdio: ["ignore", out, err],
|
|
42
|
-
cwd: options.cwd || process.cwd(),
|
|
43
|
-
env: options.env || process.env
|
|
44
|
-
});
|
|
45
|
-
child.unref();
|
|
46
|
-
logger.info("process", `Spawned detached process ${child.pid}`);
|
|
47
|
-
return child.pid;
|
|
48
|
-
}
|
|
49
|
-
function writePidFile(pid) {
|
|
50
|
-
const pidDir = path.dirname(PID_FILE);
|
|
51
|
-
if (!fs.existsSync(pidDir)) {
|
|
52
|
-
fs.mkdirSync(pidDir, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
fs.writeFileSync(PID_FILE, pid.toString(), { mode: 384 });
|
|
55
|
-
logger.info("process", `Wrote PID ${pid} to ${PID_FILE}`);
|
|
56
|
-
}
|
|
57
|
-
function readPidFile() {
|
|
58
|
-
try {
|
|
59
|
-
if (!fs.existsSync(PID_FILE)) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const pidStr = fs.readFileSync(PID_FILE, "utf8").trim();
|
|
63
|
-
const pid = parseInt(pidStr, 10);
|
|
64
|
-
if (isNaN(pid)) {
|
|
65
|
-
logger.warn("process", `Invalid PID in file: ${pidStr}`);
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
return pid;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
logger.error("process", "Failed to read PID file", error);
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function removePidFile() {
|
|
75
|
-
try {
|
|
76
|
-
if (fs.existsSync(PID_FILE)) {
|
|
77
|
-
fs.unlinkSync(PID_FILE);
|
|
78
|
-
logger.info("process", "Removed PID file");
|
|
79
|
-
}
|
|
80
|
-
} catch (error) {
|
|
81
|
-
logger.error("process", "Failed to remove PID file", error);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function validatePidFile() {
|
|
85
|
-
const pid = readPidFile();
|
|
86
|
-
if (pid === null) {
|
|
87
|
-
return { valid: false, pid: null };
|
|
88
|
-
}
|
|
89
|
-
const running = isProcessRunning(pid);
|
|
90
|
-
if (!running) {
|
|
91
|
-
logger.warn("process", `PID file exists but process ${pid} is not running`);
|
|
92
|
-
removePidFile();
|
|
93
|
-
return { valid: false, pid: null };
|
|
94
|
-
}
|
|
95
|
-
return { valid: true, pid };
|
|
96
|
-
}
|
|
97
|
-
function getPidFilePath() {
|
|
98
|
-
return PID_FILE;
|
|
99
|
-
}
|
|
4
|
+
} from "./chunk-RHMTZK2J.js";
|
|
100
5
|
|
|
101
6
|
// src/services/git.ts
|
|
102
7
|
import simpleGit from "simple-git";
|
|
@@ -124,14 +29,17 @@ var GitServiceImpl = class {
|
|
|
124
29
|
* @returns Repository info including branches, status, and last commit
|
|
125
30
|
* @throws Error if not a valid repository or no commits found
|
|
126
31
|
*/
|
|
127
|
-
async getRepoInfo(path3) {
|
|
32
|
+
async getRepoInfo(path3, onProgress) {
|
|
128
33
|
try {
|
|
129
34
|
const git = this.getGit(path3);
|
|
35
|
+
onProgress?.("Getting branch information...");
|
|
130
36
|
const branchSummary = await git.branch();
|
|
131
37
|
const currentBranch = branchSummary.current;
|
|
132
38
|
const branches = branchSummary.all;
|
|
39
|
+
onProgress?.("Checking remote configuration...");
|
|
133
40
|
const remotes = await git.getRemotes(true);
|
|
134
41
|
const remoteUrl = remotes.length > 0 ? remotes[0].refs.fetch : null;
|
|
42
|
+
onProgress?.("Determining default branch...");
|
|
135
43
|
let defaultBranch = currentBranch;
|
|
136
44
|
try {
|
|
137
45
|
const result = await git.raw(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
@@ -146,7 +54,9 @@ var GitServiceImpl = class {
|
|
|
146
54
|
defaultBranch = "master";
|
|
147
55
|
}
|
|
148
56
|
}
|
|
57
|
+
onProgress?.("Checking repository status...");
|
|
149
58
|
const status = await this.getStatus(path3);
|
|
59
|
+
onProgress?.("Getting commit history...");
|
|
150
60
|
const log = await git.log({ maxCount: 1 });
|
|
151
61
|
const lastCommit = log.latest;
|
|
152
62
|
if (!lastCommit) {
|
|
@@ -253,11 +163,11 @@ var GitServiceImpl = class {
|
|
|
253
163
|
var gitService = new GitServiceImpl();
|
|
254
164
|
|
|
255
165
|
// src/services/project.ts
|
|
256
|
-
import
|
|
166
|
+
import path from "path";
|
|
257
167
|
var ProjectServiceImpl = class {
|
|
258
168
|
async linkProject(projectPath, projectId) {
|
|
259
169
|
try {
|
|
260
|
-
const absolutePath =
|
|
170
|
+
const absolutePath = path.resolve(projectPath);
|
|
261
171
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
262
172
|
const linkPath = repoRoot || absolutePath;
|
|
263
173
|
if (!repoRoot) {
|
|
@@ -279,7 +189,7 @@ var ProjectServiceImpl = class {
|
|
|
279
189
|
}
|
|
280
190
|
async unlinkProject(projectPath) {
|
|
281
191
|
try {
|
|
282
|
-
const absolutePath =
|
|
192
|
+
const absolutePath = path.resolve(projectPath);
|
|
283
193
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
284
194
|
const lookupPath = repoRoot || absolutePath;
|
|
285
195
|
const linkedProject = config.getProject(lookupPath);
|
|
@@ -294,7 +204,7 @@ var ProjectServiceImpl = class {
|
|
|
294
204
|
}
|
|
295
205
|
}
|
|
296
206
|
async getLinkedProject(projectPath) {
|
|
297
|
-
const absolutePath =
|
|
207
|
+
const absolutePath = path.resolve(projectPath);
|
|
298
208
|
let project = config.getProject(absolutePath);
|
|
299
209
|
if (project) return project;
|
|
300
210
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
@@ -322,120 +232,99 @@ var ProjectServiceImpl = class {
|
|
|
322
232
|
};
|
|
323
233
|
var projectService = new ProjectServiceImpl();
|
|
324
234
|
|
|
325
|
-
// src/
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
235
|
+
// src/utils/process.ts
|
|
236
|
+
import fs from "fs";
|
|
237
|
+
import path2 from "path";
|
|
238
|
+
import os from "os";
|
|
239
|
+
import { spawn } from "child_process";
|
|
240
|
+
var PID_FILE = path2.join(os.homedir(), ".config", "stint", "daemon.pid");
|
|
241
|
+
function isProcessRunning(pid) {
|
|
242
|
+
try {
|
|
243
|
+
process.kill(pid, 0);
|
|
244
|
+
return true;
|
|
245
|
+
} catch {
|
|
246
|
+
return false;
|
|
338
247
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
while (this.queue.length > 0) {
|
|
348
|
-
const item = this.queue.shift();
|
|
349
|
-
if (!item) break;
|
|
350
|
-
try {
|
|
351
|
-
await this.executeCommit(item.commit, item.project);
|
|
352
|
-
} catch (error) {
|
|
353
|
-
logger.error("queue", `Failed to execute commit ${item.commit.id}`, error);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
this.isProcessing = false;
|
|
248
|
+
}
|
|
249
|
+
function killProcess(pid, signal = "SIGTERM") {
|
|
250
|
+
try {
|
|
251
|
+
process.kill(pid, signal);
|
|
252
|
+
logger.info("process", `Sent ${signal} to process ${pid}`);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
logger.error("process", `Failed to kill process ${pid}`, error);
|
|
255
|
+
throw new Error(`Failed to kill process ${pid}: ${error.message}`);
|
|
357
256
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const projectPath = this.findProjectPath(project.id);
|
|
365
|
-
if (!projectPath) {
|
|
366
|
-
throw new Error(`Project ${project.id} is not linked to any local directory`);
|
|
367
|
-
}
|
|
368
|
-
logger.info("queue", `Executing in directory: ${projectPath}`);
|
|
369
|
-
const isRepo = await gitService.isRepo(projectPath);
|
|
370
|
-
if (!isRepo) {
|
|
371
|
-
throw new Error(`Directory ${projectPath} is not a git repository`);
|
|
372
|
-
}
|
|
373
|
-
const status = await gitService.getStatus(projectPath);
|
|
374
|
-
const hasStagedChanges = status.staged.length > 0;
|
|
375
|
-
if (!hasStagedChanges) {
|
|
376
|
-
throw new Error('No staged changes to commit. Please stage files using "git add" before committing.');
|
|
377
|
-
}
|
|
378
|
-
logger.info("queue", `Committing ${status.staged.length} staged files.`);
|
|
379
|
-
logger.info("queue", `Creating commit with message: "${commit.message}"`);
|
|
380
|
-
const sha = await gitService.commit(projectPath, commit.message);
|
|
381
|
-
logger.success("queue", `Commit created successfully: ${sha}`);
|
|
382
|
-
await this.reportSuccess(commit.id, sha);
|
|
383
|
-
return sha;
|
|
384
|
-
} catch (error) {
|
|
385
|
-
const errorMessage = error.message;
|
|
386
|
-
logger.error("queue", `Commit execution failed: ${errorMessage}`);
|
|
387
|
-
await this.reportFailure(commit.id, errorMessage);
|
|
388
|
-
throw error;
|
|
389
|
-
}
|
|
257
|
+
}
|
|
258
|
+
function spawnDetached(command, args, options = {}) {
|
|
259
|
+
const logDir = path2.join(os.homedir(), ".config", "stint", "logs");
|
|
260
|
+
if (!fs.existsSync(logDir)) {
|
|
261
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
390
262
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
263
|
+
const stdoutPath = options.stdout || path2.join(logDir, "daemon.log");
|
|
264
|
+
const stderrPath = options.stderr || path2.join(logDir, "daemon-error.log");
|
|
265
|
+
const out = fs.openSync(stdoutPath, "a");
|
|
266
|
+
const err = fs.openSync(stderrPath, "a");
|
|
267
|
+
const child = spawn(command, args, {
|
|
268
|
+
detached: true,
|
|
269
|
+
stdio: ["ignore", out, err],
|
|
270
|
+
cwd: options.cwd || process.cwd(),
|
|
271
|
+
env: options.env || process.env
|
|
272
|
+
});
|
|
273
|
+
child.unref();
|
|
274
|
+
logger.info("process", `Spawned detached process ${child.pid}`);
|
|
275
|
+
return child.pid;
|
|
276
|
+
}
|
|
277
|
+
function writePidFile(pid) {
|
|
278
|
+
const pidDir = path2.dirname(PID_FILE);
|
|
279
|
+
if (!fs.existsSync(pidDir)) {
|
|
280
|
+
fs.mkdirSync(pidDir, { recursive: true });
|
|
401
281
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
} catch (apiError) {
|
|
410
|
-
logger.error("queue", "Failed to report commit failure to API", apiError);
|
|
282
|
+
fs.writeFileSync(PID_FILE, pid.toString(), { mode: 384 });
|
|
283
|
+
logger.info("process", `Wrote PID ${pid} to ${PID_FILE}`);
|
|
284
|
+
}
|
|
285
|
+
function readPidFile() {
|
|
286
|
+
try {
|
|
287
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
288
|
+
return null;
|
|
411
289
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const allProjects = projectService.getAllLinkedProjects();
|
|
418
|
-
for (const [path3, linkedProject] of Object.entries(allProjects)) {
|
|
419
|
-
if (linkedProject.projectId === projectId) {
|
|
420
|
-
return path3;
|
|
421
|
-
}
|
|
290
|
+
const pidStr = fs.readFileSync(PID_FILE, "utf8").trim();
|
|
291
|
+
const pid = parseInt(pidStr, 10);
|
|
292
|
+
if (isNaN(pid)) {
|
|
293
|
+
logger.warn("process", `Invalid PID in file: ${pidStr}`);
|
|
294
|
+
return null;
|
|
422
295
|
}
|
|
296
|
+
return pid;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
logger.error("process", "Failed to read PID file", error);
|
|
423
299
|
return null;
|
|
424
300
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
301
|
+
}
|
|
302
|
+
function removePidFile() {
|
|
303
|
+
try {
|
|
304
|
+
if (fs.existsSync(PID_FILE)) {
|
|
305
|
+
fs.unlinkSync(PID_FILE);
|
|
306
|
+
logger.info("process", "Removed PID file");
|
|
307
|
+
}
|
|
308
|
+
} catch (error) {
|
|
309
|
+
logger.error("process", "Failed to remove PID file", error);
|
|
430
310
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
return
|
|
311
|
+
}
|
|
312
|
+
function validatePidFile() {
|
|
313
|
+
const pid = readPidFile();
|
|
314
|
+
if (pid === null) {
|
|
315
|
+
return { valid: false, pid: null };
|
|
436
316
|
}
|
|
437
|
-
|
|
438
|
-
|
|
317
|
+
const running = isProcessRunning(pid);
|
|
318
|
+
if (!running) {
|
|
319
|
+
logger.warn("process", `PID file exists but process ${pid} is not running`);
|
|
320
|
+
removePidFile();
|
|
321
|
+
return { valid: false, pid: null };
|
|
322
|
+
}
|
|
323
|
+
return { valid: true, pid };
|
|
324
|
+
}
|
|
325
|
+
function getPidFilePath() {
|
|
326
|
+
return PID_FILE;
|
|
327
|
+
}
|
|
439
328
|
|
|
440
329
|
export {
|
|
441
330
|
gitService,
|
|
@@ -446,6 +335,5 @@ export {
|
|
|
446
335
|
writePidFile,
|
|
447
336
|
removePidFile,
|
|
448
337
|
validatePidFile,
|
|
449
|
-
getPidFilePath
|
|
450
|
-
commitQueue
|
|
338
|
+
getPidFilePath
|
|
451
339
|
};
|