gh-glance 0.14.0 → 0.14.1
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/CHANGELOG.md +23 -1
- package/index.mjs +56 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.1] - 2026-09-08
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **A folder with no git remote opens in setup, not in a coordination wait.**
|
|
15
|
+
The onboarding prompt (`No GitHub remote found -- press Enter to create one`)
|
|
16
|
+
was only ever produced from a `gh` error, and with no remote there is no host
|
|
17
|
+
to verify an identity against, so no `gh` command ever ran: the dashboard sat
|
|
18
|
+
on "Can't coordinate API use -- retrying" and "waiting for API budget…" for a
|
|
19
|
+
repository that did not exist. The target is now decided before any
|
|
20
|
+
coordination -- no remote, no `--repo`, no `GH_REPO` means setup -- and
|
|
21
|
+
`--doctor` says so in the same words instead of reporting an unavailable
|
|
22
|
+
identity.
|
|
23
|
+
- **An upgrade no longer stalls on a ledger that never recorded a budget for a
|
|
24
|
+
resource it charged.** A 0.12-era file whose panes only ever observed GraphQL
|
|
25
|
+
holds core reservations with no core budget to date them by, so the migration
|
|
26
|
+
reported "older spend cannot be settled or waited out" -- a hold with no
|
|
27
|
+
deadline, on every launch, forever. Each reservation records the epoch it was
|
|
28
|
+
admitted against; that epoch's reset now serves as the deadline, and a window
|
|
29
|
+
that ended weeks ago clears at once.
|
|
30
|
+
|
|
10
31
|
## [0.14.0] - 2026-09-07
|
|
11
32
|
|
|
12
33
|
### Added
|
|
@@ -1029,7 +1050,8 @@ engineering, security, QA and UX. What follows is what changed as a result.
|
|
|
1029
1050
|
- The `main` field from `package.json`. It advertised the file as importable,
|
|
1030
1051
|
but importing it took over the terminal or exited the host process.
|
|
1031
1052
|
|
|
1032
|
-
[Unreleased]: https://github.com/juan294/gh-glance/compare/v0.14.
|
|
1053
|
+
[Unreleased]: https://github.com/juan294/gh-glance/compare/v0.14.1...HEAD
|
|
1054
|
+
[0.14.1]: https://github.com/juan294/gh-glance/compare/v0.14.0...v0.14.1
|
|
1033
1055
|
[0.14.0]: https://github.com/juan294/gh-glance/compare/v0.13.3...v0.14.0
|
|
1034
1056
|
[0.13.3]: https://github.com/juan294/gh-glance/compare/v0.13.2...v0.13.3
|
|
1035
1057
|
[0.13.2]: https://github.com/juan294/gh-glance/compare/v0.13.1...v0.13.2
|
package/index.mjs
CHANGED
|
@@ -428,6 +428,9 @@ function isAuthProblem(err) {
|
|
|
428
428
|
function isMissingRemote(err) {
|
|
429
429
|
return /(?:failed to determine base repo:\s*)?no git remotes found/i.test(errText(err));
|
|
430
430
|
}
|
|
431
|
+
// The shorter of the two, so a verdict recorded without a gh subprocess reads
|
|
432
|
+
// exactly like one gh produced and classifies the same way.
|
|
433
|
+
const NO_REMOTE_ERROR_TEXT = "no git remotes found";
|
|
431
434
|
|
|
432
435
|
const UNUSABLE_OUTPUT_CODE = "GH_GLANCE_UNUSABLE_OUTPUT";
|
|
433
436
|
|
|
@@ -1060,6 +1063,25 @@ function resolveEffectiveHost({
|
|
|
1060
1063
|
|
|
1061
1064
|
const UNRESOLVED_REPOSITORY = "Repository could not be resolved; pass --repo owner/name";
|
|
1062
1065
|
|
|
1066
|
+
// Whether there is a repository to talk about at all. A folder with no git
|
|
1067
|
+
// remote, no --repo and no GH_REPO is the onboarding state the no-remote verdict
|
|
1068
|
+
// renders -- but that verdict was only ever produced from a gh error, and with
|
|
1069
|
+
// no remote there is no host to verify an identity against, so no gh command
|
|
1070
|
+
// ran: the coordinator reported unknown-host and the dashboard sat on a generic
|
|
1071
|
+
// "retrying" notice, waiting for a budget it could never resolve. Decided from
|
|
1072
|
+
// the same inputs as the host so the two cannot disagree; an ambiguous set of
|
|
1073
|
+
// remotes is still a coordination question, not this one.
|
|
1074
|
+
function noRepositoryTarget({ runtimeRepo = null, ghRepo = null, remoteUrls = [] } = {}) {
|
|
1075
|
+
return !runtimeRepo && !ghRepo && remoteUrls.length === 0;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function runtimeHasNoRepositoryTarget(remoteUrls = runtimeRemoteUrls) {
|
|
1079
|
+
return noRepositoryTarget({ runtimeRepo: runtime.repo, ghRepo: process.env.GH_REPO, remoteUrls });
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const NO_REPOSITORY_TARGET_REPORT =
|
|
1083
|
+
"no repository target: this folder has no git remote (run `gh repo create`, or pass --repo owner/name)";
|
|
1084
|
+
|
|
1063
1085
|
// The owner/name half of the same question resolveEffectiveHost answers for the
|
|
1064
1086
|
// host, and in the same precedence: an explicit --repo, then GH_REPO, then an
|
|
1065
1087
|
// unambiguous git remote. Ambiguity resolves to null rather than to a guess.
|
|
@@ -2781,9 +2803,9 @@ function inspectLegacyMigration(root, state, now) {
|
|
|
2781
2803
|
for (const resource of RATE_RESOURCES) {
|
|
2782
2804
|
const budget = legacy.budgets[resource];
|
|
2783
2805
|
holdUntil = Math.max(holdUntil, budget?.blockUntil ?? 0);
|
|
2784
|
-
const uncertain = Object.values(legacy.reservations).
|
|
2806
|
+
const uncertain = Object.values(legacy.reservations).filter((reservation) =>
|
|
2785
2807
|
["started", "completed"].includes(reservation.status) && reservationCost(reservation, resource, legacy.leases, now) > 0);
|
|
2786
|
-
if (uncertain) {
|
|
2808
|
+
if (uncertain.length > 0) {
|
|
2787
2809
|
// The deadline may only be read from the migrated view for resources the
|
|
2788
2810
|
// migration kept. A v1 file's budgets are dropped on purpose -- they came
|
|
2789
2811
|
// from /rate_limit and are not spendable capacity -- but the reset they
|
|
@@ -2794,9 +2816,22 @@ function inspectLegacyMigration(root, state, now) {
|
|
|
2794
2816
|
// none, and reported legacy-unresolved -- which has no deadline and so
|
|
2795
2817
|
// never cleared. The file it came from had a perfectly good reset in it
|
|
2796
2818
|
// the whole time.
|
|
2819
|
+
//
|
|
2820
|
+
// A file can also simply never have recorded a budget for the resource:
|
|
2821
|
+
// a v2 ledger whose panes only ever observed GraphQL still charged core
|
|
2822
|
+
// per request, so its core reservations exist without a core budget to
|
|
2823
|
+
// ask. Each reservation records the epoch it was admitted against, and
|
|
2824
|
+
// that epoch's reset is when its window ended -- the same fact, kept in
|
|
2825
|
+
// the other place. Only a reservation with no budget *and* no epoch is
|
|
2826
|
+
// genuinely undated.
|
|
2827
|
+
const recorded = uncertain
|
|
2828
|
+
.map((reservation) => Number(String(reservation.epochs?.[resource] ?? "").split(":")[1]))
|
|
2829
|
+
.filter(Number.isFinite);
|
|
2797
2830
|
const reset = Number.isFinite(budget?.resetMs)
|
|
2798
2831
|
? budget.resetMs
|
|
2799
|
-
: Number(raw?.budgets?.[resource]?.resetMs)
|
|
2832
|
+
: Number.isFinite(Number(raw?.budgets?.[resource]?.resetMs))
|
|
2833
|
+
? Number(raw.budgets[resource].resetMs)
|
|
2834
|
+
: recorded.length > 0 ? Math.max(...recorded) : NaN;
|
|
2800
2835
|
if (!Number.isFinite(reset)) return { ok: false, reason: "legacy-unresolved" };
|
|
2801
2836
|
holdUntil = Math.max(holdUntil, reset + BUDGET_RESET_GRACE_MS);
|
|
2802
2837
|
}
|
|
@@ -6046,7 +6081,9 @@ async function runDoctor() {
|
|
|
6046
6081
|
runtimeIdentityCoordinator = createIdentityCoordinator({ host: effectiveHost });
|
|
6047
6082
|
const verified = await runtimeIdentityCoordinator.refresh();
|
|
6048
6083
|
const verifiedIdentity = runtimeIdentityCoordinator.current();
|
|
6049
|
-
const authStatus = verifiedIdentity
|
|
6084
|
+
const authStatus = verifiedIdentity
|
|
6085
|
+
? `${verifiedIdentity.host}: ${verifiedIdentity.login} (verified)`
|
|
6086
|
+
: runtimeHasNoRepositoryTarget(remoteUrls) ? NO_REPOSITORY_TARGET_REPORT : identityCoordinationMessage(verified.reason);
|
|
6050
6087
|
const resources = verifiedIdentity ? await readRateLimitResources(undefined, effectiveHost).catch(() => null) : null;
|
|
6051
6088
|
const budget = resources
|
|
6052
6089
|
? await rateBudget(resources)
|
|
@@ -9987,6 +10024,18 @@ function App({ onCreateRemote = () => {} } = {}) {
|
|
|
9987
10024
|
});
|
|
9988
10025
|
}
|
|
9989
10026
|
|
|
10027
|
+
// A folder with no repository target goes straight to setup. The no-remote
|
|
10028
|
+
// verdict is normally a fetch failure, but nothing can be fetched here --
|
|
10029
|
+
// there is no host to verify against -- so the same verdict is recorded on
|
|
10030
|
+
// every tab before coordination is attempted. remoteSetupRef then keeps the
|
|
10031
|
+
// scheduler out, and the Enter hint hands off to `gh repo create`.
|
|
10032
|
+
function enterRemoteSetup() {
|
|
10033
|
+
if (!runtimeHasNoRepositoryTarget()) return false;
|
|
10034
|
+
const failure = toTabError(new Error(NO_REMOTE_ERROR_TEXT));
|
|
10035
|
+
setErrors(Object.fromEntries(TABS.map((t) => [t.key, failure])));
|
|
10036
|
+
return true;
|
|
10037
|
+
}
|
|
10038
|
+
|
|
9990
10039
|
function publishControlStatus(key, refreshed, snapshot, nowMs) {
|
|
9991
10040
|
if (pendingBlockPublications.size > 0) {
|
|
9992
10041
|
pauseCoordination(key, "block-unpublished");
|
|
@@ -10989,6 +11038,8 @@ function App({ onCreateRemote = () => {} } = {}) {
|
|
|
10989
11038
|
async function bootstrap() {
|
|
10990
11039
|
remoteUrls = runtimeRemoteUrls.length > 0 ? runtimeRemoteUrls : await gitRemoteUrls();
|
|
10991
11040
|
runtimeRemoteUrls = remoteUrls;
|
|
11041
|
+
if (cancelled) return;
|
|
11042
|
+
if (enterRemoteSetup()) return;
|
|
10992
11043
|
await runtimeIdentityCoordinator.refresh();
|
|
10993
11044
|
if (cancelled) return;
|
|
10994
11045
|
const currentScope = ensureScope(Date.now());
|
|
@@ -11859,6 +11910,7 @@ export {
|
|
|
11859
11910
|
isRateLimited,
|
|
11860
11911
|
isAuthProblem,
|
|
11861
11912
|
isMissingRemote,
|
|
11913
|
+
noRepositoryTarget,
|
|
11862
11914
|
isUnusableOutput,
|
|
11863
11915
|
forwardSignalToChild,
|
|
11864
11916
|
toTabError,
|
package/package.json
CHANGED