@skylandnpm/octopus-proxy 0.1.75 → 0.1.76
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/README.md +21 -9
- package/dist/{chunk-TSX64XMW.js → chunk-X5EQMMYB.js} +30 -6
- package/dist/{chunk-4VKDJVX3.js → chunk-ZBKVXNM5.js} +84 -31
- package/dist/cli.js +16 -7
- package/dist/main.js +2 -2
- package/dist/remote-worker.js +1 -1
- package/native/remote-windows.cs +56 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,31 +8,39 @@ Octopus Proxy 可运行在 macOS、Linux 和 Windows 主机上,需要 Node.js
|
|
|
8
8
|
|
|
9
9
|
### 一键检查和安装
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
在仓库根目录运行以下脚本。脚本会先探测本机程序的版本,Git、OpenSSL、Node.js 22(含 npm)、OpenCode 2.0.19 和 Octopus Proxy 符合要求时会显示 `already installed` 或已安装版本并跳过安装。首次安装 Proxy 使用 npm 的 `@latest`。OpenCode 固定为 `2.0.19`,与[当前适配器的兼容基线](../../docs/opencode-compatibility.md)一致。
|
|
12
12
|
|
|
13
13
|
Linux 或 WSL(x64 / ARM64)
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
./scripts/installer/install-octopus-tools.sh
|
|
17
|
-
export PATH="$HOME/.local/share/octopus-tools/npm/bin:$HOME/.local/share/octopus-tools/node/bin:$PATH"
|
|
18
|
-
octopus-proxy onboard --tool opencode
|
|
19
17
|
```
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
脚本以当前用户安装。安装完成后执行脚本输出的 `export`,将检测到的工具目录加入 PATH。也可把该命令写入 shell 启动文件。安装需要 `flock`,自动下载的 Node.js 需要 glibc 及 `curl`、`tar`、`sha256sum`。Alpine 等 musl 发行版可先通过发行版软件源安装 Node.js 22 和 npm,再运行脚本。Proxy 的 `node-pty` 依赖在 Linux 上需要 Python 3、`make` 和 C/C++ 编译器,Debian、Ubuntu 或 WSL 的 Ubuntu 可用 `sudo apt install python3 build-essential` 安装。如果脚本安装了 Proxy,运行 `NPM_CONFIG_PREFIX="$HOME/.local/share/octopus-tools/npm" octopus-proxy update` 可用 CLI 更新这份安装。
|
|
20
|
+
|
|
21
|
+
更新 PATH 后,运行 `octopus-proxy onboard --tool opencode` 配置 Proxy。
|
|
22
|
+
|
|
23
|
+
缺少 Git 或 OpenSSL 时,脚本使用系统已配置的软件源,通过 `apt-get`、`dnf`、`yum`、`zypper`、`pacman` 或 `apk` 安装。普通用户安装这两个系统工具时会调用 `sudo`。
|
|
22
24
|
|
|
23
25
|
Windows(x64 / ARM64)
|
|
24
26
|
|
|
27
|
+
以管理员身份打开 PowerShell,从仓库根目录运行安装脚本。脚本会复用系统 PATH 中版本符合要求的工具,也会检查本机的 Program Files 安装目录。安装缺少的工具时使用 `%ProgramFiles%\Octopus\Tools`。脚本将所选工具的目录加入系统 PATH,供本机所有用户使用。 [Windows 环境变量作用域](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables)
|
|
28
|
+
|
|
25
29
|
```powershell
|
|
26
30
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\installer\install-octopus-tools.ps1
|
|
27
31
|
```
|
|
28
32
|
|
|
29
|
-
用 Windows 自带的 `powershell.exe` 启动。若系统缺少 PowerShell 7(`pwsh.exe`),脚本会以自身所在目录为基准,优先查找 `PowerShell-7.6.9-win-x64.zip` 或 `PowerShell-7.6.9-win-arm64.zip`,从任意工作目录启动都适用。将对应架构的 ZIP 与脚本一起复制到目标 Windows 主机即可使用本地包。脚本校验 SHA-256 后解压到 `%
|
|
33
|
+
用 Windows 自带的 `powershell.exe` 启动。若系统缺少 PowerShell 7(`pwsh.exe`),脚本会以自身所在目录为基准,优先查找 `PowerShell-7.6.9-win-x64.zip` 或 `PowerShell-7.6.9-win-arm64.zip`,从任意工作目录启动都适用。将对应架构的 ZIP 与脚本一起复制到目标 Windows 主机即可使用本地包。脚本校验 SHA-256 后解压到 `%ProgramFiles%\Octopus\Tools\powershell`,再继续安装其余工具。
|
|
30
34
|
|
|
31
35
|
本地包缺失或无法通过校验时,脚本会在线下载。可将 `OCTOPUS_POWERSHELL_MIRROR` 设为镜像基址,脚本会拼接 `v7.6.9/PowerShell-7.6.9-win-<架构>.zip`。镜像下载或校验失败时回退到官方源。 [PowerShell 7.6.9 官方发行页](https://github.com/PowerShell/PowerShell/releases/tag/v7.6.9)
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
缺少 Git 时,脚本优先使用同目录下的 `MinGit-2.56.1-64-bit.zip` 或 `MinGit-2.56.1-arm64.zip`。本地包缺失或校验失败时,从国内镜像下载,失败后回退到 Git for Windows 官方源。可用 `OCTOPUS_GIT_MIRROR` 指定包含 `v2.56.1.windows.1/` 的镜像基址。校验通过后,脚本将 MinGit 安装到 `%ProgramFiles%\Octopus\Tools\git`。 [Git for Windows 2.56.1 官方发行页](https://github.com/git-for-windows/git/releases/tag/v2.56.1.windows.1)
|
|
38
|
+
|
|
39
|
+
缺少 OpenSSL 时,脚本优先校验并使用同目录下的 `OpenSSL-3.5.9-win-x64.zip` 或 `OpenSSL-3.5.9-win-arm64.zip`,用 PowerShell 的 `Expand-Archive` 安装到 `%ProgramFiles%\Octopus\Tools\openssl`。如果系统级 `OPENSSL_CONF` 尚未设置,脚本会配置它,供 Proxy 生成主机证书。ZIP 缺失或校验失败时,脚本会查找或下载 Git for Windows 的上游包,先用 XZ Utils 解压,再交给 Windows 的 `tar.exe`。可用 `OCTOPUS_OPENSSL_MIRROR` 指定保留相同发行标签子目录的上游包镜像。 [Git for Windows OpenSSL x64](https://github.com/git-for-windows/pacman-repo/releases/tag/2026-09-30T09-24-18.347486000Z) · [ARM64](https://github.com/git-for-windows/pacman-repo/releases/tag/2026-09-30T10-08-42.867521900Z) · [XZ Utils](https://github.com/tukaani-project/xz/releases/tag/v5.8.4)
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
安装完成后新开终端即可读取更新的系统 PATH。支持 Windows 远控的 Proxy 版本可直接在原生 Windows x64 执行 `octopus-proxy onboard` 和 `octopus-proxy run`,详见[主机安装与配对](../../docs/claw-remote-desktop.md#windows-被控主机)。脚本安装的 npm 版本需要包含此功能。
|
|
42
|
+
|
|
43
|
+
只检查版本时,Linux 追加 `--check`,Windows 追加 `-Check`。Windows 从系统 PATH 检查各工具,包含 PowerShell 7,检查时无需管理员权限。任何一项缺失或版本不符时返回退出码 `1`。
|
|
36
44
|
|
|
37
45
|
Node.js 和 npm 默认使用 `https://npmmirror.com/mirrors/node` 和 `https://registry.npmmirror.com`,下载或安装失败时回退到官方源。可通过 `OCTOPUS_NODE_MIRROR` 和 `OCTOPUS_NPM_REGISTRY` 为本次运行指定源。Node.js 压缩包按官方 SHA-256 校验,npm 源设置仅用于本次安装。
|
|
38
46
|
|
|
@@ -67,6 +75,8 @@ octopus-proxy doctor
|
|
|
67
75
|
octopus-proxy restart
|
|
68
76
|
```
|
|
69
77
|
|
|
78
|
+
Windows 上,Claw 的项目终端使用 `ComSpec` 指向的本机可执行文件,找不到时使用 `%SystemRoot%\System32\cmd.exe`。无需设置 `SHELL`。若看到 `/bin/sh` 报错,请更新 Windows 主机上的 Proxy,并从本机终端重新运行 `octopus-proxy run` 后在 Claw 新建终端。
|
|
79
|
+
|
|
70
80
|
Claw 新建终端时若出现 `posix_spawnp failed.`,错误来自 Proxy 主机上的 PTY 启动。`node-pty 1.1.0` 的 macOS 预编译 `spawn-helper` 存在安装后缺少执行权限的[上游问题](https://github.com/microsoft/node-pty/issues/850);同一 macOS 版本的两台电脑也可能因安装方式、CPU 架构和安装目录不同而只有一台失败。
|
|
71
81
|
|
|
72
82
|
Proxy 在新建终端前检查实际加载的辅助程序,并自动补齐缺失的执行权限。`octopus-proxy doctor` 会显示辅助程序路径及当前 Node 架构,`doctor --fix` 可修复权限;检查独立于配置是否有效。若安装目录不允许当前账号修改,诊断会提示由安装账号修复或重新安装,不会自动提权。辅助程序缺失需重新安装;权限正常仍失败时,终端错误会显示 shell、工作目录、辅助程序路径和 Node 版本,供进一步核查。诊断应在发生错误的 Proxy 主机、同一 Node 安装环境下运行,之后执行 `octopus-proxy restart` 并在 Claw 新建终端。
|
|
@@ -147,13 +157,15 @@ octopus-proxy onboard --tool pi
|
|
|
147
157
|
|
|
148
158
|
## 更新
|
|
149
159
|
|
|
150
|
-
|
|
160
|
+
macOS 和 Linux 上可运行:
|
|
151
161
|
|
|
152
162
|
```sh
|
|
153
163
|
octopus-proxy update
|
|
154
164
|
```
|
|
155
165
|
|
|
156
|
-
命令通过 `npm view @skylandnpm/octopus-proxy@latest version` 查询最新版本。存在新版本时,输入 `y` 或 `yes`
|
|
166
|
+
命令通过 `npm view @skylandnpm/octopus-proxy@latest version` 查询最新版本。存在新版本时,输入 `y` 或 `yes` 确认覆盖安装;直接回车或输入其他内容会取消。脚本中可使用 `octopus-proxy update -y`(或 `--yes`),发现新版本就直接安装,无需交互终端。当前版本已是最新或高于 npm 最新版本时,不安装也不重启服务。
|
|
167
|
+
|
|
168
|
+
命令使用 npm 全局安装查到的版本,校验安装版本成功后才重启已安装的服务,并检查运行状态后反馈结果。安装期间不会主动停止服务。配置、主机身份和已有服务定义会保留;未安装服务时仅更新 npm 包。Windows 请先停止前台 Proxy,通过 npm 更新后再运行 `octopus-proxy run`。
|
|
157
169
|
|
|
158
170
|
安装或版本校验失败时不会触发服务重启;包安装失败不保证旧版本文件完整保留。已安装新版本但重启失败时会单独提示,可运行 `octopus-proxy doctor` 和 `octopus-proxy logs` 排查。此命令用于 npm 全局安装,覆盖安装前会检查 npm 安装目录是否与当前命令一致;源码、本地安装或 Node.js 环境不一致时会中止并给出提示。
|
|
159
171
|
|
|
@@ -4517,7 +4517,27 @@ function missingPermissions(permissions) {
|
|
|
4517
4517
|
}
|
|
4518
4518
|
|
|
4519
4519
|
// src/remote/windows-adapter.ts
|
|
4520
|
-
var WINDOWS_DESKTOP_UNAVAILABLE = "Windows \
|
|
4520
|
+
var WINDOWS_DESKTOP_UNAVAILABLE = "Windows \u684C\u9762\u4E0D\u53EF\u7528\uFF1B\u8BF7\u5728\u5DF2\u767B\u5F55\u4E14\u89E3\u9501\u7684\u672C\u673A\u684C\u9762\u4E2D\u8FD0\u884C octopus-proxy run";
|
|
4521
|
+
function windowsDesktopIssue(code) {
|
|
4522
|
+
switch (code) {
|
|
4523
|
+
case 10:
|
|
4524
|
+
return "Proxy \u4E0D\u5728\u6D3B\u52A8\u7684 Windows \u672C\u673A\u63A7\u5236\u53F0\u4F1A\u8BDD\u4E2D\uFF1B\u8BF7\u5728\u672C\u673A\u684C\u9762\u8FD0\u884C octopus-proxy run\uFF0CRDP \u548C Windows \u670D\u52A1\u4F1A\u8BDD\u4E0D\u652F\u6301";
|
|
4525
|
+
case 11:
|
|
4526
|
+
return "Proxy \u672A\u8FDE\u63A5 Windows \u4EA4\u4E92\u5F0F\u684C\u9762\uFF1B\u8BF7\u4ECE\u684C\u9762\u7528\u6237\u7684\u7EC8\u7AEF\u8FD0\u884C octopus-proxy run";
|
|
4527
|
+
case 12:
|
|
4528
|
+
return "Windows \u666E\u901A\u684C\u9762\u5F53\u524D\u4E0D\u53EF\u8BBF\u95EE\uFF1B\u8BF7\u5728\u4E3B\u673A\u4E0A\u89E3\u9501\u6216\u5173\u95ED UAC \u5B89\u5168\u684C\u9762\uFF0C\u7136\u540E\u91CD\u8BD5";
|
|
4529
|
+
case 13:
|
|
4530
|
+
return "Proxy \u7684 Windows \u767B\u5F55\u4F1A\u8BDD\u4E0E\u5F53\u524D\u684C\u9762\u4E0D\u5339\u914D\uFF0C\u6216\u65E0\u6CD5\u9A8C\u8BC1\uFF1B\u8BF7\u7528\u5F53\u524D\u684C\u9762\u7528\u6237\u7684\u666E\u901A\u7EC8\u7AEF\u91CD\u65B0\u8FD0\u884C octopus-proxy run";
|
|
4531
|
+
case 14:
|
|
4532
|
+
return "Windows \u684C\u9762\u8F85\u52A9\u7A0B\u5E8F\u65E0\u6CD5\u521D\u59CB\u5316\u663E\u793A\u7F29\u653E\uFF1B\u8BF7\u8FD0\u884C octopus-proxy doctor \u68C0\u67E5";
|
|
4533
|
+
case 15:
|
|
4534
|
+
return "\u6CA1\u6709\u53EF\u622A\u5C4F\u7684\u6D3B\u52A8\u663E\u793A\u5668\uFF0C\u8BF7\u68C0\u67E5 Proxy \u4E3B\u673A\u7684\u56FE\u5F62\u4F1A\u8BDD\u3002";
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
function windowsDesktopError(error) {
|
|
4538
|
+
if (!(error instanceof Error)) return void 0;
|
|
4539
|
+
return windowsDesktopIssue(error.code) ?? windowsDesktopIssue(error.cause?.code);
|
|
4540
|
+
}
|
|
4521
4541
|
var WindowsRemoteAdapter = class {
|
|
4522
4542
|
constructor(context, dependencies = nativeDependencies) {
|
|
4523
4543
|
this.context = context;
|
|
@@ -4541,7 +4561,9 @@ var WindowsRemoteAdapter = class {
|
|
|
4541
4561
|
async checkDependencies(_config) {
|
|
4542
4562
|
}
|
|
4543
4563
|
unavailableReason(permissions) {
|
|
4544
|
-
if (permissions.locked || !permissions.session || !permissions.capture || !permissions.input)
|
|
4564
|
+
if (permissions.locked || !permissions.session || !permissions.capture || !permissions.input) {
|
|
4565
|
+
return windowsDesktopIssue(permissions.desktopIssue) ?? WINDOWS_DESKTOP_UNAVAILABLE;
|
|
4566
|
+
}
|
|
4545
4567
|
}
|
|
4546
4568
|
createSession(options) {
|
|
4547
4569
|
return new WindowsRemoteSession(options, this.dependencies);
|
|
@@ -4589,7 +4611,7 @@ var executeDesktop = async (file, args) => {
|
|
|
4589
4611
|
const detail = error;
|
|
4590
4612
|
if (detail.code === "ENOENT") throw new Error(`\u684C\u9762\u622A\u5C4F\u7F3A\u5C11\u4F9D\u8D56\uFF1A${file}\u3002\u8BF7\u5728 Proxy \u4E3B\u673A\u5B89\u88C5\u540E\u91CD\u8BD5\u3002`);
|
|
4591
4613
|
if (detail.killed) throw new Error("\u684C\u9762\u622A\u5C4F\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5 Proxy \u4E3B\u673A\u7684\u684C\u9762\u4F1A\u8BDD\u540E\u91CD\u8BD5\u3002");
|
|
4592
|
-
throw new Error(`\u684C\u9762\u622A\u5C4F\u5931\u8D25\uFF1A${detail.stderr?.trim().slice(0, 2048) || detail.message}
|
|
4614
|
+
throw new Error(`\u684C\u9762\u622A\u5C4F\u5931\u8D25\uFF1A${detail.stderr?.trim().slice(0, 2048) || detail.message}`, { cause: error });
|
|
4593
4615
|
}
|
|
4594
4616
|
};
|
|
4595
4617
|
var DESKTOP_PERMISSION_GUIDANCE = "\u8BF7\u5728\u201C\u7CFB\u7EDF\u8BBE\u7F6E \u2192 \u9690\u79C1\u4E0E\u5B89\u5168\u6027 \u2192 \u5C4F\u5E55\u5F55\u5236\uFF08\u6216\u5C4F\u5E55\u4E0E\u7CFB\u7EDF\u97F3\u9891\u5F55\u5236\uFF09\u201D\u4E2D\u542F\u7528\u7CFB\u7EDF\u6388\u6743\u5F39\u7A97\u663E\u793A\u7684\u5B9E\u9645\u7A0B\u5E8F\u3002\u7EC8\u7AEF\u8FD0\u884C\u65F6\u53EF\u80FD\u663E\u793A Terminal\u3001iTerm \u6216\u7F16\u8F91\u5668\uFF0C\u540E\u53F0\u670D\u52A1\u53EF\u80FD\u663E\u793A node\uFF1B\u4E0D\u4E00\u5B9A\u4F1A\u51FA\u73B0 octopus-desktop-capture\u3002\u7EC8\u7AEF\u6388\u6743\u4E0D\u4EE3\u8868\u540E\u53F0\u670D\u52A1\u5DF2\u6388\u6743\uFF1B\u8BF7\u5728 Claw \u70B9\u51FB\u201C\u622A\u5C4F\u201D\u89E6\u53D1\u670D\u52A1\u7684\u6743\u9650\u7533\u8BF7\uFF0C\u6309\u5B9E\u9645\u540D\u79F0\u6388\u6743\u540E\u91CD\u542F Proxy\u3002";
|
|
@@ -4640,8 +4662,10 @@ var PlatformDesktop = class {
|
|
|
4640
4662
|
this.helper = void 0;
|
|
4641
4663
|
throw error;
|
|
4642
4664
|
});
|
|
4643
|
-
return this.execute(await this.helper, args).catch(() => {
|
|
4644
|
-
|
|
4665
|
+
return this.execute(await this.helper, args).catch((error) => {
|
|
4666
|
+
const message = windowsDesktopError(error);
|
|
4667
|
+
if (message) throw new Error(message);
|
|
4668
|
+
throw error;
|
|
4645
4669
|
});
|
|
4646
4670
|
}
|
|
4647
4671
|
linuxMode() {
|
|
@@ -5051,7 +5075,7 @@ export {
|
|
|
5051
5075
|
remoteResolutionTiers,
|
|
5052
5076
|
fitVideo,
|
|
5053
5077
|
checkWindowsPrivatePath,
|
|
5054
|
-
|
|
5078
|
+
windowsDesktopError,
|
|
5055
5079
|
DESKTOP_PERMISSION_GUIDANCE,
|
|
5056
5080
|
requestDesktopPermission,
|
|
5057
5081
|
checkDesktop,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
RemotePlatformRegistry,
|
|
8
8
|
RemoteQualitySchema,
|
|
9
9
|
SignalSchema,
|
|
10
|
-
WINDOWS_DESKTOP_UNAVAILABLE,
|
|
11
10
|
__commonJS,
|
|
12
11
|
__toESM,
|
|
13
12
|
assertDirectSignal,
|
|
@@ -16,8 +15,9 @@ import {
|
|
|
16
15
|
probeRemote,
|
|
17
16
|
remotePlatformError,
|
|
18
17
|
remoteSessionBudget,
|
|
18
|
+
windowsDesktopError,
|
|
19
19
|
z
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-X5EQMMYB.js";
|
|
21
21
|
|
|
22
22
|
// ../../node_modules/.pnpm/ajv@8.17.1/node_modules/ajv/dist/compile/codegen/code.js
|
|
23
23
|
var require_code = __commonJS({
|
|
@@ -14075,7 +14075,7 @@ var ProjectRegistry = class {
|
|
|
14075
14075
|
|
|
14076
14076
|
// src/terminal.ts
|
|
14077
14077
|
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
14078
|
-
import { access as access2, stat as
|
|
14078
|
+
import { access as access2, stat as stat11 } from "node:fs/promises";
|
|
14079
14079
|
import { constants as constants5 } from "node:fs";
|
|
14080
14080
|
import { platform as platform3, userInfo as userInfo3 } from "node:os";
|
|
14081
14081
|
import { delimiter } from "node:path";
|
|
@@ -14130,7 +14130,80 @@ async function checkTerminalSupport(fix) {
|
|
|
14130
14130
|
}
|
|
14131
14131
|
}
|
|
14132
14132
|
|
|
14133
|
+
// src/terminal-windows.ts
|
|
14134
|
+
import { stat as stat10 } from "node:fs/promises";
|
|
14135
|
+
import { win32 } from "node:path";
|
|
14136
|
+
var shellVariables = [
|
|
14137
|
+
"PathExt",
|
|
14138
|
+
"OS",
|
|
14139
|
+
"SystemDrive",
|
|
14140
|
+
"UserProfile",
|
|
14141
|
+
"UserName",
|
|
14142
|
+
"UserDomain",
|
|
14143
|
+
"HomeDrive",
|
|
14144
|
+
"HomePath",
|
|
14145
|
+
"AppData",
|
|
14146
|
+
"LocalAppData",
|
|
14147
|
+
"Temp",
|
|
14148
|
+
"Tmp",
|
|
14149
|
+
"ProgramData",
|
|
14150
|
+
"ProgramFiles",
|
|
14151
|
+
"ProgramFiles(x86)",
|
|
14152
|
+
"ProgramW6432",
|
|
14153
|
+
"CommonProgramFiles",
|
|
14154
|
+
"CommonProgramFiles(x86)",
|
|
14155
|
+
"CommonProgramW6432",
|
|
14156
|
+
"PSModulePath"
|
|
14157
|
+
];
|
|
14158
|
+
async function windowsTerminal(environment, home) {
|
|
14159
|
+
const variables = new Map(Object.entries(environment).map(([key, value]) => [key.toLowerCase(), value]));
|
|
14160
|
+
const get = (name) => variables.get(name.toLowerCase());
|
|
14161
|
+
const root = get("SystemRoot") || get("windir") || "C:\\Windows";
|
|
14162
|
+
const system = win32.join(root, "System32");
|
|
14163
|
+
const fallback = win32.join(system, "cmd.exe");
|
|
14164
|
+
const candidates = /* @__PURE__ */ new Set([get("ComSpec"), fallback]);
|
|
14165
|
+
for (const shell of candidates) {
|
|
14166
|
+
if (!shell || win32.parse(shell).root.length < 3 || win32.extname(shell).toLowerCase() !== ".exe") continue;
|
|
14167
|
+
if (!(await stat10(shell).catch(() => void 0))?.isFile()) continue;
|
|
14168
|
+
const env = {};
|
|
14169
|
+
for (const name of shellVariables) {
|
|
14170
|
+
const value = get(name);
|
|
14171
|
+
if (value !== void 0) env[name] = value;
|
|
14172
|
+
}
|
|
14173
|
+
return { shell, env: {
|
|
14174
|
+
...env,
|
|
14175
|
+
ComSpec: shell,
|
|
14176
|
+
SystemRoot: root,
|
|
14177
|
+
windir: root,
|
|
14178
|
+
UserProfile: env.UserProfile || home,
|
|
14179
|
+
Path: [get("Path"), system, root].filter(Boolean).join(win32.delimiter),
|
|
14180
|
+
TERM: "xterm-256color"
|
|
14181
|
+
} };
|
|
14182
|
+
}
|
|
14183
|
+
throw new Error(`\u7EC8\u7AEF Shell \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${fallback}\uFF1B\u8BF7\u68C0\u67E5 Proxy \u4E3B\u673A\u7684 ComSpec \u6216 SystemRoot \u73AF\u5883\u53D8\u91CF`);
|
|
14184
|
+
}
|
|
14185
|
+
|
|
14133
14186
|
// src/terminal.ts
|
|
14187
|
+
async function terminalShell() {
|
|
14188
|
+
const system = platform3(), account = userInfo3();
|
|
14189
|
+
if (system === "win32") return windowsTerminal(process.env, account.homedir);
|
|
14190
|
+
const shell = process.env.SHELL || (system === "darwin" ? "/bin/zsh" : "/bin/sh");
|
|
14191
|
+
try {
|
|
14192
|
+
await access2(shell, constants5.X_OK);
|
|
14193
|
+
} catch {
|
|
14194
|
+
throw new Error(`\u7EC8\u7AEF Shell \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${shell}\uFF1B\u8BF7\u4FEE\u6B63 Proxy \u670D\u52A1\u7684 SHELL \u73AF\u5883\u53D8\u91CF`);
|
|
14195
|
+
}
|
|
14196
|
+
const path = [
|
|
14197
|
+
process.env.PATH,
|
|
14198
|
+
"/usr/bin",
|
|
14199
|
+
"/bin",
|
|
14200
|
+
"/usr/sbin",
|
|
14201
|
+
"/sbin",
|
|
14202
|
+
"/usr/local/bin",
|
|
14203
|
+
...system === "darwin" ? ["/opt/homebrew/bin"] : []
|
|
14204
|
+
].filter(Boolean).join(delimiter);
|
|
14205
|
+
return { shell, env: { PATH: path, HOME: account.homedir, TERM: "xterm-256color", USER: account.username } };
|
|
14206
|
+
}
|
|
14134
14207
|
var TerminalManager = class {
|
|
14135
14208
|
constructor(workspace, emit) {
|
|
14136
14209
|
this.workspace = workspace;
|
|
@@ -14152,33 +14225,13 @@ var TerminalManager = class {
|
|
|
14152
14225
|
const count = [...this.terminals.values()].filter((t) => t.owner === owner).length;
|
|
14153
14226
|
if (count >= 8) throw new Error("Terminal limit reached");
|
|
14154
14227
|
const cwd = await this.workspace.path(project, typeof p.cwd === "string" ? p.cwd : ".");
|
|
14155
|
-
if (!(await
|
|
14156
|
-
const shell
|
|
14157
|
-
try {
|
|
14158
|
-
await access2(shell, constants5.X_OK);
|
|
14159
|
-
} catch {
|
|
14160
|
-
throw new Error(`\u7EC8\u7AEF Shell \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u6267\u884C\uFF1A${shell}\uFF1B\u8BF7\u4FEE\u6B63 Proxy \u670D\u52A1\u7684 SHELL \u73AF\u5883\u53D8\u91CF`);
|
|
14161
|
-
}
|
|
14228
|
+
if (!(await stat11(cwd)).isDirectory()) throw new Error(`\u7EC8\u7AEF\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u76EE\u5F55\uFF1A${cwd}`);
|
|
14229
|
+
const { shell, env } = await terminalShell();
|
|
14162
14230
|
const { helper } = await ensureTerminalSupport();
|
|
14163
14231
|
const id = randomUUID17();
|
|
14164
|
-
const path = [
|
|
14165
|
-
process.env.PATH,
|
|
14166
|
-
"/usr/bin",
|
|
14167
|
-
"/bin",
|
|
14168
|
-
"/usr/sbin",
|
|
14169
|
-
"/sbin",
|
|
14170
|
-
"/usr/local/bin",
|
|
14171
|
-
...platform3() === "darwin" ? ["/opt/homebrew/bin"] : []
|
|
14172
|
-
].filter(Boolean).join(delimiter);
|
|
14173
14232
|
let terminalProcess;
|
|
14174
14233
|
try {
|
|
14175
|
-
terminalProcess = pty.spawn(shell, [], {
|
|
14176
|
-
name: "xterm-256color",
|
|
14177
|
-
cols: 80,
|
|
14178
|
-
rows: 24,
|
|
14179
|
-
cwd,
|
|
14180
|
-
env: { PATH: path, HOME: userInfo3().homedir, TERM: "xterm-256color", USER: userInfo3().username }
|
|
14181
|
-
});
|
|
14234
|
+
terminalProcess = pty.spawn(shell, [], { name: "xterm-256color", cols: 80, rows: 24, cwd, env });
|
|
14182
14235
|
} catch (error) {
|
|
14183
14236
|
throw new Error(`\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}\uFF08shell=${shell}\uFF0Ccwd=${cwd}${helper ? `\uFF0Chelper=${helper}` : ""}\uFF0C${process.platform}/${process.arch}\uFF0CNode ${process.version}\uFF09\uFF1B\u8BF7\u5728 Proxy \u4E3B\u673A\u8FD0\u884C octopus-proxy doctor --fix \u68C0\u67E5`);
|
|
14184
14237
|
}
|
|
@@ -14251,10 +14304,10 @@ function windowsSessionIdentity(stateDirectory) {
|
|
|
14251
14304
|
throw error;
|
|
14252
14305
|
});
|
|
14253
14306
|
const file = await helper;
|
|
14254
|
-
const identity = (await exec(file, ["session"], { timeout: 5e3, maxBuffer: 4096, windowsHide: true }).catch(() => {
|
|
14255
|
-
throw new Error(
|
|
14307
|
+
const identity = (await exec(file, ["session"], { timeout: 5e3, maxBuffer: 4096, windowsHide: true }).catch((error) => {
|
|
14308
|
+
throw new Error(windowsDesktopError(error) ?? "Windows \u684C\u9762\u8F85\u52A9\u7A0B\u5E8F\u65E0\u6CD5\u67E5\u8BE2\u5F53\u524D\u767B\u5F55\u4F1A\u8BDD\uFF1B\u8BF7\u8FD0\u884C octopus-proxy doctor \u68C0\u67E5");
|
|
14256
14309
|
})).stdout.trim();
|
|
14257
|
-
if (!/^win32:[1-9]\d*:S-1
|
|
14310
|
+
if (!/^win32:[1-9]\d*:S-1-5-5-\d+-\d+$/.test(identity)) throw new Error("Windows \u684C\u9762\u8F85\u52A9\u7A0B\u5E8F\u8FD4\u56DE\u65E0\u6548\u7684\u767B\u5F55\u4F1A\u8BDD\uFF1B\u8BF7\u66F4\u65B0 Proxy \u540E\u91CD\u8BD5");
|
|
14258
14311
|
return identity;
|
|
14259
14312
|
};
|
|
14260
14313
|
}
|
|
@@ -14537,7 +14590,7 @@ import { platform as platform5 } from "node:os";
|
|
|
14537
14590
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
14538
14591
|
|
|
14539
14592
|
// src/node-resolution.ts
|
|
14540
|
-
import { realpath as realpath8, stat as
|
|
14593
|
+
import { realpath as realpath8, stat as stat12 } from "node:fs/promises";
|
|
14541
14594
|
import { execFile as execFile7 } from "node:child_process";
|
|
14542
14595
|
import { promisify as promisify7 } from "node:util";
|
|
14543
14596
|
import { delimiter as delimiter2, join as join14 } from "node:path";
|
|
@@ -14606,7 +14659,7 @@ async function resolveCommandOnPath(name, extraDirs = []) {
|
|
|
14606
14659
|
for (const dir of new Set(dirs)) {
|
|
14607
14660
|
const full = join14(dir, file);
|
|
14608
14661
|
try {
|
|
14609
|
-
const info = await
|
|
14662
|
+
const info = await stat12(full);
|
|
14610
14663
|
if (info.isFile() && (windows || info.mode & 73)) return full;
|
|
14611
14664
|
} catch {
|
|
14612
14665
|
}
|
package/dist/cli.js
CHANGED
|
@@ -36,14 +36,14 @@ import {
|
|
|
36
36
|
supportedPlatform,
|
|
37
37
|
systemdUnitPath,
|
|
38
38
|
toolSearchDirs
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-ZBKVXNM5.js";
|
|
40
40
|
import {
|
|
41
41
|
DESKTOP_PERMISSION_GUIDANCE,
|
|
42
42
|
PlatformDesktop,
|
|
43
43
|
checkDesktop,
|
|
44
44
|
checkWindowsPrivatePath,
|
|
45
45
|
requestDesktopPermission
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-X5EQMMYB.js";
|
|
47
47
|
|
|
48
48
|
// src/cli.ts
|
|
49
49
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -820,7 +820,7 @@ async function npmOutput(args) {
|
|
|
820
820
|
}
|
|
821
821
|
}
|
|
822
822
|
async function confirmUpdate(latest) {
|
|
823
|
-
if (!stdin2.isTTY) throw new Error("\u66F4\u65B0\u9700\u8981\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u4E2D\u786E\u8BA4\uFF1B\
|
|
823
|
+
if (!stdin2.isTTY) throw new Error("\u66F4\u65B0\u9700\u8981\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u4E2D\u786E\u8BA4\uFF1B\u4E5F\u53EF\u8FD0\u884C octopus-proxy update -y \u81EA\u52A8\u786E\u8BA4");
|
|
824
824
|
const rl = createInterface2({ input: stdin2, output: stdout2 });
|
|
825
825
|
try {
|
|
826
826
|
return /^(y|yes|是|确认)$/i.test((await rl.question(`\u662F\u5426\u8986\u76D6\u5B89\u88C5 ${proxyVersion} \u2192 ${latest}\uFF1F\u5B89\u88C5\u5E76\u6821\u9A8C\u6210\u529F\u540E\u4F1A\u91CD\u542F\u670D\u52A1 [y/N]: `)).trim());
|
|
@@ -843,7 +843,7 @@ async function restartAndVerify() {
|
|
|
843
843
|
}
|
|
844
844
|
throw new Error(`\u670D\u52A1\u91CD\u542F\u540E\u672A\u8FD0\u884C\uFF1B\u8BF7\u8FD0\u884C octopus-proxy doctor / logs \u68C0\u67E5\uFF08${logLocationHint()}\uFF09`);
|
|
845
845
|
}
|
|
846
|
-
async function runUpdate() {
|
|
846
|
+
async function runUpdate({ yes = false } = {}) {
|
|
847
847
|
console.log(`\u5F53\u524D\u7248\u672C\uFF1A${proxyVersion}\uFF1B\u6B63\u5728\u67E5\u8BE2 npm \u6700\u65B0\u7248\u672C\u2026`);
|
|
848
848
|
const output = await npmOutput(["view", `${PACKAGE}@latest`, "version", "--json", "--prefer-online", "--registry", REGISTRY]);
|
|
849
849
|
let latest;
|
|
@@ -858,7 +858,7 @@ async function runUpdate() {
|
|
|
858
858
|
return;
|
|
859
859
|
}
|
|
860
860
|
console.log(`\u53D1\u73B0\u65B0\u7248\u672C\uFF1A${latest}`);
|
|
861
|
-
if (!await confirmUpdate(latest)) {
|
|
861
|
+
if (!yes && !await confirmUpdate(latest)) {
|
|
862
862
|
console.log("\u5DF2\u53D6\u6D88\u66F4\u65B0");
|
|
863
863
|
return;
|
|
864
864
|
}
|
|
@@ -929,6 +929,9 @@ install \u9009\u9879:
|
|
|
929
929
|
--force \u91CD\u5199\u670D\u52A1\u5B9A\u4E49\u5E76\u91CD\u542F\uFF08\u914D\u7F6E\u6216\u8DEF\u5F84\u53D8\u5316\u540E\u4F7F\u7528\uFF09
|
|
930
930
|
--node-path <p> \u56FA\u5B9A\u670D\u52A1\u4F7F\u7528\u7684 node \u7EDD\u5BF9\u8DEF\u5F84
|
|
931
931
|
|
|
932
|
+
update \u9009\u9879:
|
|
933
|
+
-y, --yes \u53D1\u73B0\u65B0\u7248\u672C\u65F6\u76F4\u63A5\u66F4\u65B0\uFF0C\u65E0\u9700\u4EA4\u4E92\u786E\u8BA4
|
|
934
|
+
|
|
932
935
|
projects \u9009\u9879:
|
|
933
936
|
--remove <\u5E8F\u53F7> \u5220\u9664\u6307\u5B9A\u9879\u76EE\uFF08\u5148\u8FD0\u884C projects \u67E5\u770B\u5E8F\u53F7\uFF1B\u81EA\u52A8\u5907\u4EFD\u539F\u914D\u7F6E\uFF09
|
|
934
937
|
|
|
@@ -959,6 +962,10 @@ function parse(argv) {
|
|
|
959
962
|
flags2["--follow"] = "true";
|
|
960
963
|
continue;
|
|
961
964
|
}
|
|
965
|
+
if (arg === "-y") {
|
|
966
|
+
flags2["--yes"] = "true";
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
962
969
|
if (!arg.startsWith("--")) {
|
|
963
970
|
if (command2 === void 0) command2 = arg;
|
|
964
971
|
else throw new Error(`\u65E0\u6CD5\u8BC6\u522B\u7684\u53C2\u6570\uFF1A${arg}`);
|
|
@@ -1034,7 +1041,7 @@ try {
|
|
|
1034
1041
|
await manageProjects(configPath, flags["--remove"]);
|
|
1035
1042
|
break;
|
|
1036
1043
|
case "update":
|
|
1037
|
-
await runUpdate();
|
|
1044
|
+
await runUpdate({ yes: flags["--yes"] === "true" });
|
|
1038
1045
|
break;
|
|
1039
1046
|
case "identity": {
|
|
1040
1047
|
const config = await loadConfig(configPath);
|
|
@@ -1109,9 +1116,11 @@ try {
|
|
|
1109
1116
|
}
|
|
1110
1117
|
break;
|
|
1111
1118
|
}
|
|
1112
|
-
case "run":
|
|
1119
|
+
case "run": {
|
|
1113
1120
|
await runDaemon(configPath);
|
|
1121
|
+
console.log("Octopus Proxy is running. \u8BF7\u52FF\u5173\u95ED\u6B64\u7A97\u53E3\uFF0C\u5426\u5219 Proxy \u5C06\u505C\u6B62\u8FD0\u884C\uFF1B\u5982\u9700\u9000\u51FA\uFF0C\u8BF7\u6309 Ctrl+C\u3002");
|
|
1114
1122
|
break;
|
|
1123
|
+
}
|
|
1115
1124
|
case "logs":
|
|
1116
1125
|
await showLogs(flags["--follow"] === "true");
|
|
1117
1126
|
break;
|
package/dist/main.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ConfigError,
|
|
4
4
|
runDaemon
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-ZBKVXNM5.js";
|
|
6
|
+
import "./chunk-X5EQMMYB.js";
|
|
7
7
|
|
|
8
8
|
// src/main.ts
|
|
9
9
|
runDaemon(process.env.OCTOPUS_PROXY_CONFIG ?? "proxy.json").catch((error) => {
|
package/dist/remote-worker.js
CHANGED
package/native/remote-windows.cs
CHANGED
|
@@ -11,11 +11,16 @@ using System.Security.Principal;
|
|
|
11
11
|
using System.Text;
|
|
12
12
|
using System.Threading;
|
|
13
13
|
using System.Windows.Forms;
|
|
14
|
+
using Microsoft.Win32.SafeHandles;
|
|
14
15
|
|
|
15
16
|
internal static class WindowsRemote {
|
|
16
17
|
private const int ClipboardLimit = 1024 * 1024;
|
|
17
18
|
private const int MaxLine = 1400000;
|
|
18
19
|
private const int CopyOperation = 0x40CC0020; // SRCCOPY | CAPTUREBLT
|
|
20
|
+
private const int TokenLogonSid = 28;
|
|
21
|
+
private const int UoiUserSid = 4;
|
|
22
|
+
private const uint WinstaReadAttributes = 0x0002;
|
|
23
|
+
private enum DesktopIssue { None = 0, Console = 10, WindowStation = 11, InputDesktop = 12, Logon = 13, Dpi = 14, NoDisplays = 15 }
|
|
19
24
|
private static readonly UTF8Encoding Utf8 = new UTF8Encoding(false, true);
|
|
20
25
|
private static readonly object MessageLock = new object();
|
|
21
26
|
|
|
@@ -23,12 +28,15 @@ internal static class WindowsRemote {
|
|
|
23
28
|
[DllImport("kernel32.dll")] private static extern uint WTSGetActiveConsoleSessionId();
|
|
24
29
|
[DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId();
|
|
25
30
|
[DllImport("user32.dll")] private static extern IntPtr GetProcessWindowStation();
|
|
31
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr OpenWindowStation(string name, bool inherit, uint access);
|
|
32
|
+
[DllImport("user32.dll")] private static extern bool CloseWindowStation(IntPtr station);
|
|
26
33
|
[DllImport("user32.dll")] private static extern IntPtr GetThreadDesktop(uint thread);
|
|
27
34
|
[DllImport("user32.dll")] private static extern IntPtr OpenInputDesktop(uint flags, bool inherit, uint access);
|
|
28
35
|
[DllImport("user32.dll")] private static extern bool CloseDesktop(IntPtr desktop);
|
|
29
36
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern bool GetUserObjectInformation(IntPtr handle, int index, StringBuilder value, uint size, out uint needed);
|
|
30
37
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetUserObjectInformationW", ExactSpelling = true)]
|
|
31
|
-
private static extern bool GetUserObjectSid(IntPtr handle, int index, byte[] value, uint size, out uint needed);
|
|
38
|
+
private static extern bool GetUserObjectSid(IntPtr handle, int index, [Out] byte[] value, uint size, out uint needed);
|
|
39
|
+
[DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation(SafeAccessTokenHandle token, int kind, IntPtr value, uint size, out uint needed);
|
|
32
40
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "LogonUserW", ExactSpelling = true)]
|
|
33
41
|
private static extern bool LogonUser(string user, string domain, IntPtr password, int logonType, int provider, out IntPtr token);
|
|
34
42
|
[DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr handle);
|
|
@@ -104,23 +112,54 @@ internal static class WindowsRemote {
|
|
|
104
112
|
private static uint GetProcessSession() {
|
|
105
113
|
using (var process = Process.GetCurrentProcess()) return (uint)process.SessionId;
|
|
106
114
|
}
|
|
115
|
+
private static readonly Lazy<SecurityIdentifier> ProcessLogonSid = new Lazy<SecurityIdentifier>(ReadLogonSid);
|
|
116
|
+
private static SecurityIdentifier ReadLogonSid() {
|
|
117
|
+
using (var account = WindowsIdentity.GetCurrent()) {
|
|
118
|
+
var token = account.AccessToken;
|
|
119
|
+
uint length;
|
|
120
|
+
GetTokenInformation(token, TokenLogonSid, IntPtr.Zero, 0, out length);
|
|
121
|
+
// TOKEN_GROUPS contains DWORD GroupCount followed by an aligned
|
|
122
|
+
// SID_AND_ATTRIBUTES array. TokenLogonSid returns a single entry.
|
|
123
|
+
if (length < (uint)(3 * IntPtr.Size) || length > 1024) return null;
|
|
124
|
+
IntPtr groups = Marshal.AllocHGlobal((int)length);
|
|
125
|
+
try {
|
|
126
|
+
if (!GetTokenInformation(token, TokenLogonSid, groups, length, out length) || Marshal.ReadInt32(groups) != 1) return null;
|
|
127
|
+
IntPtr sid = Marshal.ReadIntPtr(groups, IntPtr.Size);
|
|
128
|
+
return sid == IntPtr.Zero ? null : new SecurityIdentifier(sid);
|
|
129
|
+
} finally { Marshal.FreeHGlobal(groups); }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
107
132
|
private static string ActiveDesktop() {
|
|
133
|
+
DesktopIssue issue;
|
|
134
|
+
return ActiveDesktop(out issue);
|
|
135
|
+
}
|
|
136
|
+
private static string ActiveDesktop(out DesktopIssue issue) {
|
|
137
|
+
issue = DesktopIssue.Console;
|
|
108
138
|
uint session = WTSGetActiveConsoleSessionId();
|
|
109
139
|
if (session == 0 || session == uint.MaxValue || session != ProcessSession) return null;
|
|
110
|
-
|
|
111
|
-
if (!string.Equals(ObjectName(
|
|
140
|
+
issue = DesktopIssue.WindowStation;
|
|
141
|
+
if (!string.Equals(ObjectName(GetProcessWindowStation()), "WinSta0", StringComparison.OrdinalIgnoreCase)) return null;
|
|
142
|
+
issue = DesktopIssue.InputDesktop;
|
|
112
143
|
if (!string.Equals(ObjectName(GetThreadDesktop(GetCurrentThreadId())), "Default", StringComparison.OrdinalIgnoreCase)) return null;
|
|
113
144
|
IntPtr desktop = OpenInputDesktop(0, false, 1); // DESKTOP_READOBJECTS
|
|
114
145
|
if (desktop == IntPtr.Zero) return null;
|
|
115
146
|
try {
|
|
116
147
|
if (!string.Equals(ObjectName(desktop), "Default", StringComparison.OrdinalIgnoreCase)) return null;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
148
|
+
issue = DesktopIssue.Logon;
|
|
149
|
+
var logon = ProcessLogonSid.Value;
|
|
150
|
+
if (logon == null) return null;
|
|
151
|
+
// WinSta0 belongs to a logon session (S-1-5-5-X-Y), not an
|
|
152
|
+
// account. Re-read its SID so a user switch cannot reuse a lease.
|
|
153
|
+
IntPtr station = OpenWindowStation("WinSta0", false, WinstaReadAttributes);
|
|
154
|
+
if (station == IntPtr.Zero) return null;
|
|
155
|
+
try {
|
|
156
|
+
var sid = new byte[68];
|
|
157
|
+
uint length;
|
|
158
|
+
if (!GetUserObjectSid(station, UoiUserSid, sid, (uint)sid.Length, out length) || length == 0
|
|
159
|
+
|| !logon.Equals(new SecurityIdentifier(sid, 0))) return null;
|
|
160
|
+
issue = DesktopIssue.None;
|
|
161
|
+
return "win32:" + session + ":" + logon.Value;
|
|
162
|
+
} finally { CloseWindowStation(station); }
|
|
124
163
|
} finally { CloseDesktop(desktop); }
|
|
125
164
|
}
|
|
126
165
|
private static List<Display> Displays() {
|
|
@@ -505,15 +544,18 @@ internal static class WindowsRemote {
|
|
|
505
544
|
Console.WriteLine("OK"); return 0;
|
|
506
545
|
}
|
|
507
546
|
if (args.Length == 1 && args[0] == "self-test") return SelfTest() ? 0 : 1;
|
|
508
|
-
|
|
509
|
-
|
|
547
|
+
// Set per-monitor v2 awareness before opening the desktop.
|
|
548
|
+
DesktopIssue issue = DesktopIssue.Dpi;
|
|
549
|
+
string identity = SetProcessDpiAwarenessContext(new IntPtr(-4)) ? ActiveDesktop(out issue) : null;
|
|
510
550
|
if (args.Length == 1 && args[0] == "check") {
|
|
511
551
|
bool ready = identity != null && Displays().Count != 0;
|
|
552
|
+
if (identity != null && !ready) issue = DesktopIssue.NoDisplays;
|
|
512
553
|
string flag = ready ? "true" : "false";
|
|
513
|
-
Console.WriteLine("{\"capture\":" + flag + ",\"input\":" + flag + ",\"session\":" + flag
|
|
554
|
+
Console.WriteLine("{\"capture\":" + flag + ",\"input\":" + flag + ",\"session\":" + flag
|
|
555
|
+
+ ",\"desktopIssue\":" + (int)issue + "}");
|
|
514
556
|
return 0;
|
|
515
557
|
}
|
|
516
|
-
if (identity == null) return
|
|
558
|
+
if (identity == null) return (int)issue;
|
|
517
559
|
if (args.Length == 1 && args[0] == "session") { Console.WriteLine(identity); return 0; }
|
|
518
560
|
var displays = Displays();
|
|
519
561
|
if (args.Length == 1 && args[0] == "list") {
|