@ziniao-open/cli 1.0.7 → 1.1.2

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 CHANGED
@@ -10,6 +10,43 @@
10
10
  npm install -g @ziniao-open/cli
11
11
  ```
12
12
 
13
+ 紫鸟客户端自带的 `ziniao` CLI 需要同步到 AppCache 并加入 PATH。执行 `npm install -g @ziniao-open/cli` 时会自动尝试同步;安装紫鸟客户端后,也可以手动执行以下命令:
14
+
15
+ ```bash
16
+ source "$(npm root -g)/@ziniao-open/cli/scripts/setup-ziniao-cli.sh"
17
+ ```
18
+
19
+ Windows PowerShell 执行:
20
+
21
+ ```powershell
22
+ . "$(npm root -g)\@ziniao-open\cli\scripts\setup-ziniao-cli.ps1"
23
+ ```
24
+
25
+ 也可以在安装后手动修复:
26
+
27
+ ```bash
28
+ ziniao-cli doctor fix-path
29
+ ```
30
+
31
+ 遍历当前 PATH 清理旧的 `ziniao` shim,并清理 Unix 端的 `ziniaobrowser/cli` 缓存目录(默认只预览):
32
+
33
+ ```powershell
34
+ & "$(npm root -g)\@ziniao-open\cli\scripts\remove-ziniao-cli-shims.ps1"
35
+ & "$(npm root -g)\@ziniao-open\cli\scripts\remove-ziniao-cli-shims.ps1" -Apply
36
+ ```
37
+
38
+ macOS/Linux:
39
+
40
+ ```bash
41
+ "$(npm root -g)/@ziniao-open/cli/scripts/remove-ziniao-cli-shims.sh"
42
+ "$(npm root -g)/@ziniao-open/cli/scripts/remove-ziniao-cli-shims.sh" --apply
43
+ ```
44
+
45
+ 脚本只探测 `ziniao.app` / `ziniao` 的安装目录;Windows 会优先查询卸载注册表中 `DisplayIcon` 包含 `ziniao.exe` 的条目,再从本体目录的 `resources/app.asar.unpacked/envkit/cli` 查找 CLI。Windows 使用 `%APPDATA%\ziniaobrowser\cli`,macOS 使用 `~/Library/Application Support/ziniaobrowser/cli`,Linux 使用 `${XDG_CONFIG_HOME:-~/.config}/ziniaobrowser/cli`,在目标目录缺少 CLI 或文件内容发生变化时同步最新文件,并注册当前用户 PATH。找不到源文件时只提示并继续,不阻断 PATH 配置。若安装目录无法自动探测,可设置 `ZINIAO_INSTALL_DIR`;若目标位置不同,可设置 `ZINIAO_APP_CACHE_DIR`。
46
+ Windows 同时会把找到的紫鸟本体绝对路径写入 `%APPDATA%\ziniaobrowser\gui-path`,供 CLI 定位桌面客户端。
47
+ macOS/Linux 也会在找到桌面本体时写入对应的 `gui-path`(macOS 为 `~/Library/Application Support/ziniaobrowser/gui-path`,Linux 为 `${XDG_CONFIG_HOME:-~/.config}/ziniaobrowser/gui-path`)。
48
+ macOS/Linux 还会在 `~/.local/bin/ziniao` 创建固定 shim,避免向随机 PATH 目录写入文件。
49
+
13
50
  安装时会自动下载当前平台对应的预编译二进制(支持 macOS / Linux / Windows × amd64 / arm64),并做 SHA-256 校验。
14
51
 
15
52
  ## 快速开始
@@ -46,14 +83,49 @@ ziniao-cli --help
46
83
 
47
84
  完整列表见 `ziniao-cli --help`。
48
85
 
86
+ ## 本地 dev/prod 打包与切换
87
+
88
+ 在仓库根目录执行以下命令,可分别生成本地测试环境包和本地正式环境包:
89
+
90
+ ```bash
91
+ make package-dev
92
+ make package-prod
93
+ ```
94
+
95
+ 两个命令都会生成本地多平台 snapshot 包。版本基于最近的发布 tag,并使用本机递增计数器:最近 tag 为 `v1.0.7` 时,依次生成 `1.0.7-dev-1`、`1.0.7-dev-2`、`1.0.7-dev-3`。
96
+
97
+ 计数器保存在 `~/.ziniao-cli/dev-build-counter`;当最近 tag 变化时计数从 `1` 重新开始。可通过 `ZINIAO_CLI_DEV_COUNTER_FILE` 指定其他计数器路径,便于 CI 或测试隔离。产物写入 `dist/`,该命令只打包,不发布。
98
+
99
+ `package-dev` 内置测试环境 `https://test-sbappstoreapi.ziniao.com` 与 `https://test-open.ziniao.com`,默认使用独立配置目录 `~/.ziniao-cli-dev/`,本地二进制写入 `~/.ziniao-cli-dev/bin/ziniao-cli`。
100
+
101
+ `package-prod` 内置正式环境 `https://sbappstoreapi.ziniao.com` 与 `https://open.ziniao.com`,默认使用独立配置目录 `~/.ziniao-cli-prod/`,本地二进制写入 `~/.ziniao-cli-prod/bin/ziniao-cli`。
102
+
103
+ 仓库内的 `scripts/ziniao-cli-use` 提供统一切换入口;如果你已将该脚本加入 PATH,可直接执行:
104
+
105
+ ```bash
106
+ ziniao-cli-use dev
107
+ ziniao-cli-use prod
108
+ ziniao-cli-use online
109
+ ```
110
+
111
+ - `ziniao-cli-use dev`:切换到 `~/.ziniao-cli-dev/bin/ziniao-cli`
112
+ - `ziniao-cli-use prod`:切换到 `~/.ziniao-cli-prod/bin/ziniao-cli`
113
+ - `ziniao-cli-use online`:切换回 npm 安装的线上版 `ziniao-cli`,继续使用默认线上配置目录 `~/.ziniao-cli/`
114
+
115
+ `make package-dev` 与 `make package-prod` 只会更新各自隔离的本地输出路径,不会改写仓库根目录的 `./ziniao-cli`。如果你需要仓库根目录的开发测试二进制,继续使用 `make build`。
116
+
117
+ dev、本地 prod、online 三套 CLI 默认分别使用 `~/.ziniao-cli-dev/`、`~/.ziniao-cli-prod/`、`~/.ziniao-cli/`,API Key 和配置互不读取。首次运行某个本地环境前,执行 `ziniao-cli config init` 即可在对应目录完成初始化。
118
+
49
119
  ## AI Agent Skills
