@genkaku/protocol 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/bridge.d.ts +46 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +609 -0
- package/dist/capabilities.d.ts +50 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +66 -0
- package/dist/capabilities.test.d.ts +2 -0
- package/dist/capabilities.test.d.ts.map +1 -0
- package/dist/capabilities.test.js +39 -0
- package/dist/chain.d.ts +496 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +763 -0
- package/dist/credit.d.ts +35 -0
- package/dist/credit.d.ts.map +1 -0
- package/dist/credit.js +112 -0
- package/dist/dial.d.ts +51 -0
- package/dist/dial.d.ts.map +1 -0
- package/dist/dial.js +287 -0
- package/dist/directory.d.ts +39 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +149 -0
- package/dist/discovery.d.ts +20 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +58 -0
- package/dist/hello.d.ts +14 -0
- package/dist/hello.d.ts.map +1 -0
- package/dist/hello.js +42 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/keys.d.ts +30 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +126 -0
- package/dist/llmBridge.d.ts +100 -0
- package/dist/llmBridge.d.ts.map +1 -0
- package/dist/llmBridge.js +284 -0
- package/dist/lp.d.ts +31 -0
- package/dist/lp.d.ts.map +1 -0
- package/dist/lp.js +116 -0
- package/dist/modeManager.d.ts +30 -0
- package/dist/modeManager.d.ts.map +1 -0
- package/dist/modeManager.js +81 -0
- package/dist/netconfig.d.ts +11 -0
- package/dist/netconfig.d.ts.map +1 -0
- package/dist/netconfig.js +39 -0
- package/dist/offer.d.ts +89 -0
- package/dist/offer.d.ts.map +1 -0
- package/dist/offer.js +86 -0
- package/dist/offer.test.d.ts +2 -0
- package/dist/offer.test.d.ts.map +1 -0
- package/dist/offer.test.js +33 -0
- package/dist/ollama.d.ts +53 -0
- package/dist/ollama.d.ts.map +1 -0
- package/dist/ollama.js +162 -0
- package/dist/p2pNode.d.ts +52 -0
- package/dist/p2pNode.d.ts.map +1 -0
- package/dist/p2pNode.js +454 -0
- package/dist/paywall.d.ts +30 -0
- package/dist/paywall.d.ts.map +1 -0
- package/dist/paywall.js +84 -0
- package/dist/publishOptIn.d.ts +15 -0
- package/dist/publishOptIn.d.ts.map +1 -0
- package/dist/publishOptIn.js +34 -0
- package/dist/receipts.d.ts +61 -0
- package/dist/receipts.d.ts.map +1 -0
- package/dist/receipts.js +135 -0
- package/dist/registry.d.ts +19 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +50 -0
- package/dist/rpc.d.ts +24 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +143 -0
- package/dist/status.d.ts +56 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +354 -0
- package/dist/userSession.d.ts +77 -0
- package/dist/userSession.d.ts.map +1 -0
- package/dist/userSession.js +132 -0
- package/dist/workerSession.d.ts +87 -0
- package/dist/workerSession.d.ts.map +1 -0
- package/dist/workerSession.js +97 -0
- package/dist/workerSession.test.d.ts +2 -0
- package/dist/workerSession.test.d.ts.map +1 -0
- package/dist/workerSession.test.js +49 -0
- package/package.json +56 -3
- package/README.md +0 -3
package/dist/offer.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type Offer = {
|
|
2
|
+
/**
|
|
3
|
+
* Board revision this row was read from. `offers/1` adds the capability
|
|
4
|
+
* fields below; a v0 row is still valid and is treated as a chat-only model,
|
|
5
|
+
* because a worker that cannot report capabilities cannot be trusted to
|
|
6
|
+
* support tools either.
|
|
7
|
+
*/
|
|
8
|
+
proto: 'offers/0' | 'offers/1';
|
|
9
|
+
workerName: string;
|
|
10
|
+
model: string;
|
|
11
|
+
/** 6-decimal USDC per hour of usage, as bigint */
|
|
12
|
+
pricePerHour: bigint;
|
|
13
|
+
/** coarse, self-reported (privacy: never street-level) */
|
|
14
|
+
city?: string;
|
|
15
|
+
/** 0–100; v0 local heuristic, chain-verified in P4.4 */
|
|
16
|
+
creditScore: number;
|
|
17
|
+
/** completed sessions; join into creditScore on-chain later */
|
|
18
|
+
sessionsServed: number;
|
|
19
|
+
/** where creditScore came from: chain-derived (P4.9h) or the worker's own
|
|
20
|
+
* claim. The UI should only show a chain score as a score. */
|
|
21
|
+
creditSource?: 'chain' | 'self';
|
|
22
|
+
/** measured RTT to this worker at discovery time (ms) */
|
|
23
|
+
latencyMs?: number;
|
|
24
|
+
/** parallel sessions the worker accepts */
|
|
25
|
+
maxConcurrent?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Ollama's capability list for the model being sold (`tools`, `thinking`,
|
|
28
|
+
* `vision`, …). Present on `offers/1` rows.
|
|
29
|
+
*/
|
|
30
|
+
capabilities?: string[];
|
|
31
|
+
/** raw `details.parameter_size`, e.g. '9.0B'. Present on `offers/1` rows. */
|
|
32
|
+
parameterSize?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Marketplace tier derived from capabilities + size. `chat` is what the UI
|
|
35
|
+
* labels "not suitable for agents"; it is a label, not a filter.
|
|
36
|
+
*/
|
|
37
|
+
tier?: ModelTier;
|
|
38
|
+
/** true when `workerWallet` was proven by the P4.9e hello handshake rather
|
|
39
|
+
* than self-reported. Priced sessions should require this. */
|
|
40
|
+
walletVerified?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The worker's EVM wallet (escrow settle recipient). The client REPLACES the
|
|
43
|
+
* self-reported value with the wallet proven by the P4.9e hello handshake and
|
|
44
|
+
* sets `walletVerified`; an offer whose claim contradicts its proof is
|
|
45
|
+
* dropped before it is ever listed.
|
|
46
|
+
*/
|
|
47
|
+
workerWallet?: string;
|
|
48
|
+
};
|
|
49
|
+
/** JSON-safe shape used over /genkaku/offers/0 frames. */
|
|
50
|
+
export type OfferWire = Omit<Offer, 'pricePerHour'> & {
|
|
51
|
+
pricePerHour: string;
|
|
52
|
+
};
|
|
53
|
+
export declare function toWire(offer: Offer): OfferWire;
|
|
54
|
+
export declare function fromWire(wire: OfferWire): Offer;
|
|
55
|
+
export declare function ceilDiv(a: bigint, b: bigint): bigint;
|
|
56
|
+
/**
|
|
57
|
+
* Cumulative billed amount for a session. `unitMs` must divide 3600000
|
|
58
|
+
* (v0 default 60000 → 60 units/hour). Rate per unit is rounded UP once per
|
|
59
|
+
* hour — never per tick — so the user-perceived hourly price never exceeds
|
|
60
|
+
* the quoted pricePerHour, and the worker's per-tick exposure stays bounded.
|
|
61
|
+
*/
|
|
62
|
+
export declare function cumulativeBilled(pricePerHour: bigint, unitMs: number, elapsedMs: number): bigint;
|
|
63
|
+
import type { ModelTier } from './capabilities.ts';
|
|
64
|
+
export type OfferSortKey = 'creditScore' | 'pricePerHour' | 'workerName' | 'model' | 'city' | 'latencyMs';
|
|
65
|
+
/** One offer row with the address it arrived from. */
|
|
66
|
+
export type AddressedOffer = {
|
|
67
|
+
addr: string;
|
|
68
|
+
offer: Offer;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Collapse the same worker offer reached through several multiaddrs. A stable
|
|
72
|
+
* peer can be announced over IPv4, IPv6 and relay addresses; the board should
|
|
73
|
+
* list the offer once, not once per address.
|
|
74
|
+
* @param rows - flattened offer rows in discovery order.
|
|
75
|
+
* @returns rows with duplicate (wallet-or-address, model, price) entries removed.
|
|
76
|
+
*/
|
|
77
|
+
export declare function dedupeOffers(rows: readonly AddressedOffer[]): AddressedOffer[];
|
|
78
|
+
/** One-key sort with stable tiebreakers (name asc, then model asc). */
|
|
79
|
+
export declare function sortOffers(offers: Offer[], key?: OfferSortKey, dir?: 'asc' | 'desc'): Offer[];
|
|
80
|
+
export type OfferFilter = {
|
|
81
|
+
model?: string;
|
|
82
|
+
maxPricePerHour?: bigint;
|
|
83
|
+
minCredit?: number;
|
|
84
|
+
city?: string;
|
|
85
|
+
/** case-insensitive substring across workerName/model/city */
|
|
86
|
+
query?: string;
|
|
87
|
+
};
|
|
88
|
+
export declare function filterOffers(offers: Offer[], f: OfferFilter): Offer[];
|
|
89
|
+
//# sourceMappingURL=offer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer.d.ts","sourceRoot":"","sources":["../src/offer.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,KAAK,GAAG;IAClB;;;;;OAKG;IACH,KAAK,EAAE,UAAU,GAAG,UAAU,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAA;IACpB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAA;IACtB;mEAC+D;IAC/D,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC/B,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;mEAC+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9E,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAE9C;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,KAAK,CAI/C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIpD;AAID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAahG;AAED,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAA;AAWzG,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAA;AAE3D;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,EAAE,CAW9E;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,GAAE,YAA4B,EAAE,GAAG,GAAE,KAAK,GAAG,MAAe,GAAG,KAAK,EAAE,CAOpH;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,WAAW,GAAG,KAAK,EAAE,CASrE"}
|
package/dist/offer.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// GENKAKU: worker offer board (P4.3 marketplace).
|
|
2
|
+
// A worker publishes one Offer row per (model, price) it chooses to serve.
|
|
3
|
+
// Price is FREELY set by the worker and quoted PER HOUR of user usage time
|
|
4
|
+
// (MockUSDC 6 decimals in testnet; canonical USDC 6 decimals in prod). Billing
|
|
5
|
+
// accrues per STARTED MINUTE: worker's max unbilled exposure = one minute.
|
|
6
|
+
// Credit score: v0 is a local heuristic (sessions served); P4.4 upgrades it to
|
|
7
|
+
// the on-chain settled-claim ratio from JobEscrow/WorkerRegistry events.
|
|
8
|
+
export function toWire(offer) {
|
|
9
|
+
return { ...offer, pricePerHour: offer.pricePerHour.toString() };
|
|
10
|
+
}
|
|
11
|
+
export function fromWire(wire) {
|
|
12
|
+
// Preserve the revision the row arrived with: a v0 row must not be upgraded
|
|
13
|
+
// silently, or it would look like a worker that reported capabilities.
|
|
14
|
+
return { ...wire, pricePerHour: BigInt(wire.pricePerHour) };
|
|
15
|
+
}
|
|
16
|
+
export function ceilDiv(a, b) {
|
|
17
|
+
if (b <= 0n) {
|
|
18
|
+
throw new Error('[genkaku:pricing] divisor must be positive');
|
|
19
|
+
}
|
|
20
|
+
return (a + b - 1n) / b;
|
|
21
|
+
}
|
|
22
|
+
const MS_PER_HOUR = 3600000n;
|
|
23
|
+
/**
|
|
24
|
+
* Cumulative billed amount for a session. `unitMs` must divide 3600000
|
|
25
|
+
* (v0 default 60000 → 60 units/hour). Rate per unit is rounded UP once per
|
|
26
|
+
* hour — never per tick — so the user-perceived hourly price never exceeds
|
|
27
|
+
* the quoted pricePerHour, and the worker's per-tick exposure stays bounded.
|
|
28
|
+
*/
|
|
29
|
+
export function cumulativeBilled(pricePerHour, unitMs, elapsedMs) {
|
|
30
|
+
if (unitMs <= 0) {
|
|
31
|
+
throw new Error('[genkaku:pricing] unitMs must be positive');
|
|
32
|
+
}
|
|
33
|
+
const unit = BigInt(unitMs);
|
|
34
|
+
if (MS_PER_HOUR % unit !== 0n) {
|
|
35
|
+
throw new Error('[genkaku:pricing] unitMs must divide 3600000');
|
|
36
|
+
}
|
|
37
|
+
const units = Math.ceil(elapsedMs / unitMs);
|
|
38
|
+
if (units <= 0) {
|
|
39
|
+
return 0n;
|
|
40
|
+
}
|
|
41
|
+
return BigInt(units) * ceilDiv(pricePerHour, MS_PER_HOUR / unit);
|
|
42
|
+
}
|
|
43
|
+
const comparators = {
|
|
44
|
+
creditScore: (a, b) => a.creditScore - b.creditScore,
|
|
45
|
+
pricePerHour: (a, b) => (a.pricePerHour <= b.pricePerHour ? -1 : 1),
|
|
46
|
+
workerName: (a, b) => a.workerName.localeCompare(b.workerName),
|
|
47
|
+
model: (a, b) => a.model.localeCompare(b.model),
|
|
48
|
+
city: (a, b) => (a.city ?? '').localeCompare(b.city ?? ''),
|
|
49
|
+
latencyMs: (a, b) => (a.latencyMs ?? Number.POSITIVE_INFINITY) - (b.latencyMs ?? Number.POSITIVE_INFINITY),
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Collapse the same worker offer reached through several multiaddrs. A stable
|
|
53
|
+
* peer can be announced over IPv4, IPv6 and relay addresses; the board should
|
|
54
|
+
* list the offer once, not once per address.
|
|
55
|
+
* @param rows - flattened offer rows in discovery order.
|
|
56
|
+
* @returns rows with duplicate (wallet-or-address, model, price) entries removed.
|
|
57
|
+
*/
|
|
58
|
+
export function dedupeOffers(rows) {
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const row of rows) {
|
|
62
|
+
const worker = row.offer.workerWallet ?? row.addr;
|
|
63
|
+
const key = `${worker}|${row.offer.model}|${row.offer.pricePerHour.toString()}`;
|
|
64
|
+
if (seen.has(key))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(key);
|
|
67
|
+
out.push(row);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/** One-key sort with stable tiebreakers (name asc, then model asc). */
|
|
72
|
+
export function sortOffers(offers, key = 'creditScore', dir = 'desc') {
|
|
73
|
+
const mul = dir === 'desc' ? -1 : 1;
|
|
74
|
+
return [...offers].sort((a, b) => mul * comparators[key](a, b)
|
|
75
|
+
|| comparators.workerName(a, b)
|
|
76
|
+
|| comparators.model(a, b));
|
|
77
|
+
}
|
|
78
|
+
export function filterOffers(offers, f) {
|
|
79
|
+
const q = f.query?.toLowerCase();
|
|
80
|
+
return offers.filter(o => (!f.model || o.model === f.model)
|
|
81
|
+
&& (!f.minCredit || o.creditScore >= f.minCredit)
|
|
82
|
+
&& (!f.city || o.city === f.city)
|
|
83
|
+
&& (!q || `${o.workerName} ${o.model} ${o.city ?? ''}`.toLowerCase().includes(q))
|
|
84
|
+
&& (!f.maxPricePerHour || o.pricePerHour <= f.maxPricePerHour));
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=offer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer.test.d.ts","sourceRoot":"","sources":["../src/offer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { fromWire, toWire } from "./offer.js";
|
|
4
|
+
const base = {
|
|
5
|
+
proto: 'offers/1',
|
|
6
|
+
workerName: 'worker-a',
|
|
7
|
+
model: 'qwen3.8:27b',
|
|
8
|
+
pricePerHour: 6000000n,
|
|
9
|
+
creditScore: 80,
|
|
10
|
+
sessionsServed: 3,
|
|
11
|
+
capabilities: ['completion', 'tools'],
|
|
12
|
+
parameterSize: '27B',
|
|
13
|
+
tier: 'agent',
|
|
14
|
+
};
|
|
15
|
+
test('offers/1 survives the wire round trip', () => {
|
|
16
|
+
const wire = toWire(base);
|
|
17
|
+
assert.equal(wire.pricePerHour, '6000000');
|
|
18
|
+
assert.deepEqual(fromWire(wire), base);
|
|
19
|
+
});
|
|
20
|
+
test('a v0 row stays v0 after decoding, so it cannot pose as capability-aware', () => {
|
|
21
|
+
const v0 = {
|
|
22
|
+
proto: 'offers/0',
|
|
23
|
+
workerName: 'old-worker',
|
|
24
|
+
model: 'llama3:8b',
|
|
25
|
+
pricePerHour: 1000000n,
|
|
26
|
+
creditScore: 10,
|
|
27
|
+
sessionsServed: 0,
|
|
28
|
+
};
|
|
29
|
+
const decoded = fromWire(toWire(v0));
|
|
30
|
+
assert.equal(decoded.proto, 'offers/0');
|
|
31
|
+
assert.equal(decoded.tier, undefined);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=offer.test.js.map
|
package/dist/ollama.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type ModelInfo, type PullProgress } from './llmBridge.ts';
|
|
2
|
+
/** Facts the settings page renders before it offers a next step. */
|
|
3
|
+
export type OllamaState = {
|
|
4
|
+
/** an `ollama` executable was found on this machine */
|
|
5
|
+
installed: boolean;
|
|
6
|
+
/** the local HTTP API answers right now */
|
|
7
|
+
running: boolean;
|
|
8
|
+
/** version reported by `GET /api/version`, when running */
|
|
9
|
+
version?: string;
|
|
10
|
+
/** base URL the plugin uses */
|
|
11
|
+
host: string;
|
|
12
|
+
/** official installer page for this platform */
|
|
13
|
+
downloadUrl: string;
|
|
14
|
+
/** installed models, empty when the service is not running */
|
|
15
|
+
models: ModelInfo[];
|
|
16
|
+
/** last detection or start failure, for the onboarding hint */
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
/** Official installer page per platform. */
|
|
20
|
+
export declare function ollamaDownloadUrl(platform?: NodeJS.Platform): string;
|
|
21
|
+
/**
|
|
22
|
+
* Locate an `ollama` executable without trusting the GUI process PATH.
|
|
23
|
+
*
|
|
24
|
+
* A packaged macOS app inherits a minimal PATH, so the Homebrew and app-bundle
|
|
25
|
+
* locations are probed directly; on Windows the per-user installer path is the
|
|
26
|
+
* usual one.
|
|
27
|
+
* @param platform - target platform, injectable for tests.
|
|
28
|
+
* @param environment - environment to read PATH from.
|
|
29
|
+
* @returns absolute executable path, or undefined when Ollama is absent.
|
|
30
|
+
*/
|
|
31
|
+
export declare function locateOllama(platform?: NodeJS.Platform, environment?: NodeJS.ProcessEnv): string | undefined;
|
|
32
|
+
export type OllamaSupervisor = {
|
|
33
|
+
/** Detect the binary, probe the service and list models. */
|
|
34
|
+
state: () => Promise<OllamaState>;
|
|
35
|
+
/** Start `ollama serve` when installed but stopped; idempotent. */
|
|
36
|
+
ensureRunning: () => Promise<OllamaState>;
|
|
37
|
+
/** Download a registry model, forwarding progress frames. */
|
|
38
|
+
pull: (model: string, onProgress?: (p: PullProgress) => void, signal?: AbortSignal) => Promise<{
|
|
39
|
+
status: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Remove a locally installed model. */
|
|
42
|
+
remove: (model: string) => Promise<void>;
|
|
43
|
+
/** Stop the child this supervisor started, if any. */
|
|
44
|
+
dispose: () => void;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Build the worker-side Ollama supervisor.
|
|
48
|
+
* @param host - local Ollama base URL.
|
|
49
|
+
* @param log - diagnostic line sink.
|
|
50
|
+
* @returns supervisor over the local install.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createOllamaSupervisor(host: string, log: (line: string) => void): OllamaSupervisor;
|
|
53
|
+
//# sourceMappingURL=ollama.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../src/ollama.ts"],"names":[],"mappings":"AAWA,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE/E,oEAAoE;AACpE,MAAM,MAAM,WAAW,GAAG;IACxB,uDAAuD;IACvD,SAAS,EAAE,OAAO,CAAA;IAClB,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAA;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAA;IACnB,8DAA8D;IAC9D,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,4CAA4C;AAC5C,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAAG,MAAM,CAItF;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,WAAW,GAAE,MAAM,CAAC,UAAwB,GAC3C,MAAM,GAAG,SAAS,CAuBpB;AASD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAA;IACjC,mEAAmE;IACnE,aAAa,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAA;IACzC,6DAA6D;IAC7D,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClH,wCAAwC;IACxC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,sDAAsD;IACtD,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,gBAAgB,CA0FlG"}
|
package/dist/ollama.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// GENKAKU: local Ollama lifecycle for worker onboarding (P4.7b).
|
|
2
|
+
//
|
|
3
|
+
// The desktop ships no multi-GB runtime. A worker either already runs Ollama
|
|
4
|
+
// or is guided to install it; this module only detects, starts and talks to
|
|
5
|
+
// the local HTTP API. Model management lives in llmBridge.ollamaAdmin.
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { delimiter, join } from 'node:path';
|
|
10
|
+
import { ollamaAdmin } from "./llmBridge.js";
|
|
11
|
+
/** Official installer page per platform. */
|
|
12
|
+
export function ollamaDownloadUrl(platform = process.platform) {
|
|
13
|
+
if (platform === 'darwin')
|
|
14
|
+
return 'https://ollama.com/download/mac';
|
|
15
|
+
if (platform === 'win32')
|
|
16
|
+
return 'https://ollama.com/download/windows';
|
|
17
|
+
return 'https://ollama.com/download/linux';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Locate an `ollama` executable without trusting the GUI process PATH.
|
|
21
|
+
*
|
|
22
|
+
* A packaged macOS app inherits a minimal PATH, so the Homebrew and app-bundle
|
|
23
|
+
* locations are probed directly; on Windows the per-user installer path is the
|
|
24
|
+
* usual one.
|
|
25
|
+
* @param platform - target platform, injectable for tests.
|
|
26
|
+
* @param environment - environment to read PATH from.
|
|
27
|
+
* @returns absolute executable path, or undefined when Ollama is absent.
|
|
28
|
+
*/
|
|
29
|
+
export function locateOllama(platform = process.platform, environment = process.env) {
|
|
30
|
+
const executable = platform === 'win32' ? 'ollama.exe' : 'ollama';
|
|
31
|
+
// Scan PATH directly: a packaged GUI app cannot rely on `which`/`where`
|
|
32
|
+
// because the inherited PATH is minimal and those tools may be unresolvable.
|
|
33
|
+
for (const dir of (environment.PATH ?? '').split(delimiter)) {
|
|
34
|
+
if (dir === '')
|
|
35
|
+
continue;
|
|
36
|
+
const candidate = join(dir, executable);
|
|
37
|
+
if (existsSync(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
const candidates = platform === 'darwin'
|
|
41
|
+
? [
|
|
42
|
+
'/usr/local/bin/ollama',
|
|
43
|
+
'/opt/homebrew/bin/ollama',
|
|
44
|
+
'/Applications/Ollama.app/Contents/Resources/ollama',
|
|
45
|
+
join(homedir(), 'Applications/Ollama.app/Contents/Resources/ollama'),
|
|
46
|
+
]
|
|
47
|
+
: platform === 'win32'
|
|
48
|
+
? [
|
|
49
|
+
join(environment.LOCALAPPDATA ?? join(homedir(), 'AppData/Local'), 'Programs/Ollama/ollama.exe'),
|
|
50
|
+
join(environment.ProgramFiles ?? 'C:/Program Files', 'Ollama/ollama.exe'),
|
|
51
|
+
]
|
|
52
|
+
: ['/usr/local/bin/ollama', '/usr/bin/ollama', '/snap/bin/ollama'];
|
|
53
|
+
return candidates.find(candidate => existsSync(candidate));
|
|
54
|
+
}
|
|
55
|
+
/** Read one JSON document from the local API with a bounded wait. */
|
|
56
|
+
async function getJson(url, timeoutMs = 1500) {
|
|
57
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
58
|
+
if (!response.ok)
|
|
59
|
+
throw new Error(`${url} refused (${response.status})`);
|
|
60
|
+
return await response.json();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build the worker-side Ollama supervisor.
|
|
64
|
+
* @param host - local Ollama base URL.
|
|
65
|
+
* @param log - diagnostic line sink.
|
|
66
|
+
* @returns supervisor over the local install.
|
|
67
|
+
*/
|
|
68
|
+
export function createOllamaSupervisor(host, log) {
|
|
69
|
+
const admin = ollamaAdmin(host);
|
|
70
|
+
const base = host.replace(/\/$/u, '');
|
|
71
|
+
let child;
|
|
72
|
+
let starting;
|
|
73
|
+
const running = async () => {
|
|
74
|
+
try {
|
|
75
|
+
const versionDoc = await getJson(`${base}/api/version`);
|
|
76
|
+
const models = await admin.list();
|
|
77
|
+
return {
|
|
78
|
+
running: true,
|
|
79
|
+
...(typeof versionDoc.version === 'string' ? { version: versionDoc.version } : {}),
|
|
80
|
+
models,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return { running: false, models: [], error: error instanceof Error ? error.message : String(error) };
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const state = async () => {
|
|
88
|
+
const probe = await running();
|
|
89
|
+
const binary = locateOllama();
|
|
90
|
+
if (probe.running) {
|
|
91
|
+
return {
|
|
92
|
+
installed: true,
|
|
93
|
+
running: true,
|
|
94
|
+
...(probe.version === undefined ? {} : { version: probe.version }),
|
|
95
|
+
host, downloadUrl: ollamaDownloadUrl(), models: probe.models,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
installed: binary !== undefined,
|
|
100
|
+
running: false,
|
|
101
|
+
host,
|
|
102
|
+
downloadUrl: ollamaDownloadUrl(),
|
|
103
|
+
models: [],
|
|
104
|
+
...(binary === undefined
|
|
105
|
+
? { error: 'ollama executable not found' }
|
|
106
|
+
: probe.error === undefined ? {} : { error: probe.error }),
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const start = async () => {
|
|
110
|
+
const binary = locateOllama();
|
|
111
|
+
if (binary === undefined)
|
|
112
|
+
return await state();
|
|
113
|
+
const url = new URL(base);
|
|
114
|
+
log(`ollama: starting ${binary} serve (OLLAMA_HOST=${url.host})`);
|
|
115
|
+
const spawned = spawn(binary, ['serve'], {
|
|
116
|
+
env: { ...process.env, OLLAMA_HOST: url.host },
|
|
117
|
+
stdio: 'ignore',
|
|
118
|
+
detached: false,
|
|
119
|
+
});
|
|
120
|
+
child = spawned;
|
|
121
|
+
spawned.once('error', (error) => { log(`ollama spawn failed (${error.message})`); });
|
|
122
|
+
spawned.once('exit', (code, signal) => {
|
|
123
|
+
if (child === spawned)
|
|
124
|
+
child = undefined;
|
|
125
|
+
log(`ollama exited (${code ?? signal ?? 'unknown'})`);
|
|
126
|
+
});
|
|
127
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
128
|
+
const probe = await running();
|
|
129
|
+
if (probe.running)
|
|
130
|
+
return await state();
|
|
131
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
132
|
+
}
|
|
133
|
+
const current = await state();
|
|
134
|
+
return { ...current, error: current.error ?? 'ollama did not answer after 10s' };
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
state,
|
|
138
|
+
ensureRunning: async () => {
|
|
139
|
+
const current = await state();
|
|
140
|
+
if (current.running || !current.installed)
|
|
141
|
+
return current;
|
|
142
|
+
starting ??= start().finally(() => { starting = undefined; });
|
|
143
|
+
return await starting;
|
|
144
|
+
},
|
|
145
|
+
pull: async (model, onProgress, signal) => await admin.pull(model, onProgress, signal),
|
|
146
|
+
remove: async (model) => {
|
|
147
|
+
const response = await fetch(`${base}/api/delete`, {
|
|
148
|
+
method: 'DELETE',
|
|
149
|
+
headers: { 'content-type': 'application/json' },
|
|
150
|
+
body: JSON.stringify({ model }),
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok)
|
|
153
|
+
throw new Error(`ollama delete refused (${response.status})`);
|
|
154
|
+
},
|
|
155
|
+
dispose: () => {
|
|
156
|
+
if (child !== undefined && !child.killed)
|
|
157
|
+
child.kill();
|
|
158
|
+
child = undefined;
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Libp2p } from 'libp2p';
|
|
2
|
+
import { type Hex } from 'viem';
|
|
3
|
+
import type { Paywall } from './paywall.ts';
|
|
4
|
+
import type { LocalChatSource, LocalCompletionSource } from './llmBridge.ts';
|
|
5
|
+
import type { ModeManager } from './modeManager.ts';
|
|
6
|
+
import { type Offer } from './offer.ts';
|
|
7
|
+
import type { SessionEvent } from './workerSession.ts';
|
|
8
|
+
export { FrameReader, lpEncode, lpFrames } from './lp.ts';
|
|
9
|
+
export type { ByteChunk } from './lp.ts';
|
|
10
|
+
export declare const GENKAKU_PROTOCOLS: readonly ["/genkaku/open/0", "/genkaku/token/0", "/genkaku/receipt/0", "/genkaku/settle/0", "/genkaku/offers/0", "/genkaku/policy/0", "/genkaku/hello/0"];
|
|
11
|
+
export type SidecarOptions = {
|
|
12
|
+
modeManager: ModeManager;
|
|
13
|
+
source: LocalCompletionSource;
|
|
14
|
+
/** Serves `open/1` chat requests (messages + tool schemas). */
|
|
15
|
+
chatSource?: LocalChatSource;
|
|
16
|
+
/** ip4/tcp listen addrs; LAN first, NAT traversal (hole punching) later */
|
|
17
|
+
listen: string[];
|
|
18
|
+
/** one Offer row per (model, price) the worker publishes — visible to users */
|
|
19
|
+
catalog: Offer[];
|
|
20
|
+
sessionEvents?: (e: SessionEvent) => void;
|
|
21
|
+
/** P4.9d: refuse to spend GPU time unless the named escrow pays this worker. */
|
|
22
|
+
paywall?: Paywall;
|
|
23
|
+
/** P4.4: with a worker wallet, priced receipts MUST carry a valid signature,
|
|
24
|
+
* offers self-report the wallet, and settle co-signing becomes available. */
|
|
25
|
+
workerKey?: Hex;
|
|
26
|
+
};
|
|
27
|
+
/** One stored (latest) receipt per jobId — the evidence the settle co-sign
|
|
28
|
+
* defends against (spend == max cumulative receipt the client piped). */
|
|
29
|
+
export type StoredReceipt = {
|
|
30
|
+
seq: number;
|
|
31
|
+
amount: bigint;
|
|
32
|
+
sig: string;
|
|
33
|
+
/** the escrow/stream binding used to build the digest (jobId, escrowId) */
|
|
34
|
+
escrowId: string;
|
|
35
|
+
/** commit-challenge commitments carried by the receipt (P4.9f) */
|
|
36
|
+
inputHash: `0x${string}`;
|
|
37
|
+
outputHash: `0x${string}`;
|
|
38
|
+
};
|
|
39
|
+
/** A bare libp2p node for the USER side (no genkaku handlers — dials only). */
|
|
40
|
+
export declare function createUserNode(listen?: string[]): Promise<Libp2p>;
|
|
41
|
+
export type GenkakuSidecar = {
|
|
42
|
+
node: Libp2p;
|
|
43
|
+
peerAddrs: () => string[];
|
|
44
|
+
stop(): Promise<void>;
|
|
45
|
+
activeSessions: () => number;
|
|
46
|
+
/** P4.5: the base58 PeerId string the registry binding pins */
|
|
47
|
+
peerIdStr: () => string;
|
|
48
|
+
/** Replace the served offer board without restarting the sidecar. */
|
|
49
|
+
setCatalog: (offers: Offer[]) => void;
|
|
50
|
+
};
|
|
51
|
+
export declare function createSidecar(opts: SidecarOptions): Promise<GenkakuSidecar>;
|
|
52
|
+
//# sourceMappingURL=p2pNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"p2pNode.d.ts","sourceRoot":"","sources":["../src/p2pNode.ts"],"names":[],"mappings":"AAYA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAClD,OAAO,EAAE,KAAK,GAAG,EAAW,MAAM,MAAM,CAAA;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAE5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,OAAO,EAAoB,KAAK,KAAK,EAAU,MAAM,YAAY,CAAA;AAajE,OAAO,KAAK,EAAgB,YAAY,EAAiB,MAAM,oBAAoB,CAAA;AAGnF,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACzD,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,eAAO,MAAM,iBAAiB,2JAQpB,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,WAAW,CAAA;IACxB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,2EAA2E;IAC3E,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,+EAA+E;IAC/E,OAAO,EAAE,KAAK,EAAE,CAAA;IAChB,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;IACzC,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;kFAC8E;IAC9E,SAAS,CAAC,EAAE,GAAG,CAAA;CAChB,CAAA;AAED;0EAC0E;AAC1E,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB,kEAAkE;IAClE,SAAS,EAAE,KAAK,MAAM,EAAE,CAAA;IACxB,UAAU,EAAE,KAAK,MAAM,EAAE,CAAA;CAC1B,CAAA;AAkBD,+EAA+E;AAC/E,wBAAsB,cAAc,CAAC,MAAM,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAY3E;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,MAAM,EAAE,CAAA;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,cAAc,EAAE,MAAM,MAAM,CAAA;IAC5B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,CAAA;CACtC,CAAA;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAkfjF"}
|