@termix-ssh/plugin-sdk 1.0.20 → 1.0.21
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 +8 -0
- package/PLUGIN-GUIDE.md +5 -2
- package/dist/host/{PluginsTab-ZSMTJ5C5.js → PluginsTab-HQGSDSHS.js} +7 -5
- package/dist/host/{chunk-J3ZQPBDK.js → chunk-A5CULDWV.js} +10 -5
- package/dist/host/{chunk-2MU6WNSX.js → chunk-FGX27MAA.js} +1 -1
- package/dist/host/{chunk-EYONBBRJ.js → chunk-GTHINW3L.js} +1 -1
- package/dist/host/{chunk-7LEJ3HYF.js → chunk-JFQGI4OD.js} +5 -3
- package/dist/host/{chunk-CITUENSU.js → chunk-WWFRBU2E.js} +3 -2
- package/dist/host/{linked-server-OUF3POT7.js → linked-server-UDSFY3N7.js} +1 -1
- package/dist/host/{rbac-api-LZVENREG.js → rbac-api-AAAPMF52.js} +1 -1
- package/dist/host/testing-host.js +4 -4
- package/dist/host/types/src/ui/api/look-api.d.ts +2 -0
- package/dist/host/types/src/ui/api/plugins-api.d.ts +1 -1
- package/dist/host/types/src/ui/lib/app-return.d.ts +13 -0
- package/dist/host/types/src/ui/plugins/plugin-model.d.ts +1 -1
- package/dist/host/ui.js +67 -24
- package/dist/host/{use-permissions-BYCDS5PD.js → use-permissions-VAND5GEG.js} +1 -1
- package/dist/host-commands.d.ts +2 -6
- package/dist/host-commands.js +3 -6
- package/dist/host-scripts/docker.d.ts +137 -0
- package/dist/host-scripts/docker.js +345 -0
- package/dist/host-scripts/index.d.ts +15 -0
- package/dist/host-scripts/index.js +15 -0
- package/dist/host-scripts/journal.d.ts +45 -0
- package/dist/host-scripts/journal.js +194 -0
- package/dist/host-scripts/metrics.d.ts +134 -0
- package/dist/host-scripts/metrics.js +320 -0
- package/dist/host-scripts/openrc.d.ts +6 -0
- package/dist/host-scripts/openrc.js +90 -0
- package/dist/host-scripts/shell.d.ts +43 -0
- package/dist/host-scripts/shell.js +95 -0
- package/dist/host-scripts/systemd.d.ts +73 -0
- package/dist/host-scripts/systemd.js +320 -0
- package/dist/host-scripts/text.d.ts +23 -0
- package/dist/host-scripts/text.js +111 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.21
|
|
4
|
+
|
|
5
|
+
Plugin API 1.12.0.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `@termix-ssh/plugin-sdk/host-scripts`: Docker, Linux metrics, systemd, journal and OpenRC command builders and parsers, plus `scriptShell()` and `passwordSudoScript()` to send scripts to `bash -s` on stdin. It has no Node or ssh2 imports, so the Termix mobile app runs the same code
|
|
10
|
+
|
|
3
11
|
## 1.0.20
|
|
4
12
|
|
|
5
13
|
Plugin API 1.12.0.
|
package/PLUGIN-GUIDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Termix plugin guide
|
|
2
2
|
|
|
3
|
-
SDK 1.0.
|
|
3
|
+
SDK 1.0.21 | Plugin API 1.12.0
|
|
4
4
|
|
|
5
5
|
This guide is for anyone, person or AI coding tool, building a Termix plugin. It ships inside
|
|
6
6
|
`@termix-ssh/plugin-sdk`, so the copy in your `node_modules` always matches the SDK you have installed.
|
|
@@ -335,7 +335,9 @@ try {
|
|
|
335
335
|
|
|
336
336
|
`ctx.ssh.withConnection(hostId, { pool, purpose }, fn)` borrows a pooled connection for frequent short
|
|
337
337
|
commands. `@termix-ssh/plugin-sdk/host-commands` has helpers to detect the OS and run package manager and
|
|
338
|
-
sudo commands.
|
|
338
|
+
sudo commands. `@termix-ssh/plugin-sdk/host-scripts` builds Docker, metrics, systemd and journal
|
|
339
|
+
commands and parses their output; send a script on stdin to the command `scriptShell()` returns. Hosts from
|
|
340
|
+
`ctx.hosts` never carry secrets. Pass the host or its id to `ctx.ssh`.
|
|
339
341
|
|
|
340
342
|
### The desktop app
|
|
341
343
|
|
|
@@ -905,6 +907,7 @@ To run your own registry instead, see https://docs.termix.site/develop/registrie
|
|
|
905
907
|
| `@termix-ssh/plugin-sdk/testing` | `createMockCtx`, `createTestDb`, `renderWithApp`, `createFakeContext` |
|
|
906
908
|
| `@termix-ssh/plugin-sdk/vitest-preset` | `pluginVitestConfig` |
|
|
907
909
|
| `@termix-ssh/plugin-sdk/host-commands` | OS detection, package manager and sudo helpers over ssh2 |
|
|
910
|
+
| `@termix-ssh/plugin-sdk/host-scripts` | Docker, metrics, systemd and journal command builders and parsers, scripts sent to `bash -s` |
|
|
908
911
|
| `@termix-ssh/plugin-sdk/ssh-certs` | `applyCertificateAuth()` for certificate based SSH auth |
|
|
909
912
|
| `@termix-ssh/plugin-sdk/ssh-keys` | `generateSshKeyPair()`, `detectSshKeyType()`, `publicKeyFromPrivate()`, `validateSshKeyPair()` |
|
|
910
913
|
| `@termix-ssh/plugin-sdk/desktop` | The desktop app bridge, typed. Check it exists before use |
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
sortPlugins,
|
|
30
30
|
uploadEntry,
|
|
31
31
|
youtubeEmbedUrl
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-WWFRBU2E.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-GTHINW3L.js";
|
|
111
|
+
import "./chunk-JFQGI4OD.js";
|
|
112
112
|
import {
|
|
113
113
|
__toESM,
|
|
114
114
|
define_import_meta_env_default,
|
|
@@ -2681,7 +2681,7 @@ function InstalledCard({
|
|
|
2681
2681
|
)
|
|
2682
2682
|
] }) : !running ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 border-t border-border px-3 py-2", children: [
|
|
2683
2683
|
/* @__PURE__ */ jsx6("span", { className: "size-1.5 shrink-0 bg-muted-foreground/40" }),
|
|
2684
|
-
/* @__PURE__ */ jsx6("span", { className: "text-[11px] text-muted-foreground", children: plugin.status === "server" ? t("plugins.manager.status.server") : t("plugins.manager.status.disabled") })
|
|
2684
|
+
/* @__PURE__ */ jsx6("span", { className: "text-[11px] text-muted-foreground", children: plugin.status === "server" ? t("plugins.manager.status.server") : plugin.status === "safeMode" ? t("plugins.manager.status.safeMode") : t("plugins.manager.status.disabled") })
|
|
2685
2685
|
] }) : /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 border-t border-border px-3 py-2", children: [
|
|
2686
2686
|
/* @__PURE__ */ jsx6("span", { className: "size-1.5 shrink-0 bg-green-500" }),
|
|
2687
2687
|
/* @__PURE__ */ jsx6("span", { className: "text-[11px] text-muted-foreground", children: plugin.pinnedVersion ? t("plugins.manager.pinnedTo", {
|
|
@@ -2759,7 +2759,9 @@ function StatusCell({ plugin }) {
|
|
|
2759
2759
|
const { t } = useTranslation7();
|
|
2760
2760
|
const running = plugin.status === "running";
|
|
2761
2761
|
const failed = plugin.status === "failed" || plugin.status === "blocked";
|
|
2762
|
-
const label = plugin.status === "blocked" ? t("plugins.manager.status.blocked") : failed ? t("plugins.manager.status.failed") : plugin.status === "server" ? t("plugins.manager.status.server") :
|
|
2762
|
+
const label = plugin.status === "blocked" ? t("plugins.manager.status.blocked") : failed ? t("plugins.manager.status.failed") : plugin.status === "server" ? t("plugins.manager.status.server") : plugin.status === "safeMode" ? t("plugins.manager.status.safeMode") : !running ? t("plugins.manager.status.disabled") : plugin.pinnedVersion ? t("plugins.manager.pinnedTo", {
|
|
2763
|
+
version: plugin.pinnedVersion
|
|
2764
|
+
}) : t("plugins.manager.status.running");
|
|
2763
2765
|
return /* @__PURE__ */ jsxs6(
|
|
2764
2766
|
"span",
|
|
2765
2767
|
{
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
hasPermission,
|
|
4
4
|
resetPermissionsCache,
|
|
5
5
|
usePermissions
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FGX27MAA.js";
|
|
7
7
|
import {
|
|
8
8
|
ArrowDownToLine,
|
|
9
9
|
ArrowLeftToLine,
|
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
withIconBoundary,
|
|
84
84
|
withPluginContext,
|
|
85
85
|
withPluginScope
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-GTHINW3L.js";
|
|
87
87
|
import {
|
|
88
88
|
SYNC_CHANGED_EVENT,
|
|
89
89
|
TRUST_TOKEN_KEY,
|
|
@@ -132,7 +132,7 @@ import {
|
|
|
132
132
|
usePluginStore,
|
|
133
133
|
useTabTypes,
|
|
134
134
|
websocketAuthProtocols
|
|
135
|
-
} from "./chunk-
|
|
135
|
+
} from "./chunk-JFQGI4OD.js";
|
|
136
136
|
import {
|
|
137
137
|
define_import_meta_env_default,
|
|
138
138
|
init_define_import_meta_env
|
|
@@ -1144,6 +1144,10 @@ var en_default = {
|
|
|
1144
1144
|
ready: "Termix {{version}} is ready to install",
|
|
1145
1145
|
readyHint: "It installs the next time Termix restarts."
|
|
1146
1146
|
},
|
|
1147
|
+
safeMode: {
|
|
1148
|
+
title: "Safe mode",
|
|
1149
|
+
description: "Only layout and theme plugins are running. Restart the server without TERMIX_SAFE_MODE to turn the rest back on."
|
|
1150
|
+
},
|
|
1147
1151
|
companion: {
|
|
1148
1152
|
close: "Close panel",
|
|
1149
1153
|
moveSide: "Move panel",
|
|
@@ -2220,7 +2224,8 @@ var en_default = {
|
|
|
2220
2224
|
disabled: "Disabled",
|
|
2221
2225
|
failed: "Stopped unexpectedly",
|
|
2222
2226
|
blocked: "Waiting on another plugin",
|
|
2223
|
-
server: "Runs on your server"
|
|
2227
|
+
server: "Runs on your server",
|
|
2228
|
+
safeMode: "Off in safe mode"
|
|
2224
2229
|
},
|
|
2225
2230
|
groups: {
|
|
2226
2231
|
running: "Running",
|
|
@@ -10263,7 +10268,7 @@ function CompanionLayout({
|
|
|
10263
10268
|
// src/ui/shell/tabUtils.tsx
|
|
10264
10269
|
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
10265
10270
|
var PluginsTab = lazy(
|
|
10266
|
-
() => import("./PluginsTab-
|
|
10271
|
+
() => import("./PluginsTab-HQGSDSHS.js").then((m) => ({ default: m.PluginsTab }))
|
|
10267
10272
|
);
|
|
10268
10273
|
function preloadTabSurface(type) {
|
|
10269
10274
|
const loader = getTabType(type)?.preload;
|
|
@@ -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-AAAPMF52.js").then(({ getMyPermissions }) => getMyPermissions()).then((result) => {
|
|
30
30
|
cache = {
|
|
31
31
|
permissions: result.permissions ?? [],
|
|
32
32
|
isAdmin: !!result.isAdmin
|
|
@@ -4087,7 +4087,9 @@ function unregisteredViewStatus(kind, id) {
|
|
|
4087
4087
|
return { status: getPluginStoreState().loaded ? "missing" : "loading" };
|
|
4088
4088
|
}
|
|
4089
4089
|
const serverState = owner.summary.state;
|
|
4090
|
-
if (!owner.summary.enabled
|
|
4090
|
+
if (!owner.summary.enabled || serverState === "safe-mode") {
|
|
4091
|
+
return { status: "disabled", owner };
|
|
4092
|
+
}
|
|
4091
4093
|
if (owner.frontend === "failed" || owner.frontend === "blocked" || serverState === "failed" || serverState === "blocked") {
|
|
4092
4094
|
return { status: "failed", owner };
|
|
4093
4095
|
}
|
|
@@ -6316,7 +6318,7 @@ async function updateRole(roleId, roleData) {
|
|
|
6316
6318
|
try {
|
|
6317
6319
|
const response = await rbacApi.put(`/rbac/roles/${roleId}`, roleData);
|
|
6318
6320
|
if (roleData.permissions) {
|
|
6319
|
-
const { notifyPermissionsChanged } = await import("./use-permissions-
|
|
6321
|
+
const { notifyPermissionsChanged } = await import("./use-permissions-VAND5GEG.js");
|
|
6320
6322
|
notifyPermissionsChanged();
|
|
6321
6323
|
}
|
|
6322
6324
|
return response.data;
|
|
@@ -6898,7 +6900,7 @@ function createRemoteOriginApiInstance(path) {
|
|
|
6898
6900
|
});
|
|
6899
6901
|
instance.interceptors.request.use(
|
|
6900
6902
|
async (config) => {
|
|
6901
|
-
const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-
|
|
6903
|
+
const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-UDSFY3N7.js");
|
|
6902
6904
|
const linked = await getLinkedSession2();
|
|
6903
6905
|
config.baseURL = linked ? `${linked.serverUrl}${path}` : "http://no-server-configured";
|
|
6904
6906
|
if (config.headers.set) {
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
cn,
|
|
9
9
|
gridSizeStyle,
|
|
10
10
|
useAreaPreferences
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GTHINW3L.js";
|
|
12
12
|
import {
|
|
13
13
|
isElectron
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-JFQGI4OD.js";
|
|
15
15
|
import {
|
|
16
16
|
__commonJS,
|
|
17
17
|
__toESM,
|
|
@@ -2486,6 +2486,7 @@ function pluginStatus(summary) {
|
|
|
2486
2486
|
if (summary.state === "failed") return "failed";
|
|
2487
2487
|
if (summary.state === "blocked") return "blocked";
|
|
2488
2488
|
if (!summary.enabled) return "disabled";
|
|
2489
|
+
if (summary.state === "safe-mode") return "safeMode";
|
|
2489
2490
|
return "running";
|
|
2490
2491
|
}
|
|
2491
2492
|
function mergePlugins(installed, registry) {
|
|
@@ -59,10 +59,10 @@ import {
|
|
|
59
59
|
trackTabHandle,
|
|
60
60
|
useShellController,
|
|
61
61
|
useTabStageStore
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-A5CULDWV.js";
|
|
63
63
|
import {
|
|
64
64
|
setPermissionsForTesting
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-FGX27MAA.js";
|
|
66
66
|
import {
|
|
67
67
|
SurfaceScope,
|
|
68
68
|
notifyShellReady,
|
|
@@ -70,14 +70,14 @@ import {
|
|
|
70
70
|
setShellCallbacks,
|
|
71
71
|
setShellHosts,
|
|
72
72
|
setShellLayoutProvider
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-GTHINW3L.js";
|
|
74
74
|
import {
|
|
75
75
|
getTabType,
|
|
76
76
|
listTabTypes,
|
|
77
77
|
resetPluginStore,
|
|
78
78
|
setFrontendState,
|
|
79
79
|
setPluginSummaries
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-JFQGI4OD.js";
|
|
81
81
|
import {
|
|
82
82
|
init_define_import_meta_env
|
|
83
83
|
} from "./chunk-YGYBM7SQ.js";
|
|
@@ -22,6 +22,8 @@ export interface LookResponse {
|
|
|
22
22
|
};
|
|
23
23
|
/** TERMIX_SAFE_LAYOUT is set on the server. */
|
|
24
24
|
safeLayout: boolean;
|
|
25
|
+
/** TERMIX_SAFE_MODE is set: only layout and theme plugins run. */
|
|
26
|
+
safeMode?: boolean;
|
|
25
27
|
unavailable: UnavailableLook[];
|
|
26
28
|
}
|
|
27
29
|
export declare function getLook(): Promise<LookResponse>;
|
|
@@ -120,7 +120,7 @@ export interface PluginSummary {
|
|
|
120
120
|
name: string;
|
|
121
121
|
version: string;
|
|
122
122
|
enabled: boolean;
|
|
123
|
-
/** enabled | disabled | blocked | failed, or the loader's live state. */
|
|
123
|
+
/** enabled | disabled | blocked | failed | safe-mode, or the loader's live state. */
|
|
124
124
|
state: string;
|
|
125
125
|
/** A linked desktop leaves this plugin to its server. */
|
|
126
126
|
runsOnServer?: boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign in for the Termix mobile app in a real browser. The app opens the
|
|
3
|
+
* login page with ?return_to=termix-mobile://..., and after any login method
|
|
4
|
+
* the page sends the token back to that address. Only the app's own scheme
|
|
5
|
+
* is accepted, so the value can never send a token to a website.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isAppReturn(value: unknown): value is string;
|
|
8
|
+
/** Reads return_to from the address bar once and keeps it for this tab. */
|
|
9
|
+
export declare function captureAppReturn(): string | null;
|
|
10
|
+
export declare function pendingAppReturn(): string | null;
|
|
11
|
+
export declare function appReturnUrl(base: string, params: Record<string, string>): string;
|
|
12
|
+
/** Sends the result to the app. False when no app asked for it. */
|
|
13
|
+
export declare function handOffToApp(params: Record<string, string>): boolean;
|
|
@@ -2,7 +2,7 @@ import { type CapabilityRisk } from "@termix-ssh/plugin-sdk/capabilities";
|
|
|
2
2
|
import type { PluginEnvVar } from "@termix-ssh/plugin-sdk/docs";
|
|
3
3
|
import { type ChangeType, type ChangelogChange, type ChangelogRelease } from "@termix-ssh/plugin-sdk/changelog";
|
|
4
4
|
import type { PluginChannel, PluginSummary, PluginUploadPreview, RegistryPluginEntry, RegistryPluginVersion } from "../api/plugins-api";
|
|
5
|
-
export type PluginStatus = "running" | "disabled" | "failed" | "blocked" | "server";
|
|
5
|
+
export type PluginStatus = "running" | "disabled" | "failed" | "blocked" | "server" | "safeMode";
|
|
6
6
|
/** One row of the Plugins tab: what is installed merged with the registry. */
|
|
7
7
|
export interface PluginEntry {
|
|
8
8
|
id: string;
|
package/dist/host/ui.js
CHANGED
|
@@ -160,10 +160,10 @@ import {
|
|
|
160
160
|
useThemes,
|
|
161
161
|
verifySecondFactor,
|
|
162
162
|
withProtocolSettings
|
|
163
|
-
} from "./chunk-
|
|
163
|
+
} from "./chunk-A5CULDWV.js";
|
|
164
164
|
import {
|
|
165
165
|
usePermissions
|
|
166
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-FGX27MAA.js";
|
|
167
167
|
import {
|
|
168
168
|
AddButton,
|
|
169
169
|
Card,
|
|
@@ -184,7 +184,7 @@ import {
|
|
|
184
184
|
isPrerelease,
|
|
185
185
|
reportBetaFeedbackUrl,
|
|
186
186
|
reportCoreIssueUrl
|
|
187
|
-
} from "./chunk-
|
|
187
|
+
} from "./chunk-WWFRBU2E.js";
|
|
188
188
|
import {
|
|
189
189
|
Action,
|
|
190
190
|
Activity,
|
|
@@ -374,7 +374,7 @@ import {
|
|
|
374
374
|
useSurfaceKind,
|
|
375
375
|
useUiPreference,
|
|
376
376
|
useUiPreferencesContext
|
|
377
|
-
} from "./chunk-
|
|
377
|
+
} from "./chunk-GTHINW3L.js";
|
|
378
378
|
import {
|
|
379
379
|
CREDENTIAL_ROW_FIELD_KEYS,
|
|
380
380
|
PRESETS,
|
|
@@ -535,7 +535,7 @@ import {
|
|
|
535
535
|
useTabs,
|
|
536
536
|
useTabsSafe,
|
|
537
537
|
verifyPasswordResetCode
|
|
538
|
-
} from "./chunk-
|
|
538
|
+
} from "./chunk-JFQGI4OD.js";
|
|
539
539
|
import {
|
|
540
540
|
define_import_meta_env_default,
|
|
541
541
|
init_define_import_meta_env
|
|
@@ -3327,6 +3327,39 @@ function isInElectronWebView() {
|
|
|
3327
3327
|
}
|
|
3328
3328
|
return false;
|
|
3329
3329
|
}
|
|
3330
|
+
function isEmbeddedAuthFrame() {
|
|
3331
|
+
return isInMobileWebView() || isInElectronWebView();
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/ui/lib/app-return.ts
|
|
3335
|
+
init_define_import_meta_env();
|
|
3336
|
+
var STORAGE_KEY = "termix-app-return";
|
|
3337
|
+
var APP_RETURN = /^termix-mobile:\/\/[A-Za-z0-9._~/-]*$/;
|
|
3338
|
+
function isAppReturn(value) {
|
|
3339
|
+
return typeof value === "string" && APP_RETURN.test(value);
|
|
3340
|
+
}
|
|
3341
|
+
function pendingAppReturn() {
|
|
3342
|
+
try {
|
|
3343
|
+
const value = sessionStorage.getItem(STORAGE_KEY);
|
|
3344
|
+
return isAppReturn(value) ? value : null;
|
|
3345
|
+
} catch {
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
function appReturnUrl(base2, params) {
|
|
3350
|
+
const query = new URLSearchParams(params).toString();
|
|
3351
|
+
return query ? `${base2}?${query}` : base2;
|
|
3352
|
+
}
|
|
3353
|
+
function handOffToApp(params) {
|
|
3354
|
+
const base2 = pendingAppReturn();
|
|
3355
|
+
if (!base2) return false;
|
|
3356
|
+
try {
|
|
3357
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
3358
|
+
} catch {
|
|
3359
|
+
}
|
|
3360
|
+
window.location.replace(appReturnUrl(base2, params));
|
|
3361
|
+
return true;
|
|
3362
|
+
}
|
|
3330
3363
|
|
|
3331
3364
|
// src/ui/auth/Auth.tsx
|
|
3332
3365
|
import { useState as useState7, useEffect as useEffect6, useCallback as useCallback5, useRef as useRef4 } from "react";
|
|
@@ -3878,7 +3911,7 @@ function currentDeps() {
|
|
|
3878
3911
|
return deps ?? defaultDeps;
|
|
3879
3912
|
}
|
|
3880
3913
|
function serverAllowsFrontend(summary) {
|
|
3881
|
-
return summary.enabled && summary.state !== "failed" && summary.state !== "blocked" && !!summary.frontend;
|
|
3914
|
+
return summary.enabled && summary.state !== "failed" && summary.state !== "blocked" && summary.state !== "safe-mode" && !!summary.frontend;
|
|
3882
3915
|
}
|
|
3883
3916
|
function orderForActivation(summaries) {
|
|
3884
3917
|
const byId = new Map(summaries.map((summary) => [summary.id, summary]));
|
|
@@ -4252,7 +4285,7 @@ function LanguageRow({
|
|
|
4252
4285
|
)
|
|
4253
4286
|
] });
|
|
4254
4287
|
}
|
|
4255
|
-
var
|
|
4288
|
+
var STORAGE_KEY2 = "termix_auth";
|
|
4256
4289
|
function parseSecondFactorIds(value) {
|
|
4257
4290
|
const ids = (value ?? "").split(",").filter(Boolean);
|
|
4258
4291
|
return ids.map((id) => ({ id, pluginId: "", labelKey: "" }));
|
|
@@ -4266,10 +4299,15 @@ function hasDesktopManualLogout() {
|
|
|
4266
4299
|
}
|
|
4267
4300
|
function storeAuth(username) {
|
|
4268
4301
|
localStorage.setItem(
|
|
4269
|
-
|
|
4302
|
+
STORAGE_KEY2,
|
|
4270
4303
|
JSON.stringify({ loggedIn: true, username })
|
|
4271
4304
|
);
|
|
4272
4305
|
}
|
|
4306
|
+
async function handOffLogin(token) {
|
|
4307
|
+
if (!pendingAppReturn() || isEmbeddedAuthFrame()) return false;
|
|
4308
|
+
const value = token || await getCurrentToken();
|
|
4309
|
+
return handOffToApp(value ? { token: value } : { error: "no_token" });
|
|
4310
|
+
}
|
|
4273
4311
|
function PasswordInput2({
|
|
4274
4312
|
value,
|
|
4275
4313
|
onChange,
|
|
@@ -4407,8 +4445,9 @@ function Auth({ onLogin }) {
|
|
|
4407
4445
|
useEffect6(() => {
|
|
4408
4446
|
if (proxySigninHandledRef.current || isElectron()) return;
|
|
4409
4447
|
proxySigninHandledRef.current = true;
|
|
4410
|
-
requestTrustedProxyLogin().then((result) => {
|
|
4448
|
+
requestTrustedProxyLogin().then(async (result) => {
|
|
4411
4449
|
if (!result.enabled || !result.success) return;
|
|
4450
|
+
if (await handOffLogin()) return;
|
|
4412
4451
|
storeAuth(result.username || "");
|
|
4413
4452
|
onLogin(
|
|
4414
4453
|
result.username || "",
|
|
@@ -4625,21 +4664,24 @@ function Auth({ onLogin }) {
|
|
|
4625
4664
|
}
|
|
4626
4665
|
return;
|
|
4627
4666
|
}
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4667
|
+
void handOffLogin(urlParams.get("token")).then((handedOff) => {
|
|
4668
|
+
if (handedOff) return;
|
|
4669
|
+
getUserInfo().then((meRes) => {
|
|
4670
|
+
storeAuth(meRes.username || "");
|
|
4671
|
+
clearDesktopManualLogout();
|
|
4672
|
+
onLogin(
|
|
4673
|
+
meRes.username || "",
|
|
4674
|
+
meRes.userId || void 0,
|
|
4675
|
+
!!meRes.is_admin
|
|
4676
|
+
);
|
|
4677
|
+
window.history.replaceState(
|
|
4678
|
+
{},
|
|
4679
|
+
document.title,
|
|
4680
|
+
window.location.pathname
|
|
4681
|
+
);
|
|
4682
|
+
}).catch(() => {
|
|
4683
|
+
toast2.error(t3("errors.failedUserInfo"));
|
|
4684
|
+
});
|
|
4643
4685
|
});
|
|
4644
4686
|
}
|
|
4645
4687
|
}, [onLogin, t3]);
|
|
@@ -4721,6 +4763,7 @@ function Auth({ onLogin }) {
|
|
|
4721
4763
|
setWebviewAuthSuccess(true);
|
|
4722
4764
|
return;
|
|
4723
4765
|
}
|
|
4766
|
+
if (await handOffLogin(res?.token)) return;
|
|
4724
4767
|
const meRes = await getUserInfo();
|
|
4725
4768
|
const name2 = meRes.username || res.username || options.fallbackUsername || "";
|
|
4726
4769
|
storeAuth(name2);
|
package/dist/host-commands.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export type { Client, ClientChannel } from "ssh2";
|
|
12
12
|
import type { Client } from "ssh2";
|
|
13
|
+
import { EXTRA_PATH_DIRS } from "./host-scripts/docker.js";
|
|
14
|
+
export { EXTRA_PATH_DIRS };
|
|
13
15
|
export interface ExecResult {
|
|
14
16
|
stdout: string;
|
|
15
17
|
stderr: string;
|
|
@@ -26,12 +28,6 @@ export interface PlatformInfo {
|
|
|
26
28
|
hasDocker: boolean;
|
|
27
29
|
osPrettyName: string | null;
|
|
28
30
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Non-interactive SSH shells don't source ~/.zprofile or ~/.bash_profile,
|
|
31
|
-
* so PATH additions from installers like Homebrew or OrbStack are missing.
|
|
32
|
-
* Extend PATH with their common install locations before probing.
|
|
33
|
-
*/
|
|
34
|
-
export declare const EXTRA_PATH_DIRS = "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:\"$HOME/.orbstack/bin\"";
|
|
35
31
|
/**
|
|
36
32
|
* Single probe that reports which tooling is available. Each line is
|
|
37
33
|
* "key=value" so the parser is trivial and order-independent.
|
package/dist/host-commands.js
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* A plugin passes the `Client` it got from ctx.ssh.connect/withConnection.
|
|
10
10
|
*/
|
|
11
|
+
// Homebrew and OrbStack paths a non-interactive shell misses.
|
|
12
|
+
import { EXTRA_PATH_DIRS } from "./host-scripts/docker.js";
|
|
13
|
+
export { EXTRA_PATH_DIRS };
|
|
11
14
|
/** Runs one non-interactive command over an existing ssh2 client. */
|
|
12
15
|
export function execCommand(client, command, timeoutMs = 30000) {
|
|
13
16
|
return new Promise((resolve, reject) => {
|
|
@@ -83,12 +86,6 @@ export function execCommand(client, command, timeoutMs = 30000) {
|
|
|
83
86
|
});
|
|
84
87
|
});
|
|
85
88
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Non-interactive SSH shells don't source ~/.zprofile or ~/.bash_profile,
|
|
88
|
-
* so PATH additions from installers like Homebrew or OrbStack are missing.
|
|
89
|
-
* Extend PATH with their common install locations before probing.
|
|
90
|
-
*/
|
|
91
|
-
export const EXTRA_PATH_DIRS = '/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:"$HOME/.orbstack/bin"';
|
|
92
89
|
const DOCKER_PROBE = `command -v docker >/dev/null 2>&1` +
|
|
93
90
|
` || [ -S /var/run/docker.sock ]` +
|
|
94
91
|
` || [ -S "$HOME/.orbstack/run/docker.sock" ]`;
|