feat: turn installer into number menu with start/stop/status

Open install.ps1/sh to choose install, start dsh web in a new window, stop listeners, status, or open the browser.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
owo
2026-08-14 17:53:22 +08:00
co-authored by Cursor
parent f8501a92f8
commit 4ac16aa82f
3 changed files with 478 additions and 197 deletions
+38 -43
View File
@@ -18,7 +18,7 @@ Telegram **手机遥控器** for DeepSeek Harness:附着本机正在跑的 Web
---
### 一键安装(推荐)
### 一键管理菜单(推荐)
**先准备两样东西:**
@@ -31,66 +31,56 @@ Telegram **手机遥控器** for DeepSeek Harness:附着本机正在跑的 Web
#### Windows
> 下面命令请在 **PowerShell** 里执行(开始菜单搜 “Windows PowerShell” / “终端”)。
> 若窗口标题是 **命令提示符 / CMD**,先输入 `powershell` 回车,再贴命令。
**方式 A — 远程一键(交互输入 Token / User ID):**
> 请在 **PowerShell** 执行。若当前是 **CMD**,用下面「CMD 一键」那行。
```powershell
irm https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | iex
```
等价写法(更不易混淆):
```powershell
Invoke-RestMethod https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | Invoke-Expression
```
**CMD 用户一键进入并执行:**
**CMD 一键:**
```bat
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | iex"
```
**方式 B — 带参数(不交互):**
启动后用**数字**选择:
```powershell
git clone https://github.com/hi-wenw/dsh-telegram-channel.git
cd dsh-telegram-channel
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\install.ps1 -Token '这里粘贴Token' -UserId '这里粘贴数字ID'
```
1) 安装 / 重装插件(写入 Token + 白名单)
2) 启动 dsh web(新窗口)
3) 停止 dsh web
4) 查看状态
5) 打开浏览器
0) 退出
```
脚本会自动:
也可直接指定动作(不进菜单):
1. 写入用户环境变量 `DSH_TELEGRAM_TOKEN`、`DSH_TELEGRAM_ALLOWED_USER_IDS`
2. 给 profile 补上 `allowBuilds`(防止 pnpm 拦截)
3. 执行 `dsh plugin --profile web add github:hi-wenw/dsh-telegram-channel`
```powershell
.\scripts\install.ps1 -Action install -Token '...' -UserId '123456789'
.\scripts\install.ps1 -Action start
.\scripts\install.ps1 -Action stop
.\scripts\install.ps1 -Action status
```
**不会**再 `insert` 同名插件行(避免 `duplicate loader entry id`)。
安装时脚本会:写环境变量、补 `allowBuilds`、执行 `dsh plugin add`(**不会**再 insert 同名 id)。
#### macOS / Linux
```bash
export DSH_TELEGRAM_TOKEN='你的Token'
export DSH_TELEGRAM_ALLOWED_USER_IDS='你的数字ID'
curl -fsSL https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.sh | bash
# 同样出现数字菜单;或:
# ./scripts/install.sh install --token '...' --user-id '...'
# ./scripts/install.sh start|stop|status
```
或克隆后:`./scripts/install.sh --token '...' --user-id '...'`
---
### 安装后只用三步
### 手机怎么用
```powershell
# 1. 关掉旧终端,新开一个,然后:
dsh web
# 2. 浏览器打开一个对话(保持运行)
# 3. 手机对 Bot:
# /start → /sessions → 点选 → 正常聊天
```
1. 菜单选 **2** 启动 `dsh web`(或自己运行 `dsh web`)
2. 浏览器打开一个对话并保持运行
3. 手机对 Bot:`/start` → `/sessions` → 点选 → 聊天
输入框旁的 **/** 菜单应有:`start` `sessions` `status` `unbind` `help`。
@@ -185,25 +175,30 @@ npm run build
Telegram **mobile remote** for **live** DeepSeek Harness Web agents. Desktop/Web is the source of truth; the phone **attaches** (no parallel hidden agent).
### One-click install (Windows)
### One-click manager (Windows)
Run in **PowerShell** (not CMD). Opens a number menu: install / start / stop / status / open browser.
```powershell
irm https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | iex
```
Or with params after clone:
CMD:
```powershell
.\scripts\install.ps1 -Token '...' -UserId '123456789'
```bat
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | iex"
```
The script sets user env vars, ensures `allowBuilds`, and runs `dsh plugin add`. Then:
Direct actions:
```powershell
dsh web
# open a Web chat → phone: /sessions → bind
.\scripts\install.ps1 -Action start
.\scripts\install.ps1 -Action stop
.\scripts\install.ps1 -Action install -Token '...' -UserId '123456789'
```
The script sets user env vars, ensures `allowBuilds`, and runs `dsh plugin add`. After **start**, open a Web chat → phone: `/sessions` → bind.
### Unix
```bash
+313 -88
View File
@@ -1,31 +1,24 @@
#Requires -Version 5.1
#Requires -Version 5.1
<#
.SYNOPSIS
One-click install for dsh-telegram-channel (DeepSeek Harness Telegram remote).
.DESCRIPTION
1) Writes user env: DSH_TELEGRAM_TOKEN + DSH_TELEGRAM_ALLOWED_USER_IDS
2) Ensures allowBuilds for the plugin (belt-and-suspenders)
3) Runs: dsh plugin --profile <name> add <source>
Does NOT edit cordis.patch.yml (avoids duplicate loader entry).
dsh-telegram-channel manager menu: install / start / stop / status.
.EXAMPLE
# Interactive (prompts for token + user id)
irm https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.ps1 | iex
.EXAMPLE
.\scripts\install.ps1 -Token '123:AA...' -UserId '7057906059'
.EXAMPLE
# Install from this cloned repo instead of GitHub
.\scripts\install.ps1 -Token '...' -UserId '...' -Local
.\scripts\install.ps1 -Action start
.\scripts\install.ps1 -Action install -Token '...' -UserId '123'
#>
[CmdletBinding()]
param(
[ValidateSet('', 'menu', 'install', 'start', 'stop', 'status', 'open')]
[string] $Action = '',
[string] $Token = $env:DSH_TELEGRAM_TOKEN,
[string] $UserId = $env:DSH_TELEGRAM_ALLOWED_USER_IDS,
[string] $ProfileName = 'web',
[string] $Source = 'github:hi-wenw/dsh-telegram-channel',
[int] $Port = 3080,
[switch] $Local,
[switch] $NoPersist
)
@@ -35,121 +28,353 @@ $ErrorActionPreference = 'Stop'
function Write-Step([string] $Message) {
Write-Host ""
Write-Host "==> $Message" -ForegroundColor Cyan
Write-Host ("==> " + $Message) -ForegroundColor Cyan
}
function Write-Ok([string] $Message) {
Write-Host $Message -ForegroundColor Green
}
function Write-Warn2([string] $Message) {
Write-Host $Message -ForegroundColor Yellow
}
function Ensure-Command([string] $Name) {
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "找不到命令 '$Name'。请先安装并确保 DeepSeek Harness CLI(dsh)在 PATH 中。"
throw ("Command not found: " + $Name + ". Install DeepSeek Harness CLI (dsh) and add it to PATH.")
}
}
function Get-DshHome {
if ($env:DSH_HOME) { return $env:DSH_HOME }
return (Join-Path $env:USERPROFILE '.dsh')
}
function Get-ProfileDir {
$dir = Join-Path (Get-DshHome) ('profiles\' + $ProfileName)
if (-not (Test-Path -LiteralPath $dir)) {
throw ("Profile directory not found: " + $dir + "`nRun dsh web once first.")
}
return $dir
}
function Set-UserEnv([string] $Name, [string] $Value) {
Set-Item -Path "Env:$Name" -Value $Value
Set-Item -Path ("Env:" + $Name) -Value $Value
if (-not $NoPersist) {
[Environment]::SetEnvironmentVariable($Name, $Value, 'User')
}
}
function Get-UserEnv([string] $Name) {
$v = [Environment]::GetEnvironmentVariable($Name, 'User')
if ($v) { return $v }
return [Environment]::GetEnvironmentVariable($Name, 'Process')
}
function Ensure-AllowBuilds([string] $ProfileDir) {
$path = Join-Path $ProfileDir 'pnpm-workspace.yaml'
$block = @'
allowBuilds:
dsh-telegram-channel: true
'@
$blockLines = @(
'allowBuilds:'
' dsh-telegram-channel: true'
)
$block = $blockLines -join "`r`n"
if (-not (Test-Path -LiteralPath $path)) {
$content = @"
packages:
- .
nodeLinker: hoisted
autoInstallPeers: false
$block
"@
$content = @(
'packages:'
' - .'
''
'nodeLinker: hoisted'
'autoInstallPeers: false'
''
$block
) -join "`r`n"
Set-Content -LiteralPath $path -Value $content -Encoding utf8
Write-Host "已创建 $path (allowBuilds)"
Write-Host ("Created " + $path + " (allowBuilds)")
return
}
$raw = Get-Content -LiteralPath $path -Raw
if ($raw -match '(?m)^\s*dsh-telegram-channel\s*:\s*true\s*$') {
Write-Host "allowBuilds 已存在,跳过"
Write-Host 'allowBuilds already present, skip'
return
}
if ($raw -match '(?m)^allowBuilds\s*:') {
if ($raw -notmatch '(?m)^allowBuilds\s*:[\s\S]*?dsh-telegram-channel\s*:') {
$raw = $raw -replace '(?m)^(allowBuilds\s*:)', "`$1`r`n dsh-telegram-channel: true"
$raw = $raw -replace '(?m)^(allowBuilds\s*:)', ('$1' + "`r`n dsh-telegram-channel: true")
$out = ($raw.TrimEnd() + "`r`n")
Set-Content -LiteralPath $path -Value $out -Encoding utf8
Write-Host "已写入 allowBuilds.dsh-telegram-channel → $path"
Write-Host ("Wrote allowBuilds.dsh-telegram-channel -> " + $path)
}
return
}
Add-Content -LiteralPath $path -Value "`r`n$block`r`n" -Encoding utf8
Write-Host "已追加 allowBuilds → $path"
Add-Content -LiteralPath $path -Value ("`r`n" + $block + "`r`n") -Encoding utf8
Write-Host ("Appended allowBuilds -> " + $path)
}
Write-Host "dsh-telegram-channel installer" -ForegroundColor Green
Write-Host "Profile: $ProfileName"
Ensure-Command 'dsh'
if (-not $Token) {
$Token = Read-Host '粘贴 BotFather 的 Bot Token(不要发到公开群)'
}
$Token = ([string]$Token).Trim()
if (-not $Token) { throw '缺少 Bot Token。' }
if (-not $UserId) {
$UserId = Read-Host '粘贴你的 Telegram 数字 User ID(@userinfobot)'
}
$UserId = ([string]$UserId).Trim()
if ($UserId -notmatch '^\d+(?:\s*,\s*\d+)*$') {
throw "User ID 必须是数字(多个用逗号分隔),收到: '$UserId'"
}
$UserId = ($UserId -replace '\s+', '')
Write-Step '写入用户环境变量(当前进程 + 用户级永久)'
Set-UserEnv 'DSH_TELEGRAM_TOKEN' $Token
Set-UserEnv 'DSH_TELEGRAM_ALLOWED_USER_IDS' $UserId
Write-Host "DSH_TELEGRAM_TOKEN = (已设置,长度 $($Token.Length))"
Write-Host "DSH_TELEGRAM_ALLOWED_USER_IDS = $UserId"
if ($NoPersist) {
Write-Host '(-NoPersist:未写入用户级环境变量)' -ForegroundColor Yellow
} else {
Write-Host '已写入用户级环境变量。请关闭旧终端后再开新的。' -ForegroundColor Yellow
function Get-ListenPids([int] $ListenPort) {
$pids = @()
try {
$pids = @(
Get-NetTCPConnection -LocalPort $ListenPort -State Listen -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique
)
} catch {
$pids = @()
}
if ($pids.Count -eq 0) {
$lines = netstat -ano | Select-String -Pattern 'LISTENING' | Select-String -Pattern (':' + $ListenPort + '\s')
foreach ($line in $lines) {
if ($line -match '\s(\d+)\s*$') {
$pids += [int]$Matches[1]
}
}
$pids = @($pids | Select-Object -Unique)
}
return $pids
}
$dshHome = if ($env:DSH_HOME) { $env:DSH_HOME } else { Join-Path $env:USERPROFILE '.dsh' }
$profileDir = Join-Path $dshHome "profiles\$ProfileName"
if (-not (Test-Path -LiteralPath $profileDir)) {
throw "找不到 profile 目录: $profileDir (请先成功跑过一次 dsh web)"
function Get-DshWebPids {
$found = @()
Get-CimInstance Win32_Process -Filter "Name='node.exe'" -ErrorAction SilentlyContinue | ForEach-Object {
$cmd = [string]$_.CommandLine
if ($cmd -match 'bin\.js' -and $cmd -match '\bweb\b' -and $cmd -match 'deepseek-ai') {
$found += [int]$_.ProcessId
} elseif ($cmd -match 'dsh\\lib\\bin\.js' -and $cmd -match '\bweb\b') {
$found += [int]$_.ProcessId
}
}
return @($found | Select-Object -Unique)
}
Write-Step '确保 pnpm allowBuilds(防止 git 依赖 prepare 被拦)'
Ensure-AllowBuilds $profileDir
function Show-Status {
Write-Step ('Status (profile=' + $ProfileName + ', port=' + $Port + ')')
if ($Local) {
$scriptRoot = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
$repoRoot = Resolve-Path (Join-Path $scriptRoot '..')
$Source = $repoRoot.Path
$token = Get-UserEnv 'DSH_TELEGRAM_TOKEN'
$allow = Get-UserEnv 'DSH_TELEGRAM_ALLOWED_USER_IDS'
if ($token) { Write-Ok ('Token: set (length ' + $token.Length + ')') } else { Write-Warn2 'Token: NOT set (DSH_TELEGRAM_TOKEN)' }
if ($allow) { Write-Ok ('Allowlist: ' + $allow) } else { Write-Warn2 'Allowlist: NOT set (DSH_TELEGRAM_ALLOWED_USER_IDS)' }
$profileDir = $null
try { $profileDir = Get-ProfileDir } catch { Write-Warn2 $_.Exception.Message }
if ($profileDir) {
$pkg = Join-Path $profileDir 'package.json'
if (Test-Path -LiteralPath $pkg) {
$raw = Get-Content -LiteralPath $pkg -Raw
if ($raw -match 'dsh-telegram-channel') {
Write-Ok ('Plugin: found in ' + $pkg)
} else {
Write-Warn2 'Plugin: not in package.json yet (choose 1 to install)'
}
}
}
$listen = @(Get-ListenPids $Port)
if ($listen.Count -gt 0) {
Write-Ok ('dsh web: RUNNING http://127.0.0.1:' + $Port + ' (PID ' + ($listen -join ', ') + ')')
} else {
Write-Warn2 ('dsh web: not listening on port ' + $Port + ' (choose 2 to start)')
}
}
Write-Step "安装插件: dsh plugin --profile $ProfileName add $Source"
& dsh plugin --profile $ProfileName add $Source
if ($LASTEXITCODE -ne 0) {
throw "dsh plugin add 失败(exit=$LASTEXITCODE)。可尝试: dsh plugin --profile $ProfileName remove dsh-telegram-channel 后再跑本脚本。"
function Invoke-Install {
Ensure-Command 'dsh'
$useToken = $Token
$useUserId = $UserId
if (-not $useToken) { $useToken = Get-UserEnv 'DSH_TELEGRAM_TOKEN' }
if (-not $useUserId) { $useUserId = Get-UserEnv 'DSH_TELEGRAM_ALLOWED_USER_IDS' }
if (-not $useToken) {
$useToken = Read-Host 'Paste BotFather Bot Token'
}
$useToken = ([string]$useToken).Trim()
if (-not $useToken) { throw 'Missing Bot Token.' }
if (-not $useUserId) {
$useUserId = Read-Host 'Paste Telegram numeric User ID (@userinfobot)'
}
$useUserId = ([string]$useUserId).Trim()
if ($useUserId -notmatch '^[0-9]+(,[0-9]+)*$') {
throw ("User ID must be numeric (comma-separated ok), got: '" + $useUserId + "'")
}
$useUserId = ($useUserId -replace '\s+', '')
Write-Step 'Write user environment variables'
Set-UserEnv 'DSH_TELEGRAM_TOKEN' $useToken
Set-UserEnv 'DSH_TELEGRAM_ALLOWED_USER_IDS' $useUserId
Write-Host ('DSH_TELEGRAM_TOKEN = (set, length ' + $useToken.Length + ')')
Write-Host ('DSH_TELEGRAM_ALLOWED_USER_IDS = ' + $useUserId)
if ($NoPersist) {
Write-Warn2 '(-NoPersist: not written to user-level env)'
}
$profileDir = Get-ProfileDir
Write-Step 'Ensure pnpm allowBuilds'
Ensure-AllowBuilds $profileDir
$installSource = $Source
if ($Local) {
$scriptRoot = $PSScriptRoot
if (-not $scriptRoot -and $MyInvocation.MyCommand.Path) {
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
}
if (-not $scriptRoot) {
throw 'Cannot use -Local with remote irm|iex. git clone first, then run local script.'
}
$installSource = (Resolve-Path (Join-Path $scriptRoot '..')).Path
}
Write-Step ('Install plugin: dsh plugin --profile ' + $ProfileName + ' add ' + $installSource)
& dsh plugin --profile $ProfileName add $installSource
if ($LASTEXITCODE -ne 0) {
throw ('dsh plugin add failed (exit=' + $LASTEXITCODE + '). Try: dsh plugin --profile ' + $ProfileName + ' remove dsh-telegram-channel')
}
Write-Ok 'Install complete.'
Write-Host 'Next: choose 2 to start dsh web -> open a Web chat -> phone /sessions'
}
Write-Host ""
Write-Host '安装完成。接下来:' -ForegroundColor Green
Write-Host ' 1. 关掉所有旧终端,新开 PowerShell'
Write-Host ' 2. 确认代理(若需要访问 Telegram)已开启'
Write-Host ' 3. 运行: dsh web'
Write-Host ' 4. 浏览器打开一个对话'
Write-Host ' 5. 手机对 Bot 发 /start → /sessions → 点选附着'
Write-Host ""
Write-Host '命令菜单应出现: /start /sessions /status /unbind /help'
function Invoke-Start {
Ensure-Command 'dsh'
$listen = @(Get-ListenPids $Port)
if ($listen.Count -gt 0) {
Write-Warn2 ('Already running: http://127.0.0.1:' + $Port + ' (PID ' + ($listen -join ', ') + ')')
Write-Host 'To restart: choose 3 (stop), then 2 (start).'
return
}
$token = Get-UserEnv 'DSH_TELEGRAM_TOKEN'
$allow = Get-UserEnv 'DSH_TELEGRAM_ALLOWED_USER_IDS'
if (-not $token) { Write-Warn2 'Warning: DSH_TELEGRAM_TOKEN not set; bot may not poll.' }
if (-not $allow) { Write-Warn2 'Warning: DSH_TELEGRAM_ALLOWED_USER_IDS not set; all users denied.' }
Write-Step 'Start dsh web in a new window (close that window to stop)'
$lines = @(
"`$env:DSH_TELEGRAM_TOKEN = [Environment]::GetEnvironmentVariable('DSH_TELEGRAM_TOKEN','User')"
"`$env:DSH_TELEGRAM_ALLOWED_USER_IDS = [Environment]::GetEnvironmentVariable('DSH_TELEGRAM_ALLOWED_USER_IDS','User')"
"if (-not `$env:DSH_TELEGRAM_TOKEN) { `$env:DSH_TELEGRAM_TOKEN = [Environment]::GetEnvironmentVariable('DSH_TELEGRAM_TOKEN','Process') }"
"if (-not `$env:DSH_TELEGRAM_ALLOWED_USER_IDS) { `$env:DSH_TELEGRAM_ALLOWED_USER_IDS = [Environment]::GetEnvironmentVariable('DSH_TELEGRAM_ALLOWED_USER_IDS','Process') }"
"Write-Host 'dsh-telegram-channel: starting dsh web ...' -ForegroundColor Cyan"
"Write-Host ('Token length: ' + (`$env:DSH_TELEGRAM_TOKEN + '').Length)"
"Write-Host ('Allow IDs: ' + `$env:DSH_TELEGRAM_ALLOWED_USER_IDS)"
"dsh web"
)
$psCommand = $lines -join '; '
Start-Process -FilePath 'powershell.exe' -ArgumentList @(
'-NoExit',
'-NoProfile',
'-ExecutionPolicy', 'Bypass',
'-Command', $psCommand
) | Out-Null
Write-Host 'New window opened. Waiting for port...'
$ok = $false
for ($i = 0; $i -lt 40; $i++) {
Start-Sleep -Milliseconds 500
if ((Get-ListenPids $Port).Count -gt 0) {
$ok = $true
break
}
}
if ($ok) {
Write-Ok ('Started: http://127.0.0.1:' + $Port)
} else {
Write-Warn2 'Port not listening yet. Check the new window for errors.'
}
}
function Invoke-Stop {
Write-Step ('Stop dsh web (port ' + $Port + ')')
$pids = @()
$pids += Get-ListenPids $Port
$pids += Get-DshWebPids
$pids = @($pids | Where-Object { $_ -gt 0 } | Select-Object -Unique)
if ($pids.Count -eq 0) {
Write-Warn2 'No running dsh web found.'
return
}
foreach ($procId in $pids) {
try {
$p = Get-Process -Id $procId -ErrorAction SilentlyContinue
$name = if ($p) { $p.ProcessName } else { '?' }
Write-Host ('Kill PID ' + $procId + ' (' + $name + ')')
Stop-Process -Id $procId -Force -ErrorAction Stop
} catch {
Write-Warn2 ('Cannot kill PID ' + $procId + ': ' + $_.Exception.Message)
}
}
Start-Sleep -Seconds 1
$left = @(Get-ListenPids $Port)
if ($left.Count -eq 0) {
Write-Ok 'Stopped.'
} else {
Write-Warn2 ('Port still in use: PID ' + ($left -join ', '))
}
}
function Invoke-Open {
$url = ('http://127.0.0.1:' + $Port)
$listen = @(Get-ListenPids $Port)
if ($listen.Count -eq 0) {
Write-Warn2 'Service not running. Choose 2 to start first.'
return
}
Start-Process $url
Write-Ok ('Opened ' + $url)
}
function Show-Menu {
Write-Host ''
Write-Host '========================================' -ForegroundColor Green
Write-Host ' dsh-telegram-channel manager' -ForegroundColor Green
Write-Host '========================================' -ForegroundColor Green
Write-Host (' profile = ' + $ProfileName + ' port = ' + $Port)
Write-Host ''
Write-Host ' 1) Install / reinstall plugin (Token + allowlist)'
Write-Host ' 2) Start dsh web (new window)'
Write-Host ' 3) Stop dsh web'
Write-Host ' 4) Status'
Write-Host ' 5) Open browser'
Write-Host ' 0) Exit'
Write-Host ''
}
function Invoke-MenuLoop {
while ($true) {
Show-Menu
$choice = Read-Host 'Enter number'
switch ($choice.Trim()) {
'1' { try { Invoke-Install } catch { Write-Host $_.Exception.Message -ForegroundColor Red } }
'2' { try { Invoke-Start } catch { Write-Host $_.Exception.Message -ForegroundColor Red } }
'3' { try { Invoke-Stop } catch { Write-Host $_.Exception.Message -ForegroundColor Red } }
'4' { try { Show-Status } catch { Write-Host $_.Exception.Message -ForegroundColor Red } }
'5' { try { Invoke-Open } catch { Write-Host $_.Exception.Message -ForegroundColor Red } }
'0' { Write-Host 'Bye.'; return }
'' { continue }
default { Write-Warn2 ('Invalid option: ' + $choice) }
}
}
}
if (-not $Action) {
if ($PSBoundParameters.ContainsKey('Token') -or $PSBoundParameters.ContainsKey('UserId') -or $Local) {
$Action = 'install'
} else {
$Action = 'menu'
}
}
switch ($Action) {
'menu' { Invoke-MenuLoop }
'install' { Invoke-Install }
'start' { Invoke-Start }
'stop' { Invoke-Stop }
'status' { Show-Status }
'open' { Invoke-Open }
default { throw ('Unknown Action: ' + $Action) }
}
+127 -66
View File
@@ -1,19 +1,16 @@
#!/usr/bin/env bash
# One-click-ish install for dsh-telegram-channel (macOS / Linux).
# Usage:
# export DSH_TELEGRAM_TOKEN='123:AA...'
# export DSH_TELEGRAM_ALLOWED_USER_IDS='7057906059'
# curl -fsSL https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.sh | bash
# Or:
# ./scripts/install.sh --token '...' --user-id '...'
# dsh-telegram-channel manager (install / start / stop / status)
# curl -fsSL https://raw.githubusercontent.com/hi-wenw/dsh-telegram-channel/master/scripts/install.sh | bash
set -euo pipefail
PROFILE_NAME="${DSH_PROFILE:-web}"
SOURCE="${DSH_TELEGRAM_SOURCE:-github:hi-wenw/dsh-telegram-channel}"
TOKEN="${DSH_TELEGRAM_TOKEN:-}"
USER_ID="${DSH_TELEGRAM_ALLOWED_USER_IDS:-}"
PORT="${DSH_WEB_PORT:-3080}"
LOCAL=0
NO_PERSIST=0
ACTION="menu"
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -21,35 +18,29 @@ while [[ $# -gt 0 ]]; do
--user-id) USER_ID="${2:-}"; shift 2 ;;
--profile) PROFILE_NAME="${2:-}"; shift 2 ;;
--source) SOURCE="${2:-}"; shift 2 ;;
--port) PORT="${2:-}"; shift 2 ;;
--local) LOCAL=1; shift ;;
--no-persist) NO_PERSIST=1; shift ;;
menu|install|start|stop|status) ACTION="$1"; shift ;;
-h|--help)
sed -n '2,12p' "$0"
cat <<EOF
Usage: install.sh [menu|install|start|stop|status] [--token T] [--user-id ID]
EOF
exit 0
;;
*) echo "Unknown arg: $1" >&2; exit 1 ;;
esac
done
need_dsh() {
command -v dsh >/dev/null 2>&1 || { echo "dsh not found in PATH" >&2; exit 1; }
}
if ! command -v dsh >/dev/null 2>&1; then
echo "dsh not found in PATH" >&2
exit 1
fi
if [[ -z "$TOKEN" ]]; then
read -r -p "Bot Token: " TOKEN
fi
if [[ -z "$USER_ID" ]]; then
read -r -p "Telegram numeric User ID: " USER_ID
fi
USER_ID="$(echo "$USER_ID" | tr -d '[:space:]')"
if ! [[ "$USER_ID" =~ ^[0-9]+(,[0-9]+)*$ ]]; then
echo "User ID must be numeric (comma-separated ok)" >&2
exit 1
fi
export DSH_TELEGRAM_TOKEN="$TOKEN"
export DSH_TELEGRAM_ALLOWED_USER_IDS="$USER_ID"
profile_dir() {
local home="${DSH_HOME:-$HOME/.dsh}"
local dir="$home/profiles/$PROFILE_NAME"
[[ -d "$dir" ]] || { echo "Profile dir missing: $dir (run dsh web once first)" >&2; exit 1; }
echo "$dir"
}
persist_env() {
local name="$1" value="$2"
@@ -57,7 +48,6 @@ persist_env() {
for f in "$HOME/.bashrc" "$HOME/.zshrc"; do
[[ -f "$f" ]] || continue
if grep -q "^export ${name}=" "$f" 2>/dev/null; then
# portable-ish replace
tmp="$(mktemp)"
grep -v "^export ${name}=" "$f" >"$tmp" || true
echo "$line" >>"$tmp"
@@ -68,22 +58,10 @@ persist_env() {
done
}
if [[ "$NO_PERSIST" -eq 0 ]]; then
persist_env DSH_TELEGRAM_TOKEN "$TOKEN"
persist_env DSH_TELEGRAM_ALLOWED_USER_IDS "$USER_ID"
echo "Wrote exports to ~/.bashrc and/or ~/.zshrc (if present)."
fi
DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
PROFILE_DIR="$DSH_HOME/profiles/$PROFILE_NAME"
if [[ ! -d "$PROFILE_DIR" ]]; then
echo "Profile dir missing: $PROFILE_DIR (run dsh web once first)" >&2
exit 1
fi
WS="$PROFILE_DIR/pnpm-workspace.yaml"
if [[ ! -f "$WS" ]]; then
cat >"$WS" <<'EOF'
ensure_allow_builds() {
local ws="$1/pnpm-workspace.yaml"
if [[ ! -f "$ws" ]]; then
cat >"$ws" <<'EOF'
packages:
- .
@@ -93,32 +71,115 @@ autoInstallPeers: false
allowBuilds:
dsh-telegram-channel: true
EOF
elif ! grep -q 'dsh-telegram-channel:' "$WS"; then
if grep -q '^allowBuilds:' "$WS"; then
awk '
BEGIN{done=0}
/^allowBuilds:/{print; print " dsh-telegram-channel: true"; done=1; next}
{print}
END{if(!done){print ""; print "allowBuilds:"; print " dsh-telegram-channel: true"}}
' "$WS" >"$WS.tmp" && mv "$WS.tmp" "$WS"
else
printf '\nallowBuilds:\n dsh-telegram-channel: true\n' >>"$WS"
elif ! grep -q 'dsh-telegram-channel:' "$ws"; then
if grep -q '^allowBuilds:' "$ws"; then
awk 'BEGIN{done=0} /^allowBuilds:/{print; print " dsh-telegram-channel: true"; done=1; next} {print} END{if(!done){print ""; print "allowBuilds:"; print " dsh-telegram-channel: true"}}' "$ws" >"$ws.tmp" && mv "$ws.tmp" "$ws"
else
printf '\nallowBuilds:\n dsh-telegram-channel: true\n' >>"$ws"
fi
fi
fi
}
if [[ "$LOCAL" -eq 1 ]]; then
SOURCE="$(cd "$(dirname "$0")/.." && pwd)"
fi
listen_pids() {
if command -v lsof >/dev/null 2>&1; then
lsof -tiTCP:"$PORT" -sTCP:LISTEN 2>/dev/null || true
elif command -v ss >/dev/null 2>&1; then
ss -ltnp 2>/dev/null | awk -v p=":$PORT" '$4 ~ p {print}' | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | sort -u
fi
}
echo "==> dsh plugin --profile $PROFILE_NAME add $SOURCE"
dsh plugin --profile "$PROFILE_NAME" add "$SOURCE"
do_status() {
echo "==> status profile=$PROFILE_NAME port=$PORT"
if [[ -n "${DSH_TELEGRAM_TOKEN:-}" ]]; then echo "Token: set (len=${#DSH_TELEGRAM_TOKEN})"; else echo "Token: missing"; fi
if [[ -n "${DSH_TELEGRAM_ALLOWED_USER_IDS:-}" ]]; then echo "Allow: $DSH_TELEGRAM_ALLOWED_USER_IDS"; else echo "Allow: missing"; fi
local pids
pids="$(listen_pids | tr '\n' ' ')"
if [[ -n "${pids// /}" ]]; then echo "dsh web: running PIDs $pids http://127.0.0.1:$PORT"; else echo "dsh web: not listening"; fi
}
cat <<EOF
do_install() {
need_dsh
[[ -n "$TOKEN" ]] || read -r -p "Bot Token: " TOKEN
[[ -n "$USER_ID" ]] || read -r -p "Telegram numeric User ID: " USER_ID
USER_ID="$(echo "$USER_ID" | tr -d '[:space:]')"
[[ "$USER_ID" =~ ^[0-9]+(,[0-9]+)*$ ]] || { echo "bad user id" >&2; exit 1; }
export DSH_TELEGRAM_TOKEN="$TOKEN"
export DSH_TELEGRAM_ALLOWED_USER_IDS="$USER_ID"
if [[ "$NO_PERSIST" -eq 0 ]]; then
persist_env DSH_TELEGRAM_TOKEN "$TOKEN"
persist_env DSH_TELEGRAM_ALLOWED_USER_IDS "$USER_ID"
fi
local dir
dir="$(profile_dir)"
ensure_allow_builds "$dir"
if [[ "$LOCAL" -eq 1 ]]; then
SOURCE="$(cd "$(dirname "$0")/.." && pwd)"
fi
echo "==> dsh plugin --profile $PROFILE_NAME add $SOURCE"
dsh plugin --profile "$PROFILE_NAME" add "$SOURCE"
echo "Install done. Use menu option 2 / ./install.sh start"
}
Install done. Next:
1. Open a new shell (so env vars load)
2. Run: dsh web
3. Open a Web conversation
4. On phone: /start → /sessions → bind
do_start() {
need_dsh
if [[ -n "$(listen_pids)" ]]; then
echo "Already running on :$PORT"
return 0
fi
echo "==> starting dsh web in background (log: /tmp/dsh-web.log)"
nohup dsh web >/tmp/dsh-web.log 2>&1 &
echo $! >/tmp/dsh-web.pid
sleep 1
do_status
}
do_stop() {
echo "==> stop"
local pids
pids="$(listen_pids)"
if [[ -z "$pids" && -f /tmp/dsh-web.pid ]]; then
pids="$(cat /tmp/dsh-web.pid 2>/dev/null || true)"
fi
if [[ -z "$pids" ]]; then
echo "Not running"
return 0
fi
echo "$pids" | xargs -r kill 2>/dev/null || echo "$pids" | xargs kill 2>/dev/null || true
rm -f /tmp/dsh-web.pid
echo "Stopped"
}
do_menu() {
while true; do
cat <<EOF
========================================
dsh-telegram-channel manager
========================================
1) install / reinstall
2) start dsh web
3) stop dsh web
4) status
0) quit
EOF
read -r -p "Choice: " c
case "$c" in
1) do_install ;;
2) do_start ;;
3) do_stop ;;
4) do_status ;;
0) echo Bye; exit 0 ;;
*) echo "invalid" ;;
esac
done
}
case "$ACTION" in
menu) do_menu ;;
install) do_install ;;
start) do_start ;;
stop) do_stop ;;
status) do_status ;;
*) echo "Unknown action: $ACTION" >&2; exit 1 ;;
esac