@linkshell/host 0.3.1 → 0.4.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.
@@ -107,25 +107,92 @@ export interface HelperStatus {
107
107
  trusted: boolean;
108
108
  recording: boolean;
109
109
  app: string;
110
+ /** The app can send the screen as a video track (it carries the media engine). */
111
+ video: boolean;
110
112
  }
113
+ /** A display as the app sees it. `screen` is its number in everything said to the app. */
114
+ export interface AppDisplay {
115
+ screen: number;
116
+ name: string;
117
+ /** In pixels. */
118
+ w: number;
119
+ h: number;
120
+ main: boolean;
121
+ }
122
+ /** The picture asked of the app for a pipe that loses nothing: rates in bits a second. */
123
+ export interface StreamProfile {
124
+ width: number;
125
+ fps: number;
126
+ bitrate: number;
127
+ ceiling: number;
128
+ }
129
+ /** A stream under way. */
130
+ export interface AppStream {
131
+ /** Another profile, without stopping: a new size starts a new generation, at a keyframe. */
132
+ set(profile: StreamProfile): void;
133
+ /** The next frame a keyframe. */
134
+ key(): void;
135
+ end(): void;
136
+ }
137
+ /** How the app is to offer a screen as a video track. */
138
+ export interface VideoOffer {
139
+ /** Which of the system's displays. */
140
+ screen: number;
141
+ iceServers: {
142
+ urls: string[];
143
+ }[];
144
+ /** Frames a second; the app's own choice when left out. */
145
+ fps?: number;
146
+ }
147
+ /** What a viewer answers the app's offer with, and the candidates it finds. */
148
+ export type VideoSignal = {
149
+ t: "rtc.answer";
150
+ sdp: string;
151
+ } | {
152
+ t: "rtc.ice";
153
+ candidate: string;
154
+ sdpMid: string | null;
155
+ sdpMLineIndex: number | null;
156
+ };
157
+ export declare const videoSignal: z.ZodDiscriminatedUnion<"t", [z.ZodObject<{
158
+ t: z.ZodLiteral<"rtc.answer">;
159
+ sdp: z.ZodString;
160
+ }, "strip", z.ZodTypeAny, {
161
+ t: "rtc.answer";
162
+ sdp: string;
163
+ }, {
164
+ t: "rtc.answer";
165
+ sdp: string;
166
+ }>, z.ZodObject<{
167
+ t: z.ZodLiteral<"rtc.ice">;
168
+ candidate: z.ZodString;
169
+ sdpMid: z.ZodNullable<z.ZodString>;
170
+ sdpMLineIndex: z.ZodNullable<z.ZodNumber>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ t: "rtc.ice";
173
+ candidate: string;
174
+ sdpMid: string | null;
175
+ sdpMLineIndex: number | null;
176
+ }, {
177
+ t: "rtc.ice";
178
+ candidate: string;
179
+ sdpMid: string | null;
180
+ sdpMLineIndex: number | null;
181
+ }>]>;
111
182
  /**
112
- * The app as it came in the package, made runnable. Packing a package drops
113
- * the program's permission to run (pnpm stores every file that isn't a `bin`
114
- * as a plain one): it is given back here, and where the installation can't be
115
- * written to (a root-owned one), a copy under the user's own directory is used.
116
- * Neither touches the signature.
183
+ * The app out of the archive it is shipped in, unpacked under the user's own
184
+ * directory. An archive, because a package can carry neither the links a
185
+ * framework is made of nor a program's permission to run; unpacked whole, the
186
+ * app is as it was signed. Always at the same place, so that an upgrade
187
+ * replaces the app instead of adding another: macOS has been seen to ask
188
+ * about recording again for the same app run from a new path. (A host still
189
+ * running the app being replaced keeps the one it has open.)
117
190
  */
118
- export declare function usableApp(packaged: string, copyTo?: string): string | undefined;
119
- /** The signed app this package was made with, if it was. */
191
+ export declare function unpackedApp(archive: string, at?: string): string | undefined;
192
+ /** LinkShell.app, where this installation has it: a Mac with Apple silicon, with `@linkshell/mac` installed beside the host. */
120
193
  export declare function shippedApp(): string | undefined;
