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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +246 -0
  3. package/apps/agent/dist/adapters.d.ts +15 -0
  4. package/apps/agent/dist/adapters.js +25 -0
  5. package/apps/agent/dist/adapters.js.map +1 -0
  6. package/apps/agent/dist/cli.d.ts +2 -0
  7. package/apps/agent/dist/cli.js +175 -0
  8. package/apps/agent/dist/cli.js.map +1 -0
  9. package/apps/agent/dist/config.d.ts +38 -0
  10. package/apps/agent/dist/config.js +150 -0
  11. package/apps/agent/dist/config.js.map +1 -0
  12. package/apps/agent/dist/connection.d.ts +40 -0
  13. package/apps/agent/dist/connection.js +359 -0
  14. package/apps/agent/dist/connection.js.map +1 -0
  15. package/apps/agent/dist/doctor.d.ts +14 -0
  16. package/apps/agent/dist/doctor.js +125 -0
  17. package/apps/agent/dist/doctor.js.map +1 -0
  18. package/apps/agent/dist/health.d.ts +15 -0
  19. package/apps/agent/dist/health.js +45 -0
  20. package/apps/agent/dist/health.js.map +1 -0
  21. package/apps/agent/dist/index.d.ts +5 -0
  22. package/apps/agent/dist/index.js +6 -0
  23. package/apps/agent/dist/index.js.map +1 -0
  24. package/apps/agent/package.json +20 -0
  25. package/apps/hub/dist/admin.d.ts +10 -0
  26. package/apps/hub/dist/admin.js +208 -0
  27. package/apps/hub/dist/admin.js.map +1 -0
  28. package/apps/hub/dist/auth.d.ts +17 -0
  29. package/apps/hub/dist/auth.js +28 -0
  30. package/apps/hub/dist/auth.js.map +1 -0
  31. package/apps/hub/dist/cli.d.ts +2 -0
  32. package/apps/hub/dist/cli.js +243 -0
  33. package/apps/hub/dist/cli.js.map +1 -0
  34. package/apps/hub/dist/config.d.ts +25 -0
  35. package/apps/hub/dist/config.js +53 -0
  36. package/apps/hub/dist/config.js.map +1 -0
  37. package/apps/hub/dist/consumer.d.ts +18 -0
  38. package/apps/hub/dist/consumer.js +157 -0
  39. package/apps/hub/dist/consumer.js.map +1 -0
  40. package/apps/hub/dist/db.d.ts +91 -0
  41. package/apps/hub/dist/db.js +236 -0
  42. package/apps/hub/dist/db.js.map +1 -0
  43. package/apps/hub/dist/http.d.ts +31 -0
  44. package/apps/hub/dist/http.js +118 -0
  45. package/apps/hub/dist/http.js.map +1 -0
  46. package/apps/hub/dist/limiter.d.ts +35 -0
  47. package/apps/hub/dist/limiter.js +78 -0
  48. package/apps/hub/dist/limiter.js.map +1 -0
  49. package/apps/hub/dist/limits.d.ts +30 -0
  50. package/apps/hub/dist/limits.js +52 -0
  51. package/apps/hub/dist/limits.js.map +1 -0
  52. package/apps/hub/dist/meter.d.ts +25 -0
  53. package/apps/hub/dist/meter.js +135 -0
  54. package/apps/hub/dist/meter.js.map +1 -0
  55. package/apps/hub/dist/sanitize.d.ts +3 -0
  56. package/apps/hub/dist/sanitize.js +56 -0
  57. package/apps/hub/dist/sanitize.js.map +1 -0
  58. package/apps/hub/dist/server.d.ts +25 -0
  59. package/apps/hub/dist/server.js +77 -0
  60. package/apps/hub/dist/server.js.map +1 -0
  61. package/apps/hub/dist/tokens.d.ts +9 -0
  62. package/apps/hub/dist/tokens.js +35 -0
  63. package/apps/hub/dist/tokens.js.map +1 -0
  64. package/apps/hub/dist/tunnel.d.ts +108 -0
  65. package/apps/hub/dist/tunnel.js +424 -0
  66. package/apps/hub/dist/tunnel.js.map +1 -0
  67. package/apps/hub/package.json +21 -0
  68. package/bin/aweshare.mjs +43 -0
  69. package/package.json +47 -0
