aweshare 0.2.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/LICENSE +21 -0
- package/README.md +246 -0
- package/apps/agent/dist/adapters.d.ts +15 -0
- package/apps/agent/dist/adapters.js +25 -0
- package/apps/agent/dist/adapters.js.map +1 -0
- package/apps/agent/dist/cli.d.ts +2 -0
- package/apps/agent/dist/cli.js +175 -0
- package/apps/agent/dist/cli.js.map +1 -0
- package/apps/agent/dist/config.d.ts +38 -0
- package/apps/agent/dist/config.js +150 -0
- package/apps/agent/dist/config.js.map +1 -0
- package/apps/agent/dist/connection.d.ts +40 -0
- package/apps/agent/dist/connection.js +359 -0
- package/apps/agent/dist/connection.js.map +1 -0
- package/apps/agent/dist/doctor.d.ts +14 -0
- package/apps/agent/dist/doctor.js +125 -0
- package/apps/agent/dist/doctor.js.map +1 -0
- package/apps/agent/dist/health.d.ts +15 -0
- package/apps/agent/dist/health.js +45 -0
- package/apps/agent/dist/health.js.map +1 -0
- package/apps/agent/dist/index.d.ts +5 -0
- package/apps/agent/dist/index.js +6 -0
- package/apps/agent/dist/index.js.map +1 -0
- package/apps/agent/package.json +20 -0
- package/apps/hub/dist/admin.d.ts +10 -0
- package/apps/hub/dist/admin.js +208 -0
- package/apps/hub/dist/admin.js.map +1 -0
- package/apps/hub/dist/auth.d.ts +17 -0
- package/apps/hub/dist/auth.js +28 -0
- package/apps/hub/dist/auth.js.map +1 -0
- package/apps/hub/dist/cli.d.ts +2 -0
- package/apps/hub/dist/cli.js +243 -0
- package/apps/hub/dist/cli.js.map +1 -0
- package/apps/hub/dist/config.d.ts +25 -0
- package/apps/hub/dist/config.js +53 -0
- package/apps/hub/dist/config.js.map +1 -0
- package/apps/hub/dist/consumer.d.ts +18 -0
- package/apps/hub/dist/consumer.js +157 -0
- package/apps/hub/dist/consumer.js.map +1 -0
- package/apps/hub/dist/db.d.ts +91 -0
- package/apps/hub/dist/db.js +236 -0
- package/apps/hub/dist/db.js.map +1 -0
- package/apps/hub/dist/http.d.ts +31 -0
- package/apps/hub/dist/http.js +118 -0
- package/apps/hub/dist/http.js.map +1 -0
- package/apps/hub/dist/limiter.d.ts +35 -0
- package/apps/hub/dist/limiter.js +78 -0
- package/apps/hub/dist/limiter.js.map +1 -0
- package/apps/hub/dist/limits.d.ts +30 -0
- package/apps/hub/dist/limits.js +52 -0
- package/apps/hub/dist/limits.js.map +1 -0
- package/apps/hub/dist/meter.d.ts +25 -0
- package/apps/hub/dist/meter.js +135 -0
- package/apps/hub/dist/meter.js.map +1 -0
- package/apps/hub/dist/sanitize.d.ts +3 -0
- package/apps/hub/dist/sanitize.js +56 -0
- package/apps/hub/dist/sanitize.js.map +1 -0
- package/apps/hub/dist/server.d.ts +25 -0
- package/apps/hub/dist/server.js +77 -0
- package/apps/hub/dist/server.js.map +1 -0
- package/apps/hub/dist/tokens.d.ts +9 -0
- package/apps/hub/dist/tokens.js +35 -0
- package/apps/hub/dist/tokens.js.map +1 -0
- package/apps/hub/dist/tunnel.d.ts +108 -0
- package/apps/hub/dist/tunnel.js +424 -0
- package/apps/hub/dist/tunnel.js.map +1 -0
- package/apps/hub/package.json +21 -0
- package/bin/aweshare.mjs +43 -0
- package/package.json +47 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { ensureDataDir, readAdminToken, resolvePaths } from './config.js';
|
|
4
|
+
import { createHubServer } from './server.js';
|
|
5
|
+
import { loadOrCreateAdminToken, loadOrCreatePepper } from './tokens.js';
|
|
6
|
+
const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
7
|
+
const USAGE = `aweshare hub — control plane for an aweshare hub
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
aweshare hub init create data dir, pepper and admin token
|
|
11
|
+
aweshare hub serve [--host H] [--port N] run the hub server
|
|
12
|
+
aweshare hub token issue --role producer|consumer --name NAME
|
|
13
|
+
aweshare hub token list
|
|
14
|
+
aweshare hub token revoke --role producer|consumer --id N
|
|
15
|
+
aweshare hub grant add --alias ALIAS --consumer NAME [--expires-in 7d|12h|30m|90s]
|
|
16
|
+
aweshare hub grant remove --alias ALIAS --consumer NAME
|
|
17
|
+
aweshare hub grant list
|
|
18
|
+
aweshare hub consumer limits --name NAME show per-consumer overrides
|
|
19
|
+
aweshare hub consumer limits --name NAME [--rps N] [--burst N] [--concurrency N]
|
|
20
|
+
[--tpm N] [--max-total-tokens N]
|
|
21
|
+
set only the keys you pass; unset keys keep the hub-wide defaults.
|
|
22
|
+
--clear removes all overrides (back to hub-wide defaults).
|
|
23
|
+
aweshare hub usage [--alias ALIAS] [--limit N]
|
|
24
|
+
|
|
25
|
+
Environment:
|
|
26
|
+
AWESHARE_HUB_DATA_DIR data directory (default ~/.aweshare-hub)
|
|
27
|
+
AWESHARE_HUB_URL hub base URL for admin commands (default http://127.0.0.1:8787)
|
|
28
|
+
AWESHARE_HUB_PORT listen port for serve (default 8787)`;
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const positional = [];
|
|
31
|
+
const flags = new Map();
|
|
32
|
+
for (let i = 0; i < argv.length; i++) {
|
|
33
|
+
const a = argv[i];
|
|
34
|
+
if (a.startsWith('--')) {
|
|
35
|
+
const eq = a.indexOf('=');
|
|
36
|
+
if (eq >= 0) {
|
|
37
|
+
flags.set(a.slice(2, eq), a.slice(eq + 1));
|
|
38
|
+
}
|
|
39
|
+
else if (i + 1 < argv.length && !argv[i + 1].startsWith('--')) {
|
|
40
|
+
flags.set(a.slice(2), argv[++i]);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
flags.set(a.slice(2), 'true');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
positional.push(a);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { positional, flags };
|
|
51
|
+
}
|
|
52
|
+
function fail(message) {
|
|
53
|
+
console.error(`error: ${message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
/** Node fetch reports "fetch failed" without the reason; surface the cause. */
|
|
57
|
+
function errorMessage(err) {
|
|
58
|
+
if (err instanceof Error) {
|
|
59
|
+
const cause = err.cause;
|
|
60
|
+
return cause ? `${err.message} (${cause.code ?? cause.message})` : err.message;
|
|
61
|
+
}
|
|
62
|
+
return String(err);
|
|
63
|
+
}
|
|
64
|
+
/** Parse a human duration like 90s, 30m, 12h, 7d into milliseconds. */
|
|
65
|
+
function parseDuration(raw) {
|
|
66
|
+
const m = /^(\d+)([smhd])$/.exec(raw);
|
|
67
|
+
if (!m)
|
|
68
|
+
fail(`--expires-in must look like 90s, 30m, 12h or 7d, got '${raw}'`);
|
|
69
|
+
const unit = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2]];
|
|
70
|
+
return Number(m[1]) * unit;
|
|
71
|
+
}
|
|
72
|
+
/** Thin client for the admin REST API using the server-local admin token. */
|
|
73
|
+
function adminApi() {
|
|
74
|
+
const paths = resolvePaths();
|
|
75
|
+
const token = readAdminToken(paths);
|
|
76
|
+
if (!token)
|
|
77
|
+
fail(`no admin token at ${paths.adminTokenFile} — run 'aweshare hub init' first`);
|
|
78
|
+
const base = process.env.AWESHARE_HUB_URL ?? 'http://127.0.0.1:8787';
|
|
79
|
+
return async function call(method, path, body) {
|
|
80
|
+
const res = await fetch(`${base}${path}`, {
|
|
81
|
+
method,
|
|
82
|
+
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
|
|
83
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
const data = (await res.json().catch(() => ({})));
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
const err = data.error;
|
|
88
|
+
fail(`${res.status} ${err?.message ?? res.statusText}`);
|
|
89
|
+
}
|
|
90
|
+
return data;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function main() {
|
|
94
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
95
|
+
const { positional, flags } = parseArgs(rest);
|
|
96
|
+
if (cmd === '-v' || cmd === '--version') {
|
|
97
|
+
console.log(`aweshare hub ${VERSION}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h' || flags.get('help') === 'true') {
|
|
101
|
+
console.log(USAGE);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const paths = resolvePaths();
|
|
105
|
+
switch (cmd) {
|
|
106
|
+
case 'init': {
|
|
107
|
+
ensureDataDir(paths);
|
|
108
|
+
loadOrCreatePepper(paths);
|
|
109
|
+
const adminToken = loadOrCreateAdminToken(paths);
|
|
110
|
+
const hub = createHubServer({ paths }); // creates the db schema
|
|
111
|
+
await hub.close();
|
|
112
|
+
console.log(`data dir: ${paths.dataDir}`);
|
|
113
|
+
console.log(`admin token: ${adminToken}`);
|
|
114
|
+
console.log('(store it now — this is the only time it is printed)');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
case 'serve': {
|
|
118
|
+
const hub = createHubServer({ paths });
|
|
119
|
+
const host = flags.get('host') ?? hub.config.host;
|
|
120
|
+
const port = Number(flags.get('port') ?? hub.config.port);
|
|
121
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
122
|
+
fail(`--port must be an integer between 1 and 65535, got '${flags.get('port')}'`);
|
|
123
|
+
}
|
|
124
|
+
hub.server.listen(port, host, () => {
|
|
125
|
+
hub.log.info({ host, port }, 'aweshare hub listening');
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
case 'token': {
|
|
130
|
+
const api = adminApi();
|
|
131
|
+
const sub = positional[0];
|
|
132
|
+
if (sub === 'issue') {
|
|
133
|
+
const role = flags.get('role');
|
|
134
|
+
const name = flags.get('name');
|
|
135
|
+
if (role !== 'producer' && role !== 'consumer')
|
|
136
|
+
fail('--role must be producer or consumer');
|
|
137
|
+
if (!name)
|
|
138
|
+
fail('--name is required');
|
|
139
|
+
const out = (await api('POST', '/admin/v1/tokens', { role, name }));
|
|
140
|
+
console.log(out.token);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (sub === 'list') {
|
|
144
|
+
console.log(JSON.stringify(await api('GET', '/admin/v1/tokens'), null, 2));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (sub === 'revoke') {
|
|
148
|
+
const role = flags.get('role');
|
|
149
|
+
const id = flags.get('id');
|
|
150
|
+
if (role !== 'producer' && role !== 'consumer')
|
|
151
|
+
fail('--role must be producer or consumer');
|
|
152
|
+
if (!id)
|
|
153
|
+
fail('--id is required');
|
|
154
|
+
console.log(JSON.stringify(await api('DELETE', `/admin/v1/tokens/${role}s/${id}`)));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
fail(`unknown token subcommand: ${sub ?? '(none)'}`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
case 'grant': {
|
|
161
|
+
const api = adminApi();
|
|
162
|
+
const sub = positional[0];
|
|
163
|
+
if (sub === 'add') {
|
|
164
|
+
const alias = flags.get('alias');
|
|
165
|
+
const consumer = flags.get('consumer');
|
|
166
|
+
if (!alias || !consumer)
|
|
167
|
+
fail('--alias and --consumer are required');
|
|
168
|
+
const body = { alias, consumer };
|
|
169
|
+
const expiresIn = flags.get('expires-in');
|
|
170
|
+
if (expiresIn)
|
|
171
|
+
body.expiresAt = new Date(Date.now() + parseDuration(expiresIn)).toISOString();
|
|
172
|
+
console.log(JSON.stringify(await api('PUT', '/admin/v1/grants', body)));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (sub === 'remove') {
|
|
176
|
+
const alias = flags.get('alias');
|
|
177
|
+
const consumer = flags.get('consumer');
|
|
178
|
+
if (!alias || !consumer)
|
|
179
|
+
fail('--alias and --consumer are required');
|
|
180
|
+
console.log(JSON.stringify(await api('DELETE', `/admin/v1/grants?alias=${encodeURIComponent(alias)}&consumer=${encodeURIComponent(consumer)}`)));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (sub === 'list') {
|
|
184
|
+
console.log(JSON.stringify(await api('GET', '/admin/v1/grants'), null, 2));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
fail(`unknown grant subcommand: ${sub ?? '(none)'}`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
case 'consumer': {
|
|
191
|
+
const api = adminApi();
|
|
192
|
+
const sub = positional[0];
|
|
193
|
+
if (sub !== 'limits')
|
|
194
|
+
fail(`unknown consumer subcommand: ${sub ?? '(none)'}`);
|
|
195
|
+
const name = flags.get('name');
|
|
196
|
+
if (!name)
|
|
197
|
+
fail('--name is required');
|
|
198
|
+
const base = `/admin/v1/consumers/${encodeURIComponent(name)}/limits`;
|
|
199
|
+
if (flags.get('clear') === 'true') {
|
|
200
|
+
console.log(JSON.stringify(await api('DELETE', base)));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const body = {};
|
|
204
|
+
for (const [flag, key] of [
|
|
205
|
+
['rps', 'rps'],
|
|
206
|
+
['burst', 'burst'],
|
|
207
|
+
['concurrency', 'maxConcurrent'],
|
|
208
|
+
['tpm', 'tpm'],
|
|
209
|
+
['max-total-tokens', 'maxTotalTokens'],
|
|
210
|
+
]) {
|
|
211
|
+
const value = flags.get(flag);
|
|
212
|
+
if (value !== undefined)
|
|
213
|
+
body[key] = value;
|
|
214
|
+
}
|
|
215
|
+
if (Object.keys(body).length === 0) {
|
|
216
|
+
console.log(JSON.stringify(await api('GET', base), null, 2));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
for (const [key, value] of Object.entries(body)) {
|
|
220
|
+
const n = Number(value);
|
|
221
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
222
|
+
fail(`--${key} must be a positive number, got '${value}'`);
|
|
223
|
+
}
|
|
224
|
+
console.log(JSON.stringify(await api('PUT', base, body)));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
case 'usage': {
|
|
228
|
+
const api = adminApi();
|
|
229
|
+
const params = new URLSearchParams();
|
|
230
|
+
if (flags.get('alias'))
|
|
231
|
+
params.set('alias', flags.get('alias'));
|
|
232
|
+
if (flags.get('limit'))
|
|
233
|
+
params.set('limit', flags.get('limit'));
|
|
234
|
+
const qs = params.toString();
|
|
235
|
+
console.log(JSON.stringify(await api('GET', `/admin/v1/usage${qs ? `?${qs}` : ''}`), null, 2));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
fail(`unknown command: ${cmd}\n\n${USAGE}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
main().catch((err) => fail(errorMessage(err)));
|
|
243
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Hub runtime directory layout (all state in one place, easy to volume-mount). */
|
|
2
|
+
export interface HubPaths {
|
|
3
|
+
dataDir: string;
|
|
4
|
+
dbFile: string;
|
|
5
|
+
pepperFile: string;
|
|
6
|
+
adminTokenFile: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolvePaths(env?: NodeJS.ProcessEnv): HubPaths;
|
|
9
|
+
export declare function ensureDataDir(paths: HubPaths): void;
|
|
10
|
+
/** Read the admin token created by `hub init`; null if not initialized. */
|
|
11
|
+
export declare function readAdminToken(paths: HubPaths): string | null;
|
|
12
|
+
export declare function hubConfigFromEnv(env?: NodeJS.ProcessEnv): {
|
|
13
|
+
host: string;
|
|
14
|
+
port: number;
|
|
15
|
+
/** Per-consumer token bucket, refilled per second. */
|
|
16
|
+
consumerRps: number;
|
|
17
|
+
consumerBurst: number;
|
|
18
|
+
consumerMaxConcurrent: number;
|
|
19
|
+
/** No response head within this window -> 504 TICKET_EXPIRED. */
|
|
20
|
+
headTimeoutMs: number;
|
|
21
|
+
/** No response bytes within this window after head -> 504 STREAM_IDLE_TIMEOUT. */
|
|
22
|
+
idleTimeoutMs: number;
|
|
23
|
+
maxBodyBytes: number;
|
|
24
|
+
};
|
|
25
|
+
export type HubConfig = ReturnType<typeof hubConfigFromEnv>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
export function resolvePaths(env = process.env) {
|
|
5
|
+
const dataDir = env.AWESHARE_HUB_DATA_DIR ?? join(homedir(), '.aweshare-hub');
|
|
6
|
+
return {
|
|
7
|
+
dataDir,
|
|
8
|
+
dbFile: join(dataDir, 'hub.db'),
|
|
9
|
+
pepperFile: join(dataDir, 'pepper'),
|
|
10
|
+
adminTokenFile: join(dataDir, 'admin-token'),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function ensureDataDir(paths) {
|
|
14
|
+
if (!existsSync(paths.dataDir)) {
|
|
15
|
+
mkdirSync(paths.dataDir, { recursive: true, mode: 0o700 });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Read the admin token created by `hub init`; null if not initialized. */
|
|
19
|
+
export function readAdminToken(paths) {
|
|
20
|
+
try {
|
|
21
|
+
return readFileSync(paths.adminTokenFile, 'utf8').trim() || null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Parse a positive number env var; garbage values fail fast with the key named. */
|
|
28
|
+
function positiveNumber(env, key, fallback) {
|
|
29
|
+
const raw = env[key];
|
|
30
|
+
if (raw === undefined || raw === '')
|
|
31
|
+
return fallback;
|
|
32
|
+
const value = Number(raw);
|
|
33
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
34
|
+
throw new Error(`${key} must be a positive number, got '${raw}'`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
export function hubConfigFromEnv(env = process.env) {
|
|
39
|
+
return {
|
|
40
|
+
host: env.AWESHARE_HUB_HOST ?? '0.0.0.0',
|
|
41
|
+
port: positiveNumber(env, 'AWESHARE_HUB_PORT', 8787),
|
|
42
|
+
/** Per-consumer token bucket, refilled per second. */
|
|
43
|
+
consumerRps: positiveNumber(env, 'AWESHARE_CONSUMER_RPS', 10),
|
|
44
|
+
consumerBurst: positiveNumber(env, 'AWESHARE_CONSUMER_BURST', 20),
|
|
45
|
+
consumerMaxConcurrent: positiveNumber(env, 'AWESHARE_CONSUMER_CONCURRENCY', 8),
|
|
46
|
+
/** No response head within this window -> 504 TICKET_EXPIRED. */
|
|
47
|
+
headTimeoutMs: positiveNumber(env, 'AWESHARE_HEAD_TIMEOUT_MS', 120_000),
|
|
48
|
+
/** No response bytes within this window after head -> 504 STREAM_IDLE_TIMEOUT. */
|
|
49
|
+
idleTimeoutMs: positiveNumber(env, 'AWESHARE_IDLE_TIMEOUT_MS', 300_000),
|
|
50
|
+
maxBodyBytes: positiveNumber(env, 'AWESHARE_MAX_BODY_BYTES', 32 * 1024 * 1024),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAUhC,MAAM,UAAU,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,MAAM,OAAO,GAAG,GAAG,CAAC,qBAAqB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAA;IAC7E,OAAO;QACL,OAAO;QACP,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;QAC/B,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;QACnC,cAAc,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;KAC7C,CAAA;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAe;IAC3C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,KAAe;IAC5C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,SAAS,cAAc,CAAC,GAAsB,EAAE,GAAW,EAAE,QAAgB;IAC3E,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;IACpB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAA;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,oCAAoC,GAAG,GAAG,CAAC,CAAA;IACnE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,iBAAiB,IAAI,SAAS;QACxC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,CAAC;QACpD,sDAAsD;QACtD,WAAW,EAAE,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,EAAE,CAAC;QAC7D,aAAa,EAAE,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,EAAE,CAAC;QACjE,qBAAqB,EAAE,cAAc,CAAC,GAAG,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC9E,iEAAiE;QACjE,aAAa,EAAE,cAAc,CAAC,GAAG,EAAE,0BAA0B,EAAE,OAAO,CAAC;QACvE,kFAAkF;QAClF,aAAa,EAAE,cAAc,CAAC,GAAG,EAAE,0BAA0B,EAAE,OAAO,CAAC;QACvE,YAAY,EAAE,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;KAC/E,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { Logger } from 'pino';
|
|
3
|
+
import type { AuthContext } from './auth.js';
|
|
4
|
+
import type { HubConfig } from './config.js';
|
|
5
|
+
import { type Router } from './http.js';
|
|
6
|
+
import type { AliasSlots, ConsumerLimiter, TokenWindow } from './limiter.js';
|
|
7
|
+
import type { Tunnel } from './tunnel.js';
|
|
8
|
+
export interface ConsumerDeps {
|
|
9
|
+
db: Database.Database;
|
|
10
|
+
auth: AuthContext;
|
|
11
|
+
config: HubConfig;
|
|
12
|
+
tunnel: Tunnel;
|
|
13
|
+
limiter: ConsumerLimiter;
|
|
14
|
+
tpm: TokenWindow;
|
|
15
|
+
slots: AliasSlots;
|
|
16
|
+
log: Logger;
|
|
17
|
+
}
|
|
18
|
+
export declare function registerConsumerRoutes(router: Router, deps: ConsumerDeps): void;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { parseAlias } from 'aweshare-protocol';
|
|
2
|
+
import { requireConsumer } from './auth.js';
|
|
3
|
+
import { getGrant, getOffering, insertUsageEvent, listOfferingsForConsumer, sumTokensUsed, } from './db.js';
|
|
4
|
+
import { HttpError, readBody, sendError, sendJson } from './http.js';
|
|
5
|
+
import { resolveLimits, safeParseLimits } from './limits.js';
|
|
6
|
+
import { UsageExtractor } from './meter.js';
|
|
7
|
+
import { sanitizeTunnelRequestHeaders, sanitizeUpstreamResponseHeaders } from './sanitize.js';
|
|
8
|
+
export function registerConsumerRoutes(router, deps) {
|
|
9
|
+
router.add('POST', '/v1/chat/completions', (ctx) => handleInference(ctx, deps, 'openai', '/chat/completions'));
|
|
10
|
+
router.add('POST', '/v1/messages', (ctx) => handleInference(ctx, deps, 'anthropic', '/messages'));
|
|
11
|
+
router.add('POST', '/v1/responses', (ctx) => handleInference(ctx, deps, 'responses', '/responses'));
|
|
12
|
+
router.add('GET', '/v1/models', (ctx) => handleModels(ctx, deps));
|
|
13
|
+
}
|
|
14
|
+
/** The consumer endpoint (and tunnel path) each protocol speaks. */
|
|
15
|
+
const ENDPOINT_FOR_PROTOCOL = {
|
|
16
|
+
openai: '/v1/chat/completions',
|
|
17
|
+
anthropic: '/v1/messages',
|
|
18
|
+
responses: '/v1/responses',
|
|
19
|
+
};
|
|
20
|
+
async function handleInference(ctx, deps, protocol, path) {
|
|
21
|
+
const { db, auth, config, tunnel, limiter, tpm, slots } = deps;
|
|
22
|
+
const { row: consumer } = requireConsumer(ctx.req, auth);
|
|
23
|
+
const raw = await readBody(ctx.req, config.maxBodyBytes);
|
|
24
|
+
let body;
|
|
25
|
+
try {
|
|
26
|
+
body = JSON.parse(raw.toString('utf8') || '{}');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new HttpError(400, 'INVALID_JSON', 'request body is not valid JSON');
|
|
30
|
+
}
|
|
31
|
+
const model = body.model;
|
|
32
|
+
const alias = typeof model === 'string' ? parseAlias(model) : null;
|
|
33
|
+
if (!alias || typeof model !== 'string') {
|
|
34
|
+
throw new HttpError(400, 'PROTOCOL_MISMATCH', `model must be an alias like namespace/name, got: ${String(model)}`);
|
|
35
|
+
}
|
|
36
|
+
const offering = getOffering(db, model);
|
|
37
|
+
if (!offering)
|
|
38
|
+
throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown model alias: ${model}`);
|
|
39
|
+
if (offering.protocol !== protocol) {
|
|
40
|
+
throw new HttpError(400, 'PROTOCOL_MISMATCH', `alias ${model} speaks ${offering.protocol}; use the ${ENDPOINT_FOR_PROTOCOL[offering.protocol]} endpoint`);
|
|
41
|
+
}
|
|
42
|
+
const grant = getGrant(db, offering.producer_id, consumer.id, model);
|
|
43
|
+
if (!grant) {
|
|
44
|
+
throw new HttpError(403, 'NOT_GRANTED', `alias ${model} is not granted to consumer ${consumer.name}`);
|
|
45
|
+
}
|
|
46
|
+
if (grant.expires_at !== null && grant.expires_at <= new Date().toISOString()) {
|
|
47
|
+
throw new HttpError(403, 'GRANT_EXPIRED', `grant for ${model} expired at ${grant.expires_at}`);
|
|
48
|
+
}
|
|
49
|
+
const session = tunnel.getSession(offering.producer_id);
|
|
50
|
+
if (!session)
|
|
51
|
+
throw new HttpError(503, 'PRODUCER_OFFLINE', `producer of ${model} is offline`);
|
|
52
|
+
if (session.backendStatus.get(offering.backend_id)?.state === 'degraded') {
|
|
53
|
+
throw new HttpError(503, 'BACKEND_DEGRADED', `backend for ${model} is degraded`);
|
|
54
|
+
}
|
|
55
|
+
const limits = resolveLimits(safeParseLimits(consumer.config), config);
|
|
56
|
+
if (limits.maxTotalTokens !== null && sumTokensUsed(db, consumer.id) >= limits.maxTotalTokens) {
|
|
57
|
+
throw new HttpError(429, 'QUOTA_EXCEEDED', `consumer ${consumer.name} reached its total token budget (${limits.maxTotalTokens})`);
|
|
58
|
+
}
|
|
59
|
+
if (limits.tpm !== null && !tpm.allow(consumer.id, limits.tpm)) {
|
|
60
|
+
throw new HttpError(429, 'RATE_LIMITED', `consumer ${consumer.name} exceeded ${limits.tpm} TPM`);
|
|
61
|
+
}
|
|
62
|
+
const limited = limiter.admit(consumer.id, limits);
|
|
63
|
+
if (limited)
|
|
64
|
+
throw new HttpError(429, limited, 'consumer rate limit exceeded');
|
|
65
|
+
if (!slots.acquire(model, offering.max_concurrency)) {
|
|
66
|
+
limiter.release(consumer.id);
|
|
67
|
+
throw new HttpError(429, 'PRODUCER_MAX_CONCURRENCY', `alias ${model} is at its max concurrency (${offering.max_concurrency})`);
|
|
68
|
+
}
|
|
69
|
+
// route on the public alias, but hand the producer the real upstream model id
|
|
70
|
+
body.model = offering.upstream_model;
|
|
71
|
+
const outBody = Buffer.from(JSON.stringify(body), 'utf8');
|
|
72
|
+
const query = {};
|
|
73
|
+
for (const [key, value] of ctx.query)
|
|
74
|
+
query[key] = value;
|
|
75
|
+
const relay = tunnel.openRequest(session, {
|
|
76
|
+
protocol,
|
|
77
|
+
backendId: offering.backend_id,
|
|
78
|
+
method: 'POST',
|
|
79
|
+
path,
|
|
80
|
+
query: Object.keys(query).length > 0 ? query : null,
|
|
81
|
+
headers: sanitizeTunnelRequestHeaders(ctx.req),
|
|
82
|
+
body: outBody,
|
|
83
|
+
});
|
|
84
|
+
/** Release admission + write the usage row; exactly once per exit path. */
|
|
85
|
+
const finalize = (result, usage) => {
|
|
86
|
+
limiter.release(consumer.id);
|
|
87
|
+
slots.release(model);
|
|
88
|
+
const counts = usage?.finish();
|
|
89
|
+
if (counts) {
|
|
90
|
+
tpm.record(consumer.id, (counts.promptTokens ?? 0) + (counts.completionTokens ?? 0));
|
|
91
|
+
}
|
|
92
|
+
insertUsageEvent(db, {
|
|
93
|
+
consumer_id: consumer.id,
|
|
94
|
+
producer_id: offering.producer_id,
|
|
95
|
+
alias: model,
|
|
96
|
+
upstream_model: offering.upstream_model,
|
|
97
|
+
protocol,
|
|
98
|
+
streaming: usage?.streaming ? 1 : 0,
|
|
99
|
+
status: result.status,
|
|
100
|
+
error_code: result.errorCode,
|
|
101
|
+
duration_ms: result.durationMs,
|
|
102
|
+
bytes_in: outBody.byteLength,
|
|
103
|
+
bytes_out: result.bytesOut,
|
|
104
|
+
prompt_tokens: counts?.promptTokens ?? null,
|
|
105
|
+
completion_tokens: counts?.completionTokens ?? null,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
let usage = null;
|
|
109
|
+
relay.on('chunk', (bytes) => {
|
|
110
|
+
usage?.onChunk(bytes);
|
|
111
|
+
ctx.res.write(bytes);
|
|
112
|
+
});
|
|
113
|
+
relay.on('end', () => ctx.res.end());
|
|
114
|
+
relay.on('error', (failure) => {
|
|
115
|
+
if (!ctx.res.headersSent) {
|
|
116
|
+
sendError(ctx.res, new HttpError(failure.status, failure.code, failure.message), relay.requestId);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
ctx.res.destroy();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
ctx.res.on('close', () => {
|
|
123
|
+
if (!ctx.res.writableEnded)
|
|
124
|
+
relay.cancel();
|
|
125
|
+
});
|
|
126
|
+
let head;
|
|
127
|
+
try {
|
|
128
|
+
await relay.dispatch();
|
|
129
|
+
head = await relay.waitHead();
|
|
130
|
+
}
|
|
131
|
+
catch (failure) {
|
|
132
|
+
const f = failure;
|
|
133
|
+
if (!ctx.res.headersSent) {
|
|
134
|
+
sendError(ctx.res, new HttpError(f.status, f.code, f.message), relay.requestId);
|
|
135
|
+
}
|
|
136
|
+
finalize(await relay.waitFinished(), usage);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
ctx.res.writeHead(head.status, sanitizeUpstreamResponseHeaders(head.headers));
|
|
140
|
+
usage = new UsageExtractor(protocol, head.headers['content-type']);
|
|
141
|
+
relay.startStreaming();
|
|
142
|
+
finalize(await relay.waitFinished(), usage);
|
|
143
|
+
}
|
|
144
|
+
function handleModels(ctx, deps) {
|
|
145
|
+
const { row: consumer } = requireConsumer(ctx.req, deps.auth);
|
|
146
|
+
const rows = listOfferingsForConsumer(deps.db, consumer.id);
|
|
147
|
+
const data = rows.map((o) => ({
|
|
148
|
+
id: o.alias,
|
|
149
|
+
object: 'model',
|
|
150
|
+
created: Math.floor(Date.parse(o.updated_at) / 1000) || 0,
|
|
151
|
+
owned_by: o.alias.split('/')[0],
|
|
152
|
+
protocol: o.protocol,
|
|
153
|
+
status: deps.tunnel.offeringStatus(o),
|
|
154
|
+
}));
|
|
155
|
+
sendJson(ctx.res, 200, { object: 'list', data });
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.js","sourceRoot":"","sources":["../src/consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Offering, Protocol } from 'aweshare-protocol';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
export type ProducerRow = {
|
|
4
|
+
id: number;
|
|
5
|
+
name: string;
|
|
6
|
+
token_hash: string;
|
|
7
|
+
created_at: string;
|
|
8
|
+
revoked_at: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type ConsumerRow = {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
token_hash: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
revoked_at: string | null;
|
|
16
|
+
config: string | null;
|
|
17
|
+
};
|
|
18
|
+
export type OfferingRow = {
|
|
19
|
+
alias: string;
|
|
20
|
+
producer_id: number;
|
|
21
|
+
protocol: Protocol;
|
|
22
|
+
upstream_model: string;
|
|
23
|
+
backend_id: string;
|
|
24
|
+
max_concurrency: number;
|
|
25
|
+
updated_at: string;
|
|
26
|
+
};
|
|
27
|
+
export type UsageEventRow = {
|
|
28
|
+
id: number;
|
|
29
|
+
at: string;
|
|
30
|
+
consumer_id: number;
|
|
31
|
+
producer_id: number;
|
|
32
|
+
alias: string;
|
|
33
|
+
upstream_model: string;
|
|
34
|
+
protocol: string;
|
|
35
|
+
streaming: number;
|
|
36
|
+
status: number | null;
|
|
37
|
+
error_code: string | null;
|
|
38
|
+
duration_ms: number;
|
|
39
|
+
bytes_in: number;
|
|
40
|
+
bytes_out: number;
|
|
41
|
+
prompt_tokens: number | null;
|
|
42
|
+
completion_tokens: number | null;
|
|
43
|
+
};
|
|
44
|
+
export type NewUsageEvent = Omit<UsageEventRow, 'id' | 'at'>;
|
|
45
|
+
export declare function openDb(dbFile: string): Database.Database;
|
|
46
|
+
export declare function insertProducer(db: Database.Database, name: string, tokenHash: string): ProducerRow;
|
|
47
|
+
export declare function insertConsumer(db: Database.Database, name: string, tokenHash: string): ConsumerRow;
|
|
48
|
+
export declare function getProducerById(db: Database.Database, id: number): ProducerRow;
|
|
49
|
+
export declare function getConsumerById(db: Database.Database, id: number): ConsumerRow;
|
|
50
|
+
export declare function getProducerByName(db: Database.Database, name: string): ProducerRow | undefined;
|
|
51
|
+
export declare function getConsumerByName(db: Database.Database, name: string): ConsumerRow | undefined;
|
|
52
|
+
export declare function findProducerByHash(db: Database.Database, hash: string): ProducerRow | undefined;
|
|
53
|
+
export declare function findConsumerByHash(db: Database.Database, hash: string): ConsumerRow | undefined;
|
|
54
|
+
export declare function listTokens(db: Database.Database): {
|
|
55
|
+
producers: Omit<ProducerRow, "token_hash">[];
|
|
56
|
+
consumers: Omit<ConsumerRow, "token_hash">[];
|
|
57
|
+
};
|
|
58
|
+
export declare function revokeToken(db: Database.Database, role: 'producer' | 'consumer', id: number): void;
|
|
59
|
+
export declare function addGrant(db: Database.Database, producerId: number, consumerId: number, alias: string, expiresAt?: string | null): void;
|
|
60
|
+
export declare function removeGrant(db: Database.Database, producerId: number, consumerId: number, alias: string): boolean;
|
|
61
|
+
export type GrantRow = {
|
|
62
|
+
producer_id: number;
|
|
63
|
+
consumer_id: number;
|
|
64
|
+
alias: string;
|
|
65
|
+
created_at: string;
|
|
66
|
+
expires_at: string | null;
|
|
67
|
+
};
|
|
68
|
+
/** Returns the grant row regardless of expiry; callers decide missing vs expired. */
|
|
69
|
+
export declare function getGrant(db: Database.Database, producerId: number, consumerId: number, alias: string): GrantRow | undefined;
|
|
70
|
+
export declare function listGrants(db: Database.Database, producerId?: number): {
|
|
71
|
+
alias: string;
|
|
72
|
+
created_at: string;
|
|
73
|
+
expires_at: string | null;
|
|
74
|
+
producer: string;
|
|
75
|
+
consumer: string;
|
|
76
|
+
}[];
|
|
77
|
+
export declare function upsertOfferings(db: Database.Database, producerId: number, offerings: Offering[]): void;
|
|
78
|
+
export declare function getOffering(db: Database.Database, alias: string): OfferingRow | undefined;
|
|
79
|
+
export declare function listOfferingsForConsumer(db: Database.Database, consumerId: number): OfferingRow[];
|
|
80
|
+
export declare function insertUsageEvent(db: Database.Database, event: NewUsageEvent): void;
|
|
81
|
+
export interface UsageFilter {
|
|
82
|
+
consumerId?: number;
|
|
83
|
+
producerId?: number;
|
|
84
|
+
alias?: string;
|
|
85
|
+
limit: number;
|
|
86
|
+
}
|
|
87
|
+
export declare function queryUsage(db: Database.Database, filter: UsageFilter): UsageEventRow[];
|
|
88
|
+
export declare function getConsumerConfig(db: Database.Database, consumerId: number): string | null;
|
|
89
|
+
export declare function setConsumerConfig(db: Database.Database, consumerId: number, config: string | null): void;
|
|
90
|
+
/** Lifetime tokens consumed by a consumer; NULL counts (e.g. Ollama streams) read as 0. */
|
|
91
|
+
export declare function sumTokensUsed(db: Database.Database, consumerId: number): number;
|