@termix-ssh/plugin-sdk 1.0.12 → 1.0.15
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 +42 -0
- package/PLUGIN-GUIDE.md +106 -59
- package/dist/backend.d.ts +534 -1
- package/dist/backend.js +7 -0
- package/dist/capabilities.js +7 -0
- package/dist/db.js +1 -0
- package/dist/desktop.d.ts +0 -243
- package/dist/frontend.d.ts +23 -2
- package/dist/host/{PluginsTab-EV4MSGY6.js → PluginsTab-S33WIIGC.js} +511 -178
- package/dist/host/{chunk-AWR6FYDO.js → chunk-2IV3VX7F.js} +5552 -5550
- package/dist/host/{chunk-RUPLD6RF.js → chunk-3L45NNT5.js} +29 -84
- package/dist/host/{chunk-VEWOYAMZ.js → chunk-57DJU2ZA.js} +1 -1
- package/dist/host/{chunk-XKA3DBB2.js → chunk-V44FCJBQ.js} +135 -54
- package/dist/host/{chunk-X4QZKO5G.js → chunk-VFTB2RDP.js} +54 -5
- package/dist/host/core-classes.json +1 -1
- package/dist/host/{linked-server-B4NEG4MA.js → linked-server-SCJH7DSL.js} +1 -1
- package/dist/host/{rbac-api-OUO3LUOC.js → rbac-api-KA5IPQQL.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 -2
- package/dist/host/types/src/types/ui-types.d.ts +3 -1
- package/dist/host/types/src/ui/api/audit-log-api.d.ts +0 -10
- package/dist/host/types/src/ui/api/auth-methods-api.d.ts +2 -0
- package/dist/host/types/src/ui/api/credentials-api.d.ts +0 -22
- package/dist/host/types/src/ui/api/plugins-api.d.ts +4 -0
- package/dist/host/types/src/ui/api/settings-api.d.ts +6 -6
- package/dist/host/types/src/ui/api/ssh-host-management-api.d.ts +6 -28
- package/dist/host/types/src/ui/main-axios.d.ts +3 -3
- package/dist/host/types/src/ui/onboarding/onboarding-registry.d.ts +3 -1
- package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +6 -7
- package/dist/host/types/src/ui/plugins/plugin-model.d.ts +20 -0
- package/dist/host/types/src/ui/plugins/use-plugins-manager.d.ts +3 -0
- package/dist/host/types/src/ui/sidebar/AuthEnrollmentSections.d.ts +4 -1
- package/dist/host/types/src/ui/sidebar/host-contributions.d.ts +10 -0
- package/dist/host/ui.js +36 -210
- package/dist/host/{use-permissions-6VSHHUMB.js → use-permissions-LLN6TLAL.js} +1 -1
- package/dist/manifest.d.ts +10 -1
- package/dist/manifest.js +16 -1
- package/dist/ssh-keys.d.ts +29 -0
- package/dist/ssh-keys.js +85 -0
- package/dist/testing.d.ts +52 -15
- package/dist/testing.js +279 -2
- package/dist/vitest-preset.js +23 -0
- package/package.json +5 -1
- package/schema/manifest.schema.json +12 -0
- package/dist/host/types/src/ui/sidebar/host-export-payload.d.ts +0 -30
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.7.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. */
|
|
@@ -192,6 +192,11 @@ export const PLUGIN_DESKTOP_MODES = [
|
|
|
192
192
|
"local",
|
|
193
193
|
"server",
|
|
194
194
|
];
|
|
195
|
+
export const PLUGIN_RUNTIMES = ["desktop"];
|
|
196
|
+
/** The desktop mode a manifest resolves to. */
|
|
197
|
+
export function resolvePluginDesktopMode(m) {
|
|
198
|
+
return m.desktop ?? (m.runtime === "desktop" ? "local" : "mirror");
|
|
199
|
+
}
|
|
195
200
|
export const DEFAULT_BACKEND_ENTRY = "dist/backend.js";
|
|
196
201
|
export const DEFAULT_FRONTEND_ENTRY = "dist/frontend.js";
|
|
197
202
|
export const DEFAULT_LOCALES_DIR = "locales";
|
|
@@ -227,6 +232,7 @@ const ALLOWED_TOP_LEVEL = new Set([
|
|
|
227
232
|
"platforms",
|
|
228
233
|
"nativeDependencies",
|
|
229
234
|
"desktop",
|
|
235
|
+
"runtime",
|
|
230
236
|
]);
|
|
231
237
|
const ALLOWED_CONTRIBUTES = new Set([
|
|
232
238
|
"tabs",
|
|
@@ -365,6 +371,15 @@ export function validateManifest(manifest) {
|
|
|
365
371
|
!PLUGIN_DESKTOP_MODES.includes(m.desktop)) {
|
|
366
372
|
errors.push(`Field "desktop" must be one of: ${PLUGIN_DESKTOP_MODES.join(", ")}`);
|
|
367
373
|
}
|
|
374
|
+
if (m.runtime !== undefined &&
|
|
375
|
+
!PLUGIN_RUNTIMES.includes(m.runtime)) {
|
|
376
|
+
errors.push(`Field "runtime" must be one of: ${PLUGIN_RUNTIMES.join(", ")}`);
|
|
377
|
+
}
|
|
378
|
+
if (m.runtime === "desktop" &&
|
|
379
|
+
m.desktop !== undefined &&
|
|
380
|
+
m.desktop !== "local") {
|
|
381
|
+
errors.push(`A "desktop" runtime plugin must use desktop "local"`);
|
|
382
|
+
}
|
|
368
383
|
validateDependencyMap(m.dependencies, "dependencies", errors);
|
|
369
384
|
validateDependencyMap(m.optionalDependencies, "optionalDependencies", errors);
|
|
370
385
|
validateProvides(m.provides, errors);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSH key helpers for plugins that make, check or deploy keys: generate a key
|
|
3
|
+
* pair, tell what a key is, derive a public key and check a pair belongs
|
|
4
|
+
* together. Pure functions over ssh2, which the host supplies. Core keeps its
|
|
5
|
+
* own copy for the connect pipeline.
|
|
6
|
+
*/
|
|
7
|
+
export type SshKeyKind = "ssh-ed25519" | "ssh-rsa" | "ecdsa-sha2-nistp256";
|
|
8
|
+
export interface GeneratedSshKeyPair {
|
|
9
|
+
privateKey: string;
|
|
10
|
+
publicKey: string;
|
|
11
|
+
keyType: SshKeyKind;
|
|
12
|
+
}
|
|
13
|
+
export interface GenerateSshKeyPairOptions {
|
|
14
|
+
/** RSA only. 2048 to 8192, default 4096. */
|
|
15
|
+
bits?: number;
|
|
16
|
+
passphrase?: string;
|
|
17
|
+
comment?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Makes a new key pair; the public half is one authorized_keys line. */
|
|
20
|
+
export declare function generateSshKeyPair(type: SshKeyKind, options?: GenerateSshKeyPairOptions): GeneratedSshKeyPair;
|
|
21
|
+
/** The key's algorithm, like "ssh-ed25519", or null when it cannot be read. */
|
|
22
|
+
export declare function detectSshKeyType(key: string, passphrase?: string): string | null;
|
|
23
|
+
/** The authorized_keys line for a private key, or null. */
|
|
24
|
+
export declare function publicKeyFromPrivate(privateKey: string, passphrase?: string): string | null;
|
|
25
|
+
/** Whether a public key line belongs to the private key. */
|
|
26
|
+
export declare function validateSshKeyPair(privateKey: string, publicKey: string, passphrase?: string): {
|
|
27
|
+
isValid: boolean;
|
|
28
|
+
error?: string;
|
|
29
|
+
};
|
package/dist/ssh-keys.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSH key helpers for plugins that make, check or deploy keys: generate a key
|
|
3
|
+
* pair, tell what a key is, derive a public key and check a pair belongs
|
|
4
|
+
* together. Pure functions over ssh2, which the host supplies. Core keeps its
|
|
5
|
+
* own copy for the connect pipeline.
|
|
6
|
+
*/
|
|
7
|
+
import ssh2 from "ssh2";
|
|
8
|
+
/** Makes a new key pair; the public half is one authorized_keys line. */
|
|
9
|
+
export function generateSshKeyPair(type, options = {}) {
|
|
10
|
+
const algorithm = type === "ssh-rsa" ? "rsa" : type === "ssh-ed25519" ? "ed25519" : "ecdsa";
|
|
11
|
+
if (type !== "ssh-rsa" &&
|
|
12
|
+
type !== "ssh-ed25519" &&
|
|
13
|
+
type !== "ecdsa-sha2-nistp256") {
|
|
14
|
+
throw new Error(`Unsupported key type: ${String(type)}`);
|
|
15
|
+
}
|
|
16
|
+
const generateOptions = {
|
|
17
|
+
comment: options.comment ?? "",
|
|
18
|
+
};
|
|
19
|
+
if (options.passphrase) {
|
|
20
|
+
generateOptions.passphrase = options.passphrase;
|
|
21
|
+
generateOptions.cipher = "aes256-ctr";
|
|
22
|
+
}
|
|
23
|
+
if (type === "ssh-rsa") {
|
|
24
|
+
const bits = options.bits ?? 4096;
|
|
25
|
+
if (!Number.isInteger(bits) || bits < 2048 || bits > 8192) {
|
|
26
|
+
throw new Error("RSA keys need 2048 to 8192 bits");
|
|
27
|
+
}
|
|
28
|
+
generateOptions.bits = bits;
|
|
29
|
+
}
|
|
30
|
+
if (type === "ecdsa-sha2-nistp256")
|
|
31
|
+
generateOptions.bits = 256;
|
|
32
|
+
const pair = ssh2.utils.generateKeyPairSync(algorithm, generateOptions);
|
|
33
|
+
return {
|
|
34
|
+
privateKey: pair.private,
|
|
35
|
+
publicKey: pair.public.trim(),
|
|
36
|
+
keyType: type,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function parse(key, passphrase) {
|
|
40
|
+
const parsed = ssh2.utils.parseKey(key.trim(), passphrase || undefined);
|
|
41
|
+
const first = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
42
|
+
if (!first || first instanceof Error) {
|
|
43
|
+
throw first instanceof Error ? first : new Error("Not a readable SSH key");
|
|
44
|
+
}
|
|
45
|
+
return first;
|
|
46
|
+
}
|
|
47
|
+
/** The key's algorithm, like "ssh-ed25519", or null when it cannot be read. */
|
|
48
|
+
export function detectSshKeyType(key, passphrase) {
|
|
49
|
+
try {
|
|
50
|
+
return parse(key, passphrase).type;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** The authorized_keys line for a private key, or null. */
|
|
57
|
+
export function publicKeyFromPrivate(privateKey, passphrase) {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = parse(privateKey, passphrase);
|
|
60
|
+
return `${parsed.type} ${parsed.getPublicSSH().toString("base64")}`;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Whether a public key line belongs to the private key. */
|
|
67
|
+
export function validateSshKeyPair(privateKey, publicKey, passphrase) {
|
|
68
|
+
try {
|
|
69
|
+
const priv = parse(privateKey, passphrase);
|
|
70
|
+
const pub = parse(publicKey);
|
|
71
|
+
const same = priv.getPublicSSH().equals(pub.getPublicSSH());
|
|
72
|
+
return same
|
|
73
|
+
? { isValid: true }
|
|
74
|
+
: {
|
|
75
|
+
isValid: false,
|
|
76
|
+
error: "The public key does not match the private key",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return {
|
|
81
|
+
isValid: false,
|
|
82
|
+
error: error instanceof Error ? error.message : String(error),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
package/dist/testing.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* The vitest config these run under comes from @termix-ssh/plugin-sdk/vitest-preset.
|
|
9
9
|
*/
|
|
10
|
-
import type { PluginContext } from "./backend.js";
|
|
10
|
+
import type { PluginAuditEntry, PluginContext, PluginDesktopLocal } from "./backend.js";
|
|
11
11
|
import type { HostActionContribution, TermixApp } from "./frontend.js";
|
|
12
12
|
import type { PluginManifest } from "./manifest.js";
|
|
13
13
|
import type { PluginTableDefinition } from "./db.js";
|
|
@@ -25,7 +25,7 @@ export interface FakeWsRoute {
|
|
|
25
25
|
import type { PluginLoginMethod, PluginVerifiedIdentity, PluginSecondFactor, PluginSshAuthProvider, PluginSshConnectOptions, PluginSshHost, PluginHostSummary, PluginShareableUser, PluginShareableRole, PluginSshKeyCredential, PluginSshKeyCredentialInput } from "./backend.js";
|
|
26
26
|
import type { SyncEntityRegistration } from "./backend.js";
|
|
27
27
|
import type { PluginDatabase } from "./backend.js";
|
|
28
|
-
import type { PluginExternalClientRequest, PluginProtocolTarget, PluginHostStatusEntry, 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";
|
|
@@ -42,6 +42,8 @@ export interface FakeContextOptions {
|
|
|
42
42
|
settings?: Record<string, unknown>;
|
|
43
43
|
/** Seed for the core settings ctx.settings.readCore can reach. */
|
|
44
44
|
coreSettings?: Record<string, string>;
|
|
45
|
+
/** What ctx.files.dataDir() answers. Defaults to /tmp/<pluginId>. */
|
|
46
|
+
dataDir?: string;
|
|
45
47
|
/** What ctx.ssh.connect and withConnection hand back as the client. */
|
|
46
48
|
sshClient?: unknown;
|
|
47
49
|
/** A real database behind ctx.db, usually createTestDb().database. */
|
|
@@ -73,10 +75,22 @@ export interface FakeContextOptions {
|
|
|
73
75
|
protocolTargets?: Record<string, PluginProtocolTarget>;
|
|
74
76
|
/** What ctx.credentials.listSshKeys answers. */
|
|
75
77
|
sshKeyCredentials?: PluginSshKeyCredential[];
|
|
78
|
+
/** What ctx.credentials.list answers. */
|
|
79
|
+
credentialSummaries?: PluginCredentialSummary[];
|
|
76
80
|
/** What ctx.desktop.available() answers. Defaults to false. */
|
|
77
81
|
desktopAvailable?: boolean;
|
|
82
|
+
/** What ctx.desktop.local is. Calls throw when this is not given. */
|
|
83
|
+
desktopLocal?: PluginDesktopLocal;
|
|
84
|
+
/** The stored audit lines ctx.audit.list and exportBatches serve. */
|
|
85
|
+
auditEntries?: PluginAuditEntry[];
|
|
78
86
|
/** What ctx.hosts.status.get and check answer, by host id. */
|
|
79
87
|
hostStatuses?: Record<number, PluginHostStatusEntry>;
|
|
88
|
+
/** What ctx.hosts.probeTargets answers. */
|
|
89
|
+
probeTargets?: PluginHostProbeTarget[];
|
|
90
|
+
/** Host ids ctx.hosts.sessionActive answers true for. */
|
|
91
|
+
activeSessions?: number[];
|
|
92
|
+
/** What ctx.network.tcpProbe and ctx.ssh.probe answer. Defaults to false. */
|
|
93
|
+
reachable?: (target: string | number, port?: number) => boolean;
|
|
80
94
|
/** The acting user's channels, what ctx.notify.channels answers. */
|
|
81
95
|
notificationChannels?: PluginNotificationChannel[];
|
|
82
96
|
/**
|
|
@@ -110,6 +124,11 @@ export interface FakeContextOptions {
|
|
|
110
124
|
baseUrl?: string;
|
|
111
125
|
/** What ctx.auth.countLinkedUsers answers, by provider. Defaults to 0. */
|
|
112
126
|
linkedUsers?: Record<string, number>;
|
|
127
|
+
/** Second factors from other plugins that ctx.auth.listSecondFactors adds. */
|
|
128
|
+
secondFactors?: {
|
|
129
|
+
id: string;
|
|
130
|
+
pluginId: string;
|
|
131
|
+
}[];
|
|
113
132
|
/** Failures before ctx.auth.loginRateLimit locks a key. Defaults to 5. */
|
|
114
133
|
loginAttemptLimit?: number;
|
|
115
134
|
/** What ctx.users.getLocale answers, by user id. A missing user answers null. */
|
|
@@ -155,6 +174,8 @@ export interface FakeAuthRegistrations {
|
|
|
155
174
|
purpose: string;
|
|
156
175
|
ttlSeconds?: number;
|
|
157
176
|
}>;
|
|
177
|
+
/** Providers passed to ctx.auth.policy.provide that are still registered. */
|
|
178
|
+
policies: PluginAuthPolicyProvider[];
|
|
158
179
|
}
|
|
159
180
|
export interface FakePluginContext {
|
|
160
181
|
ctx: PluginContext;
|
|
@@ -164,6 +185,12 @@ export interface FakePluginContext {
|
|
|
164
185
|
}) => Promise<Record<string, string>>;
|
|
165
186
|
/** Everything the plugin registered through ctx.disposables. */
|
|
166
187
|
disposals: Array<() => void | Promise<void>>;
|
|
188
|
+
/** Providers registered through ctx.hosts.status.provide. */
|
|
189
|
+
hostStatusProviders: PluginHostStatusProvider[];
|
|
190
|
+
/** Every provider passed to ctx.hosts.activity.provide that is still registered. */
|
|
191
|
+
activityProviders: PluginActivityProvider[];
|
|
192
|
+
/** Every provider passed to ctx.hosts.locks.provide that is still registered. */
|
|
193
|
+
hostLockProviders: PluginHostLockProvider[];
|
|
167
194
|
/** Topics emitted through ctx.events.emit, in order. */
|
|
168
195
|
emitted: Array<{
|
|
169
196
|
topic: string;
|
|
@@ -199,6 +226,11 @@ export interface FakePluginContext {
|
|
|
199
226
|
permissionLevel: string;
|
|
200
227
|
durationHours?: number;
|
|
201
228
|
}>;
|
|
229
|
+
/** Every ctx.hosts.setShares call, in order. */
|
|
230
|
+
hostShareSets: Array<{
|
|
231
|
+
hostId: number;
|
|
232
|
+
grants: PluginHostShareGrant[];
|
|
233
|
+
}>;
|
|
202
234
|
/** Everything registered through ctx.auth. */
|
|
203
235
|
auth: FakeAuthRegistrations;
|
|
204
236
|
/** Every ctx.desktop.openIsolatedWindow call, in order. */
|
|
@@ -255,6 +287,8 @@ export interface FakePluginContext {
|
|
|
255
287
|
challenges: Map<string, string>;
|
|
256
288
|
renewers: number;
|
|
257
289
|
};
|
|
290
|
+
/** Calls every ctx.audit.subscribe handler, like a line core just accepted. */
|
|
291
|
+
emitAudit: (entry: PluginAuditEntry) => void;
|
|
258
292
|
/** Every ctx.audit.record entry, in order. */
|
|
259
293
|
audits: Array<{
|
|
260
294
|
action: string;
|
|
@@ -321,6 +355,8 @@ export interface MockContextOptions {
|
|
|
321
355
|
settings?: Record<string, unknown>;
|
|
322
356
|
/** Seed for the core settings readCore can reach. */
|
|
323
357
|
coreSettings?: Record<string, string>;
|
|
358
|
+
/** What ctx.files.dataDir() answers. */
|
|
359
|
+
dataDir?: string;
|
|
324
360
|
/** Acting user returned by currentActor and used by asUser. */
|
|
325
361
|
actor?: string;
|
|
326
362
|
/** What ctx.ssh.connect and withConnection hand back as the client. */
|
|
@@ -344,10 +380,22 @@ export interface MockContextOptions {
|
|
|
344
380
|
protocolTargets?: Record<string, PluginProtocolTarget>;
|
|
345
381
|
/** What ctx.credentials.listSshKeys serves. */
|
|
346
382
|
sshKeyCredentials?: PluginSshKeyCredential[];
|
|
383
|
+
/** What ctx.credentials.list serves. */
|
|
384
|
+
credentialSummaries?: PluginCredentialSummary[];
|
|
347
385
|
/** What ctx.desktop.available() answers. */
|
|
348
386
|
desktopAvailable?: boolean;
|
|
387
|
+
/** What ctx.desktop.local is. See FakeContextOptions. */
|
|
388
|
+
desktopLocal?: PluginDesktopLocal;
|
|
389
|
+
/** See FakeContextOptions. */
|
|
390
|
+
auditEntries?: PluginAuditEntry[];
|
|
349
391
|
/** What ctx.hosts.status answers. See FakeContextOptions. */
|
|
350
392
|
hostStatuses?: Record<number, PluginHostStatusEntry>;
|
|
393
|
+
/** What ctx.hosts.probeTargets answers. */
|
|
394
|
+
probeTargets?: PluginHostProbeTarget[];
|
|
395
|
+
/** Host ids ctx.hosts.sessionActive answers true for. */
|
|
396
|
+
activeSessions?: number[];
|
|
397
|
+
/** What ctx.network.tcpProbe and ctx.ssh.probe answer. */
|
|
398
|
+
reachable?: (target: string | number, port?: number) => boolean;
|
|
351
399
|
/** The acting user's channels, what ctx.notify.channels answers. */
|
|
352
400
|
notificationChannels?: PluginNotificationChannel[];
|
|
353
401
|
/**
|
|
@@ -372,25 +420,14 @@ export interface MockContextOptions {
|
|
|
372
420
|
/** See FakeContextOptions. */
|
|
373
421
|
loginAttemptLimit?: number;
|
|
374
422
|
/** See FakeContextOptions. */
|
|
423
|
+
secondFactors?: FakeContextOptions["secondFactors"];
|
|
424
|
+
/** See FakeContextOptions. */
|
|
375
425
|
locales?: Record<string, string>;
|
|
376
426
|
}
|
|
377
427
|
export interface MockPluginContext extends FakePluginContext {
|
|
378
428
|
/** Capability ids checked during the test, in order, denied ones included. */
|
|
379
429
|
checked: string[];
|
|
380
430
|
}
|
|
381
|
-
/**
|
|
382
|
-
* A context double that gates on capabilities the way src/backend/plugins/ctx.ts
|
|
383
|
-
* does, so a test can prove a plugin fails closed without a running server.
|
|
384
|
-
*
|
|
385
|
-
* The gates on today's ctx surface: kv:own on every ctx.kv call, db:own on
|
|
386
|
-
* ctx.db, network:serve on ctx.http and ctx.ws, events:core on emitting a
|
|
387
|
-
* topic outside the plugin's own namespace, settings:read-core on
|
|
388
|
-
* ctx.settings.readCore, ssh:connect plus credentials:use on ctx.ssh,
|
|
389
|
-
* notify:send on ctx.notify (notify:hub on serve), network:outbound on ctx.fetch, process:spawn on
|
|
390
|
-
* ctx.process, system:tls on ctx.system, plugins:read on ctx.plugins, auth:provide on ctx.auth
|
|
391
|
-
* (users:impersonate on issueUserToken), and users:read on ctx.users. Reading a plugin's own settings is deliberately
|
|
392
|
-
* ungated. As the SDK grows a member, add its gate here in the same shape.
|
|
393
|
-
*/
|
|
394
431
|
export declare function createMockCtx(options?: MockContextOptions): MockPluginContext;
|
|
395
432
|
/** A sqlite handle as createTestDb exposes it. Structural, like better-sqlite3's. */
|
|
396
433
|
export interface TestSqlite {
|