@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.
@@ -1,102 +1,7 @@
1
1
  import {
2
- apiService,
3
2
  config,
4
3
  logger
5
- } from "./chunk-IAERVP6F.js";
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 path2 from "path";
166
+ import path from "path";
257
167
  var ProjectServiceImpl = class {
258
168
  async linkProject(projectPath, projectId) {
259
169
  try {
260
- const absolutePath = path2.resolve(projectPath);
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 = path2.resolve(projectPath);
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 = path2.resolve(projectPath);
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/daemon/queue.ts
326
- var CommitQueueProcessor = class {
327
- queue = [];
328
- isProcessing = false;
329
- /**
330
- * Add commit to processing queue
331
- */
332
- addToQueue(commit, project) {
333
- this.queue.push({ commit, project });
334
- logger.info("queue", `Added commit ${commit.id} to queue (position: ${this.queue.length})`);
335
- if (!this.isProcessing) {
336
- this.processQueue();
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
- * Process commits sequentially
341
- */
342
- async processQueue() {
343
- if (this.isProcessing) {
344
- return;
345
- }
346
- this.isProcessing = true;
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
- * Execute a single commit
360
- */
361
- async executeCommit(commit, project) {
362
- logger.info("queue", `Processing commit: ${commit.id} - ${commit.message}`);
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
- * Report successful execution to API
393
- */
394
- async reportSuccess(commitId, sha) {
395
- try {
396
- await apiService.markCommitExecuted(commitId, sha);
397
- logger.success("queue", `Reported commit execution to API: ${commitId} -> ${sha}`);
398
- } catch (error) {
399
- logger.error("queue", "Failed to report commit success to API", error);
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
- * Report failed execution to API
404
- */
405
- async reportFailure(commitId, error) {
406
- try {
407
- await apiService.markCommitFailed(commitId, error);
408
- logger.info("queue", `Reported commit failure to API: ${commitId}`);
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
- * Find local path for a project ID
415
- */
416
- findProjectPath(projectId) {
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
- * Check if queue is currently processing
427
- */
428
- isCurrentlyProcessing() {
429
- return this.isProcessing;
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
- * Get queue length
433
- */
434
- getQueueLength() {
435
- return this.queue.length;
311
+ }
312
+ function validatePidFile() {
313
+ const pid = readPidFile();
314
+ if (pid === null) {
315
+ return { valid: false, pid: null };
436
316
  }
437
- };
438
- var commitQueue = new CommitQueueProcessor();
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
  };