@aitty/protocol 0.1.1 → 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/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # @aitty/protocol
2
+
3
+ Shared protocol types and helpers for aitty browser-terminal sessions.
4
+
5
+ This package has no Node runtime dependency. Use it when a host, browser
6
+ adapter, or integration needs to share status, control-frame, theme, or portless
7
+ session-path semantics without depending on the full server or browser runtime.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm i @aitty/protocol
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import {
19
+ createAittyPortlessIdentity,
20
+ createHelloControlFrame,
21
+ parseAittyControlFrame
22
+ } from "@aitty/protocol";
23
+
24
+ const identity = createAittyPortlessIdentity({
25
+ project: "my-app",
26
+ label: "codex",
27
+ token: "local-session-token"
28
+ });
29
+
30
+ console.log(identity.path);
31
+
32
+ const frame = parseAittyControlFrame(createHelloControlFrame({
33
+ cols: 120,
34
+ rows: 32,
35
+ replay: ""
36
+ }));
37
+ ```
38
+
39
+ ## Main Exports
40
+
41
+ - Portless helpers: `createAittyPortlessIdentity()`, `normalizePortlessSegment()`, `normalizeAittyPortlessPathname()`.
42
+ - Control frames: `createHelloControlFrame()`, `createResizeControlFrame()`, `createExitControlFrame()`, `createThemeControlFrame()`, `parseAittyControlFrame()`.
43
+ - Status types: `AittyTerminalStatusSnapshot`, connection states, output states, and session presentation states.
44
+ - Theme helpers: `normalizeTheme()`, `readThemeSource()`, `subscribeThemeSource()`.
45
+ - Session metadata helpers: `createSessionInfoBody()` and `AittySessionInfo`.
46
+
47
+ ## License
48
+
49
+ Apache-2.0
package/dist/index.d.ts CHANGED
@@ -36,6 +36,7 @@ type AittyTerminalStatusSnapshot = {
36
36
  output: AittyTerminalOutputState;
37
37
  sessionState: AittyTerminalSessionPresentationState;
38
38
  };
39
+ type AittyClientRole = "controller" | "viewer";
39
40
  type AittyHelloControlFrame = {
40
41
  cols: number;
41
42
  replay: string;
@@ -62,7 +63,44 @@ type AittyThemeControlFrame = {
62
63
  theme: AittyTheme | null;
63
64
  type: "theme";
64
65
  };
65
- type AittyControlFrame = AittyExitControlFrame | AittyHelloControlFrame | AittyPingControlFrame | AittyPongControlFrame | AittyResizeControlFrame | AittyThemeControlFrame;
66
+ type AittyRoleControlFrame = {
67
+ clientId: string;
68
+ role: AittyClientRole;
69
+ type: "role";
70
+ };
71
+ type AittyTakeoverRequestControlFrame = {
72
+ requestId?: string;
73
+ requesterId?: string;
74
+ type: "takeover-request";
75
+ };
76
+ type AittyTakeoverResponseControlFrame = {
77
+ approved: boolean;
78
+ requestId: string;
79
+ type: "takeover-response";
80
+ };
81
+ type AittyTakeoverResultControlFrame = {
82
+ approved: boolean;
83
+ reason?: string;
84
+ type: "takeover-result";
85
+ };
86
+ type AittyPushSubscriptionPayload = {
87
+ endpoint: string;
88
+ expirationTime?: null | number;
89
+ keys: {
90
+ auth: string;
91
+ p256dh: string;
92
+ };
93
+ };
94
+ type AittyPushSubscribeControlFrame = {
95
+ subscription: AittyPushSubscriptionPayload;
96
+ type: "push-subscribe";
97
+ url?: string;
98
+ };
99
+ type AittyPushUnsubscribeControlFrame = {
100
+ endpoint?: string;
101
+ type: "push-unsubscribe";
102
+ };
103
+ type AittyControlFrame = AittyExitControlFrame | AittyHelloControlFrame | AittyPingControlFrame | AittyPongControlFrame | AittyPushSubscribeControlFrame | AittyPushUnsubscribeControlFrame | AittyRoleControlFrame | AittyResizeControlFrame | AittyTakeoverRequestControlFrame | AittyTakeoverResponseControlFrame | AittyTakeoverResultControlFrame | AittyThemeControlFrame;
66
104
  declare function createAittyPortlessIdentity(options: {
67
105
  label?: string;
68
106
  labelFallback?: string;
@@ -87,8 +125,37 @@ declare function createExitControlFrame(options: {
87
125
  code: number | null;
88
126
  signal?: string | null;
89
127
  }): string;
128
+ declare function createRoleControlFrame(options: {
129
+ clientId: string;
130
+ role: AittyClientRole;
131
+ }): string;
132
+ declare function createTakeoverRequestControlFrame(options?: {
133
+ requestId?: string;
134
+ requesterId?: string;
135
+ }): string;
136
+ declare function createTakeoverResponseControlFrame(options: {
137
+ approved: boolean;
138
+ requestId: string;
139
+ }): string;
140
+ declare function createTakeoverResultControlFrame(options: {
141
+ approved: boolean;
142
+ reason?: string;
143
+ }): string;
144
+ declare function createPushSubscribeControlFrame(options: {
145
+ subscription: AittyPushSubscriptionPayload;
146
+ url?: string;
147
+ }): string;
148
+ declare function createPushUnsubscribeControlFrame(options?: {
149
+ endpoint?: string;
150
+ }): string;
90
151
  declare function parseAittyControlFrame(data: string): AittyControlFrame | null;
152
+ declare function isAittyRoleControlFrame(frame: AittyControlFrame): frame is AittyRoleControlFrame;
91
153
  declare function isAittyResizeControlFrame(frame: AittyControlFrame): frame is AittyResizeControlFrame;
154
+ declare function isAittyTakeoverRequestControlFrame(frame: AittyControlFrame): frame is AittyTakeoverRequestControlFrame;
155
+ declare function isAittyTakeoverResponseControlFrame(frame: AittyControlFrame): frame is AittyTakeoverResponseControlFrame;
156
+ declare function isAittyTakeoverResultControlFrame(frame: AittyControlFrame): frame is AittyTakeoverResultControlFrame;
157
+ declare function isAittyPushSubscribeControlFrame(frame: AittyControlFrame): frame is AittyPushSubscribeControlFrame;
158
+ declare function isAittyPushUnsubscribeControlFrame(frame: AittyControlFrame): frame is AittyPushUnsubscribeControlFrame;
92
159
  declare function isValidAittyResizeDimension(value: unknown): value is number;
93
160
  declare function normalizeTheme(theme: AittyTheme | undefined): AittyTheme | undefined;
94
161
  declare function readThemeSource(source: AittyThemeSource | undefined): Promise<AittyTheme | undefined>;
@@ -96,4 +163,4 @@ declare function subscribeThemeSource(source: AittyThemeSource | undefined, list
96
163
  dispose(): void;
97
164
  };
98
165
  //#endregion
99
- export { AITTY_SESSION_PATH_PREFIX, AittyControlFrame, AittyExitControlFrame, AittyHelloControlFrame, AittyPingControlFrame, AittyPongControlFrame, AittyPortlessIdentity, AittyPortlessOptions, AittyResizeControlFrame, AittyRuntimeKind, AittySessionInfo, AittyTerminalConnectionState, AittyTerminalLoadingPresentation, AittyTerminalOutputState, AittyTerminalSessionPresentationState, AittyTerminalStatusSnapshot, AittyTheme, AittyThemeControlFrame, AittyThemeSource, AittyThemeSubscription, createAittyPortlessIdentity, createExitControlFrame, createHelloControlFrame, createPingControlFrame, createPongControlFrame, createResizeControlFrame, createSessionInfoBody, createThemeControlFrame, isAittyResizeControlFrame, isValidAittyResizeDimension, normalizeAittyPortlessPathname, normalizePortlessSegment, normalizeTheme, parseAittyControlFrame, readThemeSource, subscribeThemeSource };
166
+ export { AITTY_SESSION_PATH_PREFIX, AittyClientRole, AittyControlFrame, AittyExitControlFrame, AittyHelloControlFrame, AittyPingControlFrame, AittyPongControlFrame, AittyPortlessIdentity, AittyPortlessOptions, AittyPushSubscribeControlFrame, AittyPushSubscriptionPayload, AittyPushUnsubscribeControlFrame, AittyResizeControlFrame, AittyRoleControlFrame, AittyRuntimeKind, AittySessionInfo, AittyTakeoverRequestControlFrame, AittyTakeoverResponseControlFrame, AittyTakeoverResultControlFrame, AittyTerminalConnectionState, AittyTerminalLoadingPresentation, AittyTerminalOutputState, AittyTerminalSessionPresentationState, AittyTerminalStatusSnapshot, AittyTheme, AittyThemeControlFrame, AittyThemeSource, AittyThemeSubscription, createAittyPortlessIdentity, createExitControlFrame, createHelloControlFrame, createPingControlFrame, createPongControlFrame, createPushSubscribeControlFrame, createPushUnsubscribeControlFrame, createResizeControlFrame, createRoleControlFrame, createSessionInfoBody, createTakeoverRequestControlFrame, createTakeoverResponseControlFrame, createTakeoverResultControlFrame, createThemeControlFrame, isAittyPushSubscribeControlFrame, isAittyPushUnsubscribeControlFrame, isAittyResizeControlFrame, isAittyRoleControlFrame, isAittyTakeoverRequestControlFrame, isAittyTakeoverResponseControlFrame, isAittyTakeoverResultControlFrame, isValidAittyResizeDimension, normalizeAittyPortlessPathname, normalizePortlessSegment, normalizeTheme, parseAittyControlFrame, readThemeSource, subscribeThemeSource };
package/dist/index.js CHANGED
@@ -58,6 +58,47 @@ function createExitControlFrame(options) {
58
58
  type: "exit"
59
59
  });
60
60
  }
61
+ function createRoleControlFrame(options) {
62
+ return JSON.stringify({
63
+ clientId: options.clientId,
64
+ role: options.role,
65
+ type: "role"
66
+ });
67
+ }
68
+ function createTakeoverRequestControlFrame(options = {}) {
69
+ return JSON.stringify({
70
+ ...options.requestId ? { requestId: options.requestId } : {},
71
+ ...options.requesterId ? { requesterId: options.requesterId } : {},
72
+ type: "takeover-request"
73
+ });
74
+ }
75
+ function createTakeoverResponseControlFrame(options) {
76
+ return JSON.stringify({
77
+ approved: options.approved,
78
+ requestId: options.requestId,
79
+ type: "takeover-response"
80
+ });
81
+ }
82
+ function createTakeoverResultControlFrame(options) {
83
+ return JSON.stringify({
84
+ approved: options.approved,
85
+ ...options.reason ? { reason: options.reason } : {},
86
+ type: "takeover-result"
87
+ });
88
+ }
89
+ function createPushSubscribeControlFrame(options) {
90
+ return JSON.stringify({
91
+ subscription: options.subscription,
92
+ ...options.url ? { url: options.url } : {},
93
+ type: "push-subscribe"
94
+ });
95
+ }
96
+ function createPushUnsubscribeControlFrame(options = {}) {
97
+ return JSON.stringify({
98
+ ...options.endpoint ? { endpoint: options.endpoint } : {},
99
+ type: "push-unsubscribe"
100
+ });
101
+ }
61
102
  function parseAittyControlFrame(data) {
62
103
  try {
63
104
  const parsed = JSON.parse(data);
@@ -68,12 +109,36 @@ function parseAittyControlFrame(data) {
68
109
  return null;
69
110
  }
70
111
  }
112
+ function isAittyRoleControlFrame(frame) {
113
+ return frame.type === "role" && (frame.role === "controller" || frame.role === "viewer") && typeof frame.clientId === "string" && frame.clientId.length > 0;
114
+ }
71
115
  function isAittyResizeControlFrame(frame) {
72
116
  return frame.type === "resize" && isValidAittyResizeDimension(frame.cols) && isValidAittyResizeDimension(frame.rows);
73
117
  }
118
+ function isAittyTakeoverRequestControlFrame(frame) {
119
+ return frame.type === "takeover-request";
120
+ }
121
+ function isAittyTakeoverResponseControlFrame(frame) {
122
+ return frame.type === "takeover-response" && typeof frame.requestId === "string" && frame.requestId.length > 0 && typeof frame.approved === "boolean";
123
+ }
124
+ function isAittyTakeoverResultControlFrame(frame) {
125
+ return frame.type === "takeover-result" && typeof frame.approved === "boolean";
126
+ }
127
+ function isAittyPushSubscribeControlFrame(frame) {
128
+ return frame.type === "push-subscribe" && isAittyPushSubscriptionPayload(frame.subscription) && (frame.url === void 0 || typeof frame.url === "string");
129
+ }
130
+ function isAittyPushUnsubscribeControlFrame(frame) {
131
+ return frame.type === "push-unsubscribe" && (frame.endpoint === void 0 || typeof frame.endpoint === "string");
132
+ }
74
133
  function isValidAittyResizeDimension(value) {
75
134
  return typeof value === "number" && Number.isInteger(value) && value > 0;
76
135
  }
136
+ function isAittyPushSubscriptionPayload(value) {
137
+ if (!value || typeof value !== "object") return false;
138
+ const subscription = value;
139
+ const keys = subscription.keys;
140
+ return typeof subscription.endpoint === "string" && subscription.endpoint.length > 0 && (subscription.expirationTime === void 0 || subscription.expirationTime === null || typeof subscription.expirationTime === "number") && Boolean(keys) && typeof keys?.auth === "string" && keys.auth.length > 0 && typeof keys.p256dh === "string" && keys.p256dh.length > 0;
141
+ }
77
142
  function normalizeTheme(theme) {
78
143
  if (typeof theme !== "string") return;
79
144
  const normalized = theme.trim();
@@ -91,4 +156,4 @@ function subscribeThemeSource(source, listener) {
91
156
  return { dispose() {} };
92
157
  }
93
158
  //#endregion
94
- export { AITTY_SESSION_PATH_PREFIX, createAittyPortlessIdentity, createExitControlFrame, createHelloControlFrame, createPingControlFrame, createPongControlFrame, createResizeControlFrame, createSessionInfoBody, createThemeControlFrame, isAittyResizeControlFrame, isValidAittyResizeDimension, normalizeAittyPortlessPathname, normalizePortlessSegment, normalizeTheme, parseAittyControlFrame, readThemeSource, subscribeThemeSource };
159
+ export { AITTY_SESSION_PATH_PREFIX, createAittyPortlessIdentity, createExitControlFrame, createHelloControlFrame, createPingControlFrame, createPongControlFrame, createPushSubscribeControlFrame, createPushUnsubscribeControlFrame, createResizeControlFrame, createRoleControlFrame, createSessionInfoBody, createTakeoverRequestControlFrame, createTakeoverResponseControlFrame, createTakeoverResultControlFrame, createThemeControlFrame, isAittyPushSubscribeControlFrame, isAittyPushUnsubscribeControlFrame, isAittyResizeControlFrame, isAittyRoleControlFrame, isAittyTakeoverRequestControlFrame, isAittyTakeoverResponseControlFrame, isAittyTakeoverResultControlFrame, isValidAittyResizeDimension, normalizeAittyPortlessPathname, normalizePortlessSegment, normalizeTheme, parseAittyControlFrame, readThemeSource, subscribeThemeSource };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aitty/protocol",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Shared protocol types and helpers for aitty.",
5
5
  "keywords": [
6
6
  "agent",