@gowelle/stint-agent 1.2.35 → 1.2.36
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-VOOOQA7A.js} +104 -24
- package/dist/api-6KY43TBB.js +7 -0
- package/dist/{chunk-MISINEGG.js → chunk-BYBGWAQO.js} +254 -94
- package/dist/{chunk-RC7Z6GTK.js → chunk-E3WNZ2CK.js} +34 -3
- package/dist/{chunk-4655SBXG.js → chunk-KHLFCZRY.js} +45 -12
- package/dist/{chunk-H5GHDNXY.js → chunk-VE7Z43P7.js} +67 -20
- package/dist/daemon/runner.js +113 -51
- package/dist/index.js +714 -316
- package/package.json +1 -1
- package/dist/api-Z3HF3YU7.js +0 -7
package/dist/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commitQueue,
|
|
4
4
|
websocketService
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-BYBGWAQO.js";
|
|
6
6
|
import {
|
|
7
7
|
apiService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VE7Z43P7.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-KHLFCZRY.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-E3WNZ2CK.js";
|
|
25
25
|
|
|
26
26
|
// node_modules/semver/internal/constants.js
|
|
27
27
|
var require_constants = __commonJS({
|
|
@@ -1983,7 +1983,10 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
1983
1983
|
server.on("request", (req, res) => {
|
|
1984
1984
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1985
1985
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
1986
|
-
res.setHeader(
|
|
1986
|
+
res.setHeader(
|
|
1987
|
+
"Access-Control-Allow-Headers",
|
|
1988
|
+
"Content-Type, Authorization, X-XSRF-TOKEN, X-Requested-With, X-Inertia, X-Inertia-Version"
|
|
1989
|
+
);
|
|
1987
1990
|
res.setHeader("Access-Control-Expose-Headers", "X-Inertia-Location");
|
|
1988
1991
|
res.setHeader("Access-Control-Allow-Private-Network", "true");
|
|
1989
1992
|
if (req.method === "OPTIONS") {
|
|
@@ -2006,11 +2009,13 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2006
2009
|
const errorDescription = url.searchParams.get("error_description") || error;
|
|
2007
2010
|
if (isJsonRequest) {
|
|
2008
2011
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2009
|
-
res.end(
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2012
|
+
res.end(
|
|
2013
|
+
JSON.stringify({
|
|
2014
|
+
status: "error",
|
|
2015
|
+
error,
|
|
2016
|
+
message: errorDescription
|
|
2017
|
+
})
|
|
2018
|
+
);
|
|
2014
2019
|
} else {
|
|
2015
2020
|
const escapedError = escapeHtml(errorDescription);
|
|
2016
2021
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
@@ -2031,11 +2036,13 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2031
2036
|
if (state !== expectedState) {
|
|
2032
2037
|
if (isJsonRequest) {
|
|
2033
2038
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2034
|
-
res.end(
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
+
res.end(
|
|
2040
|
+
JSON.stringify({
|
|
2041
|
+
status: "error",
|
|
2042
|
+
error: "invalid_state",
|
|
2043
|
+
message: "State parameter mismatch"
|
|
2044
|
+
})
|
|
2045
|
+
);
|
|
2039
2046
|
} else {
|
|
2040
2047
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
2041
2048
|
res.end(`
|
|
@@ -2055,11 +2062,13 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2055
2062
|
if (!token) {
|
|
2056
2063
|
if (isJsonRequest) {
|
|
2057
2064
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2058
|
-
res.end(
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2065
|
+
res.end(
|
|
2066
|
+
JSON.stringify({
|
|
2067
|
+
status: "error",
|
|
2068
|
+
error: "missing_token",
|
|
2069
|
+
message: "No token provided"
|
|
2070
|
+
})
|
|
2071
|
+
);
|
|
2063
2072
|
} else {
|
|
2064
2073
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
2065
2074
|
res.end(`
|
|
@@ -2081,14 +2090,16 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2081
2090
|
res.end();
|
|
2082
2091
|
} else if (isJsonRequest) {
|
|
2083
2092
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2084
|
-
res.end(
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2093
|
+
res.end(
|
|
2094
|
+
JSON.stringify({
|
|
2095
|
+
status: "success",
|
|
2096
|
+
message: "Authentication successful",
|
|
2097
|
+
user: "Authenticated",
|
|
2098
|
+
next: next || void 0
|
|
2099
|
+
})
|
|
2100
|
+
);
|
|
2090
2101
|
} else if (next) {
|
|
2091
|
-
res.writeHead(302, {
|
|
2102
|
+
res.writeHead(302, { Location: next });
|
|
2092
2103
|
res.end();
|
|
2093
2104
|
} else {
|
|
2094
2105
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
@@ -2176,10 +2187,12 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2176
2187
|
const isJsonRequest = req.headers.accept?.includes("application/json");
|
|
2177
2188
|
if (isJsonRequest) {
|
|
2178
2189
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2179
|
-
res.end(
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2190
|
+
res.end(
|
|
2191
|
+
JSON.stringify({
|
|
2192
|
+
status: "error",
|
|
2193
|
+
message: "Internal server error processing callback"
|
|
2194
|
+
})
|
|
2195
|
+
);
|
|
2183
2196
|
} else {
|
|
2184
2197
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2185
2198
|
res.end(`
|
|
@@ -2239,7 +2252,11 @@ function startCallbackServer(expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
|
2239
2252
|
const port = address.port;
|
|
2240
2253
|
timeout = setTimeout(() => {
|
|
2241
2254
|
server.close();
|
|
2242
|
-
callbackReject(
|
|
2255
|
+
callbackReject(
|
|
2256
|
+
new Error(
|
|
2257
|
+
"Authentication timeout: No callback received within 5 minutes"
|
|
2258
|
+
)
|
|
2259
|
+
);
|
|
2243
2260
|
}, timeoutMs);
|
|
2244
2261
|
callbackPromise.then(() => clearTimeout(timeout)).catch(() => clearTimeout(timeout)).finally(() => {
|
|
2245
2262
|
setTimeout(() => {
|
|
@@ -2267,11 +2284,17 @@ function registerLoginCommand(program2) {
|
|
|
2267
2284
|
authUrl.searchParams.set("state", state);
|
|
2268
2285
|
authUrl.searchParams.set("machine_id", machineId);
|
|
2269
2286
|
authUrl.searchParams.set("machine_name", machineName);
|
|
2270
|
-
authUrl.searchParams.set(
|
|
2287
|
+
authUrl.searchParams.set(
|
|
2288
|
+
"redirect_uri",
|
|
2289
|
+
`http://localhost:${port}/auth/callback`
|
|
2290
|
+
);
|
|
2271
2291
|
spinner.text = "Opening browser for authentication...";
|
|
2272
2292
|
await open(authUrl.toString());
|
|
2273
2293
|
spinner.text = "Waiting for authentication...";
|
|
2274
|
-
logger.info(
|
|
2294
|
+
logger.info(
|
|
2295
|
+
"login",
|
|
2296
|
+
`Login initiated, callback server listening on port ${port}`
|
|
2297
|
+
);
|
|
2275
2298
|
const token = await callbackPromise;
|
|
2276
2299
|
spinner.text = "Completing authentication...";
|
|
2277
2300
|
await completeLogin(token);
|
|
@@ -2297,8 +2320,12 @@ async function completeLogin(token) {
|
|
|
2297
2320
|
spinner.succeed("Authentication successful!");
|
|
2298
2321
|
console.log(chalk.green(`
|
|
2299
2322
|
\u2713 Logged in as ${chalk.bold(user.email)}`));
|
|
2300
|
-
console.log(
|
|
2301
|
-
|
|
2323
|
+
console.log(
|
|
2324
|
+
chalk.gray(
|
|
2325
|
+
`Machine: ${authService.getMachineName()} (${authService.getMachineId()})
|
|
2326
|
+
`
|
|
2327
|
+
)
|
|
2328
|
+
);
|
|
2302
2329
|
logger.success("login", `Logged in as ${user.email}`);
|
|
2303
2330
|
} catch (error) {
|
|
2304
2331
|
spinner.fail("Authentication failed");
|
|
@@ -2321,11 +2348,18 @@ function registerLogoutCommand(program2) {
|
|
|
2321
2348
|
try {
|
|
2322
2349
|
await apiService.disconnect();
|
|
2323
2350
|
} catch {
|
|
2324
|
-
logger.warn(
|
|
2351
|
+
logger.warn(
|
|
2352
|
+
"logout",
|
|
2353
|
+
"Failed to disconnect session, continuing with logout"
|
|
2354
|
+
);
|
|
2325
2355
|
}
|
|
2326
2356
|
await authService.clearToken();
|
|
2327
2357
|
spinner.succeed("Logged out successfully");
|
|
2328
|
-
console.log(
|
|
2358
|
+
console.log(
|
|
2359
|
+
chalk2.gray(
|
|
2360
|
+
"\nYour credentials have been removed from this machine.\n"
|
|
2361
|
+
)
|
|
2362
|
+
);
|
|
2329
2363
|
logger.success("logout", "User logged out");
|
|
2330
2364
|
} catch (error) {
|
|
2331
2365
|
spinner.fail("Logout failed");
|
|
@@ -2360,30 +2394,40 @@ function registerWhoamiCommand(program2) {
|
|
|
2360
2394
|
const user = await authService.validateToken();
|
|
2361
2395
|
if (!user) {
|
|
2362
2396
|
if (options.json) {
|
|
2363
|
-
console.log(
|
|
2397
|
+
console.log(
|
|
2398
|
+
JSON.stringify({ authenticated: false, error: "Token invalid" })
|
|
2399
|
+
);
|
|
2364
2400
|
await authService.clearToken();
|
|
2365
2401
|
return;
|
|
2366
2402
|
}
|
|
2367
2403
|
if (spinner) spinner.fail("Authentication invalid");
|
|
2368
|
-
console.log(
|
|
2404
|
+
console.log(
|
|
2405
|
+
chalk3.red("\n\u2716 Your authentication token is invalid or expired.")
|
|
2406
|
+
);
|
|
2369
2407
|
console.log(chalk3.gray('Run "stint login" to re-authenticate.\n'));
|
|
2370
2408
|
await authService.clearToken();
|
|
2371
2409
|
return;
|
|
2372
2410
|
}
|
|
2373
2411
|
if (options.json) {
|
|
2374
2412
|
logger.disableConsole();
|
|
2375
|
-
console.log(
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2413
|
+
console.log(
|
|
2414
|
+
JSON.stringify(
|
|
2415
|
+
{
|
|
2416
|
+
authenticated: true,
|
|
2417
|
+
user: {
|
|
2418
|
+
name: user.name,
|
|
2419
|
+
email: user.email,
|
|
2420
|
+
id: user.id
|
|
2421
|
+
},
|
|
2422
|
+
machine: {
|
|
2423
|
+
name: authService.getMachineName(),
|
|
2424
|
+
id: authService.getMachineId()
|
|
2425
|
+
}
|
|
2426
|
+
},
|
|
2427
|
+
null,
|
|
2428
|
+
2
|
|
2429
|
+
)
|
|
2430
|
+
);
|
|
2387
2431
|
return;
|
|
2388
2432
|
}
|
|
2389
2433
|
if (spinner) spinner.succeed("Authenticated");
|
|
@@ -2394,10 +2438,15 @@ function registerWhoamiCommand(program2) {
|
|
|
2394
2438
|
console.log(`${chalk3.bold("User ID:")} ${user.id}`);
|
|
2395
2439
|
console.log(chalk3.blue("\n\u{1F4BB} Machine Information:"));
|
|
2396
2440
|
console.log(chalk3.gray("\u2500".repeat(50)));
|
|
2397
|
-
console.log(
|
|
2441
|
+
console.log(
|
|
2442
|
+
`${chalk3.bold("Name:")} ${authService.getMachineName()}`
|
|
2443
|
+
);
|
|
2398
2444
|
console.log(`${chalk3.bold("ID:")} ${authService.getMachineId()}`);
|
|
2399
2445
|
console.log();
|
|
2400
|
-
logger.info(
|
|
2446
|
+
logger.info(
|
|
2447
|
+
"whoami",
|
|
2448
|
+
`User: ${user.email}, Machine: ${authService.getMachineName()}`
|
|
2449
|
+
);
|
|
2401
2450
|
} catch (error) {
|
|
2402
2451
|
if (spinner) spinner.fail("Failed to retrieve information");
|
|
2403
2452
|
logger.error("whoami", "Command failed", error);
|
|
@@ -2426,9 +2475,17 @@ function registerLinkCommand(program2) {
|
|
|
2426
2475
|
const existingLink = await projectService.getLinkedProject(cwd);
|
|
2427
2476
|
if (existingLink) {
|
|
2428
2477
|
spinner.warn("Directory already linked");
|
|
2429
|
-
console.log(
|
|
2430
|
-
|
|
2431
|
-
|
|
2478
|
+
console.log(
|
|
2479
|
+
chalk4.yellow(
|
|
2480
|
+
`
|
|
2481
|
+
\u26A0 This directory is already linked to project ${existingLink.projectId}`
|
|
2482
|
+
)
|
|
2483
|
+
);
|
|
2484
|
+
console.log(
|
|
2485
|
+
chalk4.gray(
|
|
2486
|
+
'Run "stint unlink" first if you want to link to a different project.\n'
|
|
2487
|
+
)
|
|
2488
|
+
);
|
|
2432
2489
|
return;
|
|
2433
2490
|
}
|
|
2434
2491
|
spinner.text = "Verifying git repository...";
|
|
@@ -2436,15 +2493,21 @@ function registerLinkCommand(program2) {
|
|
|
2436
2493
|
if (!isRepo) {
|
|
2437
2494
|
spinner.fail("Not a git repository");
|
|
2438
2495
|
console.log(chalk4.red("\n\u2716 This directory is not a git repository."));
|
|
2439
|
-
console.log(
|
|
2496
|
+
console.log(
|
|
2497
|
+
chalk4.gray("Please run this command in a git repository.\n")
|
|
2498
|
+
);
|
|
2440
2499
|
process2.exit(1);
|
|
2441
2500
|
}
|
|
2442
2501
|
spinner.text = "Fetching projects...";
|
|
2443
2502
|
const projects = await apiService.getLinkedProjects();
|
|
2444
2503
|
if (projects.length === 0) {
|
|
2445
2504
|
spinner.info("No projects available");
|
|
2446
|
-
console.log(
|
|
2447
|
-
|
|
2505
|
+
console.log(
|
|
2506
|
+
chalk4.yellow("\n\u26A0 No projects found in your Stint account.")
|
|
2507
|
+
);
|
|
2508
|
+
console.log(
|
|
2509
|
+
chalk4.gray("Create a project at https://stint.codes first.\n")
|
|
2510
|
+
);
|
|
2448
2511
|
return;
|
|
2449
2512
|
}
|
|
2450
2513
|
spinner.succeed("Ready to link");
|
|
@@ -2481,7 +2544,10 @@ function registerLinkCommand(program2) {
|
|
|
2481
2544
|
try {
|
|
2482
2545
|
repoInfo = await gitService.getRepoInfo(cwd);
|
|
2483
2546
|
} catch (e) {
|
|
2484
|
-
logger.warn(
|
|
2547
|
+
logger.warn(
|
|
2548
|
+
"link",
|
|
2549
|
+
`Failed to get repo info for creation metadata: ${e.message}`
|
|
2550
|
+
);
|
|
2485
2551
|
}
|
|
2486
2552
|
}
|
|
2487
2553
|
const newProject = await apiService.createProject({
|
|
@@ -2502,8 +2568,12 @@ function registerLinkCommand(program2) {
|
|
|
2502
2568
|
const linkSpinner = ora4("Linking project...").start();
|
|
2503
2569
|
await projectService.linkProject(cwd, selectedProjectId);
|
|
2504
2570
|
linkSpinner.succeed("Project linked successfully!");
|
|
2505
|
-
console.log(
|
|
2506
|
-
|
|
2571
|
+
console.log(
|
|
2572
|
+
chalk4.green(
|
|
2573
|
+
`
|
|
2574
|
+
\u2713 Linked to ${chalk4.bold(selectedProject?.name || selectedProjectId)}`
|
|
2575
|
+
)
|
|
2576
|
+
);
|
|
2507
2577
|
console.log(chalk4.gray(`Directory: ${cwd}`));
|
|
2508
2578
|
console.log(chalk4.gray(`Project ID: ${selectedProjectId}
|
|
2509
2579
|
`));
|
|
@@ -2532,14 +2602,18 @@ function registerUnlinkCommand(program2) {
|
|
|
2532
2602
|
const linkedProject = await projectService.getLinkedProject(cwd);
|
|
2533
2603
|
if (!linkedProject) {
|
|
2534
2604
|
spinner.info("Not linked");
|
|
2535
|
-
console.log(
|
|
2605
|
+
console.log(
|
|
2606
|
+
chalk5.yellow("\n\u26A0 This directory is not linked to any project.\n")
|
|
2607
|
+
);
|
|
2536
2608
|
return;
|
|
2537
2609
|
}
|
|
2538
2610
|
spinner.stop();
|
|
2539
2611
|
console.log(chalk5.blue("\n\u{1F4CB} Current Link:"));
|
|
2540
2612
|
console.log(chalk5.gray("\u2500".repeat(50)));
|
|
2541
2613
|
console.log(`${chalk5.bold("Project ID:")} ${linkedProject.projectId}`);
|
|
2542
|
-
console.log(
|
|
2614
|
+
console.log(
|
|
2615
|
+
`${chalk5.bold("Linked At:")} ${new Date(linkedProject.linkedAt).toLocaleString()}`
|
|
2616
|
+
);
|
|
2543
2617
|
console.log();
|
|
2544
2618
|
if (!options.force) {
|
|
2545
2619
|
const shouldUnlink = await confirm({
|
|
@@ -2583,11 +2657,13 @@ function registerStatusCommand(program2) {
|
|
|
2583
2657
|
try {
|
|
2584
2658
|
const { render } = await import("ink");
|
|
2585
2659
|
const { createElement } = await import("react");
|
|
2586
|
-
const { StatusDashboard } = await import("./StatusDashboard-
|
|
2660
|
+
const { StatusDashboard } = await import("./StatusDashboard-VOOOQA7A.js");
|
|
2587
2661
|
render(createElement(StatusDashboard, { cwd }));
|
|
2588
2662
|
return;
|
|
2589
2663
|
} catch (error) {
|
|
2590
|
-
console.error(
|
|
2664
|
+
console.error(
|
|
2665
|
+
chalk6.red(`Failed to start dashboard: ${error.message}`)
|
|
2666
|
+
);
|
|
2591
2667
|
process4.exit(1);
|
|
2592
2668
|
}
|
|
2593
2669
|
}
|
|
@@ -2641,7 +2717,13 @@ function registerStatusCommand(program2) {
|
|
|
2641
2717
|
daemon: {
|
|
2642
2718
|
running: valid && !!pid,
|
|
2643
2719
|
pid: pid || null,
|
|
2644
|
-
logFile: path2.join(
|
|
2720
|
+
logFile: path2.join(
|
|
2721
|
+
os.homedir(),
|
|
2722
|
+
".config",
|
|
2723
|
+
"stint",
|
|
2724
|
+
"logs",
|
|
2725
|
+
"daemon.log"
|
|
2726
|
+
)
|
|
2645
2727
|
}
|
|
2646
2728
|
};
|
|
2647
2729
|
console.log(JSON.stringify(statusOutput, null, 2));
|
|
@@ -2651,34 +2733,67 @@ function registerStatusCommand(program2) {
|
|
|
2651
2733
|
console.log(chalk6.blue("\n\u{1F4E6} Project Status:"));
|
|
2652
2734
|
console.log(chalk6.gray("\u2500".repeat(50)));
|
|
2653
2735
|
if (linkedProject) {
|
|
2654
|
-
console.log(
|
|
2655
|
-
|
|
2656
|
-
|
|
2736
|
+
console.log(
|
|
2737
|
+
`${chalk6.bold("Status:")} ${chalk6.green("\u2713 Linked")}`
|
|
2738
|
+
);
|
|
2739
|
+
console.log(
|
|
2740
|
+
`${chalk6.bold("Project ID:")} ${linkedProject.projectId}`
|
|
2741
|
+
);
|
|
2742
|
+
console.log(
|
|
2743
|
+
`${chalk6.bold("Linked At:")} ${new Date(linkedProject.linkedAt).toLocaleString()}`
|
|
2744
|
+
);
|
|
2657
2745
|
} else {
|
|
2658
|
-
console.log(
|
|
2659
|
-
|
|
2746
|
+
console.log(
|
|
2747
|
+
`${chalk6.bold("Status:")} ${chalk6.yellow("Not linked")}`
|
|
2748
|
+
);
|
|
2749
|
+
console.log(
|
|
2750
|
+
chalk6.gray('Run "stint link" to link this directory to a project.')
|
|
2751
|
+
);
|
|
2660
2752
|
}
|
|
2661
2753
|
console.log(chalk6.blue("\n\u{1F4C2} Git Repository:"));
|
|
2662
2754
|
console.log(chalk6.gray("\u2500".repeat(50)));
|
|
2663
2755
|
if (isRepo) {
|
|
2664
2756
|
try {
|
|
2665
2757
|
if (repoInfo) {
|
|
2666
|
-
console.log(
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
console.log(
|
|
2758
|
+
console.log(
|
|
2759
|
+
`${chalk6.bold("Branch:")} ${chalk6.cyan(repoInfo.currentBranch)}`
|
|
2760
|
+
);
|
|
2761
|
+
console.log(
|
|
2762
|
+
`${chalk6.bold("Remote:")} ${repoInfo.remoteUrl || chalk6.gray("None")}`
|
|
2763
|
+
);
|
|
2764
|
+
console.log(
|
|
2765
|
+
`${chalk6.bold("Last Commit:")} ${repoInfo.lastCommitSha.substring(0, 7)} - ${repoInfo.lastCommitMessage}`
|
|
2766
|
+
);
|
|
2767
|
+
console.log(
|
|
2768
|
+
`${chalk6.bold("Commit Date:")} ${new Date(repoInfo.lastCommitDate).toLocaleString()}`
|
|
2769
|
+
);
|
|
2670
2770
|
const { staged, unstaged, untracked, ahead, behind } = repoInfo.status;
|
|
2671
2771
|
const totalChanges = staged.length + unstaged.length + untracked.length;
|
|
2672
2772
|
if (totalChanges > 0) {
|
|
2673
|
-
console.log(
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
if (
|
|
2773
|
+
console.log(
|
|
2774
|
+
`${chalk6.bold("Changes:")} ${chalk6.yellow(`${totalChanges} file(s)`)}`
|
|
2775
|
+
);
|
|
2776
|
+
if (staged.length > 0)
|
|
2777
|
+
console.log(
|
|
2778
|
+
` ${chalk6.green("Staged:")} ${staged.length}`
|
|
2779
|
+
);
|
|
2780
|
+
if (unstaged.length > 0)
|
|
2781
|
+
console.log(
|
|
2782
|
+
` ${chalk6.yellow("Unstaged:")} ${unstaged.length}`
|
|
2783
|
+
);
|
|
2784
|
+
if (untracked.length > 0)
|
|
2785
|
+
console.log(
|
|
2786
|
+
` ${chalk6.gray("Untracked:")} ${untracked.length}`
|
|
2787
|
+
);
|
|
2677
2788
|
} else {
|
|
2678
|
-
console.log(
|
|
2789
|
+
console.log(
|
|
2790
|
+
`${chalk6.bold("Changes:")} ${chalk6.green("Clean working tree")}`
|
|
2791
|
+
);
|
|
2679
2792
|
}
|
|
2680
2793
|
if (ahead > 0 || behind > 0) {
|
|
2681
|
-
console.log(
|
|
2794
|
+
console.log(
|
|
2795
|
+
`${chalk6.bold("Sync Status:")} ${ahead > 0 ? chalk6.yellow(`\u2191${ahead}`) : ""} ${behind > 0 ? chalk6.yellow(`\u2193${behind}`) : ""}`
|
|
2796
|
+
);
|
|
2682
2797
|
}
|
|
2683
2798
|
} else {
|
|
2684
2799
|
console.log(chalk6.red("Error reading repository information"));
|
|
@@ -2692,22 +2807,40 @@ function registerStatusCommand(program2) {
|
|
|
2692
2807
|
console.log(chalk6.blue("\n\u{1F510} Authentication:"));
|
|
2693
2808
|
console.log(chalk6.gray("\u2500".repeat(50)));
|
|
2694
2809
|
if (user) {
|
|
2695
|
-
console.log(
|
|
2696
|
-
|
|
2697
|
-
|
|
2810
|
+
console.log(
|
|
2811
|
+
`${chalk6.bold("Status:")} ${chalk6.green("\u2713 Authenticated")}`
|
|
2812
|
+
);
|
|
2813
|
+
console.log(
|
|
2814
|
+
`${chalk6.bold("User:")} ${user.name} (${user.email})`
|
|
2815
|
+
);
|
|
2816
|
+
console.log(
|
|
2817
|
+
`${chalk6.bold("Machine:")} ${authService.getMachineName()}`
|
|
2818
|
+
);
|
|
2698
2819
|
} else {
|
|
2699
|
-
console.log(
|
|
2820
|
+
console.log(
|
|
2821
|
+
`${chalk6.bold("Status:")} ${chalk6.yellow("Not logged in")}`
|
|
2822
|
+
);
|
|
2700
2823
|
console.log(chalk6.gray('Run "stint login" to authenticate.'));
|
|
2701
2824
|
}
|
|
2702
2825
|
console.log(chalk6.blue("\n\u2699\uFE0F Daemon:"));
|
|
2703
2826
|
console.log(chalk6.gray("\u2500".repeat(50)));
|
|
2704
2827
|
if (valid && pid) {
|
|
2705
|
-
console.log(
|
|
2828
|
+
console.log(
|
|
2829
|
+
`${chalk6.bold("Status:")} ${chalk6.green("\u2713 Running")}`
|
|
2830
|
+
);
|
|
2706
2831
|
console.log(`${chalk6.bold("PID:")} ${pid}`);
|
|
2707
|
-
console.log(
|
|
2832
|
+
console.log(
|
|
2833
|
+
`${chalk6.bold("Logs:")} ${path2.join(os.homedir(), ".config", "stint", "logs", "daemon.log")}`
|
|
2834
|
+
);
|
|
2708
2835
|
} else {
|
|
2709
|
-
console.log(
|
|
2710
|
-
|
|
2836
|
+
console.log(
|
|
2837
|
+
`${chalk6.bold("Status:")} ${chalk6.yellow("Not running")}`
|
|
2838
|
+
);
|
|
2839
|
+
console.log(
|
|
2840
|
+
chalk6.gray(
|
|
2841
|
+
'Run "stint daemon start" to start the background agent.'
|
|
2842
|
+
)
|
|
2843
|
+
);
|
|
2711
2844
|
}
|
|
2712
2845
|
console.log();
|
|
2713
2846
|
logger.info("status", "Status command executed");
|
|
@@ -2734,8 +2867,12 @@ function registerSyncCommand(program2) {
|
|
|
2734
2867
|
const linkedProject = await projectService.getLinkedProject(cwd);
|
|
2735
2868
|
if (!linkedProject) {
|
|
2736
2869
|
spinner.fail("Not linked");
|
|
2737
|
-
console.log(
|
|
2738
|
-
|
|
2870
|
+
console.log(
|
|
2871
|
+
chalk7.yellow("\n\u26A0 This directory is not linked to any project.")
|
|
2872
|
+
);
|
|
2873
|
+
console.log(
|
|
2874
|
+
chalk7.gray('Run "stint link" first to link this directory.\n')
|
|
2875
|
+
);
|
|
2739
2876
|
process5.exit(1);
|
|
2740
2877
|
}
|
|
2741
2878
|
spinner.text = "Analyzing repository...";
|
|
@@ -2749,7 +2886,11 @@ function registerSyncCommand(program2) {
|
|
|
2749
2886
|
spinner.text = "Preparing sync payload...";
|
|
2750
2887
|
const syncSpinner = ora7("Connecting to server...").start();
|
|
2751
2888
|
try {
|
|
2752
|
-
await apiService.syncProject(
|
|
2889
|
+
await apiService.syncProject(
|
|
2890
|
+
linkedProject.projectId,
|
|
2891
|
+
repoInfo,
|
|
2892
|
+
changedFiles
|
|
2893
|
+
);
|
|
2753
2894
|
syncSpinner.succeed("Server sync completed");
|
|
2754
2895
|
} catch (error) {
|
|
2755
2896
|
syncSpinner.fail("Server sync failed");
|
|
@@ -2757,14 +2898,25 @@ function registerSyncCommand(program2) {
|
|
|
2757
2898
|
}
|
|
2758
2899
|
console.log(chalk7.green("\n\u2713 Repository sync completed"));
|
|
2759
2900
|
console.log(chalk7.gray("\u2500".repeat(50)));
|
|
2760
|
-
console.log(
|
|
2761
|
-
|
|
2901
|
+
console.log(
|
|
2902
|
+
`${chalk7.bold("Files:")} ${totalFiles} total (${status.staged.length} staged, ${status.unstaged.length} modified, ${status.untracked.length} untracked)`
|
|
2903
|
+
);
|
|
2904
|
+
console.log(
|
|
2905
|
+
`${chalk7.bold("Changed:")} ${changedFiles.length} files synced for commit selection`
|
|
2906
|
+
);
|
|
2762
2907
|
console.log(`${chalk7.bold("Project ID:")} ${linkedProject.projectId}`);
|
|
2763
2908
|
console.log(`${chalk7.bold("Branch:")} ${repoInfo.currentBranch}`);
|
|
2764
|
-
console.log(
|
|
2765
|
-
|
|
2909
|
+
console.log(
|
|
2910
|
+
`${chalk7.bold("Commit:")} ${repoInfo.lastCommitSha.substring(0, 7)} - ${repoInfo.lastCommitMessage}`
|
|
2911
|
+
);
|
|
2912
|
+
console.log(
|
|
2913
|
+
`${chalk7.bold("Remote:")} ${repoInfo.remoteUrl || chalk7.gray("None")}`
|
|
2914
|
+
);
|
|
2766
2915
|
console.log();
|
|
2767
|
-
logger.success(
|
|
2916
|
+
logger.success(
|
|
2917
|
+
"sync",
|
|
2918
|
+
`Synced ${cwd} to project ${linkedProject.projectId}`
|
|
2919
|
+
);
|
|
2768
2920
|
} catch (error) {
|
|
2769
2921
|
spinner.fail("Sync failed");
|
|
2770
2922
|
logger.error("sync", "Sync command failed", error);
|
|
@@ -2827,7 +2979,9 @@ function getLinuxStats(pid) {
|
|
|
2827
2979
|
};
|
|
2828
2980
|
}
|
|
2829
2981
|
function getMacStats(pid) {
|
|
2830
|
-
const psOutput = execSync(
|
|
2982
|
+
const psOutput = execSync(
|
|
2983
|
+
`ps -p ${pid} -o %cpu,%mem,etime,thcount`
|
|
2984
|
+
).toString();
|
|
2831
2985
|
const [cpu, mem, etime, threads] = psOutput.split("\n")[1].trim().split(/\s+/);
|
|
2832
2986
|
const uptimeSeconds = parseElapsedTime(etime);
|
|
2833
2987
|
const totalMem = os2.totalmem() / (1024 * 1024);
|
|
@@ -2942,16 +3096,20 @@ function registerDaemonCommands(program2) {
|
|
|
2942
3096
|
const { valid, pid } = validatePidFile();
|
|
2943
3097
|
if (valid && pid) {
|
|
2944
3098
|
spinner.info("Daemon already running");
|
|
2945
|
-
console.log(
|
|
3099
|
+
console.log(
|
|
3100
|
+
chalk8.yellow(`
|
|
2946
3101
|
\u26A0 Daemon is already running (PID: ${pid})
|
|
2947
|
-
`)
|
|
3102
|
+
`)
|
|
3103
|
+
);
|
|
2948
3104
|
return;
|
|
2949
3105
|
}
|
|
2950
3106
|
spinner.text = "Validating authentication...";
|
|
2951
3107
|
const user = await authService.validateToken();
|
|
2952
3108
|
if (!user) {
|
|
2953
3109
|
spinner.fail("Not authenticated");
|
|
2954
|
-
console.log(
|
|
3110
|
+
console.log(
|
|
3111
|
+
chalk8.red("\n\u2716 You must be logged in to start the daemon.")
|
|
3112
|
+
);
|
|
2955
3113
|
console.log(chalk8.gray('Run "stint login" first.\n'));
|
|
2956
3114
|
process.exit(1);
|
|
2957
3115
|
}
|
|
@@ -2969,8 +3127,12 @@ function registerDaemonCommands(program2) {
|
|
|
2969
3127
|
console.log(chalk8.green(`
|
|
2970
3128
|
\u2713 Daemon is running in the background`));
|
|
2971
3129
|
console.log(chalk8.gray(`PID: ${daemonPid}`));
|
|
2972
|
-
console.log(
|
|
2973
|
-
|
|
3130
|
+
console.log(
|
|
3131
|
+
chalk8.gray(
|
|
3132
|
+
`Logs: ${path3.join(os3.homedir(), ".config", "stint", "logs", "daemon.log")}
|
|
3133
|
+
`
|
|
3134
|
+
)
|
|
3135
|
+
);
|
|
2974
3136
|
logger.success("daemon", `Daemon started with PID ${daemonPid}`);
|
|
2975
3137
|
} catch (error) {
|
|
2976
3138
|
spinner.fail("Failed to start daemon");
|
|
@@ -3027,10 +3189,14 @@ function registerDaemonCommands(program2) {
|
|
|
3027
3189
|
console.log(chalk8.blue("\n\u2699\uFE0F Daemon Status:"));
|
|
3028
3190
|
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
3029
3191
|
if (valid && pid) {
|
|
3030
|
-
console.log(
|
|
3192
|
+
console.log(
|
|
3193
|
+
`${chalk8.bold("Status:")} ${chalk8.green("\u2713 Running")}`
|
|
3194
|
+
);
|
|
3031
3195
|
console.log(`${chalk8.bold("PID:")} ${pid}`);
|
|
3032
3196
|
console.log(`${chalk8.bold("PID File:")} ${getPidFilePath()}`);
|
|
3033
|
-
console.log(
|
|
3197
|
+
console.log(
|
|
3198
|
+
`${chalk8.bold("Logs:")} ${path3.join(os3.homedir(), ".config", "stint", "logs", "daemon.log")}`
|
|
3199
|
+
);
|
|
3034
3200
|
const stats = await getProcessStats(pid);
|
|
3035
3201
|
if (stats) {
|
|
3036
3202
|
console.log(chalk8.blue("\n\u{1F4CA} Resource Usage:"));
|
|
@@ -3038,38 +3204,66 @@ function registerDaemonCommands(program2) {
|
|
|
3038
3204
|
console.log(`${chalk8.bold("CPU:")} ${stats.cpuPercent}%`);
|
|
3039
3205
|
console.log(`${chalk8.bold("Memory:")} ${stats.memoryMB} MB`);
|
|
3040
3206
|
console.log(`${chalk8.bold("Threads:")} ${stats.threads}`);
|
|
3041
|
-
console.log(
|
|
3207
|
+
console.log(
|
|
3208
|
+
`${chalk8.bold("Uptime:")} ${formatUptime(stats.uptime)}`
|
|
3209
|
+
);
|
|
3042
3210
|
}
|
|
3043
|
-
const statusPath = path3.join(
|
|
3211
|
+
const statusPath = path3.join(
|
|
3212
|
+
os3.homedir(),
|
|
3213
|
+
".config",
|
|
3214
|
+
"stint",
|
|
3215
|
+
"daemon.status.json"
|
|
3216
|
+
);
|
|
3044
3217
|
if (fs.existsSync(statusPath)) {
|
|
3045
3218
|
try {
|
|
3046
|
-
const statusData = JSON.parse(
|
|
3219
|
+
const statusData = JSON.parse(
|
|
3220
|
+
fs.readFileSync(statusPath, "utf8")
|
|
3221
|
+
);
|
|
3047
3222
|
console.log(chalk8.blue("\n\u{1F4E1} WebSocket Status:"));
|
|
3048
3223
|
console.log(chalk8.gray("\u2500".repeat(50)));
|
|
3049
3224
|
if (statusData.websocket?.connected) {
|
|
3050
|
-
console.log(
|
|
3225
|
+
console.log(
|
|
3226
|
+
`${chalk8.bold("Connected:")} ${chalk8.green("\u2713 Yes")}`
|
|
3227
|
+
);
|
|
3051
3228
|
if (statusData.websocket.channel) {
|
|
3052
|
-
console.log(
|
|
3229
|
+
console.log(
|
|
3230
|
+
`${chalk8.bold("Channel:")} ${statusData.websocket.channel}`
|
|
3231
|
+
);
|
|
3053
3232
|
}
|
|
3054
3233
|
} else {
|
|
3055
|
-
console.log(
|
|
3234
|
+
console.log(
|
|
3235
|
+
`${chalk8.bold("Connected:")} ${chalk8.yellow("\u2717 No")}`
|
|
3236
|
+
);
|
|
3056
3237
|
}
|
|
3057
3238
|
if (statusData.websocket?.lastEvent) {
|
|
3058
|
-
console.log(
|
|
3239
|
+
console.log(
|
|
3240
|
+
`${chalk8.bold("Last Event:")} ${statusData.websocket.lastEvent}`
|
|
3241
|
+
);
|
|
3059
3242
|
if (statusData.websocket.lastEventTime) {
|
|
3060
|
-
const ago = Math.floor(
|
|
3061
|
-
|
|
3243
|
+
const ago = Math.floor(
|
|
3244
|
+
(Date.now() - new Date(statusData.websocket.lastEventTime).getTime()) / 1e3
|
|
3245
|
+
);
|
|
3246
|
+
console.log(
|
|
3247
|
+
`${chalk8.bold("Event Time:")} ${formatUptime(ago)} ago`
|
|
3248
|
+
);
|
|
3062
3249
|
}
|
|
3063
3250
|
}
|
|
3064
3251
|
} catch {
|
|
3065
3252
|
}
|
|
3066
3253
|
}
|
|
3067
3254
|
} else {
|
|
3068
|
-
console.log(
|
|
3069
|
-
|
|
3255
|
+
console.log(
|
|
3256
|
+
`${chalk8.bold("Status:")} ${chalk8.yellow("Not running")}`
|
|
3257
|
+
);
|
|
3258
|
+
console.log(
|
|
3259
|
+
chalk8.gray('Run "stint daemon start" to start the daemon.')
|
|
3260
|
+
);
|
|
3070
3261
|
}
|
|
3071
3262
|
console.log();
|
|
3072
|
-
logger.info(
|
|
3263
|
+
logger.info(
|
|
3264
|
+
"daemon",
|
|
3265
|
+
`Status check: ${valid ? "running" : "not running"}`
|
|
3266
|
+
);
|
|
3073
3267
|
} catch (error) {
|
|
3074
3268
|
spinner.fail("Failed to check status");
|
|
3075
3269
|
logger.error("daemon", "Status command failed", error);
|
|
@@ -3114,19 +3308,38 @@ function registerDaemonCommands(program2) {
|
|
|
3114
3308
|
throw new Error("Daemon failed to start");
|
|
3115
3309
|
}
|
|
3116
3310
|
startSpinner.succeed("Daemon started");
|
|
3117
|
-
console.log(
|
|
3311
|
+
console.log(
|
|
3312
|
+
chalk8.green(
|
|
3313
|
+
`
|
|
3118
3314
|
\u2713 Daemon restarted successfully (PID: ${daemonPid})
|
|
3119
|
-
`
|
|
3315
|
+
`
|
|
3316
|
+
)
|
|
3317
|
+
);
|
|
3120
3318
|
logger.success("daemon", `Daemon restarted with PID ${daemonPid}`);
|
|
3121
3319
|
} catch (error) {
|
|
3122
3320
|
startSpinner.fail("Failed to start daemon");
|
|
3123
3321
|
throw error;
|
|
3124
3322
|
}
|
|
3125
3323
|
});
|
|
3126
|
-
daemon.command("logs").description("View and filter daemon logs").option(
|
|
3324
|
+
daemon.command("logs").description("View and filter daemon logs").option(
|
|
3325
|
+
"-l, --level <level>",
|
|
3326
|
+
"Filter by log level (INFO, WARN, ERROR, DEBUG)"
|
|
3327
|
+
).option("-c, --category <category>", "Filter by log category").option(
|
|
3328
|
+
"-s, --since <date>",
|
|
3329
|
+
'Show logs since date/time (ISO format or relative time like "1h", "2d")'
|
|
3330
|
+
).option(
|
|
3331
|
+
"-u, --until <date>",
|
|
3332
|
+
'Show logs until date/time (ISO format or relative time like "1h", "2d")'
|
|
3333
|
+
).option("--search <text>", "Search for specific text in log messages").option("-f, --follow", "Follow log output in real time").option("-n, --lines <number>", "Number of lines to show", "50").action(async (command) => {
|
|
3127
3334
|
const spinner = ora8("Loading logs...").start();
|
|
3128
3335
|
try {
|
|
3129
|
-
const logPath = path3.join(
|
|
3336
|
+
const logPath = path3.join(
|
|
3337
|
+
os3.homedir(),
|
|
3338
|
+
".config",
|
|
3339
|
+
"stint",
|
|
3340
|
+
"logs",
|
|
3341
|
+
"agent.log"
|
|
3342
|
+
);
|
|
3130
3343
|
if (!fs.existsSync(logPath)) {
|
|
3131
3344
|
spinner.fail("No logs found");
|
|
3132
3345
|
return;
|
|
@@ -3252,16 +3465,27 @@ function registerCommitCommands(program2) {
|
|
|
3252
3465
|
const linkedProject = await projectService.getLinkedProject(cwd);
|
|
3253
3466
|
if (!linkedProject) {
|
|
3254
3467
|
if (options.json) {
|
|
3255
|
-
console.error(
|
|
3468
|
+
console.error(
|
|
3469
|
+
JSON.stringify({
|
|
3470
|
+
error: "Not linked to a project",
|
|
3471
|
+
code: "NOT_LINKED"
|
|
3472
|
+
})
|
|
3473
|
+
);
|
|
3256
3474
|
process6.exit(1);
|
|
3257
3475
|
}
|
|
3258
3476
|
if (spinner) spinner.fail("Not linked");
|
|
3259
|
-
console.log(
|
|
3260
|
-
|
|
3477
|
+
console.log(
|
|
3478
|
+
chalk9.yellow("\n\u26A0 This directory is not linked to any project.")
|
|
3479
|
+
);
|
|
3480
|
+
console.log(
|
|
3481
|
+
chalk9.gray('Run "stint link" first to link this directory.\n')
|
|
3482
|
+
);
|
|
3261
3483
|
process6.exit(1);
|
|
3262
3484
|
}
|
|
3263
3485
|
if (spinner) spinner.text = "Fetching pending commits...";
|
|
3264
|
-
const commits = await apiService.getPendingCommits(
|
|
3486
|
+
const commits = await apiService.getPendingCommits(
|
|
3487
|
+
linkedProject.projectId
|
|
3488
|
+
);
|
|
3265
3489
|
if (options.json) {
|
|
3266
3490
|
logger.disableConsole();
|
|
3267
3491
|
console.log(JSON.stringify(commits, null, 2));
|
|
@@ -3280,170 +3504,227 @@ function registerCommitCommands(program2) {
|
|
|
3280
3504
|
const shortId = commit.id.substring(0, 7);
|
|
3281
3505
|
const date = new Date(commit.createdAt);
|
|
3282
3506
|
const timeAgo = getTimeAgo(date);
|
|
3283
|
-
console.log(
|
|
3507
|
+
console.log(
|
|
3508
|
+
` ${chalk9.cyan(shortId)} ${commit.message.padEnd(40)} ${chalk9.gray(timeAgo)}`
|
|
3509
|
+
);
|
|
3284
3510
|
if (commit.files && commit.files.length > 0) {
|
|
3285
|
-
console.log(
|
|
3511
|
+
console.log(
|
|
3512
|
+
chalk9.gray(` Files: ${commit.files.join(", ")}`)
|
|
3513
|
+
);
|
|
3286
3514
|
}
|
|
3287
3515
|
});
|
|
3288
|
-
console.log(
|
|
3516
|
+
console.log(
|
|
3517
|
+
chalk9.gray(
|
|
3518
|
+
'\nRun "stint commit <id>" to execute a specific commit.\n'
|
|
3519
|
+
)
|
|
3520
|
+
);
|
|
3289
3521
|
logger.info("commits", `Listed ${commits.length} pending commits`);
|
|
3290
3522
|
} catch (error) {
|
|
3291
3523
|
if (spinner) spinner.fail("Failed to fetch commits");
|
|
3292
|
-
else if (options.json)
|
|
3524
|
+
else if (options.json)
|
|
3525
|
+
console.error(JSON.stringify({ error: error.message }));
|
|
3293
3526
|
logger.error("commits", "Failed to fetch commits", error);
|
|
3294
|
-
if (!options.json)
|
|
3527
|
+
if (!options.json)
|
|
3528
|
+
console.error(chalk9.red(`
|
|
3295
3529
|
\u2716 Error: ${error.message}
|
|
3296
3530
|
`));
|
|
3297
3531
|
process6.exit(1);
|
|
3298
3532
|
}
|
|
3299
3533
|
});
|
|
3300
|
-
program2.command("commit <id>").description("Execute a specific pending commit").option(
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
activeSpinner
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3534
|
+
program2.command("commit <id>").description("Execute a specific pending commit").option(
|
|
3535
|
+
"--auto-stage",
|
|
3536
|
+
"Automatically stage files specified in the pending commit"
|
|
3537
|
+
).option("--push", "Push changes to remote after committing").option("--force", "Skip file validation warnings").action(
|
|
3538
|
+
async (id, options) => {
|
|
3539
|
+
let activeSpinner = null;
|
|
3540
|
+
try {
|
|
3541
|
+
activeSpinner = ora9("Checking repository status...").start();
|
|
3542
|
+
const cwd = process6.cwd();
|
|
3543
|
+
const linkedProject = await projectService.getLinkedProject(cwd);
|
|
3544
|
+
if (!linkedProject) {
|
|
3545
|
+
activeSpinner.fail("Not linked");
|
|
3546
|
+
console.log(
|
|
3547
|
+
chalk9.yellow("\n\u26A0 This directory is not linked to any project.")
|
|
3548
|
+
);
|
|
3549
|
+
console.log(
|
|
3550
|
+
chalk9.gray('Run "stint link" first to link this directory.\n')
|
|
3551
|
+
);
|
|
3552
|
+
process6.exit(1);
|
|
3553
|
+
}
|
|
3554
|
+
activeSpinner.text = "Fetching commit details...";
|
|
3555
|
+
const commits = await apiService.getPendingCommits(
|
|
3556
|
+
linkedProject.projectId
|
|
3557
|
+
);
|
|
3558
|
+
const matchingCommits = commits.filter((c) => c.id.startsWith(id));
|
|
3559
|
+
if (matchingCommits.length === 0) {
|
|
3560
|
+
activeSpinner.fail("Commit not found");
|
|
3561
|
+
console.log(
|
|
3562
|
+
chalk9.red(`
|
|
3318
3563
|
\u2716 Commit ${id} not found in pending commits.
|
|
3319
|
-
`)
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3564
|
+
`)
|
|
3565
|
+
);
|
|
3566
|
+
console.log(
|
|
3567
|
+
chalk9.gray('Run "stint commits" to see available commits.\n')
|
|
3568
|
+
);
|
|
3569
|
+
process6.exit(1);
|
|
3570
|
+
}
|
|
3571
|
+
if (matchingCommits.length > 1) {
|
|
3572
|
+
activeSpinner.fail("Ambiguous commit ID");
|
|
3573
|
+
console.log(chalk9.yellow(`
|
|
3326
3574
|
\u26A0 Multiple commits match "${id}":
|
|
3327
3575
|
`));
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
let status = await gitService.getStatus(cwd);
|
|
3337
|
-
if (options.autoStage) {
|
|
3338
|
-
if (commit.files && commit.files.length > 0) {
|
|
3339
|
-
activeSpinner.text = `Staging ${commit.files.length} files...`;
|
|
3340
|
-
await gitService.stageFiles(cwd, commit.files);
|
|
3341
|
-
logger.info("commit", `Auto-staged files: ${commit.files.join(", ")}`);
|
|
3342
|
-
} else {
|
|
3343
|
-
activeSpinner.text = "Staging all changes...";
|
|
3344
|
-
await gitService.stageAll(cwd);
|
|
3345
|
-
logger.info("commit", "Auto-staged all changes");
|
|
3346
|
-
}
|
|
3347
|
-
status = await gitService.getStatus(cwd);
|
|
3348
|
-
}
|
|
3349
|
-
if (status.staged.length === 0) {
|
|
3350
|
-
activeSpinner.fail("No staged changes");
|
|
3351
|
-
console.log(chalk9.yellow("\n\u26A0 No staged changes detected."));
|
|
3352
|
-
if (commit.files && commit.files.length > 0) {
|
|
3353
|
-
console.log(chalk9.gray("Expected files: " + commit.files.join(", ")));
|
|
3354
|
-
console.log(chalk9.gray("\nUse --auto-stage to automatically stage expected files."));
|
|
3355
|
-
} else {
|
|
3356
|
-
console.log(chalk9.gray("Use --auto-stage to automatically stage all changes, or stage files manually:"));
|
|
3357
|
-
console.log(chalk9.gray(" git add <files>\n"));
|
|
3576
|
+
matchingCommits.forEach((c) => {
|
|
3577
|
+
const shortId = c.id.substring(0, 12);
|
|
3578
|
+
console.log(` ${chalk9.cyan(shortId)} ${c.message}`);
|
|
3579
|
+
});
|
|
3580
|
+
console.log(
|
|
3581
|
+
chalk9.gray("\nPlease use a longer ID to be more specific.\n")
|
|
3582
|
+
);
|
|
3583
|
+
process6.exit(1);
|
|
3358
3584
|
}
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3585
|
+
const commit = matchingCommits[0];
|
|
3586
|
+
let status = await gitService.getStatus(cwd);
|
|
3587
|
+
if (options.autoStage) {
|
|
3588
|
+
if (commit.files && commit.files.length > 0) {
|
|
3589
|
+
activeSpinner.text = `Staging ${commit.files.length} files...`;
|
|
3590
|
+
await gitService.stageFiles(cwd, commit.files);
|
|
3591
|
+
logger.info(
|
|
3592
|
+
"commit",
|
|
3593
|
+
`Auto-staged files: ${commit.files.join(", ")}`
|
|
3594
|
+
);
|
|
3595
|
+
} else {
|
|
3596
|
+
activeSpinner.text = "Staging all changes...";
|
|
3597
|
+
await gitService.stageAll(cwd);
|
|
3598
|
+
logger.info("commit", "Auto-staged all changes");
|
|
3372
3599
|
}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3600
|
+
status = await gitService.getStatus(cwd);
|
|
3601
|
+
}
|
|
3602
|
+
if (status.staged.length === 0) {
|
|
3603
|
+
activeSpinner.fail("No staged changes");
|
|
3604
|
+
console.log(chalk9.yellow("\n\u26A0 No staged changes detected."));
|
|
3605
|
+
if (commit.files && commit.files.length > 0) {
|
|
3606
|
+
console.log(
|
|
3607
|
+
chalk9.gray("Expected files: " + commit.files.join(", "))
|
|
3608
|
+
);
|
|
3609
|
+
console.log(
|
|
3610
|
+
chalk9.gray(
|
|
3611
|
+
"\nUse --auto-stage to automatically stage expected files."
|
|
3612
|
+
)
|
|
3613
|
+
);
|
|
3614
|
+
} else {
|
|
3615
|
+
console.log(
|
|
3616
|
+
chalk9.gray(
|
|
3617
|
+
"Use --auto-stage to automatically stage all changes, or stage files manually:"
|
|
3618
|
+
)
|
|
3619
|
+
);
|
|
3620
|
+
console.log(chalk9.gray(" git add <files>\n"));
|
|
3376
3621
|
}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3622
|
+
process6.exit(1);
|
|
3623
|
+
}
|
|
3624
|
+
if (commit.files && commit.files.length > 0 && !options.force && !options.autoStage) {
|
|
3625
|
+
const stagedSet = new Set(status.staged);
|
|
3626
|
+
const expectedSet = new Set(commit.files);
|
|
3627
|
+
const missing = commit.files.filter((f) => !stagedSet.has(f));
|
|
3628
|
+
const extra = status.staged.filter((f) => !expectedSet.has(f));
|
|
3629
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
3630
|
+
activeSpinner.stop();
|
|
3631
|
+
console.log(
|
|
3632
|
+
chalk9.yellow("\n\u26A0 Staged files do not match expected files:\n")
|
|
3633
|
+
);
|
|
3634
|
+
if (missing.length > 0) {
|
|
3635
|
+
console.log(chalk9.red(" Missing (expected but not staged):"));
|
|
3636
|
+
missing.forEach((f) => console.log(chalk9.red(` - ${f}`)));
|
|
3637
|
+
}
|
|
3638
|
+
if (extra.length > 0) {
|
|
3639
|
+
console.log(
|
|
3640
|
+
chalk9.yellow("\n Extra (staged but not expected):")
|
|
3641
|
+
);
|
|
3642
|
+
extra.forEach((f) => console.log(chalk9.yellow(` + ${f}`)));
|
|
3643
|
+
}
|
|
3644
|
+
console.log();
|
|
3645
|
+
const proceed = await confirm2({
|
|
3646
|
+
message: "Do you want to proceed anyway?",
|
|
3647
|
+
default: false
|
|
3648
|
+
});
|
|
3649
|
+
if (!proceed) {
|
|
3650
|
+
console.log(
|
|
3651
|
+
chalk9.gray(
|
|
3652
|
+
"\nCommit cancelled. Use --auto-stage to stage expected files.\n"
|
|
3653
|
+
)
|
|
3654
|
+
);
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
activeSpinner = ora9("Continuing...").start();
|
|
3385
3658
|
}
|
|
3386
|
-
activeSpinner = ora9("Continuing...").start();
|
|
3387
3659
|
}
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
console.log(chalk9.gray("\u2500".repeat(40)));
|
|
3392
|
-
status.staged.forEach((file) => {
|
|
3393
|
-
console.log(chalk9.green(` + ${file}`));
|
|
3394
|
-
});
|
|
3395
|
-
console.log();
|
|
3396
|
-
console.log(`${chalk9.bold("Message:")} ${commit.message}`);
|
|
3397
|
-
if (options.push) {
|
|
3398
|
-
console.log(`${chalk9.bold("Push:")} ${chalk9.cyan("Yes (will push after commit)")}`);
|
|
3399
|
-
}
|
|
3400
|
-
console.log();
|
|
3401
|
-
const confirmed = await confirm2({
|
|
3402
|
-
message: "Are you sure you want to commit these changes?",
|
|
3403
|
-
default: true
|
|
3404
|
-
});
|
|
3405
|
-
if (!confirmed) {
|
|
3406
|
-
console.log(chalk9.yellow("\nCommit cancelled.\n"));
|
|
3407
|
-
return;
|
|
3408
|
-
}
|
|
3409
|
-
activeSpinner = ora9("Preparing commit...").start();
|
|
3410
|
-
const project = {
|
|
3411
|
-
id: linkedProject.projectId,
|
|
3412
|
-
name: "Current Project",
|
|
3413
|
-
createdAt: "",
|
|
3414
|
-
updatedAt: ""
|
|
3415
|
-
};
|
|
3416
|
-
const sha = await commitQueue.executeCommit(commit, project, (stage) => {
|
|
3417
|
-
if (activeSpinner) activeSpinner.text = stage;
|
|
3418
|
-
}, { push: options.push ?? false });
|
|
3419
|
-
activeSpinner.succeed("Commit executed successfully!");
|
|
3420
|
-
console.log(chalk9.green("\n\u2713 Commit executed"));
|
|
3421
|
-
console.log(chalk9.gray("\u2500".repeat(50)));
|
|
3422
|
-
console.log(`${chalk9.bold("Commit ID:")} ${commit.id}`);
|
|
3423
|
-
console.log(`${chalk9.bold("Message:")} ${commit.message}`);
|
|
3424
|
-
console.log(`${chalk9.bold("SHA:")} ${sha}`);
|
|
3425
|
-
console.log(`${chalk9.bold("Files:")} ${status.staged.length} files committed`);
|
|
3426
|
-
if (options.push) {
|
|
3427
|
-
console.log(`${chalk9.bold("Pushed:")} ${chalk9.green("Yes")}`);
|
|
3428
|
-
}
|
|
3429
|
-
console.log();
|
|
3430
|
-
if (status.staged.length > 0) {
|
|
3431
|
-
console.log(chalk9.gray("Committed files:"));
|
|
3660
|
+
activeSpinner.stop();
|
|
3661
|
+
console.log(chalk9.blue("\n\u{1F4CB} Staged changes to commit:"));
|
|
3662
|
+
console.log(chalk9.gray("\u2500".repeat(40)));
|
|
3432
3663
|
status.staged.forEach((file) => {
|
|
3433
3664
|
console.log(chalk9.green(` + ${file}`));
|
|
3434
3665
|
});
|
|
3435
3666
|
console.log();
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3667
|
+
console.log(`${chalk9.bold("Message:")} ${commit.message}`);
|
|
3668
|
+
if (options.push) {
|
|
3669
|
+
console.log(
|
|
3670
|
+
`${chalk9.bold("Push:")} ${chalk9.cyan("Yes (will push after commit)")}`
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
console.log();
|
|
3674
|
+
const confirmed = await confirm2({
|
|
3675
|
+
message: "Are you sure you want to commit these changes?",
|
|
3676
|
+
default: true
|
|
3677
|
+
});
|
|
3678
|
+
if (!confirmed) {
|
|
3679
|
+
console.log(chalk9.yellow("\nCommit cancelled.\n"));
|
|
3680
|
+
return;
|
|
3681
|
+
}
|
|
3682
|
+
activeSpinner = ora9("Preparing commit...").start();
|
|
3683
|
+
const project = {
|
|
3684
|
+
id: linkedProject.projectId,
|
|
3685
|
+
name: "Current Project",
|
|
3686
|
+
createdAt: "",
|
|
3687
|
+
updatedAt: ""
|
|
3688
|
+
};
|
|
3689
|
+
const sha = await commitQueue.executeCommit(
|
|
3690
|
+
commit,
|
|
3691
|
+
project,
|
|
3692
|
+
(stage) => {
|
|
3693
|
+
if (activeSpinner) activeSpinner.text = stage;
|
|
3694
|
+
},
|
|
3695
|
+
{ push: options.push ?? false }
|
|
3696
|
+
);
|
|
3697
|
+
activeSpinner.succeed("Commit executed successfully!");
|
|
3698
|
+
console.log(chalk9.green("\n\u2713 Commit executed"));
|
|
3699
|
+
console.log(chalk9.gray("\u2500".repeat(50)));
|
|
3700
|
+
console.log(`${chalk9.bold("Commit ID:")} ${commit.id}`);
|
|
3701
|
+
console.log(`${chalk9.bold("Message:")} ${commit.message}`);
|
|
3702
|
+
console.log(`${chalk9.bold("SHA:")} ${sha}`);
|
|
3703
|
+
console.log(
|
|
3704
|
+
`${chalk9.bold("Files:")} ${status.staged.length} files committed`
|
|
3705
|
+
);
|
|
3706
|
+
if (options.push) {
|
|
3707
|
+
console.log(`${chalk9.bold("Pushed:")} ${chalk9.green("Yes")}`);
|
|
3708
|
+
}
|
|
3709
|
+
console.log();
|
|
3710
|
+
if (status.staged.length > 0) {
|
|
3711
|
+
console.log(chalk9.gray("Committed files:"));
|
|
3712
|
+
status.staged.forEach((file) => {
|
|
3713
|
+
console.log(chalk9.green(` + ${file}`));
|
|
3714
|
+
});
|
|
3715
|
+
console.log();
|
|
3716
|
+
}
|
|
3717
|
+
logger.success("commit", `Executed commit ${commit.id} -> ${sha}`);
|
|
3718
|
+
} catch (error) {
|
|
3719
|
+
activeSpinner?.fail("Commit execution failed");
|
|
3720
|
+
logger.error("commit", "Failed to execute commit", error);
|
|
3721
|
+
console.error(chalk9.red(`
|
|
3442
3722
|
\u2716 Error: ${error.message}
|
|
3443
3723
|
`));
|
|
3444
|
-
|
|
3724
|
+
process6.exit(1);
|
|
3725
|
+
}
|
|
3445
3726
|
}
|
|
3446
|
-
|
|
3727
|
+
);
|
|
3447
3728
|
}
|
|
3448
3729
|
function getTimeAgo(date) {
|
|
3449
3730
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3475,10 +3756,14 @@ async function installWindows() {
|
|
|
3475
3756
|
const command = getDaemonCommand();
|
|
3476
3757
|
const escapedCommand = command.replace(/"/g, '\\"');
|
|
3477
3758
|
try {
|
|
3478
|
-
await execAsync(
|
|
3759
|
+
await execAsync(
|
|
3760
|
+
`schtasks /Create /SC ONLOGON /TN "${WINDOWS_TASK_NAME}" /TR "${escapedCommand}" /F`
|
|
3761
|
+
);
|
|
3479
3762
|
} catch (error) {
|
|
3480
3763
|
if (error.message.includes("Access is denied")) {
|
|
3481
|
-
throw new Error(
|
|
3764
|
+
throw new Error(
|
|
3765
|
+
"Access denied. Please run this command as Administrator (Right-click Terminal > Run as administrator)."
|
|
3766
|
+
);
|
|
3482
3767
|
}
|
|
3483
3768
|
throw error;
|
|
3484
3769
|
}
|
|
@@ -3488,8 +3773,20 @@ async function uninstallWindows() {
|
|
|
3488
3773
|
}
|
|
3489
3774
|
function getMacPlistContent() {
|
|
3490
3775
|
const scriptPath = process.argv[1];
|
|
3491
|
-
const logPath = path4.join(
|
|
3492
|
-
|
|
3776
|
+
const logPath = path4.join(
|
|
3777
|
+
os4.homedir(),
|
|
3778
|
+
".config",
|
|
3779
|
+
"stint",
|
|
3780
|
+
"logs",
|
|
3781
|
+
"launchd.log"
|
|
3782
|
+
);
|
|
3783
|
+
const errorPath = path4.join(
|
|
3784
|
+
os4.homedir(),
|
|
3785
|
+
".config",
|
|
3786
|
+
"stint",
|
|
3787
|
+
"logs",
|
|
3788
|
+
"launchd.error.log"
|
|
3789
|
+
);
|
|
3493
3790
|
const logDir = path4.dirname(logPath);
|
|
3494
3791
|
if (!fs2.existsSync(logDir)) {
|
|
3495
3792
|
fs2.mkdirSync(logDir, { recursive: true });
|
|
@@ -3595,7 +3892,11 @@ function registerInstallCommand(program2) {
|
|
|
3595
3892
|
const user = await authService.validateToken();
|
|
3596
3893
|
if (!user) {
|
|
3597
3894
|
spinner.fail("Not authenticated");
|
|
3598
|
-
console.log(
|
|
3895
|
+
console.log(
|
|
3896
|
+
chalk10.red(
|
|
3897
|
+
"\n\u2716 You must be logged in to install the background agent on startup."
|
|
3898
|
+
)
|
|
3899
|
+
);
|
|
3599
3900
|
console.log(chalk10.gray('Run "stint login" first.\n'));
|
|
3600
3901
|
process.exit(1);
|
|
3601
3902
|
}
|
|
@@ -3611,14 +3912,24 @@ function registerInstallCommand(program2) {
|
|
|
3611
3912
|
throw new Error(`Unsupported platform: ${platform}`);
|
|
3612
3913
|
}
|
|
3613
3914
|
spinner.succeed("Installed successfully!");
|
|
3614
|
-
console.log(
|
|
3615
|
-
|
|
3915
|
+
console.log(
|
|
3916
|
+
chalk10.green(`
|
|
3917
|
+
\u2713 Stint agent configured to start on login`)
|
|
3918
|
+
);
|
|
3616
3919
|
if (platform === "win32") {
|
|
3617
|
-
console.log(
|
|
3920
|
+
console.log(
|
|
3921
|
+
chalk10.gray(`Registered Task Scheduler task: ${WINDOWS_TASK_NAME}`)
|
|
3922
|
+
);
|
|
3618
3923
|
} else if (platform === "darwin") {
|
|
3619
|
-
console.log(
|
|
3924
|
+
console.log(
|
|
3925
|
+
chalk10.gray(
|
|
3926
|
+
`Created LaunchAgent: ~/Library/LaunchAgents/${MAC_PLIST_NAME}`
|
|
3927
|
+
)
|
|
3928
|
+
);
|
|
3620
3929
|
} else if (platform === "linux") {
|
|
3621
|
-
console.log(
|
|
3930
|
+
console.log(
|
|
3931
|
+
chalk10.gray(`Created systemd user service: ${SYSTEMD_SERVICE_NAME}`)
|
|
3932
|
+
);
|
|
3622
3933
|
}
|
|
3623
3934
|
console.log();
|
|
3624
3935
|
logger.success("install", `Agent installed on startup for ${platform}`);
|
|
@@ -3648,7 +3959,10 @@ function registerUninstallCommand(program2) {
|
|
|
3648
3959
|
}
|
|
3649
3960
|
spinner.succeed("Uninstalled successfully");
|
|
3650
3961
|
console.log(chalk10.gray("\nStint agent removed from system startup.\n"));
|
|
3651
|
-
logger.success(
|
|
3962
|
+
logger.success(
|
|
3963
|
+
"install",
|
|
3964
|
+
`Agent uninstalled from startup for ${platform}`
|
|
3965
|
+
);
|
|
3652
3966
|
} catch (error) {
|
|
3653
3967
|
spinner.fail("Uninstall failed");
|
|
3654
3968
|
logger.error("install", "Uninstall command failed", error);
|
|
@@ -3671,7 +3985,12 @@ import path5 from "path";
|
|
|
3671
3985
|
import os5 from "os";
|
|
3672
3986
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3673
3987
|
var CACHE_TTL = 60 * 60 * 1e3;
|
|
3674
|
-
var CACHE_FILE = path5.join(
|
|
3988
|
+
var CACHE_FILE = path5.join(
|
|
3989
|
+
os5.homedir(),
|
|
3990
|
+
".config",
|
|
3991
|
+
"stint",
|
|
3992
|
+
"version-cache.json"
|
|
3993
|
+
);
|
|
3675
3994
|
var PACKAGE_NAME = "@gowelle/stint-agent";
|
|
3676
3995
|
var VersionService = class {
|
|
3677
3996
|
currentVersion;
|
|
@@ -3702,7 +4021,10 @@ var VersionService = class {
|
|
|
3702
4021
|
throw new Error(`Invalid current version: ${this.currentVersion}`);
|
|
3703
4022
|
}
|
|
3704
4023
|
const hasUpdate = import_semver.default.gt(latestVersion, this.currentVersion);
|
|
3705
|
-
logger.info(
|
|
4024
|
+
logger.info(
|
|
4025
|
+
"version",
|
|
4026
|
+
`Current: ${this.currentVersion}, Latest (${channel}): ${latestVersion}, Update available: ${hasUpdate}`
|
|
4027
|
+
);
|
|
3706
4028
|
return {
|
|
3707
4029
|
current: this.currentVersion,
|
|
3708
4030
|
latest: latestVersion,
|
|
@@ -3736,7 +4058,9 @@ var VersionService = class {
|
|
|
3736
4058
|
logger.debug("version", "Fetching fresh registry data");
|
|
3737
4059
|
const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`);
|
|
3738
4060
|
if (!response.ok) {
|
|
3739
|
-
throw new Error(
|
|
4061
|
+
throw new Error(
|
|
4062
|
+
`Failed to fetch registry data: ${response.status} ${response.statusText}`
|
|
4063
|
+
);
|
|
3740
4064
|
}
|
|
3741
4065
|
const data = await response.json();
|
|
3742
4066
|
this.saveCache(data);
|
|
@@ -3884,7 +4208,10 @@ var UpdateService = class {
|
|
|
3884
4208
|
attempts++;
|
|
3885
4209
|
}
|
|
3886
4210
|
if (isProcessRunning(pid)) {
|
|
3887
|
-
logger.warn(
|
|
4211
|
+
logger.warn(
|
|
4212
|
+
"update",
|
|
4213
|
+
"Daemon did not stop gracefully, forcing shutdown"
|
|
4214
|
+
);
|
|
3888
4215
|
killProcess(pid, "SIGKILL");
|
|
3889
4216
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3890
4217
|
}
|
|
@@ -3906,7 +4233,9 @@ var UpdateService = class {
|
|
|
3906
4233
|
} catch (error) {
|
|
3907
4234
|
logger.error("update", "Failed to restart daemon", error);
|
|
3908
4235
|
console.log(chalk11.yellow("\n\u26A0 Failed to restart daemon automatically."));
|
|
3909
|
-
console.log(
|
|
4236
|
+
console.log(
|
|
4237
|
+
chalk11.gray('Run "stint daemon start" to start it manually.\n')
|
|
4238
|
+
);
|
|
3910
4239
|
}
|
|
3911
4240
|
}
|
|
3912
4241
|
};
|
|
@@ -3924,16 +4253,26 @@ function registerUpdateCommand(program2) {
|
|
|
3924
4253
|
console.log(chalk12.blue("\n\u{1F4E6} Version Information:"));
|
|
3925
4254
|
console.log(chalk12.gray("\u2500".repeat(50)));
|
|
3926
4255
|
console.log(`${chalk12.bold("Current:")} ${versionInfo.current}`);
|
|
3927
|
-
console.log(
|
|
4256
|
+
console.log(
|
|
4257
|
+
`${chalk12.bold("Latest:")} ${versionInfo.latest} (${channel})`
|
|
4258
|
+
);
|
|
3928
4259
|
console.log();
|
|
3929
4260
|
if (!versionInfo.hasUpdate) {
|
|
3930
|
-
console.log(
|
|
4261
|
+
console.log(
|
|
4262
|
+
chalk12.green("\u2713 You are already on the latest version!\n")
|
|
4263
|
+
);
|
|
3931
4264
|
return;
|
|
3932
4265
|
}
|
|
3933
|
-
console.log(
|
|
3934
|
-
|
|
4266
|
+
console.log(
|
|
4267
|
+
chalk12.yellow(
|
|
4268
|
+
`\u26A0 Update available: ${versionInfo.current} \u2192 ${versionInfo.latest}
|
|
4269
|
+
`
|
|
4270
|
+
)
|
|
4271
|
+
);
|
|
3935
4272
|
if (options.check) {
|
|
3936
|
-
console.log(
|
|
4273
|
+
console.log(
|
|
4274
|
+
chalk12.gray('Run "stint update" to install the latest version.\n')
|
|
4275
|
+
);
|
|
3937
4276
|
return;
|
|
3938
4277
|
}
|
|
3939
4278
|
if (!options.yes) {
|
|
@@ -3951,9 +4290,11 @@ function registerUpdateCommand(program2) {
|
|
|
3951
4290
|
console.log();
|
|
3952
4291
|
const result = await updateService.performUpdate(channel);
|
|
3953
4292
|
if (result.success) {
|
|
3954
|
-
console.log(
|
|
4293
|
+
console.log(
|
|
4294
|
+
chalk12.green(`
|
|
3955
4295
|
\u2713 Successfully updated to ${versionInfo.latest}!
|
|
3956
|
-
`)
|
|
4296
|
+
`)
|
|
4297
|
+
);
|
|
3957
4298
|
logger.success("update", `Updated to ${versionInfo.latest}`);
|
|
3958
4299
|
} else {
|
|
3959
4300
|
console.log(chalk12.red(`
|
|
@@ -4100,8 +4441,12 @@ function registerDoctorCommand(program2) {
|
|
|
4100
4441
|
name: "Git Configuration",
|
|
4101
4442
|
check: async () => {
|
|
4102
4443
|
try {
|
|
4103
|
-
const { stdout: userName } = await execAsync2(
|
|
4104
|
-
|
|
4444
|
+
const { stdout: userName } = await execAsync2(
|
|
4445
|
+
"git config --global user.name"
|
|
4446
|
+
);
|
|
4447
|
+
const { stdout: userEmail } = await execAsync2(
|
|
4448
|
+
"git config --global user.email"
|
|
4449
|
+
);
|
|
4105
4450
|
if (!userName.trim() || !userEmail.trim()) {
|
|
4106
4451
|
return {
|
|
4107
4452
|
success: false,
|
|
@@ -4214,10 +4559,14 @@ function registerDoctorCommand(program2) {
|
|
|
4214
4559
|
try {
|
|
4215
4560
|
const result = await check.check();
|
|
4216
4561
|
if (result.success) {
|
|
4217
|
-
console.log(
|
|
4562
|
+
console.log(
|
|
4563
|
+
`${chalk13.green("\u2713")} ${chalk13.bold(check.name)}: ${result.message}`
|
|
4564
|
+
);
|
|
4218
4565
|
} else {
|
|
4219
4566
|
hasErrors = true;
|
|
4220
|
-
console.log(
|
|
4567
|
+
console.log(
|
|
4568
|
+
`${chalk13.red("\u2716")} ${chalk13.bold(check.name)}: ${result.message}`
|
|
4569
|
+
);
|
|
4221
4570
|
if (result.details) {
|
|
4222
4571
|
result.details.forEach((detail) => {
|
|
4223
4572
|
console.log(chalk13.gray(` ${detail}`));
|
|
@@ -4226,16 +4575,24 @@ function registerDoctorCommand(program2) {
|
|
|
4226
4575
|
}
|
|
4227
4576
|
} catch (error) {
|
|
4228
4577
|
hasErrors = true;
|
|
4229
|
-
console.log(
|
|
4578
|
+
console.log(
|
|
4579
|
+
`${chalk13.red("\u2716")} ${chalk13.bold(check.name)}: Check failed - ${error.message}`
|
|
4580
|
+
);
|
|
4230
4581
|
}
|
|
4231
4582
|
}
|
|
4232
4583
|
spinner.stop();
|
|
4233
4584
|
console.log();
|
|
4234
4585
|
if (hasErrors) {
|
|
4235
|
-
console.log(
|
|
4586
|
+
console.log(
|
|
4587
|
+
chalk13.yellow(
|
|
4588
|
+
"Some checks failed. Please address the issues above."
|
|
4589
|
+
)
|
|
4590
|
+
);
|
|
4236
4591
|
process7.exit(1);
|
|
4237
4592
|
} else {
|
|
4238
|
-
console.log(
|
|
4593
|
+
console.log(
|
|
4594
|
+
chalk13.green("All checks passed! Your environment is healthy.")
|
|
4595
|
+
);
|
|
4239
4596
|
}
|
|
4240
4597
|
} catch (error) {
|
|
4241
4598
|
spinner.fail("Diagnostics failed");
|
|
@@ -4256,7 +4613,10 @@ var ALLOWED_KEYS = [
|
|
|
4256
4613
|
"apiUrl",
|
|
4257
4614
|
"wsUrl",
|
|
4258
4615
|
"reverbAppKey",
|
|
4259
|
-
"notifications.enabled"
|
|
4616
|
+
"notifications.enabled",
|
|
4617
|
+
"notifications.commits",
|
|
4618
|
+
"notifications.sync",
|
|
4619
|
+
"notifications.suggestions"
|
|
4260
4620
|
];
|
|
4261
4621
|
var SENSITIVE_KEYS = ["token"];
|
|
4262
4622
|
function isAllowedKey(key) {
|
|
@@ -4311,11 +4671,16 @@ function registerConfigCommand(program2) {
|
|
|
4311
4671
|
if (isSensitiveKey(key)) {
|
|
4312
4672
|
console.log(chalk14.yellow(`
|
|
4313
4673
|
\u26A0 Cannot read sensitive key "${key}"`));
|
|
4314
|
-
console.log(
|
|
4674
|
+
console.log(
|
|
4675
|
+
chalk14.gray('Use "stint whoami" to check authentication status.\n')
|
|
4676
|
+
);
|
|
4315
4677
|
return;
|
|
4316
4678
|
}
|
|
4317
4679
|
const allConfig = config.getAll();
|
|
4318
|
-
const value = getNestedValue(
|
|
4680
|
+
const value = getNestedValue(
|
|
4681
|
+
allConfig,
|
|
4682
|
+
key
|
|
4683
|
+
);
|
|
4319
4684
|
if (value === void 0) {
|
|
4320
4685
|
console.log(chalk14.yellow(`
|
|
4321
4686
|
\u26A0 Key "${key}" is not set.
|
|
@@ -4362,13 +4727,21 @@ ${chalk14.bold(key)}: ${formatValue(value)}
|
|
|
4362
4727
|
const allConfig = config.getAll();
|
|
4363
4728
|
setNestedValue(allConfig, key, parsedValue);
|
|
4364
4729
|
const topKey = key.split(".")[0];
|
|
4365
|
-
config.set(
|
|
4730
|
+
config.set(
|
|
4731
|
+
topKey,
|
|
4732
|
+
allConfig[topKey]
|
|
4733
|
+
);
|
|
4366
4734
|
} else {
|
|
4367
|
-
config.set(
|
|
4735
|
+
config.set(
|
|
4736
|
+
key,
|
|
4737
|
+
parsedValue
|
|
4738
|
+
);
|
|
4368
4739
|
}
|
|
4369
|
-
console.log(
|
|
4740
|
+
console.log(
|
|
4741
|
+
chalk14.green(`
|
|
4370
4742
|
\u2713 Set ${key} = ${formatValue(parsedValue)}
|
|
4371
|
-
`)
|
|
4743
|
+
`)
|
|
4744
|
+
);
|
|
4372
4745
|
logger.success("config", `Set config key: ${key} = ${value}`);
|
|
4373
4746
|
} catch (error) {
|
|
4374
4747
|
logger.error("config", "Failed to set config", error);
|
|
@@ -4383,14 +4756,23 @@ ${chalk14.bold(key)}: ${formatValue(value)}
|
|
|
4383
4756
|
console.log(chalk14.bold("\n\u{1F4CB} Configuration:\n"));
|
|
4384
4757
|
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4385
4758
|
for (const key of ALLOWED_KEYS) {
|
|
4386
|
-
const value = getNestedValue(
|
|
4759
|
+
const value = getNestedValue(
|
|
4760
|
+
allConfig,
|
|
4761
|
+
key
|
|
4762
|
+
);
|
|
4387
4763
|
console.log(`${chalk14.cyan(key.padEnd(25))} ${formatValue(value)}`);
|
|
4388
4764
|
}
|
|
4389
4765
|
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4390
|
-
console.log(
|
|
4766
|
+
console.log(
|
|
4767
|
+
`${chalk14.cyan("token".padEnd(25))} ${chalk14.yellow("[REDACTED]")}`
|
|
4768
|
+
);
|
|
4391
4769
|
console.log(chalk14.gray("\u2500".repeat(50)));
|
|
4392
|
-
console.log(
|
|
4393
|
-
|
|
4770
|
+
console.log(
|
|
4771
|
+
`${chalk14.gray("machineId".padEnd(25))} ${chalk14.gray(allConfig.machineId)}`
|
|
4772
|
+
);
|
|
4773
|
+
console.log(
|
|
4774
|
+
`${chalk14.gray("machineName".padEnd(25))} ${chalk14.gray(allConfig.machineName)}`
|
|
4775
|
+
);
|
|
4394
4776
|
console.log();
|
|
4395
4777
|
logger.debug("config", "Listed config");
|
|
4396
4778
|
} catch (error) {
|
|
@@ -4402,16 +4784,22 @@ ${chalk14.bold(key)}: ${formatValue(value)}
|
|
|
4402
4784
|
});
|
|
4403
4785
|
configCmd.command("path").description("Show the configuration file path").action(() => {
|
|
4404
4786
|
const configPath = getConfigPath();
|
|
4405
|
-
console.log(
|
|
4787
|
+
console.log(
|
|
4788
|
+
`
|
|
4406
4789
|
${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
|
|
4407
|
-
`
|
|
4790
|
+
`
|
|
4791
|
+
);
|
|
4408
4792
|
logger.debug("config", `Config path: ${configPath}`);
|
|
4409
4793
|
});
|
|
4410
4794
|
configCmd.command("reset").description("Reset configuration to defaults (keeps token)").option("--force", "Skip confirmation prompt").action(async (options) => {
|
|
4411
4795
|
try {
|
|
4412
4796
|
if (!options.force) {
|
|
4413
|
-
console.log(
|
|
4414
|
-
|
|
4797
|
+
console.log(
|
|
4798
|
+
chalk14.yellow("\n\u26A0 This will reset all configuration to defaults.")
|
|
4799
|
+
);
|
|
4800
|
+
console.log(
|
|
4801
|
+
chalk14.gray("Your authentication token will be preserved.\n")
|
|
4802
|
+
);
|
|
4415
4803
|
const readline = await import("readline");
|
|
4416
4804
|
const rl = readline.createInterface({
|
|
4417
4805
|
input: process.stdin,
|
|
@@ -4428,12 +4816,19 @@ ${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
|
|
|
4428
4816
|
}
|
|
4429
4817
|
const token = config.getToken();
|
|
4430
4818
|
for (const key of ALLOWED_KEYS) {
|
|
4431
|
-
if (key
|
|
4432
|
-
config.set("notifications", {
|
|
4819
|
+
if (key.startsWith("notifications.")) {
|
|
4820
|
+
config.set("notifications", {
|
|
4821
|
+
enabled: true,
|
|
4822
|
+
commits: true,
|
|
4823
|
+
sync: false,
|
|
4824
|
+
suggestions: true
|
|
4825
|
+
});
|
|
4433
4826
|
} else {
|
|
4434
4827
|
if (key === "apiUrl") config.set("apiUrl", "https://stint.codes");
|
|
4435
|
-
if (key === "wsUrl")
|
|
4436
|
-
|
|
4828
|
+
if (key === "wsUrl")
|
|
4829
|
+
config.set("wsUrl", "wss://stint.codes/reverb");
|
|
4830
|
+
if (key === "reverbAppKey")
|
|
4831
|
+
config.set("reverbAppKey", "wtn6tu6lirfv6yflujk7");
|
|
4437
4832
|
}
|
|
4438
4833
|
}
|
|
4439
4834
|
if (token) {
|
|
@@ -4451,9 +4846,11 @@ ${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
|
|
|
4451
4846
|
}
|
|
4452
4847
|
|
|
4453
4848
|
// src/index.ts
|
|
4454
|
-
var AGENT_VERSION = "1.2.
|
|
4849
|
+
var AGENT_VERSION = "1.2.36";
|
|
4455
4850
|
var program = new Command();
|
|
4456
|
-
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText(
|
|
4851
|
+
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText(
|
|
4852
|
+
"after",
|
|
4853
|
+
`
|
|
4457
4854
|
${chalk15.bold("Examples:")}
|
|
4458
4855
|
${chalk15.cyan("$")} stint login ${chalk15.gray("# Authenticate with Stint")}
|
|
4459
4856
|
${chalk15.cyan("$")} stint install ${chalk15.gray("# Install agent to run on startup")}
|
|
@@ -4464,7 +4861,8 @@ ${chalk15.bold("Examples:")}
|
|
|
4464
4861
|
|
|
4465
4862
|
${chalk15.bold("Documentation:")}
|
|
4466
4863
|
For more information, visit: ${chalk15.blue("https://stint.codes/docs")}
|
|
4467
|
-
`
|
|
4864
|
+
`
|
|
4865
|
+
);
|
|
4468
4866
|
registerLoginCommand(program);
|
|
4469
4867
|
registerLogoutCommand(program);
|
|
4470
4868
|
registerWhoamiCommand(program);
|