50
120
 
51
- 仓库提供 13 个开箱即用的 AI Agent Skill,包括员工、店铺、角色、部门、设备、访问策略管理,以及批量授权、店铺巡检等编排工作流。
121
+ 仓库提供 12 个开箱即用的 AI Agent Skill:访问策略、账号、部门、设备、OpenAPI 探索、页面、角色、共享基础、Skill 创建、员工、店铺和批量账号授权。
52
122
 
53
123
  安装 Skill:
54
124
 
55
125
  ```bash
56
- npx skills add ziniao-open/skills -y -g
126
+ ziniao-cli skills install
57
127
  ```
58
128
 
129
+ 备选:`npx skills add ziniao-open/skills -y -g`
130
+
59
131
  详见 [ziniao-skills](https://github.com/ziniao-open/skills)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziniao-open/cli",
3
- "version": "1.0.7",
3
+ "version": "1.1.2",
4
4
  "description": "紫鸟开放平台命令行工具,为开发者和 AI Agent 提供统一的接口调用入口。",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -12,7 +12,11 @@
12
12
  },
13
13
  "files": [
14
14
  "scripts/install.js",
15
- "scripts/run.js"
15
+ "scripts/run.js",
16
+ "scripts/setup-ziniao-cli.sh",
17
+ "scripts/setup-ziniao-cli.ps1",
18
+ "scripts/remove-ziniao-cli-shims.ps1",
19
+ "scripts/remove-ziniao-cli-shims.sh"
16
20
  ],