121
- /** The helper compiled here; built on first use, once per version of its source. */
122
- export declare function inputHelper(log?: (message: string) => void): Promise<string>;
123
- /**
124
- * What this process itself (so: the host, and the capture it starts) is
125
- * allowed to do, and under which app's name. `ask` has the system put its
126
- * question for that permission, and opens the settings page for it.
127
- */
128
- export declare function hostAccess(log: (message: string) => void, ask?: "recording" | "control"): Promise<HelperStatus>;
194
+ /** What to tell someone whose Mac has no LinkShell.app: it is an Intel Mac (the app is built for Apple silicon only), or the package that carries it wasn't installed. */
195
+ export declare const NO_APP: string;
129
196
  type Route = (message: Record<string, unknown>) => void;
130
197
  declare class HelperApp {
131
198
  private readonly app;
@@ -138,6 +205,7 @@ declare class HelperApp {
138
205
  private readonly routes;
139
206
  private readonly watchers;
140
207
  private waiting;
208
+ private listing;
141
209
  constructor(app: string, dryRun: boolean, log: (message: string) => void);
142
210
  /** The app's end of the socket; opens the app when it isn't there. */
143
211
  private link;
@@ -146,17 +214,34 @@ declare class HelperApp {
146
214
  private write;
147
215
  /** What the app may do. It says so as it connects; asked again, it looks again. */
148
216
  access(): Promise<HelperStatus>;
149
- /** The system's dialog and its settings page, for the app. */
150
- ask(what?: "control" | "recording"): Promise<void>;
217
+ /** The displays there are to watch. */
218
+ displays(): Promise<AppDisplay[]>;
219
+ /**
220
+ * Has the app capture a screen and encode it for a pipe that loses nothing:
221
+ * the socket to a viewer it couldn't reach directly. Whole frames come to
222
+ * `on.frame`, each with the generation of picture size it belongs to.
223
+ */
224
+ stream(screen: number, profile: StreamProfile, on: {
225
+ frame(unit: Buffer, key: boolean, generation: number): void;
226
+ exit(error?: string): void;
227
+ }): Promise<AppStream>;
228
+ /**
229
+ * Opens LinkShell's own window for its permissions, on this computer: it says
230
+ * what each is for, has the system ask, takes the user to the switch, and
231
+ * ticks each off as it is turned on. Nothing appears when both are allowed
232
+ * already, and there is only ever one such window.
233
+ */
234
+ ask(): Promise<void>;
151
235
  /**
152
- * Has the app run a screen capture: as the app's child, the recording is the
153
- * app's to be allowed. The picture comes down a socket of its own; the
154
- * returned function ends the capture.
236
+ * Has the app offer a screen to one viewer as a video track. What the app
237
+ * says of it (`rtc.offer`, `rtc.ice`, `rtc.state`, `rtc.error`, and `gone`
238
+ * when the app itself went) comes to `route`; the viewer's answer goes back
239
+ * with `signal`. The picture and the viewer's hands then travel between the
240
+ * two of them, not through here.
155
241
  */
156
- capture(exec: string, args: string[], on: {
157
- data(chunk: Buffer): void;
158
- exit(code: number, errors: string): void;
159
- }): Promise<() => void>;
242
+ offerVideo(id: string, offer: VideoOffer, route: Route): Promise<void>;
243
+ signal(id: string, signal: VideoSignal): void;
244
+ endVideo(id: string): void;
160
245
  join(id: string, screen: number, route: Route, watcher: (status: HelperStatus) => void): Promise<void>;
161
246
  send(id: string, event: InputEvent): void;
162
247
  leave(id: string, watcher: (status: HelperStatus) => void): void;
@@ -171,19 +256,19 @@ export type { HelperApp };
171
256
  export declare class InputControl {
172
257
  private readonly listener;
173
258
  private readonly log;
174
- /** `app: false` keeps to a child process even when the package has the app (tests). */
259
+ /** `app: false` runs the app's program as this process's child instead of having the system open it (tests). */
175
260
  private readonly options;
176
261
  private child?;
177
262
  private joined?;
178
263
  private stopped;
179
264
  private started;
180
265
  constructor(listener: InputListener, log: (message: string) => void,
181
- /** `app: false` keeps to a child process even when the package has the app (tests). */
266
+ /** `app: false` runs the app's program as this process's child instead of having the system open it (tests). */
182
267
  options?: {
183
268
  dryRun?: boolean;
184
269
  app?: boolean;
185
270
  });
186
- /** Starts the helper for the display ffmpeg calls "Capture screen `screen`". Reports through the listener; never throws. */
271
+ /** Starts control of the display the app numbers `screen`. Reports through the listener; never throws. */
187
272
  start(screen: number): Promise<void>;
188
273
  private joinApp;
189
274
  private spawn;