@tiens.nguyen/gu-cli 1.0.727 → 1.0.728
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/cli-token.mjs +13 -4
- package/gu-cli.mjs +27 -10
- package/package.json +1 -1
- package/version-check.mjs +34 -0
package/cli-token.mjs
CHANGED
|
@@ -291,7 +291,11 @@ export function barNotice({ token, publicKeyB64, mode, now = new Date(), attempt
|
|
|
291
291
|
if (Number.isInteger(p.activeDevices) && p.activeDevices > p.deviceLimit) {
|
|
292
292
|
return {
|
|
293
293
|
kind: "over-limit",
|
|
294
|
-
|
|
294
|
+
// PLAIN WORDS (asked: "make it simple to understand"). It read "gu is active on 4 devices —
|
|
295
|
+
// Free covers 1", which leaves the reader to work out that "active" means "used gu in the
|
|
296
|
+
// last 30 days" and "covers" means "the plan allows". Kept short: the bar drops it entirely
|
|
297
|
+
// when it does not fit the terminal's width.
|
|
298
|
+
text: `${planName(p.plan)} allows ${computers(p.deviceLimit)} — you've used gu on ${p.activeDevices} this month`,
|
|
295
299
|
};
|
|
296
300
|
}
|
|
297
301
|
const age = now.getTime() - Date.parse(p.issuedAt);
|
|
@@ -305,6 +309,11 @@ export function barNotice({ token, publicKeyB64, mode, now = new Date(), attempt
|
|
|
305
309
|
: { kind: "stale", text: "Sign in to keep getting gu updates — run gu login" };
|
|
306
310
|
}
|
|
307
311
|
|
|
312
|
+
/** "Free plan" / "Pro plan" — the name a person uses. */
|
|
313
|
+
const planName = (plan) => (plan === "pro" ? "Pro plan" : "Free plan");
|
|
314
|
+
/** "1 computer" / "4 computers" — "computer", not "device": a device is also a phone. */
|
|
315
|
+
const computers = (n) => `${n} ${n === 1 ? "computer" : "computers"}`;
|
|
316
|
+
|
|
308
317
|
/** What `gu token` prints about a valid token — plain lines, no colour, for the command to style. */
|
|
309
318
|
export function tokenSummary(payload, now = new Date()) {
|
|
310
319
|
const lines = [];
|
|
@@ -317,7 +326,7 @@ export function tokenSummary(payload, now = new Date()) {
|
|
|
317
326
|
}
|
|
318
327
|
if (Number.isInteger(payload.activeDevices)) {
|
|
319
328
|
const n = payload.activeDevices;
|
|
320
|
-
lines.push(`
|
|
329
|
+
lines.push(`Used on ${computers(n)} in the last 30 days — the ${planName(payload.plan)} allows ${payload.deviceLimit}.`);
|
|
321
330
|
}
|
|
322
331
|
lines.push(`Issued ${shortDate(payload.issuedAt)}.`);
|
|
323
332
|
return lines;
|
|
@@ -370,8 +379,8 @@ export function enforcementDecision({ token, publicKeyB64, now = new Date(), sit
|
|
|
370
379
|
enforced: true,
|
|
371
380
|
allowed: false,
|
|
372
381
|
why:
|
|
373
|
-
`This account
|
|
374
|
-
|
|
382
|
+
`This account has used gu on ${computers(p.activeDevices)} in the last 30 days, and the ` +
|
|
383
|
+
`${planName(p.plan)} allows ${p.deviceLimit}. Sign out of one, or remove it at ${accountUrl}.`,
|
|
375
384
|
};
|
|
376
385
|
}
|
|
377
386
|
return { enforced: true, allowed: true, why: "" };
|
package/gu-cli.mjs
CHANGED
|
@@ -679,7 +679,6 @@ const currentApiVersion = async () => {
|
|
|
679
679
|
// machine that was already paired, i.e. every machine that could need an upgrade, had no
|
|
680
680
|
// path to one. A published API fix reached nobody.
|
|
681
681
|
const run = await import("./client-expert-run.mjs");
|
|
682
|
-
const { isNewer } = await import("./version-check.mjs");
|
|
683
682
|
const running = await currentVersion();
|
|
684
683
|
|
|
685
684
|
// TOP UP THE SEEDED SETTINGS FIRST, AND UNCONDITIONALLY.
|
|
@@ -719,8 +718,19 @@ const currentApiVersion = async () => {
|
|
|
719
718
|
const dir = run.apiInstallDir(manifest.version);
|
|
720
719
|
const depsIn = await ce.apiDepsInstalled(dir);
|
|
721
720
|
const present = (await run.probeApiInstall(manifest.version)).installed;
|
|
722
|
-
|
|
723
|
-
|
|
721
|
+
// DECIDED FROM WHAT IS RUNNING, not only what is on disk (see upgradePlan). A build already
|
|
722
|
+
// downloaded but never started used to answer "Already on 1.0.161 — nothing newer published"
|
|
723
|
+
// and leave yesterday's 1.0.153 process serving. Reported from the startup scaffold line.
|
|
724
|
+
const { upgradePlan } = await import("./version-check.mjs");
|
|
725
|
+
const liveVersion = String((await ce.probeLocalApi()).body?.version ?? "").trim();
|
|
726
|
+
const plan = upgradePlan({
|
|
727
|
+
published: manifest.version,
|
|
728
|
+
installed: running,
|
|
729
|
+
running: liveVersion,
|
|
730
|
+
ready: present && depsIn,
|
|
731
|
+
});
|
|
732
|
+
if (plan.action === "none") {
|
|
733
|
+
console.log(`\n ${plan.say}\n`);
|
|
724
734
|
process.exit(0);
|
|
725
735
|
}
|
|
726
736
|
// A version already on disk but unusable is a RESUME, not an upgrade — someone whose
|
|
@@ -730,12 +740,12 @@ const currentApiVersion = async () => {
|
|
|
730
740
|
// Caught by running it: "Finishing an interrupted install of 1.0.30" on a box going
|
|
731
741
|
// cleanly from 1.0.29.
|
|
732
742
|
console.log(
|
|
733
|
-
present && !depsIn
|
|
743
|
+
plan.action === "install" && present && !depsIn
|
|
734
744
|
? `\n Finishing an interrupted install of ${manifest.version}`
|
|
735
|
-
: `\n
|
|
745
|
+
: `\n ${plan.say}`
|
|
736
746
|
);
|
|
737
747
|
try {
|
|
738
|
-
if (!present || !depsIn) {
|
|
748
|
+
if (plan.action === "install" && (!present || !depsIn)) {
|
|
739
749
|
await run.downloadApiPackage(manifest, { onProgress: (l) => console.log(` ${l}`) });
|
|
740
750
|
await ce.installApiDeps(dir, {
|
|
741
751
|
onLine: (l) => l && l.length < 160 && console.log(` ${l}`),
|
|
@@ -760,12 +770,19 @@ const currentApiVersion = async () => {
|
|
|
760
770
|
console.log(` Starting ${version}…`);
|
|
761
771
|
await ce.startLocalApi({ apiDir: run.apiInstallDir(version) });
|
|
762
772
|
const health = await ce.waitForApi({});
|
|
773
|
+
// "An API answers" is not "the NEW API answers": an old process that never stopped answers
|
|
774
|
+
// too, and this line once reported the new version while the old one kept serving.
|
|
775
|
+
const serving = String((await ce.probeLocalApi()).body?.version ?? "").trim();
|
|
776
|
+
const wrong = health.ok && Boolean(serving) && serving !== version;
|
|
763
777
|
console.log(
|
|
764
|
-
health.ok
|
|
765
|
-
? ` API
|
|
766
|
-
:
|
|
778
|
+
!health.ok
|
|
779
|
+
? ` API did not come up: ${health.why}\n Log: ${ce.API_LOG_FILE}\n`
|
|
780
|
+
: wrong
|
|
781
|
+
? ` ${serving} is still the one answering on ${ce.LOCAL_API_BASE}, not ${version}.\n` +
|
|
782
|
+
` Stop it (gu-cli api stop), then: gu-cli api start\n`
|
|
783
|
+
: ` API is up on ${ce.LOCAL_API_BASE} (${serving || version})\n`
|
|
767
784
|
);
|
|
768
|
-
process.exit(health.ok ? 0 : 1);
|
|
785
|
+
process.exit(health.ok && !wrong ? 0 : 1);
|
|
769
786
|
}
|
|
770
787
|
|
|
771
788
|
if (sub === "stop") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gu-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.728",
|
|
4
4
|
"description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/version-check.mjs
CHANGED
|
@@ -70,3 +70,37 @@ export function updatesAvailable({ cliRunning, cliLatest, apiRunning, apiLatest
|
|
|
70
70
|
const api = isNewer(apiLatest, apiRunning) ? String(apiLatest) : "";
|
|
71
71
|
return { cli, api, any: Boolean(cli || api) };
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* WHAT `gu api upgrade` SHOULD DO — decided from the version that is RUNNING, not only the one on
|
|
76
|
+
* disk. Pure, so every case is tested.
|
|
77
|
+
*
|
|
78
|
+
* REPORTED: the startup line said "your local gu API is older than this gu — run gu api upgrade",
|
|
79
|
+
* and upgrading did nothing. 1.0.161 was already downloaded (by the in-app updater), so the old
|
|
80
|
+
* check compared "published 1.0.161" with "installed 1.0.161", answered "Already on 1.0.161 —
|
|
81
|
+
* nothing newer published" and exited — while a process started the day before from 1.0.153 kept
|
|
82
|
+
* serving. `gu api status` had learnt this difference long ago; `upgrade` had not.
|
|
83
|
+
*
|
|
84
|
+
* published — the manifest's version installed — the newest usable build on disk
|
|
85
|
+
* running — what /api/health reports ("" when the API is down or too old to say)
|
|
86
|
+
* ready — `installed` has its dependencies (a runnable install)
|
|
87
|
+
* Returns { action: "none" | "install" | "restart", say }:
|
|
88
|
+
* install → download/finish the published build, then restart onto it;
|
|
89
|
+
* restart → nothing to download, but the running process is not the installed build.
|
|
90
|
+
*/
|
|
91
|
+
export function upgradePlan({ published = "", installed = "", running = "", ready = false } = {}) {
|
|
92
|
+
const target = published && (!installed || isNewer(published, installed)) ? published : installed;
|
|
93
|
+
if (target === published && published && (!ready || installed !== published)) {
|
|
94
|
+
return { action: "install", say: `Upgrading the API: ${running || installed || "none"} → ${published}` };
|
|
95
|
+
}
|
|
96
|
+
if (running && (running === target || isNewer(running, target))) {
|
|
97
|
+
// Running something at least as new as anything here — never "restart" onto an older build.
|
|
98
|
+
return { action: "none", say: `Already on ${running} — nothing newer published.` };
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
action: "restart",
|
|
102
|
+
say: running
|
|
103
|
+
? `${target} is installed but ${running} is still running — restarting onto ${target}`
|
|
104
|
+
: `Starting ${target}`,
|
|
105
|
+
};
|
|
106
|
+
}
|