pi-cloud 2.0.0
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/.env.example +115 -0
- package/CHANGELOG.md +177 -0
- package/LICENSE +201 -0
- package/README.md +134 -0
- package/README.zh-CN.md +134 -0
- package/THIRD_PARTY_NOTICES +13 -0
- package/bin/pi-cloud.mjs +248 -0
- package/bin/runtime-config.mjs +11 -0
- package/client/dist/.vite/manifest.json +1553 -0
- package/client/dist/android-chrome-192x192.png +0 -0
- package/client/dist/android-chrome-512x512.png +0 -0
- package/client/dist/apple-touch-icon.png +0 -0
- package/client/dist/assets/EditorPanel-C4j_LnbI.js +968 -0
- package/client/dist/assets/EditorPanel-qAJ9i_Hs.css +1 -0
- package/client/dist/assets/GitHistoryView-BZF9vYG5.css +1 -0
- package/client/dist/assets/GitHistoryView-i__uu43I.js +2 -0
- package/client/dist/assets/MemoryCenter-BthPz7a1.css +1 -0
- package/client/dist/assets/MemoryCenter-DOvz1FIy.js +1 -0
- package/client/dist/assets/MesloLGMNerdFontMono-Regular-COOxA6qy.ttf +0 -0
- package/client/dist/assets/PhCaretLeft.vue-C3HxQ-jr.js +1 -0
- package/client/dist/assets/SettingsDialog-BTyTnZYM.css +1 -0
- package/client/dist/assets/SettingsDialog-VlpollL1.js +2 -0
- package/client/dist/assets/TaskQueuePanel-Bnvgy4cm.css +1 -0
- package/client/dist/assets/TaskQueuePanel-DPnDhZFb.js +1 -0
- package/client/dist/assets/TerminalPanel-BQhGwCE5.js +1 -0
- package/client/dist/assets/TerminalPanel-DmemnVxo.css +1 -0
- package/client/dist/assets/abap-DLDM7-KI.js +1 -0
- package/client/dist/assets/abnfDiagram-VCTEODGH-DyGBNBbN.js +1 -0
- package/client/dist/assets/apex-DNDY2TF8.js +1 -0
- package/client/dist/assets/arc-B_21hOEg.js +1 -0
- package/client/dist/assets/architectureDiagram-5GKGNRK7-CjyAJ-2z.js +36 -0
- package/client/dist/assets/azcli-Y6nb8tq_.js +1 -0
- package/client/dist/assets/bat-BwHxbl9M.js +1 -0
- package/client/dist/assets/bicep-CFznDFnq.js +2 -0
- package/client/dist/assets/blockDiagram-NRAW4CY4-CmrEp6Fe.js +129 -0
- package/client/dist/assets/c4Diagram-UCG6FXSJ-DfT2xU1s.js +38 -0
- package/client/dist/assets/cameligo-Bf6VGUru.js +1 -0
- package/client/dist/assets/channel-CROBmx9n.js +1 -0
- package/client/dist/assets/chunk-2Q5K7J3B-BVEJoy95.js +1 -0
- package/client/dist/assets/chunk-5VM5RSS4-AYOc2H0F.js +15 -0
- package/client/dist/assets/chunk-F27PBJKO-CAdIcM4C.js +1 -0
- package/client/dist/assets/chunk-G27WJ6UU-Bk4Qjg6Y.js +231 -0
- package/client/dist/assets/chunk-JWPE2WC7-u-wi2HbJ.js +1 -0
- package/client/dist/assets/chunk-LCL6LL3I-BgicKCMi.js +206 -0
- package/client/dist/assets/chunk-POPQ4Y6H-DhJ3_oti.js +1 -0
- package/client/dist/assets/chunk-SVP7TREG-B-FKnWYy.js +88 -0
- package/client/dist/assets/chunk-XXDRQBXY-Dnsv7iwr.js +1 -0
- package/client/dist/assets/classDiagram-DTDB5LWJ-D2KSupgp.js +1 -0
- package/client/dist/assets/classDiagram-v2-JRS7N3AN-D2KSupgp.js +1 -0
- package/client/dist/assets/clojure-Dnu-v4kV.js +1 -0
- package/client/dist/assets/codicon-Brq4_Ui5.ttf +0 -0
- package/client/dist/assets/coffee-Bd8akH9Z.js +1 -0
- package/client/dist/assets/cose-bilkent-JH36ORCC-DL2lIpOf.js +1 -0
- package/client/dist/assets/cpp-BbWJElDN.js +1 -0
- package/client/dist/assets/csharp-Co3qMtFm.js +1 -0
- package/client/dist/assets/csp-D-4FJmMZ.js +1 -0
- package/client/dist/assets/css-DdJfP1eB.js +3 -0
- package/client/dist/assets/css.worker-CXZsIV5y.js +97 -0
- package/client/dist/assets/cssMode-BcZYukVu.js +1 -0
- package/client/dist/assets/cynefin-OW5HDTMX-tZ1QaCp9.js +166 -0
- package/client/dist/assets/cynefinDiagram-5FMLGOSQ-GLzVU_pJ.js +62 -0
- package/client/dist/assets/cypher-cTPe9QuQ.js +1 -0
- package/client/dist/assets/cytoscape.esm-yzknjiTM.js +321 -0
- package/client/dist/assets/dagre-3AP2YEHR-B8V1RvVr.js +4 -0
- package/client/dist/assets/dart-BOtBlQCF.js +1 -0
- package/client/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/client/dist/assets/diagram-S7CK7UJ4-MTofUt2F.js +30 -0
- package/client/dist/assets/diagram-UQ7AKVKN-Dw6Onbe3.js +41 -0
- package/client/dist/assets/diagram-VSXAHHWV-C4WupsI1.js +3 -0
- package/client/dist/assets/diagram-VX7I27RA-CF78AvRx.js +24 -0
- package/client/dist/assets/diagram-Z3DM3KII-xT7zx4S2.js +24 -0
- package/client/dist/assets/dockerfile-BG73LgW2.js +1 -0
- package/client/dist/assets/ebnfDiagram-PWID7BFC-zkR-_zOG.js +1 -0
- package/client/dist/assets/ecl-BEgZUVRK.js +1 -0
- package/client/dist/assets/editor.worker-CRaYpIdo.js +30 -0
- package/client/dist/assets/elixir-BkW5O-1t.js +1 -0
- package/client/dist/assets/erDiagram-SSCWMZ5O-BzU9E1j8.js +99 -0
- package/client/dist/assets/flow9-BeJ5waoc.js +1 -0
- package/client/dist/assets/flowDiagram-A5DVABFB-BM-160SB.js +168 -0
- package/client/dist/assets/freemarker2-B4PXq-2i.js +3 -0
- package/client/dist/assets/fsharp-PahG7c26.js +1 -0
- package/client/dist/assets/ganttDiagram-EL5Y4UJY-LVhMiHMo.js +292 -0
- package/client/dist/assets/gitGraphDiagram-WWUBYQGX-CYGEpNO-.js +106 -0
- package/client/dist/assets/go-acbASCJo.js +1 -0
- package/client/dist/assets/graphql-BxJiqAUM.js +1 -0
- package/client/dist/assets/handlebars-DleyW4LO.js +1 -0
- package/client/dist/assets/hcl-DtV1sZF8.js +1 -0
- package/client/dist/assets/html-BJ95-ybC.js +1 -0
- package/client/dist/assets/html.worker-Ca87u24v.js +474 -0
- package/client/dist/assets/html2canvas.esm-DXEQVQnt.js +5 -0
- package/client/dist/assets/htmlMode-Cqq8N0XF.js +1 -0
- package/client/dist/assets/index-C1R3qRwd.js +407 -0
- package/client/dist/assets/index-D1Z3F2UU.css +1 -0
- package/client/dist/assets/index-PSBDlhaw.js +39 -0
- package/client/dist/assets/infoDiagram-RXCK75RN-Ctmoz63S.js +2 -0
- package/client/dist/assets/ini-Kd9XrMLS.js +1 -0
- package/client/dist/assets/init-Gi6I4Gst.js +1 -0
- package/client/dist/assets/ishikawaDiagram-5VMMS53U-DiWG9VHU.js +70 -0
- package/client/dist/assets/java-CXBNlu9o.js +1 -0
- package/client/dist/assets/javascript-BIGigzp1.js +1 -0
- package/client/dist/assets/journeyDiagram-EYS64GPL-CImdI3K_.js +139 -0
- package/client/dist/assets/json.worker-C5PEkLU0.js +62 -0
- package/client/dist/assets/jsonMode-l6_K9bFf.js +7 -0
- package/client/dist/assets/julia-cl7-CwDS.js +1 -0
- package/client/dist/assets/kanban-definition-3QL26DDD-CSSranPL.js +89 -0
- package/client/dist/assets/katex-HP8lGamR.js +257 -0
- package/client/dist/assets/kotlin-s7OhZKlX.js +1 -0
- package/client/dist/assets/layout-CE5PddK3.js +1 -0
- package/client/dist/assets/less-9HpZscsL.js +2 -0
- package/client/dist/assets/lexon-OrD6JF1K.js +1 -0
- package/client/dist/assets/linear-CkzMiZL-.js +1 -0
- package/client/dist/assets/liquid-C8hVjUZv.js +1 -0
- package/client/dist/assets/lspLanguageFeatures-ZfiCKJ_J.js +4 -0
- package/client/dist/assets/lua-Cyyb5UIc.js +1 -0
- package/client/dist/assets/m3-B8OfTtLu.js +1 -0
- package/client/dist/assets/markdown-BFxVWTOG.js +1 -0
- package/client/dist/assets/mdx-DWmYYep4.js +1 -0
- package/client/dist/assets/mermaid.core-BlcsAN4s.js +308 -0
- package/client/dist/assets/mindmap-definition-FBJOCRG2-D01B1gH8.js +96 -0
- package/client/dist/assets/mips-CiqrrVzr.js +1 -0
- package/client/dist/assets/msdax-DmeGPVcC.js +1 -0
- package/client/dist/assets/mysql-C_tMU-Nz.js +1 -0
- package/client/dist/assets/objective-c-BDtDVThU.js +1 -0
- package/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/client/dist/assets/pascal-vHIfCaH5.js +1 -0
- package/client/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
- package/client/dist/assets/pdf-DMRcuM1e.js +55 -0
- package/client/dist/assets/pdf.worker.min-CHFwMXne.mjs +29 -0
- package/client/dist/assets/pegDiagram-XKGWAZYB-DhdSTBt5.js +1 -0
- package/client/dist/assets/perl-Ub6l9XKa.js +1 -0
- package/client/dist/assets/pgsql-BlNEE0v7.js +1 -0
- package/client/dist/assets/php-BBUBE1dy.js +1 -0
- package/client/dist/assets/pieDiagram-E7YTZNPT-hVSzmo7b.js +39 -0
- package/client/dist/assets/pla-DSh2-awV.js +1 -0
- package/client/dist/assets/postiats-CocnycG-.js +1 -0
- package/client/dist/assets/powerquery-tScXyioY.js +1 -0
- package/client/dist/assets/powershell-COWaemsV.js +1 -0
- package/client/dist/assets/protobuf-Brw8urJB.js +2 -0
- package/client/dist/assets/pug-8SOpv6rk.js +1 -0
- package/client/dist/assets/python-AbIuqkub.js +1 -0
- package/client/dist/assets/qsharp-Bw9ernYp.js +1 -0
- package/client/dist/assets/quadrantDiagram-AXDQQJYC-CVexQ_Dn.js +7 -0
- package/client/dist/assets/r-j7ic8hl3.js +1 -0
- package/client/dist/assets/railroadDiagram-O6MQD6OU-BPCMbhAx.js +1 -0
- package/client/dist/assets/razor-PLr1HLk0.js +1 -0
- package/client/dist/assets/redis-Bu5POkcn.js +1 -0
- package/client/dist/assets/redshift-Bs9aos_-.js +1 -0
- package/client/dist/assets/requirementDiagram-EFPCY7ZU-EntABUqc.js +84 -0
- package/client/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
- package/client/dist/assets/ruby-zBfavPgS.js +1 -0
- package/client/dist/assets/rust-BzKRNQWT.js +1 -0
- package/client/dist/assets/sankeyDiagram-P5KCCOFB-D68oih_M.js +40 -0
- package/client/dist/assets/sb-BBc9UKZt.js +1 -0
- package/client/dist/assets/scala-D9hQfWCl.js +1 -0
- package/client/dist/assets/scheme-BPhDTwHR.js +1 -0
- package/client/dist/assets/scss-CBJaRo0y.js +3 -0
- package/client/dist/assets/sequenceDiagram-WJ2MYXX4-DWzaT5vI.js +162 -0
- package/client/dist/assets/shell-DiJ1NA_G.js +1 -0
- package/client/dist/assets/sizeCapture-X5ZJPWSS-HbYBaSbU.js +1 -0
- package/client/dist/assets/solidity-Db0IVjzk.js +1 -0
- package/client/dist/assets/sophia-CnS9iZB_.js +1 -0
- package/client/dist/assets/sparql-CJmd_6j2.js +1 -0
- package/client/dist/assets/sql-ClhHkBeG.js +1 -0
- package/client/dist/assets/st-CHwy0fLd.js +1 -0
- package/client/dist/assets/stateDiagram-HBIQ2CUA-BteRSnYL.js +1 -0
- package/client/dist/assets/stateDiagram-v2-4QOOHH4V-CN8Njcij.js +1 -0
- package/client/dist/assets/swift-CnmFD0ga.js +1 -0
- package/client/dist/assets/swimlanes-XN3QIQJK-BAvVt5mV.js +1 -0
- package/client/dist/assets/swimlanesDiagram-VK2B7HYN-p55moRoN.js +8 -0
- package/client/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
- package/client/dist/assets/tcl-Dm6ycUr_.js +1 -0
- package/client/dist/assets/timeline-definition-24CTP7MA-D6yKUiYl.js +120 -0
- package/client/dist/assets/ts.worker-Y8i9TNcE.js +67722 -0
- package/client/dist/assets/tsMode-DrakxHLR.js +11 -0
- package/client/dist/assets/twig-Csy3S7wG.js +1 -0
- package/client/dist/assets/typescript-DcMOCMAD.js +1 -0
- package/client/dist/assets/typespec-Btyra-wh.js +1 -0
- package/client/dist/assets/useTerminal-DDGTF8rc.css +1 -0
- package/client/dist/assets/useTerminal-Dhk5K03J.js +20 -0
- package/client/dist/assets/vb-Db0cS2oM.js +1 -0
- package/client/dist/assets/vennDiagram-4TSXK5OY-CGU61Pyq.js +34 -0
- package/client/dist/assets/wardleyDiagram-VM6X3IG4-BrTe4Lxv.js +78 -0
- package/client/dist/assets/wgsl-BTesnYfV.js +298 -0
- package/client/dist/assets/workers-CqVatS52.js +1 -0
- package/client/dist/assets/xml-xUSiNagm.js +1 -0
- package/client/dist/assets/xychartDiagram-S5SC5T6Z-CnMmHAmD.js +7 -0
- package/client/dist/assets/yaml-CSavWraE.js +1 -0
- package/client/dist/favicon-16x16.ico +0 -0
- package/client/dist/favicon-16x16.png +0 -0
- package/client/dist/favicon-32x32.ico +0 -0
- package/client/dist/favicon-32x32.png +0 -0
- package/client/dist/favicon.ico +0 -0
- package/client/dist/favicon.svg +10 -0
- package/client/dist/icon.svg +17 -0
- package/client/dist/index.html +43 -0
- package/client/dist/site.webmanifest +19 -0
- package/client/src/assets/fonts/MesloLGMNerdFontMono-Regular.LICENSE +126 -0
- package/package.json +74 -0
- package/server/dist/index.js +15143 -0
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Pi Cloud
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
一个基于 Web、适配移动设备的 [Pi 编程智能体](http://pi.dev)界面。它为以终端为主的 CLI 提供了补充,让智能体会话和开发工作流拥有一个持久化工作区。
|
|
6
|
+
|
|
7
|
+
| 深色主题 | 浅色主题 |
|
|
8
|
+
| ------------------------------------------------------------------ | ------------------------------------------------------------------- |
|
|
9
|
+
|  |  |
|
|
10
|
+
|
|
11
|
+
**[▶ 观看 Pi Cloud 完成 Git 缺陷修复工作流](https://xianzhon.github.io/pi-cloud-website/assets/videos/demo-git-bugfix-workflow.mp4)**
|
|
12
|
+
|
|
13
|
+
## 功能特性
|
|
14
|
+
|
|
15
|
+
- **移动端友好** — 无需本地终端,即可在电脑、手机或平板上使用 Pi。
|
|
16
|
+
- **技能控制** — 配置预设,并为每个会话启用或禁用技能。
|
|
17
|
+
- **会话管理** — 搜索、恢复、重命名和分支会话,并在不同标签页中运行独立会话。
|
|
18
|
+
- **任务队列** — 保存和配置任务,准备就绪后再启动。
|
|
19
|
+
- **完成通知** — 任务完成时接收提示音,并在浏览器标签页中看到铃铛提示。
|
|
20
|
+
- **AI 辅助写作** — 生成或润色提示词、提交信息、拉取请求内容及其他工作流文本。
|
|
21
|
+
- **Git 工作流** — 在 WebUI 中使用 `/branch`、`/commit`、`/push` 等命令。
|
|
22
|
+
- **GitHub 和 Gitea 集成** — 使用 `/pr` 审查更改并创建拉取请求。
|
|
23
|
+
- **记忆支持** — 维护项目级和全局记忆,并可选择自动提取和自适应召回。
|
|
24
|
+
- **消息网关** — 通过简洁的配置接入飞书和微信。
|
|
25
|
+
- **工作区工具** — 浏览和搜索文件、编辑代码,并在对话旁使用内嵌终端。
|
|
26
|
+
- **安全访问** — 使用密码和可选的 TOTP 身份验证保护 WebUI。
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
### 前置要求
|
|
31
|
+
|
|
32
|
+
- Node.js 22 或更高版本
|
|
33
|
+
- pnpm 9 或更高版本(仅从源码开发时需要)
|
|
34
|
+
|
|
35
|
+
Pi Cloud 已包含 [Pi 编程智能体](http://pi.dev),因此使用 API 密钥进行身份验证时无需单独安装 `pi`。API 密钥可以在 Agent 配置对话框中设置。
|
|
36
|
+
|
|
37
|
+
WebUI 目前尚不支持 OAuth 订阅登录;原生 OAuth 支持已列为未来改进计划。现阶段若要使用 OAuth,请安装独立的 Pi CLI,并以运行 Pi Cloud 的同一操作系统用户在终端中完成 `/login`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
|
41
|
+
pi
|
|
42
|
+
# 输入 /login 并选择提供商
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
之后,Pi Cloud 会检测默认 `~/.pi/agent` 配置中存储的 OAuth 凭据。
|
|
46
|
+
|
|
47
|
+
### 无需安装直接运行
|
|
48
|
+
|
|
49
|
+
如果 npm 在构建原生依赖时失败,请安装 `build-essential`、`python3` 等系统构建工具,然后重试。
|
|
50
|
+
|
|
51
|
+
设置登录凭据,然后运行最新版本:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
PI_CLOUD_AUTH_USERNAME=pi PI_CLOUD_AUTH_PASSWORD='change-this-password' npx pi-cloud@latest
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
打开 http://127.0.0.1:3000。服务器准备就绪后,CLI 会自动打开浏览器。
|
|
58
|
+
|
|
59
|
+
### 全局安装
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install -g pi-cloud
|
|
63
|
+
pi-cloud
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
首次运行时,Pi Cloud 会创建受保护的配置文件,用户名默认为 `admin`,并仅显示一次随机生成的密码。
|
|
67
|
+
|
|
68
|
+
请参阅[配置手册](docs/manuals-cn/configuration.md),了解如何自定义凭据、端口和其他设置。如需远程或公网访问,请参阅[部署手册](docs/manuals-cn/deployment.md)。
|
|
69
|
+
|
|
70
|
+
### 作为系统服务运行
|
|
71
|
+
|
|
72
|
+
在 Linux、macOS 或 Windows 上将 Pi Cloud 安装为开机启动服务:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pi-cloud service install
|
|
76
|
+
pi-cloud service status
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
在 Windows 上,防病毒软件可能会阻止 `pi-cloud service install`,因为该命令会创建自动启动的计划任务。允许前请核对防病毒软件的检测内容;请勿禁用防病毒软件,也不要排除整个 npm 目录。
|
|
80
|
+
|
|
81
|
+
### 从源码运行
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/xianzhon/pi-cloud && cd pi-cloud
|
|
85
|
+
pnpm install
|
|
86
|
+
cp .env.example .env # 设置 PI_CLOUD_AUTH_USERNAME 和 PI_CLOUD_AUTH_PASSWORD
|
|
87
|
+
pnpm dev
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
开发期间请打开 http://localhost:5173。生产服务器监听 `PORT`(默认值为 `3000`)。
|
|
91
|
+
|
|
92
|
+
## 配置
|
|
93
|
+
|
|
94
|
+
基本设置如下:
|
|
95
|
+
|
|
96
|
+
| 变量 | 说明 |
|
|
97
|
+
| -------------------------- | --------------------------------- |
|
|
98
|
+
| `PI_CLOUD_AUTH_USERNAME` | 登录用户名 |
|
|
99
|
+
| `PI_CLOUD_AUTH_PASSWORD` | 简单部署所使用的登录密码 |
|
|
100
|
+
| `PORT` | 后端端口(默认值:`3000`) |
|
|
101
|
+
| `HOST` | 绑定地址(默认值:`127.0.0.1`) |
|
|
102
|
+
|
|
103
|
+
生产环境中,请使用 `PI_CLOUD_AUTH_PASSWORD_HASH`,而不是明文密码。有关配置文件、安全、会话、存储、工作区、记忆、提供商和网关设置,请参阅[配置手册](docs/manuals-cn/configuration.md)。
|
|
104
|
+
|
|
105
|
+
## 部署
|
|
106
|
+
|
|
107
|
+
有关源码和 npm 软件包部署以及 nginx 反向代理配置,请参阅[部署手册](docs/manuals-cn/deployment.md)。
|
|
108
|
+
|
|
109
|
+
## 文档
|
|
110
|
+
|
|
111
|
+
浏览[文档索引](docs/README.md),查看全部用户手册和开发者参考资料。
|
|
112
|
+
|
|
113
|
+
## 开发
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pnpm dev # 启动前后端服务器
|
|
117
|
+
pnpm build # 类型检查并构建前后端
|
|
118
|
+
pnpm test # 运行所有测试
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- 前端:http://localhost:5173(Vite,将 `/api` 和 `/ws` 代理到后端)
|
|
122
|
+
- 后端:http://localhost:3000(Fastify)
|
|
123
|
+
- 日志:`.logs/`
|
|
124
|
+
- PID 文件:`.pids/`
|
|
125
|
+
|
|
126
|
+
有关架构、API、本地软件包测试和项目目录结构的说明,请参阅[开发者说明(英文)](docs/to-developers/developer-notes.md)。
|
|
127
|
+
|
|
128
|
+
## 贡献
|
|
129
|
+
|
|
130
|
+
欢迎贡献。有关开发环境设置、分支和拉取请求规范、测试、文档要求及安全问题报告方式,请参阅 [CONTRIBUTING.md(英文)](CONTRIBUTING.md)。
|
|
131
|
+
|
|
132
|
+
## 许可证
|
|
133
|
+
|
|
134
|
+
本项目采用 Apache License 2.0 许可。详情请参阅 [LICENSE](./LICENSE)。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Third-Party Notices
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
PDF.js
|
|
5
|
+
------
|
|
6
|
+
|
|
7
|
+
Copyright 2024 Mozilla Foundation
|
|
8
|
+
|
|
9
|
+
PDF.js is licensed under the Apache License, Version 2.0.
|
|
10
|
+
A copy of the Apache License is included in the LICENSE file distributed with
|
|
11
|
+
this package.
|
|
12
|
+
|
|
13
|
+
Source: https://github.com/mozilla/pdf.js
|
package/bin/pi-cloud.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
8
|
+
import { loadRuntimeConfig } from './runtime-config.mjs';
|
|
9
|
+
|
|
10
|
+
function printHelp() {
|
|
11
|
+
console.log(`Usage: pi-cloud [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
-p, --port <port> Listen on this port (default: 3000)
|
|
15
|
+
-H, --hostname <host> Bind to this hostname (default: 127.0.0.1)
|
|
16
|
+
--no-open Do not open the browser
|
|
17
|
+
-v, --version Show the current version
|
|
18
|
+
-h, --help Show this help
|
|
19
|
+
|
|
20
|
+
Environment variables PORT and HOST are also supported.
|
|
21
|
+
|
|
22
|
+
Service commands:
|
|
23
|
+
service install Install and enable automatic startup
|
|
24
|
+
service start Start the service
|
|
25
|
+
service stop Stop the service
|
|
26
|
+
service restart Restart the service
|
|
27
|
+
service status Show service status
|
|
28
|
+
service uninstall Remove the service
|
|
29
|
+
update [version] Update the global npm installation
|
|
30
|
+
update --check Check for available updates`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseArgs(args) {
|
|
34
|
+
const options = {};
|
|
35
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
36
|
+
const arg = args[index];
|
|
37
|
+
if (arg === '-h' || arg === '--help') {
|
|
38
|
+
printHelp();
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
if (arg === '-v' || arg === '--version') {
|
|
42
|
+
console.log(installedVersion());
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
if (arg === '--no-open') {
|
|
46
|
+
options.noOpen = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (arg === '-p' || arg === '--port' || arg === '-H' || arg === '--hostname') {
|
|
50
|
+
const value = args[index + 1];
|
|
51
|
+
if (!value || value.startsWith('-')) throw new Error(`${arg} requires a value`);
|
|
52
|
+
options[arg === '-p' || arg === '--port' ? 'port' : 'hostname'] = value;
|
|
53
|
+
index += 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
57
|
+
}
|
|
58
|
+
return options;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const SERVICE_NAME = 'pi-cloud';
|
|
62
|
+
const SERVICE_LABEL = 'com.pi-cloud';
|
|
63
|
+
const NPM_PACKAGE_NAME = 'pi-cloud';
|
|
64
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
65
|
+
|
|
66
|
+
function runCommand(command, args) {
|
|
67
|
+
const result = spawnSync(command, args, { stdio: 'inherit' });
|
|
68
|
+
if (result.error) throw result.error;
|
|
69
|
+
if (result.status !== 0) throw new Error(`${command} exited with status ${result.status}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function spawnNpm(args, options) {
|
|
73
|
+
if (process.platform === 'win32') {
|
|
74
|
+
// npm is installed as npm.cmd on Windows, which must run through cmd.exe.
|
|
75
|
+
return spawnSync(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'npm.cmd', ...args], options);
|
|
76
|
+
}
|
|
77
|
+
return spawnSync('npm', args, options);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function runNpm(args) {
|
|
81
|
+
const result = spawnNpm(args, { stdio: 'inherit' });
|
|
82
|
+
if (result.error) throw result.error;
|
|
83
|
+
if (result.status !== 0) throw new Error(`npm exited with status ${result.status}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function linuxServicePath() {
|
|
87
|
+
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'systemd', 'user', `${SERVICE_NAME}.service`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function macServicePath() {
|
|
91
|
+
return join(homedir(), 'Library', 'LaunchAgents', `${SERVICE_LABEL}.plist`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function installLinuxService() {
|
|
95
|
+
const servicePath = linuxServicePath();
|
|
96
|
+
mkdirSync(join(servicePath, '..'), { recursive: true });
|
|
97
|
+
writeFileSync(servicePath, `[Unit]\nDescription=Pi Cloud\nAfter=network-online.target\n\n[Service]\nType=simple\nExecStart="${process.execPath}" "${scriptPath}" --no-open\nWorkingDirectory=${homedir()}\nEnvironment=HOME=${homedir()}\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=default.target\n`);
|
|
98
|
+
runCommand('systemctl', ['--user', 'daemon-reload']);
|
|
99
|
+
runCommand('systemctl', ['--user', 'enable', '--now', SERVICE_NAME]);
|
|
100
|
+
console.log(`Installed Linux service at ${servicePath}`);
|
|
101
|
+
console.log('To start automatically before login, enable user lingering with: sudo loginctl enable-linger "$USER"');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function installMacService() {
|
|
105
|
+
const servicePath = macServicePath();
|
|
106
|
+
mkdirSync(join(servicePath, '..'), { recursive: true });
|
|
107
|
+
const xmlEscape = (value) => value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
108
|
+
writeFileSync(servicePath, `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n<key>Label</key><string>${SERVICE_LABEL}</string>\n<key>ProgramArguments</key><array><string>${xmlEscape(process.execPath)}</string><string>${xmlEscape(scriptPath)}</string><string>--no-open</string></array>\n<key>WorkingDirectory</key><string>${xmlEscape(homedir())}</string>\n<key>RunAtLoad</key><true/>\n<key>KeepAlive</key><true/>\n</dict></plist>\n`);
|
|
109
|
+
const domain = `gui/${process.getuid()}`;
|
|
110
|
+
spawnSync('launchctl', ['bootout', domain, servicePath], { stdio: 'ignore' });
|
|
111
|
+
runCommand('launchctl', ['bootstrap', domain, servicePath]);
|
|
112
|
+
console.log(`Installed macOS LaunchAgent at ${servicePath}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function installWindowsService() {
|
|
116
|
+
const taskCommand = `"${process.execPath}" "${scriptPath}" --no-open`;
|
|
117
|
+
runCommand('schtasks', ['/Create', '/TN', SERVICE_NAME, '/SC', 'ONLOGON', '/TR', taskCommand, '/F']);
|
|
118
|
+
runCommand('schtasks', ['/Run', '/TN', SERVICE_NAME]);
|
|
119
|
+
console.log('Installed Windows startup task Pi Cloud.');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function installedVersion() {
|
|
123
|
+
const packagePath = join(dirname(scriptPath), '..', 'package.json');
|
|
124
|
+
return JSON.parse(readFileSync(packagePath, 'utf8')).version;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function updateCommand(version) {
|
|
128
|
+
const currentVersion = installedVersion();
|
|
129
|
+
if (version === '--check') {
|
|
130
|
+
const result = spawnNpm(['view', NPM_PACKAGE_NAME, 'version', '--silent'], { encoding: 'utf8' });
|
|
131
|
+
if (result.error) throw result.error;
|
|
132
|
+
if (result.status !== 0) throw new Error(result.stderr?.trim() || 'Could not check the latest version.');
|
|
133
|
+
const latestVersion = result.stdout.trim();
|
|
134
|
+
console.log(`Installed version: ${currentVersion}`);
|
|
135
|
+
console.log(`Latest version: ${latestVersion}`);
|
|
136
|
+
console.log(currentVersion === latestVersion ? 'Pi Cloud is up to date.' : 'An update is available. Run: pi-cloud update');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (version && !/^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(version)) {
|
|
141
|
+
throw new Error(`Invalid version or npm tag: ${version}`);
|
|
142
|
+
}
|
|
143
|
+
const packageSpec = version ? `${NPM_PACKAGE_NAME}@${version}` : `${NPM_PACKAGE_NAME}@latest`;
|
|
144
|
+
runNpm(['install', '-g', packageSpec]);
|
|
145
|
+
console.log(`Updated ${packageSpec}.`);
|
|
146
|
+
console.log('If Pi Cloud is running as a service, restart it to use the new version:');
|
|
147
|
+
console.log(' pi-cloud service restart');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function serviceCommand(action) {
|
|
151
|
+
if (!action) throw new Error('Usage: pi-cloud service <install|start|stop|restart|status|uninstall>');
|
|
152
|
+
|
|
153
|
+
if (process.platform === 'linux') {
|
|
154
|
+
if (action === 'install') return installLinuxService();
|
|
155
|
+
if (action === 'start') return runCommand('systemctl', ['--user', 'start', SERVICE_NAME]);
|
|
156
|
+
if (action === 'stop') return runCommand('systemctl', ['--user', 'stop', SERVICE_NAME]);
|
|
157
|
+
if (action === 'restart') return runCommand('systemctl', ['--user', 'restart', SERVICE_NAME]);
|
|
158
|
+
if (action === 'status') return runCommand('systemctl', ['--user', 'status', SERVICE_NAME]);
|
|
159
|
+
if (action === 'uninstall') {
|
|
160
|
+
runCommand('systemctl', ['--user', 'disable', '--now', SERVICE_NAME]);
|
|
161
|
+
rmSync(linuxServicePath(), { force: true });
|
|
162
|
+
return runCommand('systemctl', ['--user', 'daemon-reload']);
|
|
163
|
+
}
|
|
164
|
+
} else if (process.platform === 'darwin') {
|
|
165
|
+
const domain = `gui/${process.getuid()}`;
|
|
166
|
+
const servicePath = macServicePath();
|
|
167
|
+
if (action === 'install') return installMacService();
|
|
168
|
+
if (action === 'start') return runCommand('launchctl', ['kickstart', `${domain}/${SERVICE_LABEL}`]);
|
|
169
|
+
if (action === 'stop') return runCommand('launchctl', ['kill', 'SIGTERM', `${domain}/${SERVICE_LABEL}`]);
|
|
170
|
+
if (action === 'restart') return runCommand('launchctl', ['kickstart', '-k', `${domain}/${SERVICE_LABEL}`]);
|
|
171
|
+
if (action === 'status') return runCommand('launchctl', ['print', `${domain}/${SERVICE_LABEL}`]);
|
|
172
|
+
if (action === 'uninstall') {
|
|
173
|
+
runCommand('launchctl', ['bootout', domain, servicePath]);
|
|
174
|
+
return rmSync(servicePath, { force: true });
|
|
175
|
+
}
|
|
176
|
+
} else if (process.platform === 'win32') {
|
|
177
|
+
if (action === 'install') return installWindowsService();
|
|
178
|
+
if (action === 'start') return runCommand('schtasks', ['/Run', '/TN', SERVICE_NAME]);
|
|
179
|
+
if (action === 'stop') return runCommand('schtasks', ['/End', '/TN', SERVICE_NAME]);
|
|
180
|
+
if (action === 'restart') {
|
|
181
|
+
runCommand('schtasks', ['/End', '/TN', SERVICE_NAME]);
|
|
182
|
+
return runCommand('schtasks', ['/Run', '/TN', SERVICE_NAME]);
|
|
183
|
+
}
|
|
184
|
+
if (action === 'status') return runCommand('schtasks', ['/Query', '/TN', SERVICE_NAME]);
|
|
185
|
+
if (action === 'uninstall') return runCommand('schtasks', ['/Delete', '/TN', SERVICE_NAME, '/F']);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
throw new Error(`Unknown service action: ${action}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function openBrowser(url) {
|
|
192
|
+
let command;
|
|
193
|
+
let args;
|
|
194
|
+
let shell = false;
|
|
195
|
+
|
|
196
|
+
if (process.platform === 'darwin') {
|
|
197
|
+
command = 'open';
|
|
198
|
+
args = [url];
|
|
199
|
+
} else if (process.platform === 'win32') {
|
|
200
|
+
command = 'cmd.exe';
|
|
201
|
+
args = ['/c', 'start', '', url];
|
|
202
|
+
} else {
|
|
203
|
+
command = 'xdg-open';
|
|
204
|
+
args = [url];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore', shell });
|
|
208
|
+
child.on('error', (error) => {
|
|
209
|
+
if (error.code === 'ENOENT') {
|
|
210
|
+
console.warn(`Could not find ${command}; open ${url} manually or use --no-open.`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
console.warn(`Could not open ${url}: ${error.message}`);
|
|
214
|
+
});
|
|
215
|
+
child.unref();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const args = process.argv.slice(2);
|
|
220
|
+
if (args[0] === 'service') {
|
|
221
|
+
serviceCommand(args[1]);
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
if (args[0] === 'update') {
|
|
225
|
+
updateCommand(args[1]);
|
|
226
|
+
process.exit(0);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const options = parseArgs(args);
|
|
230
|
+
process.env.PI_CLOUD_CLI_IMPORT = '1';
|
|
231
|
+
|
|
232
|
+
const { startServer } = await loadRuntimeConfig(options, () => import('../server/dist/index.js'));
|
|
233
|
+
const app = await startServer();
|
|
234
|
+
const address = app.server.address();
|
|
235
|
+
const port = typeof address === 'object' && address ? address.port : process.env.PORT;
|
|
236
|
+
const url = `http://${process.env.HOST}:${port}`;
|
|
237
|
+
const configHome = process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config');
|
|
238
|
+
const configPath = join(configHome, 'pi-cloud', '.env');
|
|
239
|
+
const databasePath = process.env.PI_CLOUD_DB_PATH ?? join(configHome, 'pi-cloud', 'pi-cloud.sqlite');
|
|
240
|
+
console.log(`Pi Cloud running at ${url}`);
|
|
241
|
+
console.log(`Configuration: ${configPath}`);
|
|
242
|
+
console.log(`Database: ${databasePath}`);
|
|
243
|
+
console.log('These files are preserved when Pi Cloud is uninstalled.');
|
|
244
|
+
if (!options.noOpen) openBrowser(url);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error(error instanceof Error ? error.message : error);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// CLI options must be visible during module initialization, but defaults must wait until dotenv has loaded.
|
|
2
|
+
export async function loadRuntimeConfig(options, loadServer, env = process.env) {
|
|
3
|
+
if (options.port !== undefined) env.PORT = options.port;
|
|
4
|
+
if (options.hostname !== undefined) env.HOST = options.hostname;
|
|
5
|
+
|
|
6
|
+
const server = await loadServer();
|
|
7
|
+
|
|
8
|
+
env.PORT ??= '3000';
|
|
9
|
+
env.HOST ??= '127.0.0.1';
|
|
10
|
+
return server;
|
|
11
|
+
}
|