responses-proxy 0.1.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 (161) hide show
  1. package/README.md +56 -0
  2. package/cli.js +118 -0
  3. package/dist/anthropic-messages.js +383 -0
  4. package/dist/anthropic-messages.test.js +209 -0
  5. package/dist/audit-log.js +138 -0
  6. package/dist/audit-log.test.js +480 -0
  7. package/dist/billing-expiration.js +70 -0
  8. package/dist/billing-expiration.test.js +114 -0
  9. package/dist/billing.js +716 -0
  10. package/dist/billing.test.js +228 -0
  11. package/dist/chatgpt-oauth-store.js +240 -0
  12. package/dist/chatgpt-oauth-store.test.js +88 -0
  13. package/dist/chatgpt-oauth.js +118 -0
  14. package/dist/chatgpt-oauth.test.js +63 -0
  15. package/dist/chatgpt-provider-auth.js +60 -0
  16. package/dist/chatgpt-provider-auth.test.js +101 -0
  17. package/dist/client/app-icon.svg +17 -0
  18. package/dist/client/assets/index-C7Vvhst8.js +14 -0
  19. package/dist/client/assets/index-DpqgYK3L.css +1 -0
  20. package/dist/client/favicon.svg +17 -0
  21. package/dist/client/index.html +31 -0
  22. package/dist/client-config-apply.js +345 -0
  23. package/dist/client-config-apply.test.js +185 -0
  24. package/dist/client-token-limits.js +111 -0
  25. package/dist/client-token-limits.test.js +129 -0
  26. package/dist/codex-config.js +47 -0
  27. package/dist/codex-setup.js +87 -0
  28. package/dist/codex-setup.test.js +30 -0
  29. package/dist/config.js +314 -0
  30. package/dist/cost-analytics.js +31 -0
  31. package/dist/cost-analytics.test.js +38 -0
  32. package/dist/customer-key-access.js +126 -0
  33. package/dist/customer-key-access.test.js +178 -0
  34. package/dist/customer-keys.js +209 -0
  35. package/dist/customer-keys.test.js +68 -0
  36. package/dist/customer-usage.js +18 -0
  37. package/dist/customer-usage.test.js +55 -0
  38. package/dist/dashboard-auth.js +318 -0
  39. package/dist/dashboard-auth.test.js +133 -0
  40. package/dist/dashboard-serving.test.js +235 -0
  41. package/dist/error-response.js +174 -0
  42. package/dist/error-response.test.js +88 -0
  43. package/dist/forward.js +357 -0
  44. package/dist/health-websocket-manager.js +174 -0
  45. package/dist/http-rate-limit.js +36 -0
  46. package/dist/http-rate-limit.test.js +62 -0
  47. package/dist/kiro-auth.js +136 -0
  48. package/dist/kiro-auth.test.js +234 -0
  49. package/dist/kiro-codewhisperer.js +646 -0
  50. package/dist/kiro-codewhisperer.test.js +219 -0
  51. package/dist/kiro-device-login.js +338 -0
  52. package/dist/kiro-eventstream.js +219 -0
  53. package/dist/kiro-eventstream.test.js +79 -0
  54. package/dist/kiro-forward.js +401 -0
  55. package/dist/kiro-import-cli.js +69 -0
  56. package/dist/kiro-import.js +94 -0
  57. package/dist/kiro-import.test.js +125 -0
  58. package/dist/kiro-token-store.js +196 -0
  59. package/dist/kiro-token-store.test.js +207 -0
  60. package/dist/krouter-usage.js +243 -0
  61. package/dist/model-combo-repository.js +147 -0
  62. package/dist/model-routing.js +69 -0
  63. package/dist/model-routing.test.js +41 -0
  64. package/dist/normalize-request.js +531 -0
  65. package/dist/normalize-request.test.js +277 -0
  66. package/dist/omv-public-firewall.test.js +11 -0
  67. package/dist/package.json +17 -0
  68. package/dist/prompt-cache-state.js +146 -0
  69. package/dist/prompt-cache-state.test.js +71 -0
  70. package/dist/prompt-cache.js +229 -0
  71. package/dist/provider-health-service.js +404 -0
  72. package/dist/provider-request-parameters.js +107 -0
  73. package/dist/provider-request-parameters.test.js +26 -0
  74. package/dist/provider-routing.js +114 -0
  75. package/dist/provider-routing.test.js +64 -0
  76. package/dist/provider-usage.js +314 -0
  77. package/dist/request-timeout-policy.js +61 -0
  78. package/dist/request-timeout-policy.test.js +40 -0
  79. package/dist/response-cache.js +69 -0
  80. package/dist/response-cache.test.js +28 -0
  81. package/dist/routing-combo-repository.js +300 -0
  82. package/dist/routing-engine.js +377 -0
  83. package/dist/routing-integration.js +155 -0
  84. package/dist/routing-simulation-engine.js +326 -0
  85. package/dist/rtk-layer.js +483 -0
  86. package/dist/rtk-layer.test.js +198 -0
  87. package/dist/runtime-provider-repository.js +1742 -0
  88. package/dist/runtime-provider-repository.test.js +1177 -0
  89. package/dist/schema.js +118 -0
  90. package/dist/schema.test.js +16 -0
  91. package/dist/sepay-webhook.js +87 -0
  92. package/dist/sepay-webhook.test.js +142 -0
  93. package/dist/server-body-limit.test.js +35 -0
  94. package/dist/server-client-token-limits.test.js +161 -0
  95. package/dist/server-codex-config-setup.test.js +76 -0
  96. package/dist/server-http-rate-limit.test.js +80 -0
  97. package/dist/server-response-cache.test.js +105 -0
  98. package/dist/server-routes-alias.test.js +39 -0
  99. package/dist/server-sepay-webhook-security.test.js +59 -0
  100. package/dist/server.js +5906 -0
  101. package/dist/session-log.js +178 -0
  102. package/dist/tailnet-funnel-script.test.js +33 -0
  103. package/dist/telegram-bot/actions.js +118 -0
  104. package/dist/telegram-bot/admin-actions.js +103 -0
  105. package/dist/telegram-bot/auth.js +46 -0
  106. package/dist/telegram-bot/auth.test.js +1 -0
  107. package/dist/telegram-bot/bot-identity-repository.js +189 -0
  108. package/dist/telegram-bot/bot-identity-repository.test.js +78 -0
  109. package/dist/telegram-bot/callbacks.js +30 -0
  110. package/dist/telegram-bot/codex-config-delivery.js +38 -0
  111. package/dist/telegram-bot/codex-config-delivery.test.js +75 -0
  112. package/dist/telegram-bot/commands/accounts.js +140 -0
  113. package/dist/telegram-bot/commands/apikey.js +737 -0
  114. package/dist/telegram-bot/commands/apply.js +265 -0
  115. package/dist/telegram-bot/commands/clients.js +13 -0
  116. package/dist/telegram-bot/commands/customer-billing.test.js +271 -0
  117. package/dist/telegram-bot/commands/grant.js +138 -0
  118. package/dist/telegram-bot/commands/grant.test.js +217 -0
  119. package/dist/telegram-bot/commands/help.js +52 -0
  120. package/dist/telegram-bot/commands/me.js +53 -0
  121. package/dist/telegram-bot/commands/models.js +6 -0
  122. package/dist/telegram-bot/commands/oauth.js +64 -0
  123. package/dist/telegram-bot/commands/plans.js +96 -0
  124. package/dist/telegram-bot/commands/providers.js +27 -0
  125. package/dist/telegram-bot/commands/quota.js +10 -0
  126. package/dist/telegram-bot/commands/renew-user.js +139 -0
  127. package/dist/telegram-bot/commands/renew-user.test.js +184 -0
  128. package/dist/telegram-bot/commands/renew.js +1369 -0
  129. package/dist/telegram-bot/commands/renew.test.js +1633 -0
  130. package/dist/telegram-bot/commands/start.js +212 -0
  131. package/dist/telegram-bot/commands/start.test.js +280 -0
  132. package/dist/telegram-bot/commands/status.js +6 -0
  133. package/dist/telegram-bot/commands/tailscale.js +15 -0
  134. package/dist/telegram-bot/commands/tailscale.test.js +76 -0
  135. package/dist/telegram-bot/commands/test.js +51 -0
  136. package/dist/telegram-bot/commands/test.test.js +14 -0
  137. package/dist/telegram-bot/commands/usage.js +10 -0
  138. package/dist/telegram-bot/config.js +98 -0
  139. package/dist/telegram-bot/config.test.js +42 -0
  140. package/dist/telegram-bot/customer-actions.js +160 -0
  141. package/dist/telegram-bot/customer-api-keys.js +68 -0
  142. package/dist/telegram-bot/customer-billing.js +72 -0
  143. package/dist/telegram-bot/customer-workspace-repository.js +134 -0
  144. package/dist/telegram-bot/customer-workspace-repository.test.js +47 -0
  145. package/dist/telegram-bot/dashboard-login.js +39 -0
  146. package/dist/telegram-bot/format.js +140 -0
  147. package/dist/telegram-bot/grants.js +370 -0
  148. package/dist/telegram-bot/grants.test.js +290 -0
  149. package/dist/telegram-bot/index.js +85 -0
  150. package/dist/telegram-bot/message-cleanup.js +55 -0
  151. package/dist/telegram-bot/message-cleanup.test.js +77 -0
  152. package/dist/telegram-bot/message-format.js +45 -0
  153. package/dist/telegram-bot/message-format.test.js +10 -0
  154. package/dist/telegram-bot/proxy-client.js +174 -0
  155. package/dist/telegram-bot/rate-limit.js +95 -0
  156. package/dist/telegram-bot/rate-limit.test.js +58 -0
  157. package/dist/telegram-bot/sessions.js +171 -0
  158. package/dist/telegram-bot/sessions.test.js +107 -0
  159. package/dist/telegram-bot/telegram-adapter.js +126 -0
  160. package/dist/telegram-bot/worker.js +63 -0
  161. package/package.json +39 -0
