@gowelle/stint-agent 1.0.8 → 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 +55 -47
- package/dist/StatusDashboard-7ZIGEOQ4.js +232 -0
- package/dist/api-HLTR2LTF.js +7 -0
- package/dist/{chunk-56UZEICO.js → chunk-FBQA4K5J.js} +106 -207
- package/dist/chunk-IJUJ6NEL.js +369 -0
- package/dist/{chunk-5OKRSNU4.js → chunk-RHMTZK2J.js} +60 -242
- package/dist/chunk-W4JGOGR7.js +384 -0
- package/dist/daemon/runner.js +8 -210
- package/dist/index.js +577 -70
- package/package.json +24 -1
- package/dist/api-2MI5XBJX.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";
|
|
@@ -104,6 +9,11 @@ var GitServiceImpl = class {
|
|
|
104
9
|
getGit(path3) {
|
|
105
10
|
return simpleGit(path3);
|
|
106
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Check if a directory is a git repository
|
|
14
|
+
* @param path - Directory path to check
|
|
15
|
+
* @returns True if directory is a git repository
|
|
16
|
+
*/
|
|
107
17
|
async isRepo(path3) {
|
|
108
18
|
try {
|
|
109
19
|
const git = this.getGit(path3);
|
|
@@ -113,14 +23,23 @@ var GitServiceImpl = class {
|
|
|
113
23
|
return false;
|
|
114
24
|
}
|
|
115
25
|
}
|
|
116
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Get comprehensive repository information
|
|
28
|
+
* @param path - Repository path
|
|
29
|
+
* @returns Repository info including branches, status, and last commit
|
|
30
|
+
* @throws Error if not a valid repository or no commits found
|
|
31
|
+
*/
|
|
32
|
+
async getRepoInfo(path3, onProgress) {
|
|
117
33
|
try {
|
|
118
34
|
const git = this.getGit(path3);
|
|
35
|
+
onProgress?.("Getting branch information...");
|
|
119
36
|
const branchSummary = await git.branch();
|
|
120
37
|
const currentBranch = branchSummary.current;
|
|
121
38
|
const branches = branchSummary.all;
|
|
39
|
+
onProgress?.("Checking remote configuration...");
|
|
122
40
|
const remotes = await git.getRemotes(true);
|
|
123
41
|
const remoteUrl = remotes.length > 0 ? remotes[0].refs.fetch : null;
|
|
42
|
+
onProgress?.("Determining default branch...");
|
|
124
43
|
let defaultBranch = currentBranch;
|
|
125
44
|
try {
|
|
126
45
|
const result = await git.raw(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
@@ -135,7 +54,9 @@ var GitServiceImpl = class {
|
|
|
135
54
|
defaultBranch = "master";
|
|
136
55
|
}
|
|
137
56
|
}
|
|
57
|
+
onProgress?.("Checking repository status...");
|
|
138
58
|
const status = await this.getStatus(path3);
|
|
59
|
+
onProgress?.("Getting commit history...");
|
|
139
60
|
const log = await git.log({ maxCount: 1 });
|
|
140
61
|
const lastCommit = log.latest;
|
|
141
62
|
if (!lastCommit) {
|
|
@@ -242,11 +163,11 @@ var GitServiceImpl = class {
|
|
|
242
163
|
var gitService = new GitServiceImpl();
|
|
243
164
|
|
|
244
165
|
// src/services/project.ts
|
|
245
|
-
import
|
|
166
|
+
import path from "path";
|
|
246
167
|
var ProjectServiceImpl = class {
|
|
247
168
|
async linkProject(projectPath, projectId) {
|
|
248
169
|
try {
|
|
249
|
-
const absolutePath =
|
|
170
|
+
const absolutePath = path.resolve(projectPath);
|
|
250
171
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
251
172
|
const linkPath = repoRoot || absolutePath;
|
|
252
173
|
if (!repoRoot) {
|
|
@@ -268,7 +189,7 @@ var ProjectServiceImpl = class {
|
|
|
268
189
|
}
|
|
269
190
|
async unlinkProject(projectPath) {
|
|
270
191
|
try {
|
|
271
|
-
const absolutePath =
|
|
192
|
+
const absolutePath = path.resolve(projectPath);
|
|
272
193
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
273
194
|
const lookupPath = repoRoot || absolutePath;
|
|
274
195
|
const linkedProject = config.getProject(lookupPath);
|
|
@@ -283,7 +204,7 @@ var ProjectServiceImpl = class {
|
|
|
283
204
|
}
|
|
284
205
|
}
|
|
285
206
|
async getLinkedProject(projectPath) {
|
|
286
|
-
const absolutePath =
|
|
207
|
+
const absolutePath = path.resolve(projectPath);
|
|
287
208
|
let project = config.getProject(absolutePath);
|
|
288
209
|
if (project) return project;
|
|
289
210
|
const repoRoot = await gitService.getRepoRoot(absolutePath);
|
|
@@ -311,120 +232,99 @@ var ProjectServiceImpl = class {
|
|
|
311
232
|
};
|
|
312
233
|
var projectService = new ProjectServiceImpl();
|
|
313
234
|
|
|
314
|
-
// src/
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
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;
|
|
327
247
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
while (this.queue.length > 0) {
|
|
337
|
-
const item = this.queue.shift();
|
|
338
|
-
if (!item) break;
|
|
339
|
-
try {
|
|
340
|
-
await this.executeCommit(item.commit, item.project);
|
|
341
|
-
} catch (error) {
|
|
342
|
-
logger.error("queue", `Failed to execute commit ${item.commit.id}`, error);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
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}`);
|
|
346
256
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
const projectPath = this.findProjectPath(project.id);
|
|
354
|
-
if (!projectPath) {
|
|
355
|
-
throw new Error(`Project ${project.id} is not linked to any local directory`);
|
|
356
|
-
}
|
|
357
|
-
logger.info("queue", `Executing in directory: ${projectPath}`);
|
|
358
|
-
const isRepo = await gitService.isRepo(projectPath);
|
|
359
|
-
if (!isRepo) {
|
|
360
|
-
throw new Error(`Directory ${projectPath} is not a git repository`);
|
|
361
|
-
}
|
|
362
|
-
const status = await gitService.getStatus(projectPath);
|
|
363
|
-
const hasStagedChanges = status.staged.length > 0;
|
|
364
|
-
if (!hasStagedChanges) {
|
|
365
|
-
throw new Error('No staged changes to commit. Please stage files using "git add" before committing.');
|
|
366
|
-
}
|
|
367
|
-
logger.info("queue", `Committing ${status.staged.length} staged files.`);
|
|
368
|
-
logger.info("queue", `Creating commit with message: "${commit.message}"`);
|
|
369
|
-
const sha = await gitService.commit(projectPath, commit.message);
|
|
370
|
-
logger.success("queue", `Commit created successfully: ${sha}`);
|
|
371
|
-
await this.reportSuccess(commit.id, sha);
|
|
372
|
-
return sha;
|
|
373
|
-
} catch (error) {
|
|
374
|
-
const errorMessage = error.message;
|
|
375
|
-
logger.error("queue", `Commit execution failed: ${errorMessage}`);
|
|
376
|
-
await this.reportFailure(commit.id, errorMessage);
|
|
377
|
-
throw error;
|
|
378
|
-
}
|
|
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 });
|
|
379
262
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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 });
|
|
390
281
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
} catch (apiError) {
|
|
399
|
-
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;
|
|
400
289
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const allProjects = projectService.getAllLinkedProjects();
|
|
407
|
-
for (const [path3, linkedProject] of Object.entries(allProjects)) {
|
|
408
|
-
if (linkedProject.projectId === projectId) {
|
|
409
|
-
return path3;
|
|
410
|
-
}
|
|
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;
|
|
411
295
|
}
|
|
296
|
+
return pid;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
logger.error("process", "Failed to read PID file", error);
|
|
412
299
|
return null;
|
|
413
300
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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);
|
|
419
310
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return
|
|
311
|
+
}
|
|
312
|
+
function validatePidFile() {
|
|
313
|
+
const pid = readPidFile();
|
|
314
|
+
if (pid === null) {
|
|
315
|
+
return { valid: false, pid: null };
|
|
425
316
|
}
|
|
426
|
-
|
|
427
|
-
|
|
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
|
+
}
|
|
428
328
|
|
|
429
329
|
export {
|
|
430
330
|
gitService,
|
|
@@ -435,6 +335,5 @@ export {
|
|
|
435
335
|
writePidFile,
|
|
436
336
|
removePidFile,
|
|
437
337
|
validatePidFile,
|
|
438
|
-
getPidFilePath
|
|
439
|
-
commitQueue
|
|
338
|
+
getPidFilePath
|
|
440
339
|
};
|