@kenkaiiii/ggcoder 5.74.3 → 5.75.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/README.md +1 -1
- package/assets/skills/bulletproof/SKILL.md +1 -1
- package/assets/skills/clarify/SKILL.md +1 -1
- package/assets/skills/code-review/SKILL.md +1 -1
- package/assets/skills/compliance-guard/SKILL.md +1 -1
- package/assets/skills/durable/SKILL.md +1 -1
- package/assets/skills/evidence-led-ui/SKILL.md +1 -1
- package/assets/skills/lean/SKILL.md +1 -1
- package/assets/skills/refactoring/SKILL.md +1 -1
- package/assets/skills/root-cause/SKILL.md +1 -1
- package/assets/skills/shared-language/SKILL.md +1 -1
- package/assets/skills/tdd/SKILL.md +1 -1
- package/dist/app-sidecar/app-settings.d.ts +65 -0
- package/dist/app-sidecar/app-settings.js +147 -0
- package/dist/app-sidecar/app-settings.js.map +1 -0
- package/dist/app-sidecar/attachments.d.ts +11 -0
- package/dist/app-sidecar/attachments.js +47 -0
- package/dist/app-sidecar/attachments.js.map +1 -0
- package/dist/app-sidecar/auth-routes.d.ts +3 -0
- package/dist/app-sidecar/auth-routes.js +290 -0
- package/dist/app-sidecar/auth-routes.js.map +1 -0
- package/dist/app-sidecar/autopilot-runner.d.ts +61 -0
- package/dist/app-sidecar/autopilot-runner.js +367 -0
- package/dist/app-sidecar/autopilot-runner.js.map +1 -0
- package/dist/app-sidecar/file-search.d.ts +15 -0
- package/dist/app-sidecar/file-search.js +103 -0
- package/dist/app-sidecar/file-search.js.map +1 -0
- package/dist/app-sidecar/hf-pull-manager.d.ts +20 -0
- package/dist/app-sidecar/hf-pull-manager.js +224 -0
- package/dist/app-sidecar/hf-pull-manager.js.map +1 -0
- package/dist/app-sidecar/history-routes.d.ts +3 -0
- package/dist/app-sidecar/history-routes.js +375 -0
- package/dist/app-sidecar/history-routes.js.map +1 -0
- package/dist/app-sidecar/history.d.ts +67 -0
- package/dist/app-sidecar/history.js +23 -0
- package/dist/app-sidecar/history.js.map +1 -0
- package/dist/app-sidecar/http.d.ts +5 -0
- package/dist/app-sidecar/http.js +27 -0
- package/dist/app-sidecar/http.js.map +1 -0
- package/dist/app-sidecar/integration-routes.d.ts +3 -0
- package/dist/app-sidecar/integration-routes.js +286 -0
- package/dist/app-sidecar/integration-routes.js.map +1 -0
- package/dist/app-sidecar/json-mode.d.ts +10 -0
- package/dist/app-sidecar/json-mode.js +73 -0
- package/dist/app-sidecar/json-mode.js.map +1 -0
- package/dist/app-sidecar/keep-awake.d.ts +4 -0
- package/dist/app-sidecar/keep-awake.js +5 -0
- package/dist/app-sidecar/keep-awake.js.map +1 -0
- package/dist/app-sidecar/ken-context.d.ts +20 -0
- package/dist/app-sidecar/ken-context.js +55 -0
- package/dist/app-sidecar/ken-context.js.map +1 -0
- package/dist/app-sidecar/mcp-routes.d.ts +3 -0
- package/dist/app-sidecar/mcp-routes.js +185 -0
- package/dist/app-sidecar/mcp-routes.js.map +1 -0
- package/dist/app-sidecar/mcp-rows.d.ts +31 -0
- package/dist/app-sidecar/mcp-rows.js +77 -0
- package/dist/app-sidecar/mcp-rows.js.map +1 -0
- package/dist/app-sidecar/progress-manager.d.ts +10 -0
- package/dist/app-sidecar/progress-manager.js +116 -0
- package/dist/app-sidecar/progress-manager.js.map +1 -0
- package/dist/app-sidecar/prompt-routes.d.ts +3 -0
- package/dist/app-sidecar/prompt-routes.js +354 -0
- package/dist/app-sidecar/prompt-routes.js.map +1 -0
- package/dist/app-sidecar/repo-polls.d.ts +15 -0
- package/dist/app-sidecar/repo-polls.js +25 -0
- package/dist/app-sidecar/repo-polls.js.map +1 -0
- package/dist/app-sidecar/route-context.d.ts +145 -0
- package/dist/app-sidecar/route-context.js +2 -0
- package/dist/app-sidecar/route-context.js.map +1 -0
- package/dist/app-sidecar/run-control-routes.d.ts +3 -0
- package/dist/app-sidecar/run-control-routes.js +430 -0
- package/dist/app-sidecar/run-control-routes.js.map +1 -0
- package/dist/app-sidecar/session-routes.d.ts +3 -0
- package/dist/app-sidecar/session-routes.js +115 -0
- package/dist/app-sidecar/session-routes.js.map +1 -0
- package/dist/app-sidecar/session-types.d.ts +39 -0
- package/dist/app-sidecar/session-types.js +5 -0
- package/dist/app-sidecar/session-types.js.map +1 -0
- package/dist/app-sidecar/task-routes.d.ts +3 -0
- package/dist/app-sidecar/task-routes.js +149 -0
- package/dist/app-sidecar/task-routes.js.map +1 -0
- package/dist/app-sidecar/usage.d.ts +19 -0
- package/dist/app-sidecar/usage.js +153 -0
- package/dist/app-sidecar/usage.js.map +1 -0
- package/dist/app-sidecar/workspace-routes.d.ts +3 -0
- package/dist/app-sidecar/workspace-routes.js +244 -0
- package/dist/app-sidecar/workspace-routes.js.map +1 -0
- package/dist/app-sidecar.js +467 -3775
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/core/acceptance-checks.js +3 -9
- package/dist/core/acceptance-checks.js.map +1 -1
- package/dist/core/agent-session/helpers.d.ts +26 -0
- package/dist/core/agent-session/helpers.js +47 -0
- package/dist/core/agent-session/helpers.js.map +1 -0
- package/dist/core/agent-session/mcp-tools.d.ts +68 -0
- package/dist/core/agent-session/mcp-tools.js +195 -0
- package/dist/core/agent-session/mcp-tools.js.map +1 -0
- package/dist/core/agent-session/run-loop-monitor.d.ts +63 -0
- package/dist/core/agent-session/run-loop-monitor.js +164 -0
- package/dist/core/agent-session/run-loop-monitor.js.map +1 -0
- package/dist/core/agent-session/transcript-markers.d.ts +57 -0
- package/dist/core/agent-session/transcript-markers.js +166 -0
- package/dist/core/agent-session/transcript-markers.js.map +1 -0
- package/dist/core/agent-session/verification-tracker.d.ts +46 -0
- package/dist/core/agent-session/verification-tracker.js +246 -0
- package/dist/core/agent-session/verification-tracker.js.map +1 -0
- package/dist/core/agent-session.d.ts +21 -87
- package/dist/core/agent-session.js +141 -712
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.js +3 -3
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/bundled-agents.js +6 -6
- package/dist/core/bundled-agents.js.map +1 -1
- package/dist/core/checklist-git.d.ts +19 -0
- package/dist/core/checklist-git.js +54 -0
- package/dist/core/checklist-git.js.map +1 -0
- package/dist/core/checklist-items.d.ts +10 -1
- package/dist/core/checklist-items.js +22 -0
- package/dist/core/checklist-items.js.map +1 -1
- package/dist/core/checklist-prompt.d.ts +5 -1
- package/dist/core/checklist-prompt.js +16 -5
- package/dist/core/checklist-prompt.js.map +1 -1
- package/dist/core/checklist-snapshot.d.ts +5 -1
- package/dist/core/checklist-snapshot.js +19 -2
- package/dist/core/checklist-snapshot.js.map +1 -1
- package/dist/core/context-limits.js +1 -1
- package/dist/core/context-limits.js.map +1 -1
- package/dist/core/skills.js +6 -9
- package/dist/core/skills.js.map +1 -1
- package/dist/core/style-packs/index.js +2 -11
- package/dist/core/style-packs/index.js.map +1 -1
- package/dist/core/style-packs/packs.d.ts +3 -12
- package/dist/core/style-packs/packs.js +132 -173
- package/dist/core/style-packs/packs.js.map +1 -1
- package/dist/system-prompt.js +55 -78
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/ask-user.js +13 -74
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/bash.js +13 -78
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/checklist.d.ts +2 -10
- package/dist/tools/checklist.js +7 -39
- package/dist/tools/checklist.js.map +1 -1
- package/dist/tools/code-nav.js +7 -29
- package/dist/tools/code-nav.js.map +1 -1
- package/dist/tools/edit.d.ts +3 -9
- package/dist/tools/edit.js +92 -65
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/enter-plan.js +2 -7
- package/dist/tools/enter-plan.js.map +1 -1
- package/dist/tools/exit-plan.js +2 -3
- package/dist/tools/exit-plan.js.map +1 -1
- package/dist/tools/find.js +3 -4
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/grep.js +6 -18
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/ls.js +3 -3
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/prompt-hints.js +34 -47
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read.js +8 -20
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/search-code.js +4 -12
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/skill.js +13 -7
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.js +7 -22
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent.js +4 -7
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/task-output.js +4 -15
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.js +5 -17
- package/dist/tools/task-send.js.map +1 -1
- package/dist/tools/task-stop.js +2 -2
- package/dist/tools/task-stop.js.map +1 -1
- package/dist/tools/tool-search.js +2 -7
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-tiers.d.ts +17 -4
- package/dist/tools/tool-tiers.js +38 -15
- package/dist/tools/tool-tiers.js.map +1 -1
- package/dist/tools/web-fetch.js +7 -33
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.js +3 -4
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/components/Footer.js +1 -2
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/utils/shared-poll.d.ts +37 -0
- package/dist/utils/shared-poll.js +104 -0
- package/dist/utils/shared-poll.js.map +1 -0
- package/package.json +7 -4
- package/dist/core/api-benchmark.d.ts +0 -63
- package/dist/core/api-benchmark.js +0 -381
- package/dist/core/api-benchmark.js.map +0 -1
- package/dist/core/bash-spawn-benchmark.d.ts +0 -1
- package/dist/core/bash-spawn-benchmark.js +0 -139
- package/dist/core/bash-spawn-benchmark.js.map +0 -1
- package/dist/core/cache-warm-benchmark.d.ts +0 -21
- package/dist/core/cache-warm-benchmark.js +0 -170
- package/dist/core/cache-warm-benchmark.js.map +0 -1
- package/dist/core/fast-apply-benchmark.d.ts +0 -25
- package/dist/core/fast-apply-benchmark.js +0 -284
- package/dist/core/fast-apply-benchmark.js.map +0 -1
- package/dist/core/hashline-edit-benchmark.d.ts +0 -88
- package/dist/core/hashline-edit-benchmark.js +0 -518
- package/dist/core/hashline-edit-benchmark.js.map +0 -1
- package/dist/core/semantic-search-benchmark.d.ts +0 -36
- package/dist/core/semantic-search-benchmark.js +0 -211
- package/dist/core/semantic-search-benchmark.js.map +0 -1
- package/dist/core/speed-benchmark.d.ts +0 -132
- package/dist/core/speed-benchmark.js +0 -410
- package/dist/core/speed-benchmark.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { loadTasksSync, saveTasksSync, pruneDoneTasksSync } from "../core/tasks-store.js";
|
|
2
|
+
import { readChecklistSnapshot } from "../core/checklist-snapshot.js";
|
|
3
|
+
import { log } from "../core/logger.js";
|
|
4
|
+
import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "../core/radio.js";
|
|
5
|
+
import { readBody, json } from "./http.js";
|
|
6
|
+
export function handleTaskRoutes(ctx, req, res, url, method) {
|
|
7
|
+
if (method === "GET" && url === "/tasks") {
|
|
8
|
+
json(res, 200, { tasks: pruneDoneTasksSync(ctx.cwd) });
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (method === "GET" && url === "/checklist") {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
res.once("close", () => controller.abort());
|
|
14
|
+
void (async () => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await readChecklistSnapshot(ctx.cwd, new Date(), controller.signal);
|
|
17
|
+
if (res.destroyed)
|
|
18
|
+
return;
|
|
19
|
+
if (!result.ok) {
|
|
20
|
+
log("WARN", "app-sidecar", "checklist read failed", { error: result.error });
|
|
21
|
+
json(res, 500, { error: result.error });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
json(res, 200, result.value);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
log("WARN", "app-sidecar", "checklist snapshot failed", { error: String(error) });
|
|
28
|
+
if (!res.destroyed)
|
|
29
|
+
json(res, 500, { error: "Could not read the project checklist" });
|
|
30
|
+
}
|
|
31
|
+
})();
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
// ── Radio (app-wide) ──────────────────────────────────────
|
|
35
|
+
// Radio is now APP-WIDE: all windows share one daemon process, and the
|
|
36
|
+
// player lives in `core/radio.ts` module-level singletons (one stream for
|
|
37
|
+
// the whole app). Any window's /radio reads/controls that single stream —
|
|
38
|
+
// starting a station in one window replaces whatever was playing, and every
|
|
39
|
+
// window's footer reflects the same `current`. This intentionally prevents
|
|
40
|
+
// duplicate audio across windows (the original per-window goal), now for
|
|
41
|
+
// free. (To restore per-window radio, key playback by sessionId.)
|
|
42
|
+
if (method === "GET" && url === "/radio") {
|
|
43
|
+
json(res, 200, {
|
|
44
|
+
stations: RADIO_STATIONS,
|
|
45
|
+
current: getCurrentStation(),
|
|
46
|
+
volume: getRadioVolume(),
|
|
47
|
+
});
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (method === "POST" && url === "/radio/volume") {
|
|
51
|
+
void readBody(req, res).then((raw) => {
|
|
52
|
+
if (raw === null)
|
|
53
|
+
return;
|
|
54
|
+
let volume;
|
|
55
|
+
try {
|
|
56
|
+
volume = Number(JSON.parse(raw).volume);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!Number.isFinite(volume)) {
|
|
63
|
+
json(res, 400, { error: "volume must be a number" });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const result = setRadioVolume(volume);
|
|
67
|
+
if (!result.ok) {
|
|
68
|
+
json(res, 400, { error: result.error ?? "Radio volume failed to update." });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
json(res, 200, { current: getCurrentStation(), volume: getRadioVolume() });
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (method === "POST" && url === "/radio") {
|
|
76
|
+
void readBody(req, res).then((raw) => {
|
|
77
|
+
if (raw === null)
|
|
78
|
+
return;
|
|
79
|
+
let station;
|
|
80
|
+
try {
|
|
81
|
+
station = JSON.parse(raw).station ?? "";
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!station || station === "off") {
|
|
88
|
+
stopRadio();
|
|
89
|
+
json(res, 200, { current: null });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const result = playRadio(station);
|
|
93
|
+
if (!result.ok) {
|
|
94
|
+
json(res, 400, { error: result.error ?? "Radio failed to start." });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
json(res, 200, { current: getCurrentStation() });
|
|
98
|
+
});
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (method === "POST" && url === "/tasks/run") {
|
|
102
|
+
void readBody(req, res).then((raw) => {
|
|
103
|
+
if (raw === null)
|
|
104
|
+
return;
|
|
105
|
+
let id;
|
|
106
|
+
let all;
|
|
107
|
+
try {
|
|
108
|
+
const body = JSON.parse(raw);
|
|
109
|
+
id = body.id ?? null;
|
|
110
|
+
all = Boolean(body.all);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (ctx.running) {
|
|
117
|
+
json(res, 409, { error: "cannot run a task while the agent is running" });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
json(res, 202, { accepted: true });
|
|
121
|
+
void ctx.runTasks(id, all);
|
|
122
|
+
});
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (method === "POST" && url === "/tasks/delete") {
|
|
126
|
+
void readBody(req, res).then((raw) => {
|
|
127
|
+
if (raw === null)
|
|
128
|
+
return;
|
|
129
|
+
let id;
|
|
130
|
+
try {
|
|
131
|
+
id = JSON.parse(raw).id ?? "";
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!id.trim()) {
|
|
138
|
+
json(res, 400, { error: "missing task id" });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const remaining = loadTasksSync(ctx.cwd).filter((t) => t.id !== id && !t.id.startsWith(id));
|
|
142
|
+
saveTasksSync(ctx.cwd, remaining);
|
|
143
|
+
json(res, 200, { tasks: remaining });
|
|
144
|
+
});
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=task-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-routes.js","sourceRoot":"","sources":["../../src/app-sidecar/task-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type SubscriptionUsageProvider, type SubscriptionUsageSnapshot } from "@kenkaiiii/gg-core";
|
|
2
|
+
import type { AuthStorage } from "../core/auth-storage.js";
|
|
3
|
+
export type UsageResult = (SubscriptionUsageSnapshot & {
|
|
4
|
+
connected: true;
|
|
5
|
+
error?: never;
|
|
6
|
+
stale?: boolean;
|
|
7
|
+
}) | {
|
|
8
|
+
provider: SubscriptionUsageProvider;
|
|
9
|
+
displayName: string;
|
|
10
|
+
connected: boolean;
|
|
11
|
+
windows: [];
|
|
12
|
+
fetchedAt: number;
|
|
13
|
+
error?: string;
|
|
14
|
+
stale?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** Daemon-level subscription-usage fetcher with caching, 429 backoff and last-good replay. */
|
|
17
|
+
export declare function createUsageService(auth: AuthStorage): {
|
|
18
|
+
subscriptionUsage: (provider: SubscriptionUsageProvider) => Promise<UsageResult>;
|
|
19
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { fetchSubscriptionUsage, SubscriptionUsageError, oauthStorageKey, } from "@kenkaiiii/gg-core";
|
|
2
|
+
import { log } from "../core/logger.js";
|
|
3
|
+
/** Daemon-level subscription-usage fetcher with caching, 429 backoff and last-good replay. */
|
|
4
|
+
export function createUsageService(auth) {
|
|
5
|
+
const usageCache = new Map();
|
|
6
|
+
const usageRequests = new Map();
|
|
7
|
+
// Last snapshot that actually carried windows, per provider. Replayed while a
|
|
8
|
+
// fetch is failing so the title meter never blinks out of existence. Bounded
|
|
9
|
+
// by USAGE_LAST_GOOD_MAX_AGE_MS — a provider that never recovers must stop
|
|
10
|
+
// reporting rather than freeze a percentage (and a long-past reset time) on
|
|
11
|
+
// screen forever. The 429 backoff alone runs to 24h, far past any usefulness.
|
|
12
|
+
const usageLastGood = new Map();
|
|
13
|
+
const USAGE_LAST_GOOD_MAX_AGE_MS = 30 * 60_000;
|
|
14
|
+
// 429 backoff: quota endpoints are auxiliary UI data. Honor Retry-After when
|
|
15
|
+
// provided; otherwise retain the unavailable snapshot for 30 minutes. Clamp
|
|
16
|
+
// the provider value so a malformed header can neither hammer the endpoint
|
|
17
|
+
// nor suppress usage data forever.
|
|
18
|
+
const usageRateLimitedUntil = new Map();
|
|
19
|
+
// Providers currently in a logged rate-limit episode (cleared on success).
|
|
20
|
+
const usageRateLimitLogged = new Set();
|
|
21
|
+
const USAGE_RATE_LIMIT_FALLBACK_BACKOFF_MS = 30 * 60_000;
|
|
22
|
+
const USAGE_RATE_LIMIT_MIN_BACKOFF_MS = 60_000;
|
|
23
|
+
const USAGE_RATE_LIMIT_MAX_BACKOFF_MS = 24 * 60 * 60_000;
|
|
24
|
+
function clearUsageRateLimit(provider) {
|
|
25
|
+
usageRateLimitedUntil.delete(provider);
|
|
26
|
+
if (usageRateLimitLogged.delete(provider)) {
|
|
27
|
+
log("INFO", "app-sidecar", "subscription usage recovered", { provider });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function fetchUsageProvider(provider) {
|
|
31
|
+
const displayName = provider === "anthropic" ? "Anthropic" : provider === "openai" ? "Codex" : "Kimi";
|
|
32
|
+
// Kimi plan usage is tracked on the OAuth credential specifically — the
|
|
33
|
+
// Moonshot platform API key is metered per-token, not per plan window.
|
|
34
|
+
const authKey = oauthStorageKey(provider) ?? provider;
|
|
35
|
+
if (!(await auth.hasProviderAuth(authKey))) {
|
|
36
|
+
// Logged out: drop the replay cache so a later login on a DIFFERENT
|
|
37
|
+
// account can never inherit the previous one's numbers.
|
|
38
|
+
usageLastGood.delete(provider);
|
|
39
|
+
return { provider, displayName, connected: false, windows: [], fetchedAt: Date.now() };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
let credentials = await auth.resolveCredentials(authKey);
|
|
43
|
+
try {
|
|
44
|
+
const snapshot = {
|
|
45
|
+
...(await fetchSubscriptionUsage(provider, credentials)),
|
|
46
|
+
connected: true,
|
|
47
|
+
};
|
|
48
|
+
clearUsageRateLimit(provider);
|
|
49
|
+
return snapshot;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// A provider can revoke an access token before its stored expiry. Refresh
|
|
53
|
+
// once on 401, matching inference auth recovery, then retry the usage call.
|
|
54
|
+
if (error instanceof SubscriptionUsageError && error.status === 401) {
|
|
55
|
+
// Name the rejected token. This poller runs on a timer alongside live
|
|
56
|
+
// agent runs, so an unconditional refresh here would invalidate the
|
|
57
|
+
// token those runs are holding and fail them mid-turn — a background
|
|
58
|
+
// status check must never be able to log out the foreground.
|
|
59
|
+
credentials = await auth.resolveCredentials(authKey, {
|
|
60
|
+
forceRefresh: true,
|
|
61
|
+
rejectedToken: credentials.accessToken,
|
|
62
|
+
});
|
|
63
|
+
const snapshot = {
|
|
64
|
+
...(await fetchSubscriptionUsage(provider, credentials)),
|
|
65
|
+
connected: true,
|
|
66
|
+
};
|
|
67
|
+
clearUsageRateLimit(provider);
|
|
68
|
+
return snapshot;
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
+
if (error instanceof SubscriptionUsageError && error.status === 429) {
|
|
76
|
+
const backoffMs = Math.min(USAGE_RATE_LIMIT_MAX_BACKOFF_MS, Math.max(USAGE_RATE_LIMIT_MIN_BACKOFF_MS, error.retryAfterMs ?? USAGE_RATE_LIMIT_FALLBACK_BACKOFF_MS));
|
|
77
|
+
usageRateLimitedUntil.set(provider, Date.now() + backoffMs);
|
|
78
|
+
// Rate limits on this auxiliary endpoint are expected (every GG process
|
|
79
|
+
// on the machine polls it) and already handled by the backoff plus the
|
|
80
|
+
// last-good replay. Log the transition, not every retry.
|
|
81
|
+
if (!usageRateLimitLogged.has(provider)) {
|
|
82
|
+
usageRateLimitLogged.add(provider);
|
|
83
|
+
log("INFO", "app-sidecar", "subscription usage rate-limited; backing off", {
|
|
84
|
+
provider,
|
|
85
|
+
backoffMs: String(backoffMs),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
log("WARN", "app-sidecar", "subscription usage fetch failed", { provider, message });
|
|
91
|
+
}
|
|
92
|
+
const connected = await auth.hasProviderAuth(authKey);
|
|
93
|
+
// Transient failures (notably the 429s these auxiliary quota endpoints
|
|
94
|
+
// hand out) must not blank the meter. Keep serving the last good
|
|
95
|
+
// snapshot, flagged `stale`, so the bar stays put instead of flickering
|
|
96
|
+
// out for the whole backoff window and back in on the next success.
|
|
97
|
+
// Past the max age it's dropped — no data beats confidently wrong data.
|
|
98
|
+
const lastGood = usageLastGood.get(provider);
|
|
99
|
+
if (lastGood && Date.now() - lastGood.fetchedAt >= USAGE_LAST_GOOD_MAX_AGE_MS) {
|
|
100
|
+
usageLastGood.delete(provider);
|
|
101
|
+
}
|
|
102
|
+
else if (connected && lastGood) {
|
|
103
|
+
return { ...lastGood, stale: true };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
provider,
|
|
107
|
+
displayName,
|
|
108
|
+
connected,
|
|
109
|
+
windows: [],
|
|
110
|
+
fetchedAt: Date.now(),
|
|
111
|
+
error: connected ? "Usage is temporarily unavailable." : undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function subscriptionUsage(provider) {
|
|
116
|
+
const cached = usageCache.get(provider);
|
|
117
|
+
if (cached && cached.expiresAt > Date.now())
|
|
118
|
+
return cached.result;
|
|
119
|
+
const inFlight = usageRequests.get(provider);
|
|
120
|
+
if (inFlight)
|
|
121
|
+
return inFlight;
|
|
122
|
+
const request = fetchUsageProvider(provider);
|
|
123
|
+
usageRequests.set(provider, request);
|
|
124
|
+
try {
|
|
125
|
+
const result = await request;
|
|
126
|
+
// Never re-store a replay — it would keep its original `fetchedAt`, but
|
|
127
|
+
// writing it back muddies the "last GOOD" contract for no gain.
|
|
128
|
+
if (result.connected && !result.error && !result.stale && result.windows.length > 0) {
|
|
129
|
+
usageLastGood.set(provider, result);
|
|
130
|
+
}
|
|
131
|
+
// Anthropic can return utilization before it assigns reset timestamps
|
|
132
|
+
// (notably before the account's first active request). Retry that partial
|
|
133
|
+
// snapshot quickly; complete snapshots keep the normal one-minute cache.
|
|
134
|
+
const missingReset = result.connected &&
|
|
135
|
+
result.windows.length > 0 &&
|
|
136
|
+
result.windows.some((window) => window.resetsAt === undefined);
|
|
137
|
+
const rateLimitedUntil = usageRateLimitedUntil.get(provider) ?? 0;
|
|
138
|
+
usageCache.set(provider, {
|
|
139
|
+
result,
|
|
140
|
+
expiresAt: rateLimitedUntil > Date.now()
|
|
141
|
+
? rateLimitedUntil
|
|
142
|
+
: Date.now() + (missingReset ? 10_000 : 60_000),
|
|
143
|
+
});
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
if (usageRequests.get(provider) === request)
|
|
148
|
+
usageRequests.delete(provider);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { subscriptionUsage };
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/app-sidecar/usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { sessionToMarkdown, defaultExportFilename } from "../core/session-export.js";
|
|
4
|
+
import { installPlugin, listInstalledPlugins, removePlugin, } from "../core/extensions/plugin-bundles.js";
|
|
5
|
+
import { discoverProjects } from "../core/project-discovery.js";
|
|
6
|
+
import { listSidecarSessions } from "../app-sidecar-sessions.js";
|
|
7
|
+
import { log } from "../core/logger.js";
|
|
8
|
+
import { readBody, json } from "./http.js";
|
|
9
|
+
import { parseToolDetail } from "./history.js";
|
|
10
|
+
import { searchProjectFiles } from "./file-search.js";
|
|
11
|
+
import { appSettingsFile, loadAppSettings, saveAppSettings, isValidProjectName, } from "./app-settings.js";
|
|
12
|
+
export function handleWorkspaceRoutes(ctx, req, res, url, method) {
|
|
13
|
+
if (method === "GET" && url === "/plugins") {
|
|
14
|
+
void listInstalledPlugins(ctx.paths.extensionsDir)
|
|
15
|
+
.then((plugins) => json(res, 200, { plugins }))
|
|
16
|
+
.catch((error) => {
|
|
17
|
+
json(res, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
18
|
+
});
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (method === "POST" && url === "/plugins/install") {
|
|
22
|
+
void readBody(req, res).then(async (raw) => {
|
|
23
|
+
if (raw === null)
|
|
24
|
+
return;
|
|
25
|
+
try {
|
|
26
|
+
const bundlePath = JSON.parse(raw).bundlePath;
|
|
27
|
+
if (typeof bundlePath !== "string" || !path.isAbsolute(bundlePath)) {
|
|
28
|
+
json(res, 400, { error: "bundlePath must be an absolute path" });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const plugin = await installPlugin(bundlePath, ctx.paths.extensionsDir);
|
|
32
|
+
json(res, 200, { plugin, restartRequired: true });
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
json(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (method === "DELETE" && url.startsWith("/plugins/")) {
|
|
41
|
+
const pluginId = decodeURIComponent(url.slice("/plugins/".length));
|
|
42
|
+
void removePlugin(pluginId, ctx.paths.extensionsDir)
|
|
43
|
+
.then(() => json(res, 200, { removed: pluginId, restartRequired: true }))
|
|
44
|
+
.catch((error) => {
|
|
45
|
+
json(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
46
|
+
});
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (method === "GET" && url === "/settings") {
|
|
50
|
+
// `configured` is true only when the user explicitly saved a projects root
|
|
51
|
+
// (the gg-app.json file exists with a value) — not when we fall back to the
|
|
52
|
+
// default. The home screen gates "Your Projects" on this.
|
|
53
|
+
void (async () => {
|
|
54
|
+
const s = await loadAppSettings();
|
|
55
|
+
let configured;
|
|
56
|
+
try {
|
|
57
|
+
const raw = JSON.parse(await fs.readFile(appSettingsFile(), "utf-8"));
|
|
58
|
+
configured = typeof raw.projectsRoot === "string" && raw.projectsRoot.trim().length > 0;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
configured = false;
|
|
62
|
+
}
|
|
63
|
+
// Only projectsRoot + configured flag are webview-facing; the
|
|
64
|
+
// per-project model map is internal persistence, never shipped out.
|
|
65
|
+
json(res, 200, { projectsRoot: s.projectsRoot, configured });
|
|
66
|
+
})();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (method === "POST" && url === "/settings") {
|
|
70
|
+
void readBody(req, res).then(async (raw) => {
|
|
71
|
+
if (raw === null)
|
|
72
|
+
return;
|
|
73
|
+
let projectsRoot;
|
|
74
|
+
try {
|
|
75
|
+
projectsRoot = JSON.parse(raw).projectsRoot ?? "";
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!projectsRoot.trim()) {
|
|
82
|
+
json(res, 400, { error: "projectsRoot is required" });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Read-modify-write so the per-project model map survives a projectsRoot
|
|
86
|
+
// change (a naive overwrite would drop every window's saved model).
|
|
87
|
+
const s = await loadAppSettings();
|
|
88
|
+
s.projectsRoot = projectsRoot;
|
|
89
|
+
await saveAppSettings(s);
|
|
90
|
+
json(res, 200, { projectsRoot });
|
|
91
|
+
});
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (method === "POST" && url === "/create-project") {
|
|
95
|
+
void readBody(req, res).then(async (raw) => {
|
|
96
|
+
if (raw === null)
|
|
97
|
+
return;
|
|
98
|
+
let name;
|
|
99
|
+
try {
|
|
100
|
+
name = JSON.parse(raw).name ?? "";
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
name = name.trim();
|
|
107
|
+
if (!isValidProjectName(name)) {
|
|
108
|
+
json(res, 400, {
|
|
109
|
+
error: "Project name must be lowercase letters, digits, and dashes (e.g. my-project).",
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const { projectsRoot } = await loadAppSettings();
|
|
114
|
+
const dir = path.join(projectsRoot, name);
|
|
115
|
+
try {
|
|
116
|
+
// Refuse to clobber an existing directory.
|
|
117
|
+
const exists = await fs
|
|
118
|
+
.stat(dir)
|
|
119
|
+
.then(() => true)
|
|
120
|
+
.catch(() => false);
|
|
121
|
+
if (exists) {
|
|
122
|
+
json(res, 409, { error: `A folder named "${name}" already exists.` });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
await fs.mkdir(dir, { recursive: true });
|
|
126
|
+
json(res, 200, { path: dir });
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
json(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
// Dismiss a project from the picker (or restore it with hidden:false). The
|
|
135
|
+
// path is kept rather than the row: sessions in scratch dirs keep
|
|
136
|
+
// re-surfacing, so the user's decision has to outlive any one scan.
|
|
137
|
+
if (method === "POST" && url === "/projects/hidden") {
|
|
138
|
+
void readBody(req, res).then(async (raw) => {
|
|
139
|
+
if (raw === null)
|
|
140
|
+
return;
|
|
141
|
+
let body;
|
|
142
|
+
try {
|
|
143
|
+
body = JSON.parse(raw);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
json(res, 400, { error: "invalid JSON body" });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const target = body.path?.trim();
|
|
150
|
+
if (!target) {
|
|
151
|
+
json(res, 400, { error: "path is required" });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const key = path.resolve(target);
|
|
155
|
+
try {
|
|
156
|
+
const settings = await loadAppSettings();
|
|
157
|
+
const current = new Set((settings.hiddenProjects ?? []).map((p) => path.resolve(p)));
|
|
158
|
+
if (body.hidden === false)
|
|
159
|
+
current.delete(key);
|
|
160
|
+
else
|
|
161
|
+
current.add(key);
|
|
162
|
+
settings.hiddenProjects = current.size > 0 ? Array.from(current) : undefined;
|
|
163
|
+
await saveAppSettings(settings);
|
|
164
|
+
json(res, 200, { hidden: Array.from(current) });
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
json(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
if (method === "GET" && url === "/projects") {
|
|
173
|
+
// Session stores (ggcoder + Claude Code + Codex) for projects with
|
|
174
|
+
// history, plus a filesystem scan of the configured projects folder so
|
|
175
|
+
// projects you have not opened yet are still listed.
|
|
176
|
+
void loadAppSettings()
|
|
177
|
+
.then(({ projectsRoot, projectRoots, hiddenProjects }) => discoverProjects({
|
|
178
|
+
projectsRoot,
|
|
179
|
+
extraRoots: projectRoots,
|
|
180
|
+
hiddenPaths: hiddenProjects,
|
|
181
|
+
}))
|
|
182
|
+
.then((projects) => json(res, 200, { projects }))
|
|
183
|
+
.catch((err) => {
|
|
184
|
+
log("ERROR", "app-sidecar", "discoverProjects failed", {
|
|
185
|
+
message: err instanceof Error ? err.message : String(err),
|
|
186
|
+
});
|
|
187
|
+
json(res, 200, { projects: [] });
|
|
188
|
+
});
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (method === "GET" && url.startsWith("/sessions")) {
|
|
192
|
+
const target = new URL(url, `http://${ctx.host}`).searchParams.get("cwd");
|
|
193
|
+
if (!target) {
|
|
194
|
+
json(res, 400, { error: "missing cwd query param" });
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
const requestedAgent = new URL(url, `http://${ctx.host}`).searchParams.get("chatAgent");
|
|
198
|
+
// An omitted chatAgent means coding history; chat callers identify one
|
|
199
|
+
// agent or request the combined, recency-sorted "all" listing; the
|
|
200
|
+
// reserved value "motion" lists Motion sessions.
|
|
201
|
+
void listSidecarSessions(target, requestedAgent, ctx.paths.sessionsDir)
|
|
202
|
+
.then((sessions) => json(res, 200, { sessions }))
|
|
203
|
+
.catch(() => json(res, 200, { sessions: [] }));
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
if (method === "GET" && url.startsWith("/files")) {
|
|
207
|
+
const q = new URL(url, `http://${ctx.host}`).searchParams.get("q") ?? "";
|
|
208
|
+
void searchProjectFiles(ctx.cwd, q)
|
|
209
|
+
.then((files) => json(res, 200, { files }))
|
|
210
|
+
.catch((err) => {
|
|
211
|
+
log("ERROR", "app-sidecar", "searchProjectFiles failed", {
|
|
212
|
+
message: err instanceof Error ? err.message : String(err),
|
|
213
|
+
});
|
|
214
|
+
json(res, 200, { files: [] });
|
|
215
|
+
});
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
// Markdown transcript export for the app's download button. Serialized
|
|
219
|
+
// here rather than in the webview because the webview's transcript model
|
|
220
|
+
// deliberately keeps tool activity in the LiveToolPanel — exporting from
|
|
221
|
+
// there would hand the user a coding session with the coding missing.
|
|
222
|
+
// `?name=1` asks for the suggested filename only (the save dialog needs it
|
|
223
|
+
// before there is a path), so the markdown never crosses IPC twice.
|
|
224
|
+
if (method === "GET" && (url === "/export" || url.startsWith("/export?"))) {
|
|
225
|
+
const query = new URLSearchParams(url.slice(url.indexOf("?") + 1));
|
|
226
|
+
const st = ctx.session.getState();
|
|
227
|
+
const filename = defaultExportFilename(ctx.mode);
|
|
228
|
+
if (query.get("name") === "1") {
|
|
229
|
+
json(res, 200, { filename });
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
const markdown = sessionToMarkdown({
|
|
233
|
+
mode: ctx.mode,
|
|
234
|
+
cwd: ctx.cwd,
|
|
235
|
+
provider: st.provider,
|
|
236
|
+
model: st.model,
|
|
237
|
+
...(st.sessionId ? { sessionId: st.sessionId } : {}),
|
|
238
|
+
}, ctx.session.getMessages(), { toolDetail: parseToolDetail(query.get("tools")) });
|
|
239
|
+
json(res, 200, { filename, markdown });
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=workspace-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-routes.js","sourceRoot":"","sources":["../../src/app-sidecar/workspace-routes.ts"],"names":[],"mappings":"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"}
|