@aifight/aifight 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/dist/bin.mjs +291 -0
- package/dist/index.mjs +107 -0
- package/dist/schemas/README.md +57 -0
- package/dist/schemas/common/README.md +40 -0
- package/dist/schemas/common/action.schema.json +19 -0
- package/dist/schemas/common/error.schema.json +23 -0
- package/dist/schemas/common/event.schema.json +33 -0
- package/dist/schemas/common/game_result.schema.json +31 -0
- package/dist/schemas/common/player_identity.schema.json +29 -0
- package/dist/schemas/common/player_info.schema.json +27 -0
- package/dist/schemas/common/rules.schema.json +34 -0
- package/dist/schemas/games/README.md +43 -0
- package/dist/schemas/games/coup/README.md +104 -0
- package/dist/schemas/games/coup/action.schema.json +198 -0
- package/dist/schemas/games/coup/event.schema.json +249 -0
- package/dist/schemas/games/coup/rules.schema.json +46 -0
- package/dist/schemas/games/coup/state.schema.json +123 -0
- package/dist/schemas/games/liars_dice/README.md +59 -0
- package/dist/schemas/games/liars_dice/action.schema.json +45 -0
- package/dist/schemas/games/liars_dice/event.schema.json +120 -0
- package/dist/schemas/games/liars_dice/rules.schema.json +36 -0
- package/dist/schemas/games/liars_dice/state.schema.json +72 -0
- package/dist/schemas/games/texas_holdem/README.md +58 -0
- package/dist/schemas/games/texas_holdem/action.schema.json +88 -0
- package/dist/schemas/games/texas_holdem/config.schema.json +30 -0
- package/dist/schemas/games/texas_holdem/event.schema.json +135 -0
- package/dist/schemas/games/texas_holdem/rules.schema.json +39 -0
- package/dist/schemas/games/texas_holdem/state.schema.json +98 -0
- package/dist/schemas/messages/README.md +98 -0
- package/dist/schemas/messages/client_action.schema.json +20 -0
- package/dist/schemas/messages/client_join_queue.schema.json +39 -0
- package/dist/schemas/messages/client_leave_queue.schema.json +18 -0
- package/dist/schemas/messages/client_match_confirm.schema.json +25 -0
- package/dist/schemas/messages/client_runtime_status.schema.json +46 -0
- package/dist/schemas/messages/server_action_request.schema.json +71 -0
- package/dist/schemas/messages/server_error.schema.json +16 -0
- package/dist/schemas/messages/server_event.schema.json +30 -0
- package/dist/schemas/messages/server_game_over.schema.json +49 -0
- package/dist/schemas/messages/server_game_start.schema.json +99 -0
- package/dist/schemas/messages/server_game_state.schema.json +33 -0
- package/dist/schemas/messages/server_match_cancelled.schema.json +51 -0
- package/dist/schemas/messages/server_match_confirm_request.schema.json +37 -0
- package/dist/schemas/messages/server_queue_joined.schema.json +26 -0
- package/dist/schemas/messages/server_queue_left.schema.json +24 -0
- package/dist/schemas/messages/server_readiness_check.schema.json +42 -0
- package/dist/schemas/messages/server_welcome.schema.json +49 -0
- package/dist/schemas/rest/README.md +85 -0
- package/dist/schemas/rest/agent_status_response.schema.json +34 -0
- package/dist/schemas/rest/claim_request.schema.json +20 -0
- package/dist/schemas/rest/claim_response.schema.json +24 -0
- package/dist/schemas/rest/error_response.schema.json +15 -0
- package/dist/schemas/rest/register_request.schema.json +35 -0
- package/dist/schemas/rest/register_response.schema.json +54 -0
- package/dist/types/account/credentials.d.ts +29 -0
- package/dist/types/account/errors.d.ts +61 -0
- package/dist/types/account/registration.d.ts +26 -0
- package/dist/types/agents/agent.d.ts +82 -0
- package/dist/types/agents/state-machine.d.ts +96 -0
- package/dist/types/bridge/config.d.ts +35 -0
- package/dist/types/bridge/hermes-provider.d.ts +9 -0
- package/dist/types/bridge/openclaw-provider.d.ts +9 -0
- package/dist/types/bridge/pairing.d.ts +18 -0
- package/dist/types/bridge/provider.d.ts +18 -0
- package/dist/types/bridge/runner.d.ts +30 -0
- package/dist/types/bridge/service.d.ts +55 -0
- package/dist/types/bridge/update-check.d.ts +23 -0
- package/dist/types/cli/agent-resolver.d.ts +25 -0
- package/dist/types/cli/argv.d.ts +13 -0
- package/dist/types/cli/commands/agent-list.d.ts +2 -0
- package/dist/types/cli/commands/agent-status.d.ts +2 -0
- package/dist/types/cli/commands/bridge-accept.d.ts +2 -0
- package/dist/types/cli/commands/bridge-challenge.d.ts +2 -0
- package/dist/types/cli/commands/bridge-connect.d.ts +2 -0
- package/dist/types/cli/commands/bridge-register.d.ts +2 -0
- package/dist/types/cli/commands/bridge-run.d.ts +7 -0
- package/dist/types/cli/commands/bridge-service.d.ts +3 -0
- package/dist/types/cli/commands/bridge-set.d.ts +2 -0
- package/dist/types/cli/commands/bridge-start.d.ts +2 -0
- package/dist/types/cli/commands/bridge-status.d.ts +2 -0
- package/dist/types/cli/commands/bridge-uninstall.d.ts +4 -0
- package/dist/types/cli/commands/config-init.d.ts +2 -0
- package/dist/types/cli/commands/config-probe.d.ts +2 -0
- package/dist/types/cli/commands/config-validate.d.ts +2 -0
- package/dist/types/cli/commands/daily-off.d.ts +2 -0
- package/dist/types/cli/commands/daily-on.d.ts +2 -0
- package/dist/types/cli/commands/daily-set.d.ts +2 -0
- package/dist/types/cli/commands/daily-show.d.ts +2 -0
- package/dist/types/cli/commands/doctor.d.ts +2 -0
- package/dist/types/cli/commands/init.d.ts +2 -0
- package/dist/types/cli/commands/join.d.ts +2 -0
- package/dist/types/cli/commands/leave.d.ts +2 -0
- package/dist/types/cli/commands/mcp.d.ts +2 -0
- package/dist/types/cli/commands/runtime-management.d.ts +16 -0
- package/dist/types/cli/commands/runtime-setup-state.d.ts +21 -0
- package/dist/types/cli/commands/runtime-setup.d.ts +23 -0
- package/dist/types/cli/commands/serve.d.ts +2 -0
- package/dist/types/cli/commands/service/launchd.d.ts +71 -0
- package/dist/types/cli/commands/service/platform.d.ts +69 -0
- package/dist/types/cli/commands/service/systemd.d.ts +55 -0
- package/dist/types/cli/commands/service/types.d.ts +64 -0
- package/dist/types/cli/commands/service-install.d.ts +29 -0
- package/dist/types/cli/commands/setup.d.ts +2 -0
- package/dist/types/cli/commands/shutdown.d.ts +2 -0
- package/dist/types/cli/commands/stubs.d.ts +24 -0
- package/dist/types/cli/commands/version.d.ts +2 -0
- package/dist/types/cli/control-client.d.ts +59 -0
- package/dist/types/cli/format.d.ts +52 -0
- package/dist/types/cli/main.d.ts +18 -0
- package/dist/types/cli/runtime-files.d.ts +11 -0
- package/dist/types/cli/shared.d.ts +74 -0
- package/dist/types/controlapi/profile-routes.d.ts +49 -0
- package/dist/types/controlapi/server.d.ts +3 -0
- package/dist/types/controlapi/types.d.ts +136 -0
- package/dist/types/daemon/agent-decision-adapter.d.ts +40 -0
- package/dist/types/daemon/lifecycle.d.ts +85 -0
- package/dist/types/daemon/router.d.ts +97 -0
- package/dist/types/daemon/runtime-files-write.d.ts +76 -0
- package/dist/types/decision/direct-model/anthropic.d.ts +12 -0
- package/dist/types/decision/direct-model/errors.d.ts +59 -0
- package/dist/types/decision/direct-model/openai.d.ts +12 -0
- package/dist/types/decision/direct-model/types.d.ts +20 -0
- package/dist/types/decision/parser-types.d.ts +31 -0
- package/dist/types/decision/prompt-builder.d.ts +10 -0
- package/dist/types/decision/provider.d.ts +50 -0
- package/dist/types/decision/types.d.ts +87 -0
- package/dist/types/games/_shared/player-info.d.ts +14 -0
- package/dist/types/games/coup/action-parser.d.ts +3 -0
- package/dist/types/games/coup/fallback.d.ts +8 -0
- package/dist/types/games/coup/state-formatter.d.ts +14 -0
- package/dist/types/games/liars_dice/action-parser.d.ts +3 -0
- package/dist/types/games/liars_dice/fallback.d.ts +8 -0
- package/dist/types/games/liars_dice/state-formatter.d.ts +14 -0
- package/dist/types/games/texas_holdem/action-parser.d.ts +3 -0
- package/dist/types/games/texas_holdem/fallback.d.ts +8 -0
- package/dist/types/games/texas_holdem/state-formatter.d.ts +14 -0
- package/dist/types/identity/identity-manager.d.ts +59 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/llm/adapter-registry.d.ts +27 -0
- package/dist/types/llm/adapters/anthropic-messages.d.ts +2 -0
- package/dist/types/llm/adapters/deepseek-chat-completions.d.ts +2 -0
- package/dist/types/llm/adapters/openai-chat-compat.d.ts +2 -0
- package/dist/types/llm/adapters/openai-chat-completions.d.ts +2 -0
- package/dist/types/llm/adapters/openai-responses.d.ts +2 -0
- package/dist/types/llm/adapters/types.d.ts +128 -0
- package/dist/types/llm/capabilities/validate-capabilities.d.ts +68 -0
- package/dist/types/mcp/control-client.d.ts +54 -0
- package/dist/types/mcp/profile-tools.d.ts +10 -0
- package/dist/types/mcp/server.d.ts +10 -0
- package/dist/types/mcp/tools.d.ts +31 -0
- package/dist/types/mcp/types.d.ts +27 -0
- package/dist/types/profile/config-schema.d.ts +199 -0
- package/dist/types/profile/identity-schema.d.ts +75 -0
- package/dist/types/profile/index.d.ts +7 -0
- package/dist/types/profile/migrate.d.ts +16 -0
- package/dist/types/profile/profile-loader.d.ts +64 -0
- package/dist/types/profile/secret-ref.d.ts +82 -0
- package/dist/types/profile/soul.d.ts +46 -0
- package/dist/types/profile/strategy-schema.d.ts +70 -0
- package/dist/types/protocol/schemas.d.ts +11 -0
- package/dist/types/protocol/types.d.ts +1333 -0
- package/dist/types/reflection/proposal-store.d.ts +50 -0
- package/dist/types/reflection/reflection-engine.d.ts +81 -0
- package/dist/types/scheduler/daily.d.ts +47 -0
- package/dist/types/scheduler/types.d.ts +42 -0
- package/dist/types/session/match-session-manager.d.ts +113 -0
- package/dist/types/session/session-context-builder.d.ts +68 -0
- package/dist/types/store/errors.d.ts +23 -0
- package/dist/types/store/paths.d.ts +3 -0
- package/dist/types/store/schema.generated.d.ts +1 -0
- package/dist/types/store/sqlite.d.ts +36 -0
- package/dist/types/wsclient/client.d.ts +220 -0
- package/dist/types/wsclient/errors.d.ts +106 -0
- package/dist/types/wsclient/frame-handler.d.ts +20 -0
- package/dist/types/wsclient/reconnect.d.ts +84 -0
- package/package.json +53 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { type WSClientError } from "./errors";
|
|
2
|
+
import { type ServerMessageEnvelope } from "./frame-handler";
|
|
3
|
+
export interface WSWelcome {
|
|
4
|
+
readonly type: "welcome";
|
|
5
|
+
readonly data: {
|
|
6
|
+
readonly server_protocol_version: string;
|
|
7
|
+
readonly agent_id: string;
|
|
8
|
+
readonly agent_name: string;
|
|
9
|
+
readonly server_time: string;
|
|
10
|
+
readonly games: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
readonly match_id?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Discriminated union of valid outbound messages the client may
|
|
16
|
+
* `send()` to the server. Mirrors the four `client_*.schema.json`
|
|
17
|
+
* files at the TypeScript layer (rev 3 P2 #4 — TS first line of
|
|
18
|
+
* defence; ajv via `serializeClientMessage` is the authoritative
|
|
19
|
+
* runtime check).
|
|
20
|
+
*
|
|
21
|
+
* Required-vs-optional contracts here track schema `required` arrays:
|
|
22
|
+
* - join_queue: `data` required (game name lives here)
|
|
23
|
+
* - leave_queue: `data` optional (no fields needed)
|
|
24
|
+
* - match_confirm: `data` required (carries confirm_id)
|
|
25
|
+
* - action: `match_id` required (server uses it for the
|
|
26
|
+
* per-player session_id) AND `data` required
|
|
27
|
+
* (the chosen action object)
|
|
28
|
+
*
|
|
29
|
+
* `match_id` on the three non-`action` types is envelope-level
|
|
30
|
+
* scaffolding — the schemas accept it but no current message uses it.
|
|
31
|
+
* Keep optional so callers don't have to thread an empty string.
|
|
32
|
+
*
|
|
33
|
+
* The `unknown` typing on `data` is deliberate: per-message strict
|
|
34
|
+
* payload typing is M1-22 codegen territory. ajv enforces the inner
|
|
35
|
+
* shape at runtime; surfacing it through TS now would duplicate
|
|
36
|
+
* conformance work.
|
|
37
|
+
*/
|
|
38
|
+
export type WSClientMessage = {
|
|
39
|
+
type: "join_queue";
|
|
40
|
+
data: unknown;
|
|
41
|
+
match_id?: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: "leave_queue";
|
|
44
|
+
data?: unknown;
|
|
45
|
+
match_id?: string;
|
|
46
|
+
} | {
|
|
47
|
+
type: "match_confirm";
|
|
48
|
+
data: unknown;
|
|
49
|
+
match_id?: string;
|
|
50
|
+
} | {
|
|
51
|
+
type: "action";
|
|
52
|
+
match_id: string;
|
|
53
|
+
data: unknown;
|
|
54
|
+
} | {
|
|
55
|
+
type: "runtime_status";
|
|
56
|
+
data: unknown;
|
|
57
|
+
match_id?: string;
|
|
58
|
+
};
|
|
59
|
+
export interface WSClientOptions {
|
|
60
|
+
/** WebSocket URL — e.g. "wss://aifight.ai/api/ws" or
|
|
61
|
+
* "ws://127.0.0.1:<port>/api/ws" in tests. */
|
|
62
|
+
url: string;
|
|
63
|
+
/** In-memory plaintext API key. Caller resolves from credentials.ts
|
|
64
|
+
* (M1-05) before this call; transport never touches keychain. */
|
|
65
|
+
apiKey: string;
|
|
66
|
+
/** Runtime's compiled-in protocol version, SemVer (e.g. "1.0.0"
|
|
67
|
+
* or "v1.0.0"). The optional "v" prefix is stripped before
|
|
68
|
+
* comparison. Major component must match server's
|
|
69
|
+
* server_protocol_version per plan §5.8 / ADR-016. */
|
|
70
|
+
expectedProtocolVersion: string;
|
|
71
|
+
/** Time after WS open() to receive the welcome frame. Default
|
|
72
|
+
* 10_000 ms. Tests can lower for fast-fail coverage. */
|
|
73
|
+
welcomeTimeoutMs?: number;
|
|
74
|
+
/** Client-initiated WS ping frame (opcode 0x9) interval in ms.
|
|
75
|
+
* Default 25_000 (per plan §5.8 / ADR-015 — keeps server's
|
|
76
|
+
* 60s ReadDeadline safely fed). Set to 0 to DISABLE the
|
|
77
|
+
* client-initiated ping entirely (the server's own ping +
|
|
78
|
+
* the `ws` library's automatic pong reply still keep the
|
|
79
|
+
* link alive in that case, but the structural Batch D fix
|
|
80
|
+
* — independent client ping timer — is not active). */
|
|
81
|
+
pingIntervalMs?: number;
|
|
82
|
+
/** Optional caller-controlled AbortSignal. If pre-aborted
|
|
83
|
+
* (signal.aborted=true at the moment createWSClient is called),
|
|
84
|
+
* rejects synchronously with WSAbortedError. If aborted
|
|
85
|
+
* mid-handshake, terminates the socket and rejects with
|
|
86
|
+
* WSAbortedError. If aborted AFTER createWSClient resolves,
|
|
87
|
+
* the WSClient transitions to "closed" (timers cleared,
|
|
88
|
+
* socket terminated) — equivalent to calling close(); no
|
|
89
|
+
* error is thrown since the signal owner is the actor. */
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
}
|
|
92
|
+
export type WSMessageHandler = (msg: ServerMessageEnvelope) => void | Promise<void>;
|
|
93
|
+
/** Frame-level errors surfaced AFTER createWSClient resolves —
|
|
94
|
+
* malformed inbound JSON, schema violations, unknown server
|
|
95
|
+
* message types. Connect / handshake / welcome errors are
|
|
96
|
+
* rejected by createWSClient itself and never reach onError. */
|
|
97
|
+
export type WSErrorHandler = (err: WSClientError) => void | Promise<void>;
|
|
98
|
+
/** Information passed to onClose handlers. The connection is
|
|
99
|
+
* fully torn down by the time this fires; no further onMessage
|
|
100
|
+
* / onError handlers will run. */
|
|
101
|
+
export interface WSCloseInfo {
|
|
102
|
+
/** WS close code from the close frame, or 0 for synthetic
|
|
103
|
+
* closes (abort, transport error before close frame). */
|
|
104
|
+
readonly code: number;
|
|
105
|
+
/** Human-readable reason. For client-initiated close, the
|
|
106
|
+
* string passed to close(); for server-initiated, what the
|
|
107
|
+
* server sent; for abort, the constant "aborted". */
|
|
108
|
+
readonly reason: string;
|
|
109
|
+
/** Who initiated the close: client (close() call), server
|
|
110
|
+
* (server-side close), or abort (AbortSignal fired
|
|
111
|
+
* post-connect). */
|
|
112
|
+
readonly initiator: "client" | "server" | "abort";
|
|
113
|
+
}
|
|
114
|
+
export type WSCloseHandler = (info: WSCloseInfo) => void | Promise<void>;
|
|
115
|
+
/** @internal — passed from createWSClient to WSClientImpl constructor. */
|
|
116
|
+
interface WSClientInternalOpts {
|
|
117
|
+
pingIntervalMs?: number;
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
}
|
|
120
|
+
declare class WSClientImpl {
|
|
121
|
+
#private;
|
|
122
|
+
/** The authenticated welcome frame received from the server. */
|
|
123
|
+
readonly welcome: WSWelcome;
|
|
124
|
+
/**
|
|
125
|
+
* Constructor is REACHABLE only from this module's createWSClient
|
|
126
|
+
* factory. Because WSClientImpl is not exported, consumers of
|
|
127
|
+
* @aifight/aifight cannot `new` it (they only see the
|
|
128
|
+
* `export type WSClient = WSClientImpl` alias, which carries
|
|
129
|
+
* no constructor signature). The `socket` parameter is typed as
|
|
130
|
+
* `unknown` belt-and-suspenders so that even if an internal
|
|
131
|
+
* caller someday passes the wrong shape it fails as a cast
|
|
132
|
+
* mistake rather than a structural type leak.
|
|
133
|
+
*
|
|
134
|
+
* Step 5a additions:
|
|
135
|
+
* - Starts a client-initiated ping timer (default 25s; pass
|
|
136
|
+
* pingIntervalMs=0 to disable).
|
|
137
|
+
* - Wires opts.signal so a post-connect abort triggers a forced
|
|
138
|
+
* close-equivalent transition (state→closed, timer cleared,
|
|
139
|
+
* socket terminated) without throwing — the signal owner is
|
|
140
|
+
* the actor and already knows.
|
|
141
|
+
*
|
|
142
|
+
* Step 5b1 additions:
|
|
143
|
+
* - Re-attaches "message", "error", "close" listeners on the
|
|
144
|
+
* socket (createWSClient's cleanup() removed all listeners
|
|
145
|
+
* during the handshake settle; this is the post-handshake
|
|
146
|
+
* re-attachment Roy flagged in the Step 5b1 brief).
|
|
147
|
+
* - "message" routes through parseServerFrame and dispatches
|
|
148
|
+
* to onMessage / onError handlers.
|
|
149
|
+
* - "close" emits onClose (idempotent via #closeDispatched).
|
|
150
|
+
* - "error" goes to a silent sink; transport errors usually
|
|
151
|
+
* pair with "close" which carries the user-facing signal.
|
|
152
|
+
* Frame-level errors (parse / schema / unknown) come through
|
|
153
|
+
* the message path, not the error event.
|
|
154
|
+
*/
|
|
155
|
+
constructor(socket: unknown, welcome: WSWelcome, opts: WSClientInternalOpts);
|
|
156
|
+
/** Current lifecycle state. Step 5b1 expanded to three values:
|
|
157
|
+
* "connected" after createWSClient resolves, "closing" while
|
|
158
|
+
* close() is awaiting the socket close handshake (briefly),
|
|
159
|
+
* "closed" after close completes (or abort / server close
|
|
160
|
+
* fires). M1-07 may add "reconnecting". */
|
|
161
|
+
get state(): "connected" | "closing" | "closed";
|
|
162
|
+
onMessage(handler: WSMessageHandler): () => void;
|
|
163
|
+
onError(handler: WSErrorHandler): () => void;
|
|
164
|
+
onClose(handler: WSCloseHandler): () => void;
|
|
165
|
+
/**
|
|
166
|
+
* Send an outbound message to the server. Synchronous.
|
|
167
|
+
*
|
|
168
|
+
* @throws {WSClosedError} when the client is not in the "connected"
|
|
169
|
+
* state (i.e. close()/abort/server-close has fired).
|
|
170
|
+
* @throws {WSOutboundSchemaError} when `msg` fails ajv validation
|
|
171
|
+
* against client_<type>.schema.json. The error carries the
|
|
172
|
+
* offending message type and the raw ajv error array.
|
|
173
|
+
*/
|
|
174
|
+
send(msg: WSClientMessage): void;
|
|
175
|
+
/**
|
|
176
|
+
* Initiate a clean WS close. State transitions
|
|
177
|
+
* connected → closing → closed; onClose fires exactly once when
|
|
178
|
+
* the socket's "close" event arrives.
|
|
179
|
+
*
|
|
180
|
+
* Idempotent semantics:
|
|
181
|
+
* - Calling on a "closing" instance returns the in-flight
|
|
182
|
+
* close promise (so two awaiters share the same await).
|
|
183
|
+
* - Calling on a "closed" instance returns immediately.
|
|
184
|
+
* - Subsequent call after either resolves does nothing.
|
|
185
|
+
*/
|
|
186
|
+
close(code?: number, reason?: string): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
/** The public WSClient type. This is a type alias over the internal
|
|
189
|
+
* WSClientImpl class — consumers can use `WSClient` as a TypeScript
|
|
190
|
+
* type (parameter / return / variable annotation) but cannot
|
|
191
|
+
* construct one directly because no class value with this name is
|
|
192
|
+
* exported. The only way to obtain a WSClient is via the
|
|
193
|
+
* createWSClient() factory. */
|
|
194
|
+
export type WSClient = WSClientImpl;
|
|
195
|
+
/**
|
|
196
|
+
* Open a WebSocket to `opts.url`, send `X-API-Key` in the upgrade
|
|
197
|
+
* request, wait for the server's welcome frame, validate it
|
|
198
|
+
* (ajv + protocol-version major), and return a WSClient bound to
|
|
199
|
+
* the open socket.
|
|
200
|
+
*
|
|
201
|
+
* Resolves on welcome accepted. Rejects with one of:
|
|
202
|
+
* - WSConnectError — TCP/TLS/DNS failure (no HTTP response)
|
|
203
|
+
* - WSHandshakeError — HTTP upgrade returned non-101 (4xx/5xx)
|
|
204
|
+
* - WSWelcomeTimeoutError — open succeeded but no frame within
|
|
205
|
+
* welcomeTimeoutMs (default 10s)
|
|
206
|
+
* - WSWelcomeInvalidError — first frame is not a valid welcome:
|
|
207
|
+
* malformed JSON, unknown type,
|
|
208
|
+
* wrong type, or schema-invalid welcome
|
|
209
|
+
* (ajv errors carried)
|
|
210
|
+
* - WSProtocolVersionError — welcome valid but
|
|
211
|
+
* server_protocol_version major
|
|
212
|
+
* differs from expectedProtocolVersion
|
|
213
|
+
*
|
|
214
|
+
* Step 4 caveats:
|
|
215
|
+
* - No AbortSignal (Step 5).
|
|
216
|
+
* - No reconnect (M1-07).
|
|
217
|
+
* - No retry on any error class — caller decides.
|
|
218
|
+
*/
|
|
219
|
+
export declare function createWSClient(opts: WSClientOptions): Promise<WSClient>;
|
|
220
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface AjvLikeError {
|
|
2
|
+
readonly instancePath: string;
|
|
3
|
+
readonly message?: string;
|
|
4
|
+
}
|
|
5
|
+
export type WSClientErrorKind = "connect" | "handshake" | "welcome-timeout" | "welcome-invalid" | "protocol-version" | "closed" | "schema" | "outbound-schema" | "unknown-message" | "aborted";
|
|
6
|
+
export declare abstract class WSClientError extends Error {
|
|
7
|
+
abstract readonly kind: WSClientErrorKind;
|
|
8
|
+
}
|
|
9
|
+
/** TCP / TLS / DNS layer failure: the WebSocket couldn't even
|
|
10
|
+
* reach the HTTP upgrade step. Wraps the underlying Node net /
|
|
11
|
+
* tls / dns error (or whatever the `ws` library surfaced). */
|
|
12
|
+
export declare class WSConnectError extends WSClientError {
|
|
13
|
+
readonly kind: "connect";
|
|
14
|
+
readonly cause?: unknown;
|
|
15
|
+
constructor(message: string, cause?: unknown);
|
|
16
|
+
}
|
|
17
|
+
/** HTTP upgrade was attempted and the server replied with an error
|
|
18
|
+
* status (typically 401 invalid api key, 404 wrong path, 5xx
|
|
19
|
+
* server fault). `responseBody` is the raw response payload as a
|
|
20
|
+
* string; callers SHOULD NOT JSON.parse it without checking
|
|
21
|
+
* Content-Type, since some 5xx pages are HTML. */
|
|
22
|
+
export declare class WSHandshakeError extends WSClientError {
|
|
23
|
+
readonly kind: "handshake";
|
|
24
|
+
readonly statusCode: number;
|
|
25
|
+
readonly responseBody: string;
|
|
26
|
+
readonly cause?: unknown;
|
|
27
|
+
constructor(statusCode: number, responseBody: string, message: string, cause?: unknown);
|
|
28
|
+
}
|
|
29
|
+
/** WS open succeeded but no frame arrived within
|
|
30
|
+
* `welcomeTimeoutMs` (default 10s). Distinguished from
|
|
31
|
+
* WSConnectError so callers can differentiate "couldn't reach
|
|
32
|
+
* server" from "reached server but it's not speaking". */
|
|
33
|
+
export declare class WSWelcomeTimeoutError extends WSClientError {
|
|
34
|
+
readonly kind: "welcome-timeout";
|
|
35
|
+
constructor(message: string);
|
|
36
|
+
}
|
|
37
|
+
/** First server frame arrived but is not a valid welcome — wrong
|
|
38
|
+
* type, malformed JSON, or fails ajv against
|
|
39
|
+
* server_welcome.schema.json. `ajvErrors` is the raw ajv error
|
|
40
|
+
* array (or empty for non-ajv failures like "wrong type"). */
|
|
41
|
+
export declare class WSWelcomeInvalidError extends WSClientError {
|
|
42
|
+
readonly kind: "welcome-invalid";
|
|
43
|
+
readonly ajvErrors: readonly AjvLikeError[];
|
|
44
|
+
constructor(ajvErrors: readonly AjvLikeError[], message: string);
|
|
45
|
+
}
|
|
46
|
+
/** server_protocol_version's major component does not match the
|
|
47
|
+
* runtime's compiled-in expectedProtocolVersion. Per plan §5.8 +
|
|
48
|
+
* server_welcome.schema.json the runtime MUST refuse such
|
|
49
|
+
* connections (major bumps are breaking). Minor / patch
|
|
50
|
+
* mismatches do NOT throw this — they are silently accepted. */
|
|
51
|
+
export declare class WSProtocolVersionError extends WSClientError {
|
|
52
|
+
readonly kind: "protocol-version";
|
|
53
|
+
readonly clientVersion: string;
|
|
54
|
+
readonly serverVersion: string;
|
|
55
|
+
constructor(clientVersion: string, serverVersion: string, message: string);
|
|
56
|
+
}
|
|
57
|
+
/** Operation attempted on a WSClient whose state is not
|
|
58
|
+
* "connected": send() during closing, send() after close,
|
|
59
|
+
* close() called twice (this is no-op, not an error — but
|
|
60
|
+
* internal use may distinguish). */
|
|
61
|
+
export declare class WSClosedError extends WSClientError {
|
|
62
|
+
readonly kind: "closed";
|
|
63
|
+
constructor(message: string);
|
|
64
|
+
}
|
|
65
|
+
/** Inbound frame failed ajv validation against its
|
|
66
|
+
* per-message schema. Indicates server bug or protocol drift —
|
|
67
|
+
* surfaced to onError handler; the connection stays open and
|
|
68
|
+
* the offending message is dropped. `messageType` is the
|
|
69
|
+
* envelope's `type` field if parseable, else "<unknown>". */
|
|
70
|
+
export declare class WSSchemaError extends WSClientError {
|
|
71
|
+
readonly kind: "schema";
|
|
72
|
+
readonly messageType: string;
|
|
73
|
+
readonly ajvErrors: readonly AjvLikeError[];
|
|
74
|
+
constructor(messageType: string, ajvErrors: readonly AjvLikeError[], message: string);
|
|
75
|
+
}
|
|
76
|
+
/** Outbound message passed to send() failed ajv validation
|
|
77
|
+
* against the matching client_*.schema.json. Indicates LOCAL
|
|
78
|
+
* code bug (we tried to send something the server would
|
|
79
|
+
* reject) — thrown synchronously to the calling code, message
|
|
80
|
+
* never reaches the wire. Kept distinct from WSSchemaError
|
|
81
|
+
* because callers MUST treat it as a programming error, not a
|
|
82
|
+
* runtime drift signal. */
|
|
83
|
+
export declare class WSOutboundSchemaError extends WSClientError {
|
|
84
|
+
readonly kind: "outbound-schema";
|
|
85
|
+
readonly messageType: string;
|
|
86
|
+
readonly ajvErrors: readonly AjvLikeError[];
|
|
87
|
+
constructor(messageType: string, ajvErrors: readonly AjvLikeError[], message: string);
|
|
88
|
+
}
|
|
89
|
+
/** Inbound frame's `type` field is not in the dispatch table
|
|
90
|
+
* (see protocol/schemas.ts MESSAGE_TYPE_TO_FILE). Server bug
|
|
91
|
+
* or version skew — surfaced to onError, message dropped,
|
|
92
|
+
* connection stays open. */
|
|
93
|
+
export declare class WSUnknownMessageError extends WSClientError {
|
|
94
|
+
readonly kind: "unknown-message";
|
|
95
|
+
readonly messageType: string;
|
|
96
|
+
constructor(messageType: string, message: string);
|
|
97
|
+
}
|
|
98
|
+
/** The caller-controlled AbortSignal fired during connect() or a
|
|
99
|
+
* mid-flight operation. `cause` carries the abort reason
|
|
100
|
+
* (AbortSignal.reason) so callers can distinguish their own
|
|
101
|
+
* abort categories without parsing message text. */
|
|
102
|
+
export declare class WSAbortedError extends WSClientError {
|
|
103
|
+
readonly kind: "aborted";
|
|
104
|
+
readonly cause?: unknown;
|
|
105
|
+
constructor(message: string, cause?: unknown);
|
|
106
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ClientMessageEnvelope {
|
|
2
|
+
type: string;
|
|
3
|
+
data?: unknown;
|
|
4
|
+
/** REQUIRED for type=action (per client_action.schema.json), optional otherwise. */
|
|
5
|
+
match_id?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ServerMessageEnvelope {
|
|
8
|
+
type: string;
|
|
9
|
+
data: unknown;
|
|
10
|
+
match_id?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Warm the ajv cache up-front. Optional — getAjv() is lazy and
|
|
13
|
+
* serializeClientMessage / parseServerFrame call it on first use. */
|
|
14
|
+
export declare function initFrameHandler(): void;
|
|
15
|
+
/** Test-only: drop the ajv singleton so the next getAjv() call
|
|
16
|
+
* rewalks loadAllSchemas(). Pairs with __resetSchemasRootCache()
|
|
17
|
+
* in protocol/schemas.ts. NOT exported from src/index.ts. */
|
|
18
|
+
export declare function __resetFrameHandlerCache(): void;
|
|
19
|
+
export declare function serializeClientMessage(msg: ClientMessageEnvelope): string;
|
|
20
|
+
export declare function parseServerFrame(raw: string | Buffer): ServerMessageEnvelope;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type WSClientMessage, type WSWelcome, type WSMessageHandler, type WSErrorHandler } from "./client";
|
|
2
|
+
import { WSClientError } from "./errors";
|
|
3
|
+
/** Reasons the reconnect facade can transition to terminal "closed" state.
|
|
4
|
+
* ReconnectCloseInfo.kind and ReconnectStoppedError.kind share this union —
|
|
5
|
+
* 5 values used consistently across onClose handler, give-up event, and
|
|
6
|
+
* cause chain (rev 2 Codex C4). */
|
|
7
|
+
export type ReconnectStopReason = "caller-close" | "signal" | "fatal-close" | "fatal-error" | "max-attempts";
|
|
8
|
+
/** Inline error class — reconnect's own final-state error. Lives at the top
|
|
9
|
+
* of reconnect.ts, NOT in wsclient/errors.ts (scope fence #1).
|
|
10
|
+
* rev 2 Codex C4: renamed from ReconnectAbortedError to ReconnectStoppedError. */
|
|
11
|
+
export declare class ReconnectStoppedError extends Error {
|
|
12
|
+
readonly name = "ReconnectStoppedError";
|
|
13
|
+
readonly kind: ReconnectStopReason;
|
|
14
|
+
readonly cause: WSClientError | undefined;
|
|
15
|
+
constructor(kind: ReconnectStopReason, cause: WSClientError | undefined, message: string);
|
|
16
|
+
}
|
|
17
|
+
/** Terminal-close info passed to facade.onClose handler (rev 2 Codex C3).
|
|
18
|
+
* NOT a re-use of M1-06 WSCloseInfo — facade close semantics are wider than
|
|
19
|
+
* a single WS close frame. **No wasClean field** (M1-06 WSCloseInfo also
|
|
20
|
+
* doesn't have one; rev 1 wrote it incorrectly, rev 2 removed). */
|
|
21
|
+
export interface ReconnectCloseInfo {
|
|
22
|
+
readonly kind: ReconnectStopReason;
|
|
23
|
+
readonly code?: number;
|
|
24
|
+
readonly closeReason?: string;
|
|
25
|
+
readonly cause?: WSClientError | ReconnectStoppedError;
|
|
26
|
+
}
|
|
27
|
+
export type ReconnectCloseHandler = (info: ReconnectCloseInfo) => void;
|
|
28
|
+
/** Backoff jitter strategy. Default "full" (rev 2 Roy 拍板 #2). */
|
|
29
|
+
export type JitterStrategy = "none" | "full" | "equal";
|
|
30
|
+
export interface ReconnectingWSClientOptions {
|
|
31
|
+
url: string;
|
|
32
|
+
apiKey: string;
|
|
33
|
+
expectedProtocolVersion: string;
|
|
34
|
+
initialBackoffMs?: number;
|
|
35
|
+
backoffFactor?: number;
|
|
36
|
+
maxBackoffMs?: number;
|
|
37
|
+
jitter?: JitterStrategy;
|
|
38
|
+
/** Default: undefined → no cap (Roy 拍板 #3). Caller controls termination
|
|
39
|
+
* via signal + AbortController.abort(timeoutMs). */
|
|
40
|
+
maxAttempts?: number;
|
|
41
|
+
welcomeTimeoutMs?: number;
|
|
42
|
+
pingIntervalMs?: number;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}
|
|
45
|
+
export interface ReconnectEvent {
|
|
46
|
+
readonly type: "attempt-start" | "attempt-success" | "attempt-failure" | "give-up";
|
|
47
|
+
readonly attempt: number;
|
|
48
|
+
readonly nextDelayMs?: number;
|
|
49
|
+
readonly cause?: WSClientError | ReconnectStoppedError;
|
|
50
|
+
readonly elapsedMs: number;
|
|
51
|
+
readonly severity: "info" | "warning" | "error";
|
|
52
|
+
}
|
|
53
|
+
export type ReconnectEventHandler = (ev: ReconnectEvent) => void;
|
|
54
|
+
/** Stable facade — caller holds this reference indefinitely. Inner WSClient
|
|
55
|
+
* is mutable across reconnects; facade type is stable. */
|
|
56
|
+
export interface ReconnectingWSClient {
|
|
57
|
+
readonly state: "connecting" | "connected" | "backoff" | "closed";
|
|
58
|
+
readonly attempt: number;
|
|
59
|
+
readonly welcome: WSWelcome | null;
|
|
60
|
+
send(msg: WSClientMessage): void;
|
|
61
|
+
onMessage(handler: WSMessageHandler): () => void;
|
|
62
|
+
onError(handler: WSErrorHandler): () => void;
|
|
63
|
+
onClose(handler: ReconnectCloseHandler): () => void;
|
|
64
|
+
onReconnect(handler: ReconnectEventHandler): () => void;
|
|
65
|
+
close(code?: number, reason?: string): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Open a reconnecting WebSocket session. Returns a Promise that:
|
|
69
|
+
*
|
|
70
|
+
* - **resolves** on the FIRST inner WSClient connect+welcome success
|
|
71
|
+
* - **rejects** with ReconnectStoppedError on fatal first failure (signal
|
|
72
|
+
* pre-aborted / WSHandshakeError 401|403|404 / WSWelcomeInvalidError /
|
|
73
|
+
* WSProtocolVersionError / WSAbortedError / max-attempts during the
|
|
74
|
+
* first-connect retry chain)
|
|
75
|
+
* - **stays pending** while transient first failures (WSConnectError /
|
|
76
|
+
* WSWelcomeTimeoutError / WSHandshakeError 408|429|5xx) drive backoff
|
|
77
|
+
* and re-attempt, until a success or fatal terminator
|
|
78
|
+
*
|
|
79
|
+
* After the Promise resolves, the returned facade survives across server
|
|
80
|
+
* disconnects: inner WSClient close → backoff → new createWSClient →
|
|
81
|
+
* handlers re-wired. Caller's onMessage / onError / onClose / onReconnect
|
|
82
|
+
* handlers persist across reconnects automatically.
|
|
83
|
+
*/
|
|
84
|
+
export declare function createReconnectingWSClient(opts: ReconnectingWSClientOptions): Promise<ReconnectingWSClient>;
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aifight/aifight",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "AIFight CLI — local outbound bridge that connects AIFight to user-selected Agent runtimes.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/types/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"aifight": "dist/bin.mjs",
|
|
17
|
+
"aifight-bridge": "dist/bin.mjs"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.19"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/better-sqlite3": "7.6.13",
|
|
31
|
+
"@types/node": "^22.10.0",
|
|
32
|
+
"@types/ws": "8.18.1",
|
|
33
|
+
"ajv": "^8.17.1",
|
|
34
|
+
"ajv-formats": "^3.0.1",
|
|
35
|
+
"esbuild": "^0.24.0",
|
|
36
|
+
"typescript": "^5.6.0",
|
|
37
|
+
"vitest": "^2.1.0",
|
|
38
|
+
"ws": "8.20.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "./build.sh",
|
|
42
|
+
"gen:schema": "node scripts/bundle-schema.mjs",
|
|
43
|
+
"pack:check": "node scripts/verify-packlist.mjs",
|
|
44
|
+
"prepublishOnly": "npm run build && npm run pack:check",
|
|
45
|
+
"test": "npm run gen:schema && vitest run",
|
|
46
|
+
"check-types": "npm run gen:schema && tsc --noEmit -p tsconfig.json"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
50
|
+
"@napi-rs/keyring": "1.2.0",
|
|
51
|
+
"better-sqlite3": "12.9.0"
|
|
52
|
+
}
|
|
53
|
+
}
|