@kb-labs/host-agent-client 0.2.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/dist/index.d.ts +39 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ILocalTransport } from '@kb-labs/host-agent-transport';
|
|
2
|
+
import { IpcStatusResponse } from '@kb-labs/host-agent-contracts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HostAgentClient — connects to the Host Agent daemon via ILocalTransport
|
|
6
|
+
* and provides typed methods for CLI/Studio.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const client = new HostAgentClient({ transport: createTransport({ mode: 'auto' }) });
|
|
10
|
+
* await client.connect();
|
|
11
|
+
* const status = await client.status();
|
|
12
|
+
* for await (const event of client.execute('workflow:run', { workflowId: 'x' })) { ... }
|
|
13
|
+
* client.close();
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface HostAgentClientOptions {
|
|
17
|
+
transport: ILocalTransport;
|
|
18
|
+
/** ms to wait for a response before rejecting (default: 10_000) */
|
|
19
|
+
requestTimeout?: number;
|
|
20
|
+
}
|
|
21
|
+
declare class HostAgentClient {
|
|
22
|
+
private readonly opts;
|
|
23
|
+
private readonly timeout;
|
|
24
|
+
private pending;
|
|
25
|
+
private connected;
|
|
26
|
+
constructor(opts: HostAgentClientOptions);
|
|
27
|
+
connect(): Promise<void>;
|
|
28
|
+
close(): void;
|
|
29
|
+
/** Query daemon status */
|
|
30
|
+
status(): Promise<Omit<IpcStatusResponse, 'type'>>;
|
|
31
|
+
/**
|
|
32
|
+
* Execute a command on the daemon and stream events back.
|
|
33
|
+
* Yields event payloads until done or error.
|
|
34
|
+
*/
|
|
35
|
+
execute(command: string, params?: unknown): AsyncGenerator<unknown>;
|
|
36
|
+
private handleMessage;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { HostAgentClient, type HostAgentClientOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
var HostAgentClient = class {
|
|
3
|
+
constructor(opts) {
|
|
4
|
+
this.opts = opts;
|
|
5
|
+
this.timeout = opts.requestTimeout ?? 1e4;
|
|
6
|
+
}
|
|
7
|
+
timeout;
|
|
8
|
+
pending = /* @__PURE__ */ new Map();
|
|
9
|
+
connected = false;
|
|
10
|
+
async connect() {
|
|
11
|
+
this.opts.transport.onMessage((msg) => this.handleMessage(msg));
|
|
12
|
+
await this.opts.transport.connect();
|
|
13
|
+
this.connected = true;
|
|
14
|
+
}
|
|
15
|
+
close() {
|
|
16
|
+
this.connected = false;
|
|
17
|
+
for (const pending of this.pending.values()) {
|
|
18
|
+
clearTimeout(pending.timer);
|
|
19
|
+
pending.reject(new Error("HostAgentClient closed"));
|
|
20
|
+
}
|
|
21
|
+
this.pending.clear();
|
|
22
|
+
this.opts.transport.close();
|
|
23
|
+
}
|
|
24
|
+
/** Query daemon status */
|
|
25
|
+
async status() {
|
|
26
|
+
const requestId = randomId();
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const timer = setTimeout(() => {
|
|
29
|
+
this.pending.delete(requestId);
|
|
30
|
+
reject(new Error("status request timed out"));
|
|
31
|
+
}, this.timeout);
|
|
32
|
+
this.pending.set(requestId, { resolve, reject, events: null, timer });
|
|
33
|
+
this.opts.transport.send({ type: "status", requestId });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Execute a command on the daemon and stream events back.
|
|
38
|
+
* Yields event payloads until done or error.
|
|
39
|
+
*/
|
|
40
|
+
async *execute(command, params = {}) {
|
|
41
|
+
const requestId = randomId();
|
|
42
|
+
const queue = [];
|
|
43
|
+
let done = false;
|
|
44
|
+
let error = null;
|
|
45
|
+
let notify = null;
|
|
46
|
+
const push = (item) => {
|
|
47
|
+
queue.push(item);
|
|
48
|
+
notify?.();
|
|
49
|
+
};
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
error = new Error(`execute '${command}' timed out`);
|
|
52
|
+
done = true;
|
|
53
|
+
notify?.();
|
|
54
|
+
}, this.timeout);
|
|
55
|
+
this.pending.set(requestId, {
|
|
56
|
+
resolve: () => {
|
|
57
|
+
done = true;
|
|
58
|
+
notify?.();
|
|
59
|
+
},
|
|
60
|
+
reject: (err) => {
|
|
61
|
+
error = err;
|
|
62
|
+
done = true;
|
|
63
|
+
notify?.();
|
|
64
|
+
},
|
|
65
|
+
events: push,
|
|
66
|
+
timer
|
|
67
|
+
});
|
|
68
|
+
this.opts.transport.send({ type: "execute", requestId, command, params });
|
|
69
|
+
while (!done || queue.length > 0) {
|
|
70
|
+
if (queue.length > 0) {
|
|
71
|
+
yield queue.shift();
|
|
72
|
+
} else {
|
|
73
|
+
await new Promise((res) => {
|
|
74
|
+
notify = res;
|
|
75
|
+
});
|
|
76
|
+
notify = null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (error) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
handleMessage(msg) {
|
|
84
|
+
if (typeof msg !== "object" || msg === null) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const m = msg;
|
|
88
|
+
if (m["type"] === "status") {
|
|
89
|
+
for (const [id, pending2] of this.pending) {
|
|
90
|
+
if (pending2.events === null) {
|
|
91
|
+
clearTimeout(pending2.timer);
|
|
92
|
+
this.pending.delete(id);
|
|
93
|
+
pending2.resolve(m);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const requestId = typeof m["requestId"] === "string" ? m["requestId"] : null;
|
|
100
|
+
if (!requestId) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const pending = this.pending.get(requestId);
|
|
104
|
+
if (!pending) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (m["type"] === "event") {
|
|
108
|
+
pending.events?.(m["data"]);
|
|
109
|
+
} else if (m["type"] === "done") {
|
|
110
|
+
clearTimeout(pending.timer);
|
|
111
|
+
this.pending.delete(requestId);
|
|
112
|
+
pending.resolve(m["result"]);
|
|
113
|
+
} else if (m["type"] === "error") {
|
|
114
|
+
clearTimeout(pending.timer);
|
|
115
|
+
this.pending.delete(requestId);
|
|
116
|
+
pending.reject(new Error(typeof m["message"] === "string" ? m["message"] : "Unknown error"));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
function randomId() {
|
|
121
|
+
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { HostAgentClient };
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"names":["pending"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HostAgentClient — connects to the Host Agent daemon via ILocalTransport\n * and provides typed methods for CLI/Studio.\n *\n * Usage:\n * const client = new HostAgentClient({ transport: createTransport({ mode: 'auto' }) });\n * await client.connect();\n * const status = await client.status();\n * for await (const event of client.execute('workflow:run', { workflowId: 'x' })) { ... }\n * client.close();\n */\n\nimport type { ILocalTransport } from '@kb-labs/host-agent-transport';\nimport type { IpcStatusResponse } from '@kb-labs/host-agent-contracts';\n\nexport interface HostAgentClientOptions {\n transport: ILocalTransport;\n /** ms to wait for a response before rejecting (default: 10_000) */\n requestTimeout?: number;\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n events: ((event: unknown) => void) | null;\n timer: ReturnType<typeof setTimeout>;\n}\n\nexport class HostAgentClient {\n private readonly timeout: number;\n private pending = new Map<string, PendingRequest>();\n private connected = false;\n\n constructor(private readonly opts: HostAgentClientOptions) {\n this.timeout = opts.requestTimeout ?? 10_000;\n }\n\n async connect(): Promise<void> {\n this.opts.transport.onMessage((msg) => this.handleMessage(msg));\n await this.opts.transport.connect();\n this.connected = true;\n }\n\n close(): void {\n this.connected = false;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(new Error('HostAgentClient closed'));\n }\n this.pending.clear();\n this.opts.transport.close();\n }\n\n /** Query daemon status */\n async status(): Promise<Omit<IpcStatusResponse, 'type'>> {\n const requestId = randomId();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(requestId);\n reject(new Error('status request timed out'));\n }, this.timeout);\n\n this.pending.set(requestId, { resolve: resolve as (v: unknown) => void, reject, events: null, timer });\n this.opts.transport.send({ type: 'status', requestId });\n }) as Promise<Omit<IpcStatusResponse, 'type'>>;\n }\n\n /**\n * Execute a command on the daemon and stream events back.\n * Yields event payloads until done or error.\n */\n async *execute(command: string, params: unknown = {}): AsyncGenerator<unknown> {\n const requestId = randomId();\n const queue: unknown[] = [];\n let done = false;\n let error: Error | null = null;\n let notify: (() => void) | null = null;\n\n const push = (item: unknown): void => {\n queue.push(item);\n notify?.();\n };\n\n const timer = setTimeout(() => {\n error = new Error(`execute '${command}' timed out`);\n done = true;\n notify?.();\n }, this.timeout);\n\n this.pending.set(requestId, {\n resolve: () => { done = true; notify?.(); },\n reject: (err) => { error = err; done = true; notify?.(); },\n events: push,\n timer,\n });\n\n this.opts.transport.send({ type: 'execute', requestId, command, params });\n\n while (!done || queue.length > 0) {\n if (queue.length > 0) {\n yield queue.shift();\n } else {\n await new Promise<void>((res) => { notify = res; });\n notify = null;\n }\n }\n\n if (error) { throw error; }\n }\n\n private handleMessage(msg: unknown): void {\n if (typeof msg !== 'object' || msg === null) { return; }\n const m = msg as Record<string, unknown>;\n\n // Status response (requestId optional on status)\n if (m['type'] === 'status') {\n // Find any pending status request\n for (const [id, pending] of this.pending) {\n if (pending.events === null) {\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.resolve(m);\n return;\n }\n }\n return;\n }\n\n const requestId = typeof m['requestId'] === 'string' ? m['requestId'] : null;\n if (!requestId) { return; }\n const pending = this.pending.get(requestId);\n if (!pending) { return; }\n\n if (m['type'] === 'event') {\n pending.events?.(m['data']);\n } else if (m['type'] === 'done') {\n clearTimeout(pending.timer);\n this.pending.delete(requestId);\n pending.resolve(m['result']);\n } else if (m['type'] === 'error') {\n clearTimeout(pending.timer);\n this.pending.delete(requestId);\n pending.reject(new Error(typeof m['message'] === 'string' ? m['message'] : 'Unknown error'));\n }\n }\n}\n\nfunction randomId(): string {\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kb-labs/host-agent-client",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clean": "rimraf dist",
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"type-check": "tsc --noEmit",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"lint:fix": "eslint . --fix",
|
|
25
|
+
"test": "vitest run -c ../../vitest.config.ts",
|
|
26
|
+
"test:watch": "vitest -c ../../vitest.config.ts"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@kb-labs/host-agent-contracts": "^0.2.0",
|
|
30
|
+
"@kb-labs/host-agent-transport": "^0.2.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@kb-labs/devkit": "link:../../../kb-labs-devkit",
|
|
34
|
+
"@types/node": "^20",
|
|
35
|
+
"rimraf": "^6",
|
|
36
|
+
"tsup": "^8.5.0",
|
|
37
|
+
"vitest": "^3.2.4"
|
|
38
|
+
}
|
|
39
|
+
}
|