@adhdev/daemon-core 0.9.82-rc.302 → 0.9.82-rc.303
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/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +2 -0
- package/dist/index.js +76 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -58
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +71 -13
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { GitFailureReason, GitRepoIdentity } from './git-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Timeout for status-collection git commands (status/log/submodule/stash/fetch).
|
|
4
|
+
* The default 5s is fine for porcelain status, but on Windows the `git` subprocess
|
|
5
|
+
* spawn itself is pathologically slow (measured: `submodule status` ~3.5s, cold
|
|
6
|
+
* `log -1` ~4.2s) and overlaps with refreshUpstream fetches — a single command
|
|
7
|
+
* routinely exceeds 5s, which previously collapsed the whole status to all-null and
|
|
8
|
+
* dropped the node from the mesh graph. Give the collection path a much larger
|
|
9
|
+
* budget so a slow-but-healthy repo never reads as "not a git repo". Windows gets a
|
|
10
|
+
* larger budget than POSIX because the spawn cost is OS-specific, not repo-specific.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GIT_STATUS_TIMEOUT_MS: number;
|
|
2
13
|
export interface GitExecutorOptions {
|
|
3
14
|
timeoutMs?: number;
|
|
4
15
|
maxBuffer?: number;
|
package/dist/git/git-status.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { GitRepoStatus } from './git-types.js';
|
|
2
2
|
import { type DaemonBuildInfo } from '../build-info.js';
|
|
3
|
+
/** Test seam: clear the last-known-good status cache between cases. */
|
|
4
|
+
export declare function __resetGitStatusCacheForTests(): void;
|
|
3
5
|
export interface GitStatusOptions {
|
|
4
6
|
timeoutMs?: number;
|
|
5
7
|
/** When true, include submodule status in the result. Defaults to true. */
|
package/dist/index.js
CHANGED
|
@@ -82,6 +82,7 @@ var init_repo_mesh_types = __esm({
|
|
|
82
82
|
// src/git/git-executor.ts
|
|
83
83
|
var git_executor_exports = {};
|
|
84
84
|
__export(git_executor_exports, {
|
|
85
|
+
GIT_STATUS_TIMEOUT_MS: () => GIT_STATUS_TIMEOUT_MS,
|
|
85
86
|
GitCommandError: () => GitCommandError,
|
|
86
87
|
isPathInside: () => isPathInside,
|
|
87
88
|
normalizeGitOutput: () => normalizeGitOutput,
|
|
@@ -246,7 +247,7 @@ function mapExecError(error, cwd, argv, behavior) {
|
|
|
246
247
|
cause: error
|
|
247
248
|
});
|
|
248
249
|
}
|
|
249
|
-
var import_node_child_process, import_node_fs, import_promises, path, import_node_util, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError;
|
|
250
|
+
var import_node_child_process, import_node_fs, import_promises, path, import_node_util, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GIT_STATUS_TIMEOUT_MS, GitCommandError;
|
|
250
251
|
var init_git_executor = __esm({
|
|
251
252
|
"src/git/git-executor.ts"() {
|
|
252
253
|
"use strict";
|
|
@@ -258,6 +259,7 @@ var init_git_executor = __esm({
|
|
|
258
259
|
execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
259
260
|
DEFAULT_TIMEOUT_MS = 5e3;
|
|
260
261
|
DEFAULT_MAX_BUFFER = 1024 * 1024;
|
|
262
|
+
GIT_STATUS_TIMEOUT_MS = process.platform === "win32" ? 3e4 : 2e4;
|
|
261
263
|
GitCommandError = class extends Error {
|
|
262
264
|
reason;
|
|
263
265
|
stdout;
|
|
@@ -293,10 +295,10 @@ function readInjected(value) {
|
|
|
293
295
|
}
|
|
294
296
|
function getDaemonBuildInfo() {
|
|
295
297
|
if (cached) return cached;
|
|
296
|
-
const commit = readInjected(true ? "
|
|
297
|
-
const commitShort = readInjected(true ? "
|
|
298
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
299
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
298
|
+
const commit = readInjected(true ? "21741b4b2d3c1f2ed8a280045e6158b2730391ed" : void 0) ?? "unknown";
|
|
299
|
+
const commitShort = readInjected(true ? "21741b4b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
300
|
+
const version = readInjected(true ? "0.9.82-rc.303" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
301
|
+
const builtAt = readInjected(true ? "2026-06-17T04:16:06.174Z" : void 0);
|
|
300
302
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
301
303
|
return cached;
|
|
302
304
|
}
|
|
@@ -308,64 +310,79 @@ var init_build_info = __esm({
|
|
|
308
310
|
});
|
|
309
311
|
|
|
310
312
|
// src/git/git-status.ts
|
|
313
|
+
function isTransientGitFailure(error) {
|
|
314
|
+
return error.reason === "timeout" || error.reason === "git_command_failed";
|
|
315
|
+
}
|
|
311
316
|
async function getGitRepoStatus(workspace, options = {}) {
|
|
312
317
|
const lastCheckedAt = Date.now();
|
|
313
318
|
const includeSubmodules = options.includeSubmodules !== false;
|
|
319
|
+
const effectiveOptions = options.timeoutMs === void 0 ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
|
|
314
320
|
try {
|
|
315
|
-
const repo = await resolveGitRepository(workspace,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
|
|
320
|
-
if (upstreamProbe.upstreamStatus === "fresh") {
|
|
321
|
-
parsed = await readPorcelainStatus(repo, options);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
const head = await readHead(repo, options);
|
|
325
|
-
const stashCount = await readStashCount(repo, options);
|
|
326
|
-
let submodules;
|
|
327
|
-
if (includeSubmodules) {
|
|
328
|
-
submodules = await getSubmoduleStatuses(repo, options);
|
|
329
|
-
}
|
|
330
|
-
const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
|
|
331
|
-
const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
|
|
332
|
-
const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
|
|
333
|
-
return {
|
|
334
|
-
workspace: repo.workspace,
|
|
335
|
-
repoRoot: repo.repoRoot,
|
|
336
|
-
isGitRepo: true,
|
|
337
|
-
branch: parsed.branch,
|
|
338
|
-
headCommit: head.commit,
|
|
339
|
-
headMessage: head.message,
|
|
340
|
-
upstream: parsed.upstream,
|
|
341
|
-
upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
|
|
342
|
-
upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
|
|
343
|
-
upstreamFetchError: upstreamProbe.upstreamFetchError,
|
|
344
|
-
ahead: parsed.ahead,
|
|
345
|
-
behind: parsed.behind,
|
|
346
|
-
staged: parsed.staged,
|
|
347
|
-
modified: parsed.modified,
|
|
348
|
-
untracked: parsed.untracked,
|
|
349
|
-
deleted: parsed.deleted,
|
|
350
|
-
renamed: parsed.renamed,
|
|
351
|
-
dirty,
|
|
352
|
-
hasConflicts: parsed.conflictFiles.length > 0,
|
|
353
|
-
conflictFiles: parsed.conflictFiles,
|
|
354
|
-
stashCount,
|
|
355
|
-
lastCheckedAt,
|
|
356
|
-
submodules,
|
|
357
|
-
...daemonBuildBehind ? { daemonBuildBehind } : {}
|
|
358
|
-
};
|
|
321
|
+
const repo = await resolveGitRepository(workspace, effectiveOptions);
|
|
322
|
+
const status = await collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, effectiveOptions);
|
|
323
|
+
lastKnownGoodStatus.set(workspace, status);
|
|
324
|
+
return status;
|
|
359
325
|
} catch (error) {
|
|
360
|
-
|
|
361
|
-
|
|
326
|
+
const gitError = error instanceof GitCommandError ? error : new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error });
|
|
327
|
+
if (isTransientGitFailure(gitError)) {
|
|
328
|
+
const cached2 = lastKnownGoodStatus.get(workspace);
|
|
329
|
+
if (cached2) {
|
|
330
|
+
return {
|
|
331
|
+
...cached2,
|
|
332
|
+
lastCheckedAt,
|
|
333
|
+
upstreamStatus: "unavailable",
|
|
334
|
+
error: gitError.stderr || gitError.message,
|
|
335
|
+
reason: gitError.reason
|
|
336
|
+
};
|
|
337
|
+
}
|
|
362
338
|
}
|
|
363
|
-
return emptyStatus(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
339
|
+
return emptyStatus(workspace, lastCheckedAt, gitError);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async function collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, options) {
|
|
343
|
+
let parsed = await readPorcelainStatus(repo, options);
|
|
344
|
+
let upstreamProbe = getInitialUpstreamProbe(parsed);
|
|
345
|
+
if (options.refreshUpstream) {
|
|
346
|
+
upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
|
|
347
|
+
if (upstreamProbe.upstreamStatus === "fresh") {
|
|
348
|
+
parsed = await readPorcelainStatus(repo, options);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const head = await readHead(repo, options);
|
|
352
|
+
const stashCount = await readStashCount(repo, options);
|
|
353
|
+
let submodules;
|
|
354
|
+
if (includeSubmodules) {
|
|
355
|
+
submodules = await getSubmoduleStatuses(repo, options);
|
|
368
356
|
}
|
|
357
|
+
const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
|
|
358
|
+
const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
|
|
359
|
+
const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
|
|
360
|
+
return {
|
|
361
|
+
workspace: repo.workspace,
|
|
362
|
+
repoRoot: repo.repoRoot,
|
|
363
|
+
isGitRepo: true,
|
|
364
|
+
branch: parsed.branch,
|
|
365
|
+
headCommit: head.commit,
|
|
366
|
+
headMessage: head.message,
|
|
367
|
+
upstream: parsed.upstream,
|
|
368
|
+
upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
|
|
369
|
+
upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
|
|
370
|
+
upstreamFetchError: upstreamProbe.upstreamFetchError,
|
|
371
|
+
ahead: parsed.ahead,
|
|
372
|
+
behind: parsed.behind,
|
|
373
|
+
staged: parsed.staged,
|
|
374
|
+
modified: parsed.modified,
|
|
375
|
+
untracked: parsed.untracked,
|
|
376
|
+
deleted: parsed.deleted,
|
|
377
|
+
renamed: parsed.renamed,
|
|
378
|
+
dirty,
|
|
379
|
+
hasConflicts: parsed.conflictFiles.length > 0,
|
|
380
|
+
conflictFiles: parsed.conflictFiles,
|
|
381
|
+
stashCount,
|
|
382
|
+
lastCheckedAt,
|
|
383
|
+
submodules,
|
|
384
|
+
...daemonBuildBehind ? { daemonBuildBehind } : {}
|
|
385
|
+
};
|
|
369
386
|
}
|
|
370
387
|
function isNonRuntimeRootFile(file) {
|
|
371
388
|
const base = file.slice(file.lastIndexOf("/") + 1);
|
|
@@ -607,7 +624,7 @@ function emptyStatus(workspace, lastCheckedAt, error) {
|
|
|
607
624
|
async function getSubmoduleStatuses(repo, options) {
|
|
608
625
|
if (!repo.repoRoot) return [];
|
|
609
626
|
try {
|
|
610
|
-
const result = await runGit(repo, ["submodule", "status"
|
|
627
|
+
const result = await runGit(repo, ["submodule", "status"], options);
|
|
611
628
|
const submodules = parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
|
|
612
629
|
await Promise.all(submodules.map((submodule) => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
|
|
613
630
|
return submodules;
|
|
@@ -651,12 +668,13 @@ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
|
|
|
651
668
|
}
|
|
652
669
|
return submodules;
|
|
653
670
|
}
|
|
654
|
-
var DAEMON_RUNTIME_PACKAGES, WEB_ONLY_PACKAGES;
|
|
671
|
+
var lastKnownGoodStatus, DAEMON_RUNTIME_PACKAGES, WEB_ONLY_PACKAGES;
|
|
655
672
|
var init_git_status = __esm({
|
|
656
673
|
"src/git/git-status.ts"() {
|
|
657
674
|
"use strict";
|
|
658
675
|
init_git_executor();
|
|
659
676
|
init_build_info();
|
|
677
|
+
lastKnownGoodStatus = /* @__PURE__ */ new Map();
|
|
660
678
|
DAEMON_RUNTIME_PACKAGES = /* @__PURE__ */ new Set([
|
|
661
679
|
"daemon-core",
|
|
662
680
|
"daemon-standalone",
|