zcode-acp-server 0.46.7 → 0.47.0
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/dist/code-fingerprint.json +2 -2
- package/dist/remote/endpoint.d.ts.map +1 -1
- package/dist/remote/endpoint.js +6 -0
- package/dist/remote/endpoint.js.map +1 -1
- package/dist/remote/hub-server.d.ts.map +1 -1
- package/dist/remote/hub-server.js +102 -0
- package/dist/remote/hub-server.js.map +1 -1
- package/dist/remote/settings-endpoint.d.ts +45 -0
- package/dist/remote/settings-endpoint.d.ts.map +1 -0
- package/dist/remote/settings-endpoint.js +920 -0
- package/dist/remote/settings-endpoint.js.map +1 -0
- package/dist/settings/agents-config.d.ts +158 -0
- package/dist/settings/agents-config.d.ts.map +1 -0
- package/dist/settings/agents-config.js +598 -0
- package/dist/settings/agents-config.js.map +1 -0
- package/dist/settings/app-update.d.ts +193 -0
- package/dist/settings/app-update.d.ts.map +1 -0
- package/dist/settings/app-update.js +659 -0
- package/dist/settings/app-update.js.map +1 -0
- package/dist/settings/atomic-write.d.ts +106 -0
- package/dist/settings/atomic-write.d.ts.map +1 -0
- package/dist/settings/atomic-write.js +228 -0
- package/dist/settings/atomic-write.js.map +1 -0
- package/dist/settings/cli-config.d.ts +145 -0
- package/dist/settings/cli-config.d.ts.map +1 -0
- package/dist/settings/cli-config.js +320 -0
- package/dist/settings/cli-config.js.map +1 -0
- package/dist/settings/coding-plan.d.ts +106 -0
- package/dist/settings/coding-plan.d.ts.map +1 -0
- package/dist/settings/coding-plan.js +299 -0
- package/dist/settings/coding-plan.js.map +1 -0
- package/dist/settings/file-lock.d.ts +39 -0
- package/dist/settings/file-lock.d.ts.map +1 -0
- package/dist/settings/file-lock.js +282 -0
- package/dist/settings/file-lock.js.map +1 -0
- package/dist/settings/provider-config.d.ts +123 -0
- package/dist/settings/provider-config.d.ts.map +1 -0
- package/dist/settings/provider-config.js +311 -0
- package/dist/settings/provider-config.js.map +1 -0
- package/dist/settings/skills.d.ts +72 -0
- package/dist/settings/skills.d.ts.map +1 -0
- package/dist/settings/skills.js +245 -0
- package/dist/settings/skills.js.map +1 -0
- package/dist/settings/usage-stats.d.ts +74 -0
- package/dist/settings/usage-stats.d.ts.map +1 -0
- package/dist/settings/usage-stats.js +235 -0
- package/dist/settings/usage-stats.js.map +1 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +22 -0
- package/dist/utils.js.map +1 -1
- package/docs/ARCHITECTURE.md +65 -0
- package/docs/REMOTE-CLIENTS.md +252 -0
- package/package.json +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill management: discovery plus the actions the app's skills page offers.
|
|
3
|
+
*
|
|
4
|
+
* `config/skill-discovery.ts` exists to build the editor's command list, so it
|
|
5
|
+
* filters out disabled skills and drops the SKILL.md path (its `input.hint` is
|
|
6
|
+
* the author's argument hint, not a location). Neither works for management —
|
|
7
|
+
* a disabled skill must stay visible so the user can re-enable it, and every
|
|
8
|
+
* action needs the real path. So discovery is re-done here, over the same roots
|
|
9
|
+
* and with the same frontmatter rules.
|
|
10
|
+
*
|
|
11
|
+
* Escape guards are the point of this module: a skill path is client-supplied
|
|
12
|
+
* text that becomes a filesystem operation. Every path is resolved through
|
|
13
|
+
* `realpath` (collapsing symlinks and `..`) BEFORE the containment check, so
|
|
14
|
+
* `~/.zcode/skills/../../../etc` or a symlink pointing outside the roots can
|
|
15
|
+
* neither delete nor copy anything outside them. The app's own delete path
|
|
16
|
+
* carries the same guard.
|
|
17
|
+
*/
|
|
18
|
+
import { cp, mkdir, readFile, realpath, rm, stat } from "node:fs/promises";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { setSkillEnabled } from "./cli-config.js";
|
|
22
|
+
import { log, zcodeHomeDir } from "../utils.js";
|
|
23
|
+
/**
|
|
24
|
+
* Discovery roots, in the app's own order: user scope first (its skills win a
|
|
25
|
+
* name clash), then the shared `~/.agents` root, then plugin caches.
|
|
26
|
+
*/
|
|
27
|
+
export function skillRoots() {
|
|
28
|
+
const home = zcodeHomeDir();
|
|
29
|
+
return [
|
|
30
|
+
{ dir: path.join(home, "skills"), scope: "user", deletable: true },
|
|
31
|
+
{ dir: path.join(homedir(), ".agents", "skills"), scope: "agents", deletable: true },
|
|
32
|
+
{ dir: path.join(home, "cli", "plugins", "cache"), scope: "plugin", deletable: false },
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
/** The user-scope root new skills are copied into. */
|
|
36
|
+
export function userSkillsRoot() {
|
|
37
|
+
return path.join(zcodeHomeDir(), "skills");
|
|
38
|
+
}
|
|
39
|
+
/** Resolve through symlinks; fall back to the lexical form when absent. */
|
|
40
|
+
async function resolveReal(target) {
|
|
41
|
+
try {
|
|
42
|
+
return await realpath(target);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return path.resolve(target);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** True when `target` is `root` itself or inside it — both sides resolved. */
|
|
49
|
+
async function isInside(target, root) {
|
|
50
|
+
const rel = path.relative(await resolveReal(root), await resolveReal(target));
|
|
51
|
+
return rel === "" || (!rel.startsWith(`..${path.sep}`) && rel !== ".." && !path.isAbsolute(rel));
|
|
52
|
+
}
|
|
53
|
+
/** Minimal SKILL.md frontmatter reader — name and description only. */
|
|
54
|
+
function parseSkillFrontmatter(content) {
|
|
55
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
56
|
+
if (!match)
|
|
57
|
+
return {};
|
|
58
|
+
const fm = {};
|
|
59
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
60
|
+
const idx = line.indexOf(":");
|
|
61
|
+
if (idx <= 0)
|
|
62
|
+
continue;
|
|
63
|
+
let value = line.slice(idx + 1).trim();
|
|
64
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
65
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
66
|
+
value = value.slice(1, -1);
|
|
67
|
+
}
|
|
68
|
+
fm[line.slice(0, idx).trim()] = value;
|
|
69
|
+
}
|
|
70
|
+
return fm;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The set of disabled SKILL.md paths from `cli/config.json`.
|
|
74
|
+
*
|
|
75
|
+
* Keys are normalized (backslashes to forward slashes) the way the app
|
|
76
|
+
* normalizes them, so a config written on Windows still matches a POSIX
|
|
77
|
+
* discovery path.
|
|
78
|
+
*/
|
|
79
|
+
async function disabledSkillPaths() {
|
|
80
|
+
try {
|
|
81
|
+
const raw = await readFile(path.join(zcodeHomeDir(), "cli", "config.json"), "utf8");
|
|
82
|
+
const parsed = JSON.parse(raw);
|
|
83
|
+
const out = new Set();
|
|
84
|
+
for (const [key, value] of Object.entries(parsed.skills ?? {})) {
|
|
85
|
+
if (value?.enable === false)
|
|
86
|
+
out.add(key.replace(/\\/gu, "/"));
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return new Set();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Every skill, enabled or not.
|
|
96
|
+
*
|
|
97
|
+
* Duplicate names are kept rather than de-duplicated: the app lists the same
|
|
98
|
+
* name from two scopes separately (they are different files), and hiding one
|
|
99
|
+
* would make its toggle unreachable.
|
|
100
|
+
*/
|
|
101
|
+
export async function listSkills() {
|
|
102
|
+
const disabled = await disabledSkillPaths();
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const root of skillRoots()) {
|
|
105
|
+
let entries;
|
|
106
|
+
try {
|
|
107
|
+
entries = await readdirSafe(root.dir);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
continue; // root absent — normal on a machine without that source
|
|
111
|
+
}
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const dir = path.join(root.dir, entry);
|
|
114
|
+
const skillMd = path.join(dir, "SKILL.md");
|
|
115
|
+
try {
|
|
116
|
+
if (!(await stat(dir)).isDirectory())
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
let content;
|
|
123
|
+
try {
|
|
124
|
+
content = await readFile(skillMd, "utf8");
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue; // not a skill directory
|
|
128
|
+
}
|
|
129
|
+
const fm = parseSkillFrontmatter(content);
|
|
130
|
+
const name = fm["name"] ?? entry;
|
|
131
|
+
if (!name || !fm["description"])
|
|
132
|
+
continue; // the runtime skips these too
|
|
133
|
+
if (fm["user-invocable"] === "false")
|
|
134
|
+
continue;
|
|
135
|
+
const absPath = path.resolve(skillMd);
|
|
136
|
+
out.push({
|
|
137
|
+
name,
|
|
138
|
+
description: fm["description"],
|
|
139
|
+
path: absPath,
|
|
140
|
+
dir,
|
|
141
|
+
scope: root.scope,
|
|
142
|
+
enabled: !disabled.has(absPath.replace(/\\/gu, "/")),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
out.sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
async function readdirSafe(dir) {
|
|
150
|
+
const { readdir } = await import("node:fs/promises");
|
|
151
|
+
try {
|
|
152
|
+
return await readdir(dir);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Enable or disable a skill by its SKILL.md path. */
|
|
159
|
+
export async function setSkillEnabledByPath(skillPath, enable) {
|
|
160
|
+
assertSkillPath(skillPath);
|
|
161
|
+
// The stored key uses forward slashes regardless of platform.
|
|
162
|
+
await setSkillEnabled(skillPath.replace(/\\/gu, "/"), enable);
|
|
163
|
+
}
|
|
164
|
+
function assertSkillPath(skillPath) {
|
|
165
|
+
if (!path.isAbsolute(skillPath)) {
|
|
166
|
+
throw new Error("skill path must be absolute");
|
|
167
|
+
}
|
|
168
|
+
if (path.basename(skillPath) !== "SKILL.md") {
|
|
169
|
+
throw new Error("skill path must point at a SKILL.md file");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Delete a skill directory.
|
|
174
|
+
*
|
|
175
|
+
* Only skills inside a deletable root can go: a plugin's skill belongs to the
|
|
176
|
+
* plugin install and would reappear on the next update, so the API refuses
|
|
177
|
+
* rather than deleting something that comes back. The containment check runs on
|
|
178
|
+
* RESOLVED paths, so a symlinked skill directory cannot point the delete at
|
|
179
|
+
* something outside the roots.
|
|
180
|
+
*/
|
|
181
|
+
export async function deleteSkill(skillPath) {
|
|
182
|
+
assertSkillPath(skillPath);
|
|
183
|
+
const dir = path.dirname(skillPath);
|
|
184
|
+
// Every deletable root counts, not just the first one — the agents scope
|
|
185
|
+
// (`~/.agents/skills`) is listed and copyable, so refusing to delete it made
|
|
186
|
+
// half the listed skills undeletable.
|
|
187
|
+
const roots = skillRoots().filter((r) => r.deletable);
|
|
188
|
+
let root;
|
|
189
|
+
for (const candidate of roots) {
|
|
190
|
+
if (await isInside(dir, candidate.dir)) {
|
|
191
|
+
root = candidate;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!root) {
|
|
196
|
+
throw new Error("only skills under the user skill roots can be deleted");
|
|
197
|
+
}
|
|
198
|
+
// `isInside` treats the root itself as contained — refuse that explicitly.
|
|
199
|
+
if ((await resolveReal(dir)) === (await resolveReal(root.dir))) {
|
|
200
|
+
throw new Error("refusing to delete the skills root itself");
|
|
201
|
+
}
|
|
202
|
+
await rm(dir, { recursive: true, force: true });
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Copy a skill into the user root.
|
|
206
|
+
*
|
|
207
|
+
* A name collision is refused rather than merged: silently merging two skill
|
|
208
|
+
* directories yields a SKILL.md whose description matches neither original.
|
|
209
|
+
*/
|
|
210
|
+
export async function copySkillToUser(skillPath) {
|
|
211
|
+
assertSkillPath(skillPath);
|
|
212
|
+
const sourceDir = path.dirname(skillPath);
|
|
213
|
+
// Containment on resolved paths: a symlinked skill elsewhere on disk must not
|
|
214
|
+
// be copyable through this API.
|
|
215
|
+
let owned = false;
|
|
216
|
+
for (const root of skillRoots()) {
|
|
217
|
+
if (root.scope !== "plugin" && (await isInside(sourceDir, root.dir))) {
|
|
218
|
+
owned = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!owned) {
|
|
223
|
+
throw new Error("only skills under the user skill roots can be copied");
|
|
224
|
+
}
|
|
225
|
+
const targetRoot = userSkillsRoot();
|
|
226
|
+
const name = path.basename(sourceDir);
|
|
227
|
+
const target = path.join(targetRoot, name);
|
|
228
|
+
if ((await resolveReal(target)) === (await resolveReal(sourceDir))) {
|
|
229
|
+
throw new Error("this skill already lives in the user skills root");
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
if ((await stat(target)).isDirectory()) {
|
|
233
|
+
throw new Error(`a skill named '${name}' already exists in the user skills root`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (error?.code !== "ENOENT")
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
await mkdir(targetRoot, { recursive: true });
|
|
241
|
+
await cp(sourceDir, target, { recursive: true });
|
|
242
|
+
log(`settings: copied skill '${name}' to ${target}`);
|
|
243
|
+
return path.join(target, "SKILL.md");
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/settings/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App usage stats, aggregated from the local agent database.
|
|
3
|
+
*
|
|
4
|
+
* The desktop app's App Usage panel reads `~/.zcode/cli/db/db.sqlite`
|
|
5
|
+
* (`model_usage` / `turn_usage` / `tool_usage`). Rather than proxying the
|
|
6
|
+
* backend's `usage/stats` RPC — which couples a settings screen to a live
|
|
7
|
+
* backend process and whose `.strict()` params turn any schema drift into a
|
|
8
|
+
* hard failure — the same tables are aggregated here in read-only mode
|
|
9
|
+
* (ADR-0027).
|
|
10
|
+
*
|
|
11
|
+
* Two things this module is careful about:
|
|
12
|
+
*
|
|
13
|
+
* - **Never blocking the writer.** The connection is opened read-only and every
|
|
14
|
+
* query is a bounded aggregate. A `SQLITE_BUSY` degrades to the same
|
|
15
|
+
* `available: false` shape a missing database produces, because the caller
|
|
16
|
+
* renders an empty state either way.
|
|
17
|
+
* - **Never claiming data that is not there.** A machine that never ran an
|
|
18
|
+
* agent has no database; that is a normal state, so the response says
|
|
19
|
+
* `available: false` with zero values instead of erroring.
|
|
20
|
+
*
|
|
21
|
+
* Day buckets use the process timezone offset. The app does the same and notes
|
|
22
|
+
* the same ≤1h DST imprecision; for a usage overview that is not worth the
|
|
23
|
+
* complexity of a per-request timezone.
|
|
24
|
+
*/
|
|
25
|
+
export type UsageRange = "7d" | "30d" | "all";
|
|
26
|
+
export interface ModelUsageRow {
|
|
27
|
+
modelId: string | null;
|
|
28
|
+
totalTokens: number;
|
|
29
|
+
inputTokens: number;
|
|
30
|
+
outputTokens: number;
|
|
31
|
+
reasoningTokens: number;
|
|
32
|
+
cacheReadTokens: number;
|
|
33
|
+
cacheCreationTokens: number;
|
|
34
|
+
requestCount: number;
|
|
35
|
+
/** Fraction of the window's total tokens, 0..1. */
|
|
36
|
+
share: number;
|
|
37
|
+
}
|
|
38
|
+
export interface DailyUsageRow {
|
|
39
|
+
/** Local calendar day, `YYYY-MM-DD`. */
|
|
40
|
+
date: string;
|
|
41
|
+
models: Array<{
|
|
42
|
+
modelId: string | null;
|
|
43
|
+
totalTokens: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export interface UsageSnapshot {
|
|
47
|
+
/** false when the database is absent — an empty state, not an error. */
|
|
48
|
+
available: boolean;
|
|
49
|
+
range: UsageRange;
|
|
50
|
+
summary: {
|
|
51
|
+
totalTokens: number;
|
|
52
|
+
inputTokens: number;
|
|
53
|
+
outputTokens: number;
|
|
54
|
+
reasoningTokens: number;
|
|
55
|
+
cacheReadTokens: number;
|
|
56
|
+
requestCount: number;
|
|
57
|
+
sessionCount: number;
|
|
58
|
+
turnCount: number;
|
|
59
|
+
toolCallCount: number;
|
|
60
|
+
models: number;
|
|
61
|
+
activeDays: number;
|
|
62
|
+
};
|
|
63
|
+
models: ModelUsageRow[];
|
|
64
|
+
daily: DailyUsageRow[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Aggregate the window.
|
|
68
|
+
*
|
|
69
|
+
* Two queries: one per-model roll-up, one per-day roll-up. Both are grouped
|
|
70
|
+
* server-side by sqlite, so the bridge transfers only the aggregated rows —
|
|
71
|
+
* the raw table on a developed machine holds tens of thousands of rows.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readUsageStats(range?: UsageRange): Promise<UsageSnapshot>;
|
|
74
|
+
//# sourceMappingURL=usage-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-stats.d.ts","sourceRoot":"","sources":["../../src/settings/usage-stats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA6DH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AA6BD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,GAAE,UAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CA8HrF"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App usage stats, aggregated from the local agent database.
|
|
3
|
+
*
|
|
4
|
+
* The desktop app's App Usage panel reads `~/.zcode/cli/db/db.sqlite`
|
|
5
|
+
* (`model_usage` / `turn_usage` / `tool_usage`). Rather than proxying the
|
|
6
|
+
* backend's `usage/stats` RPC — which couples a settings screen to a live
|
|
7
|
+
* backend process and whose `.strict()` params turn any schema drift into a
|
|
8
|
+
* hard failure — the same tables are aggregated here in read-only mode
|
|
9
|
+
* (ADR-0027).
|
|
10
|
+
*
|
|
11
|
+
* Two things this module is careful about:
|
|
12
|
+
*
|
|
13
|
+
* - **Never blocking the writer.** The connection is opened read-only and every
|
|
14
|
+
* query is a bounded aggregate. A `SQLITE_BUSY` degrades to the same
|
|
15
|
+
* `available: false` shape a missing database produces, because the caller
|
|
16
|
+
* renders an empty state either way.
|
|
17
|
+
* - **Never claiming data that is not there.** A machine that never ran an
|
|
18
|
+
* agent has no database; that is a normal state, so the response says
|
|
19
|
+
* `available: false` with zero values instead of erroring.
|
|
20
|
+
*
|
|
21
|
+
* Day buckets use the process timezone offset. The app does the same and notes
|
|
22
|
+
* the same ≤1h DST imprecision; for a usage overview that is not worth the
|
|
23
|
+
* complexity of a per-request timezone.
|
|
24
|
+
*/
|
|
25
|
+
import { createRequire } from "node:module";
|
|
26
|
+
import { log, warn, zcodeUsageDbPath } from "../utils.js";
|
|
27
|
+
/**
|
|
28
|
+
* Cached constructor; null when node:sqlite is unavailable (Node < 22).
|
|
29
|
+
*
|
|
30
|
+
* Loaded through `createRequire` rather than a dynamic `import`: under vite
|
|
31
|
+
* (the test runner) `import("node:sqlite")` is intercepted and fails to
|
|
32
|
+
* resolve, which would silently disable usage stats in every test. `require`
|
|
33
|
+
* resolves Node built-ins directly.
|
|
34
|
+
*/
|
|
35
|
+
let sqliteCtor;
|
|
36
|
+
function loadSqlite() {
|
|
37
|
+
if (sqliteCtor !== undefined)
|
|
38
|
+
return sqliteCtor;
|
|
39
|
+
try {
|
|
40
|
+
const require = createRequire(import.meta.url);
|
|
41
|
+
const mod = require("node:sqlite");
|
|
42
|
+
sqliteCtor = mod.DatabaseSync;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
sqliteCtor = null;
|
|
46
|
+
}
|
|
47
|
+
return sqliteCtor;
|
|
48
|
+
}
|
|
49
|
+
/** A `database is locked/busy` throw from node:sqlite (see tasks-index.ts). */
|
|
50
|
+
function isBusyError(e) {
|
|
51
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
52
|
+
return /database is (busy|locked)/i.test(msg);
|
|
53
|
+
}
|
|
54
|
+
const BUSY_RETRY_DELAYS_MS = [200, 400];
|
|
55
|
+
/**
|
|
56
|
+
* Open the usage db read-only, retrying briefly on writer contention.
|
|
57
|
+
*
|
|
58
|
+
* Synchronous on purpose: a settings request is a single short operation, and
|
|
59
|
+
* blocking the event loop for at most 600ms is cheaper than making every caller
|
|
60
|
+
* async. `Atomics.wait` is the synchronous sleep that does not busy-spin.
|
|
61
|
+
*/
|
|
62
|
+
function openReadOnly(dbPath) {
|
|
63
|
+
const Ctor = loadSqlite();
|
|
64
|
+
if (!Ctor)
|
|
65
|
+
return null;
|
|
66
|
+
const sleeper = new Int32Array(new SharedArrayBuffer(4));
|
|
67
|
+
for (let attempt = 0;; attempt += 1) {
|
|
68
|
+
try {
|
|
69
|
+
return new Ctor(dbPath, { readOnly: true });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const delay = BUSY_RETRY_DELAYS_MS[attempt];
|
|
73
|
+
if (delay === undefined || !isBusyError(error))
|
|
74
|
+
return null;
|
|
75
|
+
Atomics.wait(sleeper, 0, 0, delay);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const EMPTY_SNAPSHOT = (range) => ({
|
|
80
|
+
available: false,
|
|
81
|
+
range,
|
|
82
|
+
summary: {
|
|
83
|
+
totalTokens: 0,
|
|
84
|
+
inputTokens: 0,
|
|
85
|
+
outputTokens: 0,
|
|
86
|
+
reasoningTokens: 0,
|
|
87
|
+
cacheReadTokens: 0,
|
|
88
|
+
requestCount: 0,
|
|
89
|
+
sessionCount: 0,
|
|
90
|
+
turnCount: 0,
|
|
91
|
+
toolCallCount: 0,
|
|
92
|
+
models: 0,
|
|
93
|
+
activeDays: 0,
|
|
94
|
+
},
|
|
95
|
+
models: [],
|
|
96
|
+
daily: [],
|
|
97
|
+
});
|
|
98
|
+
/** Range → the `started_at` floor (ms epoch). `all` has no floor. */
|
|
99
|
+
function rangeFloor(range, now) {
|
|
100
|
+
if (range === "7d")
|
|
101
|
+
return now - 7 * 86_400_000;
|
|
102
|
+
if (range === "30d")
|
|
103
|
+
return now - 30 * 86_400_000;
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Aggregate the window.
|
|
108
|
+
*
|
|
109
|
+
* Two queries: one per-model roll-up, one per-day roll-up. Both are grouped
|
|
110
|
+
* server-side by sqlite, so the bridge transfers only the aggregated rows —
|
|
111
|
+
* the raw table on a developed machine holds tens of thousands of rows.
|
|
112
|
+
*/
|
|
113
|
+
export async function readUsageStats(range = "7d") {
|
|
114
|
+
const db = openReadOnly(zcodeUsageDbPath());
|
|
115
|
+
if (!db) {
|
|
116
|
+
log("settings: usage database unavailable — reporting an empty snapshot");
|
|
117
|
+
return EMPTY_SNAPSHOT(range);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const floor = rangeFloor(range, Date.now());
|
|
121
|
+
const modelRows = db
|
|
122
|
+
.prepare(`SELECT model_id,
|
|
123
|
+
COALESCE(SUM(computed_total_tokens), 0) AS total_tokens,
|
|
124
|
+
COALESCE(SUM(input_tokens), 0) AS input_tokens,
|
|
125
|
+
COALESCE(SUM(output_tokens), 0) AS output_tokens,
|
|
126
|
+
COALESCE(SUM(reasoning_tokens), 0) AS reasoning_tokens,
|
|
127
|
+
COALESCE(SUM(cache_read_input_tokens), 0) AS cache_read_tokens,
|
|
128
|
+
COALESCE(SUM(cache_creation_input_tokens), 0) AS cache_creation_tokens,
|
|
129
|
+
COUNT(*) AS request_count
|
|
130
|
+
FROM model_usage
|
|
131
|
+
WHERE started_at > ?
|
|
132
|
+
GROUP BY model_id
|
|
133
|
+
ORDER BY total_tokens DESC`)
|
|
134
|
+
.all(floor);
|
|
135
|
+
// Group by LOCAL calendar day, computed by sqlite. Dividing the epoch by
|
|
136
|
+
// 86400000 would bucket on UTC, which puts a morning session in the wrong
|
|
137
|
+
// day for every timezone east or west of it — the same imprecision the app
|
|
138
|
+
// documents for its own day buckets.
|
|
139
|
+
const dayRows = db
|
|
140
|
+
.prepare(`SELECT date(started_at / 1000, 'unixepoch', 'localtime') AS day,
|
|
141
|
+
model_id,
|
|
142
|
+
COALESCE(SUM(computed_total_tokens), 0) AS total_tokens
|
|
143
|
+
FROM model_usage
|
|
144
|
+
WHERE started_at > ?
|
|
145
|
+
GROUP BY day, model_id
|
|
146
|
+
ORDER BY day ASC`)
|
|
147
|
+
.all(floor);
|
|
148
|
+
// Session/turn/tool counts describe the same window but come from their own
|
|
149
|
+
// tables; a missing table reads as zero rather than failing the snapshot.
|
|
150
|
+
const scalar = (sql) => {
|
|
151
|
+
try {
|
|
152
|
+
const row = db.prepare(sql).get(floor);
|
|
153
|
+
return Number(row?.["c"] ?? 0);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const sessionCount = scalar("SELECT COUNT(DISTINCT session_id) AS c FROM turn_usage WHERE started_at > ?");
|
|
160
|
+
const turnCount = scalar("SELECT COUNT(*) AS c FROM turn_usage WHERE started_at > ?");
|
|
161
|
+
const toolCallCount = scalar("SELECT COUNT(*) AS c FROM tool_usage WHERE started_at > ?");
|
|
162
|
+
const grandTotal = modelRows.reduce((sum, r) => sum + Number(r["total_tokens"] ?? 0), 0);
|
|
163
|
+
const models = modelRows.map((r) => {
|
|
164
|
+
const total = Number(r["total_tokens"] ?? 0);
|
|
165
|
+
return {
|
|
166
|
+
modelId: typeof r["model_id"] === "string" ? r["model_id"] : null,
|
|
167
|
+
totalTokens: total,
|
|
168
|
+
inputTokens: Number(r["input_tokens"] ?? 0),
|
|
169
|
+
outputTokens: Number(r["output_tokens"] ?? 0),
|
|
170
|
+
reasoningTokens: Number(r["reasoning_tokens"] ?? 0),
|
|
171
|
+
cacheReadTokens: Number(r["cache_read_tokens"] ?? 0),
|
|
172
|
+
cacheCreationTokens: Number(r["cache_creation_tokens"] ?? 0),
|
|
173
|
+
requestCount: Number(r["request_count"] ?? 0),
|
|
174
|
+
share: grandTotal > 0 ? round4(total / grandTotal) : 0,
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
// The day rows arrive already grouped by sqlite; only the per-day model
|
|
178
|
+
// ordering (by volume, so a chart's legend matches its bars) is done here.
|
|
179
|
+
const byDay = new Map();
|
|
180
|
+
for (const row of dayRows) {
|
|
181
|
+
const day = typeof row["day"] === "string" ? row["day"] : "";
|
|
182
|
+
if (!day)
|
|
183
|
+
continue;
|
|
184
|
+
const modelId = typeof row["model_id"] === "string" ? row["model_id"] : "(unknown)";
|
|
185
|
+
const bucket = byDay.get(day) ?? new Map();
|
|
186
|
+
bucket.set(modelId, (bucket.get(modelId) ?? 0) + Number(row["total_tokens"] ?? 0));
|
|
187
|
+
byDay.set(day, bucket);
|
|
188
|
+
}
|
|
189
|
+
const daily = Array.from(byDay.entries())
|
|
190
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
191
|
+
.map(([date, bucket]) => ({
|
|
192
|
+
date,
|
|
193
|
+
models: Array.from(bucket.entries())
|
|
194
|
+
.map(([modelId, totalTokens]) => ({ modelId, totalTokens }))
|
|
195
|
+
.sort((a, b) => b.totalTokens - a.totalTokens),
|
|
196
|
+
}));
|
|
197
|
+
return {
|
|
198
|
+
available: true,
|
|
199
|
+
range,
|
|
200
|
+
summary: {
|
|
201
|
+
totalTokens: grandTotal,
|
|
202
|
+
inputTokens: models.reduce((s, m) => s + m.inputTokens, 0),
|
|
203
|
+
outputTokens: models.reduce((s, m) => s + m.outputTokens, 0),
|
|
204
|
+
reasoningTokens: models.reduce((s, m) => s + m.reasoningTokens, 0),
|
|
205
|
+
cacheReadTokens: models.reduce((s, m) => s + m.cacheReadTokens, 0),
|
|
206
|
+
requestCount: models.reduce((s, m) => s + m.requestCount, 0),
|
|
207
|
+
sessionCount,
|
|
208
|
+
turnCount,
|
|
209
|
+
toolCallCount,
|
|
210
|
+
models: models.length,
|
|
211
|
+
activeDays: daily.length,
|
|
212
|
+
},
|
|
213
|
+
models,
|
|
214
|
+
daily,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
// A schema drift (a renamed column) must not break the settings screen;
|
|
219
|
+
// the rest of the API is unaffected.
|
|
220
|
+
warn(`settings: usage aggregation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
221
|
+
return EMPTY_SNAPSHOT(range);
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
try {
|
|
225
|
+
db.close();
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
/* already closed */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function round4(value) {
|
|
233
|
+
return Math.round(value * 10_000) / 10_000;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=usage-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-stats.js","sourceRoot":"","sources":["../../src/settings/usage-stats.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -37,6 +37,20 @@ export declare function zcodePersonalProviderPath(): string;
|
|
|
37
37
|
export declare function zcodeCliConfigPath(): string;
|
|
38
38
|
/** Root of the ZCode plugin cache directory (per call — see above). */
|
|
39
39
|
export declare function zcodePluginCacheDir(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Root of the ZCode user-scope agent definitions (`~/.zcode/agents/*.md`).
|
|
42
|
+
* Per call, so discovery follows a `ZCODE_HOME` change made after import.
|
|
43
|
+
*/
|
|
44
|
+
export declare function zcodeAgentsDir(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Path of the agent state file (`~/.zcode/v2/agents-state.json`): per-agent
|
|
47
|
+
* enablement plus the built-in agents' model overrides. Per call — see above.
|
|
48
|
+
*/
|
|
49
|
+
export declare function zcodeAgentsStatePath(): string;
|
|
50
|
+
/** Path of the CLI agent database (`~/.zcode/cli/db/db.sqlite`) — usage stats. */
|
|
51
|
+
export declare function zcodeUsageDbPath(): string;
|
|
52
|
+
/** Path of the encrypted credential store (`~/.zcode/v2/credentials.json`). */
|
|
53
|
+
export declare function zcodeCredentialsPath(): string;
|
|
40
54
|
/**
|
|
41
55
|
* Slash commands surfaced to the editor. Each maps to a ZCode session method
|
|
42
56
|
* that the server forwards when the user types the command.
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAmBlC,4DAA4D;AAC5D,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QAAiD,CAAC;AAE/E;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCjB,CAAC;AAEX,0EAA0E;AAC1E,eAAO,MAAM,WAAW;;;;0BAIL,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B/C,CAAC;AAEX,iGAAiG;AACjG,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAGhF,CAAC;AAqBF,qEAAqE;AACrE,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGrC;AAED,0DAA0D;AAC1D,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEtC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAmB5D"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAmBlC,4DAA4D;AAC5D,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QAAiD,CAAC;AAE/E;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCjB,CAAC;AAEX,0EAA0E;AAC1E,eAAO,MAAM,WAAW;;;;0BAIL,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B/C,CAAC;AAEX,iGAAiG;AACjG,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAGhF,CAAC;AAqBF,qEAAqE;AACrE,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGrC;AAED,0DAA0D;AAC1D,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEtC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAmB5D"}
|
package/dist/utils.js
CHANGED
|
@@ -67,6 +67,28 @@ export function zcodeCliConfigPath() {
|
|
|
67
67
|
export function zcodePluginCacheDir() {
|
|
68
68
|
return path.join(zcodeHomeDir(), "cli", "plugins", "cache");
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Root of the ZCode user-scope agent definitions (`~/.zcode/agents/*.md`).
|
|
72
|
+
* Per call, so discovery follows a `ZCODE_HOME` change made after import.
|
|
73
|
+
*/
|
|
74
|
+
export function zcodeAgentsDir() {
|
|
75
|
+
return path.join(zcodeHomeDir(), "agents");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Path of the agent state file (`~/.zcode/v2/agents-state.json`): per-agent
|
|
79
|
+
* enablement plus the built-in agents' model overrides. Per call — see above.
|
|
80
|
+
*/
|
|
81
|
+
export function zcodeAgentsStatePath() {
|
|
82
|
+
return path.join(zcodeHomeDir(), "v2", "agents-state.json");
|
|
83
|
+
}
|
|
84
|
+
/** Path of the CLI agent database (`~/.zcode/cli/db/db.sqlite`) — usage stats. */
|
|
85
|
+
export function zcodeUsageDbPath() {
|
|
86
|
+
return path.join(zcodeHomeDir(), "cli", "db", "db.sqlite");
|
|
87
|
+
}
|
|
88
|
+
/** Path of the encrypted credential store (`~/.zcode/v2/credentials.json`). */
|
|
89
|
+
export function zcodeCredentialsPath() {
|
|
90
|
+
return path.join(zcodeHomeDir(), "v2", "credentials.json");
|
|
91
|
+
}
|
|
70
92
|
/**
|
|
71
93
|
* Slash commands surfaced to the editor. Each maps to a ZCode session method
|
|
72
94
|
* that the server forwards when the user types the command.
|