@@ -0,0 +1,125 @@
1
+ import { ConfigError, loadAgentConfig } from './config.js';
2
+ import { probeBody, probeTarget } from './connection.js';
3
+ import { classifyStatus } from './health.js';
4
+ /**
5
+ * Pre-flight diagnostics, in fasten's order of "find the first failing link":
6
+ * direct backend replay first, hub last. Never guess past a failure.
7
+ */
8
+ export async function runDoctor(dir) {
9
+ const checks = [];
10
+ let config;
11
+ let secrets;
12
+ try {
13
+ ;
14
+ ({ config, secrets } = loadAgentConfig(dir));
15
+ checks.push({
16
+ name: 'config',
17
+ ok: true,
18
+ detail: `${config.backends.length} backends, ${config.offerings.length} offerings`,
19
+ });
20
+ }
21
+ catch (err) {
22
+ const detail = err instanceof ConfigError ? err.message : String(err);
23
+ checks.push({ name: 'config', ok: false, detail });
24
+ return { ok: false, checks };
25
+ }
26
+ // 1. direct backend replay — same URL/protocol/model/key the agent would use
27
+ for (const backend of config.backends) {
28
+ checks.push(...(await checkBackend(backend, config.offerings, secrets[backend.keyRef ?? ''])));
29
+ }
30
+ // 2. hub connectivity last
31
+ try {
32
+ const started = Date.now();
33
+ const res = await fetch(`${config.hubUrl}/healthz`, { signal: AbortSignal.timeout(10_000) });
34
+ checks.push({
35
+ name: `hub ${config.hubUrl}`,
36
+ ok: res.ok,
37
+ detail: `healthz -> ${res.status} (${Date.now() - started}ms)`,
38
+ });
39
+ }
40
+ catch (err) {
41
+ checks.push({
42
+ name: `hub ${config.hubUrl}`,
43
+ ok: false,
44
+ detail: `unreachable: ${err instanceof Error ? err.message : err}`,
45
+ });
46
+ }
47
+ return { ok: checks.every((c) => c.ok), checks };
48
+ }
49
+ async function checkBackend(backend, offerings, apiKey) {
50
+ const checks = [];
51
+ const label = `backend '${backend.id}' (${backend.protocol})`;
52
+ // URL shape hints before spending a roundtrip (fasten's /v1-v1 class of errors)
53
+ const path = new URL(backend.baseUrl).pathname.replace(/\/+$/, '');
54
+ if (backend.protocol !== 'anthropic' && !/\/v\d+$/.test(path)) {
55
+ checks.push({
56
+ name: `${label} baseUrl`,
57
+ ok: false,
58
+ detail: `openai-style baseUrl usually ends with /v1 (SDK convention), got ${backend.baseUrl}; ignore only if the upstream truly has no version prefix`,
59
+ });
60
+ }
61
+ // GLM-style bases legitimately end in another version segment (e.g. /v4 with
62
+ // the real route /v4/v1/messages); only a trailing /v1 conflicts with the
63
+ // adapter-appended one.
64
+ if (backend.protocol === 'anthropic' && /\/v1$/.test(path)) {
65
+ checks.push({
66
+ name: `${label} baseUrl`,
67
+ ok: false,
68
+ detail: `anthropic-style baseUrl must not include /v1 (the agent appends it), got ${backend.baseUrl}`,
69
+ });
70
+ }
71
+ const offering = offerings.find((o) => o.backend === backend.id);
72
+ if (!offering) {
73
+ checks.push({ name: label, ok: true, detail: 'no offerings — probe skipped' });
74
+ return checks;
75
+ }
76
+ if (backend.keyRef && !apiKey) {
77
+ checks.push({
78
+ name: `${label} key`,
79
+ ok: false,
80
+ detail: `keyRef '${backend.keyRef}' has empty value in secrets.json`,
81
+ });
82
+ return checks;
83
+ }
84
+ const resolved = { ...backend, apiKey };
85
+ const { url, headers } = probeTarget(resolved);
86
+ try {
87
+ const started = Date.now();
88
+ const res = await fetch(url, {
89
+ method: 'POST',
90
+ headers,
91
+ body: JSON.stringify(probeBody(offering.upstreamModel, backend.protocol)),
92
+ signal: AbortSignal.timeout(30_000),
93
+ });
94
+ const reason = classifyStatus(res.status);
95
+ if (reason === null) {
96
+ checks.push({
97
+ name: `${label} probe`,
98
+ ok: true,
99
+ detail: `model '${offering.upstreamModel}' -> ${res.status} (${Date.now() - started}ms)`,
100
+ });
101
+ }
102
+ else {
103
+ const hint = reason === 'AUTH'
104
+ ? 'check key permissions/balance (stray whitespace counts too)'
105
+ : reason === 'QUOTA'
106
+ ? 'upstream balance or rate limit exhausted'
107
+ : 'check that upstreamModel is a real backend id (full `ollama list` tag)';
108
+ checks.push({
109
+ name: `${label} probe`,
110
+ ok: false,
111
+ detail: `model '${offering.upstreamModel}' -> ${res.status} (${reason}): ${hint}`,
112
+ });
113
+ }
114
+ await res.arrayBuffer().catch(() => undefined);
115
+ }
116
+ catch (err) {
117
+ checks.push({
118
+ name: `${label} probe`,
119
+ ok: false,
120
+ detail: `direct probe to ${url} failed: ${err instanceof Error ? err.message : err}. Check local network and whether the service is running (inside Docker, 127.0.0.1 is the container itself)`,
121
+ });
122
+ }
123
+ return checks;
124
+ }
125
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { BackendStatus } from 'aweshare-protocol';
2
+ export type FailureReason = 'AUTH' | 'QUOTA' | 'HTTP' | 'NETWORK' | 'CANCELLED';
3
+ /**
4
+ * Per-backend health state. Degraded backends are reported in heartbeats and
5
+ * the hub stops dispatching to their offerings until they recover.
6
+ */
7
+ export declare class HealthGate {
8
+ private consecutive;
9
+ private degraded;
10
+ record(backendId: string, ok: boolean, reason?: FailureReason): void;
11
+ isDegraded(backendId: string): boolean;
12
+ statusMap(): Record<string, BackendStatus>;
13
+ }
14
+ /** Map an upstream HTTP status to a failure class (ok responses pass null). */
15
+ export declare function classifyStatus(status: number): FailureReason | null;
@@ -0,0 +1,45 @@
1
+ /** AUTH/QUOTA consecutive failures >= threshold -> degraded until a success. */
2
+ const DEGRADE_THRESHOLD = 2;
3
+ /**
4
+ * Per-backend health state. Degraded backends are reported in heartbeats and
5
+ * the hub stops dispatching to their offerings until they recover.
6
+ */
7
+ export class HealthGate {
8
+ consecutive = new Map();
9
+ degraded = new Map();
10
+ record(backendId, ok, reason) {
11
+ if (ok) {
12
+ this.consecutive.delete(backendId);
13
+ this.degraded.delete(backendId);
14
+ return;
15
+ }
16
+ if (reason === undefined || reason === 'CANCELLED')
17
+ return;
18
+ const current = this.consecutive.get(backendId);
19
+ const count = current?.reason === reason ? current.count + 1 : 1;
20
+ this.consecutive.set(backendId, { reason, count });
21
+ if ((reason === 'AUTH' || reason === 'QUOTA') && count >= DEGRADE_THRESHOLD) {
22
+ this.degraded.set(backendId, { state: 'degraded', reason });
23
+ }
24
+ }
25
+ isDegraded(backendId) {
26
+ return this.degraded.has(backendId);
27
+ }
28
+ statusMap() {
29
+ const out = {};
30
+ for (const [id, status] of this.degraded)
31
+ out[id] = status;
32
+ return out;
33
+ }
34
+ }
35
+ /** Map an upstream HTTP status to a failure class (ok responses pass null). */
36
+ export function classifyStatus(status) {
37
+ if (status < 400)
38
+ return null;
39
+ if (status === 401 || status === 403)
40
+ return 'AUTH';
41
+ if (status === 402 || status === 429)
42
+ return 'QUOTA';
43
+ return 'HTTP';
44
+ }
45
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAIA,gFAAgF;AAChF,MAAM,iBAAiB,GAAG,CAAC,CAAA;AAE3B;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,WAAW,GAAG,IAAI,GAAG,EAAoD,CAAA;IACzE,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAA;IAEnD,MAAM,CAAC,SAAiB,EAAE,EAAW,EAAE,MAAsB;QAC3D,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC/B,OAAM;QACR,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,WAAW;YAAE,OAAM;QAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QAClD,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,CAAC,IAAI,KAAK,IAAI,iBAAiB,EAAE,CAAC;YAC5E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACrC,CAAC;IAED,SAAS;QACP,MAAM,GAAG,GAAkC,EAAE,CAAA;QAC7C,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ;YAAE,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAA;QAC1D,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,IAAI,CAAA;IAC7B,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,MAAM,CAAA;IACnD,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,OAAO,CAAA;IACpD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { buildUpstreamRequest, type ResolvedBackend, type UpstreamRequest } from './adapters.js';
2
+ export { type AgentConfig, type AgentOffering, agentDir, type Backend, ConfigError, loadAgentConfig, writeFileAtomic, } from './config.js';
3
+ export { AgentConnection, probeBody, probeTarget } from './connection.js';
4
+ export { type DoctorCheck, type DoctorReport, runDoctor } from './doctor.js';
5
+ export { classifyStatus, type FailureReason, HealthGate } from './health.js';
@@ -0,0 +1,6 @@
1
+ export { buildUpstreamRequest } from './adapters.js';
2
+ export { agentDir, ConfigError, loadAgentConfig, writeFileAtomic, } from './config.js';
3
+ export { AgentConnection, probeBody, probeTarget } from './connection.js';
4
+ export { runDoctor } from './doctor.js';
5
+ export { classifyStatus, HealthGate } from './health.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA8C,MAAM,eAAe,CAAA;AAChG,OAAO,EAGL,QAAQ,EAER,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAuC,SAAS,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAsB,UAAU,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@aweshare/agent",
3
+ "version": "0.2.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "scripts": {
8
+ "build": "tsc -b"
9
+ },
10
+ "dependencies": {
11
+ "aweshare-protocol": "workspace:*",
12
+ "pino": "^9.7.0",
13
+ "smol-toml": "^1.4.2",
14
+ "ws": "^8.18.3"
15
+ },
16
+ "devDependencies": {
17
+ "@types/ws": "^8.18.1",
18
+ "typescript": "^5.8.3"
19
+ }
20
+ }
@@ -0,0 +1,10 @@
1
+ import type Database from 'better-sqlite3';
2
+ import type { Logger } from 'pino';
3
+ import type { AuthContext } from './auth.js';
4
+ import { type Router } from './http.js';
5
+ export interface AdminDeps {
6
+ db: Database.Database;
7
+ auth: AuthContext;
8
+ log: Logger;
9
+ }
10
+ export declare function registerAdminRoutes(router: Router, deps: AdminDeps): void;
@@ -0,0 +1,208 @@
1
+ import { parseAlias, roleForToken } from 'aweshare-protocol';
2
+ import { requireAdmin, requireConsumer, requireProducer } from './auth.js';
3
+ import { addGrant, getConsumerByName, getProducerByName, insertConsumer, insertProducer, listGrants, listTokens, queryUsage, removeGrant, revokeToken, setConsumerConfig, } from './db.js';
4
+ import { HttpError, readJson, sendJson } from './http.js';
5
+ import { parseLimits, safeParseLimits } from './limits.js';
6
+ import { generateToken, hashToken } from './tokens.js';
7
+ /** Grants are managed by the producer owning the alias namespace, or by admin. */
8
+ function authorizeGrantChange(req, auth, alias) {
9
+ const token = req.headers.authorization?.startsWith('Bearer ')
10
+ ? req.headers.authorization.slice(7).trim()
11
+ : null;
12
+ if (!token)
13
+ throw new HttpError(401, 'INVALID_KEY', 'admin or producer token required');
14
+ if (roleForToken(token) === 'admin') {
15
+ requireAdmin(req, auth);
16
+ const parsed = parseAlias(alias);
17
+ if (!parsed)
18
+ throw new HttpError(400, 'PROTOCOL_MISMATCH', `invalid alias: ${alias}`);
19
+ const producer = getProducerByName(auth.db, parsed.namespace);
20
+ if (!producer)
21
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown producer namespace: ${parsed.namespace}`);
22
+ return { producerId: producer.id };
23
+ }
24
+ if (roleForToken(token) === 'consumer') {
25
+ throw new HttpError(403, 'NOT_GRANTED', 'consumer keys cannot manage grants');
26
+ }
27
+ const { row } = requireProducer(req, auth);
28
+ const parsed = parseAlias(alias);
29
+ if (!parsed || parsed.namespace !== row.name) {
30
+ throw new HttpError(403, 'NOT_GRANTED', `alias ${alias} is outside your namespace (${row.name})`);
31
+ }
32
+ return { producerId: row.id };
33
+ }
34
+ export function registerAdminRoutes(router, deps) {
35
+ const { db, auth } = deps;
36
+ router.add('POST', '/admin/v1/tokens', async (ctx) => {
37
+ requireAdmin(ctx.req, auth);
38
+ const body = (await readJson(ctx.req, 64 * 1024));
39
+ if (body.role !== 'producer' && body.role !== 'consumer') {
40
+ throw new HttpError(400, 'INVALID_ROLE', "role must be 'producer' or 'consumer'");
41
+ }
42
+ if (typeof body.name !== 'string' || !/^[a-z0-9][a-z0-9-]{1,31}$/.test(body.name)) {
43
+ throw new HttpError(400, 'INVALID_NAME', 'name must match ^[a-z0-9][a-z0-9-]{1,31}$ (it becomes the alias namespace for producers)');
44
+ }
45
+ const token = generateToken(body.role);
46
+ const tokenHash = hashToken(token, auth.pepper);
47
+ const row = body.role === 'producer'
48
+ ? insertProducer(db, body.name, tokenHash)
49
+ : insertConsumer(db, body.name, tokenHash);
50
+ deps.log.info({ role: body.role, name: body.name }, 'token issued');
51
+ sendJson(ctx.res, 201, { id: row.id, role: body.role, name: row.name, token });
52
+ });
53
+ router.add('GET', '/admin/v1/tokens', (ctx) => {
54
+ requireAdmin(ctx.req, auth);
55
+ sendJson(ctx.res, 200, listTokens(db));
56
+ });
57
+ router.add('DELETE', '/admin/v1/tokens/:role/:id', (ctx) => {
58
+ requireAdmin(ctx.req, auth);
59
+ const role = ctx.params.role === 'producers'
60
+ ? 'producer'
61
+ : ctx.params.role === 'consumers'
62
+ ? 'consumer'
63
+ : null;
64
+ if (role !== 'producer' && role !== 'consumer') {
65
+ throw new HttpError(400, 'INVALID_ROLE', 'path must be /admin/v1/tokens/{producers|consumers}/{id}');
66
+ }
67
+ const id = Number(ctx.params.id);
68
+ if (!Number.isInteger(id))
69
+ throw new HttpError(400, 'INVALID_ID', 'token id must be an integer');
70
+ revokeToken(db, role, id);
71
+ deps.log.info({ role, id }, 'token revoked');
72
+ sendJson(ctx.res, 200, { revoked: true, role, id });
73
+ });
74
+ router.add('PUT', '/admin/v1/grants', async (ctx) => {
75
+ const body = (await readJson(ctx.req, 64 * 1024));
76
+ if (typeof body.alias !== 'string' || typeof body.consumer !== 'string') {
77
+ throw new HttpError(400, 'INVALID_REQUEST', 'body must be {alias, consumer[, expiresAt]}');
78
+ }
79
+ let expiresAt = null;
80
+ if (body.expiresAt !== undefined) {
81
+ const t = Date.parse(body.expiresAt);
82
+ if (Number.isNaN(t)) {
83
+ throw new HttpError(400, 'INVALID_REQUEST', 'expiresAt must be an ISO-8601 timestamp');
84
+ }
85
+ if (t <= Date.now()) {
86
+ throw new HttpError(400, 'INVALID_REQUEST', 'expiresAt must be in the future');
87
+ }
88
+ // canonical UTC form — lexicographically comparable with SQLite 'now'
89
+ expiresAt = new Date(t).toISOString();
90
+ }
91
+ const { producerId } = authorizeGrantChange(ctx.req, auth, body.alias);
92
+ const consumer = getConsumerByName(db, body.consumer);
93
+ if (!consumer)
94
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${body.consumer}`);
95
+ addGrant(db, producerId, consumer.id, body.alias, expiresAt);
96
+ deps.log.info({ alias: body.alias, consumer: body.consumer, expiresAt }, 'grant added');
97
+ sendJson(ctx.res, 201, {
98
+ granted: true,
99
+ alias: body.alias,
100
+ consumer: body.consumer,
101
+ expiresAt,
102
+ });
103
+ });
104
+ router.add('DELETE', '/admin/v1/grants', async (ctx) => {
105
+ const alias = ctx.query.get('alias');
106
+ const consumerName = ctx.query.get('consumer');
107
+ if (!alias || !consumerName) {
108
+ throw new HttpError(400, 'INVALID_REQUEST', 'query params alias and consumer are required');
109
+ }
110
+ const { producerId } = authorizeGrantChange(ctx.req, auth, alias);
111
+ const consumer = getConsumerByName(db, consumerName);
112
+ if (!consumer)
113
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${consumerName}`);
114
+ const removed = removeGrant(db, producerId, consumer.id, alias);
115
+ sendJson(ctx.res, 200, { removed, alias, consumer: consumerName });
116
+ });
117
+ router.add('GET', '/admin/v1/grants', (ctx) => {
118
+ const token = ctx.req.headers.authorization?.startsWith('Bearer ')
119
+ ? ctx.req.headers.authorization.slice(7).trim()
120
+ : '';
121
+ if (roleForToken(token) === 'admin') {
122
+ requireAdmin(ctx.req, auth);
123
+ sendJson(ctx.res, 200, listGrants(db));
124
+ return;
125
+ }
126
+ const { row } = requireProducer(ctx.req, auth);
127
+ sendJson(ctx.res, 200, listGrants(db, row.id));
128
+ });
129
+ router.add('GET', '/admin/v1/usage', (ctx) => {
130
+ const token = ctx.req.headers.authorization?.startsWith('Bearer ')
131
+ ? ctx.req.headers.authorization.slice(7).trim()
132
+ : '';
133
+ const role = roleForToken(token);
134
+ const limitRaw = ctx.query.get('limit');
135
+ const limit = limitRaw === null ? 100 : Number(limitRaw);
136
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
137
+ throw new HttpError(400, 'INVALID_LIMIT', 'limit must be an integer between 1 and 1000');
138
+ }
139
+ if (role === 'admin') {
140
+ requireAdmin(ctx.req, auth);
141
+ const consumerName = ctx.query.get('consumer');
142
+ const consumer = consumerName ? getConsumerByName(db, consumerName) : undefined;
143
+ if (consumerName && !consumer)
144
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${consumerName}`);
145
+ sendJson(ctx.res, 200, queryUsage(db, {
146
+ alias: ctx.query.get('alias') ?? undefined,
147
+ consumerId: consumer?.id,
148
+ limit,
149
+ }));
150
+ return;
151
+ }
152
+ if (role === 'producer') {
153
+ const { row } = requireProducer(ctx.req, auth);
154
+ sendJson(ctx.res, 200, queryUsage(db, {
155
+ producerId: row.id,
156
+ alias: ctx.query.get('alias') ?? undefined,
157
+ limit,
158
+ }));
159
+ return;
160
+ }
161
+ // consumer key: own usage only (ignore filters that would broaden it)
162
+ const { row: consumerRow } = requireConsumer(ctx.req, auth);
163
+ sendJson(ctx.res, 200, queryUsage(db, {
164
+ consumerId: consumerRow.id,
165
+ alias: ctx.query.get('alias') ?? undefined,
166
+ limit,
167
+ }));
168
+ });
169
+ // Per-consumer limit overrides (admin only): sparse JSON, unset keys fall back
170
+ // to the hub-wide env defaults. PUT merges into what is already stored.
171
+ router.add('GET', '/admin/v1/consumers/:name/limits', (ctx) => {
172
+ requireAdmin(ctx.req, auth);
173
+ const consumer = getConsumerByName(db, ctx.params.name);
174
+ if (!consumer)
175
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${ctx.params.name}`);
176
+ sendJson(ctx.res, 200, {
177
+ name: consumer.name,
178
+ overrides: safeParseLimits(consumer.config),
179
+ });
180
+ });
181
+ router.add('PUT', '/admin/v1/consumers/:name/limits', async (ctx) => {
182
+ requireAdmin(ctx.req, auth);
183
+ const consumer = getConsumerByName(db, ctx.params.name);
184
+ if (!consumer)
185
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${ctx.params.name}`);
186
+ let incoming;
187
+ try {
188
+ incoming = parseLimits(await readJson(ctx.req, 64 * 1024));
189
+ }
190
+ catch (err) {
191
+ throw new HttpError(400, 'INVALID_REQUEST', err.message);
192
+ }
193
+ const merged = { ...safeParseLimits(consumer.config), ...incoming };
194
+ setConsumerConfig(db, consumer.id, JSON.stringify(merged));
195
+ deps.log.info({ consumer: consumer.name, limits: merged }, 'consumer limits updated');
196
+ sendJson(ctx.res, 200, { name: consumer.name, overrides: merged });
197
+ });
198
+ router.add('DELETE', '/admin/v1/consumers/:name/limits', (ctx) => {
199
+ requireAdmin(ctx.req, auth);
200
+ const consumer = getConsumerByName(db, ctx.params.name);
201
+ if (!consumer)
202
+ throw new HttpError(404, 'ALIAS_NOT_FOUND', `unknown consumer: ${ctx.params.name}`);
203
+ setConsumerConfig(db, consumer.id, null);
204
+ deps.log.info({ consumer: consumer.name }, 'consumer limits cleared');
205
+ sendJson(ctx.res, 200, { name: consumer.name, overrides: null });
206
+ });
207
+ }
208
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ import type Database from 'better-sqlite3';
3
+ import { type ConsumerRow, type ProducerRow } from './db.js';
4
+ export interface AuthContext {
5
+ db: Database.Database;
6
+ pepper: string;
7
+ adminToken: string | null;
8
+ }
9
+ export declare function requireAdmin(req: IncomingMessage, auth: AuthContext): void;
10
+ export declare function requireProducer(req: IncomingMessage, auth: AuthContext): {
11
+ row: ProducerRow;
12
+ token: string;
13
+ };
14
+ export declare function requireConsumer(req: IncomingMessage, auth: AuthContext): {
15
+ row: ConsumerRow;
16
+ token: string;
17
+ };
@@ -0,0 +1,28 @@
1
+ import { findConsumerByHash, findProducerByHash } from './db.js';
2
+ import { extractCredential, HttpError } from './http.js';
3
+ import { hashToken, safeEqual } from './tokens.js';
4
+ export function requireAdmin(req, auth) {
5
+ const token = extractCredential(req);
6
+ if (!token || !auth.adminToken || !safeEqual(token, auth.adminToken)) {
7
+ throw new HttpError(401, 'INVALID_KEY', 'admin token required');
8
+ }
9
+ }
10
+ export function requireProducer(req, auth) {
11
+ const token = extractCredential(req);
12
+ if (!token)
13
+ throw new HttpError(401, 'INVALID_KEY', 'producer token required');
14
+ const row = findProducerByHash(auth.db, hashToken(token, auth.pepper));
15
+ if (!row || row.revoked_at)
16
+ throw new HttpError(401, 'INVALID_KEY', 'invalid producer token');
17
+ return { row, token };
18
+ }
19
+ export function requireConsumer(req, auth) {
20
+ const token = extractCredential(req);
21
+ if (!token)
22
+ throw new HttpError(401, 'INVALID_KEY', 'consumer key required');
23
+ const row = findConsumerByHash(auth.db, hashToken(token, auth.pepper));
24
+ if (!row || row.revoked_at)
25
+ throw new HttpError(401, 'INVALID_KEY', 'invalid consumer key');
26
+ return { row, token };
27
+ }
28
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,kBAAkB,EAAE,kBAAkB,EAAoB,MAAM,SAAS,CAAA;AACpG,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQlD,MAAM,UAAU,YAAY,CAAC,GAAoB,EAAE,IAAiB;IAClE,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,sBAAsB,CAAC,CAAA;IACjE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,GAAoB,EACpB,IAAiB;IAEjB,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,yBAAyB,CAAC,CAAA;IAC9E,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU;QAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,wBAAwB,CAAC,CAAA;IAC7F,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,GAAoB,EACpB,IAAiB;IAEjB,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,uBAAuB,CAAC,CAAA;IAC5E,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU;QAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE,sBAAsB,CAAC,CAAA;IAC3F,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};