@termix-ssh/plugin-sdk 1.0.13 → 1.0.17
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 +49 -0
- package/PLUGIN-GUIDE.md +104 -69
- package/dist/backend.d.ts +500 -1
- package/dist/backend.js +7 -0
- package/dist/capabilities.js +4 -0
- package/dist/frontend.d.ts +45 -0
- package/dist/host/{PluginsTab-OXQQ7M4I.js → PluginsTab-YAGYRBIE.js} +3 -3
- package/dist/host/{chunk-T2PZWXFJ.js → chunk-2MU6WNSX.js} +1 -1
- package/dist/host/{chunk-54KCK6IZ.js → chunk-446UEDSK.js} +6 -2
- package/dist/host/{chunk-CN5UBTBP.js → chunk-7LEJ3HYF.js} +49 -3
- package/dist/host/{chunk-CALNJCHN.js → chunk-J2AILBXM.js} +561 -400
- package/dist/host/{chunk-ERGN4YW6.js → chunk-LQKK2JJA.js} +5 -1
- package/dist/host/core-classes.json +1 -1
- package/dist/host/{linked-server-5VFO4XQB.js → linked-server-OUF3POT7.js} +5 -3
- package/dist/host/{rbac-api-5YMBJAO5.js → rbac-api-LZVENREG.js} +1 -1
- package/dist/host/testing-host.js +4 -4
- package/dist/host/types/src/types/index.d.ts +10 -0
- package/dist/host/types/src/types/onboarding.d.ts +0 -1
- package/dist/host/types/src/types/ui-types.d.ts +3 -1
- package/dist/host/types/src/ui/api/auth-methods-api.d.ts +2 -0
- package/dist/host/types/src/ui/api/settings-api.d.ts +6 -0
- package/dist/host/types/src/ui/api/ssh-host-management-api.d.ts +6 -0
- 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/main-axios.d.ts +2 -2
- 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 +4 -8
- 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/types/src/ui/sidebar/AuthEnrollmentSections.d.ts +4 -1
- package/dist/host/ui.js +171 -820
- package/dist/host/{use-permissions-ISEDRY3J.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 +26 -2
- package/dist/testing.js +89 -1
- package/dist/vitest-preset.js +23 -0
- package/package.json +1 -1
- package/schema/manifest.schema.json +4 -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/manifest.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDoc
|
|
|
20
20
|
* "^1.2" needs 1.2 or later). engine.termix is checked against the core
|
|
21
21
|
* version by the server.
|
|
22
22
|
*/
|
|
23
|
-
export declare const PLUGIN_API_VERSION = "1.
|
|
23
|
+
export declare const PLUGIN_API_VERSION = "1.9.0";
|
|
24
24
|
/** The API major, the default engine.api for a new plugin. */
|
|
25
25
|
export declare const SUPPORTED_PLUGIN_API_VERSION: string;
|
|
26
26
|
/** Whether this build's plugin API satisfies a manifest's engine.api. */
|
package/dist/manifest.js
CHANGED
|
@@ -22,7 +22,7 @@ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDoc
|
|
|
22
22
|
* "^1.2" needs 1.2 or later). engine.termix is checked against the core
|
|
23
23
|
* version by the server.
|
|
24
24
|
*/
|
|
25
|
-
export const PLUGIN_API_VERSION = "1.
|
|
25
|
+
export const PLUGIN_API_VERSION = "1.9.0";
|
|
26
26
|
/** The API major, the default engine.api for a new plugin. */
|
|
27
27
|
export const SUPPORTED_PLUGIN_API_VERSION = String(semver.major(PLUGIN_API_VERSION));
|
|
28
28
|
/** Whether this build's plugin API satisfies a manifest's engine.api. */
|
package/dist/testing.d.ts
CHANGED
|
@@ -23,9 +23,9 @@ export interface FakeWsRoute {
|
|
|
23
23
|
options?: PluginWebSocketOptions;
|
|
24
24
|
}
|
|
25
25
|
import type { PluginLoginMethod, PluginVerifiedIdentity, PluginSecondFactor, PluginSshAuthProvider, PluginSshConnectOptions, PluginSshHost, PluginHostSummary, PluginShareableUser, PluginShareableRole, PluginSshKeyCredential, PluginSshKeyCredentialInput } from "./backend.js";
|
|
26
|
-
import type { SyncEntityRegistration } from "./backend.js";
|
|
26
|
+
import type { PluginSyncHost, SyncEntityRegistration } from "./backend.js";
|
|
27
27
|
import type { PluginDatabase } from "./backend.js";
|
|
28
|
-
import type { PluginExternalClientRequest, PluginProtocolTarget, PluginHostProbeTarget, PluginHostStatusEntry, PluginActivityProvider, PluginHostStatusProvider, PluginNotification, PluginNotificationChannel, PluginNotifyHub, PluginFetchInit, PluginBinarySpec, PluginTlsCertificateInfo, PluginTlsStatus, PluginSummary, PluginKeyboardInteractiveHandler, PluginProcessHandle, PluginProcessOptions } from "./backend.js";
|
|
28
|
+
import type { PluginExternalClientRequest, PluginProtocolTarget, PluginHostProbeTarget, PluginHostStatusEntry, PluginActivityProvider, PluginHostLockProvider, PluginAuthPolicyProvider, PluginCredentialSummary, PluginHostShareGrant, PluginHostStatusProvider, PluginNotification, PluginNotificationChannel, PluginNotifyHub, PluginFetchInit, PluginBinarySpec, PluginTlsCertificateInfo, PluginTlsStatus, PluginSummary, PluginKeyboardInteractiveHandler, PluginProcessHandle, PluginProcessOptions } from "./backend.js";
|
|
29
29
|
/** A timer registered through ctx.schedule, run by hand with runScheduled. */
|
|
30
30
|
export interface FakeScheduledJob {
|
|
31
31
|
kind: "every" | "after";
|
|
@@ -35,6 +35,8 @@ export interface FakeScheduledJob {
|
|
|
35
35
|
}
|
|
36
36
|
export interface FakeContextOptions {
|
|
37
37
|
pluginId?: string;
|
|
38
|
+
/** What ctx.sync.host answers, for a sync provider's tests. */
|
|
39
|
+
syncHost?: Partial<PluginSyncHost>;
|
|
38
40
|
manifest?: Partial<PluginManifest>;
|
|
39
41
|
/** Acting user returned by currentActor and used by asUser. */
|
|
40
42
|
actor?: string;
|
|
@@ -75,6 +77,8 @@ export interface FakeContextOptions {
|
|
|
75
77
|
protocolTargets?: Record<string, PluginProtocolTarget>;
|
|
76
78
|
/** What ctx.credentials.listSshKeys answers. */
|
|
77
79
|
sshKeyCredentials?: PluginSshKeyCredential[];
|
|
80
|
+
/** What ctx.credentials.list answers. */
|
|
81
|
+
credentialSummaries?: PluginCredentialSummary[];
|
|
78
82
|
/** What ctx.desktop.available() answers. Defaults to false. */
|
|
79
83
|
desktopAvailable?: boolean;
|
|
80
84
|
/** What ctx.desktop.local is. Calls throw when this is not given. */
|
|
@@ -122,6 +126,11 @@ export interface FakeContextOptions {
|
|
|
122
126
|
baseUrl?: string;
|
|
123
127
|
/** What ctx.auth.countLinkedUsers answers, by provider. Defaults to 0. */
|
|
124
128
|
linkedUsers?: Record<string, number>;
|
|
129
|
+
/** Second factors from other plugins that ctx.auth.listSecondFactors adds. */
|
|
130
|
+
secondFactors?: {
|
|
131
|
+
id: string;
|
|
132
|
+
pluginId: string;
|
|
133
|
+
}[];
|
|
125
134
|
/** Failures before ctx.auth.loginRateLimit locks a key. Defaults to 5. */
|
|
126
135
|
loginAttemptLimit?: number;
|
|
127
136
|
/** What ctx.users.getLocale answers, by user id. A missing user answers null. */
|
|
@@ -167,6 +176,8 @@ export interface FakeAuthRegistrations {
|
|
|
167
176
|
purpose: string;
|
|
168
177
|
ttlSeconds?: number;
|
|
169
178
|
}>;
|
|
179
|
+
/** Providers passed to ctx.auth.policy.provide that are still registered. */
|
|
180
|
+
policies: PluginAuthPolicyProvider[];
|
|
170
181
|
}
|
|
171
182
|
export interface FakePluginContext {
|
|
172
183
|
ctx: PluginContext;
|
|
@@ -180,6 +191,8 @@ export interface FakePluginContext {
|
|
|
180
191
|
hostStatusProviders: PluginHostStatusProvider[];
|
|
181
192
|
/** Every provider passed to ctx.hosts.activity.provide that is still registered. */
|
|
182
193
|
activityProviders: PluginActivityProvider[];
|
|
194
|
+
/** Every provider passed to ctx.hosts.locks.provide that is still registered. */
|
|
195
|
+
hostLockProviders: PluginHostLockProvider[];
|
|
183
196
|
/** Topics emitted through ctx.events.emit, in order. */
|
|
184
197
|
emitted: Array<{
|
|
185
198
|
topic: string;
|
|
@@ -215,6 +228,11 @@ export interface FakePluginContext {
|
|
|
215
228
|
permissionLevel: string;
|
|
216
229
|
durationHours?: number;
|
|
217
230
|
}>;
|
|
231
|
+
/** Every ctx.hosts.setShares call, in order. */
|
|
232
|
+
hostShareSets: Array<{
|
|
233
|
+
hostId: number;
|
|
234
|
+
grants: PluginHostShareGrant[];
|
|
235
|
+
}>;
|
|
218
236
|
/** Everything registered through ctx.auth. */
|
|
219
237
|
auth: FakeAuthRegistrations;
|
|
220
238
|
/** Every ctx.desktop.openIsolatedWindow call, in order. */
|
|
@@ -330,6 +348,8 @@ export interface FakePluginContext {
|
|
|
330
348
|
export declare function createFakeContext(options?: FakeContextOptions): FakePluginContext;
|
|
331
349
|
export interface MockContextOptions {
|
|
332
350
|
pluginId?: string;
|
|
351
|
+
/** What ctx.sync.host answers, for a sync provider's tests. */
|
|
352
|
+
syncHost?: Partial<PluginSyncHost>;
|
|
333
353
|
manifest?: Partial<PluginManifest>;
|
|
334
354
|
/** Capabilities the plugin is granted. Anything else throws. */
|
|
335
355
|
capabilities?: string[];
|
|
@@ -364,6 +384,8 @@ export interface MockContextOptions {
|
|
|
364
384
|
protocolTargets?: Record<string, PluginProtocolTarget>;
|
|
365
385
|
/** What ctx.credentials.listSshKeys serves. */
|
|
366
386
|
sshKeyCredentials?: PluginSshKeyCredential[];
|
|
387
|
+
/** What ctx.credentials.list serves. */
|
|
388
|
+
credentialSummaries?: PluginCredentialSummary[];
|
|
367
389
|
/** What ctx.desktop.available() answers. */
|
|
368
390
|
desktopAvailable?: boolean;
|
|
369
391
|
/** What ctx.desktop.local is. See FakeContextOptions. */
|
|
@@ -402,6 +424,8 @@ export interface MockContextOptions {
|
|
|
402
424
|
/** See FakeContextOptions. */
|
|
403
425
|
loginAttemptLimit?: number;
|
|
404
426
|
/** See FakeContextOptions. */
|
|
427
|
+
secondFactors?: FakeContextOptions["secondFactors"];
|
|
428
|
+
/** See FakeContextOptions. */
|
|
405
429
|
locales?: Record<string, string>;
|
|
406
430
|
}
|
|
407
431
|
export interface MockPluginContext extends FakePluginContext {
|
package/dist/testing.js
CHANGED
|
@@ -100,7 +100,7 @@ function emptyHostRecord(id, userId) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
function applyHostInput(record, input) {
|
|
103
|
-
const { tags, pluginSettings: _settings, forceKeyboardInteractive: _fki, ...rest } = input;
|
|
103
|
+
const { tags, pluginSettings: _settings, pluginEnable: _pluginEnable, forceKeyboardInteractive: _fki, password: _password, key: _key, keyPassword: _keyPassword, keyType: _keyType, sudoPassword: _sudoPassword, protocolAuth: _protocolAuth, ...rest } = input;
|
|
104
104
|
const next = { ...record };
|
|
105
105
|
for (const [key, value] of Object.entries(rest)) {
|
|
106
106
|
if (value !== undefined)
|
|
@@ -126,6 +126,7 @@ export function createFakeContext(options = {}) {
|
|
|
126
126
|
const emitted = [];
|
|
127
127
|
const hostStatusProviders = [];
|
|
128
128
|
const activityProviders = [];
|
|
129
|
+
const hostLockProviders = [];
|
|
129
130
|
const kv = new Map();
|
|
130
131
|
const tables = [];
|
|
131
132
|
const syncEntities = [];
|
|
@@ -138,6 +139,7 @@ export function createFakeContext(options = {}) {
|
|
|
138
139
|
const settingsValidators = new Set();
|
|
139
140
|
const sshConnections = [];
|
|
140
141
|
const hostShares = [];
|
|
142
|
+
const hostShareSets = [];
|
|
141
143
|
const desktopWindows = [];
|
|
142
144
|
const externalClientLaunches = [];
|
|
143
145
|
const credentialReads = [];
|
|
@@ -198,6 +200,7 @@ export function createFakeContext(options = {}) {
|
|
|
198
200
|
revokedSessions: [],
|
|
199
201
|
loginFailures: new Map(),
|
|
200
202
|
userTokens: [],
|
|
203
|
+
policies: [],
|
|
201
204
|
};
|
|
202
205
|
const sshClient = options.sshClient ?? {};
|
|
203
206
|
let actor = options.actor;
|
|
@@ -298,6 +301,12 @@ export function createFakeContext(options = {}) {
|
|
|
298
301
|
registerEntity: (entity) => {
|
|
299
302
|
syncEntities.push(entity);
|
|
300
303
|
},
|
|
304
|
+
provide: () => () => { },
|
|
305
|
+
get host() {
|
|
306
|
+
if (!options.syncHost)
|
|
307
|
+
throw new Error("Pass syncHost to use ctx.sync.host");
|
|
308
|
+
return options.syncHost;
|
|
309
|
+
},
|
|
301
310
|
},
|
|
302
311
|
registry: {
|
|
303
312
|
provide: (key, value) => {
|
|
@@ -526,6 +535,10 @@ export function createFakeContext(options = {}) {
|
|
|
526
535
|
hostShares.push({ hostId, targets, permissionLevel, durationHours });
|
|
527
536
|
return { hostId, shared: true };
|
|
528
537
|
},
|
|
538
|
+
setShares: async (hostId, grants) => {
|
|
539
|
+
hostShareSets.push({ hostId, grants: [...grants] });
|
|
540
|
+
return { hostId, shared: true };
|
|
541
|
+
},
|
|
529
542
|
listUsers: async () => options.shareableUsers ?? [],
|
|
530
543
|
listRoles: async () => options.shareableRoles ?? [],
|
|
531
544
|
trackSession: (hostId) => {
|
|
@@ -555,6 +568,20 @@ export function createFakeContext(options = {}) {
|
|
|
555
568
|
};
|
|
556
569
|
},
|
|
557
570
|
},
|
|
571
|
+
folders: {
|
|
572
|
+
ensure: async () => { },
|
|
573
|
+
removeEmpty: async () => true,
|
|
574
|
+
},
|
|
575
|
+
locks: {
|
|
576
|
+
provide: (provider) => {
|
|
577
|
+
hostLockProviders.push(provider);
|
|
578
|
+
return () => {
|
|
579
|
+
const at = hostLockProviders.indexOf(provider);
|
|
580
|
+
if (at >= 0)
|
|
581
|
+
hostLockProviders.splice(at, 1);
|
|
582
|
+
};
|
|
583
|
+
},
|
|
584
|
+
},
|
|
558
585
|
status: {
|
|
559
586
|
provide: (provider) => {
|
|
560
587
|
hostStatusProviders.push(provider);
|
|
@@ -616,6 +643,7 @@ export function createFakeContext(options = {}) {
|
|
|
616
643
|
tcpProbe: async (host, port) => options.reachable?.(host, port) ?? false,
|
|
617
644
|
},
|
|
618
645
|
ssh: {
|
|
646
|
+
transports: { provide: () => () => { } },
|
|
619
647
|
probe: async (hostId, probeOptions) => options.reachable?.(hostId, probeOptions?.port) ?? false,
|
|
620
648
|
connect: async (host, connectOptions) => {
|
|
621
649
|
sshConnections.push({ host, options: connectOptions });
|
|
@@ -761,6 +789,23 @@ export function createFakeContext(options = {}) {
|
|
|
761
789
|
expiresAt: new Date(Date.now() + ttl * 1000).toISOString(),
|
|
762
790
|
};
|
|
763
791
|
},
|
|
792
|
+
policy: {
|
|
793
|
+
provide: (provider) => {
|
|
794
|
+
auth.policies.push(provider);
|
|
795
|
+
return () => {
|
|
796
|
+
const at = auth.policies.indexOf(provider);
|
|
797
|
+
if (at >= 0)
|
|
798
|
+
auth.policies.splice(at, 1);
|
|
799
|
+
};
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
listSecondFactors: () => [
|
|
803
|
+
...auth.secondFactors.map((factor) => ({
|
|
804
|
+
id: factor.id,
|
|
805
|
+
pluginId,
|
|
806
|
+
})),
|
|
807
|
+
...(options.secondFactors ?? []),
|
|
808
|
+
],
|
|
764
809
|
},
|
|
765
810
|
users: {
|
|
766
811
|
getLocale: async (userId) => options.locales?.[userId] ?? null,
|
|
@@ -778,6 +823,7 @@ export function createFakeContext(options = {}) {
|
|
|
778
823
|
local: options.desktopLocal ?? unavailableDesktopLocal(),
|
|
779
824
|
},
|
|
780
825
|
credentials: {
|
|
826
|
+
list: async () => [...(options.credentialSummaries ?? [])],
|
|
781
827
|
listSshKeys: async () => [
|
|
782
828
|
...(options.sshKeyCredentials ?? []),
|
|
783
829
|
...createdSshKeys.map((key) => ({
|
|
@@ -951,6 +997,7 @@ export function createFakeContext(options = {}) {
|
|
|
951
997
|
disposals,
|
|
952
998
|
hostStatusProviders,
|
|
953
999
|
activityProviders,
|
|
1000
|
+
hostLockProviders,
|
|
954
1001
|
emitted,
|
|
955
1002
|
kv,
|
|
956
1003
|
tables,
|
|
@@ -961,6 +1008,7 @@ export function createFakeContext(options = {}) {
|
|
|
961
1008
|
coreSettings,
|
|
962
1009
|
sshConnections,
|
|
963
1010
|
hostShares,
|
|
1011
|
+
hostShareSets,
|
|
964
1012
|
auth,
|
|
965
1013
|
desktopWindows,
|
|
966
1014
|
externalClientLaunches,
|
|
@@ -1070,6 +1118,7 @@ export function createMockCtx(options = {}) {
|
|
|
1070
1118
|
const checked = [];
|
|
1071
1119
|
const base = createFakeContext({
|
|
1072
1120
|
pluginId: options.pluginId,
|
|
1121
|
+
syncHost: options.syncHost,
|
|
1073
1122
|
actor: options.actor,
|
|
1074
1123
|
settings: options.settings,
|
|
1075
1124
|
coreSettings: options.coreSettings,
|
|
@@ -1085,6 +1134,7 @@ export function createMockCtx(options = {}) {
|
|
|
1085
1134
|
services: options.services,
|
|
1086
1135
|
protocolTargets: options.protocolTargets,
|
|
1087
1136
|
sshKeyCredentials: options.sshKeyCredentials,
|
|
1137
|
+
credentialSummaries: options.credentialSummaries,
|
|
1088
1138
|
desktopAvailable: options.desktopAvailable,
|
|
1089
1139
|
desktopLocal: options.desktopLocal,
|
|
1090
1140
|
auditEntries: options.auditEntries,
|
|
@@ -1102,6 +1152,7 @@ export function createMockCtx(options = {}) {
|
|
|
1102
1152
|
baseUrl: options.baseUrl,
|
|
1103
1153
|
linkedUsers: options.linkedUsers,
|
|
1104
1154
|
loginAttemptLimit: options.loginAttemptLimit,
|
|
1155
|
+
secondFactors: options.secondFactors,
|
|
1105
1156
|
locales: options.locales,
|
|
1106
1157
|
manifest: {
|
|
1107
1158
|
capabilities: options.capabilities ?? [],
|
|
@@ -1268,6 +1319,10 @@ export function createMockCtx(options = {}) {
|
|
|
1268
1319
|
require("hosts:write");
|
|
1269
1320
|
return ctx.hosts.share(hostId, targets, permissionLevel, durationHours);
|
|
1270
1321
|
},
|
|
1322
|
+
setShares: async (hostId, grants) => {
|
|
1323
|
+
require("hosts:write");
|
|
1324
|
+
return ctx.hosts.setShares(hostId, grants);
|
|
1325
|
+
},
|
|
1271
1326
|
listUsers: async () => {
|
|
1272
1327
|
require("hosts:write");
|
|
1273
1328
|
return ctx.hosts.listUsers();
|
|
@@ -1298,6 +1353,22 @@ export function createMockCtx(options = {}) {
|
|
|
1298
1353
|
return ctx.hosts.activity.provide(provider);
|
|
1299
1354
|
},
|
|
1300
1355
|
},
|
|
1356
|
+
folders: {
|
|
1357
|
+
ensure: async (name, style) => {
|
|
1358
|
+
require("hosts:write");
|
|
1359
|
+
return ctx.hosts.folders.ensure(name, style);
|
|
1360
|
+
},
|
|
1361
|
+
removeEmpty: async (name) => {
|
|
1362
|
+
require("hosts:write");
|
|
1363
|
+
return ctx.hosts.folders.removeEmpty(name);
|
|
1364
|
+
},
|
|
1365
|
+
},
|
|
1366
|
+
locks: {
|
|
1367
|
+
provide: (provider) => {
|
|
1368
|
+
require("hosts:lock");
|
|
1369
|
+
return ctx.hosts.locks.provide(provider);
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1301
1372
|
status: {
|
|
1302
1373
|
provide: (provider) => {
|
|
1303
1374
|
require("hosts:read");
|
|
@@ -1324,6 +1395,12 @@ export function createMockCtx(options = {}) {
|
|
|
1324
1395
|
},
|
|
1325
1396
|
ssh: {
|
|
1326
1397
|
...ctx.ssh,
|
|
1398
|
+
transports: {
|
|
1399
|
+
provide: (provider) => {
|
|
1400
|
+
require("ssh:transport");
|
|
1401
|
+
return ctx.ssh.transports.provide(provider);
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1327
1404
|
probe: async (hostId, probeOptions) => {
|
|
1328
1405
|
require("ssh:connect");
|
|
1329
1406
|
return ctx.ssh.probe(hostId, probeOptions);
|
|
@@ -1423,6 +1500,13 @@ export function createMockCtx(options = {}) {
|
|
|
1423
1500
|
require("users:impersonate");
|
|
1424
1501
|
return ctx.auth.issueUserToken(userId, tokenOptions);
|
|
1425
1502
|
},
|
|
1503
|
+
policy: {
|
|
1504
|
+
provide: (provider) => {
|
|
1505
|
+
require("auth:policy");
|
|
1506
|
+
return ctx.auth.policy.provide(provider);
|
|
1507
|
+
},
|
|
1508
|
+
},
|
|
1509
|
+
listSecondFactors: () => ctx.auth.listSecondFactors(),
|
|
1426
1510
|
},
|
|
1427
1511
|
users: {
|
|
1428
1512
|
getLocale: async (userId) => {
|
|
@@ -1458,6 +1542,10 @@ export function createMockCtx(options = {}) {
|
|
|
1458
1542
|
local: gateDesktopLocal(ctx.desktop.local, () => require("desktop:local")),
|
|
1459
1543
|
},
|
|
1460
1544
|
credentials: {
|
|
1545
|
+
list: async () => {
|
|
1546
|
+
require("credentials:use");
|
|
1547
|
+
return ctx.credentials.list();
|
|
1548
|
+
},
|
|
1461
1549
|
listSshKeys: async () => {
|
|
1462
1550
|
require("credentials:use");
|
|
1463
1551
|
return ctx.credentials.listSshKeys();
|
package/dist/vitest-preset.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Two projects: tests/backend runs in node, tests/frontend in jsdom.
|
|
10
10
|
*/
|
|
11
|
+
import { createRequire } from "node:module";
|
|
11
12
|
import fs from "node:fs";
|
|
12
13
|
import path from "node:path";
|
|
13
14
|
import { fileURLToPath } from "node:url";
|
|
@@ -84,6 +85,28 @@ export function pluginVitestConfig(configUrl, options = {}) {
|
|
|
84
85
|
// Vite must be allowed to read it, and React must come from the plugin.
|
|
85
86
|
const sdkDir = fs.realpathSync(fileURLToPath(new URL("..", import.meta.url)));
|
|
86
87
|
const server = { fs: { allow: [pluginDir, sdkDir] } };
|
|
88
|
+
// dedupe alone is not enough: react-dom or the testing library loaded from
|
|
89
|
+
// the linked SDK's own node_modules pull in a second React, and hooks then
|
|
90
|
+
// see no renderer.
|
|
91
|
+
if (!sdkDir.startsWith(pluginDir)) {
|
|
92
|
+
const pluginRequire = createRequire(path.join(pluginDir, "package.json"));
|
|
93
|
+
for (const name of [
|
|
94
|
+
"react",
|
|
95
|
+
"react-dom",
|
|
96
|
+
"@testing-library/react",
|
|
97
|
+
"react-i18next",
|
|
98
|
+
"i18next",
|
|
99
|
+
]) {
|
|
100
|
+
if (alias[name])
|
|
101
|
+
continue;
|
|
102
|
+
try {
|
|
103
|
+
alias[name] = path.dirname(pluginRequire.resolve(`${name}/package.json`));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Not installed in the plugin: leave it to normal resolution.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
87
110
|
const resolve = {
|
|
88
111
|
alias,
|
|
89
112
|
dedupe: ["react", "react-dom", "react-i18next", "i18next"],
|
package/package.json
CHANGED
|
@@ -151,6 +151,9 @@
|
|
|
151
151
|
"process:spawn",
|
|
152
152
|
"users:write",
|
|
153
153
|
"auth:provide",
|
|
154
|
+
"auth:policy",
|
|
155
|
+
"sync:provide",
|
|
156
|
+
"ssh:transport",
|
|
154
157
|
"system:tls",
|
|
155
158
|
"device:serial",
|
|
156
159
|
"hosts:write",
|
|
@@ -167,6 +170,7 @@
|
|
|
167
170
|
"network:probe",
|
|
168
171
|
"hosts:status-provide",
|
|
169
172
|
"hosts:activity-provide",
|
|
173
|
+
"hosts:lock",
|
|
170
174
|
"desktop:window",
|
|
171
175
|
"desktop:local",
|
|
172
176
|
"users:impersonate",
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
export type SyncState = "idle" | "syncing" | "offline" | "error" | "signed_out";
|
|
2
|
-
export interface SyncProxyHeader {
|
|
3
|
-
name: string;
|
|
4
|
-
value: string;
|
|
5
|
-
/** A saved header whose value is not sent back. */
|
|
6
|
-
set?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface SyncBasicAuth {
|
|
9
|
-
username: string;
|
|
10
|
-
password: string;
|
|
11
|
-
set?: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface SyncEntityInfo {
|
|
14
|
-
type: string;
|
|
15
|
-
owner: string;
|
|
16
|
-
ownerName: string | null;
|
|
17
|
-
readOnly: boolean;
|
|
18
|
-
onServer: boolean;
|
|
19
|
-
onDevice: boolean;
|
|
20
|
-
enabled: boolean;
|
|
21
|
-
}
|
|
22
|
-
export interface SyncStatus {
|
|
23
|
-
linked: boolean;
|
|
24
|
-
serverUrl?: string;
|
|
25
|
-
serverName?: string | null;
|
|
26
|
-
serverVersion?: string | null;
|
|
27
|
-
account?: {
|
|
28
|
-
username?: string;
|
|
29
|
-
isAdmin?: boolean;
|
|
30
|
-
roles?: string[];
|
|
31
|
-
permissions?: string[];
|
|
32
|
-
};
|
|
33
|
-
status?: SyncState;
|
|
34
|
-
lastError?: string | null;
|
|
35
|
-
linkedAt?: string;
|
|
36
|
-
lastSyncAt?: string | null;
|
|
37
|
-
pending?: number;
|
|
38
|
-
conflicts?: number;
|
|
39
|
-
errors?: number;
|
|
40
|
-
firstSyncDone?: boolean;
|
|
41
|
-
allowInvalidCertificate?: boolean;
|
|
42
|
-
customHeaders?: SyncProxyHeader[];
|
|
43
|
-
basicAuth?: SyncBasicAuth | null;
|
|
44
|
-
entities: SyncEntityInfo[];
|
|
45
|
-
}
|
|
46
|
-
export type ProbeProblem = "invalid_url" | "unreachable" | "tls_untrusted" | "basic_auth_required" | "proxy_login" | "not_termix" | "version_mismatch" | "different_account";
|
|
47
|
-
export interface ProbeResult {
|
|
48
|
-
ok: boolean;
|
|
49
|
-
serverUrl?: string;
|
|
50
|
-
name?: string;
|
|
51
|
-
version?: string | null;
|
|
52
|
-
problem?: ProbeProblem;
|
|
53
|
-
message?: string;
|
|
54
|
-
}
|
|
55
|
-
export interface ServerTarget {
|
|
56
|
-
serverUrl: string;
|
|
57
|
-
customHeaders?: SyncProxyHeader[];
|
|
58
|
-
basicAuth?: SyncBasicAuth | null;
|
|
59
|
-
allowInvalidCertificate?: boolean;
|
|
60
|
-
}
|
|
61
|
-
export interface SyncConflictItem {
|
|
62
|
-
id: number;
|
|
63
|
-
entityType: string;
|
|
64
|
-
syncId: string;
|
|
65
|
-
name: string;
|
|
66
|
-
createdAt: string;
|
|
67
|
-
}
|
|
68
|
-
export interface SyncErrorItem {
|
|
69
|
-
entityType: string;
|
|
70
|
-
syncId: string;
|
|
71
|
-
reason: string | null;
|
|
72
|
-
}
|
|
73
|
-
export declare function getSyncStatus(): Promise<SyncStatus>;
|
|
74
|
-
export declare function probeServer(target: ServerTarget): Promise<ProbeResult>;
|
|
75
|
-
export declare function getLinkPreview(): Promise<Record<string, number>>;
|
|
76
|
-
export declare function completeLink(input: ServerTarget & {
|
|
77
|
-
token: string;
|
|
78
|
-
mode: "merge" | "replace";
|
|
79
|
-
serverName?: string;
|
|
80
|
-
}): Promise<SyncStatus>;
|
|
81
|
-
export declare function reloginLink(token: string): Promise<SyncStatus>;
|
|
82
|
-
export declare function unlinkServer(keepData: boolean): Promise<SyncStatus>;
|
|
83
|
-
export declare function syncNow(): Promise<SyncStatus>;
|
|
84
|
-
export declare function updateSyncSettings(settings: {
|
|
85
|
-
disabledTypes?: string[];
|
|
86
|
-
customHeaders?: SyncProxyHeader[];
|
|
87
|
-
basicAuth?: SyncBasicAuth | null;
|
|
88
|
-
allowInvalidCertificate?: boolean;
|
|
89
|
-
}): Promise<SyncStatus>;
|
|
90
|
-
export declare function getSyncConflicts(): Promise<SyncConflictItem[]>;
|
|
91
|
-
export declare function settleSyncConflict(id: number, keep: "mine" | "server"): Promise<void>;
|
|
92
|
-
export declare function getSyncErrors(): Promise<SyncErrorItem[]>;
|
|
93
|
-
export declare function retrySyncErrors(): Promise<SyncStatus>;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
interface ElectronLoginFormProps {
|
|
3
|
-
serverUrl: string;
|
|
4
|
-
onAuthSuccess: (token: string | null) => void | Promise<void>;
|
|
5
|
-
onChangeServer: () => void;
|
|
6
|
-
targetPurpose?: "local" | "link";
|
|
7
|
-
}
|
|
8
|
-
export declare function ElectronLoginForm({ serverUrl, onAuthSuccess, onChangeServer, targetPurpose, }: ElectronLoginFormProps): React.JSX.Element;
|
|
9
|
-
export {};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type SyncStatus } from "../api/sync-api";
|
|
2
|
-
export declare function subscribeSyncStatus(listener: () => void): () => void;
|
|
3
|
-
export declare function getSyncStatusSnapshot(): SyncStatus | null;
|
|
4
|
-
/** Takes a status a request already returned, and checks again soon. */
|
|
5
|
-
export declare function setSyncStatus(next: SyncStatus): void;
|
|
6
|
-
export declare function refreshSyncStatus(): void;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { type SyncStatus } from "../../api/sync-api";
|
|
2
|
-
interface LinkServerDialogProps {
|
|
3
|
-
open: boolean;
|
|
4
|
-
onOpenChange: (open: boolean) => void;
|
|
5
|
-
onLinked: (status: SyncStatus) => void;
|
|
6
|
-
/** Sign back in to the server this desktop is already linked to. */
|
|
7
|
-
relogin?: {
|
|
8
|
-
serverUrl: string;
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export declare function LinkServerDialog({ open, onOpenChange, onLinked, relogin, }: LinkServerDialogProps): import("react").JSX.Element;
|
|
12
|
-
export {};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { SyncBasicAuth, SyncProxyHeader } from "../../api/sync-api";
|
|
2
|
-
interface ProxySettingsFieldsProps {
|
|
3
|
-
customHeaders: SyncProxyHeader[];
|
|
4
|
-
basicAuth: SyncBasicAuth | null;
|
|
5
|
-
onChange: (next: {
|
|
6
|
-
customHeaders?: SyncProxyHeader[];
|
|
7
|
-
basicAuth?: SyncBasicAuth | null;
|
|
8
|
-
}) => void;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* What a reverse proxy in front of the server wants: extra headers (a
|
|
12
|
-
* Cloudflare Access service token, a gateway key) and basic auth.
|
|
13
|
-
*/
|
|
14
|
-
export declare function ProxySettingsFields({ customHeaders, basicAuth, onChange, }: ProxySettingsFieldsProps): import("react").JSX.Element;
|
|
15
|
-
export {};
|