@pinixai/core 0.5.0 → 0.6.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 +1 -1
- package/src/clip.ts +8 -0
- package/src/index.ts +1 -1
- package/src/ipc.ts +29 -1
- package/src/manifest.ts +26 -2
package/package.json
CHANGED
package/src/clip.ts
CHANGED
|
@@ -25,6 +25,14 @@ export abstract class Clip {
|
|
|
25
25
|
dependencies: Record<string, { package: string; version: string }> = {};
|
|
26
26
|
entities: Record<string, z.ZodObject<any>> = {};
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* How long (ms) the process stays alive after the last invoke completes.
|
|
30
|
+
* - `30_000` (default): exit after 30s idle
|
|
31
|
+
* - `0`: exit immediately after each invoke
|
|
32
|
+
* - `Infinity`: never exit (persistent)
|
|
33
|
+
*/
|
|
34
|
+
idleTimeout: number = 30_000;
|
|
35
|
+
|
|
28
36
|
protected readonly commands = new Map<string, HandlerDef>();
|
|
29
37
|
protected readonly commandDescriptions = new Map<string, string>();
|
|
30
38
|
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,6 @@ export { command } from "./command";
|
|
|
4
4
|
export { handler, type HandlerDef, type Stream } from "./handler";
|
|
5
5
|
export { serveHTTP } from "./http";
|
|
6
6
|
export { serveIPC, invoke, redirectConsoleToStderr } from "./ipc";
|
|
7
|
-
export type { IPCManifest } from "./manifest";
|
|
7
|
+
export type { IPCCommandInfo, IPCManifest } from "./manifest";
|
|
8
8
|
export { serveMCP } from "./mcp";
|
|
9
9
|
export { z } from "zod";
|
package/src/ipc.ts
CHANGED
|
@@ -176,10 +176,32 @@ export async function serveIPC(clip: Clip): Promise<void> {
|
|
|
176
176
|
const manifest = createIPCManifest(clip);
|
|
177
177
|
send({ type: "register", manifest });
|
|
178
178
|
|
|
179
|
+
// Idle timeout management
|
|
180
|
+
const idleTimeout = (clip as Clip & { idleTimeout?: number }).idleTimeout ?? 30_000;
|
|
181
|
+
let inflight = 0;
|
|
182
|
+
let idleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
183
|
+
|
|
184
|
+
function clearIdleTimer(): void {
|
|
185
|
+
if (idleTimer !== null) {
|
|
186
|
+
clearTimeout(idleTimer);
|
|
187
|
+
idleTimer = null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function resetIdleTimer(): void {
|
|
192
|
+
clearIdleTimer();
|
|
193
|
+
if (inflight > 0) return;
|
|
194
|
+
if (!Number.isFinite(idleTimeout) || idleTimeout < 0) return;
|
|
195
|
+
idleTimer = setTimeout(() => process.exit(0), idleTimeout);
|
|
196
|
+
}
|
|
197
|
+
|
|
179
198
|
// Read messages from stdin
|
|
180
199
|
const reader = createLineReader(process.stdin);
|
|
181
200
|
const commands = clip.getCommands();
|
|
182
201
|
|
|
202
|
+
// Start idle timer after registration
|
|
203
|
+
resetIdleTimer();
|
|
204
|
+
|
|
183
205
|
for await (const line of reader) {
|
|
184
206
|
let msg: BaseMessage;
|
|
185
207
|
try {
|
|
@@ -200,8 +222,13 @@ export async function serveIPC(clip: Clip): Promise<void> {
|
|
|
200
222
|
break;
|
|
201
223
|
|
|
202
224
|
case "invoke": {
|
|
225
|
+
clearIdleTimer();
|
|
226
|
+
inflight++;
|
|
203
227
|
const inv = msg as InvokeMessage;
|
|
204
|
-
handleInvoke(inv, commands)
|
|
228
|
+
handleInvoke(inv, commands).finally(() => {
|
|
229
|
+
inflight--;
|
|
230
|
+
resetIdleTimer();
|
|
231
|
+
});
|
|
205
232
|
break;
|
|
206
233
|
}
|
|
207
234
|
|
|
@@ -247,6 +274,7 @@ export async function serveIPC(clip: Clip): Promise<void> {
|
|
|
247
274
|
}
|
|
248
275
|
|
|
249
276
|
// Clean up pending invokes on EOF
|
|
277
|
+
clearIdleTimer();
|
|
250
278
|
for (const [id, pending] of pendingInvokes) {
|
|
251
279
|
pending.reject(new Error("IPC connection closed"));
|
|
252
280
|
}
|
package/src/manifest.ts
CHANGED
|
@@ -3,10 +3,17 @@ import { dirname, join } from "node:path";
|
|
|
3
3
|
import { z, type ZodType } from "zod";
|
|
4
4
|
import { getClipName, type Clip } from "./clip";
|
|
5
5
|
|
|
6
|
+
export interface IPCCommandInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
input?: string;
|
|
10
|
+
output?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
export interface IPCManifest {
|
|
7
14
|
domain: string;
|
|
8
15
|
description?: string;
|
|
9
|
-
commands:
|
|
16
|
+
commands: IPCCommandInfo[];
|
|
10
17
|
dependencies: Record<string, { package: string; version: string }>;
|
|
11
18
|
package?: string;
|
|
12
19
|
version?: string;
|
|
@@ -183,9 +190,26 @@ function asString(value: unknown): string | undefined {
|
|
|
183
190
|
export function createIPCManifest(clip: Clip): IPCManifest {
|
|
184
191
|
const pkgInfo = resolvePackageInfo();
|
|
185
192
|
|
|
193
|
+
const commands: IPCCommandInfo[] = [];
|
|
194
|
+
|
|
195
|
+
for (const [name, handler] of clip.getCommands()) {
|
|
196
|
+
const description = clip.getCommandDescription(name);
|
|
197
|
+
const cmd: IPCCommandInfo = { name };
|
|
198
|
+
if (description) cmd.description = description;
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
cmd.input = JSON.stringify(z.toJSONSchema(handler.input));
|
|
202
|
+
} catch {}
|
|
203
|
+
try {
|
|
204
|
+
cmd.output = JSON.stringify(z.toJSONSchema(handler.output));
|
|
205
|
+
} catch {}
|
|
206
|
+
|
|
207
|
+
commands.push(cmd);
|
|
208
|
+
}
|
|
209
|
+
|
|
186
210
|
return {
|
|
187
211
|
domain: clip.domain,
|
|
188
|
-
commands
|
|
212
|
+
commands,
|
|
189
213
|
dependencies: clip.dependencies,
|
|
190
214
|
...pkgInfo,
|
|
191
215
|
};
|