peerllm-host-cli 0.2.0 → 0.5.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/dist/cli/banner.d.ts +9 -0
- package/dist/cli/banner.d.ts.map +1 -0
- package/dist/cli/banner.js +50 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +4 -0
- package/dist/cli/commands/dashboard.d.ts.map +1 -0
- package/dist/cli/commands/dashboard.js +572 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/models.js +8 -8
- package/dist/cli/commands/models.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +5 -4
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +2 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tui.d.ts +52 -0
- package/dist/cli/tui.d.ts.map +1 -0
- package/dist/cli/tui.js +177 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/core/catalog.d.ts +10 -7
- package/dist/core/catalog.d.ts.map +1 -1
- package/dist/core/catalog.js +10 -2
- package/dist/core/catalog.js.map +1 -1
- package/dist/core/host-stats.d.ts +54 -0
- package/dist/core/host-stats.d.ts.map +1 -0
- package/dist/core/host-stats.js +148 -0
- package/dist/core/host-stats.js.map +1 -0
- package/dist/core/model-download.d.ts.map +1 -1
- package/dist/core/model-download.js +9 -16
- package/dist/core/model-download.js.map +1 -1
- package/dist/core/npm-version.d.ts +23 -0
- package/dist/core/npm-version.d.ts.map +1 -0
- package/dist/core/npm-version.js +73 -0
- package/dist/core/npm-version.js.map +1 -0
- package/dist/core/orchestrator.d.ts +9 -0
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +25 -4
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/token-rate.d.ts +14 -0
- package/dist/core/token-rate.d.ts.map +1 -0
- package/dist/core/token-rate.js +36 -0
- package/dist/core/token-rate.js.map +1 -0
- package/dist/daemon/methods.d.ts +4 -0
- package/dist/daemon/methods.d.ts.map +1 -1
- package/dist/daemon/methods.js +58 -6
- package/dist/daemon/methods.js.map +1 -1
- package/dist/daemon/run.d.ts.map +1 -1
- package/dist/daemon/run.js +10 -0
- package/dist/daemon/run.js.map +1 -1
- package/dist/shared/protocol.d.ts +41 -1
- package/dist/shared/protocol.d.ts.map +1 -1
- package/dist/shared/protocol.js +1 -1
- package/package.json +2 -2
package/dist/core/catalog.js
CHANGED
|
@@ -9,10 +9,18 @@ export class CatalogService {
|
|
|
9
9
|
const token = (await this.auth.getValidAccessToken()) ?? undefined;
|
|
10
10
|
const result = await this.backend.request({
|
|
11
11
|
method: "GET",
|
|
12
|
-
path: "/
|
|
12
|
+
path: "/api/llms",
|
|
13
13
|
bearerToken: token,
|
|
14
14
|
});
|
|
15
|
-
|
|
15
|
+
if (Array.isArray(result))
|
|
16
|
+
return result;
|
|
17
|
+
if ("models" in result && Array.isArray(result.models))
|
|
18
|
+
return result.models;
|
|
19
|
+
if ("data" in result && Array.isArray(result.data))
|
|
20
|
+
return result.data;
|
|
21
|
+
if ("llms" in result && Array.isArray(result.llms))
|
|
22
|
+
return result.llms;
|
|
23
|
+
return [];
|
|
16
24
|
}
|
|
17
25
|
async get(modelId) {
|
|
18
26
|
const all = await this.list();
|
package/dist/core/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/core/catalog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/core/catalog.ts"],"names":[],"mappings":"AAmBA,MAAM,OAAO,cAAc;IAEN;IACA;IAFnB,YACmB,OAAsB,EACtB,IAAiB;QADjB,YAAO,GAAP,OAAO,CAAe;QACtB,SAAI,GAAJ,IAAI,CAAa;IACjC,CAAC;IAEJ,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,IAAI,SAAS,CAAC;QACnE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAEvC;YACA,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,KAAK;SACnB,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACzC,IAAI,QAAQ,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,MAAM,CAAC;QAC7E,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QACvE,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QACvE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAe;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AuthService } from "./auth.js";
|
|
2
|
+
import { BackendClient } from "./backend-client.js";
|
|
3
|
+
import type { ConfigService } from "./config.js";
|
|
4
|
+
export interface HostSnapshot {
|
|
5
|
+
isApproved: boolean;
|
|
6
|
+
conversations: number;
|
|
7
|
+
servedTokens: number;
|
|
8
|
+
incomingTokens: number;
|
|
9
|
+
averageResponseMs: number;
|
|
10
|
+
currentUptime: string;
|
|
11
|
+
totalUptime: string;
|
|
12
|
+
fetchedAt: number;
|
|
13
|
+
}
|
|
14
|
+
export interface NetworkSnapshot {
|
|
15
|
+
activeHosts: number;
|
|
16
|
+
approvedHosts: number;
|
|
17
|
+
totalHosts: number;
|
|
18
|
+
totalServedTokens: number;
|
|
19
|
+
networkUptimePct: number;
|
|
20
|
+
fetchedAt: number;
|
|
21
|
+
}
|
|
22
|
+
export interface EarningsSnapshot {
|
|
23
|
+
pendingAmount: number;
|
|
24
|
+
currency: string;
|
|
25
|
+
fetchedAt: number;
|
|
26
|
+
}
|
|
27
|
+
export interface StatsFetchError {
|
|
28
|
+
source: "host" | "network" | "earnings";
|
|
29
|
+
message: string;
|
|
30
|
+
at: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class HostStatsService {
|
|
33
|
+
private readonly backend;
|
|
34
|
+
private readonly auth;
|
|
35
|
+
private readonly config;
|
|
36
|
+
private timer;
|
|
37
|
+
private host;
|
|
38
|
+
private network;
|
|
39
|
+
private earnings;
|
|
40
|
+
private lastError;
|
|
41
|
+
private tickCount;
|
|
42
|
+
constructor(backend: BackendClient, auth: AuthService, config: ConfigService);
|
|
43
|
+
start(intervalMs?: number): void;
|
|
44
|
+
stop(): void;
|
|
45
|
+
getHostSnapshot(): HostSnapshot | null;
|
|
46
|
+
getNetworkSnapshot(): NetworkSnapshot | null;
|
|
47
|
+
getEarningsSnapshot(): EarningsSnapshot | null;
|
|
48
|
+
getLastError(): StatsFetchError | null;
|
|
49
|
+
tick(): Promise<void>;
|
|
50
|
+
private fetchHost;
|
|
51
|
+
private fetchNetwork;
|
|
52
|
+
private fetchEarnings;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=host-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-stats.d.ts","sourceRoot":"","sources":["../../src/core/host-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAuC,MAAM,qBAAqB,CAAC;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AA2BD,qBAAa,gBAAgB;IASzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAVzB,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,SAAS,CAAK;gBAGH,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa;IAGxC,KAAK,CAAC,UAAU,GAAE,MAAwB,GAAG,IAAI;IAMjD,IAAI,IAAI,IAAI;IAOZ,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAI5C,mBAAmB,IAAI,gBAAgB,GAAG,IAAI;IAI9C,YAAY,IAAI,eAAe,GAAG,IAAI;IAIhC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA8Cb,SAAS;YAkBT,YAAY;YAeZ,aAAa;CAc5B"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { BackendError, NotAuthenticatedError } from "./backend-client.js";
|
|
2
|
+
import { getLogger } from "./logger.js";
|
|
3
|
+
const DEFAULT_POLL_MS = 10_000; // matches the Electron host's stats loop cadence
|
|
4
|
+
const EARNINGS_POLL_EVERY = 6; // every 6 ticks ≈ 60s — payouts move slowly
|
|
5
|
+
export class HostStatsService {
|
|
6
|
+
backend;
|
|
7
|
+
auth;
|
|
8
|
+
config;
|
|
9
|
+
timer = null;
|
|
10
|
+
host = null;
|
|
11
|
+
network = null;
|
|
12
|
+
earnings = null;
|
|
13
|
+
lastError = null;
|
|
14
|
+
tickCount = 0;
|
|
15
|
+
constructor(backend, auth, config) {
|
|
16
|
+
this.backend = backend;
|
|
17
|
+
this.auth = auth;
|
|
18
|
+
this.config = config;
|
|
19
|
+
}
|
|
20
|
+
start(intervalMs = DEFAULT_POLL_MS) {
|
|
21
|
+
if (this.timer)
|
|
22
|
+
return;
|
|
23
|
+
void this.tick();
|
|
24
|
+
this.timer = setInterval(() => void this.tick(), intervalMs);
|
|
25
|
+
}
|
|
26
|
+
stop() {
|
|
27
|
+
if (this.timer) {
|
|
28
|
+
clearInterval(this.timer);
|
|
29
|
+
this.timer = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
getHostSnapshot() {
|
|
33
|
+
return this.host;
|
|
34
|
+
}
|
|
35
|
+
getNetworkSnapshot() {
|
|
36
|
+
return this.network;
|
|
37
|
+
}
|
|
38
|
+
getEarningsSnapshot() {
|
|
39
|
+
return this.earnings;
|
|
40
|
+
}
|
|
41
|
+
getLastError() {
|
|
42
|
+
return this.lastError;
|
|
43
|
+
}
|
|
44
|
+
async tick() {
|
|
45
|
+
const cfg = await this.config.load();
|
|
46
|
+
const doEarnings = this.tickCount++ % EARNINGS_POLL_EVERY === 0;
|
|
47
|
+
const tasks = [
|
|
48
|
+
cfg.hostId ? this.fetchHost(cfg.hostId) : Promise.resolve(null),
|
|
49
|
+
this.fetchNetwork(),
|
|
50
|
+
];
|
|
51
|
+
if (doEarnings && cfg.userId) {
|
|
52
|
+
tasks.push(this.fetchEarnings(cfg.userId));
|
|
53
|
+
}
|
|
54
|
+
const results = await Promise.allSettled(tasks);
|
|
55
|
+
let nextError = null;
|
|
56
|
+
const hostResult = results[0];
|
|
57
|
+
if (hostResult && hostResult.status === "fulfilled") {
|
|
58
|
+
const value = hostResult.value;
|
|
59
|
+
if (value)
|
|
60
|
+
this.host = value;
|
|
61
|
+
}
|
|
62
|
+
else if (hostResult && hostResult.status === "rejected") {
|
|
63
|
+
nextError = { source: "host", message: errorMessage(hostResult.reason), at: Date.now() };
|
|
64
|
+
getLogger().warn(`host-stats: /api/hosts failed: ${nextError.message}`);
|
|
65
|
+
}
|
|
66
|
+
const netResult = results[1];
|
|
67
|
+
if (netResult && netResult.status === "fulfilled") {
|
|
68
|
+
this.network = netResult.value;
|
|
69
|
+
}
|
|
70
|
+
else if (netResult && netResult.status === "rejected") {
|
|
71
|
+
nextError = { source: "network", message: errorMessage(netResult.reason), at: Date.now() };
|
|
72
|
+
getLogger().warn(`host-stats: /api/networks/stats failed: ${nextError.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (doEarnings && results[2]) {
|
|
75
|
+
const earnResult = results[2];
|
|
76
|
+
if (earnResult.status === "fulfilled") {
|
|
77
|
+
const value = earnResult.value;
|
|
78
|
+
if (value)
|
|
79
|
+
this.earnings = value;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
nextError = { source: "earnings", message: errorMessage(earnResult.reason), at: Date.now() };
|
|
83
|
+
getLogger().warn(`host-stats: /api/users/payouts failed: ${nextError.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.lastError = nextError;
|
|
87
|
+
}
|
|
88
|
+
async fetchHost(hostId) {
|
|
89
|
+
// Public endpoint — matches Electron's unauthenticated fetch.
|
|
90
|
+
const res = await this.backend.request({
|
|
91
|
+
method: "GET",
|
|
92
|
+
path: `/api/hosts/${hostId}`,
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
isApproved: res.isApproved !== false,
|
|
96
|
+
conversations: numberOr(res.conversationsAssigned, 0),
|
|
97
|
+
servedTokens: numberOr(res.servedTokens, 0),
|
|
98
|
+
incomingTokens: numberOr(res.incomingTokensDb, 0),
|
|
99
|
+
averageResponseMs: numberOr(res.averageResponseMs, 0),
|
|
100
|
+
currentUptime: stringOr(res.currentUptime, "unknown"),
|
|
101
|
+
totalUptime: stringOr(res.totalUptime, "unknown"),
|
|
102
|
+
fetchedAt: Date.now(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async fetchNetwork() {
|
|
106
|
+
const res = await this.backend.request({
|
|
107
|
+
method: "GET",
|
|
108
|
+
path: "/api/networks/stats",
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
activeHosts: numberOr(res.activeHosts, 0),
|
|
112
|
+
approvedHosts: numberOr(res.approvedHosts, 0),
|
|
113
|
+
totalHosts: numberOr(res.totalHosts, 0),
|
|
114
|
+
totalServedTokens: numberOr(res.totalServedTokens, 0),
|
|
115
|
+
networkUptimePct: numberOr(res.networkUptimePct, 0),
|
|
116
|
+
fetchedAt: Date.now(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async fetchEarnings(userId) {
|
|
120
|
+
const token = await this.auth.getValidAccessToken();
|
|
121
|
+
if (!token)
|
|
122
|
+
return null;
|
|
123
|
+
const res = await this.backend.request({
|
|
124
|
+
method: "GET",
|
|
125
|
+
path: `/api/users/${userId}/payouts?pageSize=1`,
|
|
126
|
+
bearerToken: token,
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
pendingAmount: numberOr(res.summary?.pendingAmount, 0),
|
|
130
|
+
currency: stringOr(res.summary?.currency, "USD"),
|
|
131
|
+
fetchedAt: Date.now(),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function numberOr(v, fallback) {
|
|
136
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
137
|
+
}
|
|
138
|
+
function stringOr(v, fallback) {
|
|
139
|
+
return typeof v === "string" && v.length > 0 ? v : fallback;
|
|
140
|
+
}
|
|
141
|
+
function errorMessage(err) {
|
|
142
|
+
if (err instanceof NotAuthenticatedError)
|
|
143
|
+
return "not authenticated";
|
|
144
|
+
if (err instanceof BackendError)
|
|
145
|
+
return `HTTP ${err.status}: ${err.message}`;
|
|
146
|
+
return err instanceof Error ? err.message : String(err);
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=host-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-stats.js","sourceRoot":"","sources":["../../src/core/host-stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../../src/core/model-download.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-download.d.ts","sourceRoot":"","sources":["../../src/core/model-download.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAMjD,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,YAAY,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3C,mDAAmD;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAChB,eAAe,GACf,wBAAwB,GACxB,eAAe,GACf,WAAW,GACX,yBAAyB,GACzB,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,oBAAoB,GACpB,SAAS;gBAZb,OAAO,EAAE,MAAM,EACC,IAAI,EAChB,eAAe,GACf,wBAAwB,GACxB,eAAe,GACf,WAAW,GACX,yBAAyB,GACzB,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,oBAAoB,GACpB,SAAS;CAIhB;AAOD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmG/E;AAgCD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAG1E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQ7D"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { createWriteStream } from "node:fs";
|
|
3
2
|
import { mkdir, open, rename, stat, statfs, unlink } from "node:fs/promises";
|
|
4
3
|
import { dirname, join } from "node:path";
|
|
@@ -14,11 +13,15 @@ export class DownloadError extends Error {
|
|
|
14
13
|
this.code = code;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
16
|
+
function buildHuggingFaceUrl(entry) {
|
|
17
|
+
return `https://huggingface.co/${entry.repo}/resolve/main/${entry.file}`;
|
|
18
|
+
}
|
|
17
19
|
export async function downloadCatalogModel(opts) {
|
|
18
20
|
const { destPath, entry } = opts;
|
|
19
21
|
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const downloadUrl = buildHuggingFaceUrl(entry);
|
|
23
|
+
if (!/^https:\/\//i.test(downloadUrl)) {
|
|
24
|
+
throw new DownloadError(`catalog url must be https: ${downloadUrl}`, "url_not_https");
|
|
22
25
|
}
|
|
23
26
|
if ((await stat(destPath).catch(() => null)) !== null) {
|
|
24
27
|
throw new DownloadError(`destination already exists: ${destPath}`, "destination_exists");
|
|
@@ -27,7 +30,7 @@ export async function downloadCatalogModel(opts) {
|
|
|
27
30
|
await mkdir(destDir, { recursive: true });
|
|
28
31
|
let response;
|
|
29
32
|
try {
|
|
30
|
-
response = await fetch(
|
|
33
|
+
response = await fetch(downloadUrl, { signal: opts.signal });
|
|
31
34
|
}
|
|
32
35
|
catch (err) {
|
|
33
36
|
if (err.name === "AbortError") {
|
|
@@ -36,7 +39,7 @@ export async function downloadCatalogModel(opts) {
|
|
|
36
39
|
throw new DownloadError(`network error: ${err.message}`, "network_error");
|
|
37
40
|
}
|
|
38
41
|
if (!response.ok || !response.body) {
|
|
39
|
-
throw new DownloadError(`HTTP ${response.status} fetching ${
|
|
42
|
+
throw new DownloadError(`HTTP ${response.status} fetching ${downloadUrl}`, "http_error");
|
|
40
43
|
}
|
|
41
44
|
const contentLengthRaw = response.headers.get("content-length");
|
|
42
45
|
if (!contentLengthRaw) {
|
|
@@ -49,13 +52,9 @@ export async function downloadCatalogModel(opts) {
|
|
|
49
52
|
if (contentLength > maxBytes) {
|
|
50
53
|
throw new DownloadError(`file is ${contentLength} bytes; exceeds limit of ${maxBytes}`, "too_large");
|
|
51
54
|
}
|
|
52
|
-
if (Math.abs(contentLength - entry.sizeBytes) > 1024) {
|
|
53
|
-
throw new DownloadError(`Content-Length (${contentLength}) does not match catalog sizeBytes (${entry.sizeBytes})`, "size_mismatch");
|
|
54
|
-
}
|
|
55
55
|
await assertFreeSpace(destDir, contentLength);
|
|
56
56
|
const partPath = `${destPath}.part`;
|
|
57
57
|
await unlink(partPath).catch(() => undefined);
|
|
58
|
-
const hash = createHash("sha256");
|
|
59
58
|
const fileStream = createWriteStream(partPath);
|
|
60
59
|
const start = Date.now();
|
|
61
60
|
let bytesDownloaded = 0;
|
|
@@ -71,7 +70,6 @@ export async function downloadCatalogModel(opts) {
|
|
|
71
70
|
return;
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
|
-
hash.update(chunk);
|
|
75
73
|
bytesDownloaded += chunk.length;
|
|
76
74
|
if (onProgress) {
|
|
77
75
|
const elapsed = (Date.now() - start) / 1000;
|
|
@@ -93,11 +91,6 @@ export async function downloadCatalogModel(opts) {
|
|
|
93
91
|
}
|
|
94
92
|
throw new DownloadError(`download failed: ${err.message}`, "network_error");
|
|
95
93
|
}
|
|
96
|
-
const digest = hash.digest("hex").toLowerCase();
|
|
97
|
-
if (digest !== entry.sha256.toLowerCase()) {
|
|
98
|
-
await unlink(partPath).catch(() => undefined);
|
|
99
|
-
throw new DownloadError(`sha256 mismatch: expected ${entry.sha256}, got ${digest}`, "sha256_mismatch");
|
|
100
|
-
}
|
|
101
94
|
await verifyGgufHeader(partPath).catch(async (err) => {
|
|
102
95
|
await unlink(partPath).catch(() => undefined);
|
|
103
96
|
throw err;
|
|
@@ -133,7 +126,7 @@ async function assertFreeSpace(dir, needBytes) {
|
|
|
133
126
|
}
|
|
134
127
|
}
|
|
135
128
|
export function destPathFor(modelsDir, entry) {
|
|
136
|
-
const fileBase = entry.
|
|
129
|
+
const fileBase = entry.file.endsWith(".gguf") ? entry.file : `${entry.file}.gguf`;
|
|
137
130
|
return join(modelsDir, fileBase);
|
|
138
131
|
}
|
|
139
132
|
export function formatBytes(bytes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-download.js","sourceRoot":"","sources":["../../src/core/model-download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"model-download.js","sourceRoot":"","sources":["../../src/core/model-download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;AACnE,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ;AAC3D,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAqBlC,MAAM,OAAO,aAAc,SAAQ,KAAK;IAGpB;IAFlB,YACE,OAAe,EACC,IAWH;QAEb,KAAK,CAAC,OAAO,CAAC,CAAC;QAbC,SAAI,GAAJ,IAAI,CAWP;IAGf,CAAC;CACF;AAED,SAAS,mBAAmB,CAAC,KAAmB;IAC9C,OAAO,0BAA0B,KAAK,CAAC,IAAI,iBAAiB,KAAK,CAAC,IAAI,EAAE,CAAC;AAC3E,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAqB;IAC9D,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IAEpD,MAAM,WAAW,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,aAAa,CAAC,8BAA8B,WAAW,EAAE,EAAE,eAAe,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,aAAa,CAAC,+BAA+B,QAAQ,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAAa,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACzC,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,kBAAmB,GAAa,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,aAAa,CAAC,QAAQ,QAAQ,CAAC,MAAM,aAAa,WAAW,EAAE,EAAE,YAAY,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,aAAa,CACrB,6CAA6C,EAC7C,wBAAwB,CACzB,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,aAAa,CACrB,2BAA2B,gBAAgB,EAAE,EAC7C,wBAAwB,CACzB,CAAC;IACJ,CAAC;IACD,IAAI,aAAa,GAAG,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,aAAa,CACrB,WAAW,aAAa,4BAA4B,QAAQ,EAAE,EAC9D,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,MAAM,eAAe,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,GAAG,QAAQ,OAAO,CAAC;IACpC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAE9C,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAE9B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACnC,sFAAsF;IACtF,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAa,CAAC,CAAC;IAExD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QAClC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,iBAAiB,GAAG,IAAI,CAAC;YACzB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,MAAM,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,sCAAsC,EAAE,cAAc,CAAC,CAAC,CAAC;gBAC1F,OAAO;YACT,CAAC;QACH,CAAC;QACD,eAAe,IAAI,KAAK,CAAC,MAAM,CAAC;QAChC,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC;YAC5C,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,eAAe,CAAC,CAAC;YAC/D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3D,UAAU,CAAC,EAAE,eAAe,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,GAAG,YAAY,aAAa;YAAE,MAAM,GAAG,CAAC;QAC5C,IAAK,GAAa,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACzC,MAAM,IAAI,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,oBAAqB,GAAa,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACnD,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9C,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IAC9C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAClD,IAAI,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,aAAa,CAAC,qCAAqC,EAAE,cAAc,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,SAAiB;IAC3D,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,qBAAqB,CAAC,CAAC;QAC9D,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CACrB,mCAAmC,GAAG,UAAU,IAAI,gBAAgB,QAAQ,EAAE,EAC9E,yBAAyB,CAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,aAAa;YAAE,MAAM,GAAG,CAAC;QAC5C,8DAA8D;IAChE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,KAAmB;IAChE,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,OAAO,CAAC;IAClF,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAChE,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IACrE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAA2B;IACnD,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;IACnD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polls the public npm registry for the latest published version of the CLI.
|
|
3
|
+
* This is the right source for "update available?" indicators — the backend's
|
|
4
|
+
* `latestVersion` field on /api/hosts/{id} tracks the Electron desktop app's
|
|
5
|
+
* release stream, not the npm-published CLI.
|
|
6
|
+
*/
|
|
7
|
+
export declare class NpmVersionService {
|
|
8
|
+
private timer;
|
|
9
|
+
private latestVersion;
|
|
10
|
+
private lastFetchAt;
|
|
11
|
+
start(intervalMs?: number): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
getLatestVersion(): string | null;
|
|
14
|
+
tick(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns true when `latest` is strictly newer than `current`.
|
|
18
|
+
* Both inputs are expected to be semver-ish "major.minor.patch" strings.
|
|
19
|
+
* Falls back to a string compare when parsing fails — good enough for
|
|
20
|
+
* a UI hint, not a security boundary.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isNewerVersion(current: string, latest: string): boolean;
|
|
23
|
+
//# sourceMappingURL=npm-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-version.d.ts","sourceRoot":"","sources":["../../src/core/npm-version.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,WAAW,CAAuB;IAE1C,KAAK,CAAC,UAAU,GAAE,MAAwB,GAAG,IAAI;IAMjD,IAAI,IAAI,IAAI;IAOZ,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAI3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkB5B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAOvE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getLogger } from "./logger.js";
|
|
2
|
+
const NPM_PACKAGE = "peerllm-host-cli";
|
|
3
|
+
const NPM_REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
4
|
+
const DEFAULT_POLL_MS = 12 * 60 * 60 * 1000; // 12h — npm releases don't move quickly
|
|
5
|
+
/**
|
|
6
|
+
* Polls the public npm registry for the latest published version of the CLI.
|
|
7
|
+
* This is the right source for "update available?" indicators — the backend's
|
|
8
|
+
* `latestVersion` field on /api/hosts/{id} tracks the Electron desktop app's
|
|
9
|
+
* release stream, not the npm-published CLI.
|
|
10
|
+
*/
|
|
11
|
+
export class NpmVersionService {
|
|
12
|
+
timer = null;
|
|
13
|
+
latestVersion = null;
|
|
14
|
+
lastFetchAt = null;
|
|
15
|
+
start(intervalMs = DEFAULT_POLL_MS) {
|
|
16
|
+
if (this.timer)
|
|
17
|
+
return;
|
|
18
|
+
void this.tick();
|
|
19
|
+
this.timer = setInterval(() => void this.tick(), intervalMs);
|
|
20
|
+
}
|
|
21
|
+
stop() {
|
|
22
|
+
if (this.timer) {
|
|
23
|
+
clearInterval(this.timer);
|
|
24
|
+
this.timer = null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getLatestVersion() {
|
|
28
|
+
return this.latestVersion;
|
|
29
|
+
}
|
|
30
|
+
async tick() {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(NPM_REGISTRY_URL, {
|
|
33
|
+
headers: { Accept: "application/json" },
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
getLogger().warn(`npm-version: registry returned HTTP ${res.status}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const body = (await res.json());
|
|
40
|
+
if (typeof body.version === "string" && body.version.length > 0) {
|
|
41
|
+
this.latestVersion = body.version;
|
|
42
|
+
this.lastFetchAt = Date.now();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
getLogger().warn(`npm-version: fetch failed: ${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns true when `latest` is strictly newer than `current`.
|
|
52
|
+
* Both inputs are expected to be semver-ish "major.minor.patch" strings.
|
|
53
|
+
* Falls back to a string compare when parsing fails — good enough for
|
|
54
|
+
* a UI hint, not a security boundary.
|
|
55
|
+
*/
|
|
56
|
+
export function isNewerVersion(current, latest) {
|
|
57
|
+
const c = parseSemver(current);
|
|
58
|
+
const l = parseSemver(latest);
|
|
59
|
+
if (!c || !l)
|
|
60
|
+
return current !== latest;
|
|
61
|
+
if (l[0] !== c[0])
|
|
62
|
+
return l[0] > c[0];
|
|
63
|
+
if (l[1] !== c[1])
|
|
64
|
+
return l[1] > c[1];
|
|
65
|
+
return l[2] > c[2];
|
|
66
|
+
}
|
|
67
|
+
function parseSemver(v) {
|
|
68
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
69
|
+
if (!m)
|
|
70
|
+
return null;
|
|
71
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=npm-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-version.js","sourceRoot":"","sources":["../../src/core/npm-version.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ConfigService } from "./config.js";
|
|
2
2
|
import { DaemonEventBus } from "./events.js";
|
|
3
3
|
import type { ResolvedPaths } from "./paths.js";
|
|
4
|
+
import { type TokenRateStats } from "./token-rate.js";
|
|
4
5
|
export type OrchestratorStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
5
6
|
export interface OrchestratorOptions {
|
|
6
7
|
paths: ResolvedPaths;
|
|
@@ -20,6 +21,9 @@ export declare class OrchestratorService {
|
|
|
20
21
|
private readonly url;
|
|
21
22
|
private readonly sharedRunner;
|
|
22
23
|
private readonly sessions;
|
|
24
|
+
private readonly tokensIn;
|
|
25
|
+
private readonly tokensOut;
|
|
26
|
+
private approvedModels;
|
|
23
27
|
constructor(opts: OrchestratorOptions);
|
|
24
28
|
getStatus(): OrchestratorStatus;
|
|
25
29
|
getRunnerStats(): {
|
|
@@ -28,6 +32,11 @@ export declare class OrchestratorService {
|
|
|
28
32
|
modelName: string;
|
|
29
33
|
gpuLayers: number;
|
|
30
34
|
};
|
|
35
|
+
getTokenStats(): {
|
|
36
|
+
in: TokenRateStats;
|
|
37
|
+
out: TokenRateStats;
|
|
38
|
+
};
|
|
39
|
+
getApprovedModels(): string[];
|
|
31
40
|
updateHostName(hostName: string): Promise<void>;
|
|
32
41
|
connect(): Promise<void>;
|
|
33
42
|
disconnect(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AA2BxE,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAelB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAdjC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAsC;IAEpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgE;IACzF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,cAAc,CAAgB;gBAET,IAAI,EAAE,mBAAmB;IAQtD,SAAS,IAAI,kBAAkB;IAI/B,cAAc,IAAI;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAIvG,aAAa,IAAI;QAAE,EAAE,EAAE,cAAc,CAAC;QAAC,GAAG,EAAE,cAAc,CAAA;KAAE;IAI5D,iBAAiB,IAAI,MAAM,EAAE;IAIvB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAsHxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,yBAAyB;YAkCzB,YAAY;IA0E1B,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,UAAU;YAWJ,IAAI;IASlB,OAAO,CAAC,SAAS;CAKlB"}
|
|
@@ -3,6 +3,7 @@ import { HttpTransportType, HubConnectionBuilder, LogLevel, } from "@microsoft/s
|
|
|
3
3
|
import { getLogger } from "./logger.js";
|
|
4
4
|
import { listModelsSync } from "./models-fs.js";
|
|
5
5
|
import { SharedGGUFRunner } from "./shared-runner.js";
|
|
6
|
+
import { TokenRateTracker } from "./token-rate.js";
|
|
6
7
|
const DEFAULT_HUB_URL = "https://api.peerllm.com/hosthub";
|
|
7
8
|
const HEARTBEAT_INTERVAL_MS = 5 * 1000;
|
|
8
9
|
const CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
@@ -19,6 +20,9 @@ export class OrchestratorService {
|
|
|
19
20
|
url;
|
|
20
21
|
sharedRunner;
|
|
21
22
|
sessions = new Map();
|
|
23
|
+
tokensIn = new TokenRateTracker();
|
|
24
|
+
tokensOut = new TokenRateTracker();
|
|
25
|
+
approvedModels = [];
|
|
22
26
|
constructor(opts) {
|
|
23
27
|
this.opts = opts;
|
|
24
28
|
this.url = opts.hubUrl ?? DEFAULT_HUB_URL;
|
|
@@ -33,6 +37,12 @@ export class OrchestratorService {
|
|
|
33
37
|
getRunnerStats() {
|
|
34
38
|
return this.sharedRunner.getStats();
|
|
35
39
|
}
|
|
40
|
+
getTokenStats() {
|
|
41
|
+
return { in: this.tokensIn.getStats(), out: this.tokensOut.getStats() };
|
|
42
|
+
}
|
|
43
|
+
getApprovedModels() {
|
|
44
|
+
return [...this.approvedModels];
|
|
45
|
+
}
|
|
36
46
|
async updateHostName(hostName) {
|
|
37
47
|
if (this.connection?.state !== "Connected")
|
|
38
48
|
return;
|
|
@@ -95,6 +105,7 @@ export class OrchestratorService {
|
|
|
95
105
|
connection.on("Registered", (payload) => {
|
|
96
106
|
const hostId = payload?.HostId ?? payload?.hostId ?? "(unknown)";
|
|
97
107
|
const approved = payload?.ApprovedModels ?? payload?.approvedModels ?? [];
|
|
108
|
+
this.approvedModels = approved;
|
|
98
109
|
getLogger().info(`orchestrator: registered as ${hostId}, ${approved.length} approved model(s)`);
|
|
99
110
|
});
|
|
100
111
|
connection.on("Warning", (message) => {
|
|
@@ -232,19 +243,29 @@ export class OrchestratorService {
|
|
|
232
243
|
promptOptions.maxTokens = req.maxTokens;
|
|
233
244
|
if (req.temperature != null)
|
|
234
245
|
promptOptions.temperature = req.temperature;
|
|
235
|
-
let
|
|
246
|
+
let chunkCount = 0;
|
|
236
247
|
const startTime = Date.now();
|
|
237
|
-
await this.sharedRunner.prompt(conversationId, promptText, async (chunk) => {
|
|
238
|
-
|
|
248
|
+
const result = await this.sharedRunner.prompt(conversationId, promptText, async (chunk) => {
|
|
249
|
+
chunkCount++;
|
|
250
|
+
this.tokensOut.add(1); // streaming rate driven by emit chunks
|
|
239
251
|
await this.send("EmitToken", {
|
|
240
252
|
requestId,
|
|
241
253
|
content: chunk,
|
|
242
254
|
isFinal: false,
|
|
243
255
|
});
|
|
244
256
|
}, promptOptions);
|
|
257
|
+
// Reconcile streaming approximation with the runner's authoritative counts.
|
|
258
|
+
// `tokensOut` was incremented per chunk; replace with the runner's exact total
|
|
259
|
+
// by adding the delta (positive or zero).
|
|
260
|
+
const outDelta = (result?.completionTokens ?? chunkCount) - chunkCount;
|
|
261
|
+
if (outDelta > 0)
|
|
262
|
+
this.tokensOut.add(outDelta);
|
|
263
|
+
this.tokensIn.add(result?.promptTokens ?? 0);
|
|
245
264
|
await this.send("EmitToken", { requestId, content: "", isFinal: true });
|
|
246
265
|
session.lastActive = Date.now();
|
|
247
|
-
getLogger().info(`orchestrator: prompt complete ${conversationId}
|
|
266
|
+
getLogger().info(`orchestrator: prompt complete ${conversationId} ` +
|
|
267
|
+
`(in=${result?.promptTokens ?? 0}, out=${result?.completionTokens ?? chunkCount}, ` +
|
|
268
|
+
`${Date.now() - startTime}ms)`);
|
|
248
269
|
}
|
|
249
270
|
catch (err) {
|
|
250
271
|
const message = err.message;
|