17
21
  "engines": {
18
22
  "node": ">=18"
@@ -197,6 +197,31 @@ function findBinaryIn(dir, name) {
197
197
  return null;
198
198
  }
199
199
 
200
+ function runDesktopCliSetup() {
201
+ const scriptName = IS_WIN ? "setup-ziniao-cli.ps1" : "setup-ziniao-cli.sh";
202
+ const scriptPath = join(__dirname, scriptName);
203
+ if (!existsSync(scriptPath)) {
204
+ warn(`desktop CLI PATH setup script is missing: ${scriptPath}`);
205
+ return;
206
+ }
207
+
208
+ const result = IS_WIN
209
+ ? spawnSync("powershell.exe", [
210
+ "-NoProfile",
211
+ "-NonInteractive",
212
+ "-ExecutionPolicy",
213
+ "Bypass",
214
+ "-File",
215
+ scriptPath,
216
+ ], { stdio: "inherit" })
217
+ : spawnSync("sh", [scriptPath], { stdio: "inherit" });
218
+ if (result.error) {
219
+ warn(`desktop CLI PATH setup skipped: ${result.error.message}`);
220
+ } else if (result.status !== 0) {
221
+ warn(`desktop CLI PATH setup exited with code ${result.status}`);
222
+ }
223
+ }
224
+
200
225
  async function downloadAndInstall() {
201
226
  const version = PKG.version;
202
227
  if (!DOWNLOAD_BASE) {
@@ -266,18 +291,16 @@ async function main() {
266
291
  if (PKG.version === "0.0.0") {
267
292
  warn("package.json version is 0.0.0; skipping binary install (developer mode).");
268
293
  warn("Set ZINIAO_CLI_BINARY=/path/to/binary or run `make build` to use a local build.");
269
- return;
270
- }
271
-
272
- if (existsSync(TARGET_PATH)) {
294
+ } else if (existsSync(TARGET_PATH)) {
273
295
  makeExecutable(TARGET_PATH);
274
296
  log(`binary already present at ${TARGET_PATH}, skipping download`);
275
- return;
297
+ } else if (copyFromEnv()) {
298
+ // Local binary installed.
299
+ } else {
300
+ await downloadAndInstall();
276
301
  }
277
302
 
278
- if (copyFromEnv()) return;
279
-
280
- await downloadAndInstall();
303
+ runDesktopCliSetup();
281
304
  }
282
305
 
283
306
  main().catch((err) => fail(err.stack || err.message || String(err)));
@@ -0,0 +1,43 @@
1
+ # Find and optionally remove ziniao.exe shims created in PATH directories.
2
+ # Run without -Apply first to preview the files that would be removed.
3
+
4
+ [CmdletBinding(SupportsShouldProcess)]
5
+ param(
6
+ [switch]$Apply
7
+ )
8
+
9
+ $ErrorActionPreference = "Stop"
10
+
11
+ function Write-Log([string]$Message) {
12
+ Write-Output "[ziniao-cli-shims] $Message"
13
+ }
14
+
15
+ $targetDir = Join-Path $env:APPDATA "ziniaobrowser\cli"
16
+ $candidates = @()
17
+ foreach ($pathEntry in @($env:Path -split ";" | Where-Object { $_ })) {
18
+ $directory = $pathEntry.TrimEnd('\')
19
+ if (-not $directory -or $directory -ieq $targetDir.TrimEnd('\')) { continue }
20
+ $shimPath = Join-Path $directory "ziniao.exe"
21
+ if (-not (Test-Path -LiteralPath $shimPath -PathType Leaf)) { continue }
22
+ $candidates += $shimPath
23
+ }
24
+
25
+ if (-not $candidates) {
26
+ Write-Log "No ziniao.exe shims found in PATH directories."
27
+ exit 0
28
+ }
29
+
30
+ foreach ($candidate in ($candidates | Select-Object -Unique)) {
31
+ if (-not $Apply) {
32
+ Write-Log "Would remove: $candidate"
33
+ continue
34
+ }
35
+ if ($PSCmdlet.ShouldProcess($candidate, "Remove CLI shim")) {
36
+ Remove-Item -LiteralPath $candidate -Force
37
+ Write-Log "Removed: $candidate"
38
+ }
39
+ }
40
+
41
+ if (-not $Apply) {
42
+ Write-Log "Preview only. Re-run with -Apply to remove these files."
43
+ }
@@ -0,0 +1,82 @@
1
+ #!/bin/sh
2
+ # Find and optionally remove ziniao shims created in PATH directories.
3
+ # Run without --apply first to preview the files that would be removed.
4
+
5
+ apply=0
6
+ if [ "${1:-}" = "--apply" ]; then
7
+ apply=1
8
+ elif [ -n "${1:-}" ]; then
9
+ printf '%s\n' "[ziniao-cli-shims] ERROR: unsupported option: $1" >&2
10
+ exit 2
11
+ fi
12
+
13
+ log() { printf '%s\n' "[ziniao-cli-shims] $*"; }
14
+
15
+ home_dir="${HOME:?HOME is not set}"
16
+ platform="$(uname -s 2>/dev/null || printf 'unknown')"
17
+ case "$platform" in
18
+ Darwin) default_dir="$home_dir/Library/Application Support/ziniaobrowser/cli" ;;
19
+ Linux) default_dir="${XDG_CONFIG_HOME:-$home_dir/.config}/ziniaobrowser/cli" ;;
20
+ *)
21
+ log "ERROR: unsupported operating system: $platform"
22
+ exit 1
23
+ ;;
24
+ esac
25
+
26
+ target_dir="${ZINIAO_APP_CACHE_DIR:-${default_dir%/cli}}/cli"
27
+ found=0
28
+ old_ifs="$IFS"
29
+ IFS=:
30
+ for path_entry in ${PATH:-}; do
31
+ IFS="$old_ifs"
32
+ directory="${path_entry%/}"
33
+ [ -n "$directory" ] || continue
34
+ [ "$directory" = "${target_dir%/}" ] && continue
35
+ shim_path="$directory/ziniao"
36
+ [ -e "$shim_path" ] || [ -L "$shim_path" ] || continue
37
+ found=1
38
+ if [ "$apply" -eq 1 ]; then
39
+ rm -f -- "$shim_path"
40
+ log "Removed: $shim_path"
41
+ else
42
+ log "Would remove: $shim_path"
43
+ fi
44
+ IFS=:
45
+ done
46
+ IFS="$old_ifs"
47
+
48
+ stable_shim="$home_dir/.local/bin/ziniao"
49
+ if [ -e "$stable_shim" ] || [ -L "$stable_shim" ]; then
50
+ if [ "$stable_shim" != "$target_dir/ziniao" ]; then
51
+ found=1
52
+ if [ "$apply" -eq 1 ]; then
53
+ rm -f -- "$stable_shim"
54
+ log "Removed: $stable_shim"
55
+ else
56
+ log "Would remove: $stable_shim"
57
+ fi
58
+ fi
59
+ fi
60
+
61
+ cache_found=0
62
+ if [ -d "$target_dir" ]; then
63
+ cache_found=1
64
+ if [ "$apply" -eq 1 ]; then
65
+ if [ -f "$target_dir/ziniao" ] || [ -L "$target_dir/ziniao" ]; then
66
+ rm -f -- "$target_dir/ziniao"
67
+ fi
68
+ if rmdir "$target_dir" 2>/dev/null; then
69
+ log "Removed CLI cache directory: $target_dir"
70
+ else
71
+ log "WARN: CLI cache directory is not empty; keeping it: $target_dir"
72
+ fi
73
+ else
74
+ log "Would remove CLI cache directory: $target_dir"
75
+ fi
76
+ fi
77
+
78
+ if [ "$found" -eq 0 ] && [ "$cache_found" -eq 0 ]; then
79
+ log "No ziniao shims found in PATH directories."
80
+ elif [ "$apply" -eq 0 ]; then
81
+ log "Preview only. Re-run with --apply to remove these files."
82
+ fi
@@ -0,0 +1,179 @@
1
+ # Synchronize the Ziniao desktop CLI into the per-user AppCache and register
2
+ # that directory in the current user's PATH.
3
+ #
4
+ # Use `. .\scripts\setup-ziniao-cli.ps1` to refresh the current PowerShell.
5
+ # A normal invocation persists PATH but cannot modify its parent PowerShell.
6
+
7
+ $ErrorActionPreference = "Stop"
8
+ $isDotSourced = $MyInvocation.InvocationName -eq "."
9
+
10
+ function Write-Log([string]$Message) {
11
+ Write-Output "[ziniao-cli-path] $Message"
12
+ }
13
+
14
+ $arch = if ([Environment]::Is64BitOperatingSystem) {
15
+ if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE }
16
+ } else { $env:PROCESSOR_ARCHITECTURE }
17
+ switch ($arch.ToLowerInvariant()) {
18
+ "amd64" { $archDir = "x64" }
19
+ "x86_64" { $archDir = "x64" }
20
+ "arm64" { $archDir = "arm64" }
21
+ default { throw "Unsupported CPU architecture: $arch" }
22
+ }
23
+
24
+ $sourceName = "ziniao.exe"
25
+ $sourceRelative = Join-Path "envkit\cli\win32\$archDir" $sourceName
26
+ $resourceRoot = "resources\app.asar.unpacked"
27
+
28
+ $sourcePath = $null
29
+ $guiExecutablePath = $null
30
+ $checkedSourcePaths = @()
31
+ if ($env:ZINIAO_CLI_SOURCE -and (Test-Path -LiteralPath $env:ZINIAO_CLI_SOURCE -PathType Leaf)) {
32
+ $sourcePath = $env:ZINIAO_CLI_SOURCE
33
+ }
34
+ if (-not $sourcePath -and $env:ZINIAO_INSTALL_DIR) {
35
+ $candidate = Join-Path $env:ZINIAO_INSTALL_DIR "$resourceRoot\$sourceRelative"
36
+ $checkedSourcePaths += $candidate
37
+ if (Test-Path -LiteralPath $candidate -PathType Leaf) {
38
+ $sourcePath = $candidate
39
+ }
40
+ }
41
+ if (-not $sourcePath) {
42
+ $uninstallKeys = @(
43
+ "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
44
+ "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
45
+ "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
46
+ )
47
+ $uninstallEntries = Get-ItemProperty $uninstallKeys -ErrorAction SilentlyContinue |
48
+ Where-Object { $_.DisplayIcon -and $_.DisplayIcon -match "ziniao\.exe" }
49
+ foreach ($entry in $uninstallEntries) {
50
+ $displayIcon = ([string]$entry.DisplayIcon).Trim()
51
+ if ($displayIcon -match '^\s*"([^"]+\.exe)"') {
52
+ $executablePath = $matches[1]
53
+ } elseif ($displayIcon -match '^\s*(.*?\.exe)(?:,\d+)?(?:\s|$)') {
54
+ $executablePath = $matches[1]
55
+ } else {
56
+ continue
57
+ }
58
+ if (-not (Test-Path -LiteralPath $executablePath -PathType Leaf)) { continue }
59
+ $guiExecutablePath = $executablePath
60
+ $installDir = Split-Path -Parent $executablePath
61
+ $candidate = Join-Path $installDir "$resourceRoot\$sourceRelative"
62
+ $checkedSourcePaths += $candidate
63
+ if (Test-Path -LiteralPath $candidate -PathType Leaf) {
64
+ $sourcePath = $candidate
65
+ Write-Log "Found the Ziniao installation from the uninstall registry: $installDir"
66
+ break
67
+ }
68
+ }
69
+ }
70
+ if ($env:ZINIAO_APP_CACHE_DIR) {
71
+ $appCacheDir = $env:ZINIAO_APP_CACHE_DIR
72
+ } else {
73
+ # The Windows GUI installs its CLI under %APPDATA%\ziniaobrowser\cli.
74
+ $appCacheDir = Join-Path $env:APPDATA "ziniaobrowser"
75
+ }
76
+
77
+ $targetDir = Join-Path $appCacheDir "cli"
78
+ $targetPath = Join-Path $targetDir $sourceName
79
+ New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
80
+
81
+ if ($guiExecutablePath) {
82
+ $guiPathFile = Join-Path $env:APPDATA "ziniaobrowser\gui-path"
83
+ try {
84
+ New-Item -ItemType Directory -Path (Split-Path -Parent $guiPathFile) -Force | Out-Null
85
+ [System.IO.File]::WriteAllText(
86
+ $guiPathFile,
87
+ "$guiExecutablePath$([Environment]::NewLine)",
88
+ [System.Text.Encoding]::ASCII
89
+ )
90
+ Write-Log "Wrote GUI path hint: $guiPathFile"
91
+ } catch {
92
+ Write-Log "WARN: Could not write GUI path hint: $($_.Exception.Message)"
93
+ }
94
+ }
95
+
96
+ function Get-FileSha256([string]$Path) {
97
+ $sha256 = [System.Security.Cryptography.SHA256]::Create()
98
+ $stream = [System.IO.File]::OpenRead($Path)
99
+ try {
100
+ return ([BitConverter]::ToString($sha256.ComputeHash($stream))).Replace("-", "")
101
+ } finally {
102
+ $stream.Dispose()
103
+ $sha256.Dispose()
104
+ }
105
+ }
106
+
107
+ if (-not $sourcePath) {
108
+ foreach ($checkedPath in ($checkedSourcePaths | Select-Object -Unique)) {
109
+ Write-Log "Checked CLI source path: $checkedPath"
110
+ }
111
+ if (Test-Path -LiteralPath $targetPath -PathType Leaf) {
112
+ Write-Log "CLI source was not found in the install directory; keeping the existing AppCache CLI: $targetPath"
113
+ } else {
114
+ Write-Log "WARN: ${sourceName} was not found in the install directory; skipping copy. Set `$env:ZINIAO_INSTALL_DIR or `$env:ZINIAO_CLI_SOURCE to override."
115
+ }
116
+ } else {
117
+ $needsCopy = -not (Test-Path -LiteralPath $targetPath -PathType Leaf)
118
+ if (-not $needsCopy) {
119
+ $needsCopy = (Get-FileSha256 $sourcePath) -ne (Get-FileSha256 $targetPath)
120
+ }
121
+ if ($needsCopy) {
122
+ Copy-Item -LiteralPath $sourcePath -Destination $targetPath -Force
123
+ Write-Log "Synced CLI: $sourcePath -> $targetPath"
124
+ } else {
125
+ Write-Log "AppCache CLI is up to date: $targetPath"
126
+ }
127
+ }
128
+
129
+ function Install-PathShim {
130
+ if (-not (Test-Path -LiteralPath $targetPath -PathType Leaf)) { return }
131
+ $windowsAppsDir = Join-Path $env:LOCALAPPDATA "Microsoft\WindowsApps"
132
+ foreach ($pathEntry in @($env:Path -split ";" | Where-Object { $_ })) {
133
+ $normalizedPath = $pathEntry.TrimEnd('\')
134
+ if ($normalizedPath -ieq $windowsAppsDir.TrimEnd('\') -and (Test-Path -LiteralPath $windowsAppsDir -PathType Container)) {
135
+ $shimPath = Join-Path $windowsAppsDir $sourceName
136
+ if (-not (Test-Path -LiteralPath $shimPath)) {
137
+ try {
138
+ Copy-Item -LiteralPath $targetPath -Destination $shimPath -ErrorAction Stop
139
+ $script:shimInstalled = $true
140
+ Write-Log "Created the CLI shim in WindowsApps: $shimPath"
141
+ } catch {
142
+ Write-Log "WARN: Could not create the CLI shim in WindowsApps: $($_.Exception.Message)"
143
+ }
144
+ } else {
145
+ Write-Log "WindowsApps already contains a file named $sourceName; leaving it unchanged"
146
+ }
147
+ return
148
+ }
149
+ if ($normalizedPath -ieq $targetDir.TrimEnd('\')) {
150
+ $script:shimInstalled = $true
151
+ Write-Log "The current PATH already contains the AppCache CLI directory: $targetDir"
152
+ return
153
+ }
154
+ }
155
+ Write-Log "The AppCache CLI directory is not on the current PATH; no shim was created in another directory."
156
+ }
157
+
158
+ $shimInstalled = $false
159
+ Install-PathShim
160
+
161
+ $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
162
+ $entries = @($userPath -split ";" | Where-Object { $_ })
163
+ if (-not ($entries | Where-Object { $_.TrimEnd('\') -ieq $targetDir.TrimEnd('\') })) {
164
+ [Environment]::SetEnvironmentVariable("Path", (($entries + $targetDir) -join ";"), "User")
165
+ Write-Log "Added to the user PATH: $targetDir"
166
+ }
167
+
168
+ $currentEntries = @($env:Path -split ";" | Where-Object { $_ })
169
+ if (-not ($currentEntries | Where-Object { $_.TrimEnd('\') -ieq $targetDir.TrimEnd('\') })) {
170
+ $env:Path = "$targetDir;$env:Path"
171
+ }
172
+ if ($shimInstalled) {
173
+ Write-Log "The CLI is already available on the current PATH; this PowerShell session needs no PATH refresh"
174
+ } elseif ($isDotSourced) {
175
+ Write-Log "The current PowerShell PATH was refreshed"
176
+ } else {
177
+ Write-Log "The current process PATH was refreshed; dot-source this script in the parent PowerShell or reopen the terminal"
178
+ }
179
+ Write-Log "Verification command: Get-Command ziniao"
@@ -0,0 +1,232 @@
1
+ #!/bin/sh
2
+ # Synchronize the Ziniao desktop CLI into the per-user AppCache and register
3
+ # that directory in the current user's shell PATH.
4
+ #
5
+ # Use `source scripts/setup-ziniao-cli.sh` when the current shell should be
6
+ # refreshed immediately. A normal execution can persist PATH but cannot modify
7
+ # its parent shell.
8
+
9
+ log() { printf '%s\n' "[ziniao-cli-path] $*"; }
10
+ fail() { printf '%s\n' "[ziniao-cli-path] ERROR: $*" >&2; return 1 2>/dev/null || exit 1; }
11
+
12
+ is_sourced=0
13
+ if [ -n "${BASH_VERSION:-}" ]; then
14
+ [ "${BASH_SOURCE[0]:-$0}" != "$0" ] && is_sourced=1
15
+ elif [ -n "${ZSH_EVAL_CONTEXT:-}" ]; then
16
+ case "$ZSH_EVAL_CONTEXT" in
17
+ *:file) is_sourced=1 ;;
18
+ esac
19
+ fi
20
+
21
+ if [ "$is_sourced" -eq 0 ]; then
22
+ set -eu
23
+ fi
24
+
25
+ platform="$(uname -s 2>/dev/null || printf 'unknown')"
26
+ machine="$(uname -m 2>/dev/null || printf 'unknown')"
27
+
28
+ case "$platform" in
29
+ Darwin) platform_dir="darwin"; source_name="ziniao"; ;;
30
+ Linux) platform_dir="linux"; source_name="ziniao"; ;;
31
+ MINGW*|MSYS*|CYGWIN*)
32
+ fail "Windows shell detected; use PowerShell: scripts/setup-ziniao-cli.ps1"
33
+ return 1 2>/dev/null || exit 1
34
+ ;;
35
+ *) fail "Unsupported operating system: $platform"; return 1 2>/dev/null || exit 1 ;;
36
+ esac
37
+
38
+ case "$machine" in
39
+ arm64|aarch64) arch_dir="arm64" ;;
40
+ x86_64|amd64) arch_dir="x64" ;;
41
+ *) fail "Unsupported CPU architecture: $machine"; return 1 2>/dev/null || exit 1 ;;
42
+ esac
43
+
44
+ home_dir="${HOME:?HOME is not set}"
45
+
46
+ find_source() {
47
+ if [ -n "${ZINIAO_CLI_SOURCE:-}" ] && [ -f "$ZINIAO_CLI_SOURCE" ]; then
48
+ printf '%s\n' "$ZINIAO_CLI_SOURCE"
49
+ return 0
50
+ fi
51
+
52
+ for candidate_arch in "$arch_dir" x64 arm64; do
53
+ source_rel="envkit/cli/$platform_dir/$candidate_arch/$source_name"
54
+ if [ -n "${ZINIAO_INSTALL_DIR:-}" ]; then
55
+ candidate="$ZINIAO_INSTALL_DIR/$source_rel"
56
+ if [ -f "$candidate" ]; then
57
+ printf '%s\n' "$candidate"
58
+ return 0
59
+ fi
60
+ fi
61
+
62
+ for install_root in \
63
+ "/Applications/ziniao.app/Contents/Resources/app.asar.unpacked" \
64
+ "$home_dir/Applications/ziniao.app/Contents/Resources/app.asar.unpacked" \
65
+ "$home_dir/Library/Application Support/ziniaobrowser/app.asar.unpacked" \
66
+ "/opt/ziniao/resources/app.asar.unpacked"; do
67
+ candidate="$install_root/$source_rel"
68
+ if [ -f "$candidate" ]; then
69
+ printf '%s\n' "$candidate"
70
+ return 0
71
+ fi
72
+ done
73
+ done
74
+ return 1
75
+ }
76
+
77
+ log_source_candidates() {
78
+ [ -n "${ZINIAO_CLI_SOURCE:-}" ] && log "Checked CLI source path: $ZINIAO_CLI_SOURCE"
79
+ for candidate_arch in "$arch_dir" x64 arm64; do
80
+ source_rel="envkit/cli/$platform_dir/$candidate_arch/$source_name"
81
+ [ -n "${ZINIAO_INSTALL_DIR:-}" ] && log "Checked CLI source path: $ZINIAO_INSTALL_DIR/$source_rel"
82
+ for install_root in \
83
+ "/Applications/ziniao.app/Contents/Resources/app.asar.unpacked" \
84
+ "$home_dir/Applications/ziniao.app/Contents/Resources/app.asar.unpacked" \
85
+ "$home_dir/Library/Application Support/ziniaobrowser/app.asar.unpacked" \
86
+ "/opt/ziniao/resources/app.asar.unpacked"; do
87
+ log "Checked CLI source path: $install_root/$source_rel"
88
+ done
89
+ done
90
+ }
91
+
92
+ find_gui_executable() {
93
+ case "$platform_dir" in
94
+ darwin)
95
+ for candidate in \
96
+ "/Applications/ziniao.app/Contents/MacOS/ziniaobrowser" \
97
+ "/Applications/ziniao.app/Contents/MacOS/ziniao" \
98
+ "$home_dir/Applications/ziniao.app/Contents/MacOS/ziniaobrowser" \
99
+ "$home_dir/Applications/ziniao.app/Contents/MacOS/ziniao"; do
100
+ [ -f "$candidate" ] && { printf '%s\n' "$candidate"; return 0; }
101
+ done
102
+ ;;
103
+ linux)
104
+ for candidate in \
105
+ "/opt/ziniao/ziniaobrowser" \
106
+ "/opt/ziniao/ziniao" \
107
+ "/usr/lib/ziniaobrowser/ziniaobrowser" \
108
+ "/usr/lib/ziniaobrowser/ziniao"; do
109
+ [ -f "$candidate" ] && { printf '%s\n' "$candidate"; return 0; }
110
+ done
111
+ ;;
112
+ esac
113
+ return 1
114
+ }
115
+
116
+ if [ -n "${ZINIAO_APP_CACHE_DIR:-}" ]; then
117
+ target_dir="$ZINIAO_APP_CACHE_DIR/cli"
118
+ else
119
+ case "$platform_dir" in
120
+ darwin) target_dir="$home_dir/Library/Application Support/ziniaobrowser/cli" ;;
121
+ *) target_dir="${XDG_CONFIG_HOME:-$home_dir/.config}/ziniaobrowser/cli" ;;
122
+ esac
123
+ fi
124
+
125
+ source_path="$(find_source || true)"
126
+ gui_executable_path="$(find_gui_executable || true)"
127
+ target_path="$target_dir/$source_name"
128
+ mkdir -p "$target_dir"
129
+
130
+ if [ -n "$source_path" ]; then
131
+ log "CLI source path: $source_path"
132
+ else
133
+ log_source_candidates
134
+ fi
135
+
136
+ if [ -n "$gui_executable_path" ]; then
137
+ case "$platform_dir" in
138
+ darwin) gui_path_file="$home_dir/Library/Application Support/ziniaobrowser/gui-path" ;;
139
+ *)
140
+ xdg_config_home="${XDG_CONFIG_HOME:-$home_dir/.config}"
141
+ gui_path_file="$xdg_config_home/ziniaobrowser/gui-path"
142
+ ;;
143
+ esac
144
+ if mkdir -p "$(dirname "$gui_path_file")" && printf '%s\n' "$gui_executable_path" > "$gui_path_file"; then
145
+ log "Wrote GUI path hint: $gui_path_file"
146
+ else
147
+ log "WARN: Could not write GUI path hint: $gui_path_file"
148
+ fi
149
+ fi
150
+ if [ -z "$source_path" ]; then
151
+ if [ -f "$target_path" ]; then
152
+ log "CLI source was not found in the install directory; keeping the existing AppCache CLI: $target_path"
153
+ else
154
+ log "WARN: ${source_name} was not found in the install directory; skipping copy. Set ZINIAO_INSTALL_DIR or ZINIAO_CLI_SOURCE to override."
155
+ fi
156
+ elif [ ! -f "$target_path" ] || ! cmp -s "$source_path" "$target_path"; then
157
+ cp "$source_path" "$target_path"
158
+ chmod 755 "$target_path"
159
+ log "Synced CLI: $source_path -> $target_path"
160
+ else
161
+ log "AppCache CLI is up to date: $target_path"
162
+ fi
163
+
164
+ shim_installed=0
165
+ install_path_shim() {
166
+ [ -f "$target_path" ] || return 0
167
+ shim_dir="$home_dir/.local/bin"
168
+ shim_path="$shim_dir/$source_name"
169
+ mkdir -p "$shim_dir"
170
+ if [ -L "$shim_path" ] && [ "$(readlink "$shim_path" 2>/dev/null || true)" = "$target_path" ]; then
171
+ shim_installed=1
172
+ log "The CLI shim already points to the AppCache CLI: $shim_path"
173
+ elif [ ! -e "$shim_path" ]; then
174
+ if ln -s "$target_path" "$shim_path" 2>/dev/null; then
175
+ shim_installed=1
176
+ log "Created the CLI shim: $shim_path"
177
+ else
178
+ log "WARN: Could not create the CLI shim: $shim_path"
179
+ fi
180
+ else
181
+ log "A file already exists at the CLI shim path; leaving it unchanged: $shim_path"
182
+ fi
183
+ case ":${PATH:-}:" in
184
+ *:"$target_dir":*)
185
+ shim_installed=1
186
+ log "The current PATH already contains the AppCache CLI directory: $target_dir"
187
+ ;;
188
+ *:"$shim_dir":*)
189
+ [ "$shim_installed" -eq 1 ] && log "The current PATH contains the CLI shim directory: $shim_dir"
190
+ ;;
191
+ *)
192
+ log "The AppCache CLI directory is not on the current PATH; no shim will be created elsewhere: $target_dir"
193
+ ;;
194
+ esac
195
+ }
196
+
197
+ install_path_shim
198
+
199
+ shim_dir="$home_dir/.local/bin"
200
+ export PATH="$target_dir:$shim_dir${PATH:+:$PATH}"
201
+
202
+ profile=""
203
+ case "${SHELL:-}" in
204
+ */zsh) profile="$home_dir/.zshrc" ;;
205
+ */bash) profile="$home_dir/.bashrc" ;;
206
+ esac
207
+ if [ -z "$profile" ]; then
208
+ case "$platform_dir" in
209
+ darwin) profile="$home_dir/.zshrc" ;;
210
+ *) profile="$home_dir/.profile" ;;
211
+ esac
212
+ fi
213
+
214
+ marker="# ziniao-cli: AppCache CLI PATH"
215
+ path_line="export PATH=\"$target_dir:$shim_dir:\$PATH\""
216
+ if ! grep -Fqx "$marker" "$profile" 2>/dev/null; then
217
+ mkdir -p "$(dirname "$profile")"
218
+ printf '\n%s\n' "$marker" >> "$profile"
219
+ log "Added to shell profile: $profile"
220
+ fi
221
+ if ! grep -Fqx "$path_line" "$profile" 2>/dev/null; then
222
+ printf '%s\n' "$path_line" >> "$profile"
223
+ fi
224
+
225
+ if [ "$shim_installed" -eq 1 ]; then
226
+ log "The CLI is already on the current PATH; this terminal needs no PATH refresh"
227
+ elif [ "$is_sourced" -eq 1 ]; then
228
+ log "The current shell PATH was refreshed"
229
+ else
230
+ log "The current process PATH was refreshed; source \"$profile\" in the parent terminal"
231
+ fi
232
+ log "Verification command: command -v $source_name"