@prestyj/cli 5.3.1 → 5.4.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/dist/app-sidecar.js +117 -6
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +49 -0
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +19 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +1 -1
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/fast-apply-benchmark.d.ts +1 -1
- package/dist/core/fast-apply-benchmark.js +2 -2
- package/dist/core/fast-apply-benchmark.js.map +1 -1
- package/dist/core/progress/rebuild.d.ts +2 -0
- package/dist/core/progress/rebuild.d.ts.map +1 -1
- package/dist/core/progress/rebuild.js +17 -4
- package/dist/core/progress/rebuild.js.map +1 -1
- package/dist/core/progress/rebuild.test.js +20 -4
- package/dist/core/progress/rebuild.test.js.map +1 -1
- package/dist/tools/subagent.d.ts +2 -0
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +189 -193
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.d.ts +2 -0
- package/dist/tools/subagent.test.d.ts.map +1 -0
- package/dist/tools/subagent.test.js +73 -0
- package/dist/tools/subagent.test.js.map +1 -0
- package/dist/ui/components/Footer.js +3 -3
- package/dist/ui/components/Footer.js.map +1 -1
- package/package.json +5 -5
package/dist/app-sidecar.js
CHANGED
|
@@ -29,7 +29,7 @@ import { driveAutopilotCycle, frameAutopilotInjection } from "./core/autopilot-c
|
|
|
29
29
|
import { validateNolanModelPref, effectiveNolanModel, } from "./core/nolan-model.js";
|
|
30
30
|
import { normalizeAutopilotMarkersForHistory, normalizeAppMarkersForHistory, normalizeNolanTurnsForHistory, restoreUserRow, restoreAssistantTexts, autopilotMarkerCopySeed, } from "./core/session-history.js";
|
|
31
31
|
import { AuthStorage } from "./core/auth-storage.js";
|
|
32
|
-
import { MOONSHOT_OAUTH_KEY, XIAOMI_CREDITS_KEY } from "@prestyj/core";
|
|
32
|
+
import { fetchSubscriptionUsage, MOONSHOT_OAUTH_KEY, SubscriptionUsageError, XIAOMI_CREDITS_KEY, } from "@prestyj/core";
|
|
33
33
|
import { loginAnthropic } from "./core/oauth/anthropic.js";
|
|
34
34
|
import { loginOpenAI } from "./core/oauth/openai.js";
|
|
35
35
|
import { loginGemini } from "./core/oauth/gemini.js";
|
|
@@ -509,6 +509,70 @@ async function main() {
|
|
|
509
509
|
for (const ctx of sessions.values())
|
|
510
510
|
ctx.broadcast("progress", { ...snapshot, origin: ctx.id === originId });
|
|
511
511
|
});
|
|
512
|
+
const usageCache = new Map();
|
|
513
|
+
const usageRequests = new Map();
|
|
514
|
+
async function fetchUsageProvider(provider) {
|
|
515
|
+
const displayName = provider === "anthropic" ? "Anthropic" : "Codex";
|
|
516
|
+
if (!(await auth.hasProviderAuth(provider))) {
|
|
517
|
+
return { provider, displayName, connected: false, windows: [], fetchedAt: Date.now() };
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
let credentials = await auth.resolveCredentials(provider);
|
|
521
|
+
try {
|
|
522
|
+
return { ...(await fetchSubscriptionUsage(provider, credentials)), connected: true };
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
// A provider can revoke an access token before its stored expiry. Refresh
|
|
526
|
+
// once on 401, matching inference auth recovery, then retry the usage call.
|
|
527
|
+
if (error instanceof SubscriptionUsageError && error.status === 401) {
|
|
528
|
+
credentials = await auth.resolveCredentials(provider, { forceRefresh: true });
|
|
529
|
+
return { ...(await fetchSubscriptionUsage(provider, credentials)), connected: true };
|
|
530
|
+
}
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
536
|
+
log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
|
|
537
|
+
const connected = await auth.hasProviderAuth(provider);
|
|
538
|
+
return {
|
|
539
|
+
provider,
|
|
540
|
+
displayName,
|
|
541
|
+
connected,
|
|
542
|
+
windows: [],
|
|
543
|
+
fetchedAt: Date.now(),
|
|
544
|
+
error: connected ? "Usage is temporarily unavailable." : undefined,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async function subscriptionUsage(provider) {
|
|
549
|
+
const cached = usageCache.get(provider);
|
|
550
|
+
if (cached && cached.expiresAt > Date.now())
|
|
551
|
+
return cached.result;
|
|
552
|
+
const inFlight = usageRequests.get(provider);
|
|
553
|
+
if (inFlight)
|
|
554
|
+
return inFlight;
|
|
555
|
+
const request = fetchUsageProvider(provider);
|
|
556
|
+
usageRequests.set(provider, request);
|
|
557
|
+
try {
|
|
558
|
+
const result = await request;
|
|
559
|
+
// Anthropic can return utilization before it assigns reset timestamps
|
|
560
|
+
// (notably before the account's first active request). Retry that partial
|
|
561
|
+
// snapshot quickly; complete snapshots keep the normal one-minute cache.
|
|
562
|
+
const missingReset = result.connected &&
|
|
563
|
+
result.windows.length > 0 &&
|
|
564
|
+
result.windows.some((window) => window.resetsAt === undefined);
|
|
565
|
+
usageCache.set(provider, {
|
|
566
|
+
result,
|
|
567
|
+
expiresAt: Date.now() + (missingReset ? 10_000 : 60_000),
|
|
568
|
+
});
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
finally {
|
|
572
|
+
if (usageRequests.get(provider) === request)
|
|
573
|
+
usageRequests.delete(provider);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
512
576
|
/** Resolve the target session id: the `x-gg-session` header, else a
|
|
513
577
|
* `?session=` query param (used by the SSE /events connection). */
|
|
514
578
|
function sessionIdFromReq(req, url) {
|
|
@@ -583,6 +647,25 @@ async function main() {
|
|
|
583
647
|
daemonJson(res, 200, progress.snapshot());
|
|
584
648
|
return;
|
|
585
649
|
}
|
|
650
|
+
// Subscription quota is account-wide, not project/session-specific. OAuth
|
|
651
|
+
// tokens stay in this daemon; only the active provider's normalized snapshot
|
|
652
|
+
// reaches the webview.
|
|
653
|
+
if (method === "GET" && (url === "/usage" || url.startsWith("/usage?"))) {
|
|
654
|
+
void (async () => {
|
|
655
|
+
const provider = new URL(url, `http://${host}`).searchParams.get("provider");
|
|
656
|
+
if (provider !== "anthropic" && provider !== "openai") {
|
|
657
|
+
daemonJson(res, 400, { error: "unsupported usage provider" });
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
daemonJson(res, 200, await subscriptionUsage(provider));
|
|
661
|
+
})().catch((error) => {
|
|
662
|
+
log("ERROR", "app-sidecar", "subscription usage request failed", {
|
|
663
|
+
message: error instanceof Error ? error.message : String(error),
|
|
664
|
+
});
|
|
665
|
+
daemonJson(res, 500, { error: "Usage is temporarily unavailable." });
|
|
666
|
+
});
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
586
669
|
// ── Per-session delegation ───────────────────────────────────────────
|
|
587
670
|
const id = sessionIdFromReq(req, url);
|
|
588
671
|
const ctx = id ? sessions.get(id) : undefined;
|
|
@@ -788,6 +871,32 @@ async function createSession(deps, opts) {
|
|
|
788
871
|
for (const c of clients)
|
|
789
872
|
c.res.write(frame);
|
|
790
873
|
}
|
|
874
|
+
// Replace CLI-specific guidance (slash commands, CLI tool names) with
|
|
875
|
+
// desktop-app equivalents so the webview never shows "run ezcoder login".
|
|
876
|
+
// Applied to BOTH the message and guidance fields — the auth "Not logged in…
|
|
877
|
+
// Run "ezcoder login"" string lives in `message`, not `guidance`.
|
|
878
|
+
function desktopGuidance(guidance) {
|
|
879
|
+
return (guidance
|
|
880
|
+
// Auth: `Run "ezcoder login"` / `Run 'ezcoder login'` / `Run `ezcoder login``
|
|
881
|
+
// (any quote style, or none) → button. Do this first so the whole phrase
|
|
882
|
+
// is rewritten cleanly instead of leaving a dangling `Run "…"`.
|
|
883
|
+
.replaceAll(/Run ["'`]?ezcoder login["'`]?/gi, "Use the Login to AI Providers button")
|
|
884
|
+
// Any remaining bare mention.
|
|
885
|
+
.replaceAll(/ezcoder login/gi, "the Login to AI Providers button")
|
|
886
|
+
// /compact: the app has NO manual compact command or button — it only
|
|
887
|
+
// auto-compacts (and now auto-recovers on overflow). If this guidance is
|
|
888
|
+
// reached, auto-compaction already couldn't reduce enough, so the only
|
|
889
|
+
// real affordance is a fresh session. Don't tell the user to run a
|
|
890
|
+
// command that doesn't exist in the app.
|
|
891
|
+
.replaceAll(/Run \/compact to shrink history, or start a new session\./gi, "Start a new session to reset the context.")
|
|
892
|
+
// /model: handle each phrasing pattern
|
|
893
|
+
.replaceAll(/switch to claude-fable-5 with \/model/gi, "switch to claude-fable-5 using the model selector")
|
|
894
|
+
.replaceAll(/Switch with \/model\./gi, "Switch using the model selector.")
|
|
895
|
+
.replaceAll(/try a different model with \/model\./gi, "try a different model using the model selector.")
|
|
896
|
+
.replaceAll(/Use \/model to switch/gi, "Use the model selector to switch")
|
|
897
|
+
// /help
|
|
898
|
+
.replaceAll(/see \/help/gi, "check the help menu"));
|
|
899
|
+
}
|
|
791
900
|
// Turn any thrown value into the same clear headline/message/guidance shape
|
|
792
901
|
// the TUI shows (see gg-ai's formatError) instead of a bare `err.message`, log
|
|
793
902
|
// the full detail, and broadcast it under `type` ("error" or "nolan_error").
|
|
@@ -796,18 +905,20 @@ async function createSession(deps, opts) {
|
|
|
796
905
|
// context that the CLI has always given.
|
|
797
906
|
function broadcastError(type, logLabel, err) {
|
|
798
907
|
const f = formatError(err);
|
|
908
|
+
const message = f.message ? desktopGuidance(f.message) : undefined;
|
|
909
|
+
const guidance = desktopGuidance(f.guidance);
|
|
799
910
|
log("ERROR", "app-sidecar", logLabel, {
|
|
800
911
|
headline: f.headline,
|
|
801
912
|
source: f.source,
|
|
802
|
-
...(
|
|
913
|
+
...(message ? { message } : {}),
|
|
803
914
|
...(f.provider ? { provider: f.provider } : {}),
|
|
804
915
|
...(f.statusCode != null ? { statusCode: String(f.statusCode) } : {}),
|
|
805
916
|
...(f.requestId ? { requestId: f.requestId } : {}),
|
|
806
917
|
});
|
|
807
918
|
broadcast(type, {
|
|
808
919
|
headline: f.headline,
|
|
809
|
-
...(
|
|
810
|
-
guidance
|
|
920
|
+
...(message ? { message } : {}),
|
|
921
|
+
guidance,
|
|
811
922
|
...(f.provider ? { provider: f.provider } : {}),
|
|
812
923
|
...(f.statusCode != null ? { statusCode: f.statusCode } : {}),
|
|
813
924
|
...(f.resetsAt != null ? { resetsAt: f.resetsAt } : {}),
|
|
@@ -818,8 +929,8 @@ async function createSession(deps, opts) {
|
|
|
818
929
|
.persistAppMarker("error", {
|
|
819
930
|
scope: type,
|
|
820
931
|
headline: f.headline,
|
|
821
|
-
...(
|
|
822
|
-
guidance
|
|
932
|
+
...(message ? { message } : {}),
|
|
933
|
+
guidance,
|
|
823
934
|
})
|
|
824
935
|
.catch(() => { });
|
|
825
936
|
}
|