@agentuity/pi 1.0.36 → 2.0.17
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 +3 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +337 -177
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
- package/src/index.ts +429 -207
- package/dist/client.d.ts +0 -11
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -116
- package/dist/client.js.map +0 -1
- package/dist/handlers.d.ts +0 -19
- package/dist/handlers.d.ts.map +0 -1
- package/dist/handlers.js +0 -57
- package/dist/handlers.js.map +0 -1
- package/dist/protocol.d.ts +0 -68
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -3
- package/dist/protocol.js.map +0 -1
- package/src/client.ts +0 -144
- package/src/handlers.ts +0 -82
- package/src/protocol.ts +0 -95
package/dist/client.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/** How long to wait for a response before rejecting the pending promise (ms). */
|
|
2
|
-
const SEND_TIMEOUT_MS = 30_000;
|
|
3
|
-
/** How long to wait for the init message after connecting (ms). */
|
|
4
|
-
const CONNECT_TIMEOUT_MS = 30_000;
|
|
5
|
-
export class HubClient {
|
|
6
|
-
ws = null;
|
|
7
|
-
pending = new Map();
|
|
8
|
-
async connect(url) {
|
|
9
|
-
// Guard against overlapping connections — old socket handlers would corrupt shared state
|
|
10
|
-
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
|
|
11
|
-
throw new Error('Already connected or connecting — call close() first');
|
|
12
|
-
}
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
// Ensure ws:// or wss:// protocol — upgrade http(s) URLs automatically
|
|
15
|
-
let wsUrl = url;
|
|
16
|
-
if (wsUrl.startsWith('http://')) {
|
|
17
|
-
wsUrl = 'ws://' + wsUrl.slice(7);
|
|
18
|
-
}
|
|
19
|
-
else if (wsUrl.startsWith('https://')) {
|
|
20
|
-
wsUrl = 'wss://' + wsUrl.slice(8);
|
|
21
|
-
}
|
|
22
|
-
else if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
|
|
23
|
-
wsUrl = 'ws://' + wsUrl;
|
|
24
|
-
}
|
|
25
|
-
this.ws = new WebSocket(wsUrl);
|
|
26
|
-
// Guard against duplicate init messages re-invoking the resolve
|
|
27
|
-
let initResolved = false;
|
|
28
|
-
const connectTimer = setTimeout(() => {
|
|
29
|
-
if (!initResolved) {
|
|
30
|
-
reject(new Error(`Hub did not send init message within ${CONNECT_TIMEOUT_MS}ms`));
|
|
31
|
-
this.ws?.close();
|
|
32
|
-
}
|
|
33
|
-
}, CONNECT_TIMEOUT_MS);
|
|
34
|
-
this.ws.onmessage = (event) => {
|
|
35
|
-
let data;
|
|
36
|
-
try {
|
|
37
|
-
const raw = typeof event.data === 'string'
|
|
38
|
-
? event.data
|
|
39
|
-
: new TextDecoder().decode(event.data);
|
|
40
|
-
data = JSON.parse(raw);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// Malformed or non-JSON frame — ignore
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
// First message should be init
|
|
47
|
-
if (data.type === 'init' && !initResolved) {
|
|
48
|
-
initResolved = true;
|
|
49
|
-
clearTimeout(connectTimer);
|
|
50
|
-
resolve(data);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
// Otherwise it's a response to a pending request
|
|
54
|
-
const response = data;
|
|
55
|
-
const entry = this.pending.get(response.id);
|
|
56
|
-
if (entry) {
|
|
57
|
-
clearTimeout(entry.timer);
|
|
58
|
-
this.pending.delete(response.id);
|
|
59
|
-
entry.resolve(response);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
this.ws.onerror = (err) => {
|
|
63
|
-
// ErrorEvent has a message property; plain Event does not
|
|
64
|
-
const message = 'message' in err && typeof err.message === 'string'
|
|
65
|
-
? err.message
|
|
66
|
-
: `connection to ${wsUrl} failed`;
|
|
67
|
-
clearTimeout(connectTimer);
|
|
68
|
-
reject(new Error(`WebSocket error: ${message}`));
|
|
69
|
-
};
|
|
70
|
-
this.ws.onclose = () => {
|
|
71
|
-
clearTimeout(connectTimer);
|
|
72
|
-
// Reject connect() promise if init was never received
|
|
73
|
-
if (!initResolved) {
|
|
74
|
-
reject(new Error('WebSocket closed before init message received'));
|
|
75
|
-
}
|
|
76
|
-
// Reject all pending requests
|
|
77
|
-
for (const [id, entry] of this.pending) {
|
|
78
|
-
clearTimeout(entry.timer);
|
|
79
|
-
entry.reject(new Error('WebSocket closed'));
|
|
80
|
-
this.pending.delete(id);
|
|
81
|
-
}
|
|
82
|
-
this.ws = null;
|
|
83
|
-
};
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
nextId() {
|
|
87
|
-
return crypto.randomUUID();
|
|
88
|
-
}
|
|
89
|
-
async send(request) {
|
|
90
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
91
|
-
// Return a default ACK if not connected
|
|
92
|
-
return { id: request.id, actions: [{ action: 'ACK' }] };
|
|
93
|
-
}
|
|
94
|
-
return new Promise((resolve, reject) => {
|
|
95
|
-
const timer = setTimeout(() => {
|
|
96
|
-
const entry = this.pending.get(request.id);
|
|
97
|
-
if (entry) {
|
|
98
|
-
this.pending.delete(request.id);
|
|
99
|
-
entry.reject(new Error(`Hub response timeout after ${SEND_TIMEOUT_MS}ms for request ${request.id}`));
|
|
100
|
-
}
|
|
101
|
-
}, SEND_TIMEOUT_MS);
|
|
102
|
-
this.pending.set(request.id, { resolve, reject, timer });
|
|
103
|
-
this.ws.send(JSON.stringify(request));
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
close() {
|
|
107
|
-
if (this.ws) {
|
|
108
|
-
this.ws.close();
|
|
109
|
-
this.ws = null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
get connected() {
|
|
113
|
-
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { HubAction } from './protocol.ts';
|
|
2
|
-
export interface ActionResult {
|
|
3
|
-
block?: {
|
|
4
|
-
block: true;
|
|
5
|
-
reason: string;
|
|
6
|
-
};
|
|
7
|
-
returnValue?: unknown;
|
|
8
|
-
}
|
|
9
|
-
/** Minimal UI surface used by action handlers — avoids a hard dep on pi-coding-agent. */
|
|
10
|
-
interface ActionContext {
|
|
11
|
-
ui?: {
|
|
12
|
-
notify(message: string, level?: 'info' | 'warning' | 'error'): void;
|
|
13
|
-
confirm(title: string, message: string): Promise<boolean>;
|
|
14
|
-
setStatus(key: string, text?: string): void;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export declare function processActions(actions: HubAction[], ctx: ActionContext): Promise<ActionResult>;
|
|
18
|
-
export {};
|
|
19
|
-
//# sourceMappingURL=handlers.d.ts.map
|
package/dist/handlers.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC5B,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;CAEtB;AAED,yFAAyF;AACzF,UAAU,aAAa;IACtB,EAAE,CAAC,EAAE;QACJ,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;QACpE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1D,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5C,CAAC;CACF;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,SAAS,EAAE,EACpB,GAAG,EAAE,aAAa,GAChB,OAAO,CAAC,YAAY,CAAC,CA6DvB"}
|
package/dist/handlers.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export async function processActions(actions, ctx) {
|
|
2
|
-
let result = {};
|
|
3
|
-
for (const action of actions) {
|
|
4
|
-
switch (action.action) {
|
|
5
|
-
case 'ACK':
|
|
6
|
-
// Terminal: proceed normally
|
|
7
|
-
result = {};
|
|
8
|
-
break;
|
|
9
|
-
case 'BLOCK':
|
|
10
|
-
// Terminal: block
|
|
11
|
-
result = { block: { block: true, reason: action.reason } };
|
|
12
|
-
break;
|
|
13
|
-
case 'RETURN':
|
|
14
|
-
// Terminal: return a specific result
|
|
15
|
-
result = { returnValue: action.result };
|
|
16
|
-
break;
|
|
17
|
-
case 'NOTIFY':
|
|
18
|
-
// Side effect: show notification, continue
|
|
19
|
-
if (ctx?.ui) {
|
|
20
|
-
ctx.ui.notify(action.message, action.level ?? 'info');
|
|
21
|
-
}
|
|
22
|
-
break;
|
|
23
|
-
case 'STATUS':
|
|
24
|
-
// Side effect: set status, continue
|
|
25
|
-
if (ctx?.ui) {
|
|
26
|
-
ctx.ui.setStatus(action.key, action.text);
|
|
27
|
-
}
|
|
28
|
-
break;
|
|
29
|
-
case 'CONFIRM': {
|
|
30
|
-
// Gate: if user denies, stop and block
|
|
31
|
-
if (ctx?.ui) {
|
|
32
|
-
const confirmed = await ctx.ui.confirm(action.title, action.message);
|
|
33
|
-
if (!confirmed) {
|
|
34
|
-
return {
|
|
35
|
-
block: {
|
|
36
|
-
block: true,
|
|
37
|
-
reason: action.deny_reason ?? 'Denied by user',
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// No UI available — block by default for safety
|
|
44
|
-
result = {
|
|
45
|
-
block: {
|
|
46
|
-
block: true,
|
|
47
|
-
reason: action.deny_reason ?? 'Confirmation required but no UI available',
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=handlers.js.map
|
package/dist/handlers.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAAoB,EACpB,GAAkB;IAElB,IAAI,MAAM,GAAiB,EAAE,CAAC;IAE9B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,KAAK;gBACT,6BAA6B;gBAC7B,MAAM,GAAG,EAAE,CAAC;gBACZ,MAAM;YAEP,KAAK,OAAO;gBACX,kBAAkB;gBAClB,MAAM,GAAG,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC3D,MAAM;YAEP,KAAK,QAAQ;gBACZ,qCAAqC;gBACrC,MAAM,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;gBACxC,MAAM;YAEP,KAAK,QAAQ;gBACZ,2CAA2C;gBAC3C,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC;oBACb,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;gBACvD,CAAC;gBACD,MAAM;YAEP,KAAK,QAAQ;gBACZ,oCAAoC;gBACpC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC;oBACb,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC3C,CAAC;gBACD,MAAM;YAEP,KAAK,SAAS,CAAC,CAAC,CAAC;gBAChB,uCAAuC;gBACvC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC;oBACb,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;oBACrE,IAAI,CAAC,SAAS,EAAE,CAAC;wBAChB,OAAO;4BACN,KAAK,EAAE;gCACN,KAAK,EAAE,IAAI;gCACX,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,gBAAgB;6BAC9C;yBACD,CAAC;oBACH,CAAC;gBACF,CAAC;qBAAM,CAAC;oBACP,gDAAgD;oBAChD,MAAM,GAAG;wBACR,KAAK,EAAE;4BACN,KAAK,EAAE,IAAI;4BACX,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,2CAA2C;yBACzE;qBACD,CAAC;gBACH,CAAC;gBACD,MAAM;YACP,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC"}
|
package/dist/protocol.d.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
export interface HubToolDefinition {
|
|
2
|
-
name: string;
|
|
3
|
-
label: string;
|
|
4
|
-
description: string;
|
|
5
|
-
parameters: Record<string, unknown>;
|
|
6
|
-
}
|
|
7
|
-
export interface HubCommandDefinition {
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
}
|
|
11
|
-
export interface InitMessage {
|
|
12
|
-
type: 'init';
|
|
13
|
-
tools?: HubToolDefinition[];
|
|
14
|
-
commands?: HubCommandDefinition[];
|
|
15
|
-
}
|
|
16
|
-
export interface EventRequest {
|
|
17
|
-
id: string;
|
|
18
|
-
type: 'event';
|
|
19
|
-
event: string;
|
|
20
|
-
data: Record<string, unknown>;
|
|
21
|
-
}
|
|
22
|
-
export interface ToolRequest {
|
|
23
|
-
id: string;
|
|
24
|
-
type: 'tool';
|
|
25
|
-
name: string;
|
|
26
|
-
toolCallId: string;
|
|
27
|
-
params: Record<string, unknown>;
|
|
28
|
-
}
|
|
29
|
-
export interface CommandRequest {
|
|
30
|
-
id: string;
|
|
31
|
-
type: 'command';
|
|
32
|
-
name: string;
|
|
33
|
-
args: string;
|
|
34
|
-
}
|
|
35
|
-
export type HubRequest = EventRequest | ToolRequest | CommandRequest;
|
|
36
|
-
export interface AckAction {
|
|
37
|
-
action: 'ACK';
|
|
38
|
-
}
|
|
39
|
-
export interface BlockAction {
|
|
40
|
-
action: 'BLOCK';
|
|
41
|
-
reason: string;
|
|
42
|
-
}
|
|
43
|
-
export interface ConfirmAction {
|
|
44
|
-
action: 'CONFIRM';
|
|
45
|
-
title: string;
|
|
46
|
-
message: string;
|
|
47
|
-
deny_reason?: string;
|
|
48
|
-
}
|
|
49
|
-
export interface NotifyAction {
|
|
50
|
-
action: 'NOTIFY';
|
|
51
|
-
message: string;
|
|
52
|
-
level?: 'info' | 'warning' | 'error';
|
|
53
|
-
}
|
|
54
|
-
export interface ReturnAction {
|
|
55
|
-
action: 'RETURN';
|
|
56
|
-
result: unknown;
|
|
57
|
-
}
|
|
58
|
-
export interface StatusAction {
|
|
59
|
-
action: 'STATUS';
|
|
60
|
-
key: string;
|
|
61
|
-
text?: string;
|
|
62
|
-
}
|
|
63
|
-
export type HubAction = AckAction | BlockAction | ConfirmAction | NotifyAction | ReturnAction | StatusAction;
|
|
64
|
-
export interface HubResponse {
|
|
65
|
-
id: string;
|
|
66
|
-
actions: HubAction[];
|
|
67
|
-
}
|
|
68
|
-
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAID,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;AAIrE,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,QAAQ,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,QAAQ,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAClB,SAAS,GACT,WAAW,GACX,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,YAAY,CAAC;AAIhB,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,SAAS,EAAE,CAAC;CACrB"}
|
package/dist/protocol.js
DELETED
package/dist/protocol.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,sDAAsD"}
|
package/src/client.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import type { InitMessage, HubRequest, HubResponse } from './protocol.ts';
|
|
2
|
-
|
|
3
|
-
/** How long to wait for a response before rejecting the pending promise (ms). */
|
|
4
|
-
const SEND_TIMEOUT_MS = 30_000;
|
|
5
|
-
|
|
6
|
-
/** How long to wait for the init message after connecting (ms). */
|
|
7
|
-
const CONNECT_TIMEOUT_MS = 30_000;
|
|
8
|
-
|
|
9
|
-
export class HubClient {
|
|
10
|
-
private ws: WebSocket | null = null;
|
|
11
|
-
private pending = new Map<
|
|
12
|
-
string,
|
|
13
|
-
{
|
|
14
|
-
resolve: (resp: HubResponse) => void;
|
|
15
|
-
reject: (err: Error) => void;
|
|
16
|
-
timer: ReturnType<typeof setTimeout>;
|
|
17
|
-
}
|
|
18
|
-
>();
|
|
19
|
-
async connect(url: string): Promise<InitMessage> {
|
|
20
|
-
// Guard against overlapping connections — old socket handlers would corrupt shared state
|
|
21
|
-
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
|
|
22
|
-
throw new Error('Already connected or connecting — call close() first');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
// Ensure ws:// or wss:// protocol — upgrade http(s) URLs automatically
|
|
27
|
-
let wsUrl = url;
|
|
28
|
-
if (wsUrl.startsWith('http://')) {
|
|
29
|
-
wsUrl = 'ws://' + wsUrl.slice(7);
|
|
30
|
-
} else if (wsUrl.startsWith('https://')) {
|
|
31
|
-
wsUrl = 'wss://' + wsUrl.slice(8);
|
|
32
|
-
} else if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
|
|
33
|
-
wsUrl = 'ws://' + wsUrl;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
this.ws = new WebSocket(wsUrl);
|
|
37
|
-
|
|
38
|
-
// Guard against duplicate init messages re-invoking the resolve
|
|
39
|
-
let initResolved = false;
|
|
40
|
-
|
|
41
|
-
const connectTimer = setTimeout(() => {
|
|
42
|
-
if (!initResolved) {
|
|
43
|
-
reject(new Error(`Hub did not send init message within ${CONNECT_TIMEOUT_MS}ms`));
|
|
44
|
-
this.ws?.close();
|
|
45
|
-
}
|
|
46
|
-
}, CONNECT_TIMEOUT_MS);
|
|
47
|
-
|
|
48
|
-
this.ws.onmessage = (event: MessageEvent) => {
|
|
49
|
-
let data: Record<string, unknown>;
|
|
50
|
-
try {
|
|
51
|
-
const raw =
|
|
52
|
-
typeof event.data === 'string'
|
|
53
|
-
? event.data
|
|
54
|
-
: new TextDecoder().decode(event.data as ArrayBuffer);
|
|
55
|
-
data = JSON.parse(raw) as Record<string, unknown>;
|
|
56
|
-
} catch {
|
|
57
|
-
// Malformed or non-JSON frame — ignore
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// First message should be init
|
|
62
|
-
if (data.type === 'init' && !initResolved) {
|
|
63
|
-
initResolved = true;
|
|
64
|
-
clearTimeout(connectTimer);
|
|
65
|
-
resolve(data as unknown as InitMessage);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Otherwise it's a response to a pending request
|
|
70
|
-
const response = data as unknown as HubResponse;
|
|
71
|
-
const entry = this.pending.get(response.id);
|
|
72
|
-
if (entry) {
|
|
73
|
-
clearTimeout(entry.timer);
|
|
74
|
-
this.pending.delete(response.id);
|
|
75
|
-
entry.resolve(response);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
this.ws.onerror = (err: Event) => {
|
|
80
|
-
// ErrorEvent has a message property; plain Event does not
|
|
81
|
-
const message =
|
|
82
|
-
'message' in err && typeof (err as ErrorEvent).message === 'string'
|
|
83
|
-
? (err as ErrorEvent).message
|
|
84
|
-
: `connection to ${wsUrl} failed`;
|
|
85
|
-
clearTimeout(connectTimer);
|
|
86
|
-
reject(new Error(`WebSocket error: ${message}`));
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
this.ws.onclose = () => {
|
|
90
|
-
clearTimeout(connectTimer);
|
|
91
|
-
// Reject connect() promise if init was never received
|
|
92
|
-
if (!initResolved) {
|
|
93
|
-
reject(new Error('WebSocket closed before init message received'));
|
|
94
|
-
}
|
|
95
|
-
// Reject all pending requests
|
|
96
|
-
for (const [id, entry] of this.pending) {
|
|
97
|
-
clearTimeout(entry.timer);
|
|
98
|
-
entry.reject(new Error('WebSocket closed'));
|
|
99
|
-
this.pending.delete(id);
|
|
100
|
-
}
|
|
101
|
-
this.ws = null;
|
|
102
|
-
};
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
nextId(): string {
|
|
107
|
-
return crypto.randomUUID();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async send(request: HubRequest): Promise<HubResponse> {
|
|
111
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
112
|
-
// Return a default ACK if not connected
|
|
113
|
-
return { id: request.id, actions: [{ action: 'ACK' }] };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return new Promise<HubResponse>((resolve, reject) => {
|
|
117
|
-
const timer = setTimeout(() => {
|
|
118
|
-
const entry = this.pending.get(request.id);
|
|
119
|
-
if (entry) {
|
|
120
|
-
this.pending.delete(request.id);
|
|
121
|
-
entry.reject(
|
|
122
|
-
new Error(
|
|
123
|
-
`Hub response timeout after ${SEND_TIMEOUT_MS}ms for request ${request.id}`
|
|
124
|
-
)
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
}, SEND_TIMEOUT_MS);
|
|
128
|
-
|
|
129
|
-
this.pending.set(request.id, { resolve, reject, timer });
|
|
130
|
-
this.ws!.send(JSON.stringify(request));
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
close(): void {
|
|
135
|
-
if (this.ws) {
|
|
136
|
-
this.ws.close();
|
|
137
|
-
this.ws = null;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
get connected(): boolean {
|
|
142
|
-
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
143
|
-
}
|
|
144
|
-
}
|
package/src/handlers.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import type { HubAction } from './protocol.ts';
|
|
2
|
-
|
|
3
|
-
export interface ActionResult {
|
|
4
|
-
block?: { block: true; reason: string };
|
|
5
|
-
returnValue?: unknown;
|
|
6
|
-
// undefined means ACK (proceed normally)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Minimal UI surface used by action handlers — avoids a hard dep on pi-coding-agent. */
|
|
10
|
-
interface ActionContext {
|
|
11
|
-
ui?: {
|
|
12
|
-
notify(message: string, level?: 'info' | 'warning' | 'error'): void;
|
|
13
|
-
confirm(title: string, message: string): Promise<boolean>;
|
|
14
|
-
setStatus(key: string, text?: string): void;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function processActions(
|
|
19
|
-
actions: HubAction[],
|
|
20
|
-
ctx: ActionContext
|
|
21
|
-
): Promise<ActionResult> {
|
|
22
|
-
let result: ActionResult = {};
|
|
23
|
-
|
|
24
|
-
for (const action of actions) {
|
|
25
|
-
switch (action.action) {
|
|
26
|
-
case 'ACK':
|
|
27
|
-
// Terminal: proceed normally
|
|
28
|
-
result = {};
|
|
29
|
-
break;
|
|
30
|
-
|
|
31
|
-
case 'BLOCK':
|
|
32
|
-
// Terminal: block
|
|
33
|
-
result = { block: { block: true, reason: action.reason } };
|
|
34
|
-
break;
|
|
35
|
-
|
|
36
|
-
case 'RETURN':
|
|
37
|
-
// Terminal: return a specific result
|
|
38
|
-
result = { returnValue: action.result };
|
|
39
|
-
break;
|
|
40
|
-
|
|
41
|
-
case 'NOTIFY':
|
|
42
|
-
// Side effect: show notification, continue
|
|
43
|
-
if (ctx?.ui) {
|
|
44
|
-
ctx.ui.notify(action.message, action.level ?? 'info');
|
|
45
|
-
}
|
|
46
|
-
break;
|
|
47
|
-
|
|
48
|
-
case 'STATUS':
|
|
49
|
-
// Side effect: set status, continue
|
|
50
|
-
if (ctx?.ui) {
|
|
51
|
-
ctx.ui.setStatus(action.key, action.text);
|
|
52
|
-
}
|
|
53
|
-
break;
|
|
54
|
-
|
|
55
|
-
case 'CONFIRM': {
|
|
56
|
-
// Gate: if user denies, stop and block
|
|
57
|
-
if (ctx?.ui) {
|
|
58
|
-
const confirmed = await ctx.ui.confirm(action.title, action.message);
|
|
59
|
-
if (!confirmed) {
|
|
60
|
-
return {
|
|
61
|
-
block: {
|
|
62
|
-
block: true,
|
|
63
|
-
reason: action.deny_reason ?? 'Denied by user',
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
// No UI available — block by default for safety
|
|
69
|
-
result = {
|
|
70
|
-
block: {
|
|
71
|
-
block: true,
|
|
72
|
-
reason: action.deny_reason ?? 'Confirmation required but no UI available',
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return result;
|
|
82
|
-
}
|
package/src/protocol.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// ---- Init Message (Server → Client on connect) ----
|
|
2
|
-
|
|
3
|
-
export interface HubToolDefinition {
|
|
4
|
-
name: string;
|
|
5
|
-
label: string;
|
|
6
|
-
description: string;
|
|
7
|
-
parameters: Record<string, unknown>; // JSON Schema object
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface HubCommandDefinition {
|
|
11
|
-
name: string;
|
|
12
|
-
description: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface InitMessage {
|
|
16
|
-
type: 'init';
|
|
17
|
-
tools?: HubToolDefinition[];
|
|
18
|
-
commands?: HubCommandDefinition[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ---- Request Messages (Client → Server) ----
|
|
22
|
-
|
|
23
|
-
export interface EventRequest {
|
|
24
|
-
id: string;
|
|
25
|
-
type: 'event';
|
|
26
|
-
event: string;
|
|
27
|
-
data: Record<string, unknown>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ToolRequest {
|
|
31
|
-
id: string;
|
|
32
|
-
type: 'tool';
|
|
33
|
-
name: string;
|
|
34
|
-
toolCallId: string;
|
|
35
|
-
params: Record<string, unknown>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface CommandRequest {
|
|
39
|
-
id: string;
|
|
40
|
-
type: 'command';
|
|
41
|
-
name: string;
|
|
42
|
-
args: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export type HubRequest = EventRequest | ToolRequest | CommandRequest;
|
|
46
|
-
|
|
47
|
-
// ---- Actions ----
|
|
48
|
-
|
|
49
|
-
export interface AckAction {
|
|
50
|
-
action: 'ACK';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface BlockAction {
|
|
54
|
-
action: 'BLOCK';
|
|
55
|
-
reason: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface ConfirmAction {
|
|
59
|
-
action: 'CONFIRM';
|
|
60
|
-
title: string;
|
|
61
|
-
message: string;
|
|
62
|
-
deny_reason?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface NotifyAction {
|
|
66
|
-
action: 'NOTIFY';
|
|
67
|
-
message: string;
|
|
68
|
-
level?: 'info' | 'warning' | 'error';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface ReturnAction {
|
|
72
|
-
action: 'RETURN';
|
|
73
|
-
result: unknown;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export interface StatusAction {
|
|
77
|
-
action: 'STATUS';
|
|
78
|
-
key: string;
|
|
79
|
-
text?: string; // undefined = clear status
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export type HubAction =
|
|
83
|
-
| AckAction
|
|
84
|
-
| BlockAction
|
|
85
|
-
| ConfirmAction
|
|
86
|
-
| NotifyAction
|
|
87
|
-
| ReturnAction
|
|
88
|
-
| StatusAction;
|
|
89
|
-
|
|
90
|
-
// ---- Response Message (Server → Client) ----
|
|
91
|
-
|
|
92
|
-
export interface HubResponse {
|
|
93
|
-
id: string;
|
|
94
|
-
actions: HubAction[];
|
|
95
|
-
}
|