@double_seven/swpm 1.0.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/LICENSE +21 -0
- package/README.ja.md +81 -0
- package/README.ko.md +81 -0
- package/README.md +81 -0
- package/README.zh-CN.md +81 -0
- package/README.zh-TW.md +81 -0
- package/dist/commands/export.d.ts +1 -0
- package/dist/commands/export.js +59 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +20 -0
- package/dist/commands/lang.d.ts +2 -0
- package/dist/commands/lang.js +82 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +26 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.js +26 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +70 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +70 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/commands/upgrade.js +106 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +37 -0
- package/dist/i18n/en.json +81 -0
- package/dist/i18n/index.d.ts +14 -0
- package/dist/i18n/index.js +57 -0
- package/dist/i18n/ja.json +81 -0
- package/dist/i18n/ko.json +81 -0
- package/dist/i18n/zh-CN.json +81 -0
- package/dist/i18n/zh-TW.json +81 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +88 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +2 -0
- package/dist/ui/selector.d.ts +4 -0
- package/dist/ui/selector.js +86 -0
- package/dist/ui/spinner.d.ts +8 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/table.d.ts +2 -0
- package/dist/ui/table.js +31 -0
- package/dist/winget/client.d.ts +13 -0
- package/dist/winget/client.js +94 -0
- package/dist/winget/parser.d.ts +4 -0
- package/dist/winget/parser.js +303 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dengjiansheng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# swpm
|
|
2
|
+
|
|
3
|
+
[winget](https://github.com/microsoft/winget-cli) をラップした Windows ソフトウェア管理 CLI です。インタラクティブな一括アップグレード、YAML設定ファイルによる同期、長いパッケージIDの手動コピーはもう不要です。
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [한국어](README.ko.md)
|
|
6
|
+
|
|
7
|
+
## 機能
|
|
8
|
+
|
|
9
|
+
- インストール済みパッケージの一覧表示
|
|
10
|
+
- winget リポジトリの検索
|
|
11
|
+
- パッケージのインストール / アンインストール(アンインストールはインタラクティブな複数選択に対応)
|
|
12
|
+
- インタラクティブなアップグレード(チェックボックスで選択)
|
|
13
|
+
- 現在の環境を YAML ファイルにエクスポート
|
|
14
|
+
- 同期 — 設定ファイルを使って新しいマシンに一括インストール
|
|
15
|
+
|
|
16
|
+
## 要件
|
|
17
|
+
|
|
18
|
+
- **Windows 10 1809+** または **Windows 11**
|
|
19
|
+
- [App Installer (winget)](https://www.microsoft.com/p/app-installer/9nblggh4nns1)(Windows 11 にはプリインストール)
|
|
20
|
+
- **Node.js >= 18**
|
|
21
|
+
|
|
22
|
+
## インストール
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double_seven/swpm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **Windows 専用。** macOS や Linux ではインストールに失敗します — swpm は Windows に含まれる winget のラッパーです。
|
|
29
|
+
|
|
30
|
+
## 使い方
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
swpm list # インストール済みパッケージの表示
|
|
34
|
+
swpm search vscode # パッケージの検索
|
|
35
|
+
swpm install 7zip.7zip # パッケージIDでインストール
|
|
36
|
+
swpm uninstall 7zip.7zip # パッケージIDでアンインストール
|
|
37
|
+
swpm uninstall # インタラクティブアンインストール(複数選択、デフォルト未チェック)
|
|
38
|
+
swpm upgrade # インタラクティブアップグレード(チェックボックス選択)
|
|
39
|
+
swpm upgrade --all # すべて一括アップグレード
|
|
40
|
+
swpm upgrade 7zip.7zip # 単一パッケージのアップグレード
|
|
41
|
+
swpm export # ./swpm-packages.yaml にエクスポート
|
|
42
|
+
swpm export ./my-config.yaml # 任意のパスにエクスポート
|
|
43
|
+
swpm sync ./my-config.yaml # 設定ファイルから不足パッケージをインストール
|
|
44
|
+
swpm lang # 表示言語の切り替え
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### エクスポートと同期
|
|
48
|
+
|
|
49
|
+
エクスポートコマンドはインストール済みの winget パッケージを YAML ファイルに保存します:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
packages:
|
|
53
|
+
- 7zip.7zip
|
|
54
|
+
- CoreyButler.NVMforWindows
|
|
55
|
+
- Microsoft.VisualStudioCode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
別のマシンで `swpm sync <ファイル>` を実行すると、そのファイルを元にすべてのパッケージを自動インストールします。不足しているものはインストールされ、既に存在するものはスキップされます。
|
|
59
|
+
|
|
60
|
+
エクスポート時の選択画面では、デフォルトで Microsoft 製以外のパッケージと VS Code 関連がチェックされています。その他の Microsoft 製パッケージ(OneDrive、.NET ランタイム、Edge など)はデフォルトでチェックが外されており、システムコンポーネントを誤って別のマシンに持ち込まないようになっています。
|
|
61
|
+
|
|
62
|
+
## 多言語対応
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
swpm lang # インタラクティブな言語選択
|
|
66
|
+
SWPM_LANG=ja # または環境変数で設定
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
対応言語:English(デフォルト)、简体中文、繁體中文、日本語、한국어。
|
|
70
|
+
|
|
71
|
+
## winget の修復
|
|
72
|
+
|
|
73
|
+
winget が正常に動作しなくなった場合、管理者権限の PowerShell で以下を実行してください:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
Get-AppxPackage -AllUsers *DesktopAppInstaller* | Reset-AppxPackage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## ライセンス
|
|
80
|
+
|
|
81
|
+
MIT
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# swpm
|
|
2
|
+
|
|
3
|
+
[winget](https://github.com/microsoft/winget-cli)을 래핑한 Windows 소프트웨어 관리 CLI입니다. 대화형 일괄 업그레이드, YAML 설정 파일 기반 동기화, 긴 패키지 ID를 더 이상 복사할 필요가 없습니다.
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md)
|
|
6
|
+
|
|
7
|
+
## 기능
|
|
8
|
+
|
|
9
|
+
- 설치된 패키지 목록 표시
|
|
10
|
+
- winget 저장소 검색
|
|
11
|
+
- 패키지 설치 / 제거 (제거는 대화형 다중 선택 지원)
|
|
12
|
+
- 대화형 업그레이드 (체크박스로 선택)
|
|
13
|
+
- 현재 환경을 YAML 파일로 내보내기
|
|
14
|
+
- 동기화 — 설정 파일로 새 머신에 일괄 설치
|
|
15
|
+
|
|
16
|
+
## 요구사항
|
|
17
|
+
|
|
18
|
+
- **Windows 10 1809+** 또는 **Windows 11**
|
|
19
|
+
- [App Installer (winget)](https://www.microsoft.com/p/app-installer/9nblggh4nns1) (Windows 11에 사전 설치됨)
|
|
20
|
+
- **Node.js >= 18**
|
|
21
|
+
|
|
22
|
+
## 설치
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double_seven/swpm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **Windows 전용.** macOS와 Linux에서는 설치가 실패합니다 — swpm은 Windows의 일부인 winget을 래핑합니다.
|
|
29
|
+
|
|
30
|
+
## 사용법
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
swpm list # 설치된 패키지 표시
|
|
34
|
+
swpm search vscode # 패키지 검색
|
|
35
|
+
swpm install 7zip.7zip # 패키지 ID로 설치
|
|
36
|
+
swpm uninstall 7zip.7zip # 패키지 ID로 제거
|
|
37
|
+
swpm uninstall # 대화형 제거 (다중 선택, 기본값 체크 해제)
|
|
38
|
+
swpm upgrade # 대화형 체크박스 업그레이드
|
|
39
|
+
swpm upgrade --all # 모두 업그레이드
|
|
40
|
+
swpm upgrade 7zip.7zip # 단일 패키지 업그레이드
|
|
41
|
+
swpm export # ./swpm-packages.yaml로 내보내기
|
|
42
|
+
swpm export ./my-config.yaml # 사용자 지정 경로로 내보내기
|
|
43
|
+
swpm sync ./my-config.yaml # 설정 파일에서 누락된 패키지 설치
|
|
44
|
+
swpm lang # 표시 언어 변경
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 내보내기 및 동기화
|
|
48
|
+
|
|
49
|
+
내보내기 명령은 설치된 winget 패키지를 YAML 파일로 저장합니다:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
packages:
|
|
53
|
+
- 7zip.7zip
|
|
54
|
+
- CoreyButler.NVMforWindows
|
|
55
|
+
- Microsoft.VisualStudioCode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
다른 머신에서 `swpm sync <파일>`을 실행하면 해당 파일의 모든 패키지를 자동으로 설치합니다. 누락된 패키지는 설치되고, 이미 설치된 패키지는 건너뜁니다.
|
|
59
|
+
|
|
60
|
+
내보내기 선택 화면에서는 기본적으로 Microsoft 외 패키지와 VS Code 관련 항목이 체크되어 있습니다. 기타 Microsoft 패키지(OneDrive, .NET 런타임, Edge 등)는 기본적으로 체크 해제되어 있어 시스템 구성 요소를 실수로 다른 머신으로 가져가지 않습니다.
|
|
61
|
+
|
|
62
|
+
## 다국어
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
swpm lang # 대화형 언어 선택
|
|
66
|
+
SWPM_LANG=ja # 또는 환경 변수로 설정
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
지원 언어: English (기본값), 简体中文, 繁體中文, 日本語, 한국어.
|
|
70
|
+
|
|
71
|
+
## winget 복구
|
|
72
|
+
|
|
73
|
+
winget이 제대로 작동하지 않으면 관리자 PowerShell에서 다음을 실행하세요:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
Get-AppxPackage -AllUsers *DesktopAppInstaller* | Reset-AppxPackage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 라이선스
|
|
80
|
+
|
|
81
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# swpm
|
|
2
|
+
|
|
3
|
+
A CLI wrapper around [winget](https://github.com/microsoft/winget-cli) that makes managing Windows software less painful. Interactive upgrades, config-based sync, no more copy-pasting long package IDs.
|
|
4
|
+
|
|
5
|
+
[简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [한국어](README.ko.md)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- List installed packages
|
|
10
|
+
- Search the winget repository
|
|
11
|
+
- Install and uninstall packages (uninstall also supports interactive multi-select)
|
|
12
|
+
- Upgrade packages interactively (pick what you want with checkboxes)
|
|
13
|
+
- Export your setup to a YAML file
|
|
14
|
+
- Sync — install everything from a config file on a new machine
|
|
15
|
+
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
- **Windows 10 1809+** or **Windows 11**
|
|
19
|
+
- [App Installer (winget)](https://www.microsoft.com/p/app-installer/9nblggh4nns1) (pre-installed on Windows 11)
|
|
20
|
+
- **Node.js >= 18**
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double_seven/swpm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **Windows only.** Installation will fail on macOS and Linux — swpm wraps winget, which is part of Windows.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
swpm list # show installed packages
|
|
34
|
+
swpm search vscode # search for a package
|
|
35
|
+
swpm install 7zip.7zip # install by package ID
|
|
36
|
+
swpm uninstall 7zip.7zip # uninstall by package ID
|
|
37
|
+
swpm uninstall # interactive uninstall (multi-select, unchecked by default)
|
|
38
|
+
swpm upgrade # interactive checkbox upgrade
|
|
39
|
+
swpm upgrade --all # upgrade everything
|
|
40
|
+
swpm upgrade 7zip.7zip # upgrade a single package
|
|
41
|
+
swpm export # export to ./swpm-packages.yaml
|
|
42
|
+
swpm export ./my-config.yaml # export to a custom path
|
|
43
|
+
swpm sync ./my-config.yaml # install missing packages
|
|
44
|
+
swpm lang # change display language
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Export & Sync
|
|
48
|
+
|
|
49
|
+
Export saves your installed winget packages to a YAML file:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
packages:
|
|
53
|
+
- 7zip.7zip
|
|
54
|
+
- CoreyButler.NVMforWindows
|
|
55
|
+
- Microsoft.VisualStudioCode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run `swpm sync <file>` on another machine to install everything from that file. Missing packages are installed automatically, already-installed ones are skipped.
|
|
59
|
+
|
|
60
|
+
By default, the export selector pre-checks non-Microsoft packages and VS Code-related packages. Other Microsoft packages (OneDrive, .NET runtimes, Edge, etc.) are unchecked so you don't accidentally carry system components to another machine.
|
|
61
|
+
|
|
62
|
+
## Languages
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
swpm lang # interactive language selector
|
|
66
|
+
SWPM_LANG=ja # or set via environment variable
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Supported: English (default), 简体中文, 繁體中文, 日本語, 한국어.
|
|
70
|
+
|
|
71
|
+
## Fixing winget
|
|
72
|
+
|
|
73
|
+
If winget stops working, run this in an administrator PowerShell window:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
Get-AppxPackage -AllUsers *DesktopAppInstaller* | Reset-AppxPackage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# swpm
|
|
2
|
+
|
|
3
|
+
一个基于 [winget](https://github.com/microsoft/winget-cli) 的 Windows 软件管理命令行工具。支持交互式批量升级、配置文件导入导出,不用再手动复制粘贴那些长长的包名了。
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [한국어](README.ko.md)
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
- 列出已安装的软件包
|
|
10
|
+
- 搜索 winget 仓库
|
|
11
|
+
- 安装 / 卸载软件包(卸载支持交互式多选)
|
|
12
|
+
- 交互式升级(勾选想要的,跳过不需要的)
|
|
13
|
+
- 导出当前环境到 YAML 文件
|
|
14
|
+
- 同步 — 在新电脑上通过配置文件一键安装所有软件
|
|
15
|
+
|
|
16
|
+
## 环境要求
|
|
17
|
+
|
|
18
|
+
- **Windows 10 1809+** 或 **Windows 11**
|
|
19
|
+
- [App Installer (winget)](https://www.microsoft.com/p/app-installer/9nblggh4nns1)(Windows 11 已预装)
|
|
20
|
+
- **Node.js >= 18**
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double_seven/swpm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **仅支持 Windows。** macOS 和 Linux 下安装会直接失败——swpm 封装的是 winget,Windows 专属。
|
|
29
|
+
|
|
30
|
+
## 用法
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
swpm list # 列出已安装软件
|
|
34
|
+
swpm search vscode # 搜索软件包
|
|
35
|
+
swpm install 7zip.7zip # 通过包 ID 安装
|
|
36
|
+
swpm uninstall 7zip.7zip # 通过包 ID 卸载
|
|
37
|
+
swpm uninstall # 交互式卸载(多选,默认不勾)
|
|
38
|
+
swpm upgrade # 交互式勾选升级
|
|
39
|
+
swpm upgrade --all # 一键升级全部
|
|
40
|
+
swpm upgrade 7zip.7zip # 升级单个软件
|
|
41
|
+
swpm export # 导出到 ./swpm-packages.yaml
|
|
42
|
+
swpm export ./my-config.yaml # 导出到指定路径
|
|
43
|
+
swpm sync ./my-config.yaml # 安装配置文件中缺失的软件
|
|
44
|
+
swpm lang # 切换显示语言
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 导出与同步
|
|
48
|
+
|
|
49
|
+
导出命令会把当前环境已安装的 winget 包保存为 YAML 文件:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
packages:
|
|
53
|
+
- 7zip.7zip
|
|
54
|
+
- CoreyButler.NVMforWindows
|
|
55
|
+
- Microsoft.VisualStudioCode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
换了新电脑?运行 `swpm sync <配置文件>` 就能自动装上所有缺失的软件,已装好的会自动跳过。
|
|
59
|
+
|
|
60
|
+
导出时会弹出选择界面,默认勾选非微软软件和 VS Code 相关内容,其他微软软件(OneDrive、.NET 运行时、Edge 等)默认不勾,避免把系统组件带到别的机器上。
|
|
61
|
+
|
|
62
|
+
## 多语言
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
swpm lang # 交互式语言选择
|
|
66
|
+
SWPM_LANG=ja # 或通过环境变量设置
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
支持:English(默认)、简体中文、繁體中文、日本語、한국어。
|
|
70
|
+
|
|
71
|
+
## winget 异常修复
|
|
72
|
+
|
|
73
|
+
如果 winget 出了问题,在管理员 PowerShell 中运行:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
Get-AppxPackage -AllUsers *DesktopAppInstaller* | Reset-AppxPackage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 许可证
|
|
80
|
+
|
|
81
|
+
MIT
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# swpm
|
|
2
|
+
|
|
3
|
+
一個基於 [winget](https://github.com/microsoft/winget-cli) 的 Windows 軟體管理命令列工具。支援互動式批次升級、設定檔匯入匯出,不用再手動複製貼上那些長長的套件名稱了。
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) | [한국어](README.ko.md)
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
- 列出已安裝的軟體套件
|
|
10
|
+
- 搜尋 winget 倉庫
|
|
11
|
+
- 安裝 / 解除安裝套件(解除安裝支援互動式多選)
|
|
12
|
+
- 互動式升級(勾選想要的,跳過不需要的)
|
|
13
|
+
- 匯出當前環境到 YAML 檔案
|
|
14
|
+
- 同步 — 在新電腦上透過設定檔一鍵安裝所有軟體
|
|
15
|
+
|
|
16
|
+
## 環境需求
|
|
17
|
+
|
|
18
|
+
- **Windows 10 1809+** 或 **Windows 11**
|
|
19
|
+
- [App Installer (winget)](https://www.microsoft.com/p/app-installer/9nblggh4nns1)(Windows 11 已預裝)
|
|
20
|
+
- **Node.js >= 18**
|
|
21
|
+
|
|
22
|
+
## 安裝
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @double_seven/swpm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> **僅支援 Windows。** macOS 和 Linux 下安裝會直接失敗 — swpm 封裝的是 winget,Windows 專屬。
|
|
29
|
+
|
|
30
|
+
## 用法
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
swpm list # 列出已安裝軟體
|
|
34
|
+
swpm search vscode # 搜尋套件
|
|
35
|
+
swpm install 7zip.7zip # 透過套件 ID 安裝
|
|
36
|
+
swpm uninstall 7zip.7zip # 透過套件 ID 解除安裝
|
|
37
|
+
swpm uninstall # 互動式解除安裝(多選,預設不勾選)
|
|
38
|
+
swpm upgrade # 互動式勾選升級
|
|
39
|
+
swpm upgrade --all # 一鍵升級全部
|
|
40
|
+
swpm upgrade 7zip.7zip # 升級單個軟體
|
|
41
|
+
swpm export # 匯出到 ./swpm-packages.yaml
|
|
42
|
+
swpm export ./my-config.yaml # 匯出到指定路徑
|
|
43
|
+
swpm sync ./my-config.yaml # 安裝設定檔中缺失的軟體
|
|
44
|
+
swpm lang # 切換顯示語言
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 匯出與同步
|
|
48
|
+
|
|
49
|
+
匯出指令會把當前環境已安裝的 winget 套件儲存為 YAML 檔案:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
packages:
|
|
53
|
+
- 7zip.7zip
|
|
54
|
+
- CoreyButler.NVMforWindows
|
|
55
|
+
- Microsoft.VisualStudioCode
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
換了新電腦?執行 `swpm sync <設定檔>` 就能自動裝上所有缺失的軟體,已裝好的會自動跳過。
|
|
59
|
+
|
|
60
|
+
匯出時會彈出選擇介面,預設勾選非微軟軟體和 VS Code 相關內容,其他微軟軟體(OneDrive、.NET 執行時、Edge 等)預設不勾,避免把系統元件帶到別的機器上。
|
|
61
|
+
|
|
62
|
+
## 多語言
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
swpm lang # 互動式語言選擇
|
|
66
|
+
SWPM_LANG=ja # 或透過環境變數設定
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
支援:English(預設)、简体中文、繁體中文、日本語、한국어。
|
|
70
|
+
|
|
71
|
+
## winget 異常修復
|
|
72
|
+
|
|
73
|
+
如果 winget 出了問題,在系統管理員 PowerShell 中執行:
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
Get-AppxPackage -AllUsers *DesktopAppInstaller* | Reset-AppxPackage
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 授權
|
|
80
|
+
|
|
81
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function exportCommand(output: string | undefined): Promise<void>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.exportCommand = exportCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
11
|
+
const client_1 = require("../winget/client");
|
|
12
|
+
const spinner_1 = require("../ui/spinner");
|
|
13
|
+
const selector_1 = require("../ui/selector");
|
|
14
|
+
const i18n_1 = require("../i18n");
|
|
15
|
+
async function exportCommand(output) {
|
|
16
|
+
const filePath = path_1.default.resolve(output || "swpm-packages.yaml");
|
|
17
|
+
spinner_1.spinner.start((0, i18n_1.t)("export.getting"));
|
|
18
|
+
let packages;
|
|
19
|
+
try {
|
|
20
|
+
packages = (0, client_1.listInstalled)();
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
spinner_1.spinner.stop();
|
|
24
|
+
console.error(chalk_1.default.red(`✗ ${(0, i18n_1.t)("export.fail")}: ${err.message}`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
spinner_1.spinner.stop();
|
|
28
|
+
// Only include winget-managed packages (exclude ARP with \\, MSIX prefix, etc.)
|
|
29
|
+
const wingetPkgs = packages.filter((p) => p.id && !p.id.includes("\\") && !p.id.startsWith("MSIX\\"));
|
|
30
|
+
let selected;
|
|
31
|
+
try {
|
|
32
|
+
selected = await (0, selector_1.selectExportPackages)(wingetPkgs);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
console.log(chalk_1.default.gray(`\n${(0, i18n_1.t)("export.cancelled")}\n`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (selected.length === 0) {
|
|
39
|
+
console.log(chalk_1.default.gray(`${(0, i18n_1.t)("export.noSelection")}\n`));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const truncated = selected.filter((p) => p.id.endsWith("…"));
|
|
43
|
+
if (truncated.length > 0) {
|
|
44
|
+
console.log(chalk_1.default.gray((0, i18n_1.t)("export.resolving", truncated.length)));
|
|
45
|
+
}
|
|
46
|
+
const resolved = [];
|
|
47
|
+
for (const pkg of selected) {
|
|
48
|
+
if (pkg.id.endsWith("…")) {
|
|
49
|
+
resolved.push((0, client_1.resolveTruncatedId)(pkg));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
resolved.push(pkg.id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const ids = [...new Set(resolved)].sort();
|
|
56
|
+
const data = { packages: ids };
|
|
57
|
+
fs_1.default.writeFileSync(filePath, js_yaml_1.default.dump(data, { lineWidth: -1 }), "utf-8");
|
|
58
|
+
console.log(chalk_1.default.green(`\n✓ ${(0, i18n_1.t)("export.exported", ids.length, filePath)}\n`));
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installCommand(id: string): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installCommand = installCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const client_1 = require("../winget/client");
|
|
9
|
+
const i18n_1 = require("../i18n");
|
|
10
|
+
async function installCommand(id) {
|
|
11
|
+
console.log(chalk_1.default.cyan(`${(0, i18n_1.t)("install.installing")} ${id}...\n`));
|
|
12
|
+
try {
|
|
13
|
+
await (0, client_1.install)(id);
|
|
14
|
+
console.log(chalk_1.default.green(`\n✓ ${id} ${(0, i18n_1.t)("install.success")}\n`));
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
console.error(chalk_1.default.red(`\n✗ ${(0, i18n_1.t)("install.fail")}: ${err.message}`));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initLang = initLang;
|
|
7
|
+
exports.langCommand = langCommand;
|
|
8
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const config_1 = require("../config");
|
|
11
|
+
const i18n_1 = require("../i18n");
|
|
12
|
+
async function initLang(isFirstRun) {
|
|
13
|
+
// Check environment variable
|
|
14
|
+
const envLang = process.env.SWPM_LANG;
|
|
15
|
+
if (envLang) {
|
|
16
|
+
(0, i18n_1.setLang)(envLang);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Check saved config
|
|
20
|
+
const config = (0, config_1.readConfig)();
|
|
21
|
+
if (config.lang) {
|
|
22
|
+
(0, i18n_1.setLang)(config.lang);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// First run: prompt user
|
|
26
|
+
if (isFirstRun) {
|
|
27
|
+
await promptFirstRun();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function promptFirstRun() {
|
|
31
|
+
const langs = (0, i18n_1.getSupportedLangs)();
|
|
32
|
+
const choices = langs.map((lang) => ({
|
|
33
|
+
name: `${lang.nativeName} (${lang.name})`,
|
|
34
|
+
value: lang.code,
|
|
35
|
+
}));
|
|
36
|
+
const selected = await (0, prompts_1.select)({
|
|
37
|
+
message: `${(0, i18n_1.t)("lang.firstRun")}`,
|
|
38
|
+
choices: [
|
|
39
|
+
new prompts_1.Separator(" "),
|
|
40
|
+
...choices,
|
|
41
|
+
new prompts_1.Separator(" "),
|
|
42
|
+
],
|
|
43
|
+
theme: {
|
|
44
|
+
icon: {
|
|
45
|
+
cursor: chalk_1.default.cyan("❯"),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
(0, i18n_1.setLang)(selected);
|
|
50
|
+
(0, config_1.writeConfig)({ lang: selected });
|
|
51
|
+
console.log(chalk_1.default.green(`\n✓ ${(0, i18n_1.t)("lang.switched", (0, i18n_1.getLang)(), (0, i18n_1.getLang)())}\n`));
|
|
52
|
+
}
|
|
53
|
+
async function langCommand() {
|
|
54
|
+
const current = (0, i18n_1.getLang)();
|
|
55
|
+
const langs = (0, i18n_1.getSupportedLangs)();
|
|
56
|
+
const choices = langs.map((lang) => {
|
|
57
|
+
const marker = lang.code === current ? ` ${chalk_1.default.gray("(current)")}` : "";
|
|
58
|
+
return {
|
|
59
|
+
name: `${lang.nativeName} (${lang.name})${marker}`,
|
|
60
|
+
value: lang.code,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
const selected = await (0, prompts_1.select)({
|
|
64
|
+
message: (0, i18n_1.t)("lang.prompt"),
|
|
65
|
+
choices: [
|
|
66
|
+
new prompts_1.Separator(" "),
|
|
67
|
+
...choices,
|
|
68
|
+
new prompts_1.Separator(" "),
|
|
69
|
+
],
|
|
70
|
+
theme: {
|
|
71
|
+
icon: {
|
|
72
|
+
cursor: chalk_1.default.cyan("❯"),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
if (selected !== current) {
|
|
77
|
+
(0, i18n_1.setLang)(selected);
|
|
78
|
+
(0, config_1.writeConfig)({ lang: selected });
|
|
79
|
+
const info = langs.find((l) => l.code === selected);
|
|
80
|
+
console.log(chalk_1.default.green(`\n✓ ${(0, i18n_1.t)("lang.switched", info.nativeName, info.name)}\n`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function listCommand(): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listCommand = listCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const client_1 = require("../winget/client");
|
|
9
|
+
const table_1 = require("../ui/table");
|
|
10
|
+
const spinner_1 = require("../ui/spinner");
|
|
11
|
+
const i18n_1 = require("../i18n");
|
|
12
|
+
async function listCommand() {
|
|
13
|
+
spinner_1.spinner.start((0, i18n_1.t)("list.getting"));
|
|
14
|
+
try {
|
|
15
|
+
const packages = (0, client_1.listInstalled)();
|
|
16
|
+
spinner_1.spinner.stop();
|
|
17
|
+
console.log(chalk_1.default.bold(`\n📦 ${packages.length} packages\n`));
|
|
18
|
+
console.log((0, table_1.renderPackageTable)(packages, false));
|
|
19
|
+
console.log("");
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
spinner_1.spinner.stop();
|
|
23
|
+
console.error(chalk_1.default.red(`✗ ${(0, i18n_1.t)("list.fail")}: ${err.message}`));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function searchCommand(query: string): Promise<void>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.searchCommand = searchCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const client_1 = require("../winget/client");
|
|
9
|
+
const table_1 = require("../ui/table");
|
|
10
|
+
const spinner_1 = require("../ui/spinner");
|
|
11
|
+
const i18n_1 = require("../i18n");
|
|
12
|
+
async function searchCommand(query) {
|
|
13
|
+
spinner_1.spinner.start((0, i18n_1.t)("search.searching", query));
|
|
14
|
+
try {
|
|
15
|
+
const results = (0, client_1.search)(query);
|
|
16
|
+
spinner_1.spinner.stop();
|
|
17
|
+
console.log(chalk_1.default.bold(`\n${(0, i18n_1.t)("search.results", query, results.length)}\n`));
|
|
18
|
+
console.log((0, table_1.renderPackageTable)(results));
|
|
19
|
+
console.log("");
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
spinner_1.spinner.stop();
|
|
23
|
+
console.error(chalk_1.default.red(`✗ ${(0, i18n_1.t)("search.fail")}: ${err.message}`));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function syncCommand(configPath: string): Promise<void>;
|