@ganglion/xacpx-relay 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 (98) hide show
  1. package/README.md +10 -0
  2. package/dist/auth.d.ts +6 -0
  3. package/dist/cli.d.ts +24 -0
  4. package/dist/cli.js +1168 -0
  5. package/dist/db.d.ts +9 -0
  6. package/dist/gateway/instance-gateway.d.ts +30 -0
  7. package/dist/gateway/web-gateway.d.ts +11 -0
  8. package/dist/http/app.d.ts +33 -0
  9. package/dist/http/client-ip.d.ts +2 -0
  10. package/dist/maintenance.d.ts +22 -0
  11. package/dist/relay-web/assets/BrandLogo.vue_vue_type_script_setup_true_lang-CSPZDDtK.js +1 -0
  12. package/dist/relay-web/assets/DashboardView-AMivJ-D8.css +1 -0
  13. package/dist/relay-web/assets/DashboardView-Bhx2FJSv.js +256 -0
  14. package/dist/relay-web/assets/LoginView-CYhQnBi-.js +1 -0
  15. package/dist/relay-web/assets/SettingsView-hmSvIsKD.js +6 -0
  16. package/dist/relay-web/assets/index-DC1zO5jV.css +1 -0
  17. package/dist/relay-web/assets/index-DE2bTqBl.js +80 -0
  18. package/dist/relay-web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
  19. package/dist/relay-web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
  20. package/dist/relay-web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
  21. package/dist/relay-web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
  22. package/dist/relay-web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
  23. package/dist/relay-web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
  24. package/dist/relay-web/assets/inter-cyrillic-700-normal-CjBOestx.woff2 +0 -0
  25. package/dist/relay-web/assets/inter-cyrillic-700-normal-DrXBdSj3.woff +0 -0
  26. package/dist/relay-web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
  27. package/dist/relay-web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
  28. package/dist/relay-web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
  29. package/dist/relay-web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
  30. package/dist/relay-web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
  31. package/dist/relay-web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
  32. package/dist/relay-web/assets/inter-cyrillic-ext-700-normal-BjwYoWNd.woff2 +0 -0
  33. package/dist/relay-web/assets/inter-cyrillic-ext-700-normal-LO58E6JB.woff +0 -0
  34. package/dist/relay-web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
  35. package/dist/relay-web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
  36. package/dist/relay-web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
  37. package/dist/relay-web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
  38. package/dist/relay-web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
  39. package/dist/relay-web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
  40. package/dist/relay-web/assets/inter-greek-700-normal-BUv2fZ6O.woff +0 -0
  41. package/dist/relay-web/assets/inter-greek-700-normal-C3JjAnD8.woff2 +0 -0
  42. package/dist/relay-web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
  43. package/dist/relay-web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
  44. package/dist/relay-web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
  45. package/dist/relay-web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
  46. package/dist/relay-web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
  47. package/dist/relay-web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
  48. package/dist/relay-web/assets/inter-greek-ext-700-normal-BoQ6DsYi.woff +0 -0
  49. package/dist/relay-web/assets/inter-greek-ext-700-normal-qfdV9bQt.woff2 +0 -0
  50. package/dist/relay-web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  51. package/dist/relay-web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  52. package/dist/relay-web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  53. package/dist/relay-web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  54. package/dist/relay-web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
  55. package/dist/relay-web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  56. package/dist/relay-web/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
  57. package/dist/relay-web/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  58. package/dist/relay-web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  59. package/dist/relay-web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  60. package/dist/relay-web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
  61. package/dist/relay-web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
  62. package/dist/relay-web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
  63. package/dist/relay-web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
  64. package/dist/relay-web/assets/inter-latin-ext-700-normal-Ca8adRJv.woff2 +0 -0
  65. package/dist/relay-web/assets/inter-latin-ext-700-normal-TidjK2hL.woff +0 -0
  66. package/dist/relay-web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  67. package/dist/relay-web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  68. package/dist/relay-web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
  69. package/dist/relay-web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
  70. package/dist/relay-web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
  71. package/dist/relay-web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
  72. package/dist/relay-web/assets/inter-vietnamese-700-normal-BZaoP0fm.woff +0 -0
  73. package/dist/relay-web/assets/inter-vietnamese-700-normal-DlLaEgI2.woff2 +0 -0
  74. package/dist/relay-web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  75. package/dist/relay-web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  76. package/dist/relay-web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  77. package/dist/relay-web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  78. package/dist/relay-web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  79. package/dist/relay-web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  80. package/dist/relay-web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  81. package/dist/relay-web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  82. package/dist/relay-web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  83. package/dist/relay-web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  84. package/dist/relay-web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  85. package/dist/relay-web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  86. package/dist/relay-web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  87. package/dist/relay-web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  88. package/dist/relay-web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  89. package/dist/relay-web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  90. package/dist/relay-web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  91. package/dist/relay-web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  92. package/dist/relay-web/assets/theme-CaepIjgl.js +1 -0
  93. package/dist/relay-web/index.html +28 -0
  94. package/dist/server.d.ts +42 -0
  95. package/dist/stores/accounts.d.ts +74 -0
  96. package/dist/stores/instances.d.ts +42 -0
  97. package/dist/stores/messages.d.ts +30 -0
  98. package/package.json +24 -0
