@termix-ssh/plugin-sdk 1.0.15 → 1.0.18
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/CHANGELOG.md +31 -0
- package/PLUGIN-GUIDE.md +92 -72
- package/dist/backend.d.ts +381 -0
- package/dist/capabilities.js +2 -0
- package/dist/frontend.d.ts +45 -0
- package/dist/host/{PluginsTab-S33WIIGC.js → PluginsTab-YAGYRBIE.js} +3 -3
- package/dist/host/{chunk-57DJU2ZA.js → chunk-2MU6WNSX.js} +1 -1
- package/dist/host/{chunk-VFTB2RDP.js → chunk-446UEDSK.js} +4 -2
- package/dist/host/{chunk-3L45NNT5.js → chunk-7LEJ3HYF.js} +20 -3
- package/dist/host/{chunk-V44FCJBQ.js → chunk-J2AILBXM.js} +540 -400
- package/dist/host/{chunk-2IV3VX7F.js → chunk-LQKK2JJA.js} +5 -1
- package/dist/host/core-classes.json +1 -1
- package/dist/host/{linked-server-SCJH7DSL.js → linked-server-OUF3POT7.js} +5 -3
- package/dist/host/{rbac-api-KA5IPQQL.js → rbac-api-LZVENREG.js} +1 -1
- package/dist/host/testing-host.js +4 -4
- package/dist/host/types/src/types/onboarding.d.ts +0 -1
- package/dist/host/types/src/ui/auth/Auth.d.ts +1 -0
- package/dist/host/types/src/ui/lib/linked-server.d.ts +2 -0
- package/dist/host/types/src/ui/plugin-host/NativeView.d.ts +19 -0
- package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +2 -6
- package/dist/host/types/src/ui/shell/CompanionFrame.d.ts +11 -0
- package/dist/host/types/src/ui/shell/companions.d.ts +63 -0
- package/dist/host/ui.js +149 -815
- package/dist/host/{use-permissions-LLN6TLAL.js → use-permissions-BYCDS5PD.js} +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +1 -1
- package/dist/testing.d.ts +5 -1
- package/dist/testing.js +28 -0
- package/package.json +1 -1
- package/schema/manifest.schema.json +2 -0
- package/dist/host/types/src/ui/api/sync-api.d.ts +0 -93
- package/dist/host/types/src/ui/auth/ElectronLoginForm.d.ts +0 -9
- package/dist/host/types/src/ui/hooks/use-sync-status.d.ts +0 -4
- package/dist/host/types/src/ui/lib/sync-status.d.ts +0 -6
- package/dist/host/types/src/ui/settings/sync/LinkServerDialog.d.ts +0 -12
- package/dist/host/types/src/ui/settings/sync/ProxySettingsFields.d.ts +0 -15
- package/dist/host/types/src/ui/settings/sync/saved-servers.d.ts +0 -3
package/dist/backend.d.ts
CHANGED
|
@@ -201,6 +201,357 @@ export interface SyncEntityRegistration {
|
|
|
201
201
|
*/
|
|
202
202
|
export interface PluginSync {
|
|
203
203
|
registerEntity: (entity: SyncEntityRegistration) => void;
|
|
204
|
+
/** Needs sync:provide. Takes over /sync/v2 and /sync/link. Removed on deactivate. */
|
|
205
|
+
provide: (provider: PluginSyncProvider) => () => void;
|
|
206
|
+
/** Needs sync:provide. The replica store, change feed and link registry. */
|
|
207
|
+
readonly host: PluginSyncHost;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The plugin that owns server and desktop sync. Core forwards every request
|
|
211
|
+
* under /sync to `handle` (an Express style handler) and answers 404 when no
|
|
212
|
+
* plugin provides it. Call `next()` to let core answer instead.
|
|
213
|
+
*/
|
|
214
|
+
export interface PluginSyncProvider {
|
|
215
|
+
handle: (req: unknown, res: unknown, next: (error?: unknown) => void) => void | Promise<void>;
|
|
216
|
+
}
|
|
217
|
+
/** A kind of data that syncs, as the sync provider sees it. */
|
|
218
|
+
export interface SyncEntityInfo {
|
|
219
|
+
type: string;
|
|
220
|
+
owner: string;
|
|
221
|
+
order: number;
|
|
222
|
+
readOnly: boolean;
|
|
223
|
+
/** Whether the row would show in a load even if it exists. */
|
|
224
|
+
covers?: (syncId: string) => boolean;
|
|
225
|
+
}
|
|
226
|
+
/** A row ready for the wire, with its content hash. */
|
|
227
|
+
export interface SyncWireRow {
|
|
228
|
+
syncId: string;
|
|
229
|
+
wire: SyncRow;
|
|
230
|
+
hash: string;
|
|
231
|
+
}
|
|
232
|
+
export interface SyncRecordInfo {
|
|
233
|
+
entityType: string;
|
|
234
|
+
syncId: string;
|
|
235
|
+
revision: number;
|
|
236
|
+
hash: string | null;
|
|
237
|
+
deleted: boolean;
|
|
238
|
+
error: string | null;
|
|
239
|
+
errorHash: string | null;
|
|
240
|
+
seq: number;
|
|
241
|
+
}
|
|
242
|
+
export interface SyncConflictInfo {
|
|
243
|
+
id: number;
|
|
244
|
+
entityType: string;
|
|
245
|
+
syncId: string;
|
|
246
|
+
localRow: string;
|
|
247
|
+
createdAt: string;
|
|
248
|
+
}
|
|
249
|
+
export interface SyncAccountInfo {
|
|
250
|
+
id: string;
|
|
251
|
+
username: string;
|
|
252
|
+
isAdmin: boolean;
|
|
253
|
+
active: boolean;
|
|
254
|
+
}
|
|
255
|
+
export interface SyncProxyHeader {
|
|
256
|
+
name: string;
|
|
257
|
+
value: string;
|
|
258
|
+
}
|
|
259
|
+
export interface SyncBasicAuth {
|
|
260
|
+
username: string;
|
|
261
|
+
password: string;
|
|
262
|
+
}
|
|
263
|
+
/** A client certificate for a server that asks for mutual TLS. */
|
|
264
|
+
export interface SyncClientCert {
|
|
265
|
+
/** Base64 of the PFX/P12 file. */
|
|
266
|
+
pfx: string;
|
|
267
|
+
passphrase: string;
|
|
268
|
+
/** The file name, to show which certificate is saved. */
|
|
269
|
+
name: string | null;
|
|
270
|
+
}
|
|
271
|
+
/** Extra per server connection options. Kept open so new ones need no migration. */
|
|
272
|
+
export type SyncLinkOptions = Record<string, unknown>;
|
|
273
|
+
/** One server a desktop is linked to, with a replica of its data. */
|
|
274
|
+
export interface SyncLinkInfo {
|
|
275
|
+
id: number;
|
|
276
|
+
/** The local user the replica belongs to. */
|
|
277
|
+
userId: string;
|
|
278
|
+
serverUrl: string;
|
|
279
|
+
serverName: string | null;
|
|
280
|
+
serverVersion: string | null;
|
|
281
|
+
sessionToken: string | null;
|
|
282
|
+
customHeaders: SyncProxyHeader[];
|
|
283
|
+
basicAuth: SyncBasicAuth | null;
|
|
284
|
+
allowInvalidCertificate: boolean;
|
|
285
|
+
clientCert: SyncClientCert | null;
|
|
286
|
+
remoteUserId: string | null;
|
|
287
|
+
remoteUsername: string | null;
|
|
288
|
+
account: {
|
|
289
|
+
remoteUserId?: string;
|
|
290
|
+
username?: string;
|
|
291
|
+
isAdmin?: boolean;
|
|
292
|
+
roles?: string[];
|
|
293
|
+
permissions?: string[];
|
|
294
|
+
} | null;
|
|
295
|
+
disabledTypes: string[];
|
|
296
|
+
/** Extra connection options for this server, kept open for new ones. */
|
|
297
|
+
options: SyncLinkOptions;
|
|
298
|
+
knownTypes: string[];
|
|
299
|
+
cursor: number;
|
|
300
|
+
status: "idle" | "syncing" | "offline" | "error" | "signed_out";
|
|
301
|
+
lastError: string | null;
|
|
302
|
+
linkedAt: string;
|
|
303
|
+
lastSyncAt: string | null;
|
|
304
|
+
}
|
|
305
|
+
export interface SyncLinkCreate {
|
|
306
|
+
userId: string;
|
|
307
|
+
serverUrl: string;
|
|
308
|
+
serverName?: string | null;
|
|
309
|
+
serverVersion?: string | null;
|
|
310
|
+
sessionToken: string;
|
|
311
|
+
customHeaders?: SyncProxyHeader[];
|
|
312
|
+
basicAuth?: SyncBasicAuth | null;
|
|
313
|
+
allowInvalidCertificate?: boolean;
|
|
314
|
+
clientCert?: SyncClientCert | null;
|
|
315
|
+
remoteUserId?: string | null;
|
|
316
|
+
remoteUsername?: string | null;
|
|
317
|
+
}
|
|
318
|
+
export type SyncLinkPatch = Partial<Pick<SyncLinkInfo, "serverName" | "serverVersion" | "sessionToken" | "customHeaders" | "basicAuth" | "allowInvalidCertificate" | "clientCert" | "remoteUserId" | "remoteUsername" | "account" | "disabledTypes" | "options" | "knownTypes" | "cursor" | "status" | "lastError" | "lastSyncAt">>;
|
|
319
|
+
export type SyncRemoteErrorKind = "offline" | "tls" | "client_cert" | "proxy" | "basic_auth" | "signed_out" | "server";
|
|
320
|
+
export interface SyncRemoteError extends Error {
|
|
321
|
+
readonly kind: SyncRemoteErrorKind;
|
|
322
|
+
readonly status?: number;
|
|
323
|
+
}
|
|
324
|
+
export interface SyncRemoteTarget {
|
|
325
|
+
serverUrl: string;
|
|
326
|
+
sessionToken?: string | null;
|
|
327
|
+
customHeaders?: SyncProxyHeader[];
|
|
328
|
+
basicAuth?: SyncBasicAuth | null;
|
|
329
|
+
allowInvalidCertificate?: boolean;
|
|
330
|
+
clientCert?: SyncClientCert | null;
|
|
331
|
+
}
|
|
332
|
+
export interface SyncRemoteRequest {
|
|
333
|
+
method?: string;
|
|
334
|
+
body?: unknown;
|
|
335
|
+
timeoutMs?: number;
|
|
336
|
+
signal?: AbortSignal;
|
|
337
|
+
}
|
|
338
|
+
export interface SyncPushOp {
|
|
339
|
+
entityType: string;
|
|
340
|
+
syncId: string;
|
|
341
|
+
baseRevision: number;
|
|
342
|
+
op: "upsert" | "delete";
|
|
343
|
+
row?: SyncRow;
|
|
344
|
+
}
|
|
345
|
+
export type SyncPushResult = {
|
|
346
|
+
syncId: string;
|
|
347
|
+
entityType: string;
|
|
348
|
+
status: "ok";
|
|
349
|
+
revision: number;
|
|
350
|
+
} | {
|
|
351
|
+
syncId: string;
|
|
352
|
+
entityType: string;
|
|
353
|
+
status: "conflict";
|
|
354
|
+
revision: number;
|
|
355
|
+
deleted: boolean;
|
|
356
|
+
row: SyncRow | null;
|
|
357
|
+
} | {
|
|
358
|
+
syncId: string;
|
|
359
|
+
entityType: string;
|
|
360
|
+
status: "rejected";
|
|
361
|
+
reason: string;
|
|
362
|
+
};
|
|
363
|
+
export interface SyncRemotePlugin {
|
|
364
|
+
id: string;
|
|
365
|
+
name: string;
|
|
366
|
+
version: string;
|
|
367
|
+
source: string;
|
|
368
|
+
artifact: boolean;
|
|
369
|
+
enabled: boolean;
|
|
370
|
+
active: boolean;
|
|
371
|
+
desktop: string;
|
|
372
|
+
syncEntities: string[];
|
|
373
|
+
granted: string[];
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* What the sync provider gets from core: the replica store, the change feed,
|
|
377
|
+
* the linked server registry, sessions and a few helpers. Core keeps all of
|
|
378
|
+
* it neutral; the provider decides when and how to use it. Needs sync:provide.
|
|
379
|
+
*/
|
|
380
|
+
export interface PluginSyncHost {
|
|
381
|
+
/** True on the desktop app's own backend. */
|
|
382
|
+
readonly embedded: boolean;
|
|
383
|
+
info: {
|
|
384
|
+
appName: () => Promise<string>;
|
|
385
|
+
version: () => string;
|
|
386
|
+
};
|
|
387
|
+
entities: {
|
|
388
|
+
list: () => SyncEntityInfo[];
|
|
389
|
+
get: (type: string) => SyncEntityInfo | undefined;
|
|
390
|
+
/** Registers core's own entities once. */
|
|
391
|
+
ensureCore: () => Promise<void>;
|
|
392
|
+
onChanged: (listener: () => void) => () => void;
|
|
393
|
+
onLocalWrite: (listener: () => void) => () => void;
|
|
394
|
+
};
|
|
395
|
+
/** Rows and bookkeeping of one local user's replica. */
|
|
396
|
+
replica: {
|
|
397
|
+
resolvers: (userId: string, onMissing?: () => void) => unknown;
|
|
398
|
+
loadRows: (type: string, userId: string, resolvers: unknown) => Promise<{
|
|
399
|
+
rows: Map<string, SyncWireRow>;
|
|
400
|
+
failed: Set<string>;
|
|
401
|
+
}>;
|
|
402
|
+
loadRow: (type: string, userId: string, syncId: string, resolvers: unknown) => Promise<SyncWireRow | null>;
|
|
403
|
+
writeRow: (type: string, userId: string, wire: SyncRow, resolvers: unknown) => Promise<void>;
|
|
404
|
+
deleteRow: (type: string, userId: string, syncId: string) => Promise<void>;
|
|
405
|
+
orderSelfReferences: (type: string, rows: SyncRow[]) => SyncRow[];
|
|
406
|
+
counts: (userId: string) => Promise<Record<string, number>>;
|
|
407
|
+
wipe: (userId: string) => Promise<void>;
|
|
408
|
+
regenerateSyncIds: (userId: string) => Promise<void>;
|
|
409
|
+
removeSharedCopies: (userId: string) => Promise<void>;
|
|
410
|
+
records: {
|
|
411
|
+
list: (userId: string) => Promise<Map<string, SyncRecordInfo>>;
|
|
412
|
+
put: (userId: string, type: string, syncId: string, values: {
|
|
413
|
+
revision: number;
|
|
414
|
+
hash: string | null;
|
|
415
|
+
deleted: boolean;
|
|
416
|
+
}) => Promise<void>;
|
|
417
|
+
setError: (userId: string, type: string, syncId: string, reason: string, hash: string) => Promise<void>;
|
|
418
|
+
clearErrors: (userId: string) => Promise<void>;
|
|
419
|
+
listErrors: (userId: string) => Promise<SyncRecordInfo[]>;
|
|
420
|
+
deleteForUser: (userId: string) => Promise<void>;
|
|
421
|
+
key: (type: string, syncId: string) => string;
|
|
422
|
+
};
|
|
423
|
+
conflicts: {
|
|
424
|
+
save: (userId: string, type: string, syncId: string, row: SyncRow, revision: number) => Promise<void>;
|
|
425
|
+
list: (userId: string) => Promise<SyncConflictInfo[]>;
|
|
426
|
+
get: (userId: string, id: number) => Promise<SyncConflictInfo | null>;
|
|
427
|
+
delete: (userId: string, id: number) => Promise<void>;
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
/** The server side: what changed for an account and what a desktop pushed. */
|
|
431
|
+
feed: {
|
|
432
|
+
withUserLock: <T>(userId: string, run: () => Promise<T>) => Promise<T>;
|
|
433
|
+
changes: (userId: string, cursor: number, limit: number, types?: string[]) => Promise<{
|
|
434
|
+
records: Array<{
|
|
435
|
+
seq: number;
|
|
436
|
+
entityType: string;
|
|
437
|
+
syncId: string;
|
|
438
|
+
revision: number;
|
|
439
|
+
deleted: boolean;
|
|
440
|
+
row: SyncRow | null;
|
|
441
|
+
}>;
|
|
442
|
+
hasMore: boolean;
|
|
443
|
+
nextCursor: number;
|
|
444
|
+
entityTypes: Array<{
|
|
445
|
+
type: string;
|
|
446
|
+
owner: string;
|
|
447
|
+
order: number;
|
|
448
|
+
readOnly: boolean;
|
|
449
|
+
}>;
|
|
450
|
+
}>;
|
|
451
|
+
parsePushOps: (body: unknown) => Promise<SyncPushOp[] | null>;
|
|
452
|
+
applyPush: (userId: string, ops: SyncPushOp[]) => Promise<SyncPushResult[]>;
|
|
453
|
+
/** Streams a change notice to the account's open event streams. */
|
|
454
|
+
listen: (userId: string, res: unknown) => Promise<() => void>;
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* The linked server registry, kept in core so it is there before plugins.
|
|
458
|
+
* One link per local user. Without a user, the active account is meant.
|
|
459
|
+
*/
|
|
460
|
+
links: {
|
|
461
|
+
list: () => Promise<SyncLinkInfo[]>;
|
|
462
|
+
get: (userId?: string) => Promise<SyncLinkInfo | null>;
|
|
463
|
+
create: (link: SyncLinkCreate) => Promise<SyncLinkInfo>;
|
|
464
|
+
update: (patch: SyncLinkPatch, userId?: string) => Promise<SyncLinkInfo | null>;
|
|
465
|
+
remove: (userId?: string) => Promise<void>;
|
|
466
|
+
};
|
|
467
|
+
/** Desktop accounts: the local user and the replica user of each server. */
|
|
468
|
+
accounts: {
|
|
469
|
+
list: () => Promise<SyncAccountInfo[]>;
|
|
470
|
+
/** A new empty account, ready to be linked to a server. */
|
|
471
|
+
create: () => Promise<string>;
|
|
472
|
+
/** Which account the app signs in as from now on. */
|
|
473
|
+
setActive: (userId: string) => Promise<void>;
|
|
474
|
+
activeId: () => Promise<string | null>;
|
|
475
|
+
/** Deletes an account and all its data. The last one cannot go. */
|
|
476
|
+
remove: (userId: string) => Promise<void>;
|
|
477
|
+
};
|
|
478
|
+
http: {
|
|
479
|
+
json: <T>(target: SyncRemoteTarget, pathname: string, request?: SyncRemoteRequest) => Promise<T>;
|
|
480
|
+
fetch: (target: SyncRemoteTarget, pathname: string, request?: SyncRemoteRequest) => Promise<{
|
|
481
|
+
ok: boolean;
|
|
482
|
+
status: number;
|
|
483
|
+
body: AsyncIterable<Uint8Array> | null;
|
|
484
|
+
}>;
|
|
485
|
+
normalizeServerUrl: (input: string) => string | null;
|
|
486
|
+
isRemoteError: (error: unknown) => error is SyncRemoteError;
|
|
487
|
+
};
|
|
488
|
+
sessions: {
|
|
489
|
+
/** A long lived session for a linked desktop. Shows in the account's sessions. */
|
|
490
|
+
issueDesktop: (userId: string, options: {
|
|
491
|
+
days: number;
|
|
492
|
+
deviceInfo: string;
|
|
493
|
+
}) => Promise<string>;
|
|
494
|
+
revoke: (sessionId: string) => Promise<void>;
|
|
495
|
+
find: (sessionId: string) => Promise<{
|
|
496
|
+
expiresAt: string;
|
|
497
|
+
deviceInfo: string | null;
|
|
498
|
+
} | null>;
|
|
499
|
+
findUser: (userId: string) => Promise<{
|
|
500
|
+
id: string;
|
|
501
|
+
username: string;
|
|
502
|
+
} | null>;
|
|
503
|
+
/** The user and session behind a token, or null when it is not valid. */
|
|
504
|
+
verify: (token: string) => Promise<{
|
|
505
|
+
userId: string;
|
|
506
|
+
sessionId?: string;
|
|
507
|
+
} | null>;
|
|
508
|
+
/** Sets the sign in cookie on a response, like a login does. A name sets another cookie. */
|
|
509
|
+
setCookie: (req: unknown, res: unknown, value: string, options?: {
|
|
510
|
+
name?: string;
|
|
511
|
+
days?: number;
|
|
512
|
+
}) => void;
|
|
513
|
+
/** Removes the sign in cookie, without ending the session behind it. */
|
|
514
|
+
clearCookie: (req: unknown, res: unknown, name?: string) => void;
|
|
515
|
+
/** Express middleware that signs the request in and sets userId and sessionId. */
|
|
516
|
+
middleware: () => unknown;
|
|
517
|
+
/** Express middleware that lets a request through only on a desktop. */
|
|
518
|
+
desktopOnly: () => unknown;
|
|
519
|
+
};
|
|
520
|
+
hosts: {
|
|
521
|
+
findForSync: (userId: string, syncId: string) => Promise<{
|
|
522
|
+
id: number;
|
|
523
|
+
name: string | null;
|
|
524
|
+
} | null>;
|
|
525
|
+
statusBySyncId: (userId: string) => Promise<Record<string, unknown>>;
|
|
526
|
+
};
|
|
527
|
+
plugins: {
|
|
528
|
+
listForDesktop: () => Promise<{
|
|
529
|
+
plugins: SyncRemotePlugin[];
|
|
530
|
+
registryKeys: unknown;
|
|
531
|
+
}>;
|
|
532
|
+
/** Sends a plugin archive; false when there is no such plugin to share. */
|
|
533
|
+
sendPackage: (pluginId: string, res: unknown) => Promise<boolean>;
|
|
534
|
+
mirror: (link: SyncLinkInfo) => Promise<void>;
|
|
535
|
+
lastRemote: () => SyncRemotePlugin[];
|
|
536
|
+
localNames: () => Map<string, string>;
|
|
537
|
+
};
|
|
538
|
+
desktop: {
|
|
539
|
+
/** Tells the app's main process how to reach the linked origin. */
|
|
540
|
+
applyProxy: (link: SyncLinkInfo | null) => Promise<void>;
|
|
541
|
+
deviceName: () => string;
|
|
542
|
+
};
|
|
543
|
+
alerts: {
|
|
544
|
+
send: (alert: {
|
|
545
|
+
title: string;
|
|
546
|
+
body: string;
|
|
547
|
+
severity: "info" | "warning" | "error";
|
|
548
|
+
category: string;
|
|
549
|
+
dedupeKey: string;
|
|
550
|
+
link?: {
|
|
551
|
+
tab: string;
|
|
552
|
+
};
|
|
553
|
+
}) => Promise<void>;
|
|
554
|
+
};
|
|
204
555
|
}
|
|
205
556
|
/**
|
|
206
557
|
* A plain key/value hand-off between plugins. `provide` and `revoke` only take
|
|
@@ -798,6 +1149,17 @@ export interface PluginHosts {
|
|
|
798
1149
|
activity: PluginHostActivityApi;
|
|
799
1150
|
/** Read only hosts this plugin manages. */
|
|
800
1151
|
locks: PluginHostLocksApi;
|
|
1152
|
+
/** Folder rows for the acting user. Needs hosts:write. */
|
|
1153
|
+
folders: PluginHostFoldersApi;
|
|
1154
|
+
}
|
|
1155
|
+
export interface PluginHostFoldersApi {
|
|
1156
|
+
/** Creates the folder row if missing and sets its color and icon. */
|
|
1157
|
+
ensure: (name: string, style?: {
|
|
1158
|
+
color?: string | null;
|
|
1159
|
+
icon?: string | null;
|
|
1160
|
+
}) => Promise<void>;
|
|
1161
|
+
/** Removes the folder row and subfolders once no host is left in them. */
|
|
1162
|
+
removeEmpty: (name: string) => Promise<boolean>;
|
|
801
1163
|
}
|
|
802
1164
|
export interface PluginHostImportOptions {
|
|
803
1165
|
/** Update a host that matches on ip, port and username instead of skipping it. */
|
|
@@ -1136,7 +1498,26 @@ export interface PluginNetwork {
|
|
|
1136
1498
|
timeoutMs?: number;
|
|
1137
1499
|
}) => Promise<boolean>;
|
|
1138
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* A way to reach some hosts other than a plain TCP connection. `claims` is
|
|
1503
|
+
* asked for every connection, so it must be quick. `open` returns a stream
|
|
1504
|
+
* the SSH client runs over, like a relayed socket.
|
|
1505
|
+
*/
|
|
1506
|
+
export interface PluginSshTransportProvider {
|
|
1507
|
+
claims: (hostId: number) => boolean;
|
|
1508
|
+
open: (host: {
|
|
1509
|
+
id: number;
|
|
1510
|
+
ip: string;
|
|
1511
|
+
port: number;
|
|
1512
|
+
}) => Promise<import("node:stream").Duplex>;
|
|
1513
|
+
}
|
|
1514
|
+
export interface PluginSshTransports {
|
|
1515
|
+
/** Needs ssh:transport. Removed on deactivate. */
|
|
1516
|
+
provide: (provider: PluginSshTransportProvider) => () => void;
|
|
1517
|
+
}
|
|
1139
1518
|
export interface PluginSsh {
|
|
1519
|
+
/** Other ways to reach hosts, used by every connection to them. */
|
|
1520
|
+
transports: PluginSshTransports;
|
|
1140
1521
|
/**
|
|
1141
1522
|
* Whether a TCP port answers through the host's jump host chain. Chains are
|
|
1142
1523
|
* shared and closed when idle. The acting user must be able to connect to
|
package/dist/capabilities.js
CHANGED
|
@@ -34,6 +34,8 @@ export const CAPABILITY_CATALOG = [
|
|
|
34
34
|
entry("db:core-refs", "high"),
|
|
35
35
|
entry("auth:provide", "high"),
|
|
36
36
|
entry("auth:policy", "high"),
|
|
37
|
+
entry("sync:provide", "high"),
|
|
38
|
+
entry("ssh:transport", "high"),
|
|
37
39
|
entry("device:serial", "high"),
|
|
38
40
|
entry("notify:hub", "high"),
|
|
39
41
|
entry("ui:layout", "high"),
|
package/dist/frontend.d.ts
CHANGED
|
@@ -773,6 +773,43 @@ export interface SettingsComponentProps {
|
|
|
773
773
|
setValue: (key: string, value: unknown) => void;
|
|
774
774
|
running: boolean;
|
|
775
775
|
}
|
|
776
|
+
/** What a companion starts with. Read from the plugin's own settings. */
|
|
777
|
+
export interface CompanionDefaults {
|
|
778
|
+
/** Open by itself the first time a matching tab shows. */
|
|
779
|
+
open?: boolean;
|
|
780
|
+
side?: "left" | "right" | "bottom";
|
|
781
|
+
/** Pixels of width, or height at the bottom. */
|
|
782
|
+
size?: number;
|
|
783
|
+
/** Show the edge button that opens and closes it. Default true. */
|
|
784
|
+
edgeButton?: boolean;
|
|
785
|
+
}
|
|
786
|
+
export interface CompanionProps {
|
|
787
|
+
/** The tab it sits beside. */
|
|
788
|
+
tab: PluginTabRecord;
|
|
789
|
+
host?: PluginHostRecord;
|
|
790
|
+
/** The host in the shape the connection APIs take. */
|
|
791
|
+
sshHost?: Record<string, unknown>;
|
|
792
|
+
/** The tab's surface handle (see TabHandle), read at call time. */
|
|
793
|
+
getHandle: () => TabHandle | null;
|
|
794
|
+
side: "left" | "right" | "bottom";
|
|
795
|
+
/** False while the tab is in the background or the panel is closed. */
|
|
796
|
+
isVisible: boolean;
|
|
797
|
+
close: () => void;
|
|
798
|
+
setSide: (side: "left" | "right" | "bottom") => void;
|
|
799
|
+
}
|
|
800
|
+
export interface CompanionOptions {
|
|
801
|
+
icon?: IconComponent;
|
|
802
|
+
titleKey?: string;
|
|
803
|
+
order?: number;
|
|
804
|
+
/** Tab types it applies to. Any tab with a host when empty. */
|
|
805
|
+
tabTypes?: string[];
|
|
806
|
+
when?: (tab: PluginTabRecord, host?: PluginHostRecord) => boolean;
|
|
807
|
+
/**
|
|
808
|
+
* A hook returning the starting state. It runs in the plugin's scope, so it
|
|
809
|
+
* can read the plugin's settings.
|
|
810
|
+
*/
|
|
811
|
+
useDefaults?: () => CompanionDefaults;
|
|
812
|
+
}
|
|
776
813
|
export type ActionHandler = (...args: never[]) => unknown;
|
|
777
814
|
export interface ActionSlotDefinition {
|
|
778
815
|
id: string;
|
|
@@ -1001,6 +1038,14 @@ export interface TermixApp extends TermixAppInfo {
|
|
|
1001
1038
|
registerRailItem: (item: RailItemContribution) => Disposer;
|
|
1002
1039
|
registerPanel: (id: string, component: ComponentType<PanelProps>, options?: PanelOptions) => Disposer;
|
|
1003
1040
|
registerTab: (type: string, component: ComponentType<TabProps>, options?: TabOptions) => Disposer;
|
|
1041
|
+
/**
|
|
1042
|
+
* A panel that sits beside any host tab the options match (a terminal, a
|
|
1043
|
+
* desktop, ...), bound to that tab's host. The user opens, resizes and moves
|
|
1044
|
+
* it; the tab keeps running underneath.
|
|
1045
|
+
*/
|
|
1046
|
+
registerCompanion: (id: string, component: ComponentType<CompanionProps>, options?: CompanionOptions) => Disposer;
|
|
1047
|
+
/** Opens a companion on a tab, or closes it when it is showing. */
|
|
1048
|
+
toggleCompanion: (tab: PluginTabRecord, id?: string) => void;
|
|
1004
1049
|
registerHostEditorSection: (section: HostEditorSectionContribution) => Disposer;
|
|
1005
1050
|
registerHostAction: (action: HostActionContribution) => Disposer;
|
|
1006
1051
|
registerHostProtocol: (protocol: HostProtocolContribution) => Disposer;
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
sortPlugins,
|
|
30
30
|
uploadEntry,
|
|
31
31
|
youtubeEmbedUrl
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-446UEDSK.js";
|
|
33
33
|
import {
|
|
34
34
|
ArrowDownWideNarrow,
|
|
35
35
|
BookOpen,
|
|
@@ -107,8 +107,8 @@ import {
|
|
|
107
107
|
updatePlugin,
|
|
108
108
|
uploadPlugin,
|
|
109
109
|
useConfirm
|
|
110
|
-
} from "./chunk-
|
|
111
|
-
import "./chunk-
|
|
110
|
+
} from "./chunk-LQKK2JJA.js";
|
|
111
|
+
import "./chunk-7LEJ3HYF.js";
|
|
112
112
|
import {
|
|
113
113
|
__toESM,
|
|
114
114
|
define_import_meta_env_default,
|
|
@@ -26,7 +26,7 @@ var inFlight = null;
|
|
|
26
26
|
function load() {
|
|
27
27
|
if (cache) return Promise.resolve(cache);
|
|
28
28
|
if (inFlight) return inFlight;
|
|
29
|
-
inFlight = import("./rbac-api-
|
|
29
|
+
inFlight = import("./rbac-api-LZVENREG.js").then(({ getMyPermissions }) => getMyPermissions()).then((result) => {
|
|
30
30
|
cache = {
|
|
31
31
|
permissions: result.permissions ?? [],
|
|
32
32
|
isAdmin: !!result.isAdmin
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
Trash,
|
|
8
8
|
cn,
|
|
9
9
|
useAreaPreferences
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LQKK2JJA.js";
|
|
11
11
|
import {
|
|
12
12
|
isElectron
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7LEJ3HYF.js";
|
|
14
14
|
import {
|
|
15
15
|
__commonJS,
|
|
16
16
|
__toESM,
|
|
@@ -2383,6 +2383,8 @@ var CAPABILITY_CATALOG = [
|
|
|
2383
2383
|
entry("db:core-refs", "high"),
|
|
2384
2384
|
entry("auth:provide", "high"),
|
|
2385
2385
|
entry("auth:policy", "high"),
|
|
2386
|
+
entry("sync:provide", "high"),
|
|
2387
|
+
entry("ssh:transport", "high"),
|
|
2386
2388
|
entry("device:serial", "high"),
|
|
2387
2389
|
entry("notify:hub", "high"),
|
|
2388
2390
|
entry("ui:layout", "high"),
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/ui/lib/linked-server.ts
|
|
9
9
|
init_define_import_meta_env();
|
|
10
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
10
11
|
|
|
11
12
|
// src/ui/lib/electron.ts
|
|
12
13
|
init_define_import_meta_env();
|
|
@@ -5432,7 +5433,6 @@ init_define_import_meta_env();
|
|
|
5432
5433
|
init_define_import_meta_env();
|
|
5433
5434
|
var LEGACY_SEEN = Object.freeze({
|
|
5434
5435
|
plugins: 1,
|
|
5435
|
-
"desktop-sync": 1,
|
|
5436
5436
|
preset: 1,
|
|
5437
5437
|
appearance: 1,
|
|
5438
5438
|
security: 1
|
|
@@ -6316,7 +6316,7 @@ async function updateRole(roleId, roleData) {
|
|
|
6316
6316
|
try {
|
|
6317
6317
|
const response = await rbacApi.put(`/rbac/roles/${roleId}`, roleData);
|
|
6318
6318
|
if (roleData.permissions) {
|
|
6319
|
-
const { notifyPermissionsChanged } = await import("./use-permissions-
|
|
6319
|
+
const { notifyPermissionsChanged } = await import("./use-permissions-BYCDS5PD.js");
|
|
6320
6320
|
notifyPermissionsChanged();
|
|
6321
6321
|
}
|
|
6322
6322
|
return response.data;
|
|
@@ -6898,7 +6898,7 @@ function createRemoteOriginApiInstance(path) {
|
|
|
6898
6898
|
});
|
|
6899
6899
|
instance.interceptors.request.use(
|
|
6900
6900
|
async (config) => {
|
|
6901
|
-
const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-
|
|
6901
|
+
const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-OUF3POT7.js");
|
|
6902
6902
|
const linked = await getLinkedSession2();
|
|
6903
6903
|
config.baseURL = linked ? `${linked.serverUrl}${path}` : "http://no-server-configured";
|
|
6904
6904
|
if (config.headers.set) {
|
|
@@ -7287,6 +7287,22 @@ function notifySyncChanged() {
|
|
|
7287
7287
|
cached = null;
|
|
7288
7288
|
window.dispatchEvent(new CustomEvent(SYNC_CHANGED_EVENT));
|
|
7289
7289
|
}
|
|
7290
|
+
function useDesktopLinked() {
|
|
7291
|
+
const [linked, setLinked] = useState2(false);
|
|
7292
|
+
useEffect2(() => {
|
|
7293
|
+
let live = true;
|
|
7294
|
+
const read = () => void getLinkedSession().then((session) => {
|
|
7295
|
+
if (live) setLinked(!!session);
|
|
7296
|
+
});
|
|
7297
|
+
read();
|
|
7298
|
+
window.addEventListener(SYNC_CHANGED_EVENT, read);
|
|
7299
|
+
return () => {
|
|
7300
|
+
live = false;
|
|
7301
|
+
window.removeEventListener(SYNC_CHANGED_EVENT, read);
|
|
7302
|
+
};
|
|
7303
|
+
}, []);
|
|
7304
|
+
return linked;
|
|
7305
|
+
}
|
|
7290
7306
|
|
|
7291
7307
|
export {
|
|
7292
7308
|
getPluginStoreState,
|
|
@@ -7332,6 +7348,7 @@ export {
|
|
|
7332
7348
|
websocketAuthProtocols,
|
|
7333
7349
|
getLinkedSession,
|
|
7334
7350
|
notifySyncChanged,
|
|
7351
|
+
useDesktopLinked,
|
|
7335
7352
|
resolveConnectionOrigin,
|
|
7336
7353
|
buildOriginWsUrl,
|
|
7337
7354
|
getAllServerStatuses,
|