@pinixai/core 0.6.1 → 0.7.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/package.json +4 -1
- package/src/gen/hub_pb.ts +1450 -0
- package/src/hub.ts +70 -0
- package/src/index.ts +1 -0
- package/src/ipc.ts +7 -86
package/src/hub.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createClient } from "@connectrpc/connect";
|
|
2
|
+
import { createConnectTransport } from "@connectrpc/connect-web";
|
|
3
|
+
import { HubService, type ClipInfo } from "./gen/hub_pb";
|
|
4
|
+
|
|
5
|
+
const PINIX_URL = process.env.PINIX_URL ?? "http://127.0.0.1:9000";
|
|
6
|
+
|
|
7
|
+
function getTransport(hubUrl?: string) {
|
|
8
|
+
return createConnectTransport({
|
|
9
|
+
baseUrl: hubUrl ?? PINIX_URL,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getClient(hubUrl?: string) {
|
|
14
|
+
return createClient(HubService, getTransport(hubUrl));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const encoder = new TextEncoder();
|
|
18
|
+
const decoder = new TextDecoder();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Invoke a command on a remote clip via Hub's Connect-RPC Invoke RPC.
|
|
22
|
+
* The Invoke RPC is server-streaming: we collect all output chunks and return the aggregated result.
|
|
23
|
+
*/
|
|
24
|
+
export async function hubInvoke(
|
|
25
|
+
clipName: string,
|
|
26
|
+
command: string,
|
|
27
|
+
input: unknown,
|
|
28
|
+
clipToken?: string,
|
|
29
|
+
hubUrl?: string,
|
|
30
|
+
): Promise<unknown> {
|
|
31
|
+
const client = getClient(hubUrl);
|
|
32
|
+
const inputBytes = encoder.encode(JSON.stringify(input));
|
|
33
|
+
|
|
34
|
+
let outputBytes = new Uint8Array(0);
|
|
35
|
+
|
|
36
|
+
for await (const response of client.invoke({
|
|
37
|
+
clipName,
|
|
38
|
+
command,
|
|
39
|
+
input: inputBytes,
|
|
40
|
+
clipToken: clipToken ?? "",
|
|
41
|
+
})) {
|
|
42
|
+
if (response.error) {
|
|
43
|
+
throw new Error(response.error.message || response.error.code || "Hub invoke error");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (response.output.length > 0) {
|
|
47
|
+
// Aggregate output chunks
|
|
48
|
+
const merged = new Uint8Array(outputBytes.length + response.output.length);
|
|
49
|
+
merged.set(outputBytes);
|
|
50
|
+
merged.set(response.output, outputBytes.length);
|
|
51
|
+
outputBytes = merged;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (outputBytes.length === 0) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const outputJson = decoder.decode(outputBytes);
|
|
60
|
+
return JSON.parse(outputJson) as unknown;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* List all clips registered on the Hub.
|
|
65
|
+
*/
|
|
66
|
+
export async function hubListClips(hubUrl?: string): Promise<ClipInfo[]> {
|
|
67
|
+
const client = getClient(hubUrl);
|
|
68
|
+
const response = await client.listClips({});
|
|
69
|
+
return response.clips;
|
|
70
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type { Binding, Bindings } from "./bindings";
|
|
|
3
3
|
export { command } from "./command";
|
|
4
4
|
export { handler, type HandlerDef, type Stream } from "./handler";
|
|
5
5
|
export { serveHTTP } from "./http";
|
|
6
|
+
export { hubListClips } from "./hub";
|
|
6
7
|
export { serveIPC, invoke, redirectConsoleToStderr } from "./ipc";
|
|
7
8
|
export type { IPCCommandInfo, IPCManifest } from "./manifest";
|
|
8
9
|
export { serveMCP } from "./mcp";
|
package/src/ipc.ts
CHANGED
|
@@ -4,11 +4,12 @@ import { StringDecoder } from "node:string_decoder";
|
|
|
4
4
|
import type { Binding, Bindings } from "./bindings";
|
|
5
5
|
import type { Clip } from "./clip";
|
|
6
6
|
import type { Stream } from "./handler";
|
|
7
|
+
import { hubInvoke } from "./hub";
|
|
7
8
|
import { createIPCManifest, type IPCManifest } from "./manifest";
|
|
8
9
|
|
|
9
10
|
// === IPC Protocol Types ===
|
|
10
11
|
|
|
11
|
-
type MessageType = "register" | "registered" | "invoke"
|
|
12
|
+
type MessageType = "register" | "registered" | "invoke";
|
|
12
13
|
|
|
13
14
|
interface BaseMessage {
|
|
14
15
|
type: MessageType;
|
|
@@ -36,33 +37,8 @@ interface InvokeMessage extends BaseMessage {
|
|
|
36
37
|
clip_token?: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
interface ResultMessage extends BaseMessage {
|
|
40
|
-
type: "result";
|
|
41
|
-
id: string;
|
|
42
|
-
output: unknown;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface ErrorMessage extends BaseMessage {
|
|
46
|
-
type: "error";
|
|
47
|
-
id: string;
|
|
48
|
-
error: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface ChunkMessage extends BaseMessage {
|
|
52
|
-
type: "chunk";
|
|
53
|
-
id: string;
|
|
54
|
-
output: unknown;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface DoneMessage extends BaseMessage {
|
|
58
|
-
type: "done";
|
|
59
|
-
id: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
40
|
// === State ===
|
|
63
41
|
|
|
64
|
-
const pendingInvokes = new Map<string, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
|
|
65
|
-
let idCounter = 0;
|
|
66
42
|
let registeredAlias: string | undefined;
|
|
67
43
|
const bindings = loadBindings();
|
|
68
44
|
|
|
@@ -126,10 +102,6 @@ function asNonEmptyString(value: unknown): string | undefined {
|
|
|
126
102
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
127
103
|
}
|
|
128
104
|
|
|
129
|
-
function nextId(): string {
|
|
130
|
-
return `c${++idCounter}`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
105
|
function send(msg: Record<string, unknown>): void {
|
|
134
106
|
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
135
107
|
}
|
|
@@ -138,21 +110,11 @@ function send(msg: Record<string, unknown>): void {
|
|
|
138
110
|
|
|
139
111
|
export async function invoke(slot: string, command: string, input: unknown): Promise<unknown> {
|
|
140
112
|
const binding = bindings[slot];
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
id,
|
|
147
|
-
type: "invoke",
|
|
148
|
-
clip: binding?.alias ?? slot,
|
|
149
|
-
command,
|
|
150
|
-
input,
|
|
151
|
-
hub: binding?.hub,
|
|
152
|
-
hub_token: binding?.hub_token,
|
|
153
|
-
clip_token: binding?.clip_token,
|
|
154
|
-
});
|
|
155
|
-
});
|
|
113
|
+
const clipName = binding?.alias ?? slot;
|
|
114
|
+
const clipToken = binding?.clip_token;
|
|
115
|
+
const hubUrl = binding?.hub;
|
|
116
|
+
|
|
117
|
+
return hubInvoke(clipName, command, input, clipToken, hubUrl);
|
|
156
118
|
}
|
|
157
119
|
|
|
158
120
|
// === Stdout Protection ===
|
|
@@ -232,53 +194,12 @@ export async function serveIPC(clip: Clip): Promise<void> {
|
|
|
232
194
|
break;
|
|
233
195
|
}
|
|
234
196
|
|
|
235
|
-
case "result": {
|
|
236
|
-
const res = msg as ResultMessage;
|
|
237
|
-
const pending = pendingInvokes.get(res.id);
|
|
238
|
-
if (pending) {
|
|
239
|
-
pendingInvokes.delete(res.id);
|
|
240
|
-
pending.resolve(res.output);
|
|
241
|
-
}
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
case "error": {
|
|
246
|
-
const err = msg as ErrorMessage;
|
|
247
|
-
const pending = pendingInvokes.get(err.id);
|
|
248
|
-
if (pending) {
|
|
249
|
-
pendingInvokes.delete(err.id);
|
|
250
|
-
pending.reject(new Error(err.error));
|
|
251
|
-
}
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
case "chunk": {
|
|
256
|
-
// Streaming chunks — currently ignored on client side
|
|
257
|
-
// Future: could accumulate or forward to a stream callback
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
case "done": {
|
|
262
|
-
const done = msg as DoneMessage;
|
|
263
|
-
const pending = pendingInvokes.get(done.id);
|
|
264
|
-
if (pending) {
|
|
265
|
-
pendingInvokes.delete(done.id);
|
|
266
|
-
pending.resolve(undefined);
|
|
267
|
-
}
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
197
|
default:
|
|
272
198
|
process.stderr.write(`[ipc] unknown message type: ${msg.type}\n`);
|
|
273
199
|
}
|
|
274
200
|
}
|
|
275
201
|
|
|
276
|
-
// Clean up pending invokes on EOF
|
|
277
202
|
clearIdleTimer();
|
|
278
|
-
for (const [id, pending] of pendingInvokes) {
|
|
279
|
-
pending.reject(new Error("IPC connection closed"));
|
|
280
|
-
}
|
|
281
|
-
pendingInvokes.clear();
|
|
282
203
|
}
|
|
283
204
|
|
|
284
205
|
async function handleInvoke(
|