@@ -0,0 +1 @@
1
+ import{k as n,r as c}from"./index-DE2bTqBl.js";const a="relay-theme";function m(){var t;const e=localStorage.getItem(a);return e==="dark"||e==="light"?e:(t=window.matchMedia)!=null&&t.call(window,"(prefers-color-scheme: light)").matches?"light":"dark"}const i=n("theme",()=>{const e=c(m());function t(){document.documentElement.classList.toggle("dark",e.value==="dark"),document.documentElement.style.colorScheme=e.value}function o(r){e.value=r,localStorage.setItem(a,r),t()}function l(){o(e.value==="dark"?"light":"dark")}return t(),{mode:e,set:o,toggle:l,apply:t}});export{i as u};
@@ -0,0 +1,28 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>xacpx relay</title>
7
+ <script>
8
+ (function () {
9
+ try {
10
+ var m = localStorage.getItem('relay-theme');
11
+ if (m !== 'dark' && m !== 'light') {
12
+ m = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
13
+ }
14
+ document.documentElement.classList.toggle('dark', m === 'dark');
15
+ document.documentElement.style.colorScheme = m;
16
+ } catch (e) {
17
+ document.documentElement.classList.add('dark');
18
+ document.documentElement.style.colorScheme = 'dark';
19
+ }
20
+ })();
21
+ </script>
22
+ <script type="module" crossorigin src="/assets/index-DE2bTqBl.js"></script>
23
+ <link rel="stylesheet" crossorigin href="/assets/index-DC1zO5jV.css">
24
+ </head>
25
+ <body>
26
+ <div id="app"></div>
27
+ </body>
28
+ </html>
@@ -0,0 +1,42 @@
1
+ import { type SqlDriver } from "./db.js";
2
+ import { AccountStore } from "./stores/accounts.js";
3
+ import { InstanceStore } from "./stores/instances.js";
4
+ import { MessageStore } from "./stores/messages.js";
5
+ import { InstanceGateway } from "./gateway/instance-gateway.js";
6
+ import { WebGateway } from "./gateway/web-gateway.js";
7
+ import { createApp } from "./http/app.js";
8
+ export interface RelayRuntime {
9
+ db: SqlDriver;
10
+ accounts: AccountStore;
11
+ instances: InstanceStore;
12
+ messages: MessageStore;
13
+ gateway: InstanceGateway;
14
+ webGateway: WebGateway;
15
+ app: ReturnType<typeof createApp>;
16
+ close(): void;
17
+ }
18
+ export interface CreateRuntimeOptions {
19
+ webRoot?: string;
20
+ historyRetentionDays?: number;
21
+ requestTimeoutMs?: number;
22
+ trustProxy?: boolean;
23
+ }
24
+ /** Testable assembly without any network listener. */
25
+ export declare function createRelayRuntime(dbPath: string, options?: CreateRuntimeOptions): Promise<RelayRuntime>;
26
+ export interface StartRelayOptions {
27
+ dbPath: string;
28
+ httpPort: number;
29
+ wsPort: number;
30
+ host?: string;
31
+ webRoot?: string;
32
+ historyRetentionDays?: number;
33
+ requestTimeoutMs?: number;
34
+ trustProxy?: boolean;
35
+ }
36
+ export interface RunningRelay {
37
+ runtime: RelayRuntime;
38
+ httpPort: number;
39
+ wsPort: number;
40
+ close(): Promise<void>;
41
+ }
42
+ export declare function startRelayServer(options: StartRelayOptions): Promise<RunningRelay>;
@@ -0,0 +1,74 @@
1
+ import type { SqlDriver } from "../db.js";
2
+ export interface AccountRow {
3
+ id: string;
4
+ username: string;
5
+ createdAt: string;
6
+ }
7
+ interface AccountStoreOptions {
8
+ now?: () => Date;
9
+ }
10
+ export declare class AccountStore {
11
+ private readonly db;
12
+ private readonly now;
13
+ constructor(db: SqlDriver, options?: AccountStoreOptions);
14
+ createAccount(username: string): AccountRow;
15
+ findByUsername(username: string): AccountRow | null;
16
+ findById(id: string): AccountRow | null;
17
+ listAccounts(): Array<AccountRow & {
18
+ tokenCount: number;
19
+ instanceCount: number;
20
+ }>;
21
+ countInstances(accountId: string): number;
22
+ createLoginToken(accountId: string, label?: string): {
23
+ id: string;
24
+ token: string;
25
+ };
26
+ /**
27
+ * Single source of truth for login-token resolution: find the token row by
28
+ * hashed token, confirm the account still exists, THEN bump last_used_at.
29
+ * The bump happens only after the account is confirmed, so a token whose
30
+ * account was deleted does not get a wasted last_used_at update. Both
31
+ * findAccountByLoginToken and resolveLoginToken go through here, so the bump
32
+ * happens exactly once and identically for both.
33
+ */
34
+ private _resolveLoginToken;
35
+ findAccountByLoginToken(token: string): AccountRow | null;
36
+ resolveLoginToken(token: string): {
37
+ account: AccountRow;
38
+ loginTokenId: string;
39
+ } | null;
40
+ listLoginTokens(accountId: string): Array<{
41
+ id: string;
42
+ label: string | null;
43
+ createdAt: string;
44
+ lastUsedAt: string | null;
45
+ }>;
46
+ /**
47
+ * Returns all login tokens across all accounts, each with its owning account's
48
+ * total instance count. Intended for the `xacpx-relay ls` command output.
49
+ */
50
+ listTokens(): Array<{
51
+ id: string;
52
+ label: string | null;
53
+ createdAt: string;
54
+ accountId: string;
55
+ instanceCount: number;
56
+ }>;
57
+ /**
58
+ * Resolves a token value or id/prefix to the owning account_id.
59
+ * Try order:
60
+ * 1. Raw token value hash lookup (resolveLoginToken)
61
+ * 2. Exact id match in login_tokens
62
+ * 3. Unique prefix match (WHERE id LIKE ? || '%') — accepts only if exactly one row
63
+ * Returns null if not found or prefix is ambiguous.
64
+ */
65
+ accountIdForToken(valueOrId: string): string | null;
66
+ revokeLoginToken(tokenId: string): boolean;
67
+ createWebSession(accountId: string, loginTokenId: string, ttlMs: number): string;
68
+ getSessionAccount(token: string): AccountRow | null;
69
+ deleteWebSession(token: string): void;
70
+ deleteAccountCascade(accountId: string): void;
71
+ /** Deletes expired web sessions. Returns rows removed. */
72
+ pruneExpired(now: Date): number;
73
+ }
74
+ export {};
@@ -0,0 +1,42 @@
1
+ import type { SqlDriver } from "../db.js";
2
+ export interface InstanceRow {
3
+ id: string;
4
+ accountId: string;
5
+ name: string;
6
+ coreVersion: string | null;
7
+ lastSeenAt: string | null;
8
+ createdAt: string;
9
+ }
10
+ export interface RedeemedInstance {
11
+ instanceId: string;
12
+ credential: string;
13
+ accountId: string;
14
+ name: string;
15
+ }
16
+ interface InstanceStoreOptions {
17
+ now?: () => Date;
18
+ }
19
+ export declare class InstanceStore {
20
+ private readonly db;
21
+ private readonly now;
22
+ constructor(db: SqlDriver, options?: InstanceStoreOptions);
23
+ issuePairingToken(accountId: string, name: string | undefined, ttlMs: number): {
24
+ token: string;
25
+ expiresAt: string;
26
+ };
27
+ /**
28
+ * Creates a new instance for an account directly (without a pairing token).
29
+ * Used when the connector presents a login token instead of a pairing token.
30
+ */
31
+ registerInstanceForAccount(accountId: string, name: string | undefined, coreVersion?: string): RedeemedInstance;
32
+ /** Single-use: marks the token used and creates the instance row atomically-enough for our single-writer server. */
33
+ redeemPairingToken(token: string, coreVersion?: string): RedeemedInstance | null;
34
+ verifyCredential(instanceId: string, credential: string): InstanceRow | null;
35
+ touch(instanceId: string, coreVersion?: string): void;
36
+ listByAccount(accountId: string): InstanceRow[];
37
+ getOwned(instanceId: string, accountId: string): InstanceRow | null;
38
+ remove(instanceId: string, accountId: string): boolean;
39
+ /** Deletes expired or already-used pairing tokens. Returns rows removed. */
40
+ prunePairingTokens(now: Date): number;
41
+ }
42
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { MessageDirection, MessageRecordDto } from "@ganglion/xacpx-relay-protocol";
2
+ import type { SqlDriver } from "../db.js";
3
+ type StructuredTurn = NonNullable<MessageRecordDto["structured"]>;
4
+ export interface MessagePage {
5
+ messages: MessageRecordDto[];
6
+ /** True when older rows exist beyond this page (drives "load older" on scroll-up). */
7
+ hasMore: boolean;
8
+ }
9
+ export declare class MessageStore {
10
+ private readonly db;
11
+ private readonly now;
12
+ constructor(db: SqlDriver, now?: () => Date);
13
+ append(instanceId: string, sessionAlias: string, direction: MessageDirection, text: string, structured?: StructuredTurn): void;
14
+ /**
15
+ * One page of a session's history, oldest-first, scoped to the owning account.
16
+ * Without `before` returns the most recent `limit` rows; with `before` returns the
17
+ * `limit` rows immediately OLDER than that message id (cursor pagination for
18
+ * "load older" on scroll-up). `hasMore` reports whether further-back rows exist.
19
+ */
20
+ listBySession(accountId: string, instanceId: string, sessionAlias: string, opts?: {
21
+ limit?: number;
22
+ before?: number;
23
+ }): MessagePage;
24
+ /** Deletes messages older than maxAgeMs and/or beyond the newest maxPerSession per (instance, session). Returns rows deleted. */
25
+ prune(opts: {
26
+ maxAgeMs?: number;
27
+ maxPerSession?: number;
28
+ }): number;
29
+ }
30
+ export {};
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@ganglion/xacpx-relay",
3
+ "version": "0.1.0",
4
+ "description": "Self-hosted relay hub for xacpx: instance gateway, accounts, and web API.",
5
+ "license": "MIT",
6
+ "keywords": ["xacpx", "relay", "hub"],
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/gadzan/xacpx.git",
10
+ "directory": "packages/relay"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/cli.js",
14
+ "bin": { "xacpx-relay": "./dist/cli.js" },
15
+ "files": ["dist", "README.md"],
16
+ "engines": { "node": ">=22.13.0" },
17
+ "publishConfig": { "access": "public" },
18
+ "dependencies": {
19
+ "@ganglion/xacpx-relay-protocol": "^0.1.0",
20
+ "@hono/node-server": "^1.13.0",
21
+ "hono": "^4.6.0",
22
+ "ws": "^8.20.0"
23
+ }
24
+ }