@klhapp/skillmux 1.11.2 → 1.13.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/CHANGELOG.md +15 -0
- package/README.md +20 -10
- package/bin/skillmux.js +89 -0
- package/docs/README.md +4 -4
- package/docs/cli.md +15 -5
- package/docs/concepts.md +4 -3
- package/docs/configuration.md +22 -5
- package/docs/deployment.md +3 -3
- package/docs/getting-started.md +32 -7
- package/docs/releasing.md +17 -2
- package/docs/sdd/cli-remote-target-parity/schema-bucket-b.yaml +297 -0
- package/docs/sdd/cli-remote-target-parity/spec-bucket-b.md +95 -0
- package/docs/sdd/cli-remote-target-parity/spec.md +111 -0
- package/docs/sdd/cli-remote-target-parity/think.md +183 -0
- package/docs/sdd/cli-surface-consistency/think.md +149 -0
- package/docs/sdd/config-authority-rerank-resilience/plan.md +338 -0
- package/docs/sdd/config-authority-rerank-resilience/review.md +124 -0
- package/docs/sdd/config-authority-rerank-resilience/spec.md +78 -0
- package/docs/sdd/feat-packaging-deployment/review.md +95 -0
- package/docs/sdd/fetch-outcome-flywheel/review.md +103 -0
- package/docs/sdd/fetch-outcome-flywheel/schema.json +347 -0
- package/docs/sdd/fetch-outcome-flywheel/spec.md +185 -0
- package/docs/sdd/npm-native-binaries/review.md +103 -0
- package/docs/sdd/npm-native-binaries/spec.md +148 -0
- package/docs/sdd/ranked-shortlist-2/pr3-calibration-removal.md +56 -0
- package/docs/sdd/refactor-builtin-target-paths/spec.md +41 -0
- package/docs/sdd/runtime-resource-hardening/review.md +86 -0
- package/docs/sdd/runtime-resource-hardening/spec.md +43 -0
- package/docs/sdd/security-hardening-phase/think.md +129 -0
- package/docs/sdd/security-observability/review.md +96 -0
- package/docs/sdd/security-observability/spec.md +55 -0
- package/docs/sdd/skill-provenance-update/review.md +152 -0
- package/docs/sdd/skill-provenance-update/schema.json +193 -0
- package/docs/sdd/skill-provenance-update/spec.md +134 -0
- package/docs/sdd/supply-chain-hardening/review.md +50 -0
- package/docs/sdd/supply-chain-hardening/spec.md +42 -0
- package/docs/sdd/target-marker-rehome/spec.md +33 -0
- package/docs/skill-management.md +1 -1
- package/docs/troubleshooting.md +18 -2
- package/package.json +15 -8
- package/src/adapters.ts +0 -438
- package/src/audit.ts +0 -21
- package/src/cli.ts +0 -928
- package/src/clients.ts +0 -368
- package/src/commands/audit.ts +0 -85
- package/src/commands/config.ts +0 -224
- package/src/commands/context.ts +0 -104
- package/src/commands/core.ts +0 -56
- package/src/commands/doctor.ts +0 -97
- package/src/commands/eval.ts +0 -88
- package/src/commands/init.ts +0 -672
- package/src/commands/install.ts +0 -155
- package/src/commands/local-vault.ts +0 -60
- package/src/commands/models.ts +0 -10
- package/src/commands/outdated.ts +0 -115
- package/src/commands/project.ts +0 -580
- package/src/commands/report.ts +0 -66
- package/src/commands/scan.ts +0 -71
- package/src/commands/shared.ts +0 -38
- package/src/commands/skill.ts +0 -33
- package/src/commands/sync.ts +0 -233
- package/src/commands/target.ts +0 -225
- package/src/commands/update.ts +0 -277
- package/src/completions.ts +0 -196
- package/src/concurrency-limiter.ts +0 -61
- package/src/config-service.ts +0 -398
- package/src/config-watcher.ts +0 -253
- package/src/config.ts +0 -583
- package/src/context.ts +0 -188
- package/src/db-audit.ts +0 -286
- package/src/db-index.ts +0 -238
- package/src/db.ts +0 -3
- package/src/deployment.ts +0 -39
- package/src/doctor.ts +0 -205
- package/src/eval.ts +0 -302
- package/src/global-flags.ts +0 -46
- package/src/init-agents.ts +0 -329
- package/src/init-instructions.ts +0 -192
- package/src/init.ts +0 -389
- package/src/install.ts +0 -232
- package/src/lifecycle.ts +0 -51
- package/src/logger.ts +0 -26
- package/src/manifest.ts +0 -352
- package/src/mcp-registration.ts +0 -89
- package/src/metrics.ts +0 -121
- package/src/models.ts +0 -20
- package/src/output.ts +0 -214
- package/src/project-setup.ts +0 -36
- package/src/prompts.ts +0 -124
- package/src/provenance.ts +0 -99
- package/src/rate-limiter.ts +0 -137
- package/src/readiness.ts +0 -30
- package/src/redact.ts +0 -52
- package/src/router-core.ts +0 -666
- package/src/rrf.ts +0 -31
- package/src/scan.ts +0 -300
- package/src/server.ts +0 -930
- package/src/setup.ts +0 -145
- package/src/snapshot.ts +0 -135
- package/src/stats.ts +0 -340
- package/src/sync.ts +0 -482
- package/src/toml-writer.ts +0 -51
- package/src/types.ts +0 -212
- package/src/vault.ts +0 -207
package/src/adapters.ts
DELETED
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
import { createClients } from "./clients";
|
|
2
|
-
import { expandHome, loadConfig, resolveConfigPath } from "./config";
|
|
3
|
-
import { CliError } from "./output";
|
|
4
|
-
import {
|
|
5
|
-
computeHash,
|
|
6
|
-
getDottedKey,
|
|
7
|
-
getEffectiveConfig,
|
|
8
|
-
getLocalConfigStatus,
|
|
9
|
-
getNestedValue,
|
|
10
|
-
RELOADABLE_KEYS,
|
|
11
|
-
RESTART_REQUIRED_KEYS,
|
|
12
|
-
setDottedKey,
|
|
13
|
-
validateDottedKey,
|
|
14
|
-
type ConfigStatusResponse,
|
|
15
|
-
type SetConfigResult,
|
|
16
|
-
} from "./config-service";
|
|
17
|
-
import type { ResolvedContext } from "./context";
|
|
18
|
-
import type { Clients, Config } from "./types";
|
|
19
|
-
import { getStats, parseSince, type StatsResponse } from "./stats";
|
|
20
|
-
import { countPrunable, openAudit, pruneAuditBefore, type PruneResult } from "./db";
|
|
21
|
-
import { buildPromotedCases, evalVault, queryPromotableFetches, type EvalCase, type EvalReport } from "./eval";
|
|
22
|
-
|
|
23
|
-
export interface Capabilities {
|
|
24
|
-
config_read: boolean;
|
|
25
|
-
config_write: boolean;
|
|
26
|
-
persistence: "writable" | "externally_managed";
|
|
27
|
-
reloadable_keys: string[];
|
|
28
|
-
restart_required_keys: string[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ContextAdapterOptions {
|
|
32
|
-
configPath?: string;
|
|
33
|
-
allowInsecure?: boolean;
|
|
34
|
-
clients?: Clients;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface AuditPruneOptions {
|
|
38
|
-
older_than?: string;
|
|
39
|
-
dry_run?: boolean;
|
|
40
|
-
confirm?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface AuditPruneResult extends PruneResult {
|
|
44
|
-
dry_run: boolean;
|
|
45
|
-
cutoff: string | null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Context adapter: `local` = this CLI process has the Skillmux runtime (vault, index,
|
|
50
|
-
* audit db, embeddings/reranker clients) in-process; `remote` = thin network client to an external process.
|
|
51
|
-
*/
|
|
52
|
-
export interface ContextAdapter {
|
|
53
|
-
getCapabilities(): Promise<Capabilities>;
|
|
54
|
-
getConfigShow(): Promise<{ effective: Config; sources: Record<string, string>; active_revision: string }>;
|
|
55
|
-
getConfigGet(key: string): Promise<unknown>;
|
|
56
|
-
configValidate(): Promise<{ valid: boolean; readiness: unknown }>;
|
|
57
|
-
configDiff(): Promise<{ diff: Record<string, { prior: unknown; resulting: unknown }> }>;
|
|
58
|
-
configSet(key: string, rawValStr: string, opts?: { dryRun?: boolean }): Promise<SetConfigResult>;
|
|
59
|
-
configStatus(): Promise<ConfigStatusResponse>;
|
|
60
|
-
getStats(since: string): Promise<StatsResponse>;
|
|
61
|
-
auditPrune(opts?: AuditPruneOptions): Promise<AuditPruneResult>;
|
|
62
|
-
auditCount(older_than?: string): Promise<AuditPruneResult>;
|
|
63
|
-
evalRun(): Promise<EvalReport>;
|
|
64
|
-
evalPromote(since: string): Promise<EvalCase[]>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function isLoopbackHost(hostname: string): boolean {
|
|
68
|
-
return (
|
|
69
|
-
hostname === "localhost" ||
|
|
70
|
-
hostname === "127.0.0.1" ||
|
|
71
|
-
hostname === "::1" ||
|
|
72
|
-
hostname === "0.0.0.0" ||
|
|
73
|
-
hostname.startsWith("127.")
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export class LocalAdapter implements ContextAdapter {
|
|
78
|
-
private configPath: string;
|
|
79
|
-
private clients?: Clients;
|
|
80
|
-
|
|
81
|
-
constructor(opts?: ContextAdapterOptions) {
|
|
82
|
-
this.configPath = resolveConfigPath(opts?.configPath);
|
|
83
|
-
this.clients = opts?.clients;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async getCapabilities(): Promise<Capabilities> {
|
|
87
|
-
const isExternallyManaged = process.env.SKILLMUX_CONFIG_READONLY === "true";
|
|
88
|
-
return {
|
|
89
|
-
config_read: true,
|
|
90
|
-
config_write: !isExternallyManaged,
|
|
91
|
-
persistence: isExternallyManaged ? "externally_managed" : "writable",
|
|
92
|
-
reloadable_keys: RELOADABLE_KEYS,
|
|
93
|
-
restart_required_keys: RESTART_REQUIRED_KEYS,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async getConfigShow(): Promise<{ effective: Config; sources: Record<string, string>; active_revision: string }> {
|
|
98
|
-
const { effective, sources } = await getEffectiveConfig(this.configPath);
|
|
99
|
-
return {
|
|
100
|
-
effective,
|
|
101
|
-
sources,
|
|
102
|
-
active_revision: computeHash(effective),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async getConfigGet(key: string): Promise<unknown> {
|
|
107
|
-
return getDottedKey(key, this.configPath);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async configValidate(): Promise<{ valid: boolean; readiness: unknown }> {
|
|
111
|
-
const { effective } = await getEffectiveConfig(this.configPath);
|
|
112
|
-
return { valid: !!effective, readiness: { status: "ready", capability: "hybrid" } };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async configDiff(): Promise<{ diff: Record<string, { prior: unknown; resulting: unknown }> }> {
|
|
116
|
-
const { effective, sources } = await getEffectiveConfig(this.configPath);
|
|
117
|
-
const diff: Record<string, { prior: unknown; resulting: unknown }> = {};
|
|
118
|
-
for (const [k, src] of Object.entries(sources)) {
|
|
119
|
-
if (src !== "default") {
|
|
120
|
-
diff[k] = { prior: "default", resulting: getNestedValue(effective as any, k) };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return { diff };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async configSet(key: string, rawValStr: string, opts?: { dryRun?: boolean }): Promise<SetConfigResult> {
|
|
127
|
-
const caps = await this.getCapabilities();
|
|
128
|
-
if (caps.persistence === "externally_managed") {
|
|
129
|
-
throw new CliError("Configuration is externally managed and cannot be modified", 4);
|
|
130
|
-
}
|
|
131
|
-
validateDottedKey(key);
|
|
132
|
-
return setDottedKey(key, rawValStr, {
|
|
133
|
-
configPath: this.configPath,
|
|
134
|
-
dryRun: opts?.dryRun,
|
|
135
|
-
contextName: "local",
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async configStatus(): Promise<ConfigStatusResponse> {
|
|
140
|
-
return getLocalConfigStatus(this.configPath);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async getStats(since: string): Promise<StatsResponse> {
|
|
144
|
-
const config = await loadConfig(this.configPath);
|
|
145
|
-
const stateDir = expandHome(config.state_dir);
|
|
146
|
-
const db = openAudit(stateDir);
|
|
147
|
-
try {
|
|
148
|
-
return getStats(db, since);
|
|
149
|
-
} finally {
|
|
150
|
-
db.close();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async auditPrune(opts?: AuditPruneOptions): Promise<AuditPruneResult> {
|
|
155
|
-
const config = await loadConfig(this.configPath);
|
|
156
|
-
const stateDir = expandHome(config.state_dir);
|
|
157
|
-
const olderThan = opts?.older_than;
|
|
158
|
-
const dryRun = opts?.dry_run ?? false;
|
|
159
|
-
|
|
160
|
-
let cutoff: Date;
|
|
161
|
-
if (olderThan) {
|
|
162
|
-
cutoff = parseSince(olderThan);
|
|
163
|
-
} else {
|
|
164
|
-
const retentionDays = config.audit?.retention_days ?? 90;
|
|
165
|
-
if (retentionDays <= 0) {
|
|
166
|
-
return {
|
|
167
|
-
audit_deleted: 0,
|
|
168
|
-
fetch_deleted: 0,
|
|
169
|
-
admin_audit_deleted: 0,
|
|
170
|
-
dry_run: dryRun,
|
|
171
|
-
cutoff: null,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
cutoff = new Date(Date.now() - retentionDays * 86_400_000);
|
|
175
|
-
}
|
|
176
|
-
const cutoffIso = cutoff.toISOString();
|
|
177
|
-
|
|
178
|
-
const db = openAudit(stateDir);
|
|
179
|
-
try {
|
|
180
|
-
if (dryRun) {
|
|
181
|
-
const counts = countPrunable(db, cutoffIso);
|
|
182
|
-
return { ...counts, dry_run: true, cutoff: cutoffIso };
|
|
183
|
-
}
|
|
184
|
-
const counts = pruneAuditBefore(db, cutoffIso);
|
|
185
|
-
return { ...counts, dry_run: false, cutoff: cutoffIso };
|
|
186
|
-
} finally {
|
|
187
|
-
db.close();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async auditCount(older_than?: string): Promise<AuditPruneResult> {
|
|
192
|
-
return this.auditPrune({ older_than, dry_run: true });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async evalRun(): Promise<EvalReport> {
|
|
196
|
-
return evalVault();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async evalPromote(since: string): Promise<EvalCase[]> {
|
|
200
|
-
const sinceDate = parseSince(since);
|
|
201
|
-
const sinceIso = sinceDate.toISOString();
|
|
202
|
-
const config = await loadConfig(this.configPath);
|
|
203
|
-
const stateDir = expandHome(config.state_dir);
|
|
204
|
-
const db = openAudit(stateDir);
|
|
205
|
-
try {
|
|
206
|
-
return buildPromotedCases(queryPromotableFetches(db, sinceIso));
|
|
207
|
-
} finally {
|
|
208
|
-
db.close();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export class RemoteAdapter implements ContextAdapter {
|
|
214
|
-
private serverUrl: string;
|
|
215
|
-
private tokenEnv?: string;
|
|
216
|
-
private allowInsecure: boolean;
|
|
217
|
-
|
|
218
|
-
constructor(target: { server: string; token_env?: string }, opts?: ContextAdapterOptions) {
|
|
219
|
-
this.serverUrl = target.server.replace(/\/$/, "");
|
|
220
|
-
this.tokenEnv = target.token_env;
|
|
221
|
-
this.allowInsecure = opts?.allowInsecure ?? false;
|
|
222
|
-
|
|
223
|
-
this.validateSecurity();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private validateSecurity(): void {
|
|
227
|
-
try {
|
|
228
|
-
const url = new URL(this.serverUrl);
|
|
229
|
-
if (url.protocol === "http:" && !isLoopbackHost(url.hostname) && !this.allowInsecure) {
|
|
230
|
-
throw new Error(
|
|
231
|
-
`Plaintext HTTP admin targets are not allowed for non-loopback server "${this.serverUrl}". Pass --allow-insecure to bypass.`
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
} catch (err: any) {
|
|
235
|
-
if (err.message.includes("Plaintext HTTP")) throw err;
|
|
236
|
-
throw new Error(`Invalid server URL "${this.serverUrl}"`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
private getAuthHeader(): Record<string, string> {
|
|
241
|
-
const envVar = this.tokenEnv || "SKILLMUX_ADMIN_TOKEN";
|
|
242
|
-
const token = process.env[envVar];
|
|
243
|
-
if (!token) {
|
|
244
|
-
throw new Error(`Environment variable "${envVar}" for administrative authentication is empty`);
|
|
245
|
-
}
|
|
246
|
-
return { Authorization: `Bearer ${token}` };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private async fetchJson(path: string, options: RequestInit = {}): Promise<{ status: number; headers: Headers; data: any }> {
|
|
250
|
-
const url = `${this.serverUrl}${path}`;
|
|
251
|
-
const headers = new Headers(options.headers);
|
|
252
|
-
for (const [k, v] of Object.entries(this.getAuthHeader())) {
|
|
253
|
-
headers.set(k, v);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const res = await fetch(url, { ...options, headers });
|
|
258
|
-
const text = await res.text();
|
|
259
|
-
let data: any = text;
|
|
260
|
-
try {
|
|
261
|
-
data = JSON.parse(text);
|
|
262
|
-
} catch {
|
|
263
|
-
// text
|
|
264
|
-
}
|
|
265
|
-
if (res.status === 401 || res.status === 403) {
|
|
266
|
-
const message = typeof data === "object" && data ? data.message || data.error || data : data;
|
|
267
|
-
throw new CliError(`Remote server rejected the request (${res.status}): ${message}`, 3);
|
|
268
|
-
}
|
|
269
|
-
return { status: res.status, headers: res.headers, data };
|
|
270
|
-
} catch (err: any) {
|
|
271
|
-
if (err instanceof CliError) throw err;
|
|
272
|
-
throw new CliError(`Failed to reach remote server "${this.serverUrl}": ${err.message}`, 3);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async getCapabilities(): Promise<Capabilities> {
|
|
277
|
-
const { status, data } = await this.fetchJson("/admin/v1/capabilities");
|
|
278
|
-
if (status !== 200) {
|
|
279
|
-
throw new Error(`Remote capability discovery failed (${status}): ${typeof data === "object" ? data.message || data.error : data}`);
|
|
280
|
-
}
|
|
281
|
-
return data as Capabilities;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async getConfigShow(): Promise<{ effective: Config; sources: Record<string, string>; active_revision: string }> {
|
|
285
|
-
const { status, data } = await this.fetchJson("/admin/v1/config");
|
|
286
|
-
if (status !== 200) {
|
|
287
|
-
throw new Error(`Remote config fetch failed (${status}): ${typeof data === "object" ? data.message || data.error : data}`);
|
|
288
|
-
}
|
|
289
|
-
return data;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async getConfigGet(key: string): Promise<unknown> {
|
|
293
|
-
validateDottedKey(key);
|
|
294
|
-
const { effective } = await this.getConfigShow();
|
|
295
|
-
return getNestedValue(effective as any, key);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async configValidate(): Promise<{ valid: boolean; readiness: unknown }> {
|
|
299
|
-
const show = await this.getConfigShow();
|
|
300
|
-
return { valid: true, readiness: show.effective };
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
async configDiff(): Promise<{ diff: Record<string, { prior: unknown; resulting: unknown }> }> {
|
|
304
|
-
const { effective, sources } = await this.getConfigShow();
|
|
305
|
-
const diff: Record<string, { prior: unknown; resulting: unknown }> = {};
|
|
306
|
-
for (const [k, src] of Object.entries(sources)) {
|
|
307
|
-
if (src !== "default") {
|
|
308
|
-
diff[k] = { prior: "default", resulting: getNestedValue(effective as any, k) };
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return { diff };
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async configSet(key: string, rawValStr: string, opts?: { dryRun?: boolean }): Promise<SetConfigResult> {
|
|
315
|
-
const caps = await this.getCapabilities();
|
|
316
|
-
if (!caps.config_write || caps.persistence === "externally_managed") {
|
|
317
|
-
throw new CliError("Remote server configuration is externally managed or read-only", 4);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const { status: showStatus, headers: showHeaders, data: showData } = await this.fetchJson("/admin/v1/config");
|
|
321
|
-
if (showStatus !== 200) {
|
|
322
|
-
throw new Error(`Remote config fetch failed (${showStatus})`);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const etag = showHeaders.get("etag") || `"${showData.active_revision}"`;
|
|
326
|
-
|
|
327
|
-
if (opts?.dryRun) {
|
|
328
|
-
const priorVal = getNestedValue(showData.effective, key);
|
|
329
|
-
return {
|
|
330
|
-
ok: true,
|
|
331
|
-
key,
|
|
332
|
-
prior_val: priorVal,
|
|
333
|
-
resulting_val: rawValStr,
|
|
334
|
-
target: "remote",
|
|
335
|
-
prior_revision: showData.active_revision,
|
|
336
|
-
resulting_revision: showData.active_revision,
|
|
337
|
-
persistence: "not_persisted",
|
|
338
|
-
application: "activated",
|
|
339
|
-
readiness: { status: "ready", capability: "hybrid" },
|
|
340
|
-
restart_required_keys: [],
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const { status, data } = await this.fetchJson("/admin/v1/config", {
|
|
345
|
-
method: "PATCH",
|
|
346
|
-
headers: {
|
|
347
|
-
"Content-Type": "application/json",
|
|
348
|
-
"If-Match": etag,
|
|
349
|
-
},
|
|
350
|
-
body: JSON.stringify({ changes: { [key]: rawValStr } }),
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
if (status === 409) {
|
|
354
|
-
if (data?.error === "CONFIG_REVISION_CONFLICT") {
|
|
355
|
-
throw new CliError(`Revision conflict: ${data.message || "Remote configuration was modified concurrently"}`, 4);
|
|
356
|
-
}
|
|
357
|
-
if (data?.error === "CONFIG_EXTERNALLY_MANAGED") {
|
|
358
|
-
throw new CliError("Configuration is externally managed on the remote server", 4);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (status !== 200) {
|
|
363
|
-
throw new Error(`Remote config set failed (${status}): ${data?.message || data?.error || data}`);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return data;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
async configStatus(): Promise<ConfigStatusResponse> {
|
|
370
|
-
const { status, data } = await this.fetchJson("/admin/v1/config");
|
|
371
|
-
if (status !== 200) {
|
|
372
|
-
throw new Error(`Remote status fetch failed (${status}): ${data?.message || data}`);
|
|
373
|
-
}
|
|
374
|
-
return data.runtime;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
async getStats(since: string): Promise<StatsResponse> {
|
|
378
|
-
const searchParams = new URLSearchParams({ since });
|
|
379
|
-
const { status, data } = await this.fetchJson(`/stats?${searchParams.toString()}`);
|
|
380
|
-
if (status !== 200) {
|
|
381
|
-
throw new Error(`Remote stats fetch failed (${status}): ${typeof data === "object" ? data?.message || data?.error || JSON.stringify(data) : data}`);
|
|
382
|
-
}
|
|
383
|
-
return data as StatsResponse;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async auditPrune(opts?: AuditPruneOptions): Promise<AuditPruneResult> {
|
|
387
|
-
const { status, data } = await this.fetchJson("/admin/v1/audit/prune", {
|
|
388
|
-
method: "POST",
|
|
389
|
-
headers: { "Content-Type": "application/json" },
|
|
390
|
-
body: JSON.stringify({
|
|
391
|
-
...(opts?.older_than ? { older_than: opts.older_than } : {}),
|
|
392
|
-
...(opts?.dry_run !== undefined ? { dry_run: opts.dry_run } : {}),
|
|
393
|
-
...(opts?.confirm !== undefined ? { confirm: opts.confirm } : {}),
|
|
394
|
-
}),
|
|
395
|
-
});
|
|
396
|
-
if (status !== 200) {
|
|
397
|
-
const message = typeof data === "object" ? data?.message || data?.error || JSON.stringify(data) : data;
|
|
398
|
-
throw new Error(`Remote audit prune failed (${status}): ${message}`);
|
|
399
|
-
}
|
|
400
|
-
return data as AuditPruneResult;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async auditCount(older_than?: string): Promise<AuditPruneResult> {
|
|
404
|
-
return this.auditPrune({ older_than, dry_run: true });
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
async evalRun(): Promise<EvalReport> {
|
|
408
|
-
const { status, data } = await this.fetchJson("/admin/v1/eval", {
|
|
409
|
-
method: "POST",
|
|
410
|
-
});
|
|
411
|
-
if (status !== 200) {
|
|
412
|
-
const message = typeof data === "object" ? data?.message || data?.error || JSON.stringify(data) : data;
|
|
413
|
-
throw new Error(`Remote eval failed (${status}): ${message}`);
|
|
414
|
-
}
|
|
415
|
-
return data as EvalReport;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async evalPromote(since: string): Promise<EvalCase[]> {
|
|
419
|
-
const { status, data } = await this.fetchJson("/admin/v1/eval/promote", {
|
|
420
|
-
method: "POST",
|
|
421
|
-
headers: { "Content-Type": "application/json" },
|
|
422
|
-
body: JSON.stringify({ since }),
|
|
423
|
-
});
|
|
424
|
-
if (status !== 200) {
|
|
425
|
-
const message = typeof data === "object" ? data?.message || data?.error || JSON.stringify(data) : data;
|
|
426
|
-
throw new Error(`Remote eval promote failed (${status}): ${message}`);
|
|
427
|
-
}
|
|
428
|
-
return data.candidates as EvalCase[];
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
export function createContextAdapter(context: ResolvedContext, opts?: ContextAdapterOptions): ContextAdapter {
|
|
433
|
-
if (context.type === "local") {
|
|
434
|
-
return new LocalAdapter(opts);
|
|
435
|
-
} else {
|
|
436
|
-
return new RemoteAdapter({ server: context.server, token_env: context.token_env }, opts);
|
|
437
|
-
}
|
|
438
|
-
}
|
package/src/audit.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { AuditRow } from "./types";
|
|
2
|
-
|
|
3
|
-
/** Shape an audit row to exactly the AuditRow fields — nothing extra survives. */
|
|
4
|
-
export function buildAuditRow(row: AuditRow): AuditRow {
|
|
5
|
-
const built: AuditRow = {
|
|
6
|
-
id: row.id,
|
|
7
|
-
ts: row.ts,
|
|
8
|
-
request_id: row.request_id,
|
|
9
|
-
query: row.query,
|
|
10
|
-
retrieval: row.retrieval,
|
|
11
|
-
candidates: row.candidates.map((c) => ({ skill_id: c.skill_id, score: c.score })),
|
|
12
|
-
latency_ms: row.latency_ms,
|
|
13
|
-
};
|
|
14
|
-
if (row.degraded_from !== undefined && row.degraded_from !== null) {
|
|
15
|
-
built.degraded_from = row.degraded_from;
|
|
16
|
-
}
|
|
17
|
-
if (row.degradation_reason !== undefined && row.degradation_reason !== null) {
|
|
18
|
-
built.degradation_reason = row.degradation_reason;
|
|
19
|
-
}
|
|
20
|
-
return built;
|
|
21
|
-
}
|