@nodeterm/sdk 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/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @nodeterm/sdk
2
+
3
+ Generic Node.js SDK for communication between a local machine and external tools.
4
+
5
+ The SDK only provides contracts, runtime orchestration, and session events. It does not execute commands, restrict commands, pick directories, manage shells, or know about Telegram. Product logic belongs in the application using the SDK.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @nodeterm/sdk
11
+ ```
12
+
13
+ ## Simple Request/Response
14
+
15
+ ```ts
16
+ import { createRuntime } from "@nodeterm/sdk";
17
+
18
+ const runtime = createRuntime({
19
+ connectors: [connector],
20
+ async handler(request) {
21
+ return {
22
+ requestId: request.id,
23
+ ok: true,
24
+ payload: {
25
+ received: request.payload
26
+ },
27
+ completedAt: new Date()
28
+ };
29
+ }
30
+ });
31
+
32
+ await runtime.start();
33
+ ```
34
+
35
+ ## Streaming Session
36
+
37
+ Use sessions for long outputs, long-running processes, or interactive tools.
38
+
39
+ ```ts
40
+ import { createRuntime } from "@nodeterm/sdk";
41
+
42
+ const runtime = createRuntime({
43
+ connectors: [connector],
44
+ async handler(request, context) {
45
+ const session = context.createSession(request);
46
+
47
+ session.emit({
48
+ type: "output",
49
+ stream: "stdout",
50
+ data: "started\n"
51
+ });
52
+
53
+ session.emit({
54
+ type: "done",
55
+ exitCode: 0
56
+ });
57
+
58
+ return session.response();
59
+ }
60
+ });
61
+
62
+ await runtime.start();
63
+ ```
64
+
65
+ Session events emitted before a connector subscribes are replayed by default, so short-lived handlers can emit and return without losing output.
66
+
67
+ ## Interactive Input
68
+
69
+ Connectors can send input, resize, and close events through the same session channel. A future CLI can map these events to `node-pty`, while a Telegram connector can map them to chat messages.
70
+
71
+ ```ts
72
+ async handler(request, context) {
73
+ const session = context.createSession(request);
74
+
75
+ session.on("input", (event) => {
76
+ process.stdout.write(event.data);
77
+ });
78
+
79
+ session.on("resize", (event) => {
80
+ console.log(`resize ${event.cols}x${event.rows}`);
81
+ });
82
+
83
+ session.on("close", () => {
84
+ console.log("session closed");
85
+ });
86
+
87
+ return session.response();
88
+ }
89
+ ```
90
+
91
+ ## Test Connector
92
+
93
+ `MemoryConnector` is exported from `@nodeterm/sdk/testing` for tests and examples.
94
+
95
+ ```ts
96
+ import { createRuntime } from "@nodeterm/sdk";
97
+ import { MemoryConnector } from "@nodeterm/sdk/testing";
98
+
99
+ const connector = new MemoryConnector();
100
+ const runtime = createRuntime({
101
+ connectors: [connector],
102
+ async handler(request) {
103
+ return {
104
+ requestId: request.id,
105
+ ok: true,
106
+ payload: request.payload,
107
+ completedAt: new Date()
108
+ };
109
+ }
110
+ });
111
+
112
+ await runtime.start();
113
+ const response = await connector.send({ text: "hello" });
114
+ ```
115
+
116
+ ## Development
117
+
118
+ ```bash
119
+ cd sdk
120
+ npm install
121
+ npm run build
122
+ npm test
123
+ npm pack --dry-run
124
+ ```
@@ -0,0 +1,3 @@
1
+ export { createRuntime } from "./runtime.js";
2
+ export { LocalSession } from "./session.js";
3
+ export type { Actor, Connector, ConnectorHandler, ControlError, ControlRequest, ControlResponse, ControlRuntime, DoneSessionEvent, ErrorSessionEvent, InputSessionEvent, MaybePromise, OutputSessionEvent, ResizeSessionEvent, RuntimeContext, RuntimeHandler, Session, SessionEvent, SessionEventOf, SessionEventType, SessionListener, SessionResponseOptions, SessionSubscribeOptions, Unsubscribe } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createRuntime } from "./runtime.js";
2
+ export { LocalSession } from "./session.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Connector, ControlRuntime, RuntimeHandler } from "./types.js";
2
+ export type RuntimeOptions<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>> = {
3
+ connectors: readonly Connector<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta>[];
4
+ handler: RuntimeHandler<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta>;
5
+ };
6
+ export declare function createRuntime<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>>(options: RuntimeOptions<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta>): ControlRuntime;
@@ -0,0 +1,49 @@
1
+ import { LocalSession } from "./session.js";
2
+ export function createRuntime(options) {
3
+ return new DefaultRuntime(options);
4
+ }
5
+ class DefaultRuntime {
6
+ options;
7
+ running = false;
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ async start() {
12
+ if (this.running)
13
+ return;
14
+ await Promise.all(this.options.connectors.map((connector) => {
15
+ return connector.start((request) => this.handle(request));
16
+ }));
17
+ this.running = true;
18
+ }
19
+ async stop() {
20
+ if (!this.running)
21
+ return;
22
+ await Promise.all(this.options.connectors.map((connector) => connector.stop()));
23
+ this.running = false;
24
+ }
25
+ isRunning() {
26
+ return this.running;
27
+ }
28
+ async handle(request) {
29
+ const context = {
30
+ createSession(sessionRequest, options) {
31
+ return new LocalSession(sessionRequest, options);
32
+ }
33
+ };
34
+ try {
35
+ return await this.options.handler(request, context);
36
+ }
37
+ catch (error) {
38
+ return {
39
+ requestId: request.id,
40
+ ok: false,
41
+ error: {
42
+ message: error instanceof Error ? error.message : String(error)
43
+ },
44
+ completedAt: new Date()
45
+ };
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAY5C,MAAM,UAAU,aAAa,CAM3B,OAAuF;IAEvF,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,cAAc;IASC;IAHX,OAAO,GAAG,KAAK,CAAC;IAExB,YACmB,OAAuF;QAAvF,YAAO,GAAP,OAAO,CAAgF;IACvG,CAAC;IAEJ,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;YAC1D,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAChF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,MAAM,CAClB,OAAsD;QAEtD,MAAM,OAAO,GAAmB;YAC9B,aAAa,CAAC,cAAc,EAAE,OAAO;gBACnC,OAAO,IAAI,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YACnD,CAAC;SACF,CAAC;QAEF,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAChE;gBACD,WAAW,EAAE,IAAI,IAAI,EAAE;aACxB,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { ControlRequest, ControlResponse, Session, SessionEvent, SessionEventOf, SessionEventType, SessionListener, SessionResponseOptions, SessionSubscribeOptions, Unsubscribe } from "./types.js";
2
+ export declare class LocalSession implements Session {
3
+ readonly id: string;
4
+ readonly requestId: string;
5
+ private events;
6
+ private listeners;
7
+ constructor(request: ControlRequest, options?: {
8
+ id?: string;
9
+ });
10
+ emit(event: SessionEvent): void;
11
+ onEvent(listener: SessionListener, options?: SessionSubscribeOptions): Unsubscribe;
12
+ on<TType extends SessionEventType>(type: TType, listener: SessionListener<SessionEventOf<TType>>, options?: SessionSubscribeOptions): Unsubscribe;
13
+ history(): readonly SessionEvent[];
14
+ response<TPayload = undefined, TMeta = Record<string, unknown>>(options?: SessionResponseOptions<TPayload, TMeta>): ControlResponse<TPayload, TMeta>;
15
+ }
@@ -0,0 +1,50 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export class LocalSession {
3
+ id;
4
+ requestId;
5
+ events = [];
6
+ listeners = new Set();
7
+ constructor(request, options = {}) {
8
+ this.id = options.id ?? randomUUID();
9
+ this.requestId = request.id;
10
+ }
11
+ emit(event) {
12
+ this.events.push(event);
13
+ for (const listener of this.listeners) {
14
+ listener(event);
15
+ }
16
+ }
17
+ onEvent(listener, options = {}) {
18
+ const replay = options.replay ?? true;
19
+ this.listeners.add(listener);
20
+ if (replay) {
21
+ for (const event of this.events) {
22
+ listener(event);
23
+ }
24
+ }
25
+ return () => {
26
+ this.listeners.delete(listener);
27
+ };
28
+ }
29
+ on(type, listener, options) {
30
+ return this.onEvent((event) => {
31
+ if (event.type === type) {
32
+ listener(event);
33
+ }
34
+ }, options);
35
+ }
36
+ history() {
37
+ return Object.freeze([...this.events]);
38
+ }
39
+ response(options = {}) {
40
+ return {
41
+ requestId: this.requestId,
42
+ ok: options.ok ?? true,
43
+ ...(options.payload === undefined ? {} : { payload: options.payload }),
44
+ ...(options.meta === undefined ? {} : { meta: options.meta }),
45
+ completedAt: new Date(),
46
+ session: this
47
+ };
48
+ }
49
+ }
50
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAczC,MAAM,OAAO,YAAY;IACd,EAAE,CAAS;IACX,SAAS,CAAS;IACnB,MAAM,GAAmB,EAAE,CAAC;IAC5B,SAAS,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE/C,YAAY,OAAuB,EAAE,UAA2B,EAAE;QAChE,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,UAAU,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC,KAAmB;QACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAExB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,QAAyB,EAAE,UAAmC,EAAE;QACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC;QACtC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE7B,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAED,EAAE,CACA,IAAW,EACX,QAAgD,EAChD,OAAiC;QAEjC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACxB,QAAQ,CAAC,KAA8B,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;IAED,OAAO;QACL,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,QAAQ,CACN,UAAmD,EAAE;QAErD,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI;YACtB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;YACtE,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAC7D,WAAW,EAAE,IAAI,IAAI,EAAE;YACvB,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ import type { Actor, Connector, ConnectorHandler, ControlRequest, ControlResponse } from "../types.js";
2
+ export type MemoryRequestOptions<TMeta = Record<string, unknown>> = {
3
+ id?: string;
4
+ source?: string;
5
+ actor?: Actor;
6
+ meta?: TMeta;
7
+ receivedAt?: Date;
8
+ };
9
+ export declare class MemoryConnector<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>> implements Connector<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta> {
10
+ readonly name: string;
11
+ readonly responses: ControlResponse<TResponsePayload, TResponseMeta>[];
12
+ private handler;
13
+ private started;
14
+ constructor(name?: string);
15
+ start(handler: ConnectorHandler<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta>): Promise<void>;
16
+ stop(): Promise<void>;
17
+ isStarted(): boolean;
18
+ send(payload: TRequestPayload, options?: MemoryRequestOptions<TRequestMeta>): Promise<ControlResponse<TResponsePayload, TResponseMeta>>;
19
+ dispatch(request: ControlRequest<TRequestPayload, TRequestMeta>): Promise<ControlResponse<TResponsePayload, TResponseMeta>>;
20
+ }
@@ -0,0 +1,40 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export class MemoryConnector {
3
+ name;
4
+ responses = [];
5
+ handler;
6
+ started = false;
7
+ constructor(name = "memory") {
8
+ this.name = name;
9
+ }
10
+ async start(handler) {
11
+ this.handler = handler;
12
+ this.started = true;
13
+ }
14
+ async stop() {
15
+ this.handler = undefined;
16
+ this.started = false;
17
+ }
18
+ isStarted() {
19
+ return this.started;
20
+ }
21
+ async send(payload, options = {}) {
22
+ return this.dispatch({
23
+ id: options.id ?? randomUUID(),
24
+ source: options.source ?? this.name,
25
+ ...(options.actor === undefined ? {} : { actor: options.actor }),
26
+ payload,
27
+ ...(options.meta === undefined ? {} : { meta: options.meta }),
28
+ receivedAt: options.receivedAt ?? new Date()
29
+ });
30
+ }
31
+ async dispatch(request) {
32
+ if (!this.handler) {
33
+ throw new Error(`MemoryConnector "${this.name}" is not started.`);
34
+ }
35
+ const response = await this.handler(request);
36
+ this.responses.push(response);
37
+ return response;
38
+ }
39
+ }
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiBzC,MAAM,OAAO,eAAe;IAUL;IAJZ,SAAS,GAAuD,EAAE,CAAC;IACpE,OAAO,CAA+F;IACtG,OAAO,GAAG,KAAK,CAAC;IAExB,YAAqB,OAAO,QAAQ;QAAf,SAAI,GAAJ,IAAI,CAAW;IAAG,CAAC;IAExC,KAAK,CAAC,KAAK,CAAC,OAAyF;QACnG,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,OAAwB,EACxB,UAA8C,EAAE;QAEhD,OAAO,IAAI,CAAC,QAAQ,CAAC;YACnB,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,UAAU,EAAE;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI;YACnC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;YAChE,OAAO;YACP,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAC7D,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,OAAsD;QAEtD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,IAAI,mBAAmB,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
@@ -0,0 +1,94 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
2
+ export type Actor = {
3
+ id: string;
4
+ name?: string;
5
+ };
6
+ export type ControlError = {
7
+ message: string;
8
+ code?: string;
9
+ };
10
+ export type ControlRequest<TPayload = unknown, TMeta = Record<string, unknown>> = {
11
+ id: string;
12
+ source: string;
13
+ actor?: Actor;
14
+ payload: TPayload;
15
+ meta?: TMeta;
16
+ receivedAt: Date;
17
+ };
18
+ export type ControlResponse<TPayload = unknown, TMeta = Record<string, unknown>> = {
19
+ requestId: string;
20
+ ok: boolean;
21
+ payload?: TPayload;
22
+ error?: ControlError;
23
+ meta?: TMeta;
24
+ completedAt: Date;
25
+ session?: Session;
26
+ };
27
+ export type OutputSessionEvent = {
28
+ type: "output";
29
+ stream: "stdout" | "stderr";
30
+ data: string;
31
+ };
32
+ export type InputSessionEvent = {
33
+ type: "input";
34
+ data: string;
35
+ };
36
+ export type ResizeSessionEvent = {
37
+ type: "resize";
38
+ cols: number;
39
+ rows: number;
40
+ };
41
+ export type ErrorSessionEvent = {
42
+ type: "error";
43
+ message: string;
44
+ code?: string;
45
+ };
46
+ export type DoneSessionEvent = {
47
+ type: "done";
48
+ exitCode?: number;
49
+ };
50
+ export type CloseSessionEvent = {
51
+ type: "close";
52
+ reason?: string;
53
+ };
54
+ export type SessionEvent = OutputSessionEvent | InputSessionEvent | ResizeSessionEvent | ErrorSessionEvent | DoneSessionEvent | CloseSessionEvent;
55
+ export type SessionEventType = SessionEvent["type"];
56
+ export type SessionEventOf<TType extends SessionEventType> = Extract<SessionEvent, {
57
+ type: TType;
58
+ }>;
59
+ export type SessionListener<TEvent extends SessionEvent = SessionEvent> = (event: TEvent) => void;
60
+ export type Unsubscribe = () => void;
61
+ export type SessionSubscribeOptions = {
62
+ replay?: boolean;
63
+ };
64
+ export type SessionResponseOptions<TPayload = unknown, TMeta = Record<string, unknown>> = {
65
+ ok?: boolean;
66
+ payload?: TPayload;
67
+ meta?: TMeta;
68
+ };
69
+ export type Session = {
70
+ readonly id: string;
71
+ readonly requestId: string;
72
+ emit(event: SessionEvent): void;
73
+ onEvent(listener: SessionListener, options?: SessionSubscribeOptions): Unsubscribe;
74
+ on<TType extends SessionEventType>(type: TType, listener: SessionListener<SessionEventOf<TType>>, options?: SessionSubscribeOptions): Unsubscribe;
75
+ history(): readonly SessionEvent[];
76
+ response<TPayload = undefined, TMeta = Record<string, unknown>>(options?: SessionResponseOptions<TPayload, TMeta>): ControlResponse<TPayload, TMeta>;
77
+ };
78
+ export type RuntimeContext = {
79
+ createSession(request: ControlRequest, options?: {
80
+ id?: string;
81
+ }): Session;
82
+ };
83
+ export type RuntimeHandler<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>> = (request: ControlRequest<TRequestPayload, TRequestMeta>, context: RuntimeContext) => MaybePromise<ControlResponse<TResponsePayload, TResponseMeta>>;
84
+ export type ConnectorHandler<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>> = (request: ControlRequest<TRequestPayload, TRequestMeta>) => Promise<ControlResponse<TResponsePayload, TResponseMeta>>;
85
+ export type Connector<TRequestPayload = unknown, TResponsePayload = unknown, TRequestMeta = Record<string, unknown>, TResponseMeta = Record<string, unknown>> = {
86
+ name: string;
87
+ start(handler: ConnectorHandler<TRequestPayload, TResponsePayload, TRequestMeta, TResponseMeta>): Promise<void>;
88
+ stop(): Promise<void>;
89
+ };
90
+ export type ControlRuntime = {
91
+ start(): Promise<void>;
92
+ stop(): Promise<void>;
93
+ isRunning(): boolean;
94
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@nodeterm/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Generic Node.js SDK for request/response and session-based communication between local machines and external tools.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./testing": {
15
+ "types": "./dist/testing/index.d.ts",
16
+ "import": "./dist/testing/index.js"
17
+ }
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json",
28
+ "typecheck": "tsc -p tsconfig.json --noEmit",
29
+ "test": "npm run build && node --test test/**/*.test.mjs",
30
+ "prepack": "npm run build && cp ../README.md README.md"
31
+ },
32
+ "keywords": [
33
+ "terminal",
34
+ "sdk",
35
+ "automation",
36
+ "remote-control",
37
+ "streaming"
38
+ ],
39
+ "devDependencies": {
40
+ "@types/node": "^20.19.25",
41
+ "typescript": "^5.9.3"
42
+ }
43
+ }