xacpp 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.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/README.zh-CN.md +121 -0
- package/dist/cjs/index.js +516 -0
- package/dist/esm/commands/index.d.ts +23 -0
- package/dist/esm/events/content.d.ts +40 -0
- package/dist/esm/events/index.d.ts +10 -0
- package/dist/esm/events/interaction.d.ts +88 -0
- package/dist/esm/events/payload.d.ts +51 -0
- package/dist/esm/events/upload.d.ts +25 -0
- package/dist/esm/events/xacpp_event.d.ts +105 -0
- package/dist/esm/handler.d.ts +41 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.mjs +467 -0
- package/dist/esm/message.d.ts +99 -0
- package/dist/esm/peer.d.ts +79 -0
- package/dist/esm/session.d.ts +34 -0
- package/dist/esm/socket-transport.d.ts +35 -0
- package/dist/esm/stdio-transport.d.ts +37 -0
- package/dist/esm/transport.d.ts +61 -0
- package/package.json +44 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XACPP protocol messages — envelope layer design.
|
|
3
|
+
*
|
|
4
|
+
* Wire messages are uniformly `XacppEnvelope`, divided into two layers:
|
|
5
|
+
*
|
|
6
|
+
* - **Envelope layer**: `type` (routing) + `id` (correlation) + `payload` (business content)
|
|
7
|
+
* - **Payload layer**: `XacppRequest` / `XacppResponse`
|
|
8
|
+
*
|
|
9
|
+
* Transport handles envelope packing/unpacking and id correlation; upper layers only operate on payloads.
|
|
10
|
+
*
|
|
11
|
+
* ## JSON format examples
|
|
12
|
+
*
|
|
13
|
+
* ```json
|
|
14
|
+
* Request (Command): {"id":"r1","type":"request","payload":{"kind":"command","payload":{"establish":{"credentials":null}}}}
|
|
15
|
+
* Response (Established): {"id":"r1","type":"response","payload":{"kind":"established","sessionId":"s1"}}
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { XacppCommand } from "./commands";
|
|
19
|
+
import type { ActionResponse, QuestionResponse, SensitiveInfoOperationResponse, XacppEvent } from "./events";
|
|
20
|
+
/** XACPP error. */
|
|
21
|
+
export declare class XacppError extends Error {
|
|
22
|
+
/** Machine-readable error code. */
|
|
23
|
+
readonly code: string;
|
|
24
|
+
constructor(code: string, message: string);
|
|
25
|
+
/** Connection error: not connected. */
|
|
26
|
+
static notConnected(): XacppError;
|
|
27
|
+
/** Connection error: already connected. */
|
|
28
|
+
static alreadyConnected(): XacppError;
|
|
29
|
+
/** Connection error: connection closed. */
|
|
30
|
+
static closed(): XacppError;
|
|
31
|
+
/** Processing error: no handler registered. */
|
|
32
|
+
static noHandler(): XacppError;
|
|
33
|
+
/** Processing error: handler internal error. */
|
|
34
|
+
static internal(message: string): XacppError;
|
|
35
|
+
/** Application-layer custom error. */
|
|
36
|
+
static application(code: string, message: string): XacppError;
|
|
37
|
+
/** Processing error: request payload unparseable. */
|
|
38
|
+
static invalidRequest(message: string): XacppError;
|
|
39
|
+
/** Handshake rejected. */
|
|
40
|
+
static establishReject(reason: string): XacppError;
|
|
41
|
+
}
|
|
42
|
+
/** Request payload. Accepted by Transport's `send` method. */
|
|
43
|
+
export type XacppRequest = {
|
|
44
|
+
kind: "command";
|
|
45
|
+
payload: XacppCommand;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "event";
|
|
48
|
+
payload: XacppEvent;
|
|
49
|
+
};
|
|
50
|
+
/** Response payload. Returned by Transport's `send` method. */
|
|
51
|
+
export type XacppResponse =
|
|
52
|
+
/** Handshake success: issues session identifier and credentials. */
|
|
53
|
+
{
|
|
54
|
+
kind: "established";
|
|
55
|
+
sessionId: string;
|
|
56
|
+
credentials?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Handshake rejected. */
|
|
59
|
+
| {
|
|
60
|
+
kind: "establish_reject";
|
|
61
|
+
reason: string;
|
|
62
|
+
}
|
|
63
|
+
/** Tool call authorization response. */
|
|
64
|
+
| {
|
|
65
|
+
kind: "action";
|
|
66
|
+
requestId: string;
|
|
67
|
+
} & ActionResponse
|
|
68
|
+
/** User question response. */
|
|
69
|
+
| {
|
|
70
|
+
kind: "question";
|
|
71
|
+
requestId: string;
|
|
72
|
+
} & QuestionResponse
|
|
73
|
+
/** Sensitive info operation response (flatten: response fields spread to top level). */
|
|
74
|
+
| {
|
|
75
|
+
kind: "sensitive_info_operation";
|
|
76
|
+
requestId: string;
|
|
77
|
+
} & SensitiveInfoOperationResponse
|
|
78
|
+
/** Generic acknowledge: request processed successfully, no data returned. */
|
|
79
|
+
| {
|
|
80
|
+
kind: "acknowledge";
|
|
81
|
+
}
|
|
82
|
+
/** Processing failure. */
|
|
83
|
+
| {
|
|
84
|
+
kind: "error";
|
|
85
|
+
code: string;
|
|
86
|
+
message: string;
|
|
87
|
+
};
|
|
88
|
+
/** Wire message. Envelope layer handles routing. */
|
|
89
|
+
export type XacppEnvelope = {
|
|
90
|
+
type: "request";
|
|
91
|
+
id: string;
|
|
92
|
+
session_id?: string;
|
|
93
|
+
payload: XacppRequest;
|
|
94
|
+
} | {
|
|
95
|
+
type: "response";
|
|
96
|
+
id: string;
|
|
97
|
+
session_id?: string;
|
|
98
|
+
payload: XacppResponse;
|
|
99
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XACPP Peer — protocol layer endpoint.
|
|
3
|
+
*
|
|
4
|
+
* ## Responsibility
|
|
5
|
+
*
|
|
6
|
+
* Peer is a protocol layer entity representing one endpoint in the communication link. Core responsibilities:
|
|
7
|
+
*
|
|
8
|
+
* - **Typed operations**: encapsulate Transport's payload-layer API into semantically clear Command / Event operations
|
|
9
|
+
* - **Protocol state machine**: manage connection state
|
|
10
|
+
* - **Session routing**: route inbound requests to the corresponding Session's handler by session_id
|
|
11
|
+
*
|
|
12
|
+
* ## Boundary with Transport
|
|
13
|
+
*
|
|
14
|
+
* Peer holds `XacppTransport` (composition), delegating all low-level IO to Transport.
|
|
15
|
+
* Peer does not care about envelope id, encoding/decoding, request-response correlation (all encapsulated by Transport).
|
|
16
|
+
*
|
|
17
|
+
* ```text
|
|
18
|
+
* ┌──────────────────────────────────────────────────────────────┐
|
|
19
|
+
* │ Peer (protocol layer) │
|
|
20
|
+
* │ Typed operations: request_command / request_event │
|
|
21
|
+
* │ Session routing: session_id → XacppSessionHandler │
|
|
22
|
+
* ├──────────────────────────────────────────────────────────────┤
|
|
23
|
+
* │ Session (session layer) │
|
|
24
|
+
* │ Holds XacppSessionHandler, sends directly to Transport │
|
|
25
|
+
* │ Does not go through Peer │
|
|
26
|
+
* ├──────────────────────────────────────────────────────────────┤
|
|
27
|
+
* │ Transport (transport layer) │
|
|
28
|
+
* │ Single semantic: send (request-response), caller chooses │
|
|
29
|
+
* │ whether to wait for response │
|
|
30
|
+
* │ Internals: envelope packing/unpacking (id correlation), │
|
|
31
|
+
* │ encoding/decoding, pending matching │
|
|
32
|
+
* │ Unaware of specific business event types │
|
|
33
|
+
* └──────────────────────────────────────────────────────────────┘
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
import type { XacppTransport } from "./transport";
|
|
37
|
+
import type { XacppCommand } from "./commands";
|
|
38
|
+
import type { XacppEvent } from "./events";
|
|
39
|
+
import type { XacppResponse } from "./message";
|
|
40
|
+
import type { EstablishHandler, XacppSessionHandler } from "./handler";
|
|
41
|
+
import { XacppSession } from "./session";
|
|
42
|
+
/** Peer protocol state. */
|
|
43
|
+
export declare enum PeerState {
|
|
44
|
+
/** Not connected / connection closed. */
|
|
45
|
+
Disconnected = "disconnected",
|
|
46
|
+
/** Communication channel established, ready to create logical sessions. */
|
|
47
|
+
Connected = "connected"
|
|
48
|
+
}
|
|
49
|
+
/** XACPP protocol endpoint.
|
|
50
|
+
*
|
|
51
|
+
* Each communication party holds a `XacppPeer` instance, exchanging messages via a shared Transport.
|
|
52
|
+
*/
|
|
53
|
+
export declare class XacppPeer {
|
|
54
|
+
private transport;
|
|
55
|
+
private _state;
|
|
56
|
+
private sessions;
|
|
57
|
+
private establishHandler;
|
|
58
|
+
constructor(transport: XacppTransport, establishHandler: EstablishHandler);
|
|
59
|
+
/** Current protocol state. */
|
|
60
|
+
get state(): PeerState;
|
|
61
|
+
/** Establish connection.
|
|
62
|
+
*
|
|
63
|
+
* Registers routing closure with Transport, then starts the underlying communication channel.
|
|
64
|
+
* On success, state transitions to `Connected`; subsequent `establish` calls can create logical sessions.
|
|
65
|
+
*/
|
|
66
|
+
connect(): Promise<void>;
|
|
67
|
+
/** Establish logical session.
|
|
68
|
+
*
|
|
69
|
+
* Sends Establish command to peer, carrying optional auth credentials and session handler.
|
|
70
|
+
* Handler is registered in Peer routing table; Session is responsible for sending.
|
|
71
|
+
*/
|
|
72
|
+
establish(credentials: string | null, handler: XacppSessionHandler): Promise<XacppSession>;
|
|
73
|
+
/** Disconnect. */
|
|
74
|
+
disconnect(): Promise<void>;
|
|
75
|
+
/** Send command and wait for response (no session context). */
|
|
76
|
+
requestCommand(sessionId: string | null, command: XacppCommand): Promise<XacppResponse>;
|
|
77
|
+
/** Send interactive event and wait for response (no session context). */
|
|
78
|
+
requestEvent(sessionId: string | null, event: XacppEvent): Promise<XacppResponse>;
|
|
79
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XACPP logical session.
|
|
3
|
+
*
|
|
4
|
+
* Created via `XacppPeer.establish`, holds an independent session_id and credentials.
|
|
5
|
+
* Multiple Sessions under the same Peer share the same connection.
|
|
6
|
+
*/
|
|
7
|
+
import type { XacppTransport } from "./transport";
|
|
8
|
+
import type { XacppCommand } from "./commands";
|
|
9
|
+
import type { XacppEvent } from "./events";
|
|
10
|
+
import type { XacppResponse } from "./message";
|
|
11
|
+
/** XACPP logical session.
|
|
12
|
+
*
|
|
13
|
+
* Created via `XacppPeer.establish`, holds an independent session_id and credentials.
|
|
14
|
+
* Multiple Sessions under the same Peer share the same connection.
|
|
15
|
+
*/
|
|
16
|
+
export declare class XacppSession {
|
|
17
|
+
private transport;
|
|
18
|
+
private _sessionId;
|
|
19
|
+
private _credentials;
|
|
20
|
+
/** @internal Created by XacppPeer.establish. */
|
|
21
|
+
constructor(transport: XacppTransport, sessionId: string, credentials: string | null);
|
|
22
|
+
/** Session identifier. */
|
|
23
|
+
get sessionId(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Credentials issued by the responder.
|
|
26
|
+
*
|
|
27
|
+
* Caller can save them for use in subsequent `establish` calls.
|
|
28
|
+
*/
|
|
29
|
+
get credentials(): string | null;
|
|
30
|
+
/** Send command and wait for response. */
|
|
31
|
+
requestCommand(command: XacppCommand): Promise<XacppResponse>;
|
|
32
|
+
/** Send event and wait for response. */
|
|
33
|
+
requestEvent(event: XacppEvent): Promise<XacppResponse>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import type { RequestHandler, XacppTransport } from "./transport";
|
|
3
|
+
import type { XacppRequest, XacppResponse } from "./message";
|
|
4
|
+
export declare class SocketTransport implements XacppTransport {
|
|
5
|
+
private socket;
|
|
6
|
+
private handler;
|
|
7
|
+
private pending;
|
|
8
|
+
private nextId;
|
|
9
|
+
private _connected;
|
|
10
|
+
private _exhausted;
|
|
11
|
+
private writeQueue;
|
|
12
|
+
private inflight;
|
|
13
|
+
private port?;
|
|
14
|
+
private host?;
|
|
15
|
+
private rl;
|
|
16
|
+
/** Client mode: connect() initiates a TCP connection to the specified address. */
|
|
17
|
+
static connectTo(port: number, host?: string): SocketTransport;
|
|
18
|
+
/** Server mode: use an already-accepted Socket. */
|
|
19
|
+
constructor(socket?: net.Socket);
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
disconnect(): Promise<void>;
|
|
22
|
+
send(sessionId: string | null, payload: XacppRequest): Promise<XacppResponse>;
|
|
23
|
+
onRequest(handler: RequestHandler): void;
|
|
24
|
+
private onFrame;
|
|
25
|
+
/** Handle inbound request: spawn-per-request, does not await. */
|
|
26
|
+
private dispatchRequest;
|
|
27
|
+
/** Serialize and send envelope (write-protected: promise chain serialization).
|
|
28
|
+
*
|
|
29
|
+
* Returns only the promise for the current write, not the entire chain.
|
|
30
|
+
* Chain always continues (even if a write fails), ensuring subsequent writes are not blocked.
|
|
31
|
+
*/
|
|
32
|
+
private writeEnvelope;
|
|
33
|
+
private rejectAllPending;
|
|
34
|
+
private cleanup;
|
|
35
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdio Transport implementation.
|
|
3
|
+
*
|
|
4
|
+
* Communicates via stdin/stdout pipe handles using JSONL frame protocol (one message per line, separated by `\n`).
|
|
5
|
+
*/
|
|
6
|
+
import type { RequestHandler, XacppTransport } from "./transport";
|
|
7
|
+
import type { XacppRequest, XacppResponse } from "./message";
|
|
8
|
+
/** Stdio Transport implementation. */
|
|
9
|
+
export declare class StdioTransport implements XacppTransport {
|
|
10
|
+
private writer;
|
|
11
|
+
private reader;
|
|
12
|
+
private rl;
|
|
13
|
+
private _connected;
|
|
14
|
+
private _exhausted;
|
|
15
|
+
/** Handler registration. */
|
|
16
|
+
private requestHandler;
|
|
17
|
+
/** Pending map: id → { resolve, reject }. */
|
|
18
|
+
private pending;
|
|
19
|
+
/** Auto-incrementing id. */
|
|
20
|
+
private nextId;
|
|
21
|
+
constructor(writer: NodeJS.WritableStream, reader: NodeJS.ReadableStream);
|
|
22
|
+
private genId;
|
|
23
|
+
connect(): Promise<void>;
|
|
24
|
+
disconnect(): Promise<void>;
|
|
25
|
+
send(sessionId: string | null, payload: XacppRequest): Promise<XacppResponse>;
|
|
26
|
+
onRequest(handler: RequestHandler): void;
|
|
27
|
+
/** Process a frame received from the wire. */
|
|
28
|
+
private onFrame;
|
|
29
|
+
/** Handle inbound request envelope: dispatch handler, send response. */
|
|
30
|
+
private handleRequest;
|
|
31
|
+
/** Handle inbound response envelope: match pending. */
|
|
32
|
+
private handleResponse;
|
|
33
|
+
/** Serialize and send envelope. Returns whether write succeeded. */
|
|
34
|
+
private writeEnvelope;
|
|
35
|
+
/** Cleanup on connection close. */
|
|
36
|
+
private cleanup;
|
|
37
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XACPP Transport abstraction.
|
|
3
|
+
*
|
|
4
|
+
* ## Responsibility
|
|
5
|
+
*
|
|
6
|
+
* Transport unifies the underlying communication channel (stdio / TCP / WebSocket) into `send` semantics:
|
|
7
|
+
*
|
|
8
|
+
* - **send**: send request payload, wait for response payload to return. Caller can spawn to background if response is not needed.
|
|
9
|
+
* - **accept**: listen for peer requests, dispatch via `onRequest` callback,
|
|
10
|
+
* which receives session_id + payload; the return value is automatically sent back as a response.
|
|
11
|
+
*
|
|
12
|
+
* Transport internals:
|
|
13
|
+
*
|
|
14
|
+
* - **Envelope packing/unpacking**: auto-assign request id, pack into envelope for sending, unpack to return payload
|
|
15
|
+
* - **Request-response correlation**: match pending sends by id upon receiving a Response
|
|
16
|
+
* - **Encoding/decoding**: serialization / deserialization (JSONL)
|
|
17
|
+
* - **Connection management**: establish / tear down underlying communication channel
|
|
18
|
+
*
|
|
19
|
+
* ## Layer boundary
|
|
20
|
+
*
|
|
21
|
+
* - **Transport to upper layer**: exposes `send` / `onRequest`,
|
|
22
|
+
* does not expose raw byte send/receive, envelope id, or encoding/decoding details
|
|
23
|
+
* - **Peer to upper layer**: exposes typed `requestCommand` / `requestEvent`
|
|
24
|
+
* and session routing mechanism
|
|
25
|
+
*
|
|
26
|
+
* ## accept semantics
|
|
27
|
+
*
|
|
28
|
+
* Transport listens for peer input, delivering (session_id, payload) to registered callbacks.
|
|
29
|
+
* Handler returning `XacppResponse` indicates successful processing;
|
|
30
|
+
* throwing `XacppError` indicates processing failure (Transport auto-constructs an Error response and sends it back).
|
|
31
|
+
*
|
|
32
|
+
* ## Error semantics
|
|
33
|
+
*
|
|
34
|
+
* **Connection-level throw = connection unavailable**. All fault-tolerance logic is encapsulated inside the Transport implementation.
|
|
35
|
+
* Upper layer only needs one rule: method throw means connection error.
|
|
36
|
+
*/
|
|
37
|
+
import type { XacppRequest, XacppResponse } from "./message";
|
|
38
|
+
/** Transport layer request handler callback type. */
|
|
39
|
+
export type RequestHandler = (sessionId: string | null, payload: XacppRequest) => Promise<XacppResponse>;
|
|
40
|
+
/** XACPP transport layer abstraction. */
|
|
41
|
+
export interface XacppTransport {
|
|
42
|
+
/** Establish the underlying communication channel and start the accept loop. */
|
|
43
|
+
connect(): Promise<void>;
|
|
44
|
+
/** Tear down the underlying communication channel. */
|
|
45
|
+
disconnect(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Send request payload and wait for response.
|
|
48
|
+
*
|
|
49
|
+
* Transport auto-assigns id, packs envelope, serializes and sends, registers pending, waits for response, unpacks and returns payload.
|
|
50
|
+
* Caller can skip await if response is not needed.
|
|
51
|
+
*/
|
|
52
|
+
send(sessionId: string | null, payload: XacppRequest): Promise<XacppResponse>;
|
|
53
|
+
/**
|
|
54
|
+
* Register request callback (unified for Command and Event).
|
|
55
|
+
*
|
|
56
|
+
* Must be called before `connect`, otherwise throws XacppError(AlreadyConnected).
|
|
57
|
+
* When handler returns `Ok`, Transport auto-packs into same-id envelope and sends back;
|
|
58
|
+
* when handler throws, Transport auto-constructs an Error response and sends back.
|
|
59
|
+
*/
|
|
60
|
+
onRequest(handler: RequestHandler): void;
|
|
61
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "xacpp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent Control Plane Protocol — TypeScript implementation",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"xacpp",
|
|
7
|
+
"acpp",
|
|
8
|
+
"protocol",
|
|
9
|
+
"communication",
|
|
10
|
+
"peer"
|
|
11
|
+
],
|
|
12
|
+
"author": "drodreo",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"main": "./dist/cjs/index.js",
|
|
15
|
+
"module": "./dist/esm/index.mjs",
|
|
16
|
+
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./dist/esm/index.d.ts",
|
|
21
|
+
"default": "./dist/esm/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"require": {
|
|
24
|
+
"types": "./dist/esm/index.d.ts",
|
|
25
|
+
"default": "./dist/cjs/index.js"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "rslib build",
|
|
34
|
+
"prepublishOnly": "rslib build",
|
|
35
|
+
"test": "vitest run"
|
|
36
|
+
},
|
|
37
|
+
"packageManager": "pnpm@10.33.0",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@rslib/core": "^0.21.5",
|
|
40
|
+
"@types/node": "^25.7.0",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"vitest": "^4.1.6"
|
|
43
|
+
}
|
|
44
|
+
}
|