@gowelle/stint-agent 1.2.21 → 1.2.23
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 +1 -0
- package/dist/{StatusDashboard-A6WLLY4U.js → StatusDashboard-NDXVTKKO.js} +2 -2
- package/dist/api-GSYJMF33.js +7 -0
- package/dist/{chunk-EGEDPYQY.js → chunk-BPDL5KWL.js} +24 -6
- package/dist/{chunk-CJHTK5DX.js → chunk-LTVAFGVL.js} +32 -4
- package/dist/{chunk-E5GW4WNQ.js → chunk-WQUVXZWM.js} +1 -1
- package/dist/{chunk-5OXEEP67.js → chunk-ZUQTJV35.js} +68 -1
- package/dist/daemon/runner.js +6 -5
- package/dist/index.js +224 -18
- package/package.json +98 -92
- package/dist/api-EZ3J5BUL.js +0 -7
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ The official CLI agent for [Stint](https://stint.codes) — a lightweight daemon
|
|
|
20
20
|
- 🚀 Multiple release channels (stable/beta/nightly)
|
|
21
21
|
- 🔍 Built-in environment diagnostics
|
|
22
22
|
- 📈 Resource usage monitoring
|
|
23
|
+
- 📁 File selection for commits (sync changed files to web app)
|
|
23
24
|
|
|
24
25
|
For detailed feature documentation, see the **[Features Guide](docs/features.md)**.
|
|
25
26
|
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
gitService,
|
|
3
3
|
projectService,
|
|
4
4
|
validatePidFile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZUQTJV35.js";
|
|
6
6
|
import {
|
|
7
7
|
authService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WQUVXZWM.js";
|
|
9
9
|
|
|
10
10
|
// src/components/StatusDashboard.tsx
|
|
11
11
|
import { useState, useEffect } from "react";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
apiService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LTVAFGVL.js";
|
|
4
4
|
import {
|
|
5
5
|
gitService,
|
|
6
6
|
projectService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZUQTJV35.js";
|
|
8
8
|
import {
|
|
9
9
|
authService,
|
|
10
10
|
config,
|
|
11
11
|
logger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-WQUVXZWM.js";
|
|
13
13
|
|
|
14
14
|
// src/utils/notify.ts
|
|
15
15
|
import notifier from "node-notifier";
|
|
@@ -93,7 +93,7 @@ var HookService = class {
|
|
|
93
93
|
const startTime = Date.now();
|
|
94
94
|
logger.debug("hooks", `Executing hook: ${hook.name} (${hook.command})`);
|
|
95
95
|
const timeout = hook.timeout || 3e4;
|
|
96
|
-
|
|
96
|
+
exec(hook.command, {
|
|
97
97
|
cwd,
|
|
98
98
|
timeout,
|
|
99
99
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -549,11 +549,29 @@ var WebSocketServiceImpl = class {
|
|
|
549
549
|
logger.info("websocket", `Subscribing to private channel: ${channel}`);
|
|
550
550
|
const privateChannel = this.echo.private(channel);
|
|
551
551
|
writeStatus({ channel });
|
|
552
|
-
privateChannel.listen(".commit.approved", (data) => {
|
|
552
|
+
privateChannel.listen(".commit.approved", async (data) => {
|
|
553
553
|
logger.info("websocket", `Commit approved: ${data.pendingCommit.id}`);
|
|
554
554
|
writeStatus({ lastEvent: "commit.approved", lastEventTime: (/* @__PURE__ */ new Date()).toISOString() });
|
|
555
|
+
let commit = data.pendingCommit;
|
|
556
|
+
if (commit.has_large_files) {
|
|
557
|
+
try {
|
|
558
|
+
logger.info("websocket", `Commit ${commit.id} marked as large, fetching full details...`);
|
|
559
|
+
const { apiService: apiService2 } = await import("./api-GSYJMF33.js");
|
|
560
|
+
const fullCommit = await apiService2.getCommit(commit.id);
|
|
561
|
+
commit = {
|
|
562
|
+
...commit,
|
|
563
|
+
files: fullCommit.files,
|
|
564
|
+
has_large_files: false
|
|
565
|
+
// Clear flag as we now have the files
|
|
566
|
+
};
|
|
567
|
+
logger.success("websocket", `Fetched full details for commit ${commit.id}`);
|
|
568
|
+
} catch (error) {
|
|
569
|
+
logger.error("websocket", `Failed to fetch full commit details for ${commit.id}`, error);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
555
572
|
this.commitApprovedHandlers.forEach(
|
|
556
|
-
(handler) => handler(
|
|
573
|
+
(handler) => handler(commit, data.pendingCommit.project)
|
|
574
|
+
// Use the potentially updated commit object
|
|
557
575
|
);
|
|
558
576
|
}).listen(".commit.pending", (data) => {
|
|
559
577
|
logger.info("websocket", `Commit pending: ${data.pendingCommit.id}`);
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
authService,
|
|
3
3
|
config,
|
|
4
4
|
logger
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WQUVXZWM.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.
|
|
101
|
+
var AGENT_VERSION = "1.2.23";
|
|
102
102
|
var ApiServiceImpl = class {
|
|
103
103
|
sessionId = null;
|
|
104
104
|
circuitBreaker = new CircuitBreaker({
|
|
@@ -272,6 +272,30 @@ var ApiServiceImpl = class {
|
|
|
272
272
|
logger.info("api", `Found ${commits.length} pending commits`);
|
|
273
273
|
return commits;
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Get a specific commit by ID
|
|
277
|
+
* @param commitId - Commit ID
|
|
278
|
+
* @returns The pending commit details
|
|
279
|
+
*/
|
|
280
|
+
async getCommit(commitId) {
|
|
281
|
+
logger.info("api", `Fetching commit ${commitId}`);
|
|
282
|
+
const response = await this.request(`/api/agent/commits/${commitId}`);
|
|
283
|
+
const item = response.data;
|
|
284
|
+
const projectId = item.project_id || item.projectId;
|
|
285
|
+
const createdAt = item.created_at || item.createdAt;
|
|
286
|
+
const hasLargeFiles = item.has_large_files || item.hasLargeFiles;
|
|
287
|
+
if (!projectId || !createdAt) {
|
|
288
|
+
throw new Error(`Invalid commit data received from API: ${JSON.stringify(item)}`);
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
id: item.id,
|
|
292
|
+
projectId,
|
|
293
|
+
message: item.message,
|
|
294
|
+
files: item.files,
|
|
295
|
+
has_large_files: hasLargeFiles,
|
|
296
|
+
createdAt
|
|
297
|
+
};
|
|
298
|
+
}
|
|
275
299
|
/**
|
|
276
300
|
* Mark a commit as successfully executed
|
|
277
301
|
* @param commitId - Commit ID
|
|
@@ -330,8 +354,9 @@ var ApiServiceImpl = class {
|
|
|
330
354
|
* Sync project repository information with the API
|
|
331
355
|
* @param projectId - Project ID
|
|
332
356
|
* @param data - Repository information (path, remote URL, branches)
|
|
357
|
+
* @param changedFiles - Optional array of changed files for commit file selection
|
|
333
358
|
*/
|
|
334
|
-
async syncProject(projectId, data) {
|
|
359
|
+
async syncProject(projectId, data, changedFiles) {
|
|
335
360
|
logger.info("api", `Syncing project ${projectId}`);
|
|
336
361
|
await this.withRetry(async () => {
|
|
337
362
|
const payload = {
|
|
@@ -341,11 +366,14 @@ var ApiServiceImpl = class {
|
|
|
341
366
|
current_branch: data.currentBranch,
|
|
342
367
|
branches: data.branches
|
|
343
368
|
};
|
|
369
|
+
if (changedFiles && changedFiles.length > 0) {
|
|
370
|
+
payload.changed_files = changedFiles;
|
|
371
|
+
}
|
|
344
372
|
await this.request(`/api/agent/projects/${projectId}/sync`, {
|
|
345
373
|
method: "POST",
|
|
346
374
|
body: JSON.stringify(payload)
|
|
347
375
|
});
|
|
348
|
-
logger.success("api", `Project ${projectId} synced`);
|
|
376
|
+
logger.success("api", `Project ${projectId} synced (${changedFiles?.length ?? 0} changed files)`);
|
|
349
377
|
}, "Sync project");
|
|
350
378
|
}
|
|
351
379
|
/**
|
|
@@ -308,7 +308,7 @@ var AuthServiceImpl = class {
|
|
|
308
308
|
return null;
|
|
309
309
|
}
|
|
310
310
|
try {
|
|
311
|
-
const { apiService } = await import("./api-
|
|
311
|
+
const { apiService } = await import("./api-GSYJMF33.js");
|
|
312
312
|
const user = await apiService.getCurrentUser();
|
|
313
313
|
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
314
314
|
return user;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
config,
|
|
3
3
|
logger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WQUVXZWM.js";
|
|
5
5
|
|
|
6
6
|
// src/services/git.ts
|
|
7
7
|
import simpleGit from "simple-git";
|
|
@@ -150,6 +150,73 @@ var GitServiceImpl = class {
|
|
|
150
150
|
throw new Error(`Failed to get git status: ${error.message}`);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Get detailed information about all changed files in the working directory
|
|
155
|
+
* Used for file selection during commit generation in the web app
|
|
156
|
+
* @param path - Repository path
|
|
157
|
+
* @returns Array of changed files with their status and staging state
|
|
158
|
+
*/
|
|
159
|
+
async getChangedFiles(path3) {
|
|
160
|
+
try {
|
|
161
|
+
const git = this.getGit(path3);
|
|
162
|
+
const status = await git.status();
|
|
163
|
+
const changedFiles = [];
|
|
164
|
+
for (const file of status.staged) {
|
|
165
|
+
let fileStatus = "modified";
|
|
166
|
+
if (status.created.includes(file)) {
|
|
167
|
+
fileStatus = "added";
|
|
168
|
+
} else if (status.deleted.includes(file)) {
|
|
169
|
+
fileStatus = "deleted";
|
|
170
|
+
} else if (status.renamed.some((r) => r.to === file)) {
|
|
171
|
+
fileStatus = "renamed";
|
|
172
|
+
}
|
|
173
|
+
changedFiles.push({
|
|
174
|
+
path: file,
|
|
175
|
+
status: fileStatus,
|
|
176
|
+
staged: true
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
for (const file of status.modified) {
|
|
180
|
+
if (!status.staged.includes(file)) {
|
|
181
|
+
changedFiles.push({
|
|
182
|
+
path: file,
|
|
183
|
+
status: "modified",
|
|
184
|
+
staged: false
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const file of status.deleted) {
|
|
189
|
+
if (!status.staged.includes(file)) {
|
|
190
|
+
changedFiles.push({
|
|
191
|
+
path: file,
|
|
192
|
+
status: "deleted",
|
|
193
|
+
staged: false
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const file of status.not_added) {
|
|
198
|
+
changedFiles.push({
|
|
199
|
+
path: file,
|
|
200
|
+
status: "untracked",
|
|
201
|
+
staged: false
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
for (const renamed of status.renamed) {
|
|
205
|
+
if (!status.staged.includes(renamed.to)) {
|
|
206
|
+
changedFiles.push({
|
|
207
|
+
path: renamed.to,
|
|
208
|
+
status: "renamed",
|
|
209
|
+
staged: false
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
logger.info("git", `Found ${changedFiles.length} changed files in ${path3}`);
|
|
214
|
+
return changedFiles;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.error("git", `Failed to get changed files in ${path3}`, error);
|
|
217
|
+
throw new Error(`Failed to get changed files: ${error.message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
153
220
|
/**
|
|
154
221
|
* Push commits to remote repository
|
|
155
222
|
* @param path - Repository path
|
package/dist/daemon/runner.js
CHANGED
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
commitQueue,
|
|
4
4
|
notify,
|
|
5
5
|
websocketService
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-BPDL5KWL.js";
|
|
7
7
|
import {
|
|
8
8
|
apiService
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-LTVAFGVL.js";
|
|
10
10
|
import {
|
|
11
11
|
gitService,
|
|
12
12
|
projectService,
|
|
13
13
|
removePidFile,
|
|
14
14
|
writePidFile
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-ZUQTJV35.js";
|
|
16
16
|
import {
|
|
17
17
|
authService,
|
|
18
18
|
logger
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-WQUVXZWM.js";
|
|
20
20
|
|
|
21
21
|
// src/daemon/runner.ts
|
|
22
22
|
import "dotenv/config";
|
|
@@ -161,7 +161,8 @@ var FileWatcher = class {
|
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
const repoInfo = await gitService.getRepoInfo(projectPath);
|
|
164
|
-
await
|
|
164
|
+
const changedFiles = await gitService.getChangedFiles(projectPath);
|
|
165
|
+
await apiService.syncProject(projectId, repoInfo, changedFiles);
|
|
165
166
|
logger.success("watcher", `Synced project ${projectId}`);
|
|
166
167
|
let projectName = projectId;
|
|
167
168
|
try {
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commitQueue,
|
|
4
4
|
websocketService
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-BPDL5KWL.js";
|
|
6
6
|
import {
|
|
7
7
|
apiService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LTVAFGVL.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-
|
|
17
|
+
} from "./chunk-ZUQTJV35.js";
|
|
18
18
|
import {
|
|
19
19
|
__commonJS,
|
|
20
20
|
__toESM,
|
|
21
21
|
authService,
|
|
22
22
|
config,
|
|
23
23
|
logger
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WQUVXZWM.js";
|
|
25
25
|
|
|
26
26
|
// node_modules/semver/internal/constants.js
|
|
27
27
|
var require_constants = __commonJS({
|
|
@@ -1956,7 +1956,7 @@ var require_semver2 = __commonJS({
|
|
|
1956
1956
|
// src/index.ts
|
|
1957
1957
|
import "dotenv/config";
|
|
1958
1958
|
import { Command } from "commander";
|
|
1959
|
-
import
|
|
1959
|
+
import chalk15 from "chalk";
|
|
1960
1960
|
|
|
1961
1961
|
// src/commands/login.ts
|
|
1962
1962
|
import open from "open";
|
|
@@ -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-
|
|
2555
|
+
const { StatusDashboard } = await import("./StatusDashboard-NDXVTKKO.js");
|
|
2556
2556
|
render(createElement(StatusDashboard, { cwd }));
|
|
2557
2557
|
return;
|
|
2558
2558
|
} catch (error) {
|
|
@@ -2660,10 +2660,12 @@ function registerSyncCommand(program2) {
|
|
|
2660
2660
|
spinner.text = `Found ${totalFiles} files to analyze...`;
|
|
2661
2661
|
spinner.text = "Getting branch information...";
|
|
2662
2662
|
const repoInfo = await gitService.getRepoInfo(cwd);
|
|
2663
|
+
spinner.text = "Getting changed files...";
|
|
2664
|
+
const changedFiles = await gitService.getChangedFiles(cwd);
|
|
2663
2665
|
spinner.text = "Preparing sync payload...";
|
|
2664
2666
|
const syncSpinner = ora7("Connecting to server...").start();
|
|
2665
2667
|
try {
|
|
2666
|
-
await apiService.syncProject(linkedProject.projectId, repoInfo);
|
|
2668
|
+
await apiService.syncProject(linkedProject.projectId, repoInfo, changedFiles);
|
|
2667
2669
|
syncSpinner.succeed("Server sync completed");
|
|
2668
2670
|
} catch (error) {
|
|
2669
2671
|
syncSpinner.fail("Server sync failed");
|
|
@@ -2672,6 +2674,7 @@ function registerSyncCommand(program2) {
|
|
|
2672
2674
|
console.log(chalk7.green("\n\u2713 Repository sync completed"));
|
|
2673
2675
|
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
2674
2676
|
console.log(`${chalk7.bold("Files:")} ${totalFiles} total (${status.staged.length} staged, ${status.unstaged.length} modified, ${status.untracked.length} untracked)`);
|
|
2677
|
+
console.log(`${chalk7.bold("Changed:")} ${changedFiles.length} files synced for commit selection`);
|
|
2675
2678
|
console.log(`${chalk7.bold("Project ID:")} ${linkedProject.projectId}`);
|
|
2676
2679
|
console.log(`${chalk7.bold("Branch:")} ${repoInfo.currentBranch}`);
|
|
2677
2680
|
console.log(`${chalk7.bold("Commit:")} ${repoInfo.lastCommitSha.substring(0, 7)} - ${repoInfo.lastCommitMessage}`);
|
|
@@ -4150,20 +4153,222 @@ function registerDoctorCommand(program2) {
|
|
|
4150
4153
|
});
|
|
4151
4154
|
}
|
|
4152
4155
|
|
|
4156
|
+
// src/commands/config.ts
|
|
4157
|
+
import chalk14 from "chalk";
|
|
4158
|
+
import path6 from "path";
|
|
4159
|
+
import os6 from "os";
|
|
4160
|
+
var ALLOWED_KEYS = [
|
|
4161
|
+
"apiUrl",
|
|
4162
|
+
"wsUrl",
|
|
4163
|
+
"reverbAppKey",
|
|
4164
|
+
"notifications.enabled"
|
|
4165
|
+
];
|
|
4166
|
+
var SENSITIVE_KEYS = ["token"];
|
|
4167
|
+
function isAllowedKey(key) {
|
|
4168
|
+
return ALLOWED_KEYS.includes(key);
|
|
4169
|
+
}
|
|
4170
|
+
function isSensitiveKey(key) {
|
|
4171
|
+
return SENSITIVE_KEYS.includes(key);
|
|
4172
|
+
}
|
|
4173
|
+
function getNestedValue(obj, path7) {
|
|
4174
|
+
const parts = path7.split(".");
|
|
4175
|
+
let current = obj;
|
|
4176
|
+
for (const part of parts) {
|
|
4177
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
4178
|
+
return void 0;
|
|
4179
|
+
}
|
|
4180
|
+
current = current[part];
|
|
4181
|
+
}
|
|
4182
|
+
return current;
|
|
4183
|
+
}
|
|
4184
|
+
function setNestedValue(obj, path7, value) {
|
|
4185
|
+
const parts = path7.split(".");
|
|
4186
|
+
let current = obj;
|
|
4187
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
4188
|
+
const part = parts[i];
|
|
4189
|
+
if (!(part in current) || typeof current[part] !== "object") {
|
|
4190
|
+
current[part] = {};
|
|
4191
|
+
}
|
|
4192
|
+
current = current[part];
|
|
4193
|
+
}
|
|
4194
|
+
current[parts[parts.length - 1]] = value;
|
|
4195
|
+
}
|
|
4196
|
+
function formatValue(value) {
|
|
4197
|
+
if (value === void 0 || value === null) {
|
|
4198
|
+
return chalk14.gray("(not set)");
|
|
4199
|
+
}
|
|
4200
|
+
if (typeof value === "boolean") {
|
|
4201
|
+
return value ? chalk14.green("true") : chalk14.red("false");
|
|
4202
|
+
}
|
|
4203
|
+
if (typeof value === "object") {
|
|
4204
|
+
return chalk14.cyan(JSON.stringify(value));
|
|
4205
|
+
}
|
|
4206
|
+
return chalk14.white(String(value));
|
|
4207
|
+
}
|
|
4208
|
+
function getConfigPath() {
|
|
4209
|
+
const configDir = path6.join(os6.homedir(), ".config", "stint");
|
|
4210
|
+
return path6.join(configDir, "config.json");
|
|
4211
|
+
}
|
|
4212
|
+
function registerConfigCommand(program2) {
|
|
4213
|
+
const configCmd = program2.command("config").description("Manage agent configuration");
|
|
4214
|
+
configCmd.command("get <key>").description("Get a configuration value").action((key) => {
|
|
4215
|
+
try {
|
|
4216
|
+
if (isSensitiveKey(key)) {
|
|
4217
|
+
console.log(chalk14.yellow(`
|
|
4218
|
+
\u26A0 Cannot read sensitive key "${key}"`));
|
|
4219
|
+
console.log(chalk14.gray('Use "stint whoami" to check authentication status.\n'));
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
const allConfig = config.getAll();
|
|
4223
|
+
const value = getNestedValue(allConfig, key);
|
|
4224
|
+
if (value === void 0) {
|
|
4225
|
+
console.log(chalk14.yellow(`
|
|
4226
|
+
\u26A0 Key "${key}" is not set.
|
|
4227
|
+
`));
|
|
4228
|
+
console.log(chalk14.gray("Available keys:"));
|
|
4229
|
+
ALLOWED_KEYS.forEach((k) => console.log(chalk14.gray(` - ${k}`)));
|
|
4230
|
+
console.log();
|
|
4231
|
+
} else {
|
|
4232
|
+
console.log(`
|
|
4233
|
+
${chalk14.bold(key)}: ${formatValue(value)}
|
|
4234
|
+
`);
|
|
4235
|
+
}
|
|
4236
|
+
logger.debug("config", `Get config key: ${key}`);
|
|
4237
|
+
} catch (error) {
|
|
4238
|
+
logger.error("config", "Failed to get config", error);
|
|
4239
|
+
console.error(chalk14.red(`
|
|
4240
|
+
\u2716 Error: ${error.message}
|
|
4241
|
+
`));
|
|
4242
|
+
}
|
|
4243
|
+
});
|
|
4244
|
+
configCmd.command("set <key> <value>").description("Set a configuration value").action((key, value) => {
|
|
4245
|
+
try {
|
|
4246
|
+
if (isSensitiveKey(key)) {
|
|
4247
|
+
console.log(chalk14.red(`
|
|
4248
|
+
\u2716 Cannot set sensitive key "${key}"`));
|
|
4249
|
+
console.log(chalk14.gray('Use "stint login" to set authentication.\n'));
|
|
4250
|
+
return;
|
|
4251
|
+
}
|
|
4252
|
+
if (!isAllowedKey(key)) {
|
|
4253
|
+
console.log(chalk14.yellow(`
|
|
4254
|
+
\u26A0 Unknown key "${key}"`));
|
|
4255
|
+
console.log(chalk14.gray("Allowed keys:"));
|
|
4256
|
+
ALLOWED_KEYS.forEach((k) => console.log(chalk14.gray(` - ${k}`)));
|
|
4257
|
+
console.log();
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
4260
|
+
let parsedValue = value;
|
|
4261
|
+
if (value.toLowerCase() === "true") {
|
|
4262
|
+
parsedValue = true;
|
|
4263
|
+
} else if (value.toLowerCase() === "false") {
|
|
4264
|
+
parsedValue = false;
|
|
4265
|
+
}
|
|
4266
|
+
if (key.includes(".")) {
|
|
4267
|
+
const allConfig = config.getAll();
|
|
4268
|
+
setNestedValue(allConfig, key, parsedValue);
|
|
4269
|
+
const topKey = key.split(".")[0];
|
|
4270
|
+
config.set(topKey, allConfig[topKey]);
|
|
4271
|
+
} else {
|
|
4272
|
+
config.set(key, parsedValue);
|
|
4273
|
+
}
|
|
4274
|
+
console.log(chalk14.green(`
|
|
4275
|
+
\u2713 Set ${key} = ${formatValue(parsedValue)}
|
|
4276
|
+
`));
|
|
4277
|
+
logger.success("config", `Set config key: ${key} = ${value}`);
|
|
4278
|
+
} catch (error) {
|
|
4279
|
+
logger.error("config", "Failed to set config", error);
|
|
4280
|
+
console.error(chalk14.red(`
|
|
4281
|
+
\u2716 Error: ${error.message}
|
|
4282
|
+
`));
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4285
|
+
configCmd.command("list").description("List all configuration values").action(() => {
|
|
4286
|
+
try {
|
|
4287
|
+
const allConfig = config.getAll();
|
|
4288
|
+
console.log(chalk14.bold("\n\u{1F4CB} Configuration:\n"));
|
|
4289
|
+
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4290
|
+
for (const key of ALLOWED_KEYS) {
|
|
4291
|
+
const value = getNestedValue(allConfig, key);
|
|
4292
|
+
console.log(`${chalk14.cyan(key.padEnd(25))} ${formatValue(value)}`);
|
|
4293
|
+
}
|
|
4294
|
+
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4295
|
+
console.log(`${chalk14.cyan("token".padEnd(25))} ${chalk14.yellow("[REDACTED]")}`);
|
|
4296
|
+
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4297
|
+
console.log(`${chalk14.gray("machineId".padEnd(25))} ${chalk14.gray(allConfig.machineId)}`);
|
|
4298
|
+
console.log(`${chalk14.gray("machineName".padEnd(25))} ${chalk14.gray(allConfig.machineName)}`);
|
|
4299
|
+
console.log();
|
|
4300
|
+
logger.debug("config", "Listed config");
|
|
4301
|
+
} catch (error) {
|
|
4302
|
+
logger.error("config", "Failed to list config", error);
|
|
4303
|
+
console.error(chalk14.red(`
|
|
4304
|
+
\u2716 Error: ${error.message}
|
|
4305
|
+
`));
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
4308
|
+
configCmd.command("path").description("Show the configuration file path").action(() => {
|
|
4309
|
+
const configPath = getConfigPath();
|
|
4310
|
+
console.log(`
|
|
4311
|
+
${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
|
|
4312
|
+
`);
|
|
4313
|
+
logger.debug("config", `Config path: ${configPath}`);
|
|
4314
|
+
});
|
|
4315
|
+
configCmd.command("reset").description("Reset configuration to defaults (keeps token)").option("--force", "Skip confirmation prompt").action(async (options) => {
|
|
4316
|
+
try {
|
|
4317
|
+
if (!options.force) {
|
|
4318
|
+
console.log(chalk14.yellow("\n\u26A0 This will reset all configuration to defaults."));
|
|
4319
|
+
console.log(chalk14.gray("Your authentication token will be preserved.\n"));
|
|
4320
|
+
const readline = await import("readline");
|
|
4321
|
+
const rl = readline.createInterface({
|
|
4322
|
+
input: process.stdin,
|
|
4323
|
+
output: process.stdout
|
|
4324
|
+
});
|
|
4325
|
+
const answer = await new Promise((resolve) => {
|
|
4326
|
+
rl.question(chalk14.bold("Are you sure? (y/N) "), resolve);
|
|
4327
|
+
});
|
|
4328
|
+
rl.close();
|
|
4329
|
+
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
4330
|
+
console.log(chalk14.gray("\nReset cancelled.\n"));
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
const token = config.getToken();
|
|
4335
|
+
for (const key of ALLOWED_KEYS) {
|
|
4336
|
+
if (key === "notifications.enabled") {
|
|
4337
|
+
config.set("notifications", { enabled: true });
|
|
4338
|
+
} else {
|
|
4339
|
+
if (key === "apiUrl") config.set("apiUrl", "https://stint.codes");
|
|
4340
|
+
if (key === "wsUrl") config.set("wsUrl", "wss://stint.codes/reverb");
|
|
4341
|
+
if (key === "reverbAppKey") config.set("reverbAppKey", "wtn6tu6lirfv6yflujk7");
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
if (token) {
|
|
4345
|
+
config.setToken(token);
|
|
4346
|
+
}
|
|
4347
|
+
console.log(chalk14.green("\n\u2713 Configuration reset to defaults.\n"));
|
|
4348
|
+
logger.success("config", "Reset config to defaults");
|
|
4349
|
+
} catch (error) {
|
|
4350
|
+
logger.error("config", "Failed to reset config", error);
|
|
4351
|
+
console.error(chalk14.red(`
|
|
4352
|
+
\u2716 Error: ${error.message}
|
|
4353
|
+
`));
|
|
4354
|
+
}
|
|
4355
|
+
});
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4153
4358
|
// src/index.ts
|
|
4154
|
-
var AGENT_VERSION = "1.2.
|
|
4359
|
+
var AGENT_VERSION = "1.2.23";
|
|
4155
4360
|
var program = new Command();
|
|
4156
4361
|
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText("after", `
|
|
4157
|
-
${
|
|
4158
|
-
${
|
|
4159
|
-
${
|
|
4160
|
-
${
|
|
4161
|
-
${
|
|
4162
|
-
${
|
|
4163
|
-
${
|
|
4362
|
+
${chalk15.bold("Examples:")}
|
|
4363
|
+
${chalk15.cyan("$")} stint login ${chalk15.gray("# Authenticate with Stint")}
|
|
4364
|
+
${chalk15.cyan("$")} stint install ${chalk15.gray("# Install agent to run on startup")}
|
|
4365
|
+
${chalk15.cyan("$")} stint link ${chalk15.gray("# Link current directory to a project")}
|
|
4366
|
+
${chalk15.cyan("$")} stint daemon start ${chalk15.gray("# Start background daemon")}
|
|
4367
|
+
${chalk15.cyan("$")} stint status ${chalk15.gray("# Check status")}
|
|
4368
|
+
${chalk15.cyan("$")} stint commits ${chalk15.gray("# List pending commits")}
|
|
4164
4369
|
|
|
4165
|
-
${
|
|
4166
|
-
For more information, visit: ${
|
|
4370
|
+
${chalk15.bold("Documentation:")}
|
|
4371
|
+
For more information, visit: ${chalk15.blue("https://stint.codes/docs")}
|
|
4167
4372
|
`);
|
|
4168
4373
|
registerLoginCommand(program);
|
|
4169
4374
|
registerLogoutCommand(program);
|
|
@@ -4178,6 +4383,7 @@ registerInstallCommand(program);
|
|
|
4178
4383
|
registerUninstallCommand(program);
|
|
4179
4384
|
registerUpdateCommand(program);
|
|
4180
4385
|
registerDoctorCommand(program);
|
|
4386
|
+
registerConfigCommand(program);
|
|
4181
4387
|
program.exitOverride();
|
|
4182
4388
|
try {
|
|
4183
4389
|
await program.parseAsync(process.argv);
|
|
@@ -4185,7 +4391,7 @@ try {
|
|
|
4185
4391
|
const commanderError = error;
|
|
4186
4392
|
if (commanderError.code !== "commander.help" && commanderError.code !== "commander.version" && commanderError.code !== "commander.helpDisplayed") {
|
|
4187
4393
|
logger.error("cli", "Command execution failed", error);
|
|
4188
|
-
console.error(
|
|
4394
|
+
console.error(chalk15.red(`
|
|
4189
4395
|
\u2716 Error: ${error.message}
|
|
4190
4396
|
`));
|
|
4191
4397
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,92 +1,98 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@gowelle/stint-agent",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Local agent for Stint - Project Assistant",
|
|
5
|
-
"author": "Gowelle John <gowelle.john@icloud.com>",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"stint": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"assets",
|
|
14
|
-
"README.md",
|
|
15
|
-
"LICENSE"
|
|
16
|
-
],
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/gowelle/stint-agent.git"
|
|
20
|
-
},
|
|
21
|
-
"homepage": "https://github.com/gowelle/stint-agent#readme",
|
|
22
|
-
"bugs": {
|
|
23
|
-
"url": "https://github.com/gowelle/stint-agent/issues"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"stint",
|
|
27
|
-
"agent",
|
|
28
|
-
"project-assistant",
|
|
29
|
-
"cli",
|
|
30
|
-
"git"
|
|
31
|
-
],
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
"pattern": "^\\d+\\.\\d+\\.\\d
|
|
80
|
-
"description": "
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@gowelle/stint-agent",
|
|
3
|
+
"version": "1.2.23",
|
|
4
|
+
"description": "Local agent for Stint - Project Assistant",
|
|
5
|
+
"author": "Gowelle John <gowelle.john@icloud.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"stint": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"assets",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/gowelle/stint-agent.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/gowelle/stint-agent#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/gowelle/stint-agent/issues"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"stint",
|
|
27
|
+
"agent",
|
|
28
|
+
"project-assistant",
|
|
29
|
+
"cli",
|
|
30
|
+
"git"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"dev": "tsup --watch",
|
|
35
|
+
"lint": "eslint src",
|
|
36
|
+
"test": "vitest",
|
|
37
|
+
"test:update": "node --loader ts-node/esm scripts/test-update.ts",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@inquirer/prompts": "^8.1.0",
|
|
42
|
+
"chalk": "^5.3.0",
|
|
43
|
+
"commander": "^12.0.0",
|
|
44
|
+
"conf": "^12.0.0",
|
|
45
|
+
"dotenv": "^17.2.3",
|
|
46
|
+
"ink": "^5.2.1",
|
|
47
|
+
"laravel-echo": "^2.2.6",
|
|
48
|
+
"node-fetch": "^3.3.2",
|
|
49
|
+
"node-notifier": "^10.0.1",
|
|
50
|
+
"open": "^10.0.0",
|
|
51
|
+
"ora": "^8.0.1",
|
|
52
|
+
"prompts": "^2.4.2",
|
|
53
|
+
"pusher-js": "^8.4.0",
|
|
54
|
+
"react": "^18.3.1",
|
|
55
|
+
"simple-git": "^3.22.0",
|
|
56
|
+
"ws": "^8.16.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^20.11.0",
|
|
60
|
+
"@types/node-notifier": "^8.0.5",
|
|
61
|
+
"@types/prompts": "^2.4.9",
|
|
62
|
+
"@types/react": "^18.3.27",
|
|
63
|
+
"@types/ws": "^8.5.10",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
|
65
|
+
"@typescript-eslint/parser": "^8.50.0",
|
|
66
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
67
|
+
"eslint": "^8.56.0",
|
|
68
|
+
"ts-node": "^10.9.2",
|
|
69
|
+
"tsup": "^8.0.1",
|
|
70
|
+
"typescript": "^5.3.3",
|
|
71
|
+
"vitest": "^4.0.16"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20.0.0"
|
|
75
|
+
},
|
|
76
|
+
"stint": {
|
|
77
|
+
"channels": {
|
|
78
|
+
"stable": {
|
|
79
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
80
|
+
"description": "Production-ready releases"
|
|
81
|
+
},
|
|
82
|
+
"beta": {
|
|
83
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+-beta\\.\\d+$",
|
|
84
|
+
"description": "Pre-release versions for testing"
|
|
85
|
+
},
|
|
86
|
+
"nightly": {
|
|
87
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+-nightly\\.\\d{8}$",
|
|
88
|
+
"description": "Nightly builds from main branch"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"defaultChannel": "stable"
|
|
92
|
+
},
|
|
93
|
+
"pnpm": {
|
|
94
|
+
"onlyBuiltDependencies": [
|
|
95
|
+
"esbuild"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|