@@ -0,0 +1,80 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "responses-proxy-server-rate-limit-"));
7
+ const dbFile = path.join(tempDir, "app.sqlite");
8
+ process.env.RESPONSES_PROXY_DISABLE_LISTEN = "true";
9
+ process.env.APP_DB_PATH = dbFile;
10
+ process.env.CUSTOMER_KEY_DB_PATH = dbFile;
11
+ process.env.UPSTREAM_BASE_URL = "https://upstream.example/v1";
12
+ process.env.UPSTREAM_API_KEY = "provider-key";
13
+ process.env.PROVIDER_USAGE_CHECK_ENABLED = "false";
14
+ process.env.CHATGPT_OAUTH_ENABLED = "false";
15
+ process.env.LOG_LEVEL = "silent";
16
+ process.env.HTTP_TRUST_PROXY = "false";
17
+ process.env.HTTP_RATE_LIMIT_ENABLED = "true";
18
+ process.env.HTTP_RATE_LIMIT_WINDOW_MS = "60000";
19
+ process.env.HTTP_RATE_LIMIT_RESPONSES_MAX_REQUESTS = "1";
20
+ process.env.HTTP_RATE_LIMIT_UNAUTHENTICATED_MAX_REQUESTS = "1";
21
+ const { app } = await import("./server.js");
22
+ test.after(async () => {
23
+ await app.close();
24
+ rmSync(tempDir, { recursive: true, force: true });
25
+ });
26
+ test("responses endpoint returns 429 after configured rate limit threshold", async () => {
27
+ const first = await app.inject({
28
+ method: "POST",
29
+ url: "/v1/responses",
30
+ headers: {
31
+ "x-forwarded-for": "203.0.113.10",
32
+ },
33
+ payload: {},
34
+ });
35
+ assert.equal(first.statusCode, 400);
36
+ assert.equal(first.headers["x-ratelimit-limit"], "1");
37
+ assert.equal(first.headers["x-ratelimit-remaining"], "0");
38
+ const second = await app.inject({
39
+ method: "POST",
40
+ url: "/v1/responses",
41
+ headers: {
42
+ "x-forwarded-for": "198.51.100.10",
43
+ },
44
+ payload: {},
45
+ });
46
+ assert.equal(second.statusCode, 429);
47
+ assert.equal(second.headers["retry-after"], "60");
48
+ assert.equal(second.headers["x-ratelimit-limit"], "1");
49
+ assert.equal(second.headers["x-ratelimit-remaining"], "0");
50
+ assert.equal(second.json().error.code, "HTTP_RATE_LIMIT_EXCEEDED");
51
+ });
52
+ test("bearer requests get separate rate limit buckets by token", async () => {
53
+ const firstBearer = await app.inject({
54
+ method: "POST",
55
+ url: "/v1/responses",
56
+ headers: {
57
+ authorization: "Bearer bearer-token-a",
58
+ },
59
+ payload: {},
60
+ });
61
+ assert.equal(firstBearer.statusCode, 400);
62
+ const secondBearer = await app.inject({
63
+ method: "POST",
64
+ url: "/v1/responses",
65
+ headers: {
66
+ authorization: "Bearer bearer-token-b",
67
+ },
68
+ payload: {},
69
+ });
70
+ assert.equal(secondBearer.statusCode, 400);
71
+ const thirdBearer = await app.inject({
72
+ method: "POST",
73
+ url: "/v1/responses",
74
+ headers: {
75
+ authorization: "Bearer bearer-token-a",
76
+ },
77
+ payload: {},
78
+ });
79
+ assert.equal(thirdBearer.statusCode, 429);
80
+ });
@@ -0,0 +1,105 @@
1
+ import assert from "node:assert/strict";
2
+ import { createServer } from "node:http";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import test from "node:test";
7
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "responses-proxy-server-response-cache-"));
8
+ const dbFile = path.join(tempDir, "app.sqlite");
9
+ const upstream = createServer((request, response) => {
10
+ if (request.url !== "/responses") {
11
+ response.statusCode = 404;
12
+ response.end();
13
+ return;
14
+ }
15
+ response.setHeader("Content-Type", "application/json");
16
+ response.end(JSON.stringify({
17
+ id: `upstream-${upstreamHits + 1}`,
18
+ object: "response",
19
+ status: "completed",
20
+ output: [],
21
+ usage: {
22
+ input_tokens: 12,
23
+ output_tokens: 3,
24
+ total_tokens: 15,
25
+ input_tokens_details: { cached_tokens: 0 },
26
+ },
27
+ }));
28
+ upstreamHits += 1;
29
+ });
30
+ let upstreamHits = 0;
31
+ process.env.RESPONSES_PROXY_DISABLE_LISTEN = "true";
32
+ process.env.APP_DB_PATH = dbFile;
33
+ process.env.CUSTOMER_KEY_DB_PATH = dbFile;
34
+ process.env.UPSTREAM_BASE_URL = "http://127.0.0.1:0";
35
+ process.env.UPSTREAM_API_KEY = "provider-key";
36
+ process.env.PROVIDER_USAGE_CHECK_ENABLED = "false";
37
+ process.env.CHATGPT_OAUTH_ENABLED = "false";
38
+ process.env.LOG_LEVEL = "silent";
39
+ process.env.RESPONSE_CACHE_ENABLED = "true";
40
+ process.env.RESPONSE_CACHE_TTL_MS = "60000";
41
+ process.env.RESPONSE_CACHE_MAX_PAYLOAD_BYTES = "524288";
42
+ process.env.RESPONSES_PROXY_CLIENT_API_KEY = "provider-key";
43
+ await new Promise((resolve) => {
44
+ upstream.listen(0, "127.0.0.1", () => resolve());
45
+ });
46
+ const upstreamAddress = upstream.address();
47
+ if (!upstreamAddress || typeof upstreamAddress === "string") {
48
+ throw new Error("Upstream server did not bind to a port");
49
+ }
50
+ process.env.UPSTREAM_BASE_URL = `http://127.0.0.1:${upstreamAddress.port}`;
51
+ const { app } = await import("./server.js");
52
+ test.after(async () => {
53
+ await app.close();
54
+ await new Promise((resolve) => upstream.close(() => resolve()));
55
+ rmSync(tempDir, { recursive: true, force: true });
56
+ });
57
+ test("response cache hits on repeated non-stream requests and analytics exposes stats", async () => {
58
+ const payload = {
59
+ model: "gpt-4.1",
60
+ input: "hello",
61
+ };
62
+ const first = await app.inject({
63
+ method: "POST",
64
+ url: "/v1/responses",
65
+ headers: {
66
+ authorization: "Bearer provider-key",
67
+ },
68
+ payload,
69
+ });
70
+ assert.equal(first.statusCode, 200);
71
+ assert.equal(first.headers["x-proxy-response-cache"], undefined);
72
+ assert.equal(upstreamHits, 1);
73
+ const second = await app.inject({
74
+ method: "POST",
75
+ url: "/v1/responses",
76
+ headers: {
77
+ authorization: "Bearer provider-key",
78
+ },
79
+ payload,
80
+ });
81
+ assert.equal(second.statusCode, 200);
82
+ assert.equal(second.headers["x-proxy-response-cache"], "hit");
83
+ assert.equal(upstreamHits, 1);
84
+ assert.deepEqual(second.json(), first.json());
85
+ const analytics = await app.inject({
86
+ method: "GET",
87
+ url: "/api/analytics/cost-summary",
88
+ headers: {
89
+ authorization: "Bearer provider-key",
90
+ },
91
+ });
92
+ assert.equal(analytics.statusCode, 200);
93
+ const body = analytics.json();
94
+ assert.equal(body.summary.totalRequests >= 1, true);
95
+ assert.equal(body.responseCacheStats?.totalEntries, 1);
96
+ const flushed = await app.inject({
97
+ method: "POST",
98
+ url: "/api/analytics/response-cache/flush",
99
+ headers: {
100
+ authorization: "Bearer provider-key",
101
+ },
102
+ });
103
+ assert.equal(flushed.statusCode, 200);
104
+ assert.equal(flushed.json().deleted, 1);
105
+ });
@@ -0,0 +1,39 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "responses-proxy-server-routes-alias-"));
7
+ const dbFile = path.join(tempDir, "app.sqlite");
8
+ process.env.RESPONSES_PROXY_DISABLE_LISTEN = "true";
9
+ process.env.APP_DB_PATH = dbFile;
10
+ process.env.CUSTOMER_KEY_DB_PATH = dbFile;
11
+ process.env.UPSTREAM_BASE_URL = "https://upstream.example/v1";
12
+ process.env.UPSTREAM_API_KEY = "provider-key";
13
+ process.env.PROVIDER_USAGE_CHECK_ENABLED = "false";
14
+ process.env.CHATGPT_OAUTH_ENABLED = "false";
15
+ process.env.LOG_LEVEL = "silent";
16
+ const { app } = await import("./server.js");
17
+ test.after(async () => {
18
+ await app.close();
19
+ rmSync(tempDir, { recursive: true, force: true });
20
+ });
21
+ test("route alias /v1/chat/completions routes to responses handler and fails validation with 400", async () => {
22
+ const response = await app.inject({
23
+ method: "POST",
24
+ url: "/v1/chat/completions",
25
+ payload: {},
26
+ });
27
+ // If it was a 404, it would return 404. Returning 400 means it hit the handler and failed validation.
28
+ assert.equal(response.statusCode, 400);
29
+ assert.equal(response.json().error?.code, "INVALID_RESPONSES_REQUEST");
30
+ });
31
+ test("route alias /v1/completions routes to responses handler and fails validation with 400", async () => {
32
+ const response = await app.inject({
33
+ method: "POST",
34
+ url: "/v1/completions",
35
+ payload: {},
36
+ });
37
+ assert.equal(response.statusCode, 400);
38
+ assert.equal(response.json().error?.code, "INVALID_RESPONSES_REQUEST");
39
+ });
@@ -0,0 +1,59 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), "responses-proxy-server-sepay-"));
7
+ const dbFile = path.join(tempDir, "app.sqlite");
8
+ process.env.RESPONSES_PROXY_DISABLE_LISTEN = "true";
9
+ process.env.APP_DB_PATH = dbFile;
10
+ process.env.CUSTOMER_KEY_DB_PATH = dbFile;
11
+ process.env.UPSTREAM_BASE_URL = "https://upstream.example/v1";
12
+ process.env.UPSTREAM_API_KEY = "provider-key";
13
+ process.env.PROVIDER_USAGE_CHECK_ENABLED = "false";
14
+ process.env.CHATGPT_OAUTH_ENABLED = "false";
15
+ process.env.LOG_LEVEL = "silent";
16
+ process.env.SEPAY_WEBHOOK_ENABLED = "true";
17
+ process.env.SEPAY_WEBHOOK_SECRET = "webhook-secret";
18
+ process.env.HTTP_TRUST_PROXY = "true";
19
+ process.env.SEPAY_WEBHOOK_ALLOWED_IPS = "203.0.113.10,198.51.100.0/24";
20
+ const { app } = await import("./server.js");
21
+ test.after(async () => {
22
+ await app.close();
23
+ rmSync(tempDir, { recursive: true, force: true });
24
+ });
25
+ test("sepay webhook rejects non-allowed source ip", async () => {
26
+ const response = await app.inject({
27
+ method: "POST",
28
+ url: "/api/sepay/webhook",
29
+ headers: {
30
+ authorization: "Bearer webhook-secret",
31
+ "x-forwarded-for": "192.0.2.10",
32
+ },
33
+ payload: {},
34
+ });
35
+ assert.equal(response.statusCode, 403);
36
+ assert.equal(response.json().error.code, "SEPAY_WEBHOOK_IP_FORBIDDEN");
37
+ });
38
+ test("sepay webhook accepts allowed exact ip and cidr", async () => {
39
+ const exact = await app.inject({
40
+ method: "POST",
41
+ url: "/api/sepay/webhook",
42
+ headers: {
43
+ authorization: "Bearer webhook-secret",
44
+ "x-forwarded-for": "203.0.113.10",
45
+ },
46
+ payload: {},
47
+ });
48
+ assert.equal(exact.statusCode, 200);
49
+ const cidr = await app.inject({
50
+ method: "POST",
51
+ url: "/api/sepay/webhook",
52
+ headers: {
53
+ authorization: "Bearer webhook-secret",
54
+ "x-forwarded-for": "198.51.100.42",
55
+ },
56
+ payload: {},
57
+ });
58
+ assert.equal(cidr.statusCode, 200);
59
+ });